From 76c5d27618181c4ec9cf86085c65b5e69f5f9109 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Fri, 2 Oct 2020 00:38:44 +0200 Subject: Add opendc-simulator-compute module This change adds an opendc-simulator-compute module which contains interfaces related to simulating compute workloads. For future changes, we intend to decouple the simulation part from the opendc-compute module. --- simulator/opendc-compute/build.gradle.kts | 2 +- .../opendc-experiments-sc18/build.gradle.kts | 2 +- .../opendc-experiments-sc20/build.gradle.kts | 2 +- simulator/opendc-runner-web/build.gradle.kts | 2 +- .../src/main/kotlin/org/opendc/runner/web/Main.kt | 1 - simulator/opendc-simulator/build.gradle.kts | 32 --- .../opendc-simulator-compute/build.gradle.kts | 35 +++ .../simulator/compute/SimExecutionContext.kt | 155 ++++++++++++ .../org/opendc/simulator/compute/SimMachine.kt | 266 +++++++++++++++++++++ .../opendc/simulator/compute/SimMachineModel.kt | 34 +++ .../opendc/simulator/compute/model/MemoryUnit.kt | 38 +++ .../simulator/compute/model/ProcessingNode.kt | 38 +++ .../simulator/compute/model/ProcessingUnit.kt | 36 +++ .../simulator/compute/workload/SimFlopsWorkload.kt | 57 +++++ .../simulator/compute/workload/SimTraceWorkload.kt | 53 ++++ .../simulator/compute/workload/SimWorkload.kt | 38 +++ .../org/opendc/simulator/compute/SimMachineTest.kt | 83 +++++++ .../compute/workload/SimFlopsWorkloadTest.kt | 73 ++++++ .../opendc-simulator-core/build.gradle.kts | 32 +++ .../simulator/utils/DelayControllerClockAdapter.kt | 46 ++++ .../simulator/utils/DelayControllerClockAdapter.kt | 46 ---- simulator/opendc-workflows/build.gradle.kts | 2 +- simulator/settings.gradle.kts | 3 +- 23 files changed, 991 insertions(+), 85 deletions(-) create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/build.gradle.kts create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimExecutionContext.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineModel.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MemoryUnit.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingNode.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingUnit.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkloadTest.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-core/build.gradle.kts create mode 100644 simulator/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/utils/DelayControllerClockAdapter.kt delete mode 100644 simulator/opendc-simulator/src/main/kotlin/org/opendc/simulator/utils/DelayControllerClockAdapter.kt diff --git a/simulator/opendc-compute/build.gradle.kts b/simulator/opendc-compute/build.gradle.kts index dfdd6f35..ea82faa5 100644 --- a/simulator/opendc-compute/build.gradle.kts +++ b/simulator/opendc-compute/build.gradle.kts @@ -32,7 +32,7 @@ dependencies { implementation(project(":opendc-utils")) implementation("io.github.microutils:kotlin-logging:1.7.9") - testImplementation(project(":opendc-simulator")) + testImplementation(project(":opendc-simulator:opendc-simulator-core")) testRuntimeOnly("org.slf4j:slf4j-simple:${Library.SLF4J}") testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Library.JUNIT_JUPITER}") diff --git a/simulator/opendc-experiments/opendc-experiments-sc18/build.gradle.kts b/simulator/opendc-experiments/opendc-experiments-sc18/build.gradle.kts index 393fd0d0..b12b1ae3 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc18/build.gradle.kts +++ b/simulator/opendc-experiments/opendc-experiments-sc18/build.gradle.kts @@ -36,7 +36,7 @@ dependencies { api(project(":opendc-core")) implementation(project(":opendc-format")) implementation(project(":opendc-workflows")) - implementation(project(":opendc-simulator")) + implementation(project(":opendc-simulator:opendc-simulator-core")) implementation(kotlin("stdlib")) testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts b/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts index 4bfe21d2..1a7c2f78 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts +++ b/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts @@ -36,7 +36,7 @@ application { dependencies { api(project(":opendc-core")) implementation(project(":opendc-format")) - implementation(project(":opendc-simulator")) + implementation(project(":opendc-simulator:opendc-simulator-core")) implementation("com.github.ajalt:clikt:2.6.0") implementation("me.tongfei:progressbar:0.8.1") diff --git a/simulator/opendc-runner-web/build.gradle.kts b/simulator/opendc-runner-web/build.gradle.kts index 7e81347c..594e8197 100644 --- a/simulator/opendc-runner-web/build.gradle.kts +++ b/simulator/opendc-runner-web/build.gradle.kts @@ -37,7 +37,7 @@ dependencies { implementation(project(":opendc-compute")) implementation(project(":opendc-format")) implementation(project(":opendc-experiments:opendc-experiments-sc20")) - implementation(project(":opendc-simulator")) + implementation(project(":opendc-simulator:opendc-simulator-core")) implementation("com.github.ajalt:clikt:2.8.0") implementation("io.github.microutils:kotlin-logging:1.7.10") diff --git a/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/Main.kt b/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/Main.kt index 67ec046a..2d4272ab 100644 --- a/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/Main.kt +++ b/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/Main.kt @@ -50,7 +50,6 @@ import org.opendc.experiments.sc20.trace.Sc20RawParquetTraceReader import org.opendc.format.trace.sc20.Sc20PerformanceInterferenceReader import org.opendc.simulator.utils.DelayControllerClockAdapter import java.io.File -import java.util.* import kotlin.random.Random private val logger = KotlinLogging.logger {} diff --git a/simulator/opendc-simulator/build.gradle.kts b/simulator/opendc-simulator/build.gradle.kts index a740dcf3..e69de29b 100644 --- a/simulator/opendc-simulator/build.gradle.kts +++ b/simulator/opendc-simulator/build.gradle.kts @@ -1,32 +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. - */ - -description = "Simulation-specific code for use in OpenDC" - -/* Build configuration */ -plugins { - `kotlin-library-convention` -} - -dependencies { - api("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Library.KOTLINX_COROUTINES}") -} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/build.gradle.kts b/simulator/opendc-simulator/opendc-simulator-compute/build.gradle.kts new file mode 100644 index 00000000..cd7e5706 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/build.gradle.kts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +description = "Library for simulation of cloud computing components" + +plugins { + `kotlin-library-convention` +} + +dependencies { + api(project(":opendc-simulator:opendc-simulator-core")) + + testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Library.JUNIT_JUPITER}") + testImplementation("org.junit.platform:junit-platform-launcher:${Library.JUNIT_PLATFORM}") +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimExecutionContext.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimExecutionContext.kt new file mode 100644 index 00000000..5801fcd5 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimExecutionContext.kt @@ -0,0 +1,155 @@ +/* + * 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.simulator.compute + +import kotlinx.coroutines.selects.SelectClause0 +import kotlinx.coroutines.selects.select +import java.time.Clock + +/** + * A simulated execution context in which a bootable image runs. This interface represents the + * firmware interface between the running image (e.g. operating system) and the physical or virtual firmware on + * which the image runs. + */ +public interface SimExecutionContext { + /** + * The virtual clock tracking simulation time. + */ + public val clock: Clock + + /** + * The machine model of the machine that is running the image. + */ + public val machine: SimMachineModel + + /** + * Ask the processor cores to run the specified [slice] and suspend execution until the trigger condition is met as + * specified by [triggerMode]. + * + * After the method returns, [Slice.burst] will contain the remaining burst length for each of the cores (which + * may be zero). These changes may happen anytime during execution of this method and callers should not rely on + * the timing of this change. + * + * @param slice The representation of work to run on the processors. + * @param triggerMode The trigger condition to resume execution. + */ + public suspend fun run(slice: Slice, triggerMode: TriggerMode = TriggerMode.FIRST): Unit = + select { onRun(slice, triggerMode).invoke {} } + + /** + * Ask the processors cores to run the specified [batch] of work slices and suspend execution until the trigger + * condition is met as specified by [triggerMode]. + * + * After the method returns, [Slice.burst] will contain the remaining burst length for each of the cores (which + * may be zero). These changes may happen anytime during execution of this method and callers should not rely on + * the timing of this change. + * + * In case slices in the batch do not finish processing before their deadline, [merge] is called to merge these + * slices with the next slice to be executed. + * + * @param batch The batch of work to run on the processors. + * @param triggerMode The trigger condition to resume execution. + * @param merge The merge function for consecutive slices in case the last slice was not completed within its + * deadline. + */ + public suspend fun run( + batch: Sequence, + triggerMode: TriggerMode = TriggerMode.FIRST, + merge: (Slice, Slice) -> Slice = { _, r -> r } + ): Unit = select { onRun(batch, triggerMode, merge).invoke {} } + + /** + * Ask the processor cores to run the specified [slice] and select when the trigger condition is met as specified + * by [triggerMode]. + * + * After the method returns, [Slice.burst] will contain the remaining burst length for each of the cores (which + * may be zero). These changes may happen anytime during execution of this method and callers should not rely on + * the timing of this change. + * + * @param slice The representation of work to request from the processors. + * @param triggerMode The trigger condition to resume execution. + */ + public fun onRun(slice: Slice, triggerMode: TriggerMode = TriggerMode.FIRST): SelectClause0 = + onRun(sequenceOf(slice), triggerMode) + + /** + * Ask the processors cores to run the specified [batch] of work slices and select when the trigger condition is met + * as specified by [triggerMode]. + * + * After the method returns, [Slice.burst] will contain the remaining burst length for each of the cores (which + * may be zero). These changes may happen anytime during execution of this method and callers should not rely on + * the timing of this change. + * + * In case slices in the batch do not finish processing before their deadline, [merge] is called to merge these + * slices with the next slice to be executed. + * + * @param batch The batch of work to run on the processors. + * @param triggerMode The trigger condition to resume execution during the **last** slice. + * @param merge The merge function for consecutive slices in case the last slice was not completed within its + * deadline. + */ + public fun onRun( + batch: Sequence, + triggerMode: TriggerMode = TriggerMode.FIRST, + merge: (Slice, Slice) -> Slice = { _, r -> r } + ): SelectClause0 + + /** + * A request to the host machine for a slice of CPU time from the processor cores. + * + * Both [burst] and [limit] must be of the same size and in any other case the method will throw an + * [IllegalArgumentException]. + * + * + * @param burst The burst time to request from each of the processor cores. + * @param limit The maximum usage in terms of MHz that the processing core may use while running the burst. + * @param deadline The instant at which this slice needs to be fulfilled. + */ + public class Slice(public val burst: LongArray, public val limit: DoubleArray, public val deadline: Long) { + init { + require(burst.size == limit.size) { "Incompatible array dimensions" } + } + } + + /** + * The modes for triggering a machine exit from the machine. + */ + public enum class TriggerMode { + /** + * A machine exit occurs when either the first processor finishes processing a **non-zero** burst or the + * deadline is reached. + */ + FIRST, + + /** + * A machine exit occurs when either the last processor finishes processing a **non-zero** burst or the deadline + * is reached. + */ + LAST, + + /** + * A machine exit occurs only when the deadline is reached. + */ + DEADLINE + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt new file mode 100644 index 00000000..df74a5f1 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt @@ -0,0 +1,266 @@ +/* + * 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.simulator.compute + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.intrinsics.startCoroutineCancellable +import kotlinx.coroutines.selects.SelectClause0 +import kotlinx.coroutines.selects.SelectInstance +import org.opendc.simulator.compute.workload.SimWorkload +import java.lang.Runnable +import java.time.Clock +import kotlin.coroutines.ContinuationInterceptor +import kotlin.math.ceil +import kotlin.math.max +import kotlin.math.min + +/** + * A simulated bare-metal machine that is able to run a single workload. + * + * @param coroutineScope The [CoroutineScope] to run the simulated workload in. + * @param clock The virtual clock to track the simulation time. + * @param model The machine model to simulate. + */ +@OptIn(ExperimentalCoroutinesApi::class, InternalCoroutinesApi::class) +public class SimMachine( + private val coroutineScope: CoroutineScope, + private val clock: Clock, + public val model: SimMachineModel +) { + /** + * A [StateFlow] representing the CPU usage of the simulated machine. + */ + public val usage: StateFlow + get() = usageState + + /** + * Run the specified [SimWorkload] on this machine and suspend execution util the workload has finished. + */ + public suspend fun run(workload: SimWorkload) { + workload.run(ctx) + } + + /** + * The execution context in which the workload runs. + */ + private val ctx = object : SimExecutionContext { + override val machine: SimMachineModel + get() = this@SimMachine.model + + override val clock: Clock + get() = this@SimMachine.clock + + override fun onRun( + batch: Sequence, + triggerMode: SimExecutionContext.TriggerMode, + merge: (SimExecutionContext.Slice, SimExecutionContext.Slice) -> SimExecutionContext.Slice + ): SelectClause0 { + return object : SelectClause0 { + @InternalCoroutinesApi + override fun registerSelectClause0(select: SelectInstance, block: suspend () -> R) { + // Do not reset the usage state: we will set it ourselves + usageFlush?.dispose() + usageFlush = null + + val queue = batch.iterator() + var start = Long.MIN_VALUE + var currentWork: SliceWork? = null + var currentDisposable: DisposableHandle? = null + + fun schedule(slice: SimExecutionContext.Slice) { + start = clock.millis() + + val isLastSlice = !queue.hasNext() + val work = SliceWork(slice) + val candidateDuration = when (triggerMode) { + SimExecutionContext.TriggerMode.FIRST -> work.minExit + SimExecutionContext.TriggerMode.LAST -> work.maxExit + SimExecutionContext.TriggerMode.DEADLINE -> slice.deadline - start + } + + // Check whether the deadline is exceeded during the run of the slice. + val duration = min(candidateDuration, slice.deadline - start) + + val action = Runnable { + currentWork = null + + // Flush all the work that was performed + val hasFinished = work.stop(duration) + + if (!isLastSlice) { + val candidateSlice = queue.next() + val nextSlice = + // If our previous slice exceeds its deadline, merge it with the next candidate slice + if (hasFinished) + candidateSlice + else + merge(candidateSlice, slice) + schedule(nextSlice) + } else if (select.trySelect()) { + block.startCoroutineCancellable(select.completion) + } + } + + // Schedule the flush after the entire slice has finished + currentDisposable = delay.invokeOnTimeout(duration, action) + + // Start the slice work + currentWork = work + work.start() + } + + // Schedule the first work + if (queue.hasNext()) { + schedule(queue.next()) + + // A DisposableHandle to flush the work in case the call is cancelled + val disposable = DisposableHandle { + val end = clock.millis() + val duration = end - start + + currentWork?.stop(duration) + currentDisposable?.dispose() + + // Schedule reset the usage of the machine since the call is returning + usageFlush = delay.invokeOnTimeout(1) { + usageState.value = 0.0 + usageFlush = null + } + } + + select.disposeOnSelect(disposable) + } else if (select.trySelect()) { + // No work has been given: select immediately + block.startCoroutineCancellable(select.completion) + } + } + } + } + } + + /** + * The [MutableStateFlow] containing the load of the server. + */ + private val usageState = MutableStateFlow(0.0) + + /** + * A disposable to prevent resetting the usage state for subsequent calls to onRun. + */ + private var usageFlush: DisposableHandle? = null + + /** + * Cache the [Delay] instance for timing. + * + * XXX We need to cache this before the call to [onRun] since doing this in [onRun] is too heavy. + * XXX Note however that this is an ugly hack which may break in the future. + */ + @OptIn(InternalCoroutinesApi::class) + private val delay = coroutineScope.coroutineContext[ContinuationInterceptor] as Delay + + /** + * A slice to be processed. + */ + private inner class SliceWork(val slice: SimExecutionContext.Slice) { + /** + * The duration after which the first processor finishes processing this slice. + */ + val minExit: Long + + /** + * The duration after which the last processor finishes processing this slice. + */ + val maxExit: Long + + /** + * A flag to indicate that the slice will exceed the deadline. + */ + val exceedsDeadline: Boolean + get() = slice.deadline < maxExit + + /** + * The total amount of CPU usage. + */ + val totalUsage: Double + + /** + * A flag to indicate that this slice is empty. + */ + val isEmpty: Boolean + + init { + var totalUsage = 0.0 + var minExit = Long.MAX_VALUE + var maxExit = 0L + var nonEmpty = false + + // Determine the duration of the first/last CPU to finish + for (i in 0 until min(model.cpus.size, slice.burst.size)) { + val cpu = model.cpus[i] + val usage = min(slice.limit[i], cpu.frequency) + val cpuDuration = ceil(slice.burst[i] / usage * 1000).toLong() // Convert from seconds to milliseconds + + totalUsage += usage / cpu.frequency + + if (cpuDuration != 0L) { // We only wait for processor cores with a non-zero burst + minExit = min(minExit, cpuDuration) + maxExit = max(maxExit, cpuDuration) + nonEmpty = true + } + } + + this.isEmpty = !nonEmpty + this.totalUsage = totalUsage + this.minExit = minExit + this.maxExit = maxExit + } + + /** + * Indicate that the work on the slice has started. + */ + fun start() { + usageState.value = totalUsage / model.cpus.size + } + + /** + * Flush the work performed on the slice. + */ + fun stop(duration: Long): Boolean { + var hasFinished = true + + for (i in 0 until min(model.cpus.size, slice.burst.size)) { + val usage = min(slice.limit[i], model.cpus[i].frequency) + val granted = ceil(duration / 1000.0 * usage).toLong() + val res = max(0, slice.burst[i] - granted) + slice.burst[i] = res + + if (res != 0L) { + hasFinished = false + } + } + + return hasFinished + } + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineModel.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineModel.kt new file mode 100644 index 00000000..c2988b11 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineModel.kt @@ -0,0 +1,34 @@ +/* + * 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.simulator.compute + +import org.opendc.simulator.compute.model.MemoryUnit +import org.opendc.simulator.compute.model.ProcessingUnit + +/** + * A description of the physical or virtual machine on which a bootable image runs. + * + * @property cpus The list of processing units available to the image. + * @property memory The list of memory units available to the image. + */ +public data class SimMachineModel(public val cpus: List, public val memory: List) diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MemoryUnit.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MemoryUnit.kt new file mode 100644 index 00000000..bcbde5b1 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MemoryUnit.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.model + +/** + * A memory unit of a compute resource, either virtual or physical. + * + * @property vendor The vendor string of the memory. + * @property modelName The name of the memory model. + * @property speed The access speed of the memory in MHz. + * @property size The size of the memory unit in MBs. + */ +public data class MemoryUnit( + public val vendor: String, + public val modelName: String, + public val speed: Double, + public val size: Long +) diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingNode.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingNode.kt new file mode 100644 index 00000000..58ed816c --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingNode.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.model + +/** + * A processing node/package/socket containing possibly several CPU cores. + * + * @property vendor The vendor string of the processor node. + * @property modelName The name of the processor node. + * @property arch The micro-architecture of the processor node. + * @property coreCount The number of logical CPUs in the processor node. + */ +public data class ProcessingNode( + public val vendor: String, + public val arch: String, + public val modelName: String, + public val coreCount: Int +) diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingUnit.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingUnit.kt new file mode 100644 index 00000000..415e95e6 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingUnit.kt @@ -0,0 +1,36 @@ +/* + * 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.simulator.compute.model + +/** + * A single logical compute unit of processor node, either virtual or physical. + * + * @property node The processing node containing the CPU core. + * @property id The identifier of the CPU core within the processing node. + * @property frequency The clock rate of the CPU in MHz. + */ +public data class ProcessingUnit( + public val node: ProcessingNode, + public val id: Int, + public val frequency: Double +) diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt new file mode 100644 index 00000000..918a78bd --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.workload + +import org.opendc.simulator.compute.SimExecutionContext +import kotlin.math.min + +/** + * A [SimWorkload] that models applications performing a static number of floating point operations ([flops]) on + * a compute resource. + * + * @property flops The number of floating point operations to perform for this task in MFLOPs. + * @property cores The number of cores that the image is able to utilize. + * @property utilization A model of the CPU utilization of the application. + */ +public class SimFlopsWorkload( + public val flops: Long, + public val cores: Int, + public val utilization: Double = 0.8 +) : SimWorkload { + init { + require(flops >= 0) { "Negative number of flops" } + require(cores > 0) { "Negative number of cores or no cores" } + require(utilization > 0.0 && utilization <= 1.0) { "Utilization must be in (0, 1]" } + } + + /** + * Execute the runtime behavior based on a number of floating point operations to execute. + */ + override suspend fun run(ctx: SimExecutionContext) { + val cores = min(this.cores, ctx.machine.cpus.size) + val burst = LongArray(cores) { flops / cores } + val maxUsage = DoubleArray(cores) { i -> ctx.machine.cpus[i].frequency * utilization } + + ctx.run(SimExecutionContext.Slice(burst, maxUsage, Long.MAX_VALUE), triggerMode = SimExecutionContext.TriggerMode.LAST) + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt new file mode 100644 index 00000000..e7eaa8bf --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt @@ -0,0 +1,53 @@ +/* + * 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.simulator.compute.workload + +import org.opendc.simulator.compute.SimExecutionContext +import kotlin.math.min + +/** + * A [SimWorkload] that replays a workload trace consisting of multiple fragments, each indicating the resource + * consumption for some period of time. + */ +public class SimTraceWorkload(private val trace: Sequence) : SimWorkload { + override suspend fun run(ctx: SimExecutionContext) { + var offset = ctx.clock.millis() + + val batch = trace.map { fragment -> + val cores = min(fragment.cores, ctx.machine.cpus.size) + val burst = LongArray(cores) { fragment.flops / cores } + val usage = DoubleArray(cores) { fragment.usage / cores } + offset += fragment.duration + SimExecutionContext.Slice(burst, usage, offset) + } + + ctx.run(batch) + } + + override fun toString(): String = "SimTraceWorkload" + + /** + * A fragment of the workload. + */ + public data class Fragment(val time: Long, val flops: Long, val duration: Long, val usage: Double, val cores: Int) +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt new file mode 100644 index 00000000..0ef4130e --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.workload + +import org.opendc.simulator.compute.SimExecutionContext + +/** + * A model that characterizes the runtime behavior of some particular workload. + */ +public interface SimWorkload { + /** + * Launch the workload in the specified [SimExecutionContext]. + * + * This method should encapsulate and characterize the runtime behavior of the instance resulting from launching + * the workload on some machine, in terms of the resource consumption on the machine. + */ + public suspend fun run(ctx: SimExecutionContext) +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt new file mode 100644 index 00000000..f6fb8e04 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt @@ -0,0 +1,83 @@ +/* + * 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.simulator.compute + +import kotlinx.coroutines.* +import kotlinx.coroutines.test.TestCoroutineScope +import kotlinx.coroutines.test.runBlockingTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.opendc.simulator.compute.model.MemoryUnit +import org.opendc.simulator.compute.model.ProcessingNode +import org.opendc.simulator.compute.model.ProcessingUnit +import org.opendc.simulator.compute.workload.SimFlopsWorkload +import org.opendc.simulator.utils.DelayControllerClockAdapter + +/** + * Test suite for the [SimMachine] class. + */ +@OptIn(ExperimentalCoroutinesApi::class) +class SimMachineTest { + private lateinit var machineModel: SimMachineModel + + @BeforeEach + fun setUp() { + val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) + + machineModel = SimMachineModel( + cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, + memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + ) + } + + @Test + fun testFlopsWorkload() { + val testScope = TestCoroutineScope() + val clock = DelayControllerClockAdapter(testScope) + val machine = SimMachine(testScope, clock, machineModel) + + testScope.runBlockingTest { + machine.run(SimFlopsWorkload(2_000, 2, utilization = 1.0)) + + // Two cores execute 1000 MFlOps per second (1000 ms) + assertEquals(1000, testScope.currentTime) + } + } + + @Test + fun testUsage() { + val testScope = TestCoroutineScope() + val clock = DelayControllerClockAdapter(testScope) + val machine = SimMachine(testScope, clock, machineModel) + + testScope.runBlockingTest { + machine.run(SimFlopsWorkload(2_000, 2, utilization = 1.0)) + assertEquals(1.0, machine.usage.value) + + // Wait for the usage to reset + delay(1) + assertEquals(0.0, machine.usage.value) + } + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkloadTest.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkloadTest.kt new file mode 100644 index 00000000..51bed76c --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkloadTest.kt @@ -0,0 +1,73 @@ +/* + * 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.simulator.compute.workload + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +/** + * Test suite for [SimFlopsWorkload] class. + */ +class SimFlopsWorkloadTest { + @Test + fun testFlopsNonNegative() { + assertThrows("FLOPs must be non-negative") { + SimFlopsWorkload(-1, 1) + } + } + + @Test + fun testCoresNonZero() { + assertThrows("Cores cannot be zero") { + SimFlopsWorkload(1, 0) + } + } + + @Test + fun testCoresPositive() { + assertThrows("Cores cannot be negative") { + SimFlopsWorkload(1, -1) + } + } + + @Test + fun testUtilizationNonZero() { + assertThrows("Utilization cannot be zero") { + SimFlopsWorkload(1, 1, 0.0) + } + } + + @Test + fun testUtilizationPositive() { + assertThrows("Utilization cannot be negative") { + SimFlopsWorkload(1, 1, -1.0) + } + } + + @Test + fun testUtilizationNotLargerThanOne() { + assertThrows("Utilization cannot be larger than one") { + SimFlopsWorkload(1, 1, 2.0) + } + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-core/build.gradle.kts b/simulator/opendc-simulator/opendc-simulator-core/build.gradle.kts new file mode 100644 index 00000000..a740dcf3 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-core/build.gradle.kts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +description = "Simulation-specific code for use in OpenDC" + +/* Build configuration */ +plugins { + `kotlin-library-convention` +} + +dependencies { + api("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Library.KOTLINX_COROUTINES}") +} diff --git a/simulator/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/utils/DelayControllerClockAdapter.kt b/simulator/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/utils/DelayControllerClockAdapter.kt new file mode 100644 index 00000000..84c18e87 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/utils/DelayControllerClockAdapter.kt @@ -0,0 +1,46 @@ +/* + * 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.simulator.utils + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.DelayController +import java.time.Clock +import java.time.Instant +import java.time.ZoneId + +/** + * A virtual [Clock] that abstracts accesses to [DelayController]'s virtual clock. + */ +@OptIn(ExperimentalCoroutinesApi::class) +public class DelayControllerClockAdapter( + private val delayController: DelayController, + private val zone: ZoneId = ZoneId.systemDefault() +) : Clock() { + override fun getZone(): ZoneId = zone + + override fun withZone(zone: ZoneId): Clock = DelayControllerClockAdapter(delayController, zone) + + override fun instant(): Instant = Instant.ofEpochMilli(millis()) + + override fun millis(): Long = delayController.currentTime +} diff --git a/simulator/opendc-simulator/src/main/kotlin/org/opendc/simulator/utils/DelayControllerClockAdapter.kt b/simulator/opendc-simulator/src/main/kotlin/org/opendc/simulator/utils/DelayControllerClockAdapter.kt deleted file mode 100644 index 84c18e87..00000000 --- a/simulator/opendc-simulator/src/main/kotlin/org/opendc/simulator/utils/DelayControllerClockAdapter.kt +++ /dev/null @@ -1,46 +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.simulator.utils - -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.DelayController -import java.time.Clock -import java.time.Instant -import java.time.ZoneId - -/** - * A virtual [Clock] that abstracts accesses to [DelayController]'s virtual clock. - */ -@OptIn(ExperimentalCoroutinesApi::class) -public class DelayControllerClockAdapter( - private val delayController: DelayController, - private val zone: ZoneId = ZoneId.systemDefault() -) : Clock() { - override fun getZone(): ZoneId = zone - - override fun withZone(zone: ZoneId): Clock = DelayControllerClockAdapter(delayController, zone) - - override fun instant(): Instant = Instant.ofEpochMilli(millis()) - - override fun millis(): Long = delayController.currentTime -} diff --git a/simulator/opendc-workflows/build.gradle.kts b/simulator/opendc-workflows/build.gradle.kts index 97c588da..efffe7a1 100644 --- a/simulator/opendc-workflows/build.gradle.kts +++ b/simulator/opendc-workflows/build.gradle.kts @@ -32,7 +32,7 @@ dependencies { api(project(":opendc-compute")) implementation(project(":opendc-utils")) - testImplementation(project(":opendc-simulator")) + testImplementation(project(":opendc-simulator:opendc-simulator-core")) testImplementation(project(":opendc-format")) testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Library.JUNIT_JUPITER}") diff --git a/simulator/settings.gradle.kts b/simulator/settings.gradle.kts index e5e218b8..3de05fe1 100644 --- a/simulator/settings.gradle.kts +++ b/simulator/settings.gradle.kts @@ -28,5 +28,6 @@ include(":opendc-format") include(":opendc-experiments:opendc-experiments-sc18") include(":opendc-experiments:opendc-experiments-sc20") include(":opendc-runner-web") -include(":opendc-simulator") +include(":opendc-simulator:opendc-simulator-core") +include(":opendc-simulator:opendc-simulator-compute") include(":opendc-utils") -- cgit v1.2.3 From ac7016c4c5f15bf20b21e7d34e93d8b963aab231 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Fri, 2 Oct 2020 20:57:51 +0200 Subject: Implement BareMetalDriver using SimMachine This change creates an implementation of the BareMetalDriver interface using the simulation logic of SimMachine. This implementation will eventually replace the SimpleBareMetalDriver implementation. --- simulator/opendc-compute/build.gradle.kts | 1 + .../opendc/compute/core/execution/ServerContext.kt | 4 +- .../opendc/compute/core/image/SimWorkloadImage.kt | 45 ++++ .../compute/metal/driver/SimBareMetalDriver.kt | 229 +++++++++++++++++++++ .../compute/metal/driver/SimpleBareMetalDriver.kt | 3 +- .../opendc/compute/virt/driver/SimpleVirtDriver.kt | 2 +- .../org/opendc/compute/virt/driver/VirtDriver.kt | 2 +- .../virt/service/SimpleVirtProvisioningService.kt | 4 +- .../virt/service/VirtProvisioningService.kt | 2 +- .../core/image/FlopsApplicationImageTest.kt | 4 +- .../compute/metal/driver/SimBareMetalDriverTest.kt | 90 ++++++++ .../metal/driver/SimpleBareMetalDriverTest.kt | 4 +- .../metal/service/SimpleProvisioningServiceTest.kt | 33 ++- .../org/opendc/compute/virt/HypervisorTest.kt | 4 +- 14 files changed, 402 insertions(+), 25 deletions(-) create mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/SimWorkloadImage.kt create mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt create mode 100644 simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriverTest.kt diff --git a/simulator/opendc-compute/build.gradle.kts b/simulator/opendc-compute/build.gradle.kts index ea82faa5..94af7861 100644 --- a/simulator/opendc-compute/build.gradle.kts +++ b/simulator/opendc-compute/build.gradle.kts @@ -31,6 +31,7 @@ dependencies { api(project(":opendc-core")) implementation(project(":opendc-utils")) implementation("io.github.microutils:kotlin-logging:1.7.9") + implementation(project(":opendc-simulator:opendc-simulator-compute")) testImplementation(project(":opendc-simulator:opendc-simulator-core")) testRuntimeOnly("org.slf4j:slf4j-simple:${Library.SLF4J}") diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ServerContext.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ServerContext.kt index 9674c98d..82cec00a 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ServerContext.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ServerContext.kt @@ -66,7 +66,7 @@ public interface ServerContext { * @param triggerMode The trigger condition to resume execution. */ public suspend fun run(slice: Slice, triggerMode: TriggerMode = TriggerMode.FIRST): Unit = - select { onRun(slice, triggerMode).invoke {} } + select { onRun(slice, triggerMode).invoke {} } /** * Ask the processors cores to run the specified [batch] of work slices and suspend execution until the trigger @@ -88,7 +88,7 @@ public interface ServerContext { batch: Sequence, triggerMode: TriggerMode = TriggerMode.FIRST, merge: (Slice, Slice) -> Slice = { _, r -> r } - ): Unit = select { onRun(batch, triggerMode, merge).invoke {} } + ): Unit = select { onRun(batch, triggerMode, merge).invoke {} } /** * Ask the processor cores to run the specified [slice] and select when the trigger condition is met as specified diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/SimWorkloadImage.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/SimWorkloadImage.kt new file mode 100644 index 00000000..7caedf6d --- /dev/null +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/SimWorkloadImage.kt @@ -0,0 +1,45 @@ +/* + * 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.core.image + +import org.opendc.compute.core.execution.ServerContext +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 { + override suspend fun invoke(ctx: ServerContext): Unit = TODO() +} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt new file mode 100644 index 00000000..e9346a6c --- /dev/null +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt @@ -0,0 +1,229 @@ +/* + * 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.metal.driver + +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.execution.ShutdownException +import org.opendc.compute.core.image.EmptyImage +import org.opendc.compute.core.image.Image +import org.opendc.compute.core.image.SimWorkloadImage +import org.opendc.compute.metal.Node +import org.opendc.compute.metal.NodeEvent +import org.opendc.compute.metal.NodeState +import org.opendc.compute.metal.power.ConstantPowerModel +import org.opendc.core.power.PowerModel +import org.opendc.core.services.ServiceRegistry +import org.opendc.simulator.compute.SimMachine +import org.opendc.simulator.compute.SimMachineModel +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. + * + * @param coroutineScope The [CoroutineScope] the driver runs in. + * @param clock The virtual clock to keep track of time. + * @param uid The unique identifier of the machine. + * @param name An optional name of the machine. + * @param metadata The initial metadata of the node. + * @param machine The machine model to simulate. + * @param powerModel The power model of this machine. + */ +@OptIn(ExperimentalCoroutinesApi::class) +public class SimBareMetalDriver( + private val coroutineScope: CoroutineScope, + private val clock: Clock, + uid: UUID, + name: String, + metadata: Map, + machine: SimMachineModel, + powerModel: PowerModel = ConstantPowerModel(0.0) +) : BareMetalDriver { + /** + * The flavor that corresponds to this machine. + */ + private val flavor = Flavor( + machine.cpus.size, + machine.memory.map { it.size }.sum() + ) + + /** + * The events of the machine. + */ + private val events = EventFlow() + + /** + * The machine state. + */ + private val nodeState = + StateFlow(Node(uid, name, metadata + ("driver" to this), NodeState.SHUTOFF, EmptyImage, null, events)) + + override val node: Flow = nodeState + + override val usage: Flow + get() = this.machine.usage + + override val powerDraw: Flow = powerModel(this) + + /** + * The internal random instance. + */ + private val random = Random(uid.leastSignificantBits xor uid.mostSignificantBits) + + /** + * The [SimMachine] we use to run the workload. + */ + private val machine = SimMachine(coroutineScope, clock, machine) + + /** + * 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? = null + + override suspend fun init(): Node { + return nodeState.value + } + + override suspend fun start(): Node { + val node = nodeState.value + if (node.state != NodeState.SHUTOFF) { + return node + } + + val events = EventFlow() + serverEvents = events + val server = Server( + UUID(random.nextLong(), random.nextLong()), + node.name, + emptyMap(), + flavor, + node.image, + ServerState.BUILD, + ServiceRegistry().put(BareMetalDriver, this@SimBareMetalDriver), + events + ) + + job = coroutineScope.launch { + delay(1) // TODO Introduce boot time + initMachine() + try { + machine.run((node.image as SimWorkloadImage).workload) + exitMachine(null) + } catch (_: CancellationException) { + // Ignored + } catch (cause: Throwable) { + exitMachine(cause) + } + } + + setNode(node.copy(state = NodeState.BOOT, server = server)) + return nodeState.value + } + + private fun initMachine() { + val server = nodeState.value.server?.copy(state = ServerState.ACTIVE) + setNode(nodeState.value.copy(state = NodeState.ACTIVE, server = server)) + } + + private fun exitMachine(cause: Throwable?) { + val newServerState = + if (cause == null || (cause is ShutdownException && cause.cause == null)) + ServerState.SHUTOFF + else + ServerState.ERROR + val newNodeState = + if (cause == null || (cause is ShutdownException && cause.cause != null)) + nodeState.value.state + else + NodeState.ERROR + val server = nodeState.value.server?.copy(state = newServerState) + setNode(nodeState.value.copy(state = newNodeState, server = server)) + + serverEvents?.close() + serverEvents = null + } + + override suspend fun stop(): Node { + val node = nodeState.value + if (node.state == NodeState.SHUTOFF) { + return node + } + + job?.cancelAndJoin() + setNode(node.copy(state = NodeState.SHUTOFF, server = null)) + return node + } + + override suspend fun reboot(): Node { + stop() + return start() + } + + override suspend fun setImage(image: Image): Node { + setNode(nodeState.value.copy(image = image)) + return nodeState.value + } + + override suspend fun refresh(): Node = nodeState.value + + private fun setNode(value: Node) { + val field = nodeState.value + if (field.state != value.state) { + events.emit(NodeEvent.StateChanged(value, field.state)) + } + + if (field.server != null && value.server != null && field.server.state != value.server.state) { + serverEvents?.emit(ServerEvent.StateChanged(value.server, field.server.state)) + } + + nodeState.value = value + } + + override val scope: CoroutineScope + get() = coroutineScope + + override suspend fun fail() { + val server = nodeState.value.server?.copy(state = ServerState.ERROR) + setNode(nodeState.value.copy(state = NodeState.ERROR, server = server)) + } + + override suspend fun recover() { + val server = nodeState.value.server?.copy(state = ServerState.ACTIVE) + setNode(nodeState.value.copy(state = NodeState.ACTIVE, server = server)) + } + + override fun toString(): String = "SimBareMetalDriver(node = ${nodeState.value.uid})" +} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriver.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriver.kt index 817528f6..c4897197 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriver.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriver.kt @@ -92,7 +92,8 @@ public class SimpleBareMetalDriver( /** * The flavor that corresponds to this machine. */ - private val flavor = Flavor(cpus.size, memoryUnits.map { it.size }.sum()) + private val flavor = + Flavor(cpus.size, memoryUnits.map { it.size }.sum()) /** * The current active server context. diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimpleVirtDriver.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimpleVirtDriver.kt index fb3d5f84..d581e651 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimpleVirtDriver.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimpleVirtDriver.kt @@ -105,7 +105,7 @@ public class SimpleVirtDriver( override suspend fun spawn( name: String, image: Image, - flavor: Flavor + flavor: org.opendc.compute.core.Flavor ): Server { val requiredMemory = flavor.memorySize if (availableMemory - requiredMemory < 0) { diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/VirtDriver.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/VirtDriver.kt index 9663f4da..54728eb6 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/VirtDriver.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/VirtDriver.kt @@ -51,7 +51,7 @@ public interface VirtDriver { public suspend fun spawn( name: String, image: Image, - flavor: Flavor + flavor: org.opendc.compute.core.Flavor ): Server public companion object Key : AbstractServiceKey(UUID.randomUUID(), "virtual-driver") diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/SimpleVirtProvisioningService.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/SimpleVirtProvisioningService.kt index 3141529f..f021d3f0 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/SimpleVirtProvisioningService.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/SimpleVirtProvisioningService.kt @@ -119,7 +119,7 @@ public class SimpleVirtProvisioningService( override suspend fun deploy( name: String, image: Image, - flavor: Flavor + flavor: org.opendc.compute.core.Flavor ): Server { eventFlow.emit( VirtProvisioningEvent.MetricsAvailable( @@ -373,7 +373,7 @@ public class SimpleVirtProvisioningService( public data class ImageView( public val name: String, public val image: Image, - public val flavor: Flavor, + public val flavor: org.opendc.compute.core.Flavor, public val continuation: Continuation, public var server: Server? = null ) diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/VirtProvisioningService.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/VirtProvisioningService.kt index 7ed577c4..13dfd640 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/VirtProvisioningService.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/VirtProvisioningService.kt @@ -58,7 +58,7 @@ public interface VirtProvisioningService { public suspend fun deploy( name: String, image: Image, - flavor: Flavor + flavor: org.opendc.compute.core.Flavor ): Server /** diff --git a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/core/image/FlopsApplicationImageTest.kt b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/core/image/FlopsApplicationImageTest.kt index 309dceb8..8870f138 100644 --- a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/core/image/FlopsApplicationImageTest.kt +++ b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/core/image/FlopsApplicationImageTest.kt @@ -1,7 +1,5 @@ /* - * MIT License - * - * Copyright (c) 2020 atlarge-research + * 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 diff --git a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriverTest.kt b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriverTest.kt new file mode 100644 index 00000000..584dcef4 --- /dev/null +++ b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriverTest.kt @@ -0,0 +1,90 @@ +/* + * 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.metal.driver + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.TestCoroutineScope +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.opendc.compute.core.image.SimWorkloadImage +import org.opendc.simulator.compute.SimMachineModel +import org.opendc.simulator.compute.model.MemoryUnit +import org.opendc.simulator.compute.model.ProcessingNode +import org.opendc.simulator.compute.model.ProcessingUnit +import org.opendc.simulator.compute.workload.SimFlopsWorkload +import org.opendc.simulator.utils.DelayControllerClockAdapter +import java.util.UUID + +@OptIn(ExperimentalCoroutinesApi::class) +internal class SimBareMetalDriverTest { + private lateinit var machineModel: SimMachineModel + + @BeforeEach + fun setUp() { + val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 4) + + machineModel = SimMachineModel( + cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 2000.0) }, + memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + ) + } + + @Test + fun testFlopsWorkload() { + val testScope = TestCoroutineScope() + val clock = DelayControllerClockAdapter(testScope) + + var finalState: ServerState = ServerState.BUILD + var finalTime = 0L + + testScope.launch { + val driver = SimBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), machineModel) + val image = SimWorkloadImage(UUID.randomUUID(), "", emptyMap(), SimFlopsWorkload(4_000, 2, utilization = 1.0)) + + // Batch driver commands + withContext(coroutineContext) { + driver.init() + driver.setImage(image) + val server = driver.start().server!! + server.events.collect { event -> + when (event) { + is ServerEvent.StateChanged -> { + finalState = event.server.state + finalTime = clock.millis() + } + } + } + } + } + + testScope.advanceUntilIdle() + assertEquals(ServerState.SHUTOFF, finalState) + assertEquals(1001, finalTime) + } +} diff --git a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt index 9c9dc864..23c9408d 100644 --- a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt +++ b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt @@ -52,9 +52,9 @@ internal class SimpleBareMetalDriverTest { var finalState: ServerState = ServerState.BUILD testScope.launch { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 4) - val cpus = List(4) { ProcessingUnit(cpuNode, it, 2400.0) } + val cpus = List(4) { ProcessingUnit(cpuNode, it, 2000.0) } val driver = SimpleBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), cpus, emptyList()) - val image = FlopsApplicationImage(UUID.randomUUID(), "", emptyMap(), 1_000, 2) + val image = FlopsApplicationImage(UUID.randomUUID(), "", emptyMap(), 4_000, 2, utilization = 1.0) // Batch driver commands withContext(coroutineContext) { diff --git a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt index 91d4787c..6f13e557 100644 --- a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt +++ b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt @@ -27,11 +27,15 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collect 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.ProcessingNode -import org.opendc.compute.core.ProcessingUnit -import org.opendc.compute.core.image.FlopsApplicationImage -import org.opendc.compute.metal.driver.SimpleBareMetalDriver +import org.opendc.compute.core.image.SimWorkloadImage +import org.opendc.compute.metal.driver.SimBareMetalDriver +import org.opendc.simulator.compute.SimMachineModel +import org.opendc.simulator.compute.model.MemoryUnit +import org.opendc.simulator.compute.model.ProcessingNode +import org.opendc.simulator.compute.model.ProcessingUnit +import org.opendc.simulator.compute.workload.SimFlopsWorkload import org.opendc.simulator.utils.DelayControllerClockAdapter import java.util.UUID @@ -40,20 +44,29 @@ import java.util.UUID */ @OptIn(ExperimentalCoroutinesApi::class) internal class SimpleProvisioningServiceTest { + private lateinit var machineModel: SimMachineModel + + @BeforeEach + fun setUp() { + val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 4) + + machineModel = SimMachineModel( + cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 2000.0) }, + memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + ) + } + /** * A basic smoke test. */ @Test - fun smoke() { + fun testSmoke() { val testScope = TestCoroutineScope() val clock = DelayControllerClockAdapter(testScope) testScope.launch { - val image = FlopsApplicationImage(UUID.randomUUID(), "", emptyMap(), 1000, 2) - - val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 4) - val cpus = List(4) { ProcessingUnit(cpuNode, it, 2400.0) } - val driver = SimpleBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), cpus, emptyList()) + val image = SimWorkloadImage(UUID.randomUUID(), "", emptyMap(), SimFlopsWorkload(1000, 2)) + val driver = SimBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), machineModel) val provisioner = SimpleProvisioningService() provisioner.create(driver) diff --git a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/HypervisorTest.kt b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/HypervisorTest.kt index 68efb1a3..369b9538 100644 --- a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/HypervisorTest.kt +++ b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/HypervisorTest.kt @@ -75,7 +75,7 @@ internal class HypervisorTest { delay(5) - val flavor = Flavor(1, 0) + val flavor = org.opendc.compute.core.Flavor(1, 0) val vmDriver = metalDriver.refresh().server!!.services[VirtDriver] vmDriver.events.onEach { println(it) }.launchIn(this) val vmA = vmDriver.spawn("a", workloadA, flavor) @@ -140,7 +140,7 @@ internal class HypervisorTest { delay(5) - val flavor = Flavor(2, 0) + val flavor = org.opendc.compute.core.Flavor(2, 0) val vmDriver = metalDriver.refresh().server!!.services[VirtDriver] vmDriver.events .onEach { event -> -- cgit v1.2.3 From c8567a567348e13c341bf1a1ec64ed34ce25815a Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Sat, 3 Oct 2020 16:29:55 +0200 Subject: Implement VirtDriver using opendc-simulator-compute module This change adds an implementation of the VirtDriver interface that uses the functionality provided by the opendc-simulator-compute module. --- .../core/execution/ComputeSimExecutionContext.kt | 36 ++ .../compute/metal/driver/SimBareMetalDriver.kt | 22 +- .../opendc/compute/virt/driver/SimVirtDriver.kt | 182 +++++++ .../compute/virt/driver/SimVirtDriverWorkload.kt | 38 ++ .../org/opendc/compute/virt/HypervisorTest.kt | 6 +- .../org/opendc/compute/virt/SimHypervisorTest.kt | 147 ++++++ .../simulator/compute/SimBareMetalMachine.kt | 281 +++++++++++ .../org/opendc/simulator/compute/SimHypervisor.kt | 529 +++++++++++++++++++++ .../org/opendc/simulator/compute/SimMachine.kt | 236 +-------- .../simulator/compute/workload/SimWorkload.kt | 3 + .../opendc/simulator/compute/SimHypervisorTest.kt | 130 +++++ .../org/opendc/simulator/compute/SimMachineTest.kt | 6 +- 12 files changed, 1378 insertions(+), 238 deletions(-) create mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ComputeSimExecutionContext.kt create mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt create mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriverWorkload.kt create mode 100644 simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/SimHypervisorTest.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimHypervisorTest.kt diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ComputeSimExecutionContext.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ComputeSimExecutionContext.kt new file mode 100644 index 00000000..3295a8e8 --- /dev/null +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ComputeSimExecutionContext.kt @@ -0,0 +1,36 @@ +/* + * 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.core.execution + +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/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt index e9346a6c..4f440342 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt @@ -28,6 +28,7 @@ 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.execution.ComputeSimExecutionContext import org.opendc.compute.core.execution.ShutdownException import org.opendc.compute.core.image.EmptyImage import org.opendc.compute.core.image.Image @@ -38,8 +39,10 @@ import org.opendc.compute.metal.NodeState import org.opendc.compute.metal.power.ConstantPowerModel import org.opendc.core.power.PowerModel import org.opendc.core.services.ServiceRegistry -import org.opendc.simulator.compute.SimMachine +import org.opendc.simulator.compute.SimBareMetalMachine +import org.opendc.simulator.compute.SimExecutionContext import org.opendc.simulator.compute.SimMachineModel +import org.opendc.simulator.compute.workload.SimWorkload import org.opendc.utils.flow.EventFlow import org.opendc.utils.flow.StateFlow import java.time.Clock @@ -99,9 +102,9 @@ public class SimBareMetalDriver( private val random = Random(uid.leastSignificantBits xor uid.mostSignificantBits) /** - * The [SimMachine] we use to run the workload. + * The [SimBareMetalMachine] we use to run the workload. */ - private val machine = SimMachine(coroutineScope, clock, machine) + private val machine = SimBareMetalMachine(coroutineScope, clock, machine) /** * The [Job] that runs the simulated workload. @@ -136,11 +139,22 @@ public class SimBareMetalDriver( events ) + // Wrap the workload to pass in a ComputeSimExecutionContext + val workload = object : SimWorkload { + override suspend fun run(ctx: SimExecutionContext) { + val wrappedCtx = object : ComputeSimExecutionContext, SimExecutionContext by ctx { + override val server: Server + get() = nodeState.value.server!! + } + (node.image as SimWorkloadImage).workload.run(wrappedCtx) + } + } + job = coroutineScope.launch { delay(1) // TODO Introduce boot time initMachine() try { - machine.run((node.image as SimWorkloadImage).workload) + machine.run(workload) exitMachine(null) } catch (_: CancellationException) { // Ignored diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt new file mode 100644 index 00000000..758315aa --- /dev/null +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt @@ -0,0 +1,182 @@ +/* + * 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.virt.driver + +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.execution.ComputeSimExecutionContext +import org.opendc.compute.core.execution.ShutdownException +import org.opendc.compute.core.image.Image +import org.opendc.compute.core.image.SimWorkloadImage +import org.opendc.compute.virt.HypervisorEvent +import org.opendc.core.services.ServiceRegistry +import org.opendc.simulator.compute.SimExecutionContext +import org.opendc.simulator.compute.SimHypervisor +import org.opendc.simulator.compute.SimMachine +import org.opendc.simulator.compute.workload.SimWorkload +import org.opendc.utils.flow.EventFlow +import java.time.Clock +import java.util.* + +/** + * A [VirtDriver] that is simulates virtual machines on a physical machine using [SimHypervisor]. + */ +public class SimVirtDriver( + private val coroutineScope: CoroutineScope, + clock: Clock, + private val ctx: SimExecutionContext +) : VirtDriver { + + /** + * The [EventFlow] to emit the events. + */ + internal val eventFlow = EventFlow() + + override val events: Flow = eventFlow + + /** + * Current total memory use of the images on this hypervisor. + */ + private var availableMemory: Long = ctx.machine.memory.map { it.size }.sum() + + /** + * The hypervisor to run multiple workloads. + */ + private val hypervisor = SimHypervisor( + coroutineScope, + clock, + object : SimHypervisor.Listener { + override fun onSliceFinish( + hypervisor: SimHypervisor, + requestedBurst: Long, + grantedBurst: Long, + overcommissionedBurst: Long, + interferedBurst: Long, + cpuUsage: Double, + cpuDemand: Double + ) { + eventFlow.emit( + HypervisorEvent.SliceFinished( + this@SimVirtDriver, + requestedBurst, + grantedBurst, + overcommissionedBurst, + interferedBurst, + cpuUsage, + cpuDemand, + vms.size, + (ctx as ComputeSimExecutionContext).server + ) + ) + } + } + ) + + /** + * The virtual machines running on the hypervisor. + */ + private val vms = HashSet() + + 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() + val server = Server( + UUID.randomUUID(), + name, + emptyMap(), + flavor, + image, + ServerState.BUILD, + ServiceRegistry(), + events + ) + availableMemory -= requiredMemory + vms.add(VirtualMachine(server, events, hypervisor.createMachine(ctx.machine))) + eventFlow.emit(HypervisorEvent.VmsUpdated(this, vms.size, availableMemory)) + return server + } + + /** + * A virtual machine instance that the driver manages. + */ + private inner class VirtualMachine(server: Server, val events: EventFlow, machine: SimMachine) { + val job = coroutineScope.launch { + val workload = object : SimWorkload { + override suspend fun run(ctx: SimExecutionContext) { + val wrappedCtx = object : ComputeSimExecutionContext, SimExecutionContext by ctx { + override val server: Server + get() = this@VirtualMachine.server + } + (server.image as SimWorkloadImage).workload.run(wrappedCtx) + } + } + + delay(1) // TODO Introduce boot time + init() + try { + machine.run(workload) + exit(null) + } catch (cause: Throwable) { + exit(cause) + } + } + + 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 || (cause is ShutdownException && cause.cause == null)) + ServerState.SHUTOFF + else + ServerState.ERROR + server = server.copy(state = serverState) + availableMemory += server.flavor.memorySize + vms.remove(this) + eventFlow.emit(HypervisorEvent.VmsUpdated(this@SimVirtDriver, vms.size, availableMemory)) + events.close() + } + } + + public suspend fun run() { + hypervisor.run(ctx) + } +} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriverWorkload.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriverWorkload.kt new file mode 100644 index 00000000..91d400a4 --- /dev/null +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriverWorkload.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.compute.virt.driver + +import kotlinx.coroutines.coroutineScope +import org.opendc.simulator.compute.SimExecutionContext +import org.opendc.simulator.compute.workload.SimWorkload + +public class SimVirtDriverWorkload : SimWorkload { + public lateinit var driver: SimVirtDriver + + override suspend fun run(ctx: SimExecutionContext) { + coroutineScope { + driver = SimVirtDriver(this, ctx.clock, ctx) + driver.run() + } + } +} diff --git a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/HypervisorTest.kt b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/HypervisorTest.kt index 369b9538..82a82044 100644 --- a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/HypervisorTest.kt +++ b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/HypervisorTest.kt @@ -29,7 +29,6 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestCoroutineScope import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll import org.opendc.compute.core.Flavor @@ -53,7 +52,6 @@ internal class HypervisorTest { */ @OptIn(ExperimentalCoroutinesApi::class) @Test - @Disabled fun smoke() { val testScope = TestCoroutineScope() val clock = DelayControllerClockAdapter(testScope) @@ -75,7 +73,7 @@ internal class HypervisorTest { delay(5) - val flavor = org.opendc.compute.core.Flavor(1, 0) + val flavor = Flavor(1, 0) val vmDriver = metalDriver.refresh().server!!.services[VirtDriver] vmDriver.events.onEach { println(it) }.launchIn(this) val vmA = vmDriver.spawn("a", workloadA, flavor) @@ -140,7 +138,7 @@ internal class HypervisorTest { delay(5) - val flavor = org.opendc.compute.core.Flavor(2, 0) + val flavor = Flavor(2, 0) val vmDriver = metalDriver.refresh().server!!.services[VirtDriver] vmDriver.events .onEach { event -> diff --git a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/SimHypervisorTest.kt b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/SimHypervisorTest.kt new file mode 100644 index 00000000..9d33d395 --- /dev/null +++ b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/SimHypervisorTest.kt @@ -0,0 +1,147 @@ +/* + * 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.virt + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.TestCoroutineScope +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.image.SimWorkloadImage +import org.opendc.compute.metal.driver.SimBareMetalDriver +import org.opendc.compute.virt.driver.SimVirtDriverWorkload +import org.opendc.simulator.compute.SimMachineModel +import org.opendc.simulator.compute.model.MemoryUnit +import org.opendc.simulator.compute.model.ProcessingNode +import org.opendc.simulator.compute.model.ProcessingUnit +import org.opendc.simulator.compute.workload.SimTraceWorkload +import org.opendc.simulator.utils.DelayControllerClockAdapter +import java.time.Clock +import java.util.UUID + +/** + * Basic test-suite for the hypervisor. + */ +@OptIn(ExperimentalCoroutinesApi::class) +internal class SimHypervisorTest { + private lateinit var scope: TestCoroutineScope + private lateinit var clock: Clock + private lateinit var machineModel: SimMachineModel + + @BeforeEach + fun setUp() { + scope = TestCoroutineScope() + clock = DelayControllerClockAdapter(scope) + + val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) + + machineModel = SimMachineModel( + cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + ) + } + + /** + * Test overcommissioning of a hypervisor. + */ + @Test + fun overcommission() { + var requestedBurst = 0L + var grantedBurst = 0L + var overcommissionedBurst = 0L + + scope.launch { + val virtDriverWorkload = SimVirtDriverWorkload() + val vmm = SimWorkloadImage(UUID.randomUUID(), "vmm", emptyMap(), virtDriverWorkload) + val duration = 5 * 60L + val vmImageA = SimWorkloadImage( + UUID.randomUUID(), + "", + emptyMap(), + SimTraceWorkload( + sequenceOf( + SimTraceWorkload.Fragment(0, 28L * duration, duration * 1000, 28.0, 2), + SimTraceWorkload.Fragment(0, 3500L * duration, duration * 1000, 3500.0, 2), + SimTraceWorkload.Fragment(0, 0, duration * 1000, 0.0, 2), + SimTraceWorkload.Fragment(0, 183L * duration, duration * 1000, 183.0, 2) + ), + ) + ) + val vmImageB = SimWorkloadImage( + UUID.randomUUID(), + "", + emptyMap(), + SimTraceWorkload( + sequenceOf( + SimTraceWorkload.Fragment(0, 28L * duration, duration * 1000, 28.0, 2), + SimTraceWorkload.Fragment(0, 3100L * duration, duration * 1000, 3100.0, 2), + SimTraceWorkload.Fragment(0, 0, duration * 1000, 0.0, 2), + SimTraceWorkload.Fragment(0, 73L * duration, duration * 1000, 73.0, 2) + ) + ), + ) + + val metalDriver = + SimBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), machineModel) + + metalDriver.init() + metalDriver.setImage(vmm) + metalDriver.start() + + delay(5) + + val flavor = Flavor(2, 0) + val vmDriver = virtDriverWorkload.driver + vmDriver.events + .onEach { event -> + when (event) { + is HypervisorEvent.SliceFinished -> { + requestedBurst += event.requestedBurst + grantedBurst += event.grantedBurst + overcommissionedBurst += event.overcommissionedBurst + } + } + } + .launchIn(this) + + vmDriver.spawn("a", vmImageA, flavor) + vmDriver.spawn("b", vmImageB, flavor) + } + + scope.advanceUntilIdle() + + assertAll( + { assertEquals(emptyList(), scope.uncaughtExceptions, "No errors") }, + { assertEquals(2073600, requestedBurst, "Requested Burst does not match") }, + { assertEquals(2013600, grantedBurst, "Granted Burst does not match") }, + { assertEquals(60000, overcommissionedBurst, "Overcommissioned Burst does not match") }, + { assertEquals(1200007, scope.currentTime) } + ) + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt new file mode 100644 index 00000000..c6d5bdd1 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt @@ -0,0 +1,281 @@ +/* + * 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.simulator.compute + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.intrinsics.startCoroutineCancellable +import kotlinx.coroutines.selects.SelectClause0 +import kotlinx.coroutines.selects.SelectInstance +import org.opendc.simulator.compute.workload.SimWorkload +import java.lang.Runnable +import java.time.Clock +import kotlin.coroutines.ContinuationInterceptor +import kotlin.math.ceil +import kotlin.math.max +import kotlin.math.min + +/** + * A simulated bare-metal machine that is able to run a single workload. + * + * A [SimBareMetalMachine] is a stateful object and you should be careful when operating this object concurrently. For + * example. the class expects only a single concurrent call to [run]. + * + * @param coroutineScope The [CoroutineScope] to run the simulated workload in. + * @param clock The virtual clock to track the simulation time. + * @param model The machine model to simulate. + */ +@OptIn(ExperimentalCoroutinesApi::class, InternalCoroutinesApi::class) +public class SimBareMetalMachine( + private val coroutineScope: CoroutineScope, + private val clock: Clock, + override val model: SimMachineModel +) : SimMachine { + /** + * A [StateFlow] representing the CPU usage of the simulated machine. + */ + override val usage: StateFlow + get() = usageState + + /** + * The current active workload. + */ + private var activeWorkload: SimWorkload? = null + + /** + * Run the specified [SimWorkload] on this machine and suspend execution util the workload has finished. + */ + override suspend fun run(workload: SimWorkload) { + require(activeWorkload == null) { "Run should not be called concurrently" } + + try { + activeWorkload = workload + workload.run(ctx) + } finally { + activeWorkload = null + } + } + + /** + * The execution context in which the workload runs. + */ + private val ctx = object : SimExecutionContext { + override val machine: SimMachineModel + get() = this@SimBareMetalMachine.model + + override val clock: Clock + get() = this@SimBareMetalMachine.clock + + override fun onRun( + batch: Sequence, + triggerMode: SimExecutionContext.TriggerMode, + merge: (SimExecutionContext.Slice, SimExecutionContext.Slice) -> SimExecutionContext.Slice + ): SelectClause0 { + return object : SelectClause0 { + @InternalCoroutinesApi + override fun registerSelectClause0(select: SelectInstance, block: suspend () -> R) { + // Do not reset the usage state: we will set it ourselves + usageFlush?.dispose() + usageFlush = null + + val queue = batch.iterator() + var start = Long.MIN_VALUE + var currentWork: SliceWork? = null + var currentDisposable: DisposableHandle? = null + + fun schedule(slice: SimExecutionContext.Slice) { + start = clock.millis() + + val isLastSlice = !queue.hasNext() + val work = SliceWork(slice) + val candidateDuration = when (triggerMode) { + SimExecutionContext.TriggerMode.FIRST -> work.minExit + SimExecutionContext.TriggerMode.LAST -> work.maxExit + SimExecutionContext.TriggerMode.DEADLINE -> slice.deadline - start + } + + // Check whether the deadline is exceeded during the run of the slice. + val duration = min(candidateDuration, slice.deadline - start) + + val action = Runnable { + currentWork = null + + // Flush all the work that was performed + val hasFinished = work.stop(duration) + + if (!isLastSlice) { + val candidateSlice = queue.next() + val nextSlice = + // If our previous slice exceeds its deadline, merge it with the next candidate slice + if (hasFinished) + candidateSlice + else + merge(candidateSlice, slice) + schedule(nextSlice) + } else if (select.trySelect()) { + block.startCoroutineCancellable(select.completion) + } + } + + // Schedule the flush after the entire slice has finished + currentDisposable = delay.invokeOnTimeout(duration, action) + + // Start the slice work + currentWork = work + work.start() + } + + // Schedule the first work + if (queue.hasNext()) { + schedule(queue.next()) + + // A DisposableHandle to flush the work in case the call is cancelled + val disposable = DisposableHandle { + val end = clock.millis() + val duration = end - start + + currentWork?.stop(duration) + currentDisposable?.dispose() + + // Schedule reset the usage of the machine since the call is returning + usageFlush = delay.invokeOnTimeout(1) { + usageState.value = 0.0 + usageFlush = null + } + } + + select.disposeOnSelect(disposable) + } else if (select.trySelect()) { + // No work has been given: select immediately + block.startCoroutineCancellable(select.completion) + } + } + } + } + } + + /** + * The [MutableStateFlow] containing the load of the server. + */ + private val usageState = MutableStateFlow(0.0) + + /** + * A disposable to prevent resetting the usage state for subsequent calls to onRun. + */ + private var usageFlush: DisposableHandle? = null + + /** + * Cache the [Delay] instance for timing. + * + * XXX We need to cache this before the call to [onRun] since doing this in [onRun] is too heavy. + * XXX Note however that this is an ugly hack which may break in the future. + */ + @OptIn(InternalCoroutinesApi::class) + private val delay = coroutineScope.coroutineContext[ContinuationInterceptor] as Delay + + /** + * A slice to be processed. + */ + private inner class SliceWork(val slice: SimExecutionContext.Slice) { + /** + * The duration after which the first processor finishes processing this slice. + */ + val minExit: Long + + /** + * The duration after which the last processor finishes processing this slice. + */ + val maxExit: Long + + /** + * A flag to indicate that the slice will exceed the deadline. + */ + val exceedsDeadline: Boolean + get() = slice.deadline < maxExit + + /** + * The total amount of CPU usage. + */ + val totalUsage: Double + + /** + * A flag to indicate that this slice is empty. + */ + val isEmpty: Boolean + + init { + var totalUsage = 0.0 + var minExit = Long.MAX_VALUE + var maxExit = 0L + var nonEmpty = false + + // Determine the duration of the first/last CPU to finish + for (i in 0 until min(model.cpus.size, slice.burst.size)) { + val cpu = model.cpus[i] + val usage = min(slice.limit[i], cpu.frequency) + val cpuDuration = ceil(slice.burst[i] / usage * 1000).toLong() // Convert from seconds to milliseconds + + totalUsage += usage / cpu.frequency + + if (cpuDuration != 0L) { // We only wait for processor cores with a non-zero burst + minExit = min(minExit, cpuDuration) + maxExit = max(maxExit, cpuDuration) + nonEmpty = true + } + } + + this.isEmpty = !nonEmpty + this.totalUsage = totalUsage + this.minExit = minExit + this.maxExit = maxExit + } + + /** + * Indicate that the work on the slice has started. + */ + fun start() { + usageState.value = totalUsage / model.cpus.size + } + + /** + * Flush the work performed on the slice. + */ + fun stop(duration: Long): Boolean { + var hasFinished = true + + for (i in 0 until min(model.cpus.size, slice.burst.size)) { + val usage = min(slice.limit[i], model.cpus[i].frequency) + val granted = ceil(duration / 1000.0 * usage).toLong() + val res = max(0, slice.burst[i] - granted) + slice.burst[i] = res + + if (res != 0L) { + hasFinished = false + } + } + + return hasFinished + } + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt new file mode 100644 index 00000000..7c2cfbe3 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt @@ -0,0 +1,529 @@ +/* + * 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.simulator.compute + +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.intrinsics.startCoroutineCancellable +import kotlinx.coroutines.selects.SelectClause0 +import kotlinx.coroutines.selects.SelectInstance +import kotlinx.coroutines.selects.select +import org.opendc.simulator.compute.model.ProcessingUnit +import org.opendc.simulator.compute.workload.SimWorkload +import java.time.Clock +import kotlin.math.ceil +import kotlin.math.max +import kotlin.math.min + +/** + * SimHypervisor distributes the computing requirements of multiple [SimWorkload] on a single [SimBareMetalMachine] concurrently. + * + * @param coroutineScope The [CoroutineScope] to run the simulated workloads in. + * @param clock The virtual clock to track the simulation time. + */ +@OptIn(ExperimentalCoroutinesApi::class, InternalCoroutinesApi::class) +public class SimHypervisor( + private val coroutineScope: CoroutineScope, + private val clock: Clock, + private val listener: Listener? = null +) : SimWorkload { + /** + * A set for tracking the VM context objects. + */ + private val vms: MutableSet = mutableSetOf() + + /** + * A flag to indicate the driver is stopped. + */ + private var stopped: Boolean = false + + /** + * The channel for scheduling new CPU requests. + */ + private val schedulingQueue = Channel(Channel.UNLIMITED) + + /** + * Create a [SimMachine] instance on which users may run a [SimWorkload]. + * + * @param model The machine to create. + */ + public fun createMachine(model: SimMachineModel): SimMachine { + val vmCtx = VmExecutionContext(model) + + return object : SimMachine { + override val model: SimMachineModel + get() = vmCtx.machine + + override val usage: StateFlow + get() = vmCtx.session.usage + + /** + * The current active workload. + */ + private var activeWorkload: SimWorkload? = null + + override suspend fun run(workload: SimWorkload) { + require(activeWorkload == null) { "Run should not be called concurrently" } + + try { + activeWorkload = workload + workload.run(vmCtx) + } finally { + activeWorkload = null + } + } + + override fun toString(): String = "SimVirtualMachine" + } + } + + /** + * Run the scheduling process of the hypervisor. + */ + override suspend fun run(ctx: SimExecutionContext) { + val maxUsage = ctx.machine.cpus.sumByDouble { it.frequency } + val pCPUs = ctx.machine.cpus.indices.sortedBy { ctx.machine.cpus[it].frequency } + + val vms = mutableSetOf() + val vcpus = mutableListOf() + + val usage = DoubleArray(ctx.machine.cpus.size) + val burst = LongArray(ctx.machine.cpus.size) + + fun process(command: SchedulerCommand) { + when (command) { + is SchedulerCommand.Schedule -> { + vms += command.vm + vcpus.addAll(command.vm.vcpus) + } + is SchedulerCommand.Deschedule -> { + vms -= command.vm + vcpus.removeAll(command.vm.vcpus) + } + is SchedulerCommand.Interrupt -> { + } + } + } + + fun processRemaining() { + var command = schedulingQueue.poll() + while (command != null) { + process(command) + command = schedulingQueue.poll() + } + } + + while (!stopped) { + // Wait for a request to be submitted if we have no work yet. + if (vcpus.isEmpty()) { + process(schedulingQueue.receive()) + } + + processRemaining() + + val start = clock.millis() + + var duration: Double = Double.POSITIVE_INFINITY + var deadline: Long = Long.MAX_VALUE + var availableUsage = maxUsage + var totalRequestedUsage = 0.0 + var totalRequestedBurst = 0L + + // Sort the vCPUs based on their requested usage + // Profiling shows that it is faster to sort every slice instead of maintaining some kind of sorted set + vcpus.sort() + + // Divide the available host capacity fairly across the vCPUs using max-min fair sharing + for ((i, req) in vcpus.withIndex()) { + val remaining = vcpus.size - i + val availableShare = availableUsage / remaining + val grantedUsage = min(req.limit, availableShare) + + // Take into account the minimum deadline of this slice before we possible continue + deadline = min(deadline, req.vm.deadline) + + // Ignore empty CPUs + if (grantedUsage <= 0 || req.burst <= 0) { + req.allocatedLimit = 0.0 + continue + } + + totalRequestedUsage += req.limit + totalRequestedBurst += req.burst + + req.allocatedLimit = grantedUsage + availableUsage -= grantedUsage + + // The duration that we want to run is that of the shortest request from a vCPU + duration = min(duration, req.burst / grantedUsage) + } + + val totalAllocatedUsage = maxUsage - availableUsage + var totalAllocatedBurst = 0L + availableUsage = totalAllocatedUsage + + // Divide the requests over the available capacity of the pCPUs fairly + for (i in pCPUs) { + val maxCpuUsage = ctx.machine.cpus[i].frequency + val fraction = maxCpuUsage / maxUsage + val grantedUsage = min(maxCpuUsage, totalAllocatedUsage * fraction) + val grantedBurst = ceil(duration * grantedUsage).toLong() + + usage[i] = grantedUsage + burst[i] = grantedBurst + totalAllocatedBurst += grantedBurst + availableUsage -= grantedUsage + } + + // We run the total burst on the host processor. Note that this call may be cancelled at any moment in + // time, so not all of the burst may be executed. + select { + schedulingQueue.onReceive { schedulingQueue.offer(it); true } + ctx.onRun(SimExecutionContext.Slice(burst, usage, deadline), SimExecutionContext.TriggerMode.DEADLINE) + .invoke { false } + } + + val end = clock.millis() + + // No work was performed + if ((end - start) <= 0) { + continue + } + + // The total requested burst that the VMs wanted to run in the time-frame that we ran. + val totalRequestedSubBurst = + vcpus.map { ceil((duration * 1000) / (it.vm.deadline - start) * it.burst).toLong() }.sum() + val totalRemainder = burst.sum() + val totalGrantedBurst = totalAllocatedBurst - totalRemainder + + // The burst that was lost due to overcommissioning of CPU resources + var totalOvercommissionedBurst = 0L + // The burst that was lost due to interference. + var totalInterferedBurst = 0L + + val vmIterator = vms.iterator() + while (vmIterator.hasNext()) { + val vm = vmIterator.next() + + // Apply performance interference model + val performanceScore = 1.0 // TODO Performance interference + var hasFinished = false + + for (vcpu in vm.vcpus) { + // Compute the fraction of compute time allocated to the VM + val fraction = vcpu.allocatedLimit / totalAllocatedUsage + + // Compute the burst time that the VM was actually granted + val grantedBurst = ceil(totalGrantedBurst * fraction).toLong() + + // The burst that was actually used by the VM + val usedBurst = ceil(grantedBurst * performanceScore).toLong() + + totalInterferedBurst += grantedBurst - usedBurst + + // Compute remaining burst time to be executed for the request + if (vcpu.consume(usedBurst)) { + hasFinished = true + } else if (vm.deadline <= end) { + // Request must have its entire burst consumed or otherwise we have overcommission + // Note that we count the overcommissioned burst if the hypervisor has failed. + totalOvercommissionedBurst += vcpu.burst + } + } + + if (hasFinished || vm.deadline <= end) { + // Mark the VM as finished and deschedule the VMs if needed + if (vm.finish()) { + vmIterator.remove() + vcpus.removeAll(vm.vcpus) + } + } + } + + listener?.onSliceFinish( + this, + totalRequestedBurst, + min(totalRequestedSubBurst, totalGrantedBurst), // We can run more than requested due to timing + totalOvercommissionedBurst, + totalInterferedBurst, // Might be smaller than zero due to FP rounding errors, + min( + totalAllocatedUsage, + totalRequestedUsage + ), // The allocated usage might be slightly higher due to FP rounding + totalRequestedUsage + ) + } + } + + /** + * Event listener for hypervisor events. + */ + public interface Listener { + /** + * This method is invoked when a slice is finished. + */ + public fun onSliceFinish( + hypervisor: SimHypervisor, + requestedBurst: Long, + grantedBurst: Long, + overcommissionedBurst: Long, + interferedBurst: Long, + cpuUsage: Double, + cpuDemand: Double + ) + } + + /** + * A scheduling command processed by the scheduler. + */ + private sealed class SchedulerCommand { + /** + * Schedule the specified VM on the hypervisor. + */ + data class Schedule(val vm: VmSession) : SchedulerCommand() + + /** + * De-schedule the specified VM on the hypervisor. + */ + data class Deschedule(val vm: VmSession) : SchedulerCommand() + + /** + * Interrupt the scheduler. + */ + object Interrupt : SchedulerCommand() + } + + /** + * A virtual machine running on the hypervisor. + * + * @param ctx The execution context the vCPU runs in. + * @param triggerMode The mode when to trigger the VM exit. + * @param merge The function to merge consecutive slices on spillover. + * @param select The function to select on finish. + */ + @OptIn(InternalCoroutinesApi::class) + private data class VmSession( + val model: SimMachineModel, + var triggerMode: SimExecutionContext.TriggerMode = SimExecutionContext.TriggerMode.FIRST, + var merge: (SimExecutionContext.Slice, SimExecutionContext.Slice) -> SimExecutionContext.Slice = { _, r -> r }, + var select: () -> Unit = {} + ) { + /** + * The vCPUs of this virtual machine. + */ + val vcpus: List + + /** + * The slices that the VM wants to run. + */ + var queue: Iterator = emptyList().iterator() + + /** + * The current active slice. + */ + var activeSlice: SimExecutionContext.Slice? = null + + /** + * The current deadline of the VM. + */ + val deadline: Long + get() = activeSlice?.deadline ?: Long.MAX_VALUE + + /** + * A flag to indicate that the VM is idle. + */ + val isIdle: Boolean + get() = activeSlice == null + + /** + * The usage of the virtual machine. + */ + val usage: MutableStateFlow = MutableStateFlow(0.0) + + init { + vcpus = model.cpus.mapIndexed { i, model -> VCpu(this, model, i) } + } + + /** + * Schedule the given slices on this vCPU, replacing the existing slices. + */ + fun schedule(slices: Sequence) { + queue = slices.iterator() + + if (queue.hasNext()) { + activeSlice = queue.next() + refresh() + } + } + + /** + * Cancel the existing workload on the VM. + */ + fun cancel() { + queue = emptyList().iterator() + activeSlice = null + refresh() + } + + /** + * Finish the current slice of the VM. + * + * @return `true` if the vCPUs may be descheduled, `false` otherwise. + */ + fun finish(): Boolean { + val activeSlice = activeSlice ?: return true + + return if (queue.hasNext()) { + val needsMerge = activeSlice.burst.any { it > 0 } + val candidateSlice = queue.next() + val slice = if (needsMerge) merge(activeSlice, candidateSlice) else candidateSlice + + this.activeSlice = slice + + // Update the vCPU cache + refresh() + + false + } else { + this.activeSlice = null + select() + true + } + } + + /** + * Refresh the vCPU cache. + */ + fun refresh() { + vcpus.forEach { it.refresh() } + usage.value = vcpus.sumByDouble { it.burst / it.limit } / vcpus.size + } + } + + /** + * A virtual CPU that can be scheduled on a physical CPU. + * + * @param vm The VM of which this vCPU is part. + * @param model The model of CPU that this vCPU models. + * @param id The id of the vCPU with respect to the VM. + */ + private data class VCpu( + val vm: VmSession, + val model: ProcessingUnit, + val id: Int + ) : Comparable { + /** + * The current limit on the vCPU. + */ + var limit: Double = 0.0 + + /** + * The limit allocated by the hypervisor. + */ + var allocatedLimit: Double = 0.0 + + /** + * The current burst running on the vCPU. + */ + var burst: Long = 0L + + /** + * Consume the specified burst on this vCPU. + */ + fun consume(burst: Long): Boolean { + this.burst = max(0, this.burst - burst) + + // Flush the result to the slice if it exists + vm.activeSlice?.burst?.takeIf { id < it.size }?.set(id, this.burst) + + return allocatedLimit > 0.0 && this.burst == 0L + } + + /** + * Refresh the information of this vCPU based on the current slice. + */ + fun refresh() { + limit = vm.activeSlice?.limit?.takeIf { id < it.size }?.get(id) ?: 0.0 + burst = vm.activeSlice?.burst?.takeIf { id < it.size }?.get(id) ?: 0 + } + + /** + * Compare to another vCPU based on the current load of the vCPU. + */ + override fun compareTo(other: VCpu): Int { + return limit.compareTo(other.limit) + } + + /** + * Create a string representation of the vCPU. + */ + override fun toString(): String = + "vCPU(id=$id,burst=$burst,limit=$limit,allocatedLimit=$allocatedLimit)" + } + + /** + * The execution context in which a VM runs. + * + */ + private inner class VmExecutionContext(override val machine: SimMachineModel) : + SimExecutionContext, + DisposableHandle { + private var finalized: Boolean = false + private var initialized: Boolean = false + val session: VmSession = VmSession(machine) + + override val clock: Clock + get() = this@SimHypervisor.clock + + @OptIn(InternalCoroutinesApi::class) + override fun onRun( + batch: Sequence, + triggerMode: SimExecutionContext.TriggerMode, + merge: (SimExecutionContext.Slice, SimExecutionContext.Slice) -> SimExecutionContext.Slice + ): SelectClause0 = object : SelectClause0 { + @InternalCoroutinesApi + override fun registerSelectClause0(select: SelectInstance, block: suspend () -> R) { + session.triggerMode = triggerMode + session.merge = merge + session.select = { + if (select.trySelect()) { + block.startCoroutineCancellable(select.completion) + } + } + session.schedule(batch) + // Indicate to the hypervisor that the VM should be re-scheduled + schedulingQueue.offer(SchedulerCommand.Schedule(session)) + select.disposeOnSelect(this@VmExecutionContext) + } + } + + override fun dispose() { + if (!session.isIdle) { + session.cancel() + schedulingQueue.offer(SchedulerCommand.Deschedule(session)) + } + } + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt index df74a5f1..f66085af 100644 --- a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt @@ -22,245 +22,27 @@ package org.opendc.simulator.compute -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.intrinsics.startCoroutineCancellable -import kotlinx.coroutines.selects.SelectClause0 -import kotlinx.coroutines.selects.SelectInstance import org.opendc.simulator.compute.workload.SimWorkload -import java.lang.Runnable -import java.time.Clock -import kotlin.coroutines.ContinuationInterceptor -import kotlin.math.ceil -import kotlin.math.max -import kotlin.math.min /** - * A simulated bare-metal machine that is able to run a single workload. - * - * @param coroutineScope The [CoroutineScope] to run the simulated workload in. - * @param clock The virtual clock to track the simulation time. - * @param model The machine model to simulate. + * A generic machine that is able to run a [SimWorkload]. */ -@OptIn(ExperimentalCoroutinesApi::class, InternalCoroutinesApi::class) -public class SimMachine( - private val coroutineScope: CoroutineScope, - private val clock: Clock, +@OptIn(ExperimentalCoroutinesApi::class) +public interface SimMachine { + /** + * The model of the machine containing its specifications. + */ public val model: SimMachineModel -) { + /** * A [StateFlow] representing the CPU usage of the simulated machine. */ public val usage: StateFlow - get() = usageState /** * Run the specified [SimWorkload] on this machine and suspend execution util the workload has finished. */ - public suspend fun run(workload: SimWorkload) { - workload.run(ctx) - } - - /** - * The execution context in which the workload runs. - */ - private val ctx = object : SimExecutionContext { - override val machine: SimMachineModel - get() = this@SimMachine.model - - override val clock: Clock - get() = this@SimMachine.clock - - override fun onRun( - batch: Sequence, - triggerMode: SimExecutionContext.TriggerMode, - merge: (SimExecutionContext.Slice, SimExecutionContext.Slice) -> SimExecutionContext.Slice - ): SelectClause0 { - return object : SelectClause0 { - @InternalCoroutinesApi - override fun registerSelectClause0(select: SelectInstance, block: suspend () -> R) { - // Do not reset the usage state: we will set it ourselves - usageFlush?.dispose() - usageFlush = null - - val queue = batch.iterator() - var start = Long.MIN_VALUE - var currentWork: SliceWork? = null - var currentDisposable: DisposableHandle? = null - - fun schedule(slice: SimExecutionContext.Slice) { - start = clock.millis() - - val isLastSlice = !queue.hasNext() - val work = SliceWork(slice) - val candidateDuration = when (triggerMode) { - SimExecutionContext.TriggerMode.FIRST -> work.minExit - SimExecutionContext.TriggerMode.LAST -> work.maxExit - SimExecutionContext.TriggerMode.DEADLINE -> slice.deadline - start - } - - // Check whether the deadline is exceeded during the run of the slice. - val duration = min(candidateDuration, slice.deadline - start) - - val action = Runnable { - currentWork = null - - // Flush all the work that was performed - val hasFinished = work.stop(duration) - - if (!isLastSlice) { - val candidateSlice = queue.next() - val nextSlice = - // If our previous slice exceeds its deadline, merge it with the next candidate slice - if (hasFinished) - candidateSlice - else - merge(candidateSlice, slice) - schedule(nextSlice) - } else if (select.trySelect()) { - block.startCoroutineCancellable(select.completion) - } - } - - // Schedule the flush after the entire slice has finished - currentDisposable = delay.invokeOnTimeout(duration, action) - - // Start the slice work - currentWork = work - work.start() - } - - // Schedule the first work - if (queue.hasNext()) { - schedule(queue.next()) - - // A DisposableHandle to flush the work in case the call is cancelled - val disposable = DisposableHandle { - val end = clock.millis() - val duration = end - start - - currentWork?.stop(duration) - currentDisposable?.dispose() - - // Schedule reset the usage of the machine since the call is returning - usageFlush = delay.invokeOnTimeout(1) { - usageState.value = 0.0 - usageFlush = null - } - } - - select.disposeOnSelect(disposable) - } else if (select.trySelect()) { - // No work has been given: select immediately - block.startCoroutineCancellable(select.completion) - } - } - } - } - } - - /** - * The [MutableStateFlow] containing the load of the server. - */ - private val usageState = MutableStateFlow(0.0) - - /** - * A disposable to prevent resetting the usage state for subsequent calls to onRun. - */ - private var usageFlush: DisposableHandle? = null - - /** - * Cache the [Delay] instance for timing. - * - * XXX We need to cache this before the call to [onRun] since doing this in [onRun] is too heavy. - * XXX Note however that this is an ugly hack which may break in the future. - */ - @OptIn(InternalCoroutinesApi::class) - private val delay = coroutineScope.coroutineContext[ContinuationInterceptor] as Delay - - /** - * A slice to be processed. - */ - private inner class SliceWork(val slice: SimExecutionContext.Slice) { - /** - * The duration after which the first processor finishes processing this slice. - */ - val minExit: Long - - /** - * The duration after which the last processor finishes processing this slice. - */ - val maxExit: Long - - /** - * A flag to indicate that the slice will exceed the deadline. - */ - val exceedsDeadline: Boolean - get() = slice.deadline < maxExit - - /** - * The total amount of CPU usage. - */ - val totalUsage: Double - - /** - * A flag to indicate that this slice is empty. - */ - val isEmpty: Boolean - - init { - var totalUsage = 0.0 - var minExit = Long.MAX_VALUE - var maxExit = 0L - var nonEmpty = false - - // Determine the duration of the first/last CPU to finish - for (i in 0 until min(model.cpus.size, slice.burst.size)) { - val cpu = model.cpus[i] - val usage = min(slice.limit[i], cpu.frequency) - val cpuDuration = ceil(slice.burst[i] / usage * 1000).toLong() // Convert from seconds to milliseconds - - totalUsage += usage / cpu.frequency - - if (cpuDuration != 0L) { // We only wait for processor cores with a non-zero burst - minExit = min(minExit, cpuDuration) - maxExit = max(maxExit, cpuDuration) - nonEmpty = true - } - } - - this.isEmpty = !nonEmpty - this.totalUsage = totalUsage - this.minExit = minExit - this.maxExit = maxExit - } - - /** - * Indicate that the work on the slice has started. - */ - fun start() { - usageState.value = totalUsage / model.cpus.size - } - - /** - * Flush the work performed on the slice. - */ - fun stop(duration: Long): Boolean { - var hasFinished = true - - for (i in 0 until min(model.cpus.size, slice.burst.size)) { - val usage = min(slice.limit[i], model.cpus[i].frequency) - val granted = ceil(duration / 1000.0 * usage).toLong() - val res = max(0, slice.burst[i] - granted) - slice.burst[i] = res - - if (res != 0L) { - hasFinished = false - } - } - - return hasFinished - } - } + public suspend fun run(workload: SimWorkload) } diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt index 0ef4130e..2add8cce 100644 --- a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt @@ -26,6 +26,9 @@ import org.opendc.simulator.compute.SimExecutionContext /** * A model that characterizes the runtime behavior of some particular workload. + * + * Workloads are stateful objects that may be paused and resumed at a later moment. As such, be careful when using the + * same [SimWorkload] from multiple contexts as only a single concurrent [run] call is expected. */ public interface SimWorkload { /** diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimHypervisorTest.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimHypervisorTest.kt new file mode 100644 index 00000000..b9cd1b06 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimHypervisorTest.kt @@ -0,0 +1,130 @@ +/* + * 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.simulator.compute + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.TestCoroutineScope +import kotlinx.coroutines.yield +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertAll +import org.opendc.simulator.compute.model.MemoryUnit +import org.opendc.simulator.compute.model.ProcessingNode +import org.opendc.simulator.compute.model.ProcessingUnit +import org.opendc.simulator.compute.workload.SimTraceWorkload +import org.opendc.simulator.utils.DelayControllerClockAdapter +import java.time.Clock +import java.util.* + +/** + * Test suite for the [SimHypervisor] class. + */ +@OptIn(ExperimentalCoroutinesApi::class) +internal class SimHypervisorTest { + private lateinit var scope: TestCoroutineScope + private lateinit var clock: Clock + private lateinit var machineModel: SimMachineModel + + @BeforeEach + fun setUp() { + scope = TestCoroutineScope() + clock = DelayControllerClockAdapter(scope) + + val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) + machineModel = SimMachineModel( + cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + ) + } + + /** + * Test overcommissioning of a hypervisor. + */ + @Test + fun overcommission() { + val listener = object : SimHypervisor.Listener { + var totalRequestedBurst = 0L + var totalGrantedBurst = 0L + var totalOvercommissionedBurst = 0L + + override fun onSliceFinish( + hypervisor: SimHypervisor, + requestedBurst: Long, + grantedBurst: Long, + overcommissionedBurst: Long, + interferedBurst: Long, + cpuUsage: Double, + cpuDemand: Double + ) { + totalRequestedBurst += requestedBurst + totalGrantedBurst += grantedBurst + totalOvercommissionedBurst += overcommissionedBurst + } + } + + scope.launch { + val duration = 5 * 60L + val workloadA = + SimTraceWorkload( + sequenceOf( + SimTraceWorkload.Fragment(0, 28L * duration, duration * 1000, 28.0, 2), + SimTraceWorkload.Fragment(0, 3500L * duration, duration * 1000, 3500.0, 2), + SimTraceWorkload.Fragment(0, 0, duration * 1000, 0.0, 2), + SimTraceWorkload.Fragment(0, 183L * duration, duration * 1000, 183.0, 2) + ), + ) + val workloadB = + SimTraceWorkload( + sequenceOf( + SimTraceWorkload.Fragment(0, 28L * duration, duration * 1000, 28.0, 2), + SimTraceWorkload.Fragment(0, 3100L * duration, duration * 1000, 3100.0, 2), + SimTraceWorkload.Fragment(0, 0, duration * 1000, 0.0, 2), + SimTraceWorkload.Fragment(0, 73L * duration, duration * 1000, 73.0, 2) + ) + ) + + val machine = SimBareMetalMachine(scope, clock, machineModel) + val hypervisor = SimHypervisor(scope, clock, listener) + + launch { + machine.run(hypervisor) + } + + yield() + launch { hypervisor.createMachine(machineModel).run(workloadA) } + launch { hypervisor.createMachine(machineModel).run(workloadB) } + } + + scope.advanceUntilIdle() + + assertAll( + { Assertions.assertEquals(emptyList(), scope.uncaughtExceptions, "No errors") }, + { Assertions.assertEquals(2073600, listener.totalRequestedBurst, "Requested Burst does not match") }, + { Assertions.assertEquals(2013600, listener.totalGrantedBurst, "Granted Burst does not match") }, + { Assertions.assertEquals(60000, listener.totalOvercommissionedBurst, "Overcommissioned Burst does not match") }, + { Assertions.assertEquals(1200001, scope.currentTime) } + ) + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt index f6fb8e04..332ca8e9 100644 --- a/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt @@ -35,7 +35,7 @@ import org.opendc.simulator.compute.workload.SimFlopsWorkload import org.opendc.simulator.utils.DelayControllerClockAdapter /** - * Test suite for the [SimMachine] class. + * Test suite for the [SimBareMetalMachine] class. */ @OptIn(ExperimentalCoroutinesApi::class) class SimMachineTest { @@ -55,7 +55,7 @@ class SimMachineTest { fun testFlopsWorkload() { val testScope = TestCoroutineScope() val clock = DelayControllerClockAdapter(testScope) - val machine = SimMachine(testScope, clock, machineModel) + val machine = SimBareMetalMachine(testScope, clock, machineModel) testScope.runBlockingTest { machine.run(SimFlopsWorkload(2_000, 2, utilization = 1.0)) @@ -69,7 +69,7 @@ class SimMachineTest { fun testUsage() { val testScope = TestCoroutineScope() val clock = DelayControllerClockAdapter(testScope) - val machine = SimMachine(testScope, clock, machineModel) + val machine = SimBareMetalMachine(testScope, clock, machineModel) testScope.runBlockingTest { machine.run(SimFlopsWorkload(2_000, 2, utilization = 1.0)) -- cgit v1.2.3 From 136c1b9ddc7bd9331d3552d681e9190fc6198271 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Sat, 3 Oct 2020 17:41:59 +0200 Subject: Migrate codebase to opendc-simulator-compute This change updates the remainder of the codebase to use the opendc-simulator-compute module for the simulation of workloads. --- .../kotlin/org/opendc/compute/core/MemoryUnit.kt | 40 -- .../org/opendc/compute/core/ProcessingNode.kt | 40 -- .../org/opendc/compute/core/ProcessingUnit.kt | 38 -- .../kotlin/org/opendc/compute/core/ServerEvent.kt | 10 - .../opendc/compute/core/execution/ServerContext.kt | 167 ------ .../core/execution/ServerManagementContext.kt | 40 -- .../compute/core/execution/ShutdownException.kt | 52 -- .../org/opendc/compute/core/image/EmptyImage.kt | 3 - .../compute/core/image/FlopsApplicationImage.kt | 65 --- .../compute/core/image/FlopsHistoryFragment.kt | 3 - .../kotlin/org/opendc/compute/core/image/Image.kt | 11 +- .../opendc/compute/core/image/SimWorkloadImage.kt | 5 +- .../org/opendc/compute/core/image/VmImage.kt | 54 -- .../core/workload/PerformanceInterferenceModel.kt | 48 +- .../org/opendc/compute/core/workload/VmWorkload.kt | 4 +- .../compute/metal/driver/SimBareMetalDriver.kt | 15 +- .../compute/metal/driver/SimpleBareMetalDriver.kt | 476 --------------- .../org/opendc/compute/virt/HypervisorImage.kt | 55 -- .../opendc/compute/virt/driver/SimVirtDriver.kt | 9 +- .../opendc/compute/virt/driver/SimpleVirtDriver.kt | 640 --------------------- .../virt/service/SimpleVirtProvisioningService.kt | 90 +-- .../allocation/ComparableAllocationPolicyLogic.kt | 3 +- .../service/allocation/RandomAllocationPolicy.kt | 3 +- .../core/image/FlopsApplicationImageTest.kt | 76 --- .../metal/driver/SimpleBareMetalDriverTest.kt | 81 --- .../org/opendc/compute/virt/HypervisorTest.kt | 167 ------ .../opendc-experiments-sc18/build.gradle.kts | 5 +- .../opendc-experiments-sc20/build.gradle.kts | 1 + .../sc20/experiment/ExperimentHelpers.kt | 11 +- .../sc20/trace/Sc20ParquetTraceReader.kt | 10 +- .../sc20/trace/Sc20RawParquetTraceReader.kt | 22 +- .../sc20/trace/Sc20StreamingParquetTraceReader.kt | 36 +- .../experiments/sc20/trace/WorkloadSampler.kt | 8 +- .../src/main/resources/log4j2.xml | 2 +- .../opendc/experiments/sc20/Sc20IntegrationTest.kt | 14 +- simulator/opendc-format/build.gradle.kts | 3 +- .../environment/sc18/Sc18EnvironmentReader.kt | 14 +- .../sc20/Sc20ClusterEnvironmentReader.kt | 23 +- .../environment/sc20/Sc20EnvironmentReader.kt | 14 +- .../format/trace/bitbrains/BitbrainsTraceReader.kt | 28 +- .../org/opendc/format/trace/gwf/GwfTraceReader.kt | 5 +- .../opendc/format/trace/sc20/Sc20TraceReader.kt | 24 +- .../org/opendc/format/trace/swf/SwfTraceReader.kt | 23 +- .../org/opendc/format/trace/wtf/WtfTraceReader.kt | 5 +- .../opendc/format/trace/swf/SwfTraceReaderTest.kt | 10 +- simulator/opendc-runner-web/build.gradle.kts | 1 + .../kotlin/org/opendc/runner/web/TopologyParser.kt | 16 +- .../org/opendc/simulator/compute/SimHypervisor.kt | 14 +- .../simulator/compute/workload/SimTraceWorkload.kt | 2 +- simulator/opendc-workflows/build.gradle.kts | 4 + 50 files changed, 259 insertions(+), 2231 deletions(-) delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/MemoryUnit.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ProcessingNode.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ProcessingUnit.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ServerContext.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ServerManagementContext.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ShutdownException.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/FlopsApplicationImage.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/FlopsHistoryFragment.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/VmImage.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriver.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/HypervisorImage.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimpleVirtDriver.kt delete mode 100644 simulator/opendc-compute/src/test/kotlin/org/opendc/compute/core/image/FlopsApplicationImageTest.kt delete mode 100644 simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt delete mode 100644 simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/HypervisorTest.kt diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/MemoryUnit.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/MemoryUnit.kt deleted file mode 100644 index f41c41c4..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/MemoryUnit.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * MIT License - * - * 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.core - -/** - * A memory unit of a compute resource, either virtual or physical. - * - * @property vendor The vendor string of the memory. - * @property modelName The name of the memory model. - * @property speed The access speed of the memory in MHz. - * @property size The size of the memory unit in MBs. - */ -public data class MemoryUnit( - public val vendor: String, - public val modelName: String, - public val speed: Double, - public val size: Long -) diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ProcessingNode.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ProcessingNode.kt deleted file mode 100644 index 7e4694d4..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ProcessingNode.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * MIT License - * - * 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.core - -/** - * A processing node/package/socket containing possibly several CPU cores. - * - * @property vendor The vendor string of the processor node. - * @property modelName The name of the processor node. - * @property arch The micro-architecture of the processor node. - * @property coreCount The number of logical CPUs in the processor node. - */ -public data class ProcessingNode( - public val vendor: String, - public val arch: String, - public val modelName: String, - public val coreCount: Int -) diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ProcessingUnit.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ProcessingUnit.kt deleted file mode 100644 index e6ee7f9a..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ProcessingUnit.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * MIT License - * - * 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.core - -/** - * A single logical compute unit of processor node, either virtual or physical. - * - * @property node The processing node containing the CPU core. - * @property id The identifier of the CPU core within the processing node. - * @property frequency The clock rate of the CPU in MHz. - */ -public data class ProcessingUnit( - public val node: ProcessingNode, - public val id: Int, - public val frequency: Double -) diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ServerEvent.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ServerEvent.kt index fbef8a7d..e9212832 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ServerEvent.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ServerEvent.kt @@ -22,8 +22,6 @@ package org.opendc.compute.core -import org.opendc.core.services.ServiceKey - /** * An event that is emitted by a [Server]. */ @@ -40,12 +38,4 @@ public sealed class ServerEvent { * @property previousState The previous state of the server. */ public data class StateChanged(override val server: Server, val previousState: ServerState) : ServerEvent() - - /** - * This event is emitted when a server publishes a service. - * - * @property server The server that published the service. - * @property key The service key of the service that was published. - */ - public data class ServicePublished(override val server: Server, val key: ServiceKey<*>) : ServerEvent() } diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ServerContext.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ServerContext.kt deleted file mode 100644 index 82cec00a..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ServerContext.kt +++ /dev/null @@ -1,167 +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.core.execution - -import kotlinx.coroutines.selects.SelectClause0 -import kotlinx.coroutines.selects.select -import org.opendc.compute.core.ProcessingUnit -import org.opendc.compute.core.Server -import org.opendc.compute.core.image.Image -import org.opendc.core.services.ServiceKey -import java.time.Clock - -/** - * Represents the execution context in which a bootable [Image] runs on a [Server]. - */ -public interface ServerContext { - /** - * The virtual clock. - */ - public val clock: Clock - - /** - * The server on which the image runs. - */ - public val server: Server - - /** - * A list of processing units available to use. - */ - public val cpus: List - - /** - * Publish the specified [service] at the given [ServiceKey]. - */ - public suspend fun publishService(key: ServiceKey, service: T) - - /** - * Ask the processor cores to run the specified [slice] and suspend execution until the trigger condition is met as - * specified by [triggerMode]. - * - * After the method returns, [Slice.burst] will contain the remaining burst length for each of the cores (which - * may be zero). These changes may happen anytime during execution of this method and callers should not rely on - * the timing of this change. - * - * @param slice The representation of work to run on the processors. - * @param triggerMode The trigger condition to resume execution. - */ - public suspend fun run(slice: Slice, triggerMode: TriggerMode = TriggerMode.FIRST): Unit = - select { onRun(slice, triggerMode).invoke {} } - - /** - * Ask the processors cores to run the specified [batch] of work slices and suspend execution until the trigger - * condition is met as specified by [triggerMode]. - * - * After the method returns, [Slice.burst] will contain the remaining burst length for each of the cores (which - * may be zero). These changes may happen anytime during execution of this method and callers should not rely on - * the timing of this change. - * - * In case slices in the batch do not finish processing before their deadline, [merge] is called to merge these - * slices with the next slice to be executed. - * - * @param batch The batch of work to run on the processors. - * @param triggerMode The trigger condition to resume execution. - * @param merge The merge function for consecutive slices in case the last slice was not completed within its - * deadline. - */ - public suspend fun run( - batch: Sequence, - triggerMode: TriggerMode = TriggerMode.FIRST, - merge: (Slice, Slice) -> Slice = { _, r -> r } - ): Unit = select { onRun(batch, triggerMode, merge).invoke {} } - - /** - * Ask the processor cores to run the specified [slice] and select when the trigger condition is met as specified - * by [triggerMode]. - * - * After the method returns, [Slice.burst] will contain the remaining burst length for each of the cores (which - * may be zero). These changes may happen anytime during execution of this method and callers should not rely on - * the timing of this change. - * - * @param slice The representation of work to request from the processors. - * @param triggerMode The trigger condition to resume execution. - */ - public fun onRun(slice: Slice, triggerMode: TriggerMode = TriggerMode.FIRST): SelectClause0 = - onRun(sequenceOf(slice), triggerMode) - - /** - * Ask the processors cores to run the specified [batch] of work slices and select when the trigger condition is met - * as specified by [triggerMode]. - * - * After the method returns, [Slice.burst] will contain the remaining burst length for each of the cores (which - * may be zero). These changes may happen anytime during execution of this method and callers should not rely on - * the timing of this change. - * - * In case slices in the batch do not finish processing before their deadline, [merge] is called to merge these - * slices with the next slice to be executed. - * - * @param batch The batch of work to run on the processors. - * @param triggerMode The trigger condition to resume execution during the **last** slice. - * @param merge The merge function for consecutive slices in case the last slice was not completed within its - * deadline. - */ - public fun onRun( - batch: Sequence, - triggerMode: TriggerMode = TriggerMode.FIRST, - merge: (Slice, Slice) -> Slice = { _, r -> r } - ): SelectClause0 - - /** - * A request to the host machine for a slice of CPU time from the processor cores. - * - * Both [burst] and [limit] must be of the same size and in any other case the method will throw an - * [IllegalArgumentException]. - * - * - * @param burst The burst time to request from each of the processor cores. - * @param limit The maximum usage in terms of MHz that the processing core may use while running the burst. - * @param deadline The instant at which this slice needs to be fulfilled. - */ - public class Slice(public val burst: LongArray, public val limit: DoubleArray, public val deadline: Long) { - init { - require(burst.size == limit.size) { "Incompatible array dimensions" } - } - } - - /** - * The modes for triggering a machine exit from the machine. - */ - public enum class TriggerMode { - /** - * A machine exit occurs when either the first processor finishes processing a **non-zero** burst or the - * deadline is reached. - */ - FIRST, - - /** - * A machine exit occurs when either the last processor finishes processing a **non-zero** burst or the deadline - * is reached. - */ - LAST, - - /** - * A machine exit occurs only when the deadline is reached. - */ - DEADLINE - } -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ServerManagementContext.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ServerManagementContext.kt deleted file mode 100644 index 51727e43..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ServerManagementContext.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * MIT License - * - * 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.core.execution - -/** - * An extended [ServerContext] providing several methods for managing the execution context. - */ -public interface ServerManagementContext : ServerContext { - /** - * Initialize the management context. - */ - public suspend fun init() - - /** - * Terminate the execution of the server. - */ - public suspend fun exit(cause: Throwable? = null) -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ShutdownException.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ShutdownException.kt deleted file mode 100644 index d751fb5e..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ShutdownException.kt +++ /dev/null @@ -1,52 +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.core.execution - -import kotlinx.coroutines.CancellationException - -/** - * This exception is thrown by the underlying [ServerContext] to indicate that a shutdown flow - * has been sent to the server. - */ -public class ShutdownException(message: String? = null, override val cause: Throwable? = null) : - CancellationException(message) - -/** - * This method terminates the current active coroutine if the specified [CancellationException] is caused - * by a shutdown. - */ -public fun CancellationException.assertShutdown() { - if (this is ShutdownException) { - throw this - } -} - -/** - * This method terminates the current active coroutine if the specified [CancellationException] is caused - * by a failure. - */ -public fun CancellationException.assertFailure() { - if (this is ShutdownException && cause != null) { - throw this - } -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/EmptyImage.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/EmptyImage.kt index 2b9158bf..01f86a1b 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/EmptyImage.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/EmptyImage.kt @@ -22,7 +22,6 @@ package org.opendc.compute.core.image -import org.opendc.compute.core.execution.ServerContext import org.opendc.core.resource.TagContainer import java.util.UUID @@ -33,6 +32,4 @@ public object EmptyImage : Image { override val uid: UUID = UUID.randomUUID() override val name: String = "empty" override val tags: TagContainer = emptyMap() - - override suspend fun invoke(ctx: ServerContext) {} } diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/FlopsApplicationImage.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/FlopsApplicationImage.kt deleted file mode 100644 index 58c00e13..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/FlopsApplicationImage.kt +++ /dev/null @@ -1,65 +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.core.image - -import org.opendc.compute.core.execution.ServerContext -import org.opendc.core.resource.TagContainer -import java.util.UUID -import kotlin.math.min - -/** - * An application [Image] that models applications performing a static number of floating point operations ([flops]) on - * a compute resource. - * - * @property uid The unique identifier of this image. - * @property name The name of this image. - * @property tags The tags attached to the image. - * @property flops The number of floating point operations to perform for this task in MFLOPs. - * @property cores The number of cores that the image is able to utilize. - * @property utilization A model of the CPU utilization of the application. - */ -public data class FlopsApplicationImage( - public override val uid: UUID, - public override val name: String, - public override val tags: TagContainer, - public val flops: Long, - public val cores: Int, - public val utilization: Double = 0.8 -) : Image { - init { - require(flops >= 0) { "Negative number of flops" } - require(cores > 0) { "Negative number of cores or no cores" } - require(utilization > 0.0 && utilization <= 1.0) { "Utilization must be in (0, 1]" } - } - - /** - * Execute the runtime behavior based on a number of floating point operations to execute. - */ - override suspend fun invoke(ctx: ServerContext) { - val cores = min(this.cores, ctx.server.flavor.cpuCount) - val burst = LongArray(cores) { flops / cores } - val maxUsage = DoubleArray(cores) { i -> ctx.cpus[i].frequency * utilization } - - ctx.run(ServerContext.Slice(burst, maxUsage, Long.MAX_VALUE), triggerMode = ServerContext.TriggerMode.LAST) - } -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/FlopsHistoryFragment.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/FlopsHistoryFragment.kt deleted file mode 100644 index 5472e4e0..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/FlopsHistoryFragment.kt +++ /dev/null @@ -1,3 +0,0 @@ -package org.opendc.compute.core.image - -public data class FlopsHistoryFragment(val tick: Long, val flops: Long, val duration: Long, val usage: Double, val cores: Int) diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/Image.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/Image.kt index d04920c3..e481fcc3 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/Image.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/Image.kt @@ -22,7 +22,6 @@ package org.opendc.compute.core.image -import org.opendc.compute.core.execution.ServerContext import org.opendc.core.resource.Resource /** @@ -33,12 +32,4 @@ import org.opendc.core.resource.Resource * useful for backup purposes or for producing “gold” server images if you plan to deploy a particular server * configuration frequently. */ -public interface Image : Resource { - /** - * Launch the machine image in the specified [ServerContext]. - * - * This method should encapsulate and characterize the runtime behavior of the instance resulting from launching - * the image on some machine, in terms of the resource consumption on the machine. - */ - public suspend operator fun invoke(ctx: ServerContext) -} +public interface Image : Resource diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/SimWorkloadImage.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/SimWorkloadImage.kt index 7caedf6d..1dd9a34b 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/SimWorkloadImage.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/SimWorkloadImage.kt @@ -22,7 +22,6 @@ package org.opendc.compute.core.image -import org.opendc.compute.core.execution.ServerContext import org.opendc.core.resource.TagContainer import org.opendc.simulator.compute.workload.SimWorkload import java.util.* @@ -40,6 +39,4 @@ public data class SimWorkloadImage( public override val name: String, public override val tags: TagContainer, public val workload: SimWorkload -) : Image { - override suspend fun invoke(ctx: ServerContext): Unit = TODO() -} +) : Image diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/VmImage.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/VmImage.kt deleted file mode 100644 index 72efbe0b..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/VmImage.kt +++ /dev/null @@ -1,54 +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.core.image - -import org.opendc.compute.core.execution.ServerContext -import org.opendc.core.resource.TagContainer -import java.util.* -import kotlin.math.min - -public class VmImage( - public override val uid: UUID, - public override val name: String, - public override val tags: TagContainer, - public val flopsHistory: Sequence, - public val maxCores: Int, - public val requiredMemory: Long -) : Image { - - override suspend fun invoke(ctx: ServerContext) { - var offset = ctx.clock.millis() - - val batch = flopsHistory.map { fragment -> - val cores = min(fragment.cores, ctx.server.flavor.cpuCount) - val burst = LongArray(cores) { fragment.flops / cores } - val usage = DoubleArray(cores) { fragment.usage / cores } - offset += fragment.duration - ServerContext.Slice(burst, usage, offset) - } - - ctx.run(batch) - } - - override fun toString(): String = "VmImage(uid=$uid, name=$name, cores=$maxCores, requiredMemory=$requiredMemory)" -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/workload/PerformanceInterferenceModel.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/workload/PerformanceInterferenceModel.kt index 7956c095..51f62197 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/workload/PerformanceInterferenceModel.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/workload/PerformanceInterferenceModel.kt @@ -22,7 +22,6 @@ package org.opendc.compute.core.workload -import org.opendc.compute.core.Server import java.util.* import kotlin.random.Random @@ -43,16 +42,40 @@ public class PerformanceInterferenceModel( private var intersectingItems: List = emptyList() private val colocatedWorkloads = TreeMap() - internal fun vmStarted(server: Server) { - colocatedWorkloads.merge(server.image.name, 1, Int::plus) + /** + * Indicate that a VM has started. + */ + public fun onStart(name: String) { + colocatedWorkloads.merge(name, 1, Int::plus) intersectingItems = items.filter { item -> doesMatch(item) } } - internal fun vmStopped(server: Server) { - colocatedWorkloads.computeIfPresent(server.image.name) { _, v -> (v - 1).takeUnless { it == 0 } } + /** + * Indicate that a VM has stopped. + */ + public fun onStop(name: String) { + colocatedWorkloads.computeIfPresent(name) { _, v -> (v - 1).takeUnless { it == 0 } } intersectingItems = items.filter { item -> doesMatch(item) } } + /** + * Compute the performance interference based on the current server load. + */ + public fun apply(currentServerLoad: Double): Double { + if (intersectingItems.isEmpty()) { + return 1.0 + } + val score = intersectingItems + .firstOrNull { it.minServerLoad <= currentServerLoad } + + // Apply performance penalty to (on average) only one of the VMs + return if (score != null && random.nextInt(score.workloadNames.size) == 0) { + score.performanceScore + } else { + 1.0 + } + } + private fun doesMatch(item: PerformanceInterferenceModelItem): Boolean { var count = 0 for ( @@ -67,21 +90,6 @@ public class PerformanceInterferenceModel( } return false } - - internal fun apply(currentServerLoad: Double): Double { - if (intersectingItems.isEmpty()) { - return 1.0 - } - val score = intersectingItems - .firstOrNull { it.minServerLoad <= currentServerLoad } - - // Apply performance penalty to (on average) only one of the VMs - return if (score != null && random.nextInt(score.workloadNames.size) == 0) { - score.performanceScore - } else { - 1.0 - } - } } /** diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/workload/VmWorkload.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/workload/VmWorkload.kt index 6ab8da2a..6c724277 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/workload/VmWorkload.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/workload/VmWorkload.kt @@ -22,7 +22,7 @@ package org.opendc.compute.core.workload -import org.opendc.compute.core.image.VmImage +import org.opendc.compute.core.image.Image import org.opendc.core.User import org.opendc.core.workload.Workload import java.util.UUID @@ -39,7 +39,7 @@ public data class VmWorkload( override val uid: UUID, override val name: String, override val owner: User, - val image: VmImage + val image: Image ) : Workload { override fun equals(other: Any?): Boolean = other is VmWorkload && uid == other.uid diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt index 4f440342..4f72d599 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt @@ -29,7 +29,6 @@ import org.opendc.compute.core.Server import org.opendc.compute.core.ServerEvent import org.opendc.compute.core.ServerState import org.opendc.compute.core.execution.ComputeSimExecutionContext -import org.opendc.compute.core.execution.ShutdownException import org.opendc.compute.core.image.EmptyImage import org.opendc.compute.core.image.Image import org.opendc.compute.core.image.SimWorkloadImage @@ -89,6 +88,11 @@ public class SimBareMetalDriver( private val nodeState = StateFlow(Node(uid, name, metadata + ("driver" to this), NodeState.SHUTOFF, EmptyImage, null, events)) + /** + * The [SimBareMetalMachine] we use to run the workload. + */ + private val machine = SimBareMetalMachine(coroutineScope, clock, machine) + override val node: Flow = nodeState override val usage: Flow @@ -101,11 +105,6 @@ public class SimBareMetalDriver( */ private val random = Random(uid.leastSignificantBits xor uid.mostSignificantBits) - /** - * The [SimBareMetalMachine] we use to run the workload. - */ - private val machine = SimBareMetalMachine(coroutineScope, clock, machine) - /** * The [Job] that runs the simulated workload. */ @@ -174,12 +173,12 @@ public class SimBareMetalDriver( private fun exitMachine(cause: Throwable?) { val newServerState = - if (cause == null || (cause is ShutdownException && cause.cause == null)) + if (cause == null) ServerState.SHUTOFF else ServerState.ERROR val newNodeState = - if (cause == null || (cause is ShutdownException && cause.cause != null)) + if (cause == null) nodeState.value.state else NodeState.ERROR diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriver.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriver.kt deleted file mode 100644 index c4897197..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriver.kt +++ /dev/null @@ -1,476 +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.metal.driver - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Delay -import kotlinx.coroutines.DisposableHandle -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.InternalCoroutinesApi -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.intrinsics.startCoroutineCancellable -import kotlinx.coroutines.launch -import kotlinx.coroutines.selects.SelectClause0 -import kotlinx.coroutines.selects.SelectInstance -import org.opendc.compute.core.Flavor -import org.opendc.compute.core.MemoryUnit -import org.opendc.compute.core.ProcessingUnit -import org.opendc.compute.core.Server -import org.opendc.compute.core.ServerEvent -import org.opendc.compute.core.ServerState -import org.opendc.compute.core.execution.ServerContext -import org.opendc.compute.core.execution.ServerManagementContext -import org.opendc.compute.core.execution.ShutdownException -import org.opendc.compute.core.image.EmptyImage -import org.opendc.compute.core.image.Image -import org.opendc.compute.metal.Node -import org.opendc.compute.metal.NodeEvent -import org.opendc.compute.metal.NodeState -import org.opendc.compute.metal.power.ConstantPowerModel -import org.opendc.core.power.PowerModel -import org.opendc.core.services.ServiceKey -import org.opendc.core.services.ServiceRegistry -import org.opendc.utils.flow.EventFlow -import org.opendc.utils.flow.StateFlow -import java.lang.Exception -import java.time.Clock -import java.util.UUID -import kotlin.coroutines.ContinuationInterceptor -import kotlin.math.ceil -import kotlin.math.max -import kotlin.math.min -import kotlin.random.Random - -/** - * A basic implementation of the [BareMetalDriver] that simulates an [Image] running on a bare-metal machine. - * - * @param coroutineScope The [CoroutineScope] the driver runs in. - * @param clock The virtual clock to keep track of time. - * @param uid The unique identifier of the machine. - * @param name An optional name of the machine. - * @param metadata The initial metadata of the node. - * @param cpus The CPUs available to the bare metal machine. - * @param memoryUnits The memory units in this machine. - * @param powerModel The power model of this machine. - */ -@OptIn(ExperimentalCoroutinesApi::class) -public class SimpleBareMetalDriver( - private val coroutineScope: CoroutineScope, - private val clock: Clock, - uid: UUID, - name: String, - metadata: Map, - private val cpus: List, - private val memoryUnits: List, - powerModel: PowerModel = ConstantPowerModel( - 0.0 - ) -) : BareMetalDriver { - /** - * The flavor that corresponds to this machine. - */ - private val flavor = - Flavor(cpus.size, memoryUnits.map { it.size }.sum()) - - /** - * The current active server context. - */ - private var serverContext: BareMetalServerContext? = null - - /** - * The events of the machine. - */ - private val events = EventFlow() - - /** - * The flow containing the load of the server. - */ - private val usageState = MutableStateFlow(0.0) - - /** - * The machine state. - */ - private val nodeState = - StateFlow(Node(uid, name, metadata + ("driver" to this), NodeState.SHUTOFF, EmptyImage, null, events)) - - override val node: Flow = nodeState - - @OptIn(FlowPreview::class) - override val usage: Flow = usageState - - override val powerDraw: Flow = powerModel(this) - - /** - * The internal random instance. - */ - private val random = Random(uid.leastSignificantBits xor uid.mostSignificantBits) - - override suspend fun init(): Node { - return nodeState.value - } - - override suspend fun start(): Node { - val node = nodeState.value - if (node.state != NodeState.SHUTOFF) { - return node - } - - val events = EventFlow() - val server = Server( - UUID(random.nextLong(), random.nextLong()), - node.name, - emptyMap(), - flavor, - node.image, - ServerState.BUILD, - ServiceRegistry().put(BareMetalDriver, this@SimpleBareMetalDriver), - events - ) - - setNode(node.copy(state = NodeState.BOOT, server = server)) - serverContext = BareMetalServerContext(events) - return nodeState.value - } - - override suspend fun stop(): Node { - val node = nodeState.value - if (node.state == NodeState.SHUTOFF) { - return node - } - - // We terminate the image running on the machine - serverContext!!.cancel(fail = false) - serverContext = null - - setNode(node.copy(state = NodeState.SHUTOFF, server = null)) - return node - } - - override suspend fun reboot(): Node { - stop() - return start() - } - - override suspend fun setImage(image: Image): Node { - setNode(nodeState.value.copy(image = image)) - return nodeState.value - } - - override suspend fun refresh(): Node = nodeState.value - - private fun setNode(value: Node) { - val field = nodeState.value - if (field.state != value.state) { - events.emit(NodeEvent.StateChanged(value, field.state)) - } - - if (field.server != null && value.server != null && field.server.state != value.server.state) { - serverContext!!.events.emit(ServerEvent.StateChanged(value.server, field.server.state)) - } - - nodeState.value = value - } - - private inner class BareMetalServerContext(val events: EventFlow) : ServerManagementContext { - private var finalized: Boolean = false - - // A state in which the machine is still available, but does not run any of the work requested by the - // image - var unavailable = false - - override val cpus: List = this@SimpleBareMetalDriver.cpus - - override val server: Server - get() = nodeState.value.server!! - - override val clock: Clock - get() = this@SimpleBareMetalDriver.clock - - private val job = coroutineScope.launch { - delay(1) // TODO Introduce boot time - init() - try { - server.image(this@BareMetalServerContext) - exit() - } catch (cause: Throwable) { - exit(cause) - } - } - - /** - * Cancel the image running on the machine. - */ - suspend fun cancel(fail: Boolean) { - if (fail) - job.cancel(ShutdownException(cause = Exception("Random failure"))) - else - job.cancel(ShutdownException()) - job.join() - } - - override suspend fun publishService(key: ServiceKey, service: T) { - val server = server.copy(services = server.services.put(key, service)) - setNode(nodeState.value.copy(server = server)) - events.emit(ServerEvent.ServicePublished(server, key)) - } - - override suspend fun init() { - assert(!finalized) { "Machine is already finalized" } - - val server = server.copy(state = ServerState.ACTIVE) - setNode(nodeState.value.copy(state = NodeState.ACTIVE, server = server)) - } - - override suspend fun exit(cause: Throwable?) { - finalized = true - - val newServerState = - if (cause == null || (cause is ShutdownException && cause.cause == null)) - ServerState.SHUTOFF - else - ServerState.ERROR - val newNodeState = - if (cause == null || (cause is ShutdownException && cause.cause != null)) - nodeState.value.state - else - NodeState.ERROR - val server = server.copy(state = newServerState) - setNode(nodeState.value.copy(state = newNodeState, server = server)) - } - - /** - * A disposable to prevent resetting the usage state for subsequent calls to onRun. - */ - private var usageFlush: DisposableHandle? = null - - /** - * Cache the [Delay] instance for timing. - * - * XXX We need to cache this before the call to [onRun] since doing this in [onRun] is too heavy. - * XXX Note however that this is an ugly hack which may break in the future. - */ - @OptIn(InternalCoroutinesApi::class) - private val delay = coroutineScope.coroutineContext[ContinuationInterceptor] as Delay - - @OptIn(InternalCoroutinesApi::class) - override fun onRun( - batch: Sequence, - triggerMode: ServerContext.TriggerMode, - merge: (ServerContext.Slice, ServerContext.Slice) -> ServerContext.Slice - ): SelectClause0 { - assert(!finalized) { "Server instance is already finalized" } - - return object : SelectClause0 { - @InternalCoroutinesApi - override fun registerSelectClause0(select: SelectInstance, block: suspend () -> R) { - // Do not reset the usage state: we will set it ourselves - usageFlush?.dispose() - usageFlush = null - - val queue = batch.iterator() - var start = Long.MIN_VALUE - var currentWork: SliceWork? = null - var currentDisposable: DisposableHandle? = null - - fun schedule(slice: ServerContext.Slice) { - start = clock.millis() - - val isLastSlice = !queue.hasNext() - val work = SliceWork(slice) - val candidateDuration = when (triggerMode) { - ServerContext.TriggerMode.FIRST -> work.minExit - ServerContext.TriggerMode.LAST -> work.maxExit - ServerContext.TriggerMode.DEADLINE -> slice.deadline - start - } - - // Check whether the deadline is exceeded during the run of the slice. - val duration = min(candidateDuration, slice.deadline - start) - - val action = Runnable { - currentWork = null - - // Flush all the work that was performed - val hasFinished = work.stop(duration) - - if (!isLastSlice) { - val candidateSlice = queue.next() - val nextSlice = - // If our previous slice exceeds its deadline, merge it with the next candidate slice - if (hasFinished) - candidateSlice - else - merge(candidateSlice, slice) - schedule(nextSlice) - } else if (select.trySelect()) { - block.startCoroutineCancellable(select.completion) - } - } - - // Schedule the flush after the entire slice has finished - currentDisposable = delay.invokeOnTimeout(duration, action) - - // Start the slice work - currentWork = work - work.start() - } - - // Schedule the first work - if (queue.hasNext()) { - schedule(queue.next()) - - // A DisposableHandle to flush the work in case the call is cancelled - val disposable = DisposableHandle { - val end = clock.millis() - val duration = end - start - - currentWork?.stop(duration) - currentDisposable?.dispose() - - // Schedule reset the usage of the machine since the call is returning - usageFlush = delay.invokeOnTimeout(1) { - usageState.value = 0.0 - usageFlush = null - } - } - - select.disposeOnSelect(disposable) - } else if (select.trySelect()) { - // No work has been given: select immediately - block.startCoroutineCancellable(select.completion) - } - } - } - } - - /** - * A slice to be processed. - */ - private inner class SliceWork(val slice: ServerContext.Slice) { - /** - * The duration after which the first processor finishes processing this slice. - */ - public val minExit: Long - - /** - * The duration after which the last processor finishes processing this slice. - */ - public val maxExit: Long - - /** - * A flag to indicate that the slice will exceed the deadline. - */ - public val exceedsDeadline: Boolean - get() = slice.deadline < maxExit - - /** - * The total amount of CPU usage. - */ - public val totalUsage: Double - - /** - * A flag to indicate that this slice is empty. - */ - public val isEmpty: Boolean - - init { - var totalUsage = 0.0 - var minExit = Long.MAX_VALUE - var maxExit = 0L - var nonEmpty = false - - // Determine the duration of the first/last CPU to finish - for (i in 0 until min(cpus.size, slice.burst.size)) { - val cpu = cpus[i] - val usage = min(slice.limit[i], cpu.frequency) - val cpuDuration = - ceil(slice.burst[i] / usage * 1000).toLong() // Convert from seconds to milliseconds - - totalUsage += usage / cpu.frequency - - if (cpuDuration != 0L) { // We only wait for processor cores with a non-zero burst - minExit = min(minExit, cpuDuration) - maxExit = max(maxExit, cpuDuration) - nonEmpty = true - } - } - - this.isEmpty = !nonEmpty - this.totalUsage = totalUsage - this.minExit = minExit - this.maxExit = maxExit - } - - /** - * Indicate that the work on the slice has started. - */ - public fun start() { - usageState.value = totalUsage / cpus.size - } - - /** - * Flush the work performed on the slice. - */ - public fun stop(duration: Long): Boolean { - var hasFinished = true - - // Only flush the work if the machine is available - if (!unavailable) { - for (i in 0 until min(cpus.size, slice.burst.size)) { - val usage = min(slice.limit[i], cpus[i].frequency) - val granted = ceil(duration / 1000.0 * usage).toLong() - val res = max(0, slice.burst[i] - granted) - slice.burst[i] = res - - if (res != 0L) { - hasFinished = false - } - } - } - - return hasFinished - } - } - } - - override val scope: CoroutineScope - get() = coroutineScope - - override suspend fun fail() { - serverContext?.unavailable = true - - val server = nodeState.value.server?.copy(state = ServerState.ERROR) - setNode(nodeState.value.copy(state = NodeState.ERROR, server = server)) - } - - override suspend fun recover() { - serverContext?.unavailable = false - - val server = nodeState.value.server?.copy(state = ServerState.ACTIVE) - setNode(nodeState.value.copy(state = NodeState.ACTIVE, server = server)) - } - - override fun toString(): String = "SimpleBareMetalDriver(node = ${nodeState.value.uid})" -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/HypervisorImage.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/HypervisorImage.kt deleted file mode 100644 index ff88f0dc..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/HypervisorImage.kt +++ /dev/null @@ -1,55 +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.virt - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.suspendCancellableCoroutine -import org.opendc.compute.core.execution.ServerContext -import org.opendc.compute.core.image.Image -import org.opendc.compute.virt.driver.SimpleVirtDriver -import org.opendc.compute.virt.driver.VirtDriver -import org.opendc.core.resource.TagContainer -import java.util.UUID - -/** - * A hypervisor managing the VMs of a node. - */ -public object HypervisorImage : Image { - override val uid: UUID = UUID.randomUUID() - override val name: String = "vmm" - override val tags: TagContainer = emptyMap() - - override suspend fun invoke(ctx: ServerContext) { - coroutineScope { - val driver = SimpleVirtDriver(ctx, this) - ctx.publishService(VirtDriver.Key, driver) - - // Suspend image until it is cancelled - try { - suspendCancellableCoroutine {} - } finally { - driver.cancel() - } - } - } -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt index 758315aa..6e0df93f 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt @@ -28,7 +28,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch import org.opendc.compute.core.* import org.opendc.compute.core.execution.ComputeSimExecutionContext -import org.opendc.compute.core.execution.ShutdownException import org.opendc.compute.core.image.Image import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.virt.HypervisorEvent @@ -50,6 +49,12 @@ public class SimVirtDriver( private val ctx: SimExecutionContext ) : VirtDriver { + /** + * The server hosting this hypervisor. + */ + public val server: Server + get() = (ctx as ComputeSimExecutionContext).server + /** * The [EventFlow] to emit the events. */ @@ -164,7 +169,7 @@ public class SimVirtDriver( private fun exit(cause: Throwable?) { val serverState = - if (cause == null || (cause is ShutdownException && cause.cause == null)) + if (cause == null) ServerState.SHUTOFF else ServerState.ERROR diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimpleVirtDriver.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimpleVirtDriver.kt deleted file mode 100644 index d581e651..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimpleVirtDriver.kt +++ /dev/null @@ -1,640 +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.virt.driver - -import kotlinx.coroutines.* -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.intrinsics.startCoroutineCancellable -import kotlinx.coroutines.selects.SelectClause0 -import kotlinx.coroutines.selects.SelectInstance -import kotlinx.coroutines.selects.select -import mu.KotlinLogging -import org.opendc.compute.core.Flavor -import org.opendc.compute.core.ProcessingUnit -import org.opendc.compute.core.Server -import org.opendc.compute.core.ServerEvent -import org.opendc.compute.core.ServerState -import org.opendc.compute.core.execution.ServerContext -import org.opendc.compute.core.execution.ServerManagementContext -import org.opendc.compute.core.execution.ShutdownException -import org.opendc.compute.core.image.Image -import org.opendc.compute.core.workload.IMAGE_PERF_INTERFERENCE_MODEL -import org.opendc.compute.core.workload.PerformanceInterferenceModel -import org.opendc.compute.virt.HypervisorEvent -import org.opendc.core.services.ServiceKey -import org.opendc.core.services.ServiceRegistry -import org.opendc.utils.flow.EventFlow -import java.time.Clock -import java.util.UUID -import kotlin.math.ceil -import kotlin.math.max -import kotlin.math.min - -/** - * The logging instance to use. - */ -private val logger = KotlinLogging.logger {} - -/** - * A [VirtDriver] that is backed by a simple hypervisor implementation. - */ -@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class) -public class SimpleVirtDriver( - private val hostContext: ServerContext, - scope: CoroutineScope -) : VirtDriver, CoroutineScope by scope { - /** - * The [Server] on which this hypervisor runs. - */ - public val server: Server - get() = hostContext.server - - /** - * A set for tracking the VM context objects. - */ - private val vms: MutableSet = mutableSetOf() - - /** - * Current total memory use of the images on this hypervisor. - */ - private var availableMemory: Long = hostContext.server.flavor.memorySize - - /** - * The [EventFlow] to emit the events. - */ - internal val eventFlow = EventFlow() - - override val events: Flow = eventFlow - - init { - launch { - try { - // Yield first to allow class variables to initialize - yield() - scheduler() - } catch (e: Exception) { - if (e !is CancellationException) { - logger.error("Hypervisor scheduler failed", e) - } - throw e - } - } - } - - override suspend fun spawn( - name: String, - image: Image, - flavor: org.opendc.compute.core.Flavor - ): Server { - val requiredMemory = flavor.memorySize - if (availableMemory - requiredMemory < 0) { - throw InsufficientMemoryOnServerException() - } - require(flavor.cpuCount <= hostContext.server.flavor.cpuCount) { "Machine does not fit" } - - val events = EventFlow() - val server = Server( - UUID.randomUUID(), - name, - emptyMap(), - flavor, - image, - ServerState.BUILD, - ServiceRegistry(), - events - ) - availableMemory -= requiredMemory - vms.add(VmServerContext(server, events)) - vmStarted(server) - eventFlow.emit(HypervisorEvent.VmsUpdated(this, vms.size, availableMemory)) - return server - } - - internal fun cancel() { - eventFlow.close() - } - - private fun vmStarted(server: Server) { - vms.forEach { - val performanceModel = - it.server.image.tags[IMAGE_PERF_INTERFERENCE_MODEL] as? PerformanceInterferenceModel? - performanceModel?.vmStarted(server) - } - } - - private fun vmStopped(server: Server) { - vms.forEach { - val performanceModel = - it.server.image.tags[IMAGE_PERF_INTERFERENCE_MODEL] as? PerformanceInterferenceModel? - performanceModel?.vmStopped(server) - } - } - - /** - * A scheduling command processed by the scheduler. - */ - private sealed class SchedulerCommand { - /** - * Schedule the specified VM on the hypervisor. - */ - data class Schedule(val vm: Vm) : SchedulerCommand() - - /** - * De-schedule the specified VM on the hypervisor. - */ - data class Deschedule(val vm: Vm) : SchedulerCommand() - - /** - * Interrupt the scheduler. - */ - object Interrupt : SchedulerCommand() - } - - /** - * A flag to indicate the driver is stopped. - */ - private var stopped: Boolean = false - - /** - * The channel for scheduling new CPU requests. - */ - private val schedulingQueue = Channel(Channel.UNLIMITED) - - /** - * The scheduling process of the hypervisor. - */ - private suspend fun scheduler() { - val clock = hostContext.clock - val maxUsage = hostContext.cpus.sumByDouble { it.frequency } - val pCPUs = hostContext.cpus.indices.sortedBy { hostContext.cpus[it].frequency } - - val vms = mutableSetOf() - val vcpus = mutableListOf() - - val usage = DoubleArray(hostContext.cpus.size) - val burst = LongArray(hostContext.cpus.size) - - fun process(command: SchedulerCommand) { - when (command) { - is SchedulerCommand.Schedule -> { - vms += command.vm - vcpus.addAll(command.vm.vcpus) - } - is SchedulerCommand.Deschedule -> { - vms -= command.vm - vcpus.removeAll(command.vm.vcpus) - } - is SchedulerCommand.Interrupt -> { - } - } - } - - fun processRemaining() { - var command = schedulingQueue.poll() - while (command != null) { - process(command) - command = schedulingQueue.poll() - } - } - - while (!stopped) { - // Wait for a request to be submitted if we have no work yet. - if (vcpus.isEmpty()) { - process(schedulingQueue.receive()) - } - - processRemaining() - - val start = clock.millis() - - val vmCount = vms.size - var duration: Double = Double.POSITIVE_INFINITY - var deadline: Long = Long.MAX_VALUE - var availableUsage = maxUsage - var totalRequestedUsage = 0.0 - var totalRequestedBurst = 0L - - // Sort the vCPUs based on their requested usage - // Profiling shows that it is faster to sort every slice instead of maintaining some kind of sorted set - vcpus.sort() - - // Divide the available host capacity fairly across the vCPUs using max-min fair sharing - for ((i, req) in vcpus.withIndex()) { - val remaining = vcpus.size - i - val availableShare = availableUsage / remaining - val grantedUsage = min(req.limit, availableShare) - - // Take into account the minimum deadline of this slice before we possible continue - deadline = min(deadline, req.vm.deadline) - - // Ignore empty CPUs - if (grantedUsage <= 0 || req.burst <= 0) { - req.allocatedLimit = 0.0 - continue - } - - totalRequestedUsage += req.limit - totalRequestedBurst += req.burst - - req.allocatedLimit = grantedUsage - availableUsage -= grantedUsage - - // The duration that we want to run is that of the shortest request from a vCPU - duration = min(duration, req.burst / grantedUsage) - } - - // XXX We set the minimum duration to 5 minutes here to prevent the rounding issues that are occurring with the FLOPs. - duration = 300.0 - - val totalAllocatedUsage = maxUsage - availableUsage - var totalAllocatedBurst = 0L - availableUsage = totalAllocatedUsage - val serverLoad = totalAllocatedUsage / maxUsage - - // Divide the requests over the available capacity of the pCPUs fairly - for (i in pCPUs) { - val maxCpuUsage = hostContext.cpus[i].frequency - val fraction = maxCpuUsage / maxUsage - val grantedUsage = min(maxCpuUsage, totalAllocatedUsage * fraction) - val grantedBurst = ceil(duration * grantedUsage).toLong() - - usage[i] = grantedUsage - burst[i] = grantedBurst - totalAllocatedBurst += grantedBurst - availableUsage -= grantedUsage - } - - // We run the total burst on the host processor. Note that this call may be cancelled at any moment in - // time, so not all of the burst may be executed. - select { - schedulingQueue.onReceive { schedulingQueue.offer(it); true } - hostContext.onRun(ServerContext.Slice(burst, usage, deadline), ServerContext.TriggerMode.DEADLINE) - .invoke { false } - } - - val end = clock.millis() - - // No work was performed - if ((end - start) <= 0) { - continue - } - - // The total requested burst that the VMs wanted to run in the time-frame that we ran. - val totalRequestedSubBurst = - vcpus.map { ceil((duration * 1000) / (it.vm.deadline - start) * it.burst).toLong() }.sum() - val totalRemainder = burst.sum() - val totalGrantedBurst = totalAllocatedBurst - totalRemainder - - // The burst that was lost due to overcommissioning of CPU resources - var totalOvercommissionedBurst = 0L - // The burst that was lost due to interference. - var totalInterferedBurst = 0L - - val vmIterator = vms.iterator() - while (vmIterator.hasNext()) { - val vm = vmIterator.next() - - // Apply performance interference model - val performanceModel = - vm.ctx.server.image.tags[IMAGE_PERF_INTERFERENCE_MODEL] as? PerformanceInterferenceModel? - val performanceScore = performanceModel?.apply(serverLoad) ?: 1.0 - var hasFinished = false - - for (vcpu in vm.vcpus) { - // Compute the fraction of compute time allocated to the VM - val fraction = vcpu.allocatedLimit / totalAllocatedUsage - - // Compute the burst time that the VM was actually granted - val grantedBurst = ceil(totalGrantedBurst * fraction).toLong() - - // The burst that was actually used by the VM - val usedBurst = ceil(grantedBurst * performanceScore).toLong() - - totalInterferedBurst += grantedBurst - usedBurst - - // Compute remaining burst time to be executed for the request - if (vcpu.consume(usedBurst)) { - hasFinished = true - } else if (vm.deadline <= end && hostContext.server.state != ServerState.ERROR) { - // Request must have its entire burst consumed or otherwise we have overcommission - // Note that we count the overcommissioned burst if the hypervisor has failed. - totalOvercommissionedBurst += vcpu.burst - } - } - - if (hasFinished || vm.deadline <= end) { - // Mark the VM as finished and deschedule the VMs if needed - if (vm.finish()) { - vmIterator.remove() - vcpus.removeAll(vm.vcpus) - } - } - } - - eventFlow.emit( - HypervisorEvent.SliceFinished( - this@SimpleVirtDriver, - totalRequestedBurst, - min(totalRequestedSubBurst, totalGrantedBurst), // We can run more than requested due to timing - totalOvercommissionedBurst, - totalInterferedBurst, // Might be smaller than zero due to FP rounding errors, - min( - totalAllocatedUsage, - totalRequestedUsage - ), // The allocated usage might be slightly higher due to FP rounding - totalRequestedUsage, - vmCount, // Some VMs might already have finished, so keep initial VM count - server - ) - ) - } - } - - /** - * A virtual machine running on the hypervisor. - * - * @param ctx The execution context the vCPU runs in. - * @param triggerMode The mode when to trigger the VM exit. - * @param merge The function to merge consecutive slices on spillover. - * @param select The function to select on finish. - */ - @OptIn(InternalCoroutinesApi::class) - private data class Vm( - val ctx: VmServerContext, - var triggerMode: ServerContext.TriggerMode = ServerContext.TriggerMode.FIRST, - var merge: (ServerContext.Slice, ServerContext.Slice) -> ServerContext.Slice = { _, r -> r }, - var select: () -> Unit = {} - ) { - /** - * The vCPUs of this virtual machine. - */ - val vcpus: List - - /** - * The slices that the VM wants to run. - */ - var queue: Iterator = emptyList().iterator() - - /** - * The current active slice. - */ - var activeSlice: ServerContext.Slice? = null - - /** - * The current deadline of the VM. - */ - val deadline: Long - get() = activeSlice?.deadline ?: Long.MAX_VALUE - - /** - * A flag to indicate that the VM is idle. - */ - val isIdle: Boolean - get() = activeSlice == null - - init { - vcpus = ctx.cpus.mapIndexed { i, model -> VCpu(this, model, i) } - } - - /** - * Schedule the given slices on this vCPU, replacing the existing slices. - */ - fun schedule(slices: Sequence) { - queue = slices.iterator() - - if (queue.hasNext()) { - activeSlice = queue.next() - vcpus.forEach { it.refresh() } - } - } - - /** - * Cancel the existing workload on the VM. - */ - fun cancel() { - queue = emptyList().iterator() - activeSlice = null - vcpus.forEach { it.refresh() } - } - - /** - * Finish the current slice of the VM. - * - * @return `true` if the vCPUs may be descheduled, `false` otherwise. - */ - fun finish(): Boolean { - val activeSlice = activeSlice ?: return true - - return if (queue.hasNext()) { - val needsMerge = activeSlice.burst.any { it > 0 } - val candidateSlice = queue.next() - val slice = if (needsMerge) merge(activeSlice, candidateSlice) else candidateSlice - - this.activeSlice = slice - - // Update the vCPU cache - vcpus.forEach { it.refresh() } - - false - } else { - this.activeSlice = null - select() - true - } - } - } - - /** - * A virtual CPU that can be scheduled on a physical CPU. - * - * @param vm The VM of which this vCPU is part. - * @param model The model of CPU that this vCPU models. - * @param id The id of the vCPU with respect to the VM. - */ - private data class VCpu( - val vm: Vm, - val model: ProcessingUnit, - val id: Int - ) : Comparable { - /** - * The current limit on the vCPU. - */ - var limit: Double = 0.0 - - /** - * The limit allocated by the hypervisor. - */ - var allocatedLimit: Double = 0.0 - - /** - * The current burst running on the vCPU. - */ - var burst: Long = 0L - - /** - * Consume the specified burst on this vCPU. - */ - fun consume(burst: Long): Boolean { - this.burst = max(0, this.burst - burst) - - // Flush the result to the slice if it exists - vm.activeSlice?.burst?.takeIf { id < it.size }?.set(id, this.burst) - - return allocatedLimit > 0.0 && this.burst == 0L - } - - /** - * Refresh the information of this vCPU based on the current slice. - */ - fun refresh() { - limit = vm.activeSlice?.limit?.takeIf { id < it.size }?.get(id) ?: 0.0 - burst = vm.activeSlice?.burst?.takeIf { id < it.size }?.get(id) ?: 0 - } - - /** - * Compare to another vCPU based on the current load of the vCPU. - */ - override fun compareTo(other: VCpu): Int { - var cmp = limit.compareTo(other.limit) - - if (cmp != 0) { - return cmp - } - - cmp = vm.ctx.server.uid.compareTo(other.vm.ctx.server.uid) - - if (cmp != 0) { - return cmp - } - - return id.compareTo(other.id) - } - - /** - * Create a string representation of the vCPU. - */ - override fun toString(): String = - "vCPU(vm=${vm.ctx.server.uid},id=$id,burst=$burst,limit=$limit,allocatedLimit=$allocatedLimit)" - } - - /** - * The execution context in which a VM runs. - * - * @param server The details of the VM. - * @param events The event stream to publish to. - */ - private inner class VmServerContext(server: Server, val events: EventFlow) : - ServerManagementContext, - DisposableHandle { - private var finalized: Boolean = false - private var initialized: Boolean = false - private val vm: Vm - - internal val job: Job = launch { - delay(1) // TODO Introduce boot time - init() - try { - server.image(this@VmServerContext) - exit() - } catch (cause: Throwable) { - exit(cause) - } - } - - override var server: Server = server - set(value) { - if (field.state != value.state) { - events.emit(ServerEvent.StateChanged(value, field.state)) - } - - field = value - } - - override val cpus: List = hostContext.cpus.take(server.flavor.cpuCount) - - override val clock: Clock - get() = hostContext.clock - - init { - vm = Vm(this) - } - - override suspend fun publishService(key: ServiceKey, service: T) { - server = server.copy(services = server.services.put(key, service)) - events.emit(ServerEvent.ServicePublished(server, key)) - } - - override suspend fun init() { - assert(!finalized) { "VM is already finalized" } - - server = server.copy(state = ServerState.ACTIVE) - initialized = true - } - - override suspend fun exit(cause: Throwable?) { - finalized = true - - val serverState = - if (cause == null || (cause is ShutdownException && cause.cause == null)) - ServerState.SHUTOFF - else - ServerState.ERROR - server = server.copy(state = serverState) - availableMemory += server.flavor.memorySize - vms.remove(this) - vmStopped(server) - eventFlow.emit(HypervisorEvent.VmsUpdated(this@SimpleVirtDriver, vms.size, availableMemory)) - events.close() - } - - @OptIn(InternalCoroutinesApi::class) - override fun onRun( - batch: Sequence, - triggerMode: ServerContext.TriggerMode, - merge: (ServerContext.Slice, ServerContext.Slice) -> ServerContext.Slice - ): SelectClause0 = object : SelectClause0 { - @InternalCoroutinesApi - override fun registerSelectClause0(select: SelectInstance, block: suspend () -> R) { - vm.triggerMode = triggerMode - vm.merge = merge - vm.select = { - if (select.trySelect()) { - block.startCoroutineCancellable(select.completion) - } - } - vm.schedule(batch) - // Indicate to the hypervisor that the VM should be re-scheduled - schedulingQueue.offer(SchedulerCommand.Schedule(vm)) - select.disposeOnSelect(this@VmServerContext) - } - } - - override fun dispose() { - if (!vm.isIdle) { - vm.cancel() - schedulingQueue.offer(SchedulerCommand.Deschedule(vm)) - } - } - } -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/SimpleVirtProvisioningService.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/SimpleVirtProvisioningService.kt index f021d3f0..e7c2b8cc 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/SimpleVirtProvisioningService.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/SimpleVirtProvisioningService.kt @@ -32,16 +32,17 @@ 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.image.VmImage +import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.metal.service.ProvisioningService import org.opendc.compute.virt.HypervisorEvent -import org.opendc.compute.virt.HypervisorImage import org.opendc.compute.virt.driver.InsufficientMemoryOnServerException +import org.opendc.compute.virt.driver.SimVirtDriver +import org.opendc.compute.virt.driver.SimVirtDriverWorkload import org.opendc.compute.virt.driver.VirtDriver import org.opendc.compute.virt.service.allocation.AllocationPolicy -import org.opendc.core.services.ServiceKey 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 @@ -100,14 +101,25 @@ public class SimpleVirtProvisioningService( coroutineScope.launch { val provisionedNodes = provisioningService.nodes() provisionedNodes.forEach { node -> - val hypervisorImage = HypervisorImage - val node = provisioningService.deploy(node, hypervisorImage) - node.server!!.events.onEach { event -> - when (event) { - is ServerEvent.StateChanged -> stateChanged(event.server) - is ServerEvent.ServicePublished -> servicePublished(event.server, event.key) - } - }.launchIn(this) + val workload = SimVirtDriverWorkload() + val hypervisorImage = SimWorkloadImage(UUID.randomUUID(), "vmm", emptyMap(), workload) + launch { + var init = false + val deployedNode = provisioningService.deploy(node, hypervisorImage) + deployedNode.server!!.events.onEach { event -> + when (event) { + is ServerEvent.StateChanged -> { + if (!init) { + init = true + } + stateChanged(event.server) + } + } + }.launchIn(this) + + delay(1) + onHypervisorAvailable(deployedNode.server, workload.driver) + } } } } @@ -119,7 +131,7 @@ public class SimpleVirtProvisioningService( override suspend fun deploy( name: String, image: Image, - flavor: org.opendc.compute.core.Flavor + flavor: Flavor ): Server { eventFlow.emit( VirtProvisioningEvent.MetricsAvailable( @@ -171,7 +183,7 @@ public class SimpleVirtProvisioningService( val imagesToBeScheduled = incomingImages.toSet() for (imageInstance in imagesToBeScheduled) { - val requiredMemory = (imageInstance.image as VmImage).requiredMemory + val requiredMemory = imageInstance.image.tags["required-memory"] as Long val selectedHv = allocationLogic.select(availableHypervisors, imageInstance) if (selectedHv == null) { @@ -339,41 +351,39 @@ public class SimpleVirtProvisioningService( } } - private fun servicePublished(server: Server, key: ServiceKey<*>) { - if (key == VirtDriver.Key) { - val hv = hypervisors[server] ?: return - hv.driver = server.services[VirtDriver] - availableHypervisors += hv - - eventFlow.emit( - VirtProvisioningEvent.MetricsAvailable( - this@SimpleVirtProvisioningService, - hypervisors.size, - availableHypervisors.size, - submittedVms, - runningVms, - finishedVms, - queuedVms, - unscheduledVms - ) + private fun onHypervisorAvailable(server: Server, hypervisor: SimVirtDriver) { + val hv = hypervisors[server] ?: return + hv.driver = hypervisor + availableHypervisors += hv + + eventFlow.emit( + VirtProvisioningEvent.MetricsAvailable( + this@SimpleVirtProvisioningService, + 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) + hv.driver.events + .onEach { event -> + if (event is HypervisorEvent.VmsUpdated) { + hv.numberOfActiveServers = event.numberOfActiveServers + hv.availableMemory = event.availableMemory + } + }.launchIn(coroutineScope) - requestCycle() - } + requestCycle() } public data class ImageView( public val name: String, public val image: Image, - public val flavor: org.opendc.compute.core.Flavor, + public val flavor: Flavor, public val continuation: Continuation, public var server: Server? = null ) diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/ComparableAllocationPolicyLogic.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/ComparableAllocationPolicyLogic.kt index 8803009d..c971cd7e 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/ComparableAllocationPolicyLogic.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/ComparableAllocationPolicyLogic.kt @@ -22,7 +22,6 @@ package org.opendc.compute.virt.service.allocation -import org.opendc.compute.core.image.VmImage import org.opendc.compute.virt.service.HypervisorView import org.opendc.compute.virt.service.SimpleVirtProvisioningService @@ -41,7 +40,7 @@ public interface ComparableAllocationPolicyLogic : AllocationPolicy.Logic { ): HypervisorView? { return hypervisors.asSequence() .filter { hv -> - val fitsMemory = hv.availableMemory >= (image.image as VmImage).requiredMemory + val fitsMemory = hv.availableMemory >= (image.image.tags["required-memory"] as Long) val fitsCpu = hv.server.flavor.cpuCount >= image.flavor.cpuCount fitsMemory && fitsCpu } diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/RandomAllocationPolicy.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/RandomAllocationPolicy.kt index d143ffe6..26e61673 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/RandomAllocationPolicy.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/RandomAllocationPolicy.kt @@ -22,7 +22,6 @@ package org.opendc.compute.virt.service.allocation -import org.opendc.compute.core.image.VmImage import org.opendc.compute.virt.service.HypervisorView import org.opendc.compute.virt.service.SimpleVirtProvisioningService import kotlin.random.Random @@ -39,7 +38,7 @@ public class RandomAllocationPolicy(private val random: Random = Random(0)) : Al ): HypervisorView? { return hypervisors.asIterable() .filter { hv -> - val fitsMemory = hv.availableMemory >= (image.image as VmImage).requiredMemory + val fitsMemory = hv.availableMemory >= (image.image.tags["required-memory"] as Long) val fitsCpu = hv.server.flavor.cpuCount >= image.flavor.cpuCount fitsMemory && fitsCpu } diff --git a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/core/image/FlopsApplicationImageTest.kt b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/core/image/FlopsApplicationImageTest.kt deleted file mode 100644 index 8870f138..00000000 --- a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/core/image/FlopsApplicationImageTest.kt +++ /dev/null @@ -1,76 +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.core.image - -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import java.util.UUID - -/** - * Test suite for [FlopsApplicationImage] - */ -@DisplayName("FlopsApplicationImage") -internal class FlopsApplicationImageTest { - @Test - fun `flops must be non-negative`() { - assertThrows("FLOPs must be non-negative") { - FlopsApplicationImage(UUID.randomUUID(), "", emptyMap(), -1, 1) - } - } - - @Test - fun `cores cannot be zero`() { - assertThrows("Cores cannot be zero") { - FlopsApplicationImage(UUID.randomUUID(), "", emptyMap(), 1, 0) - } - } - - @Test - fun `cores cannot be negative`() { - assertThrows("Cores cannot be negative") { - FlopsApplicationImage(UUID.randomUUID(), "", emptyMap(), 1, -1) - } - } - - @Test - fun `utilization cannot be zero`() { - assertThrows("Utilization cannot be zero") { - FlopsApplicationImage(UUID.randomUUID(), "", emptyMap(), 1, 1, 0.0) - } - } - - @Test - fun `utilization cannot be negative`() { - assertThrows("Utilization cannot be negative") { - FlopsApplicationImage(UUID.randomUUID(), "", emptyMap(), 1, 1, -1.0) - } - } - - @Test - fun `utilization cannot be larger than one`() { - assertThrows("Utilization cannot be larger than one") { - FlopsApplicationImage(UUID.randomUUID(), "", emptyMap(), 1, 1, 2.0) - } - } -} diff --git a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt deleted file mode 100644 index 23c9408d..00000000 --- a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt +++ /dev/null @@ -1,81 +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.metal.driver - -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.TestCoroutineScope -import kotlinx.coroutines.withContext -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.opendc.compute.core.ProcessingNode -import org.opendc.compute.core.ProcessingUnit -import org.opendc.compute.core.ServerEvent -import org.opendc.compute.core.ServerState -import org.opendc.compute.core.image.FlopsApplicationImage -import org.opendc.simulator.utils.DelayControllerClockAdapter -import java.util.UUID - -@OptIn(ExperimentalCoroutinesApi::class) -internal class SimpleBareMetalDriverTest { - /** - * A smoke test for the bare-metal driver. - */ - @Test - fun smoke() { - val testScope = TestCoroutineScope() - val clock = DelayControllerClockAdapter(testScope) - - var finalState: ServerState = ServerState.BUILD - testScope.launch { - val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 4) - val cpus = List(4) { ProcessingUnit(cpuNode, it, 2000.0) } - val driver = SimpleBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), cpus, emptyList()) - val image = FlopsApplicationImage(UUID.randomUUID(), "", emptyMap(), 4_000, 2, utilization = 1.0) - - // Batch driver commands - withContext(coroutineContext) { - driver.init() - driver.setImage(image) - val server = driver.start().server!! - driver.usage - .onEach { println("${clock.millis()} $it") } - .launchIn(this) - server.events.collect { event -> - when (event) { - is ServerEvent.StateChanged -> { - println("${clock.millis()} $event") - finalState = event.server.state - } - } - } - } - } - - testScope.advanceUntilIdle() - assertEquals(ServerState.SHUTOFF, finalState) - } -} diff --git a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/HypervisorTest.kt b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/HypervisorTest.kt deleted file mode 100644 index 82a82044..00000000 --- a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/HypervisorTest.kt +++ /dev/null @@ -1,167 +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.virt - -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.TestCoroutineScope -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertAll -import org.opendc.compute.core.Flavor -import org.opendc.compute.core.ProcessingNode -import org.opendc.compute.core.ProcessingUnit -import org.opendc.compute.core.image.FlopsApplicationImage -import org.opendc.compute.core.image.FlopsHistoryFragment -import org.opendc.compute.core.image.VmImage -import org.opendc.compute.metal.driver.SimpleBareMetalDriver -import org.opendc.compute.virt.driver.VirtDriver -import org.opendc.simulator.utils.DelayControllerClockAdapter -import java.util.UUID - -/** - * Basic test-suite for the hypervisor. - */ -@OptIn(ExperimentalCoroutinesApi::class) -internal class HypervisorTest { - /** - * A smoke test for the bare-metal driver. - */ - @OptIn(ExperimentalCoroutinesApi::class) - @Test - fun smoke() { - val testScope = TestCoroutineScope() - val clock = DelayControllerClockAdapter(testScope) - - testScope.launch { - val vmm = HypervisorImage - val workloadA = FlopsApplicationImage(UUID.randomUUID(), "", emptyMap(), 1_000, 1) - val workloadB = FlopsApplicationImage(UUID.randomUUID(), "", emptyMap(), 2_000, 1) - - val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 1) - val cpus = List(1) { ProcessingUnit(cpuNode, it, 2000.0) } - val metalDriver = - SimpleBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), cpus, emptyList()) - - metalDriver.init() - metalDriver.setImage(vmm) - val node = metalDriver.start() - node.server?.events?.onEach { println(it) }?.launchIn(this) - - delay(5) - - val flavor = Flavor(1, 0) - val vmDriver = metalDriver.refresh().server!!.services[VirtDriver] - vmDriver.events.onEach { println(it) }.launchIn(this) - val vmA = vmDriver.spawn("a", workloadA, flavor) - vmA.events.onEach { println(it) }.launchIn(this) - val vmB = vmDriver.spawn("b", workloadB, flavor) - vmB.events.onEach { println(it) }.launchIn(this) - } - - testScope.advanceUntilIdle() - } - - /** - * Test overcommissioning of a hypervisor. - */ - @Test - fun overcommission() { - val testScope = TestCoroutineScope() - val clock = DelayControllerClockAdapter(testScope) - - var requestedBurst = 0L - var grantedBurst = 0L - var overcommissionedBurst = 0L - - testScope.launch { - val vmm = HypervisorImage - val duration = 5 * 60L - val vmImageA = VmImage( - UUID.randomUUID(), - "", - emptyMap(), - sequenceOf( - FlopsHistoryFragment(0, 28L * duration, duration * 1000, 28.0, 2), - FlopsHistoryFragment(0, 3500L * duration, duration * 1000, 3500.0, 2), - FlopsHistoryFragment(0, 0, duration * 1000, 0.0, 2), - FlopsHistoryFragment(0, 183L * duration, duration * 1000, 183.0, 2) - ), - 2, - 0 - ) - val vmImageB = VmImage( - UUID.randomUUID(), - "", - emptyMap(), - sequenceOf( - FlopsHistoryFragment(0, 28L * duration, duration * 1000, 28.0, 2), - FlopsHistoryFragment(0, 3100L * duration, duration * 1000, 3100.0, 2), - FlopsHistoryFragment(0, 0, duration * 1000, 0.0, 2), - FlopsHistoryFragment(0, 73L * duration, duration * 1000, 73.0, 2) - ), - 2, - 0 - ) - - val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) - val cpus = List(2) { ProcessingUnit(cpuNode, it, 3200.0) } - val metalDriver = - SimpleBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), cpus, emptyList()) - - metalDriver.init() - metalDriver.setImage(vmm) - metalDriver.start() - - delay(5) - - val flavor = Flavor(2, 0) - val vmDriver = metalDriver.refresh().server!!.services[VirtDriver] - vmDriver.events - .onEach { event -> - when (event) { - is HypervisorEvent.SliceFinished -> { - requestedBurst += event.requestedBurst - grantedBurst += event.grantedBurst - overcommissionedBurst += event.overcommissionedBurst - } - } - } - .launchIn(this) - - vmDriver.spawn("a", vmImageA, flavor) - vmDriver.spawn("b", vmImageB, flavor) - } - - testScope.advanceUntilIdle() - - assertAll( - { assertEquals(2073600, requestedBurst, "Requested Burst does not match") }, - { assertEquals(2013600, grantedBurst, "Granted Burst does not match") }, - { assertEquals(60000, overcommissionedBurst, "Overcommissioned Burst does not match") } - ) - } -} diff --git a/simulator/opendc-experiments/opendc-experiments-sc18/build.gradle.kts b/simulator/opendc-experiments/opendc-experiments-sc18/build.gradle.kts index b12b1ae3..9cf72f18 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc18/build.gradle.kts +++ b/simulator/opendc-experiments/opendc-experiments-sc18/build.gradle.kts @@ -37,7 +37,10 @@ dependencies { implementation(project(":opendc-format")) implementation(project(":opendc-workflows")) implementation(project(":opendc-simulator:opendc-simulator-core")) - implementation(kotlin("stdlib")) + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8") { + exclude("org.jetbrains.kotlin", module = "kotlin-reflect") + } + implementation(kotlin("reflect")) testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Library.JUNIT_JUPITER}") diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts b/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts index 1a7c2f78..c0992fc9 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts +++ b/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts @@ -37,6 +37,7 @@ dependencies { api(project(":opendc-core")) implementation(project(":opendc-format")) implementation(project(":opendc-simulator:opendc-simulator-core")) + implementation(project(":opendc-simulator:opendc-simulator-compute")) implementation("com.github.ajalt:clikt:2.6.0") implementation("me.tongfei:progressbar:0.8.1") diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt index 97d7d5da..7b6c4880 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt @@ -40,7 +40,7 @@ import org.opendc.compute.metal.NODE_CLUSTER import org.opendc.compute.metal.driver.BareMetalDriver import org.opendc.compute.metal.service.ProvisioningService import org.opendc.compute.virt.HypervisorEvent -import org.opendc.compute.virt.driver.SimpleVirtDriver +import org.opendc.compute.virt.driver.SimVirtDriver import org.opendc.compute.virt.service.SimpleVirtProvisioningService import org.opendc.compute.virt.service.VirtProvisioningEvent import org.opendc.compute.virt.service.allocation.AllocationPolicy @@ -171,8 +171,9 @@ public suspend fun attachMonitor( // Monitor hypervisor events for (hypervisor in hypervisors) { // TODO Do not expose VirtDriver directly but use Hypervisor class. - monitor.reportHostStateChange(clock.millis(), hypervisor, (hypervisor as SimpleVirtDriver).server) - hypervisor.server.events + val server = (hypervisor as SimVirtDriver).server + monitor.reportHostStateChange(clock.millis(), hypervisor, server) + server.events .onEach { event -> val time = clock.millis() when (event) { @@ -242,8 +243,8 @@ public suspend fun processTrace( workload.image.name, workload.image, Flavor( - workload.image.maxCores, - workload.image.requiredMemory + workload.image.tags["cores"] as Int, + workload.image.tags["required-memory"] as Long ) ) // Monitor server events diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20ParquetTraceReader.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20ParquetTraceReader.kt index 5a336865..39b00a61 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20ParquetTraceReader.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20ParquetTraceReader.kt @@ -22,7 +22,7 @@ package org.opendc.experiments.sc20.trace -import org.opendc.compute.core.image.VmImage +import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.core.workload.IMAGE_PERF_INTERFERENCE_MODEL import org.opendc.compute.core.workload.PerformanceInterferenceModel import org.opendc.compute.core.workload.VmWorkload @@ -73,13 +73,11 @@ public class Sc20ParquetTraceReader( performanceInterferenceModel[id] ?: PerformanceInterferenceModel(TreeSet()) val newImage = - VmImage( + SimWorkloadImage( image.uid, image.name, - mapOf(IMAGE_PERF_INTERFERENCE_MODEL to relevantPerformanceInterferenceModelItems), - image.flopsHistory, - image.maxCores, - image.requiredMemory + image.tags + mapOf(IMAGE_PERF_INTERFERENCE_MODEL to relevantPerformanceInterferenceModelItems), + (image as SimWorkloadImage).workload ) val newWorkload = entry.workload.copy(image = newImage) Sc20RawParquetTraceReader.TraceEntryImpl(entry.submissionTime, newWorkload) diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20RawParquetTraceReader.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20RawParquetTraceReader.kt index 8965cf43..b40cad7c 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20RawParquetTraceReader.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20RawParquetTraceReader.kt @@ -26,12 +26,12 @@ import mu.KotlinLogging import org.apache.avro.generic.GenericData import org.apache.hadoop.fs.Path import org.apache.parquet.avro.AvroParquetReader -import org.opendc.compute.core.image.FlopsHistoryFragment -import org.opendc.compute.core.image.VmImage +import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.core.workload.VmWorkload import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader +import org.opendc.simulator.compute.workload.SimTraceWorkload import java.io.File import java.util.UUID @@ -47,12 +47,12 @@ public class Sc20RawParquetTraceReader(private val path: File) { /** * Read the fragments into memory. */ - private fun parseFragments(path: File): Map> { + private fun parseFragments(path: File): Map> { val reader = AvroParquetReader.builder(Path(path.absolutePath, "trace.parquet")) .disableCompatibility() .build() - val fragments = mutableMapOf>() + val fragments = mutableMapOf>() return try { while (true) { @@ -65,7 +65,7 @@ public class Sc20RawParquetTraceReader(private val path: File) { val cpuUsage = record["cpuUsage"] as Double val flops = record["flops"] as Long - val fragment = FlopsHistoryFragment( + val fragment = SimTraceWorkload.Fragment( tick, flops, duration, @@ -85,7 +85,7 @@ public class Sc20RawParquetTraceReader(private val path: File) { /** * Read the metadata into a workload. */ - private fun parseMeta(path: File, fragments: Map>): List { + private fun parseMeta(path: File, fragments: Map>): List { val metaReader = AvroParquetReader.builder(Path(path.absolutePath, "meta.parquet")) .disableCompatibility() .build() @@ -114,17 +114,17 @@ public class Sc20RawParquetTraceReader(private val path: File) { uid, id, UnnamedUser, - VmImage( + SimWorkloadImage( uid, id, mapOf( "submit-time" to submissionTime, "end-time" to endTime, - "total-load" to totalLoad + "total-load" to totalLoad, + "cores" to maxCores, + "required-memory" to requiredMemory ), - vmFragments, - maxCores, - requiredMemory + SimTraceWorkload(vmFragments) ) ) entries.add(TraceEntryImpl(submissionTime, vmWorkload)) diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20StreamingParquetTraceReader.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20StreamingParquetTraceReader.kt index 41dc4b49..2ec01606 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20StreamingParquetTraceReader.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20StreamingParquetTraceReader.kt @@ -31,14 +31,14 @@ import org.apache.parquet.filter2.predicate.FilterApi import org.apache.parquet.filter2.predicate.Statistics import org.apache.parquet.filter2.predicate.UserDefinedPredicate import org.apache.parquet.io.api.Binary -import org.opendc.compute.core.image.FlopsHistoryFragment -import org.opendc.compute.core.image.VmImage +import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.core.workload.IMAGE_PERF_INTERFERENCE_MODEL import org.opendc.compute.core.workload.PerformanceInterferenceModel import org.opendc.compute.core.workload.VmWorkload import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader +import org.opendc.simulator.compute.workload.SimTraceWorkload import java.io.File import java.io.Serializable import java.util.SortedSet @@ -71,7 +71,7 @@ public class Sc20StreamingParquetTraceReader( /** * The intermediate buffer to store the read records in. */ - private val queue = ArrayBlockingQueue>(1024) + private val queue = ArrayBlockingQueue>(1024) /** * An optional filter for filtering the selected VMs @@ -92,7 +92,7 @@ public class Sc20StreamingParquetTraceReader( /** * A poisonous fragment. */ - private val poison = Pair("\u0000", FlopsHistoryFragment(0, 0, 0, 0.0, 0)) + private val poison = Pair("\u0000", SimTraceWorkload.Fragment(0, 0, 0, 0.0, 0)) /** * The thread to read the records in. @@ -119,7 +119,7 @@ public class Sc20StreamingParquetTraceReader( val cpuUsage = record["cpuUsage"] as Double val flops = record["flops"] as Long - val fragment = FlopsHistoryFragment( + val fragment = SimTraceWorkload.Fragment( tick, flops, duration, @@ -139,12 +139,12 @@ public class Sc20StreamingParquetTraceReader( /** * Fill the buffers with the VMs */ - private fun pull(buffers: Map>>) { + private fun pull(buffers: Map>>) { if (!hasNext) { return } - val fragments = mutableListOf>() + val fragments = mutableListOf>() queue.drainTo(fragments) for ((id, fragment) in fragments) { @@ -167,7 +167,7 @@ public class Sc20StreamingParquetTraceReader( init { val takenIds = mutableSetOf() val entries = mutableMapOf() - val buffers = mutableMapOf>>() + val buffers = mutableMapOf>>() val metaReader = AvroParquetReader.builder(Path(traceFile.absolutePath, "meta.parquet")) .disableCompatibility() @@ -200,10 +200,10 @@ public class Sc20StreamingParquetTraceReader( logger.info("Processing VM $id") - val internalBuffer = mutableListOf() - val externalBuffer = mutableListOf() + val internalBuffer = mutableListOf() + val externalBuffer = mutableListOf() buffers.getOrPut(id) { mutableListOf() }.add(externalBuffer) - val fragments = sequence { + val fragments = sequence { repeat@ while (true) { if (externalBuffer.isEmpty()) { if (hasNext) { @@ -220,7 +220,7 @@ public class Sc20StreamingParquetTraceReader( for (fragment in internalBuffer) { yield(fragment) - if (fragment.tick >= endTime) { + if (fragment.time >= endTime) { break@repeat } } @@ -239,13 +239,15 @@ public class Sc20StreamingParquetTraceReader( uid, "VM Workload $id", UnnamedUser, - VmImage( + SimWorkloadImage( uid, id, - mapOf(IMAGE_PERF_INTERFERENCE_MODEL to relevantPerformanceInterferenceModelItems), - fragments, - maxCores, - requiredMemory + mapOf( + IMAGE_PERF_INTERFERENCE_MODEL to relevantPerformanceInterferenceModelItems, + "cores" to maxCores, + "required-memory" to requiredMemory + ), + SimTraceWorkload(fragments), ) ) diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/WorkloadSampler.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/WorkloadSampler.kt index 19da97bb..e8cc2c36 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/WorkloadSampler.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/WorkloadSampler.kt @@ -23,7 +23,7 @@ package org.opendc.experiments.sc20.trace import mu.KotlinLogging -import org.opendc.compute.core.image.VmImage +import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.core.workload.VmWorkload import org.opendc.experiments.sc20.experiment.model.CompositeWorkload import org.opendc.experiments.sc20.experiment.model.SamplingStrategy @@ -197,13 +197,11 @@ public fun sampleHpcWorkload( */ private fun sample(entry: TraceEntry, i: Int): TraceEntry { val id = UUID.nameUUIDFromBytes("${entry.workload.image.uid}-$i".toByteArray()) - val image = VmImage( + val image = SimWorkloadImage( id, entry.workload.image.name, entry.workload.image.tags, - entry.workload.image.flopsHistory, - entry.workload.image.maxCores, - entry.workload.image.requiredMemory + (entry.workload.image as SimWorkloadImage).workload ) val vmWorkload = entry.workload.copy(uid = id, image = image, name = entry.workload.name) return VmTraceEntry(vmWorkload, entry.submissionTime) diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/resources/log4j2.xml b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/resources/log4j2.xml index 5ce99dfb..8029092e 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/resources/log4j2.xml +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/resources/log4j2.xml @@ -30,7 +30,7 @@ - + diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/test/kotlin/org/opendc/experiments/sc20/Sc20IntegrationTest.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/test/kotlin/org/opendc/experiments/sc20/Sc20IntegrationTest.kt index 230e7f36..ff6e4bb9 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/test/kotlin/org/opendc/experiments/sc20/Sc20IntegrationTest.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/test/kotlin/org/opendc/experiments/sc20/Sc20IntegrationTest.kt @@ -142,12 +142,14 @@ class Sc20IntegrationTest { runSimulation() // Note that these values have been verified beforehand - assertEquals(50, scheduler.submittedVms, "The trace contains 50 VMs") - assertEquals(50, scheduler.finishedVms, "All VMs should finish after a run") - assertEquals(207379117949, monitor.totalRequestedBurst) - assertEquals(203388071813, monitor.totalGrantedBurst) - assertEquals(3991046136, monitor.totalOvercommissionedBurst) - assertEquals(0, monitor.totalInterferedBurst) + assertAll( + { assertEquals(50, scheduler.submittedVms, "The trace contains 50 VMs") }, + { assertEquals(50, scheduler.finishedVms, "All VMs should finish after a run") }, + { assertEquals(207379117949, monitor.totalRequestedBurst) }, + { assertEquals(203388071813, monitor.totalGrantedBurst) }, + { assertEquals(3991046136, monitor.totalOvercommissionedBurst) }, + { assertEquals(0, monitor.totalInterferedBurst) } + ) } @Test diff --git a/simulator/opendc-format/build.gradle.kts b/simulator/opendc-format/build.gradle.kts index 38fcb329..3ab704bd 100644 --- a/simulator/opendc-format/build.gradle.kts +++ b/simulator/opendc-format/build.gradle.kts @@ -31,7 +31,8 @@ dependencies { api(project(":opendc-core")) api(project(":opendc-compute")) api(project(":opendc-workflows")) - api("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8") { + implementation(project(":opendc-simulator:opendc-simulator-compute")) + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8") { exclude("org.jetbrains.kotlin", module = "kotlin-reflect") } implementation(kotlin("reflect")) diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc18/Sc18EnvironmentReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc18/Sc18EnvironmentReader.kt index 027548a8..3fde1a6c 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc18/Sc18EnvironmentReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc18/Sc18EnvironmentReader.kt @@ -26,10 +26,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue import kotlinx.coroutines.CoroutineScope -import org.opendc.compute.core.MemoryUnit -import org.opendc.compute.core.ProcessingNode -import org.opendc.compute.core.ProcessingUnit -import org.opendc.compute.metal.driver.SimpleBareMetalDriver +import org.opendc.compute.metal.driver.SimBareMetalDriver import org.opendc.compute.metal.service.ProvisioningService import org.opendc.compute.metal.service.SimpleProvisioningService import org.opendc.core.Environment @@ -37,6 +34,10 @@ import org.opendc.core.Platform import org.opendc.core.Zone import org.opendc.core.services.ServiceRegistry import org.opendc.format.environment.EnvironmentReader +import org.opendc.simulator.compute.SimMachineModel +import org.opendc.simulator.compute.model.MemoryUnit +import org.opendc.simulator.compute.model.ProcessingNode +import org.opendc.simulator.compute.model.ProcessingUnit import java.io.InputStream import java.time.Clock import java.util.* @@ -74,14 +75,13 @@ public class Sc18EnvironmentReader(input: InputStream, mapper: ObjectMapper = ja else -> throw IllegalArgumentException("The cpu id $id is not recognized") } } - SimpleBareMetalDriver( + SimBareMetalDriver( coroutineScope, clock, UUID.randomUUID(), "node-${counter++}", emptyMap(), - cores, - listOf(MemoryUnit("", "", 2300.0, 16000)) + SimMachineModel(cores, listOf(MemoryUnit("", "", 2300.0, 16000))) ) } } diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20ClusterEnvironmentReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20ClusterEnvironmentReader.kt index 634d5de6..63539d61 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20ClusterEnvironmentReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20ClusterEnvironmentReader.kt @@ -23,11 +23,8 @@ package org.opendc.format.environment.sc20 import kotlinx.coroutines.CoroutineScope -import org.opendc.compute.core.MemoryUnit -import org.opendc.compute.core.ProcessingNode -import org.opendc.compute.core.ProcessingUnit import org.opendc.compute.metal.NODE_CLUSTER -import org.opendc.compute.metal.driver.SimpleBareMetalDriver +import org.opendc.compute.metal.driver.SimBareMetalDriver import org.opendc.compute.metal.power.LinearLoadPowerModel import org.opendc.compute.metal.service.ProvisioningService import org.opendc.compute.metal.service.SimpleProvisioningService @@ -36,6 +33,10 @@ import org.opendc.core.Platform import org.opendc.core.Zone import org.opendc.core.services.ServiceRegistry import org.opendc.format.environment.EnvironmentReader +import org.opendc.simulator.compute.SimMachineModel +import org.opendc.simulator.compute.model.MemoryUnit +import org.opendc.simulator.compute.model.ProcessingNode +import org.opendc.simulator.compute.model.ProcessingUnit import java.io.File import java.io.FileInputStream import java.io.InputStream @@ -68,7 +69,7 @@ public class Sc20ClusterEnvironmentReader( var memoryPerHost: Long var coresPerHost: Int - val nodes = mutableListOf() + val nodes = mutableListOf() val random = Random(0) input.bufferedReader().use { reader -> @@ -102,19 +103,21 @@ public class Sc20ClusterEnvironmentReader( repeat(numberOfHosts) { nodes.add( - SimpleBareMetalDriver( + SimBareMetalDriver( coroutineScope, clock, UUID(random.nextLong(), random.nextLong()), "node-$clusterId-$it", mapOf(NODE_CLUSTER to clusterId), - List(coresPerHost) { coreId -> - ProcessingUnit(unknownProcessingNode, coreId, speed) - }, + SimMachineModel( + List(coresPerHost) { coreId -> + ProcessingUnit(unknownProcessingNode, coreId, speed) + }, + listOf(unknownMemoryUnit) + ), // For now we assume a simple linear load model with an idle draw of ~200W and a maximum // power draw of 350W. // Source: https://stackoverflow.com/questions/6128960 - listOf(unknownMemoryUnit), LinearLoadPowerModel(200.0, 350.0) ) ) diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20EnvironmentReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20EnvironmentReader.kt index 9fd38d13..36136888 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20EnvironmentReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20EnvironmentReader.kt @@ -26,10 +26,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue import kotlinx.coroutines.CoroutineScope -import org.opendc.compute.core.MemoryUnit -import org.opendc.compute.core.ProcessingNode -import org.opendc.compute.core.ProcessingUnit -import org.opendc.compute.metal.driver.SimpleBareMetalDriver +import org.opendc.compute.metal.driver.SimBareMetalDriver import org.opendc.compute.metal.power.LinearLoadPowerModel import org.opendc.compute.metal.service.ProvisioningService import org.opendc.compute.metal.service.SimpleProvisioningService @@ -38,6 +35,10 @@ import org.opendc.core.Platform import org.opendc.core.Zone import org.opendc.core.services.ServiceRegistry import org.opendc.format.environment.EnvironmentReader +import org.opendc.simulator.compute.SimMachineModel +import org.opendc.simulator.compute.model.MemoryUnit +import org.opendc.simulator.compute.model.ProcessingNode +import org.opendc.simulator.compute.model.ProcessingUnit import java.io.InputStream import java.time.Clock import java.util.* @@ -80,17 +81,16 @@ public class Sc20EnvironmentReader(input: InputStream, mapper: ObjectMapper = ja else -> throw IllegalArgumentException("The cpu id $id is not recognized") } } - SimpleBareMetalDriver( + SimBareMetalDriver( coroutineScope, clock, UUID.randomUUID(), "node-${counter++}", emptyMap(), - cores, + SimMachineModel(cores, memories), // For now we assume a simple linear load model with an idle draw of ~200W and a maximum // power draw of 350W. // Source: https://stackoverflow.com/questions/6128960 - memories, LinearLoadPowerModel(200.0, 350.0) ) } diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/bitbrains/BitbrainsTraceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/bitbrains/BitbrainsTraceReader.kt index 89d4246d..f98a5a6d 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/bitbrains/BitbrainsTraceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/bitbrains/BitbrainsTraceReader.kt @@ -22,14 +22,14 @@ package org.opendc.format.trace.bitbrains -import org.opendc.compute.core.image.FlopsHistoryFragment -import org.opendc.compute.core.image.VmImage +import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.core.workload.IMAGE_PERF_INTERFERENCE_MODEL import org.opendc.compute.core.workload.PerformanceInterferenceModel import org.opendc.compute.core.workload.VmWorkload import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader +import org.opendc.simulator.compute.workload.SimTraceWorkload import java.io.BufferedReader import java.io.File import java.io.FileReader @@ -66,7 +66,7 @@ public class BitbrainsTraceReader( .filterNot { it.isDirectory } .forEach { vmFile -> println(vmFile) - val flopsHistory = mutableListOf() + val flopsHistory = mutableListOf() var vmId = -1L var cores = -1 var requiredMemory = -1L @@ -99,11 +99,11 @@ public class BitbrainsTraceReader( val flops: Long = (cpuUsage * 5 * 60 * cores).toLong() if (flopsHistory.isEmpty()) { - flopsHistory.add(FlopsHistoryFragment(timestamp, flops, traceInterval, cpuUsage, cores)) + flopsHistory.add(SimTraceWorkload.Fragment(timestamp, flops, traceInterval, cpuUsage, cores)) } else { if (flopsHistory.last().flops != flops) { flopsHistory.add( - FlopsHistoryFragment( + SimTraceWorkload.Fragment( timestamp, flops, traceInterval, @@ -114,8 +114,8 @@ public class BitbrainsTraceReader( } else { val oldFragment = flopsHistory.removeAt(flopsHistory.size - 1) flopsHistory.add( - FlopsHistoryFragment( - oldFragment.tick, + SimTraceWorkload.Fragment( + oldFragment.time, oldFragment.flops + flops, oldFragment.duration + traceInterval, cpuUsage, @@ -139,17 +139,19 @@ public class BitbrainsTraceReader( uuid, "VM Workload $vmId", UnnamedUser, - VmImage( + SimWorkloadImage( uuid, vmId.toString(), - mapOf(IMAGE_PERF_INTERFERENCE_MODEL to relevantPerformanceInterferenceModelItems), - flopsHistory.asSequence(), - cores, - requiredMemory + mapOf( + IMAGE_PERF_INTERFERENCE_MODEL to relevantPerformanceInterferenceModelItems, + "cores" to cores, + "required-memory" to requiredMemory + ), + SimTraceWorkload(flopsHistory.asSequence()) ) ) entries[vmId] = TraceEntryImpl( - flopsHistory.firstOrNull()?.tick ?: -1, + flopsHistory.firstOrNull()?.time ?: -1, vmWorkload ) } diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/gwf/GwfTraceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/gwf/GwfTraceReader.kt index 9f8fb558..53bc3956 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/gwf/GwfTraceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/gwf/GwfTraceReader.kt @@ -22,10 +22,11 @@ package org.opendc.format.trace.gwf -import org.opendc.compute.core.image.FlopsApplicationImage +import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader +import org.opendc.simulator.compute.workload.SimFlopsWorkload import org.opendc.workflows.workload.Job import org.opendc.workflows.workload.Task import org.opendc.workflows.workload.WORKFLOW_TASK_DEADLINE @@ -137,7 +138,7 @@ public class GwfTraceReader(reader: BufferedReader) : TraceReader { val task = Task( UUID(0L, taskId), "", - FlopsApplicationImage(UUID.randomUUID(), "", emptyMap(), flops, cores), + SimWorkloadImage(UUID.randomUUID(), "", emptyMap(), SimFlopsWorkload(flops, cores)), HashSet(), mapOf(WORKFLOW_TASK_DEADLINE to runtime) ) diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt index bfcc30ce..560ad846 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt @@ -22,14 +22,14 @@ package org.opendc.format.trace.sc20 -import org.opendc.compute.core.image.FlopsHistoryFragment -import org.opendc.compute.core.image.VmImage +import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.core.workload.IMAGE_PERF_INTERFERENCE_MODEL import org.opendc.compute.core.workload.PerformanceInterferenceModel import org.opendc.compute.core.workload.VmWorkload import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader +import org.opendc.simulator.compute.workload.SimTraceWorkload import java.io.BufferedReader import java.io.File import java.io.FileReader @@ -109,7 +109,7 @@ public class Sc20TraceReader( } val flopsFragments = sequence { - var last: FlopsHistoryFragment? = null + var last: SimTraceWorkload.Fragment? = null BufferedReader(FileReader(vmFile)).use { reader -> reader.lineSequence() @@ -130,8 +130,8 @@ public class Sc20TraceReader( last = if (last != null && last!!.flops == 0L && flops == 0L) { val oldFragment = last!! - FlopsHistoryFragment( - oldFragment.tick, + SimTraceWorkload.Fragment( + oldFragment.time, oldFragment.flops + flops, oldFragment.duration + traceInterval, cpuUsage, @@ -139,7 +139,7 @@ public class Sc20TraceReader( ) } else { val fragment = - FlopsHistoryFragment(timestamp, flops, traceInterval, cpuUsage, cores) + SimTraceWorkload.Fragment(timestamp, flops, traceInterval, cpuUsage, cores) if (last != null) { yield(last!!) } @@ -165,13 +165,15 @@ public class Sc20TraceReader( uuid, "VM Workload $vmId", UnnamedUser, - VmImage( + SimWorkloadImage( uuid, vmId, - mapOf(IMAGE_PERF_INTERFERENCE_MODEL to relevantPerformanceInterferenceModelItems), - flopsFragments.asSequence(), - maxCores, - requiredMemory + mapOf( + IMAGE_PERF_INTERFERENCE_MODEL to relevantPerformanceInterferenceModelItems, + "cores" to cores, + "required-memory" to requiredMemory + ), + SimTraceWorkload(flopsFragments.asSequence()) ) ) entries[uuid] = TraceEntryImpl( diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/swf/SwfTraceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/swf/SwfTraceReader.kt index 13bd7ac3..5f312d01 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/swf/SwfTraceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/swf/SwfTraceReader.kt @@ -22,12 +22,12 @@ package org.opendc.format.trace.swf -import org.opendc.compute.core.image.FlopsHistoryFragment -import org.opendc.compute.core.image.VmImage +import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.core.workload.VmWorkload import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader +import org.opendc.simulator.compute.workload.SimTraceWorkload import java.io.BufferedReader import java.io.File import java.io.FileReader @@ -103,7 +103,7 @@ public class SwfTraceReader( memory /= 1000 // convert KB to MB } - val flopsHistory = mutableListOf() + val flopsHistory = mutableListOf() // Insert waiting time slices @@ -112,7 +112,7 @@ public class SwfTraceReader( if (waitTime >= sliceDuration) { for (tick in submitTime until (submitTime + waitTime - sliceDuration) step sliceDuration) { flopsHistory.add( - FlopsHistoryFragment( + SimTraceWorkload.Fragment( tick * 1000L, 0L, sliceDuration * 1000L, @@ -137,7 +137,7 @@ public class SwfTraceReader( step sliceDuration ) { flopsHistory.add( - FlopsHistoryFragment( + SimTraceWorkload.Fragment( tick * 1000L, flopsFullSlice / sliceDuration, sliceDuration * 1000L, @@ -149,7 +149,7 @@ public class SwfTraceReader( if (runtimePartialSliceRemainder > 0) { flopsHistory.add( - FlopsHistoryFragment( + SimTraceWorkload.Fragment( submitTime + (slicedWaitTime + runTime - runtimePartialSliceRemainder), flopsPartialSlice, sliceDuration, @@ -164,13 +164,14 @@ public class SwfTraceReader( uuid, "SWF Workload $jobNumber", UnnamedUser, - VmImage( + SimWorkloadImage( uuid, jobNumber.toString(), - emptyMap(), - flopsHistory.asSequence(), - cores, - memory + mapOf( + "cores" to cores, + "required-memory" to memory + ), + SimTraceWorkload(flopsHistory.asSequence()) ) ) diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/wtf/WtfTraceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/wtf/WtfTraceReader.kt index de6647d0..09f2764b 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/wtf/WtfTraceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/wtf/WtfTraceReader.kt @@ -25,10 +25,11 @@ package org.opendc.format.trace.wtf import org.apache.avro.generic.GenericRecord import org.apache.hadoop.fs.Path import org.apache.parquet.avro.AvroParquetReader -import org.opendc.compute.core.image.FlopsApplicationImage +import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader +import org.opendc.simulator.compute.workload.SimFlopsWorkload import org.opendc.workflows.workload.Job import org.opendc.workflows.workload.Task import org.opendc.workflows.workload.WORKFLOW_TASK_DEADLINE @@ -81,7 +82,7 @@ public class WtfTraceReader(path: String) : TraceReader { val task = Task( UUID(0L, taskId), "", - FlopsApplicationImage(UUID.randomUUID(), "", emptyMap(), flops, cores), + SimWorkloadImage(UUID.randomUUID(), "", emptyMap(), SimFlopsWorkload(flops, cores)), HashSet(), mapOf(WORKFLOW_TASK_DEADLINE to runtime) ) diff --git a/simulator/opendc-format/src/test/kotlin/org/opendc/format/trace/swf/SwfTraceReaderTest.kt b/simulator/opendc-format/src/test/kotlin/org/opendc/format/trace/swf/SwfTraceReaderTest.kt index 40132ad3..78066c61 100644 --- a/simulator/opendc-format/src/test/kotlin/org/opendc/format/trace/swf/SwfTraceReaderTest.kt +++ b/simulator/opendc-format/src/test/kotlin/org/opendc/format/trace/swf/SwfTraceReaderTest.kt @@ -24,6 +24,8 @@ package org.opendc.format.trace.swf import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test +import org.opendc.compute.core.image.SimWorkloadImage +import org.opendc.simulator.compute.workload.SimTraceWorkload import java.io.File class SwfTraceReaderTest { @@ -33,13 +35,13 @@ class SwfTraceReaderTest { var entry = reader.next() assertEquals(0, entry.submissionTime) // 1961 slices for waiting, 3 full and 1 partial running slices - assertEquals(1965, entry.workload.image.flopsHistory.toList().size) + assertEquals(1965, ((entry.workload.image as SimWorkloadImage).workload as SimTraceWorkload).trace.toList().size) entry = reader.next() assertEquals(164472, entry.submissionTime) // 1188 slices for waiting, 0 full and 1 partial running slices - assertEquals(1189, entry.workload.image.flopsHistory.toList().size) - assertEquals(5_100_000L, entry.workload.image.flopsHistory.toList().last().flops) - assertEquals(0.25, entry.workload.image.flopsHistory.toList().last().usage) + assertEquals(1189, ((entry.workload.image as SimWorkloadImage).workload as SimTraceWorkload).trace.toList().size) + assertEquals(5_100_000L, ((entry.workload.image as SimWorkloadImage).workload as SimTraceWorkload).trace.toList().last().flops) + assertEquals(0.25, ((entry.workload.image as SimWorkloadImage).workload as SimTraceWorkload).trace.toList().last().usage) } } diff --git a/simulator/opendc-runner-web/build.gradle.kts b/simulator/opendc-runner-web/build.gradle.kts index 594e8197..9e2c5277 100644 --- a/simulator/opendc-runner-web/build.gradle.kts +++ b/simulator/opendc-runner-web/build.gradle.kts @@ -38,6 +38,7 @@ dependencies { implementation(project(":opendc-format")) implementation(project(":opendc-experiments:opendc-experiments-sc20")) implementation(project(":opendc-simulator:opendc-simulator-core")) + implementation(project(":opendc-simulator:opendc-simulator-compute")) implementation("com.github.ajalt:clikt:2.8.0") implementation("io.github.microutils:kotlin-logging:1.7.10") diff --git a/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/TopologyParser.kt b/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/TopologyParser.kt index de9ece75..cdcf1b08 100644 --- a/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/TopologyParser.kt +++ b/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/TopologyParser.kt @@ -31,11 +31,8 @@ import com.mongodb.client.model.Projections import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import org.bson.Document -import org.opendc.compute.core.MemoryUnit -import org.opendc.compute.core.ProcessingNode -import org.opendc.compute.core.ProcessingUnit import org.opendc.compute.metal.NODE_CLUSTER -import org.opendc.compute.metal.driver.SimpleBareMetalDriver +import org.opendc.compute.metal.driver.SimBareMetalDriver import org.opendc.compute.metal.power.LinearLoadPowerModel import org.opendc.compute.metal.service.ProvisioningService import org.opendc.compute.metal.service.SimpleProvisioningService @@ -44,6 +41,10 @@ import org.opendc.core.Platform import org.opendc.core.Zone import org.opendc.core.services.ServiceRegistry import org.opendc.format.environment.EnvironmentReader +import org.opendc.simulator.compute.SimMachineModel +import org.opendc.simulator.compute.model.MemoryUnit +import org.opendc.simulator.compute.model.ProcessingNode +import org.opendc.simulator.compute.model.ProcessingUnit import java.time.Clock import java.util.* @@ -55,7 +56,7 @@ public class TopologyParser(private val collection: MongoCollection, p * Parse the topology with the specified [id]. */ override suspend fun construct(coroutineScope: CoroutineScope, clock: Clock): Environment { - val nodes = mutableListOf() + val nodes = mutableListOf() val random = Random(0) for (machine in fetchMachines(id)) { @@ -81,14 +82,13 @@ public class TopologyParser(private val collection: MongoCollection, p ) } nodes.add( - SimpleBareMetalDriver( + SimBareMetalDriver( coroutineScope, clock, UUID(random.nextLong(), random.nextLong()), "node-$clusterId-$position", mapOf(NODE_CLUSTER to clusterId), - processors, - memoryUnits, + SimMachineModel(processors, memoryUnits), // For now we assume a simple linear load model with an idle draw of ~200W and a maximum // power draw of 350W. // Source: https://stackoverflow.com/questions/6128960 diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt index 7c2cfbe3..55eda70f 100644 --- a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt @@ -103,14 +103,15 @@ public class SimHypervisor( * Run the scheduling process of the hypervisor. */ override suspend fun run(ctx: SimExecutionContext) { - val maxUsage = ctx.machine.cpus.sumByDouble { it.frequency } - val pCPUs = ctx.machine.cpus.indices.sortedBy { ctx.machine.cpus[it].frequency } + val model = ctx.machine + val maxUsage = model.cpus.sumByDouble { it.frequency } + val pCPUs = model.cpus.indices.sortedBy { model.cpus[it].frequency } val vms = mutableSetOf() val vcpus = mutableListOf() - val usage = DoubleArray(ctx.machine.cpus.size) - val burst = LongArray(ctx.machine.cpus.size) + val usage = DoubleArray(model.cpus.size) + val burst = LongArray(model.cpus.size) fun process(command: SchedulerCommand) { when (command) { @@ -180,13 +181,16 @@ public class SimHypervisor( duration = min(duration, req.burst / grantedUsage) } + // XXX We set the minimum duration to 5 minutes here to prevent the rounding issues that are occurring with the FLOPs. + duration = 300.0 + val totalAllocatedUsage = maxUsage - availableUsage var totalAllocatedBurst = 0L availableUsage = totalAllocatedUsage // Divide the requests over the available capacity of the pCPUs fairly for (i in pCPUs) { - val maxCpuUsage = ctx.machine.cpus[i].frequency + val maxCpuUsage = model.cpus[i].frequency val fraction = maxCpuUsage / maxUsage val grantedUsage = min(maxCpuUsage, totalAllocatedUsage * fraction) val grantedBurst = ceil(duration * grantedUsage).toLong() diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt index e7eaa8bf..7b1ddf32 100644 --- a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt @@ -29,7 +29,7 @@ import kotlin.math.min * A [SimWorkload] that replays a workload trace consisting of multiple fragments, each indicating the resource * consumption for some period of time. */ -public class SimTraceWorkload(private val trace: Sequence) : SimWorkload { +public class SimTraceWorkload(public val trace: Sequence) : SimWorkload { override suspend fun run(ctx: SimExecutionContext) { var offset = ctx.clock.millis() diff --git a/simulator/opendc-workflows/build.gradle.kts b/simulator/opendc-workflows/build.gradle.kts index efffe7a1..b30ecd39 100644 --- a/simulator/opendc-workflows/build.gradle.kts +++ b/simulator/opendc-workflows/build.gradle.kts @@ -34,6 +34,10 @@ dependencies { testImplementation(project(":opendc-simulator:opendc-simulator-core")) testImplementation(project(":opendc-format")) + testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8") { + exclude("org.jetbrains.kotlin", module = "kotlin-reflect") + } + testImplementation(kotlin("reflect")) testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Library.JUNIT_JUPITER}") testImplementation("org.junit.platform:junit-platform-launcher:${Library.JUNIT_PLATFORM}") -- cgit v1.2.3 From 47f803afc14a50c467d2f5f7ff406824428223f7 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Sun, 4 Oct 2020 17:17:23 +0200 Subject: Reimplement performance interference in opendc-simulator-compute This change reimplements the performance interference model originally implemented for the SimpleVirtDriver class, for SimHypervisor. --- .../core/workload/PerformanceInterferenceModel.kt | 134 --------------------- .../opendc/compute/virt/driver/SimVirtDriver.kt | 21 +++- .../sc20/experiment/ExperimentHelpers.kt | 2 +- .../sc20/trace/Sc20ParquetTraceReader.kt | 4 +- .../sc20/trace/Sc20StreamingParquetTraceReader.kt | 4 +- .../trace/PerformanceInterferenceModelReader.kt | 2 +- .../format/trace/bitbrains/BitbrainsTraceReader.kt | 4 +- .../sc20/Sc20PerformanceInterferenceReader.kt | 9 +- .../opendc/format/trace/sc20/Sc20TraceReader.kt | 4 +- .../org/opendc/simulator/compute/SimHypervisor.kt | 22 ++-- .../interference/PerformanceInterferenceModel.kt | 134 +++++++++++++++++++++ .../opendc/simulator/compute/SimHypervisorTest.kt | 1 - 12 files changed, 180 insertions(+), 161 deletions(-) delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/workload/PerformanceInterferenceModel.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/interference/PerformanceInterferenceModel.kt diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/workload/PerformanceInterferenceModel.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/workload/PerformanceInterferenceModel.kt deleted file mode 100644 index 51f62197..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/workload/PerformanceInterferenceModel.kt +++ /dev/null @@ -1,134 +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.core.workload - -import java.util.* -import kotlin.random.Random - -/** - * Meta-data key for the [PerformanceInterferenceModel] of an image. - */ -public const val IMAGE_PERF_INTERFERENCE_MODEL: String = "image:performance-interference" - -/** - * Performance Interference Model describing the variability incurred by different sets of workloads if colocated. - * - * @param items The [PerformanceInterferenceModelItem]s that make up this model. - */ -public class PerformanceInterferenceModel( - public val items: SortedSet, - private val random: Random = Random(0) -) { - private var intersectingItems: List = emptyList() - private val colocatedWorkloads = TreeMap() - - /** - * Indicate that a VM has started. - */ - public fun onStart(name: String) { - colocatedWorkloads.merge(name, 1, Int::plus) - intersectingItems = items.filter { item -> doesMatch(item) } - } - - /** - * Indicate that a VM has stopped. - */ - public fun onStop(name: String) { - colocatedWorkloads.computeIfPresent(name) { _, v -> (v - 1).takeUnless { it == 0 } } - intersectingItems = items.filter { item -> doesMatch(item) } - } - - /** - * Compute the performance interference based on the current server load. - */ - public fun apply(currentServerLoad: Double): Double { - if (intersectingItems.isEmpty()) { - return 1.0 - } - val score = intersectingItems - .firstOrNull { it.minServerLoad <= currentServerLoad } - - // Apply performance penalty to (on average) only one of the VMs - return if (score != null && random.nextInt(score.workloadNames.size) == 0) { - score.performanceScore - } else { - 1.0 - } - } - - private fun doesMatch(item: PerformanceInterferenceModelItem): Boolean { - var count = 0 - for ( - name in item.workloadNames.subSet( - colocatedWorkloads.firstKey(), - colocatedWorkloads.lastKey() + "\u0000" - ) - ) { - count += colocatedWorkloads.getOrDefault(name, 0) - if (count > 1) - return true - } - return false - } -} - -/** - * Model describing how a specific set of workloads causes performance variability for each workload. - * - * @param workloadNames The names of the workloads that together cause performance variability for each workload in the set. - * @param minServerLoad The minimum total server load at which this interference is activated and noticeable. - * @param performanceScore The performance score that should be applied to each workload's performance. 1 means no - * influence, <1 means that performance degrades, and >1 means that performance improves. - */ -public data class PerformanceInterferenceModelItem( - public val workloadNames: SortedSet, - public val minServerLoad: Double, - public val performanceScore: Double -) : Comparable { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as PerformanceInterferenceModelItem - - if (workloadNames != other.workloadNames) return false - - return true - } - - override fun hashCode(): Int = workloadNames.hashCode() - - override fun compareTo(other: PerformanceInterferenceModelItem): Int { - var cmp = performanceScore.compareTo(other.performanceScore) - if (cmp != 0) { - return cmp - } - - cmp = minServerLoad.compareTo(other.minServerLoad) - if (cmp != 0) { - return cmp - } - - return hashCode().compareTo(other.hashCode()) - } -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt index 6e0df93f..357ad714 100644 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt +++ b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt @@ -35,6 +35,8 @@ import org.opendc.core.services.ServiceRegistry import org.opendc.simulator.compute.SimExecutionContext import org.opendc.simulator.compute.SimHypervisor import org.opendc.simulator.compute.SimMachine +import org.opendc.simulator.compute.interference.IMAGE_PERF_INTERFERENCE_MODEL +import org.opendc.simulator.compute.interference.PerformanceInterferenceModel import org.opendc.simulator.compute.workload.SimWorkload import org.opendc.utils.flow.EventFlow import java.time.Clock @@ -124,15 +126,31 @@ public class SimVirtDriver( events ) availableMemory -= requiredMemory - vms.add(VirtualMachine(server, events, hypervisor.createMachine(ctx.machine))) + val vm = VirtualMachine(server, events, hypervisor.createMachine(ctx.machine)) + vms.add(vm) + vmStarted(vm) eventFlow.emit(HypervisorEvent.VmsUpdated(this, vms.size, availableMemory)) return server } + 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, machine: SimMachine) { + val performanceInterferenceModel: PerformanceInterferenceModel? = server.image.tags[IMAGE_PERF_INTERFERENCE_MODEL] as? PerformanceInterferenceModel? + val job = coroutineScope.launch { val workload = object : SimWorkload { override suspend fun run(ctx: SimExecutionContext) { @@ -176,6 +194,7 @@ public class SimVirtDriver( 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() } diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt index 7b6c4880..3ff21558 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt @@ -34,7 +34,6 @@ import kotlinx.coroutines.launch import mu.KotlinLogging import org.opendc.compute.core.Flavor import org.opendc.compute.core.ServerEvent -import org.opendc.compute.core.workload.PerformanceInterferenceModel import org.opendc.compute.core.workload.VmWorkload import org.opendc.compute.metal.NODE_CLUSTER import org.opendc.compute.metal.driver.BareMetalDriver @@ -51,6 +50,7 @@ import org.opendc.experiments.sc20.experiment.monitor.ExperimentMonitor import org.opendc.experiments.sc20.trace.Sc20StreamingParquetTraceReader import org.opendc.format.environment.EnvironmentReader import org.opendc.format.trace.TraceReader +import org.opendc.simulator.compute.interference.PerformanceInterferenceModel import java.io.File import java.time.Clock import kotlin.math.ln diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20ParquetTraceReader.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20ParquetTraceReader.kt index 39b00a61..6ee53aec 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20ParquetTraceReader.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20ParquetTraceReader.kt @@ -23,13 +23,13 @@ package org.opendc.experiments.sc20.trace import org.opendc.compute.core.image.SimWorkloadImage -import org.opendc.compute.core.workload.IMAGE_PERF_INTERFERENCE_MODEL -import org.opendc.compute.core.workload.PerformanceInterferenceModel import org.opendc.compute.core.workload.VmWorkload import org.opendc.experiments.sc20.experiment.model.CompositeWorkload import org.opendc.experiments.sc20.experiment.model.Workload import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader +import org.opendc.simulator.compute.interference.IMAGE_PERF_INTERFERENCE_MODEL +import org.opendc.simulator.compute.interference.PerformanceInterferenceModel import java.util.TreeSet /** diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20StreamingParquetTraceReader.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20StreamingParquetTraceReader.kt index 2ec01606..0afdc5fd 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20StreamingParquetTraceReader.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20StreamingParquetTraceReader.kt @@ -32,12 +32,12 @@ import org.apache.parquet.filter2.predicate.Statistics import org.apache.parquet.filter2.predicate.UserDefinedPredicate import org.apache.parquet.io.api.Binary import org.opendc.compute.core.image.SimWorkloadImage -import org.opendc.compute.core.workload.IMAGE_PERF_INTERFERENCE_MODEL -import org.opendc.compute.core.workload.PerformanceInterferenceModel import org.opendc.compute.core.workload.VmWorkload import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader +import org.opendc.simulator.compute.interference.IMAGE_PERF_INTERFERENCE_MODEL +import org.opendc.simulator.compute.interference.PerformanceInterferenceModel import org.opendc.simulator.compute.workload.SimTraceWorkload import java.io.File import java.io.Serializable diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/PerformanceInterferenceModelReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/PerformanceInterferenceModelReader.kt index 7f60cd90..f30e64cf 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/PerformanceInterferenceModelReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/PerformanceInterferenceModelReader.kt @@ -22,7 +22,7 @@ package org.opendc.format.trace -import org.opendc.compute.core.workload.PerformanceInterferenceModel +import org.opendc.simulator.compute.interference.PerformanceInterferenceModel import java.io.Closeable import kotlin.random.Random diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/bitbrains/BitbrainsTraceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/bitbrains/BitbrainsTraceReader.kt index f98a5a6d..6292a9cc 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/bitbrains/BitbrainsTraceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/bitbrains/BitbrainsTraceReader.kt @@ -23,12 +23,12 @@ package org.opendc.format.trace.bitbrains import org.opendc.compute.core.image.SimWorkloadImage -import org.opendc.compute.core.workload.IMAGE_PERF_INTERFERENCE_MODEL -import org.opendc.compute.core.workload.PerformanceInterferenceModel import org.opendc.compute.core.workload.VmWorkload import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader +import org.opendc.simulator.compute.interference.IMAGE_PERF_INTERFERENCE_MODEL +import org.opendc.simulator.compute.interference.PerformanceInterferenceModel import org.opendc.simulator.compute.workload.SimTraceWorkload import java.io.BufferedReader import java.io.File diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20PerformanceInterferenceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20PerformanceInterferenceReader.kt index fd8cdfce..4267737d 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20PerformanceInterferenceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20PerformanceInterferenceReader.kt @@ -25,9 +25,8 @@ package org.opendc.format.trace.sc20 import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue -import org.opendc.compute.core.workload.PerformanceInterferenceModel -import org.opendc.compute.core.workload.PerformanceInterferenceModelItem import org.opendc.format.trace.PerformanceInterferenceModelReader +import org.opendc.simulator.compute.interference.PerformanceInterferenceModel import java.io.InputStream import java.util.* import kotlin.random.Random @@ -43,13 +42,13 @@ public class Sc20PerformanceInterferenceReader(input: InputStream, mapper: Objec /** * The computed value from the file. */ - private val items: Map> + private val items: Map> init { val entries: List = mapper.readValue(input) - val res = mutableMapOf>() + val res = mutableMapOf>() for (entry in entries) { - val item = PerformanceInterferenceModelItem(TreeSet(entry.vms), entry.minServerLoad, entry.performanceScore) + val item = PerformanceInterferenceModel.Item(TreeSet(entry.vms), entry.minServerLoad, entry.performanceScore) for (workload in entry.vms) { res.computeIfAbsent(workload) { TreeSet() }.add(item) } diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt index 560ad846..558003a7 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt @@ -23,12 +23,12 @@ package org.opendc.format.trace.sc20 import org.opendc.compute.core.image.SimWorkloadImage -import org.opendc.compute.core.workload.IMAGE_PERF_INTERFERENCE_MODEL -import org.opendc.compute.core.workload.PerformanceInterferenceModel import org.opendc.compute.core.workload.VmWorkload import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader +import org.opendc.simulator.compute.interference.IMAGE_PERF_INTERFERENCE_MODEL +import org.opendc.simulator.compute.interference.PerformanceInterferenceModel import org.opendc.simulator.compute.workload.SimTraceWorkload import java.io.BufferedReader import java.io.File diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt index 55eda70f..6087227b 100644 --- a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt @@ -30,6 +30,7 @@ import kotlinx.coroutines.intrinsics.startCoroutineCancellable import kotlinx.coroutines.selects.SelectClause0 import kotlinx.coroutines.selects.SelectInstance import kotlinx.coroutines.selects.select +import org.opendc.simulator.compute.interference.PerformanceInterferenceModel import org.opendc.simulator.compute.model.ProcessingUnit import org.opendc.simulator.compute.workload.SimWorkload import java.time.Clock @@ -69,15 +70,16 @@ public class SimHypervisor( * * @param model The machine to create. */ - public fun createMachine(model: SimMachineModel): SimMachine { - val vmCtx = VmExecutionContext(model) + public fun createMachine(model: SimMachineModel, performanceInterferenceModel: PerformanceInterferenceModel? = null): SimMachine { + val vm = VmSession(model, performanceInterferenceModel) + val vmCtx = VmExecutionContext(vm) return object : SimMachine { override val model: SimMachineModel get() = vmCtx.machine override val usage: StateFlow - get() = vmCtx.session.usage + get() = vm.usage /** * The current active workload. @@ -187,6 +189,7 @@ public class SimHypervisor( val totalAllocatedUsage = maxUsage - availableUsage var totalAllocatedBurst = 0L availableUsage = totalAllocatedUsage + val serverLoad = totalAllocatedUsage / maxUsage // Divide the requests over the available capacity of the pCPUs fairly for (i in pCPUs) { @@ -232,7 +235,7 @@ public class SimHypervisor( val vm = vmIterator.next() // Apply performance interference model - val performanceScore = 1.0 // TODO Performance interference + val performanceScore = vm.performanceInterferenceModel?.apply(serverLoad) ?: 1.0 var hasFinished = false for (vcpu in vm.vcpus) { @@ -330,6 +333,7 @@ public class SimHypervisor( @OptIn(InternalCoroutinesApi::class) private data class VmSession( val model: SimMachineModel, + val performanceInterferenceModel: PerformanceInterferenceModel? = null, var triggerMode: SimExecutionContext.TriggerMode = SimExecutionContext.TriggerMode.FIRST, var merge: (SimExecutionContext.Slice, SimExecutionContext.Slice) -> SimExecutionContext.Slice = { _, r -> r }, var select: () -> Unit = {} @@ -491,12 +495,10 @@ public class SimHypervisor( * The execution context in which a VM runs. * */ - private inner class VmExecutionContext(override val machine: SimMachineModel) : - SimExecutionContext, - DisposableHandle { - private var finalized: Boolean = false - private var initialized: Boolean = false - val session: VmSession = VmSession(machine) + private inner class VmExecutionContext(val session: VmSession) : + SimExecutionContext, DisposableHandle { + override val machine: SimMachineModel + get() = session.model override val clock: Clock get() = this@SimHypervisor.clock diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/interference/PerformanceInterferenceModel.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/interference/PerformanceInterferenceModel.kt new file mode 100644 index 00000000..4c409887 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/interference/PerformanceInterferenceModel.kt @@ -0,0 +1,134 @@ +/* + * 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.simulator.compute.interference + +import java.util.* +import kotlin.random.Random + +/** + * Meta-data key for the [PerformanceInterferenceModel] of an image. + */ +public const val IMAGE_PERF_INTERFERENCE_MODEL: String = "image:performance-interference" + +/** + * Performance Interference Model describing the variability incurred by different sets of workloads if colocated. + * + * @param items The [PerformanceInterferenceModel.Item]s that make up this model. + */ +public class PerformanceInterferenceModel( + public val items: SortedSet, + private val random: Random = Random(0) +) { + private var intersectingItems: List = emptyList() + private val colocatedWorkloads = TreeMap() + + /** + * Indicate that a VM has started. + */ + public fun onStart(name: String) { + colocatedWorkloads.merge(name, 1, Int::plus) + intersectingItems = items.filter { item -> doesMatch(item) } + } + + /** + * Indicate that a VM has stopped. + */ + public fun onStop(name: String) { + colocatedWorkloads.computeIfPresent(name) { _, v -> (v - 1).takeUnless { it == 0 } } + intersectingItems = items.filter { item -> doesMatch(item) } + } + + /** + * Compute the performance interference based on the current server load. + */ + public fun apply(currentServerLoad: Double): Double { + if (intersectingItems.isEmpty()) { + return 1.0 + } + val score = intersectingItems + .firstOrNull { it.minServerLoad <= currentServerLoad } + + // Apply performance penalty to (on average) only one of the VMs + return if (score != null && random.nextInt(score.workloadNames.size) == 0) { + score.performanceScore + } else { + 1.0 + } + } + + private fun doesMatch(item: Item): Boolean { + var count = 0 + for ( + name in item.workloadNames.subSet( + colocatedWorkloads.firstKey(), + colocatedWorkloads.lastKey() + "\u0000" + ) + ) { + count += colocatedWorkloads.getOrDefault(name, 0) + if (count > 1) + return true + } + return false + } + + /** + * Model describing how a specific set of workloads causes performance variability for each workload. + * + * @param workloadNames The names of the workloads that together cause performance variability for each workload in the set. + * @param minServerLoad The minimum total server load at which this interference is activated and noticeable. + * @param performanceScore The performance score that should be applied to each workload's performance. 1 means no + * influence, <1 means that performance degrades, and >1 means that performance improves. + */ + public data class Item( + public val workloadNames: SortedSet, + public val minServerLoad: Double, + public val performanceScore: Double + ) : Comparable { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Item + + if (workloadNames != other.workloadNames) return false + + return true + } + + override fun hashCode(): Int = workloadNames.hashCode() + + override fun compareTo(other: Item): Int { + var cmp = performanceScore.compareTo(other.performanceScore) + if (cmp != 0) { + return cmp + } + + cmp = minServerLoad.compareTo(other.minServerLoad) + if (cmp != 0) { + return cmp + } + + return hashCode().compareTo(other.hashCode()) + } + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimHypervisorTest.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimHypervisorTest.kt index b9cd1b06..78bd2940 100644 --- a/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimHypervisorTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimHypervisorTest.kt @@ -36,7 +36,6 @@ import org.opendc.simulator.compute.model.ProcessingUnit import org.opendc.simulator.compute.workload.SimTraceWorkload import org.opendc.simulator.utils.DelayControllerClockAdapter import java.time.Clock -import java.util.* /** * Test suite for the [SimHypervisor] class. -- cgit v1.2.3 From 8aaeb93e81fc3c4689d42747f87734fc4732d030 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Sun, 4 Oct 2020 17:55:26 +0200 Subject: Extract simulation-related code from OpenDC Compute (core) This change splits the opendc-compute module into two modules: 1. opendc-compute-core The interfaces and APIs that represent a IaaS platform. 2. opendc-compute-simulator The implementation of these interfaces using simulation components from opendc-simulator-compute. --- simulator/opendc-compute/build.gradle.kts | 22 +- .../opendc-compute-core/build.gradle.kts | 41 +++ .../main/kotlin/org/opendc/compute/core/Flavor.kt | 41 +++ .../main/kotlin/org/opendc/compute/core/Server.kt | 78 ++++ .../kotlin/org/opendc/compute/core/ServerEvent.kt | 41 +++ .../kotlin/org/opendc/compute/core/ServerState.kt | 55 +++ .../org/opendc/compute/core/image/EmptyImage.kt | 35 ++ .../kotlin/org/opendc/compute/core/image/Image.kt | 35 ++ .../org/opendc/compute/core/metal/Metadata.kt | 34 ++ .../kotlin/org/opendc/compute/core/metal/Node.kt | 72 ++++ .../org/opendc/compute/core/metal/NodeEvent.kt | 43 +++ .../org/opendc/compute/core/metal/NodeState.kt | 55 +++ .../compute/core/metal/driver/BareMetalDriver.kt | 86 +++++ .../core/metal/service/ProvisioningService.kt | 64 ++++ .../metal/service/SimpleProvisioningService.kt | 65 ++++ .../org/opendc/compute/core/virt/Hypervisor.kt | 56 +++ .../opendc/compute/core/virt/HypervisorEvent.kt | 76 ++++ .../driver/InsufficientMemoryOnServerException.kt | 3 + .../opendc/compute/core/virt/driver/VirtDriver.kt | 57 +++ .../core/virt/service/VirtProvisioningEvent.kt | 49 +++ .../core/virt/service/VirtProvisioningService.kt | 61 ++++ .../org/opendc/compute/core/workload/VmWorkload.kt | 47 +++ .../opendc-compute-simulator/build.gradle.kts | 41 +++ .../simulator/ComputeSimExecutionContext.kt | 36 ++ .../org/opendc/compute/simulator/HypervisorView.kt | 37 ++ .../opendc/compute/simulator/SimBareMetalDriver.kt | 244 +++++++++++++ .../org/opendc/compute/simulator/SimVirtDriver.kt | 206 +++++++++++ .../compute/simulator/SimVirtDriverWorkload.kt | 38 ++ .../simulator/SimVirtProvisioningService.kt | 393 +++++++++++++++++++++ .../opendc/compute/simulator/SimWorkloadImage.kt | 43 +++ .../simulator/allocation/AllocationPolicy.kt | 50 +++ .../AvailableCoreMemoryAllocationPolicy.kt | 38 ++ .../allocation/AvailableMemoryAllocationPolicy.kt | 37 ++ .../allocation/ComparableAllocationPolicyLogic.kt | 49 +++ .../NumberOfActiveServersAllocationPolicy.kt | 37 ++ .../allocation/ProvisionedCoresAllocationPolicy.kt | 40 +++ .../simulator/allocation/RandomAllocationPolicy.kt | 48 +++ .../simulator/allocation/ReplayAllocationPolicy.kt | 56 +++ .../opendc/compute/simulator/power/PowerModels.kt | 43 +++ .../compute/simulator/SimBareMetalDriverTest.kt | 89 +++++ .../simulator/SimProvisioningServiceTest.kt | 80 +++++ .../opendc/compute/simulator/SimVirtDriverTest.kt | 145 ++++++++ .../main/kotlin/org/opendc/compute/core/Flavor.kt | 41 --- .../main/kotlin/org/opendc/compute/core/Server.kt | 78 ---- .../kotlin/org/opendc/compute/core/ServerEvent.kt | 41 --- .../kotlin/org/opendc/compute/core/ServerState.kt | 55 --- .../core/execution/ComputeSimExecutionContext.kt | 36 -- .../org/opendc/compute/core/image/EmptyImage.kt | 35 -- .../kotlin/org/opendc/compute/core/image/Image.kt | 35 -- .../opendc/compute/core/image/SimWorkloadImage.kt | 42 --- .../org/opendc/compute/core/workload/VmWorkload.kt | 47 --- .../kotlin/org/opendc/compute/metal/Metadata.kt | 34 -- .../main/kotlin/org/opendc/compute/metal/Node.kt | 72 ---- .../kotlin/org/opendc/compute/metal/NodeEvent.kt | 43 --- .../kotlin/org/opendc/compute/metal/NodeState.kt | 55 --- .../opendc/compute/metal/driver/BareMetalDriver.kt | 86 ----- .../compute/metal/driver/SimBareMetalDriver.kt | 242 ------------- .../org/opendc/compute/metal/power/PowerModels.kt | 43 --- .../compute/metal/service/ProvisioningService.kt | 64 ---- .../metal/service/SimpleProvisioningService.kt | 65 ---- .../kotlin/org/opendc/compute/virt/Hypervisor.kt | 56 --- .../org/opendc/compute/virt/HypervisorEvent.kt | 76 ---- .../driver/InsufficientMemoryOnServerException.kt | 3 - .../opendc/compute/virt/driver/SimVirtDriver.kt | 206 ----------- .../compute/virt/driver/SimVirtDriverWorkload.kt | 38 -- .../org/opendc/compute/virt/driver/VirtDriver.kt | 58 --- .../opendc/compute/virt/service/HypervisorView.kt | 37 -- .../virt/service/SimpleVirtProvisioningService.kt | 390 -------------------- .../compute/virt/service/VirtProvisioningEvent.kt | 49 --- .../virt/service/VirtProvisioningService.kt | 68 ---- .../virt/service/allocation/AllocationPolicy.kt | 50 --- .../AvailableCoreMemoryAllocationPolicy.kt | 38 -- .../allocation/AvailableMemoryAllocationPolicy.kt | 37 -- .../allocation/ComparableAllocationPolicyLogic.kt | 49 --- .../NumberOfActiveServersAllocationPolicy.kt | 37 -- .../allocation/ProvisionedCoresAllocationPolicy.kt | 40 --- .../service/allocation/RandomAllocationPolicy.kt | 48 --- .../service/allocation/ReplayAllocationPolicy.kt | 56 --- .../compute/metal/driver/SimBareMetalDriverTest.kt | 90 ----- .../metal/service/SimpleProvisioningServiceTest.kt | 81 ----- .../org/opendc/compute/virt/SimHypervisorTest.kt | 147 -------- .../org/opendc/experiments/sc18/TestExperiment.kt | 2 +- .../opendc-experiments-sc20/build.gradle.kts | 1 + .../sc20/experiment/ExperimentHelpers.kt | 24 +- .../org/opendc/experiments/sc20/experiment/Run.kt | 2 +- .../sc20/experiment/monitor/ExperimentMonitor.kt | 4 +- .../experiment/monitor/ParquetExperimentMonitor.kt | 4 +- .../sc20/trace/Sc20ParquetTraceReader.kt | 2 +- .../sc20/trace/Sc20RawParquetTraceReader.kt | 2 +- .../sc20/trace/Sc20StreamingParquetTraceReader.kt | 2 +- .../experiments/sc20/trace/WorkloadSampler.kt | 2 +- .../opendc/experiments/sc20/Sc20IntegrationTest.kt | 8 +- simulator/opendc-format/build.gradle.kts | 3 +- .../environment/sc18/Sc18EnvironmentReader.kt | 6 +- .../sc20/Sc20ClusterEnvironmentReader.kt | 10 +- .../environment/sc20/Sc20EnvironmentReader.kt | 8 +- .../format/trace/bitbrains/BitbrainsTraceReader.kt | 2 +- .../org/opendc/format/trace/gwf/GwfTraceReader.kt | 2 +- .../opendc/format/trace/sc20/Sc20TraceReader.kt | 2 +- .../org/opendc/format/trace/swf/SwfTraceReader.kt | 2 +- .../org/opendc/format/trace/wtf/WtfTraceReader.kt | 2 +- .../opendc/format/trace/swf/SwfTraceReaderTest.kt | 2 +- simulator/opendc-runner-web/build.gradle.kts | 2 +- .../src/main/kotlin/org/opendc/runner/web/Main.kt | 2 +- .../kotlin/org/opendc/runner/web/TopologyParser.kt | 10 +- simulator/opendc-workflows/build.gradle.kts | 2 +- .../workflows/service/StageWorkflowService.kt | 4 +- .../org/opendc/workflows/service/TaskState.kt | 2 +- .../resource/FirstFitResourceSelectionPolicy.kt | 2 +- .../resource/FunctionalResourceFilterPolicy.kt | 2 +- .../resource/RandomResourceSelectionPolicy.kt | 2 +- .../service/stage/resource/ResourceFilterPolicy.kt | 2 +- .../stage/resource/ResourceSelectionPolicy.kt | 2 +- .../StageWorkflowSchedulerIntegrationTest.kt | 2 +- simulator/settings.gradle.kts | 3 +- 115 files changed, 2912 insertions(+), 2851 deletions(-) create mode 100644 simulator/opendc-compute/opendc-compute-core/build.gradle.kts create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/Flavor.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/Server.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/ServerEvent.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/ServerState.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/image/EmptyImage.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/image/Image.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/Metadata.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/Node.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/NodeEvent.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/NodeState.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/driver/BareMetalDriver.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/service/ProvisioningService.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/service/SimpleProvisioningService.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/Hypervisor.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/HypervisorEvent.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/driver/InsufficientMemoryOnServerException.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/driver/VirtDriver.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/service/VirtProvisioningEvent.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/service/VirtProvisioningService.kt create mode 100644 simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/workload/VmWorkload.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/ComputeSimExecutionContext.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/HypervisorView.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimBareMetalDriver.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtDriver.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtDriverWorkload.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtProvisioningService.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimWorkloadImage.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AllocationPolicy.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AvailableCoreMemoryAllocationPolicy.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AvailableMemoryAllocationPolicy.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ComparableAllocationPolicyLogic.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/NumberOfActiveServersAllocationPolicy.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ProvisionedCoresAllocationPolicy.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/RandomAllocationPolicy.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ReplayAllocationPolicy.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/PowerModels.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimBareMetalDriverTest.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimProvisioningServiceTest.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimVirtDriverTest.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/Flavor.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/Server.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ServerEvent.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ServerState.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ComputeSimExecutionContext.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/EmptyImage.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/Image.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/SimWorkloadImage.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/workload/VmWorkload.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/Metadata.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/Node.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/NodeEvent.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/NodeState.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/BareMetalDriver.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/power/PowerModels.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/service/ProvisioningService.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/service/SimpleProvisioningService.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/Hypervisor.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/HypervisorEvent.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/InsufficientMemoryOnServerException.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriverWorkload.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/VirtDriver.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/HypervisorView.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/SimpleVirtProvisioningService.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/VirtProvisioningEvent.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/VirtProvisioningService.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/AllocationPolicy.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/AvailableCoreMemoryAllocationPolicy.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/AvailableMemoryAllocationPolicy.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/ComparableAllocationPolicyLogic.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/NumberOfActiveServersAllocationPolicy.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/ProvisionedCoresAllocationPolicy.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/RandomAllocationPolicy.kt delete mode 100644 simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/ReplayAllocationPolicy.kt delete mode 100644 simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriverTest.kt delete mode 100644 simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt delete mode 100644 simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/SimHypervisorTest.kt diff --git a/simulator/opendc-compute/build.gradle.kts b/simulator/opendc-compute/build.gradle.kts index 94af7861..bf920306 100644 --- a/simulator/opendc-compute/build.gradle.kts +++ b/simulator/opendc-compute/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 AtLarge Research + * 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 @@ -20,22 +20,4 @@ * SOFTWARE. */ -description = "Cloud computing fabric simulation model" - -/* Build configuration */ -plugins { - `kotlin-library-convention` -} - -dependencies { - api(project(":opendc-core")) - implementation(project(":opendc-utils")) - implementation("io.github.microutils:kotlin-logging:1.7.9") - implementation(project(":opendc-simulator:opendc-simulator-compute")) - - testImplementation(project(":opendc-simulator:opendc-simulator-core")) - testRuntimeOnly("org.slf4j:slf4j-simple:${Library.SLF4J}") - testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Library.JUNIT_JUPITER}") - testImplementation("org.junit.platform:junit-platform-launcher:${Library.JUNIT_PLATFORM}") -} +description = "Cloud computing fabric controller of OpenDC" diff --git a/simulator/opendc-compute/opendc-compute-core/build.gradle.kts b/simulator/opendc-compute/opendc-compute-core/build.gradle.kts new file mode 100644 index 00000000..9682b50f --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/build.gradle.kts @@ -0,0 +1,41 @@ +/* + * 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. + */ + +description = "Core implementation of the OpenDC Compute service" + +/* Build configuration */ +plugins { + `kotlin-library-convention` +} + +dependencies { + api(project(":opendc-core")) + implementation(project(":opendc-utils")) + implementation("io.github.microutils:kotlin-logging:1.7.9") + + testImplementation(project(":opendc-simulator:opendc-simulator-core")) + testImplementation(project(":opendc-compute:opendc-compute-simulator")) + testRuntimeOnly("org.slf4j:slf4j-simple:${Library.SLF4J}") + testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Library.JUNIT_JUPITER}") + testImplementation("org.junit.platform:junit-platform-launcher:${Library.JUNIT_PLATFORM}") +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/Flavor.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/Flavor.kt new file mode 100644 index 00000000..e5ca115f --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/Flavor.kt @@ -0,0 +1,41 @@ +/* + * MIT License + * + * 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.core + +/** + * Flavors define the compute and memory capacity of [Server] instance. To put it simply, a flavor is an available + * hardware configuration for a server. It defines the size of a virtual server that can be launched. + */ +public data class Flavor( + /** + * The number of (virtual) processing cores to use. + */ + public val cpuCount: Int, + + /** + * The amount of RAM available to the server (in MB). + */ + public val memorySize: Long +) diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/Server.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/Server.kt new file mode 100644 index 00000000..948f622f --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/Server.kt @@ -0,0 +1,78 @@ +/* + * 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.core + +import kotlinx.coroutines.flow.Flow +import org.opendc.compute.core.image.Image +import org.opendc.core.resource.Resource +import org.opendc.core.resource.TagContainer +import org.opendc.core.services.ServiceRegistry +import java.util.UUID + +/** + * A server instance that is running on some physical or virtual machine. + */ +public data class Server( + /** + * The unique identifier of the server. + */ + public override val uid: UUID, + + /** + * The optional name of the server. + */ + public override val name: String, + + /** + * The tags of this server. + */ + public override val tags: TagContainer, + + /** + * The hardware configuration of the server. + */ + public val flavor: Flavor, + + /** + * The image running on the server. + */ + public val image: Image, + + /** + * The last known state of the server. + */ + public val state: ServerState, + + /** + * The services published by this server. + */ + public val services: ServiceRegistry, + + /** + * The events that are emitted by the server. + */ + public val events: Flow +) : Resource { + override fun hashCode(): Int = uid.hashCode() + override fun equals(other: Any?): Boolean = other is Server && uid == other.uid +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/ServerEvent.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/ServerEvent.kt new file mode 100644 index 00000000..e9212832 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/ServerEvent.kt @@ -0,0 +1,41 @@ +/* + * 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.core + +/** + * An event that is emitted by a [Server]. + */ +public sealed class ServerEvent { + /** + * The server that emitted the event. + */ + public abstract val server: Server + + /** + * This event is emitted when the state of [server] changes. + * + * @property server The server of which the state changed. + * @property previousState The previous state of the server. + */ + public data class StateChanged(override val server: Server, val previousState: ServerState) : ServerEvent() +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/ServerState.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/ServerState.kt new file mode 100644 index 00000000..4b9d7c13 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/ServerState.kt @@ -0,0 +1,55 @@ +/* + * MIT License + * + * 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.core + +/** + * An enumeration describing the possible states of a server. + */ +public enum class ServerState { + /** + * The server has not yet finished the original build process. + */ + BUILD, + + /** + * The server was powered down by the user. + */ + SHUTOFF, + + /** + * The server is active and running. + */ + ACTIVE, + + /** + * The server is in error. + */ + ERROR, + + /** + * The state of the server is unknown. + */ + UNKNOWN, +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/image/EmptyImage.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/image/EmptyImage.kt new file mode 100644 index 00000000..01f86a1b --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/image/EmptyImage.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.compute.core.image + +import org.opendc.core.resource.TagContainer +import java.util.UUID + +/** + * An empty boot disk [Image] that exits immediately on start. + */ +public object EmptyImage : Image { + override val uid: UUID = UUID.randomUUID() + override val name: String = "empty" + override val tags: TagContainer = emptyMap() +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/image/Image.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/image/Image.kt new file mode 100644 index 00000000..e481fcc3 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/image/Image.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.compute.core.image + +import org.opendc.core.resource.Resource + +/** + * An image containing a bootable operating system that can directly be executed by physical or virtual server. + * + * OpenStack: A collection of files used to create or rebuild a server. Operators provide a number of pre-built OS + * images by default. You may also create custom images from cloud servers you have launched. These custom images are + * useful for backup purposes or for producing “gold” server images if you plan to deploy a particular server + * configuration frequently. + */ +public interface Image : Resource diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/Metadata.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/Metadata.kt new file mode 100644 index 00000000..11eadd87 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/Metadata.kt @@ -0,0 +1,34 @@ +/* + * MIT License + * + * 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.core.metal + +/* + * Common metadata keys for bare-metal nodes. + */ + +/** + * The cluster to which the node belongs. + */ +public const val NODE_CLUSTER: String = "bare-metal:cluster" diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/Node.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/Node.kt new file mode 100644 index 00000000..6d9506f1 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/Node.kt @@ -0,0 +1,72 @@ +/* + * 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.core.metal + +import kotlinx.coroutines.flow.Flow +import org.opendc.compute.core.Server +import org.opendc.compute.core.image.Image +import org.opendc.core.Identity +import java.util.UUID + +/** + * A bare-metal compute node. + */ +public data class Node( + /** + * The unique identifier of the node. + */ + public override val uid: UUID, + + /** + * The optional name of the node. + */ + public override val name: String, + + /** + * Metadata of the node. + */ + public val metadata: Map, + + /** + * The last known state of the compute node. + */ + public val state: NodeState, + + /** + * The boot image of the node. + */ + public val image: Image, + + /** + * The server instance that is running on the node or `null` if no server is running. + */ + public val server: Server?, + + /** + * The events that are emitted by the node. + */ + public val events: Flow +) : Identity { + override fun hashCode(): Int = uid.hashCode() + override fun equals(other: Any?): Boolean = other is Node && uid == other.uid +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/NodeEvent.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/NodeEvent.kt new file mode 100644 index 00000000..4423e2bf --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/NodeEvent.kt @@ -0,0 +1,43 @@ +/* + * MIT License + * + * 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.core.metal + +/** + * An event that is emitted by a [Node]. + */ +public sealed class NodeEvent { + /** + * The node that emitted the event. + */ + public abstract val node: Node + + /** + * This event is emitted when the state of [node] changes. + * + * @property node The node of which the state changed. + * @property previousState The previous state of the node. + */ + public data class StateChanged(override val node: Node, val previousState: NodeState) : NodeEvent() +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/NodeState.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/NodeState.kt new file mode 100644 index 00000000..bdc4841e --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/NodeState.kt @@ -0,0 +1,55 @@ +/* + * MIT License + * + * 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.core.metal + +/** + * An enumeration describing the possible states of a bare-metal compute node. + */ +public enum class NodeState { + /** + * The node is booting. + */ + BOOT, + + /** + * The node is powered off. + */ + SHUTOFF, + + /** + * The node is active and running. + */ + ACTIVE, + + /** + * The node is in error. + */ + ERROR, + + /** + * The state of the node is unknown. + */ + UNKNOWN, +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/driver/BareMetalDriver.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/driver/BareMetalDriver.kt new file mode 100644 index 00000000..6f58aa20 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/driver/BareMetalDriver.kt @@ -0,0 +1,86 @@ +/* + * 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.core.metal.driver + +import kotlinx.coroutines.flow.Flow +import org.opendc.compute.core.Server +import org.opendc.compute.core.image.Image +import org.opendc.compute.core.metal.Node +import org.opendc.core.failure.FailureDomain +import org.opendc.core.power.Powerable +import org.opendc.core.services.AbstractServiceKey +import java.util.UUID + +/** + * A driver interface for the management interface of a bare-metal compute node. + */ +public interface BareMetalDriver : Powerable, FailureDomain { + /** + * The [Node] that is controlled by this driver. + */ + public val node: Flow + + /** + * The amount of work done by the machine in percentage with respect to the total amount of processing power + * available. + */ + public val usage: Flow + + /** + * Initialize the driver. + */ + public suspend fun init(): Node + + /** + * Start the bare metal node with the specified boot disk image. + */ + public suspend fun start(): Node + + /** + * Stop the bare metal node if it is running. + */ + public suspend fun stop(): Node + + /** + * Reboot the bare metal node. + */ + public suspend fun reboot(): Node + + /** + * Update the boot disk image of the compute node. + * + * Changing the boot disk image of node does not affect it while the node is running. In order to start the new boot + * disk image, the compute node must be restarted. + */ + public suspend fun setImage(image: Image): Node + + /** + * Obtain the state of the compute node. + */ + public suspend fun refresh(): Node + + /** + * A key that allows access to the [BareMetalDriver] instance from a [Server] that runs on the bare-metal machine. + */ + public companion object Key : AbstractServiceKey(UUID.randomUUID(), "bare-metal:driver") +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/service/ProvisioningService.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/service/ProvisioningService.kt new file mode 100644 index 00000000..bad5b47c --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/service/ProvisioningService.kt @@ -0,0 +1,64 @@ +/* + * 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.core.metal.service + +import org.opendc.compute.core.image.Image +import org.opendc.compute.core.metal.Node +import org.opendc.compute.core.metal.driver.BareMetalDriver +import org.opendc.core.services.AbstractServiceKey +import java.util.UUID + +/** + * A cloud platform service for provisioning bare-metal compute nodes on the platform. + */ +public interface ProvisioningService { + /** + * Create a new bare-metal compute node. + */ + public suspend fun create(driver: BareMetalDriver): Node + + /** + * Obtain the available nodes. + */ + public suspend fun nodes(): Set + + /** + * Refresh the state of a compute node. + */ + public suspend fun refresh(node: Node): Node + + /** + * Deploy the specified [Image] on a compute node. + */ + public suspend fun deploy(node: Node, image: Image): Node + + /** + * Stop the specified [Node] . + */ + public suspend fun stop(node: Node): Node + + /** + * The service key of this service. + */ + public companion object Key : AbstractServiceKey(UUID.randomUUID(), "provisioner") +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/service/SimpleProvisioningService.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/service/SimpleProvisioningService.kt new file mode 100644 index 00000000..5222f2fb --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/service/SimpleProvisioningService.kt @@ -0,0 +1,65 @@ +/* + * 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.core.metal.service + +import kotlinx.coroutines.CancellationException +import org.opendc.compute.core.image.Image +import org.opendc.compute.core.metal.Node +import org.opendc.compute.core.metal.driver.BareMetalDriver + +/** + * A very basic implementation of the [ProvisioningService]. + */ +public class SimpleProvisioningService : ProvisioningService { + /** + * The active nodes in this service. + */ + private val nodes: MutableMap = mutableMapOf() + + override suspend fun create(driver: BareMetalDriver): Node { + val node = driver.init() + nodes[node] = driver + return node + } + + override suspend fun nodes(): Set = nodes.keys + + override suspend fun refresh(node: Node): Node { + return nodes[node]!!.refresh() + } + + override suspend fun deploy(node: Node, image: Image): Node { + val driver = nodes[node]!! + driver.setImage(image) + return driver.reboot() + } + + override suspend fun stop(node: Node): Node { + val driver = nodes[node]!! + return try { + driver.stop() + } catch (e: CancellationException) { + node + } + } +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/Hypervisor.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/Hypervisor.kt new file mode 100644 index 00000000..1ae52baa --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/Hypervisor.kt @@ -0,0 +1,56 @@ +/* + * 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.core.virt + +import kotlinx.coroutines.flow.Flow +import org.opendc.core.Identity +import java.util.UUID + +/** + * A hypervisor (or virtual machine monitor) is software or firmware that virtualizes the host compute environment + * into several virtual guest machines. + */ +public class Hypervisor( + /** + * The unique identifier of the hypervisor. + */ + override val uid: UUID, + + /** + * The optional name of the hypervisor. + */ + override val name: String, + + /** + * Metadata of the hypervisor. + */ + public val metadata: Map, + + /** + * The events that are emitted by the hypervisor. + */ + public val events: Flow +) : Identity { + override fun hashCode(): Int = uid.hashCode() + override fun equals(other: Any?): Boolean = other is Hypervisor && uid == other.uid +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/HypervisorEvent.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/HypervisorEvent.kt new file mode 100644 index 00000000..9fb437de --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/HypervisorEvent.kt @@ -0,0 +1,76 @@ +/* + * 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.core.virt + +import org.opendc.compute.core.Server +import org.opendc.compute.core.virt.driver.VirtDriver + +/** + * An event that is emitted by a [VirtDriver]. + */ +public sealed class HypervisorEvent { + /** + * The driver that emitted the event. + */ + public abstract val driver: VirtDriver + + /** + * This event is emitted when the number of active servers on the server managed by this driver is updated. + * + * @property driver The driver that emitted the event. + * @property numberOfActiveServers The number of active servers. + * @property availableMemory The available memory, in MB. + */ + public data class VmsUpdated( + override val driver: VirtDriver, + public val numberOfActiveServers: Int, + public val availableMemory: Long + ) : HypervisorEvent() + + /** + * This event is emitted when a slice is finished. + * + * @property driver The driver that emitted the event. + * @property requestedBurst The total requested CPU time (can be above capacity). + * @property grantedBurst The actual total granted capacity, which might be lower than the requested burst due to + * the hypervisor being interrupted during a slice. + * @property overcommissionedBurst The CPU time that the hypervisor could not grant to the virtual machine since + * it did not have the capacity. + * @property interferedBurst The sum of CPU time that virtual machines could not utilize due to performance + * interference. + * @property cpuUsage CPU use in megahertz. + * @property cpuDemand CPU demand in megahertz. + * @property numberOfDeployedImages The number of images deployed on this hypervisor. + */ + public data class SliceFinished( + override val driver: VirtDriver, + public val requestedBurst: Long, + public val grantedBurst: Long, + public val overcommissionedBurst: Long, + public val interferedBurst: Long, + public val cpuUsage: Double, + public val cpuDemand: Double, + public val numberOfDeployedImages: Int, + public val hostServer: Server + ) : HypervisorEvent() +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/driver/InsufficientMemoryOnServerException.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/driver/InsufficientMemoryOnServerException.kt new file mode 100644 index 00000000..6fe84ea6 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/driver/InsufficientMemoryOnServerException.kt @@ -0,0 +1,3 @@ +package org.opendc.compute.core.virt.driver + +public class InsufficientMemoryOnServerException : IllegalStateException("Insufficient memory left on server.") diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/driver/VirtDriver.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/driver/VirtDriver.kt new file mode 100644 index 00000000..5ecfd357 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/driver/VirtDriver.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.compute.core.virt.driver + +import kotlinx.coroutines.flow.Flow +import org.opendc.compute.core.Server +import org.opendc.compute.core.image.Image +import org.opendc.compute.core.virt.HypervisorEvent +import org.opendc.core.services.AbstractServiceKey +import java.util.UUID + +/** + * A driver interface for a hypervisor running on some host server and communicating with the central compute service to + * provide virtualization for that particular resource. + */ +public interface VirtDriver { + /** + * The events emitted by the driver. + */ + public val events: Flow + + /** + * Spawn the given [Image] on the compute resource of this driver. + * + * @param name The name of the server to spawn. + * @param image The image to deploy. + * @param flavor The flavor of the server which this driver is controlling. + * @return The virtual server spawned by this method. + */ + public suspend fun spawn( + name: String, + image: Image, + flavor: org.opendc.compute.core.Flavor + ): Server + + public companion object Key : AbstractServiceKey(UUID.randomUUID(), "virtual-driver") +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/service/VirtProvisioningEvent.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/service/VirtProvisioningEvent.kt new file mode 100644 index 00000000..abd2fc95 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/service/VirtProvisioningEvent.kt @@ -0,0 +1,49 @@ +/* + * MIT License + * + * 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.core.virt.service + +/** + * An event that is emitted by the [VirtProvisioningService]. + */ +public sealed class VirtProvisioningEvent { + /** + * The service that has emitted the event. + */ + public abstract val provisioner: VirtProvisioningService + + /** + * An event emitted for writing metrics. + */ + public data class MetricsAvailable( + override val provisioner: VirtProvisioningService, + public val totalHostCount: Int, + public val availableHostCount: Int, + public val totalVmCount: Int, + public val activeVmCount: Int, + public val inactiveVmCount: Int, + public val waitingVmCount: Int, + public val failedVmCount: Int + ) : VirtProvisioningEvent() +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/service/VirtProvisioningService.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/service/VirtProvisioningService.kt new file mode 100644 index 00000000..ab96e0a3 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/virt/service/VirtProvisioningService.kt @@ -0,0 +1,61 @@ +/* + * 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.core.virt.service + +import kotlinx.coroutines.flow.Flow +import org.opendc.compute.core.Server +import org.opendc.compute.core.image.Image +import org.opendc.compute.core.virt.driver.VirtDriver + +/** + * A service for VM provisioning on a cloud. + */ +public interface VirtProvisioningService { + /** + * The events emitted by the service. + */ + public val events: Flow + + /** + * Obtain the active hypervisors for this provisioner. + */ + public suspend fun drivers(): Set + + /** + * Submit the specified [Image] to the provisioning service. + * + * @param name The name of the server to deploy. + * @param image The image to be deployed. + * @param flavor The flavor of the machine instance to run this [image] on. + */ + public suspend fun deploy( + name: String, + image: Image, + flavor: org.opendc.compute.core.Flavor + ): Server + + /** + * Terminate the provisioning service releasing all the leased bare-metal machines. + */ + public suspend fun terminate() +} diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/workload/VmWorkload.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/workload/VmWorkload.kt new file mode 100644 index 00000000..6c724277 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/workload/VmWorkload.kt @@ -0,0 +1,47 @@ +/* + * 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.core.workload + +import org.opendc.compute.core.image.Image +import org.opendc.core.User +import org.opendc.core.workload.Workload +import java.util.UUID + +/** + * A workload that represents a VM. + * + * @property uid A unique identified of this VM. + * @property name The name of this VM. + * @property owner The owner of the VM. + * @property image The image of the VM. + */ +public data class VmWorkload( + override val uid: UUID, + override val name: String, + override val owner: User, + val image: Image +) : Workload { + override fun equals(other: Any?): Boolean = other is VmWorkload && uid == other.uid + + override fun hashCode(): Int = uid.hashCode() +} diff --git a/simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts b/simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts new file mode 100644 index 00000000..e1ec6421 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts @@ -0,0 +1,41 @@ +/* + * 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. + */ + +description = "Simulator for OpenDC Compute" + +/* Build configuration */ +plugins { + `kotlin-library-convention` +} + +dependencies { + api(project(":opendc-compute:opendc-compute-core")) + implementation(project(":opendc-utils")) + implementation("io.github.microutils:kotlin-logging:1.7.9") + implementation(project(":opendc-simulator:opendc-simulator-compute")) + + testImplementation(project(":opendc-simulator:opendc-simulator-core")) + testRuntimeOnly("org.slf4j:slf4j-simple:${Library.SLF4J}") + testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Library.JUNIT_JUPITER}") + testImplementation("org.junit.platform:junit-platform-launcher:${Library.JUNIT_PLATFORM}") +} 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 new file mode 100644 index 00000000..153a86b3 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/ComputeSimExecutionContext.kt @@ -0,0 +1,36 @@ +/* + * 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 new file mode 100644 index 00000000..1a79523e --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/HypervisorView.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.compute.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 new file mode 100644 index 00000000..08bb7e79 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimBareMetalDriver.kt @@ -0,0 +1,244 @@ +/* + * 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 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.simulator.power.ConstantPowerModel +import org.opendc.core.power.PowerModel +import org.opendc.core.services.ServiceRegistry +import org.opendc.simulator.compute.SimBareMetalMachine +import org.opendc.simulator.compute.SimExecutionContext +import org.opendc.simulator.compute.SimMachineModel +import org.opendc.simulator.compute.workload.SimWorkload +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. + * + * @param coroutineScope The [CoroutineScope] the driver runs in. + * @param clock The virtual clock to keep track of time. + * @param uid The unique identifier of the machine. + * @param name An optional name of the machine. + * @param metadata The initial metadata of the node. + * @param machine The machine model to simulate. + * @param powerModel The power model of this machine. + */ +@OptIn(ExperimentalCoroutinesApi::class) +public class SimBareMetalDriver( + private val coroutineScope: CoroutineScope, + private val clock: Clock, + uid: UUID, + name: String, + metadata: Map, + machine: SimMachineModel, + powerModel: PowerModel = ConstantPowerModel(0.0) +) : BareMetalDriver { + /** + * The flavor that corresponds to this machine. + */ + private val flavor = Flavor( + machine.cpus.size, + machine.memory.map { it.size }.sum() + ) + + /** + * The events of the machine. + */ + private val events = EventFlow() + + /** + * The machine state. + */ + private val nodeState = + StateFlow(Node(uid, name, metadata + ("driver" to this), NodeState.SHUTOFF, EmptyImage, null, events)) + + /** + * The [SimBareMetalMachine] we use to run the workload. + */ + private val machine = SimBareMetalMachine(coroutineScope, clock, machine) + + override val node: Flow = nodeState + + override val usage: Flow + get() = this.machine.usage + + override val powerDraw: Flow = powerModel(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? = null + + override suspend fun init(): Node { + return nodeState.value + } + + override suspend fun start(): Node { + val node = nodeState.value + if (node.state != NodeState.SHUTOFF) { + return node + } + + val events = EventFlow() + serverEvents = events + val server = Server( + UUID(random.nextLong(), random.nextLong()), + node.name, + emptyMap(), + flavor, + node.image, + ServerState.BUILD, + ServiceRegistry().put(BareMetalDriver, this@SimBareMetalDriver), + events + ) + + // Wrap the workload to pass in a ComputeSimExecutionContext + val workload = object : SimWorkload { + override suspend fun run(ctx: SimExecutionContext) { + val wrappedCtx = object : ComputeSimExecutionContext, SimExecutionContext by ctx { + override val server: Server + get() = nodeState.value.server!! + } + (node.image as SimWorkloadImage).workload.run(wrappedCtx) + } + } + + job = coroutineScope.launch { + delay(1) // TODO Introduce boot time + initMachine() + try { + machine.run(workload) + exitMachine(null) + } catch (_: CancellationException) { + // Ignored + } catch (cause: Throwable) { + exitMachine(cause) + } + } + + setNode(node.copy(state = NodeState.BOOT, server = server)) + return nodeState.value + } + + private fun initMachine() { + val server = nodeState.value.server?.copy(state = ServerState.ACTIVE) + setNode(nodeState.value.copy(state = NodeState.ACTIVE, server = server)) + } + + private fun exitMachine(cause: Throwable?) { + val newServerState = + if (cause == null) + ServerState.SHUTOFF + else + ServerState.ERROR + val newNodeState = + if (cause == null) + nodeState.value.state + else + NodeState.ERROR + val server = nodeState.value.server?.copy(state = newServerState) + setNode(nodeState.value.copy(state = newNodeState, server = server)) + + serverEvents?.close() + serverEvents = null + } + + override suspend fun stop(): Node { + val node = nodeState.value + if (node.state == NodeState.SHUTOFF) { + return node + } + + job?.cancelAndJoin() + setNode(node.copy(state = NodeState.SHUTOFF, server = null)) + return node + } + + override suspend fun reboot(): Node { + stop() + return start() + } + + override suspend fun setImage(image: Image): Node { + setNode(nodeState.value.copy(image = image)) + return nodeState.value + } + + override suspend fun refresh(): Node = nodeState.value + + private fun setNode(value: Node) { + val field = nodeState.value + if (field.state != value.state) { + 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 + } + + override val scope: CoroutineScope + get() = coroutineScope + + override suspend fun fail() { + val server = nodeState.value.server?.copy(state = ServerState.ERROR) + setNode(nodeState.value.copy(state = NodeState.ERROR, server = server)) + } + + override suspend fun recover() { + val server = nodeState.value.server?.copy(state = ServerState.ACTIVE) + setNode(nodeState.value.copy(state = NodeState.ACTIVE, server = server)) + } + + 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/SimVirtDriver.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtDriver.kt new file mode 100644 index 00000000..09eec1ef --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtDriver.kt @@ -0,0 +1,206 @@ +/* + * 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.SimExecutionContext +import org.opendc.simulator.compute.SimHypervisor +import org.opendc.simulator.compute.SimMachine +import org.opendc.simulator.compute.interference.IMAGE_PERF_INTERFERENCE_MODEL +import org.opendc.simulator.compute.interference.PerformanceInterferenceModel +import org.opendc.simulator.compute.workload.SimWorkload +import org.opendc.utils.flow.EventFlow +import java.time.Clock +import java.util.* + +/** + * A [VirtDriver] that is simulates virtual machines on a physical machine using [SimHypervisor]. + */ +public class SimVirtDriver( + private val coroutineScope: CoroutineScope, + clock: Clock, + private val ctx: SimExecutionContext +) : VirtDriver { + + /** + * The server hosting this hypervisor. + */ + public val server: Server + get() = (ctx as ComputeSimExecutionContext).server + + /** + * The [EventFlow] to emit the events. + */ + internal val eventFlow = EventFlow() + + override val events: Flow = eventFlow + + /** + * Current total memory use of the images on this hypervisor. + */ + private var availableMemory: Long = ctx.machine.memory.map { it.size }.sum() + + /** + * The hypervisor to run multiple workloads. + */ + private val hypervisor = SimHypervisor( + coroutineScope, + clock, + object : SimHypervisor.Listener { + override fun onSliceFinish( + hypervisor: SimHypervisor, + requestedBurst: Long, + grantedBurst: Long, + overcommissionedBurst: Long, + interferedBurst: Long, + cpuUsage: Double, + cpuDemand: Double + ) { + eventFlow.emit( + HypervisorEvent.SliceFinished( + this@SimVirtDriver, + requestedBurst, + grantedBurst, + overcommissionedBurst, + interferedBurst, + cpuUsage, + cpuDemand, + vms.size, + (ctx as ComputeSimExecutionContext).server + ) + ) + } + } + ) + + /** + * The virtual machines running on the hypervisor. + */ + private val vms = HashSet() + + 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() + val server = Server( + UUID.randomUUID(), + name, + emptyMap(), + flavor, + image, + ServerState.BUILD, + ServiceRegistry(), + events + ) + availableMemory -= requiredMemory + val vm = VirtualMachine(server, events, hypervisor.createMachine(ctx.machine)) + vms.add(vm) + vmStarted(vm) + eventFlow.emit(HypervisorEvent.VmsUpdated(this, vms.size, availableMemory)) + return server + } + + 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, machine: SimMachine) { + val performanceInterferenceModel: PerformanceInterferenceModel? = server.image.tags[IMAGE_PERF_INTERFERENCE_MODEL] as? PerformanceInterferenceModel? + + val job = coroutineScope.launch { + val workload = object : SimWorkload { + override suspend fun run(ctx: SimExecutionContext) { + val wrappedCtx = object : ComputeSimExecutionContext, SimExecutionContext by ctx { + override val server: Server + get() = this@VirtualMachine.server + } + (server.image as SimWorkloadImage).workload.run(wrappedCtx) + } + } + + delay(1) // TODO Introduce boot time + init() + try { + machine.run(workload) + exit(null) + } catch (cause: Throwable) { + exit(cause) + } + } + + 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() + } + } + + public suspend fun run() { + hypervisor.run(ctx) + } +} diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtDriverWorkload.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtDriverWorkload.kt new file mode 100644 index 00000000..58b9408a --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtDriverWorkload.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.compute.simulator + +import kotlinx.coroutines.coroutineScope +import org.opendc.simulator.compute.SimExecutionContext +import org.opendc.simulator.compute.workload.SimWorkload + +public class SimVirtDriverWorkload : SimWorkload { + public lateinit var driver: SimVirtDriver + + override suspend fun run(ctx: SimExecutionContext) { + coroutineScope { + driver = SimVirtDriver(this, ctx.clock, ctx) + driver.run() + } + } +} 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 new file mode 100644 index 00000000..e83370d7 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtProvisioningService.kt @@ -0,0 +1,393 @@ +/* + * 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.simulator.allocation.AllocationPolicy +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 +) : VirtProvisioningService { + /** + * The logger instance to use. + */ + private val logger = KotlinLogging.logger {} + + /** + * The hypervisors that have been launched by the service. + */ + private val hypervisors: MutableMap = mutableMapOf() + + /** + * The available hypervisors. + */ + private val availableHypervisors: MutableSet = mutableSetOf() + + /** + * The incoming images to be processed by the provisioner. + */ + private val incomingImages: MutableSet = mutableSetOf() + + /** + * The active images in the system. + */ + private val activeImages: MutableSet = 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() + + override val events: Flow = eventFlow + + init { + coroutineScope.launch { + val provisionedNodes = provisioningService.nodes() + provisionedNodes.forEach { node -> + val workload = SimVirtDriverWorkload() + 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.driver) + } + } + } + } + + override suspend fun drivers(): Set { + return availableHypervisors.map { it.driver }.toSet() + } + + override suspend fun deploy( + name: String, + image: Image, + flavor: Flavor + ): Server { + eventFlow.emit( + VirtProvisioningEvent.MetricsAvailable( + this@SimVirtProvisioningService, + hypervisors.size, + availableHypervisors.size, + ++submittedVms, + runningVms, + finishedVms, + ++queuedVms, + unscheduledVms + ) + ) + + return suspendCancellableCoroutine { 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 var call: Job? = null + + private fun requestCycle() { + if (call != null) { + return + } + + val quantum = 300000 // 5 minutes in milliseconds + // 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 = quantum - (clock.millis() % quantum) + + val call = coroutineScope.launch { + delay(delay) + this@SimVirtProvisioningService.call = null + schedule() + } + this.call = call + } + + private suspend fun schedule() { + val imagesToBeScheduled = incomingImages.toSet() + + for (imageInstance in imagesToBeScheduled) { + val requiredMemory = imageInstance.image.tags["required-memory"] as Long + val selectedHv = allocationLogic.select(availableHypervisors, imageInstance) + + if (selectedHv == null) { + if (requiredMemory > maxMemory || imageInstance.flavor.cpuCount > maxCores) { + eventFlow.emit( + VirtProvisioningEvent.MetricsAvailable( + this@SimVirtProvisioningService, + hypervisors.size, + availableHypervisors.size, + submittedVms, + runningVms, + finishedVms, + queuedVms, + ++unscheduledVms + ) + ) + + incomingImages -= imageInstance + + 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 -= imageInstance + + // 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) + + 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." } + + 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 + } + + 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 + + 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 + + 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, + 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 new file mode 100644 index 00000000..b48de1d5 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimWorkloadImage.kt @@ -0,0 +1,43 @@ +/* + * 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 new file mode 100644 index 00000000..2018b9f2 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AllocationPolicy.kt @@ -0,0 +1,50 @@ +/* + * 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, + 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 new file mode 100644 index 00000000..38a07b2b --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AvailableCoreMemoryAllocationPolicy.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.compute.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 = + compareBy { -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 new file mode 100644 index 00000000..e87abd7b --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AvailableMemoryAllocationPolicy.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.compute.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 = compareBy { -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 new file mode 100644 index 00000000..6aff2bab --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ComparableAllocationPolicyLogic.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.compute.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 + + override fun select( + hypervisors: Set, + image: SimVirtProvisioningService.ImageView + ): HypervisorView? { + return hypervisors.asSequence() + .filter { hv -> + val fitsMemory = hv.availableMemory >= (image.image.tags["required-memory"] as Long) + val fitsCpu = hv.server.flavor.cpuCount >= image.flavor.cpuCount + fitsMemory && fitsCpu + } + .minWith(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 new file mode 100644 index 00000000..5e2b895c --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/NumberOfActiveServersAllocationPolicy.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.compute.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 = compareBy { 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 new file mode 100644 index 00000000..4344d979 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ProvisionedCoresAllocationPolicy.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.compute.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 = + compareBy { 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 new file mode 100644 index 00000000..ac34f410 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/RandomAllocationPolicy.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.compute.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, + 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 new file mode 100644 index 00000000..fda863bb --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ReplayAllocationPolicy.kt @@ -0,0 +1,56 @@ +/* + * 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) : AllocationPolicy { + override fun invoke(): AllocationPolicy.Logic = object : AllocationPolicy.Logic { + override fun select( + hypervisors: Set, + 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.maxBy { it.availableMemory } + } + + return machinesInCluster.maxBy { 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/PowerModels.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/PowerModels.kt new file mode 100644 index 00000000..09651720 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/PowerModels.kt @@ -0,0 +1,43 @@ +/* + * 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.power + +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map +import org.opendc.compute.core.metal.driver.BareMetalDriver +import org.opendc.core.power.PowerModel + +/** + * A power model which emits a single value. + */ +public fun ConstantPowerModel(value: Double): PowerModel = { _ -> flowOf(value) } + +/** + * A power model that assumes a naive linear relation between power usage and host CPU utilization. + * + * @param idle The power draw in Watts on idle. + * @param max The maximum power draw in Watts of the server. + */ +public fun LinearLoadPowerModel(idle: Double, max: Double): PowerModel = { driver -> + driver.usage.map { load -> (max - idle) * load + idle } +} 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 new file mode 100644 index 00000000..0f1bd444 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimBareMetalDriverTest.kt @@ -0,0 +1,89 @@ +/* + * 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.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.TestCoroutineScope +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.opendc.simulator.compute.SimMachineModel +import org.opendc.simulator.compute.model.MemoryUnit +import org.opendc.simulator.compute.model.ProcessingNode +import org.opendc.simulator.compute.model.ProcessingUnit +import org.opendc.simulator.compute.workload.SimFlopsWorkload +import org.opendc.simulator.utils.DelayControllerClockAdapter +import java.util.UUID + +@OptIn(ExperimentalCoroutinesApi::class) +internal class SimBareMetalDriverTest { + private lateinit var machineModel: SimMachineModel + + @BeforeEach + fun setUp() { + val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 4) + + machineModel = SimMachineModel( + cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 2000.0) }, + memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + ) + } + + @Test + fun testFlopsWorkload() { + val testScope = TestCoroutineScope() + val clock = DelayControllerClockAdapter(testScope) + + var finalState: ServerState = ServerState.BUILD + var finalTime = 0L + + testScope.launch { + val driver = SimBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), machineModel) + val image = SimWorkloadImage(UUID.randomUUID(), "", emptyMap(), SimFlopsWorkload(4_000, 2, utilization = 1.0)) + + // Batch driver commands + withContext(coroutineContext) { + driver.init() + driver.setImage(image) + val server = driver.start().server!! + server.events.collect { event -> + when (event) { + is ServerEvent.StateChanged -> { + finalState = event.server.state + finalTime = clock.millis() + } + } + } + } + } + + testScope.advanceUntilIdle() + assertEquals(ServerState.SHUTOFF, finalState) + assertEquals(1001, finalTime) + } +} 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 new file mode 100644 index 00000000..def78ce7 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimProvisioningServiceTest.kt @@ -0,0 +1,80 @@ +/* + * 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.ExperimentalCoroutinesApi +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collect +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.simulator.compute.SimMachineModel +import org.opendc.simulator.compute.model.MemoryUnit +import org.opendc.simulator.compute.model.ProcessingNode +import org.opendc.simulator.compute.model.ProcessingUnit +import org.opendc.simulator.compute.workload.SimFlopsWorkload +import org.opendc.simulator.utils.DelayControllerClockAdapter +import java.util.UUID + +/** + * Test suite for the [SimpleProvisioningService]. + */ +@OptIn(ExperimentalCoroutinesApi::class) +internal class SimProvisioningServiceTest { + private lateinit var machineModel: SimMachineModel + + @BeforeEach + fun setUp() { + val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 4) + + machineModel = SimMachineModel( + cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 2000.0) }, + memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + ) + } + + /** + * A basic smoke test. + */ + @Test + fun testSmoke() { + val testScope = TestCoroutineScope() + val clock = DelayControllerClockAdapter(testScope) + + testScope.launch { + val image = SimWorkloadImage(UUID.randomUUID(), "", emptyMap(), SimFlopsWorkload(1000, 2)) + val driver = SimBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), machineModel) + + val provisioner = SimpleProvisioningService() + provisioner.create(driver) + delay(5) + val nodes = provisioner.nodes() + val node = provisioner.deploy(nodes.first(), image) + node.server!!.events.collect { println(it) } + } + + testScope.advanceUntilIdle() + } +} 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/SimVirtDriverTest.kt new file mode 100644 index 00000000..a0c61f29 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimVirtDriverTest.kt @@ -0,0 +1,145 @@ +/* + * 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.ExperimentalCoroutinesApi +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.TestCoroutineScope +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.simulator.compute.SimMachineModel +import org.opendc.simulator.compute.model.MemoryUnit +import org.opendc.simulator.compute.model.ProcessingNode +import org.opendc.simulator.compute.model.ProcessingUnit +import org.opendc.simulator.compute.workload.SimTraceWorkload +import org.opendc.simulator.utils.DelayControllerClockAdapter +import java.time.Clock +import java.util.UUID + +/** + * Basic test-suite for the hypervisor. + */ +@OptIn(ExperimentalCoroutinesApi::class) +internal class SimVirtDriverTest { + private lateinit var scope: TestCoroutineScope + private lateinit var clock: Clock + private lateinit var machineModel: SimMachineModel + + @BeforeEach + fun setUp() { + scope = TestCoroutineScope() + clock = DelayControllerClockAdapter(scope) + + val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) + + machineModel = SimMachineModel( + cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + ) + } + + /** + * Test overcommissioning of a hypervisor. + */ + @Test + fun overcommission() { + var requestedBurst = 0L + var grantedBurst = 0L + var overcommissionedBurst = 0L + + scope.launch { + val virtDriverWorkload = SimVirtDriverWorkload() + val vmm = SimWorkloadImage(UUID.randomUUID(), "vmm", emptyMap(), virtDriverWorkload) + val duration = 5 * 60L + val vmImageA = SimWorkloadImage( + UUID.randomUUID(), + "", + emptyMap(), + SimTraceWorkload( + sequenceOf( + SimTraceWorkload.Fragment(0, 28L * duration, duration * 1000, 28.0, 2), + SimTraceWorkload.Fragment(0, 3500L * duration, duration * 1000, 3500.0, 2), + SimTraceWorkload.Fragment(0, 0, duration * 1000, 0.0, 2), + SimTraceWorkload.Fragment(0, 183L * duration, duration * 1000, 183.0, 2) + ), + ) + ) + val vmImageB = SimWorkloadImage( + UUID.randomUUID(), + "", + emptyMap(), + SimTraceWorkload( + sequenceOf( + SimTraceWorkload.Fragment(0, 28L * duration, duration * 1000, 28.0, 2), + SimTraceWorkload.Fragment(0, 3100L * duration, duration * 1000, 3100.0, 2), + SimTraceWorkload.Fragment(0, 0, duration * 1000, 0.0, 2), + SimTraceWorkload.Fragment(0, 73L * duration, duration * 1000, 73.0, 2) + ) + ), + ) + + val metalDriver = + SimBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), machineModel) + + metalDriver.init() + metalDriver.setImage(vmm) + metalDriver.start() + + delay(5) + + val flavor = Flavor(2, 0) + val vmDriver = virtDriverWorkload.driver + vmDriver.events + .onEach { event -> + when (event) { + is HypervisorEvent.SliceFinished -> { + requestedBurst += event.requestedBurst + grantedBurst += event.grantedBurst + overcommissionedBurst += event.overcommissionedBurst + } + } + } + .launchIn(this) + + vmDriver.spawn("a", vmImageA, flavor) + vmDriver.spawn("b", vmImageB, flavor) + } + + scope.advanceUntilIdle() + + assertAll( + { assertEquals(emptyList(), scope.uncaughtExceptions, "No errors") }, + { assertEquals(2073600, requestedBurst, "Requested Burst does not match") }, + { assertEquals(2013600, grantedBurst, "Granted Burst does not match") }, + { assertEquals(60000, overcommissionedBurst, "Overcommissioned Burst does not match") }, + { assertEquals(1200007, scope.currentTime) } + ) + } +} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/Flavor.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/Flavor.kt deleted file mode 100644 index e5ca115f..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/Flavor.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * MIT License - * - * 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.core - -/** - * Flavors define the compute and memory capacity of [Server] instance. To put it simply, a flavor is an available - * hardware configuration for a server. It defines the size of a virtual server that can be launched. - */ -public data class Flavor( - /** - * The number of (virtual) processing cores to use. - */ - public val cpuCount: Int, - - /** - * The amount of RAM available to the server (in MB). - */ - public val memorySize: Long -) diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/Server.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/Server.kt deleted file mode 100644 index 948f622f..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/Server.kt +++ /dev/null @@ -1,78 +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.core - -import kotlinx.coroutines.flow.Flow -import org.opendc.compute.core.image.Image -import org.opendc.core.resource.Resource -import org.opendc.core.resource.TagContainer -import org.opendc.core.services.ServiceRegistry -import java.util.UUID - -/** - * A server instance that is running on some physical or virtual machine. - */ -public data class Server( - /** - * The unique identifier of the server. - */ - public override val uid: UUID, - - /** - * The optional name of the server. - */ - public override val name: String, - - /** - * The tags of this server. - */ - public override val tags: TagContainer, - - /** - * The hardware configuration of the server. - */ - public val flavor: Flavor, - - /** - * The image running on the server. - */ - public val image: Image, - - /** - * The last known state of the server. - */ - public val state: ServerState, - - /** - * The services published by this server. - */ - public val services: ServiceRegistry, - - /** - * The events that are emitted by the server. - */ - public val events: Flow -) : Resource { - override fun hashCode(): Int = uid.hashCode() - override fun equals(other: Any?): Boolean = other is Server && uid == other.uid -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ServerEvent.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ServerEvent.kt deleted file mode 100644 index e9212832..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ServerEvent.kt +++ /dev/null @@ -1,41 +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.core - -/** - * An event that is emitted by a [Server]. - */ -public sealed class ServerEvent { - /** - * The server that emitted the event. - */ - public abstract val server: Server - - /** - * This event is emitted when the state of [server] changes. - * - * @property server The server of which the state changed. - * @property previousState The previous state of the server. - */ - public data class StateChanged(override val server: Server, val previousState: ServerState) : ServerEvent() -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ServerState.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ServerState.kt deleted file mode 100644 index 4b9d7c13..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/ServerState.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * MIT License - * - * 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.core - -/** - * An enumeration describing the possible states of a server. - */ -public enum class ServerState { - /** - * The server has not yet finished the original build process. - */ - BUILD, - - /** - * The server was powered down by the user. - */ - SHUTOFF, - - /** - * The server is active and running. - */ - ACTIVE, - - /** - * The server is in error. - */ - ERROR, - - /** - * The state of the server is unknown. - */ - UNKNOWN, -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ComputeSimExecutionContext.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/ComputeSimExecutionContext.kt deleted file mode 100644 index 3295a8e8..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/execution/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.core.execution - -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/src/main/kotlin/org/opendc/compute/core/image/EmptyImage.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/EmptyImage.kt deleted file mode 100644 index 01f86a1b..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/EmptyImage.kt +++ /dev/null @@ -1,35 +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.core.image - -import org.opendc.core.resource.TagContainer -import java.util.UUID - -/** - * An empty boot disk [Image] that exits immediately on start. - */ -public object EmptyImage : Image { - override val uid: UUID = UUID.randomUUID() - override val name: String = "empty" - override val tags: TagContainer = emptyMap() -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/Image.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/Image.kt deleted file mode 100644 index e481fcc3..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/Image.kt +++ /dev/null @@ -1,35 +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.core.image - -import org.opendc.core.resource.Resource - -/** - * An image containing a bootable operating system that can directly be executed by physical or virtual server. - * - * OpenStack: A collection of files used to create or rebuild a server. Operators provide a number of pre-built OS - * images by default. You may also create custom images from cloud servers you have launched. These custom images are - * useful for backup purposes or for producing “gold” server images if you plan to deploy a particular server - * configuration frequently. - */ -public interface Image : Resource diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/SimWorkloadImage.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/SimWorkloadImage.kt deleted file mode 100644 index 1dd9a34b..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/image/SimWorkloadImage.kt +++ /dev/null @@ -1,42 +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.core.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/src/main/kotlin/org/opendc/compute/core/workload/VmWorkload.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/workload/VmWorkload.kt deleted file mode 100644 index 6c724277..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/core/workload/VmWorkload.kt +++ /dev/null @@ -1,47 +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.core.workload - -import org.opendc.compute.core.image.Image -import org.opendc.core.User -import org.opendc.core.workload.Workload -import java.util.UUID - -/** - * A workload that represents a VM. - * - * @property uid A unique identified of this VM. - * @property name The name of this VM. - * @property owner The owner of the VM. - * @property image The image of the VM. - */ -public data class VmWorkload( - override val uid: UUID, - override val name: String, - override val owner: User, - val image: Image -) : Workload { - override fun equals(other: Any?): Boolean = other is VmWorkload && uid == other.uid - - override fun hashCode(): Int = uid.hashCode() -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/Metadata.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/Metadata.kt deleted file mode 100644 index 61a992b2..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/Metadata.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * MIT License - * - * 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.metal - -/* - * Common metadata keys for bare-metal nodes. - */ - -/** - * The cluster to which the node belongs. - */ -public const val NODE_CLUSTER: String = "bare-metal:cluster" diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/Node.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/Node.kt deleted file mode 100644 index 5cb4be1a..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/Node.kt +++ /dev/null @@ -1,72 +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.metal - -import kotlinx.coroutines.flow.Flow -import org.opendc.compute.core.Server -import org.opendc.compute.core.image.Image -import org.opendc.core.Identity -import java.util.UUID - -/** - * A bare-metal compute node. - */ -public data class Node( - /** - * The unique identifier of the node. - */ - public override val uid: UUID, - - /** - * The optional name of the node. - */ - public override val name: String, - - /** - * Metadata of the node. - */ - public val metadata: Map, - - /** - * The last known state of the compute node. - */ - public val state: NodeState, - - /** - * The boot image of the node. - */ - public val image: Image, - - /** - * The server instance that is running on the node or `null` if no server is running. - */ - public val server: Server?, - - /** - * The events that are emitted by the node. - */ - public val events: Flow -) : Identity { - override fun hashCode(): Int = uid.hashCode() - override fun equals(other: Any?): Boolean = other is Node && uid == other.uid -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/NodeEvent.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/NodeEvent.kt deleted file mode 100644 index d367f2e6..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/NodeEvent.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * MIT License - * - * 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.metal - -/** - * An event that is emitted by a [Node]. - */ -public sealed class NodeEvent { - /** - * The node that emitted the event. - */ - public abstract val node: Node - - /** - * This event is emitted when the state of [node] changes. - * - * @property node The node of which the state changed. - * @property previousState The previous state of the node. - */ - public data class StateChanged(override val node: Node, val previousState: NodeState) : NodeEvent() -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/NodeState.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/NodeState.kt deleted file mode 100644 index e76e0b43..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/NodeState.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * MIT License - * - * 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.metal - -/** - * An enumeration describing the possible states of a bare-metal compute node. - */ -public enum class NodeState { - /** - * The node is booting. - */ - BOOT, - - /** - * The node is powered off. - */ - SHUTOFF, - - /** - * The node is active and running. - */ - ACTIVE, - - /** - * The node is in error. - */ - ERROR, - - /** - * The state of the node is unknown. - */ - UNKNOWN, -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/BareMetalDriver.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/BareMetalDriver.kt deleted file mode 100644 index 5d8521ae..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/BareMetalDriver.kt +++ /dev/null @@ -1,86 +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.metal.driver - -import kotlinx.coroutines.flow.Flow -import org.opendc.compute.core.Server -import org.opendc.compute.core.image.Image -import org.opendc.compute.metal.Node -import org.opendc.core.failure.FailureDomain -import org.opendc.core.power.Powerable -import org.opendc.core.services.AbstractServiceKey -import java.util.UUID - -/** - * A driver interface for the management interface of a bare-metal compute node. - */ -public interface BareMetalDriver : Powerable, FailureDomain { - /** - * The [Node] that is controlled by this driver. - */ - public val node: Flow - - /** - * The amount of work done by the machine in percentage with respect to the total amount of processing power - * available. - */ - public val usage: Flow - - /** - * Initialize the driver. - */ - public suspend fun init(): Node - - /** - * Start the bare metal node with the specified boot disk image. - */ - public suspend fun start(): Node - - /** - * Stop the bare metal node if it is running. - */ - public suspend fun stop(): Node - - /** - * Reboot the bare metal node. - */ - public suspend fun reboot(): Node - - /** - * Update the boot disk image of the compute node. - * - * Changing the boot disk image of node does not affect it while the node is running. In order to start the new boot - * disk image, the compute node must be restarted. - */ - public suspend fun setImage(image: Image): Node - - /** - * Obtain the state of the compute node. - */ - public suspend fun refresh(): Node - - /** - * A key that allows access to the [BareMetalDriver] instance from a [Server] that runs on the bare-metal machine. - */ - public companion object Key : AbstractServiceKey(UUID.randomUUID(), "bare-metal:driver") -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt deleted file mode 100644 index 4f72d599..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriver.kt +++ /dev/null @@ -1,242 +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.metal.driver - -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.execution.ComputeSimExecutionContext -import org.opendc.compute.core.image.EmptyImage -import org.opendc.compute.core.image.Image -import org.opendc.compute.core.image.SimWorkloadImage -import org.opendc.compute.metal.Node -import org.opendc.compute.metal.NodeEvent -import org.opendc.compute.metal.NodeState -import org.opendc.compute.metal.power.ConstantPowerModel -import org.opendc.core.power.PowerModel -import org.opendc.core.services.ServiceRegistry -import org.opendc.simulator.compute.SimBareMetalMachine -import org.opendc.simulator.compute.SimExecutionContext -import org.opendc.simulator.compute.SimMachineModel -import org.opendc.simulator.compute.workload.SimWorkload -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. - * - * @param coroutineScope The [CoroutineScope] the driver runs in. - * @param clock The virtual clock to keep track of time. - * @param uid The unique identifier of the machine. - * @param name An optional name of the machine. - * @param metadata The initial metadata of the node. - * @param machine The machine model to simulate. - * @param powerModel The power model of this machine. - */ -@OptIn(ExperimentalCoroutinesApi::class) -public class SimBareMetalDriver( - private val coroutineScope: CoroutineScope, - private val clock: Clock, - uid: UUID, - name: String, - metadata: Map, - machine: SimMachineModel, - powerModel: PowerModel = ConstantPowerModel(0.0) -) : BareMetalDriver { - /** - * The flavor that corresponds to this machine. - */ - private val flavor = Flavor( - machine.cpus.size, - machine.memory.map { it.size }.sum() - ) - - /** - * The events of the machine. - */ - private val events = EventFlow() - - /** - * The machine state. - */ - private val nodeState = - StateFlow(Node(uid, name, metadata + ("driver" to this), NodeState.SHUTOFF, EmptyImage, null, events)) - - /** - * The [SimBareMetalMachine] we use to run the workload. - */ - private val machine = SimBareMetalMachine(coroutineScope, clock, machine) - - override val node: Flow = nodeState - - override val usage: Flow - get() = this.machine.usage - - override val powerDraw: Flow = powerModel(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? = null - - override suspend fun init(): Node { - return nodeState.value - } - - override suspend fun start(): Node { - val node = nodeState.value - if (node.state != NodeState.SHUTOFF) { - return node - } - - val events = EventFlow() - serverEvents = events - val server = Server( - UUID(random.nextLong(), random.nextLong()), - node.name, - emptyMap(), - flavor, - node.image, - ServerState.BUILD, - ServiceRegistry().put(BareMetalDriver, this@SimBareMetalDriver), - events - ) - - // Wrap the workload to pass in a ComputeSimExecutionContext - val workload = object : SimWorkload { - override suspend fun run(ctx: SimExecutionContext) { - val wrappedCtx = object : ComputeSimExecutionContext, SimExecutionContext by ctx { - override val server: Server - get() = nodeState.value.server!! - } - (node.image as SimWorkloadImage).workload.run(wrappedCtx) - } - } - - job = coroutineScope.launch { - delay(1) // TODO Introduce boot time - initMachine() - try { - machine.run(workload) - exitMachine(null) - } catch (_: CancellationException) { - // Ignored - } catch (cause: Throwable) { - exitMachine(cause) - } - } - - setNode(node.copy(state = NodeState.BOOT, server = server)) - return nodeState.value - } - - private fun initMachine() { - val server = nodeState.value.server?.copy(state = ServerState.ACTIVE) - setNode(nodeState.value.copy(state = NodeState.ACTIVE, server = server)) - } - - private fun exitMachine(cause: Throwable?) { - val newServerState = - if (cause == null) - ServerState.SHUTOFF - else - ServerState.ERROR - val newNodeState = - if (cause == null) - nodeState.value.state - else - NodeState.ERROR - val server = nodeState.value.server?.copy(state = newServerState) - setNode(nodeState.value.copy(state = newNodeState, server = server)) - - serverEvents?.close() - serverEvents = null - } - - override suspend fun stop(): Node { - val node = nodeState.value - if (node.state == NodeState.SHUTOFF) { - return node - } - - job?.cancelAndJoin() - setNode(node.copy(state = NodeState.SHUTOFF, server = null)) - return node - } - - override suspend fun reboot(): Node { - stop() - return start() - } - - override suspend fun setImage(image: Image): Node { - setNode(nodeState.value.copy(image = image)) - return nodeState.value - } - - override suspend fun refresh(): Node = nodeState.value - - private fun setNode(value: Node) { - val field = nodeState.value - if (field.state != value.state) { - events.emit(NodeEvent.StateChanged(value, field.state)) - } - - if (field.server != null && value.server != null && field.server.state != value.server.state) { - serverEvents?.emit(ServerEvent.StateChanged(value.server, field.server.state)) - } - - nodeState.value = value - } - - override val scope: CoroutineScope - get() = coroutineScope - - override suspend fun fail() { - val server = nodeState.value.server?.copy(state = ServerState.ERROR) - setNode(nodeState.value.copy(state = NodeState.ERROR, server = server)) - } - - override suspend fun recover() { - val server = nodeState.value.server?.copy(state = ServerState.ACTIVE) - setNode(nodeState.value.copy(state = NodeState.ACTIVE, server = server)) - } - - override fun toString(): String = "SimBareMetalDriver(node = ${nodeState.value.uid})" -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/power/PowerModels.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/power/PowerModels.kt deleted file mode 100644 index 9286626c..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/power/PowerModels.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.metal.power - -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map -import org.opendc.compute.metal.driver.BareMetalDriver -import org.opendc.core.power.PowerModel - -/** - * A power model which emits a single value. - */ -public fun ConstantPowerModel(value: Double): PowerModel = { _ -> flowOf(value) } - -/** - * A power model that assumes a naive linear relation between power usage and host CPU utilization. - * - * @param idle The power draw in Watts on idle. - * @param max The maximum power draw in Watts of the server. - */ -public fun LinearLoadPowerModel(idle: Double, max: Double): PowerModel = { driver -> - driver.usage.map { load -> (max - idle) * load + idle } -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/service/ProvisioningService.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/service/ProvisioningService.kt deleted file mode 100644 index c8b38912..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/service/ProvisioningService.kt +++ /dev/null @@ -1,64 +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.metal.service - -import org.opendc.compute.core.image.Image -import org.opendc.compute.metal.Node -import org.opendc.compute.metal.driver.BareMetalDriver -import org.opendc.core.services.AbstractServiceKey -import java.util.UUID - -/** - * A cloud platform service for provisioning bare-metal compute nodes on the platform. - */ -public interface ProvisioningService { - /** - * Create a new bare-metal compute node. - */ - public suspend fun create(driver: BareMetalDriver): Node - - /** - * Obtain the available nodes. - */ - public suspend fun nodes(): Set - - /** - * Refresh the state of a compute node. - */ - public suspend fun refresh(node: Node): Node - - /** - * Deploy the specified [Image] on a compute node. - */ - public suspend fun deploy(node: Node, image: Image): Node - - /** - * Stop the specified [Node] . - */ - public suspend fun stop(node: Node): Node - - /** - * The service key of this service. - */ - public companion object Key : AbstractServiceKey(UUID.randomUUID(), "provisioner") -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/service/SimpleProvisioningService.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/service/SimpleProvisioningService.kt deleted file mode 100644 index 3d126ba1..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/metal/service/SimpleProvisioningService.kt +++ /dev/null @@ -1,65 +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.metal.service - -import kotlinx.coroutines.CancellationException -import org.opendc.compute.core.image.Image -import org.opendc.compute.metal.Node -import org.opendc.compute.metal.driver.BareMetalDriver - -/** - * A very basic implementation of the [ProvisioningService]. - */ -public class SimpleProvisioningService : ProvisioningService { - /** - * The active nodes in this service. - */ - private val nodes: MutableMap = mutableMapOf() - - override suspend fun create(driver: BareMetalDriver): Node { - val node = driver.init() - nodes[node] = driver - return node - } - - override suspend fun nodes(): Set = nodes.keys - - override suspend fun refresh(node: Node): Node { - return nodes[node]!!.refresh() - } - - override suspend fun deploy(node: Node, image: Image): Node { - val driver = nodes[node]!! - driver.setImage(image) - return driver.reboot() - } - - override suspend fun stop(node: Node): Node { - val driver = nodes[node]!! - return try { - driver.stop() - } catch (e: CancellationException) { - node - } - } -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/Hypervisor.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/Hypervisor.kt deleted file mode 100644 index 5dd98bbc..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/Hypervisor.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.virt - -import kotlinx.coroutines.flow.Flow -import org.opendc.core.Identity -import java.util.UUID - -/** - * A hypervisor (or virtual machine monitor) is software or firmware that virtualizes the host compute environment - * into several virtual guest machines. - */ -public class Hypervisor( - /** - * The unique identifier of the hypervisor. - */ - override val uid: UUID, - - /** - * The optional name of the hypervisor. - */ - override val name: String, - - /** - * Metadata of the hypervisor. - */ - public val metadata: Map, - - /** - * The events that are emitted by the hypervisor. - */ - public val events: Flow -) : Identity { - override fun hashCode(): Int = uid.hashCode() - override fun equals(other: Any?): Boolean = other is Hypervisor && uid == other.uid -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/HypervisorEvent.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/HypervisorEvent.kt deleted file mode 100644 index 1e2e285c..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/HypervisorEvent.kt +++ /dev/null @@ -1,76 +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.virt - -import org.opendc.compute.core.Server -import org.opendc.compute.virt.driver.VirtDriver - -/** - * An event that is emitted by a [VirtDriver]. - */ -public sealed class HypervisorEvent { - /** - * The driver that emitted the event. - */ - public abstract val driver: VirtDriver - - /** - * This event is emitted when the number of active servers on the server managed by this driver is updated. - * - * @property driver The driver that emitted the event. - * @property numberOfActiveServers The number of active servers. - * @property availableMemory The available memory, in MB. - */ - public data class VmsUpdated( - override val driver: VirtDriver, - public val numberOfActiveServers: Int, - public val availableMemory: Long - ) : HypervisorEvent() - - /** - * This event is emitted when a slice is finished. - * - * @property driver The driver that emitted the event. - * @property requestedBurst The total requested CPU time (can be above capacity). - * @property grantedBurst The actual total granted capacity, which might be lower than the requested burst due to - * the hypervisor being interrupted during a slice. - * @property overcommissionedBurst The CPU time that the hypervisor could not grant to the virtual machine since - * it did not have the capacity. - * @property interferedBurst The sum of CPU time that virtual machines could not utilize due to performance - * interference. - * @property cpuUsage CPU use in megahertz. - * @property cpuDemand CPU demand in megahertz. - * @property numberOfDeployedImages The number of images deployed on this hypervisor. - */ - public data class SliceFinished( - override val driver: VirtDriver, - public val requestedBurst: Long, - public val grantedBurst: Long, - public val overcommissionedBurst: Long, - public val interferedBurst: Long, - public val cpuUsage: Double, - public val cpuDemand: Double, - public val numberOfDeployedImages: Int, - public val hostServer: Server - ) : HypervisorEvent() -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/InsufficientMemoryOnServerException.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/InsufficientMemoryOnServerException.kt deleted file mode 100644 index 83dd70d4..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/InsufficientMemoryOnServerException.kt +++ /dev/null @@ -1,3 +0,0 @@ -package org.opendc.compute.virt.driver - -public class InsufficientMemoryOnServerException : IllegalStateException("Insufficient memory left on server.") diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt deleted file mode 100644 index 357ad714..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriver.kt +++ /dev/null @@ -1,206 +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.virt.driver - -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.execution.ComputeSimExecutionContext -import org.opendc.compute.core.image.Image -import org.opendc.compute.core.image.SimWorkloadImage -import org.opendc.compute.virt.HypervisorEvent -import org.opendc.core.services.ServiceRegistry -import org.opendc.simulator.compute.SimExecutionContext -import org.opendc.simulator.compute.SimHypervisor -import org.opendc.simulator.compute.SimMachine -import org.opendc.simulator.compute.interference.IMAGE_PERF_INTERFERENCE_MODEL -import org.opendc.simulator.compute.interference.PerformanceInterferenceModel -import org.opendc.simulator.compute.workload.SimWorkload -import org.opendc.utils.flow.EventFlow -import java.time.Clock -import java.util.* - -/** - * A [VirtDriver] that is simulates virtual machines on a physical machine using [SimHypervisor]. - */ -public class SimVirtDriver( - private val coroutineScope: CoroutineScope, - clock: Clock, - private val ctx: SimExecutionContext -) : VirtDriver { - - /** - * The server hosting this hypervisor. - */ - public val server: Server - get() = (ctx as ComputeSimExecutionContext).server - - /** - * The [EventFlow] to emit the events. - */ - internal val eventFlow = EventFlow() - - override val events: Flow = eventFlow - - /** - * Current total memory use of the images on this hypervisor. - */ - private var availableMemory: Long = ctx.machine.memory.map { it.size }.sum() - - /** - * The hypervisor to run multiple workloads. - */ - private val hypervisor = SimHypervisor( - coroutineScope, - clock, - object : SimHypervisor.Listener { - override fun onSliceFinish( - hypervisor: SimHypervisor, - requestedBurst: Long, - grantedBurst: Long, - overcommissionedBurst: Long, - interferedBurst: Long, - cpuUsage: Double, - cpuDemand: Double - ) { - eventFlow.emit( - HypervisorEvent.SliceFinished( - this@SimVirtDriver, - requestedBurst, - grantedBurst, - overcommissionedBurst, - interferedBurst, - cpuUsage, - cpuDemand, - vms.size, - (ctx as ComputeSimExecutionContext).server - ) - ) - } - } - ) - - /** - * The virtual machines running on the hypervisor. - */ - private val vms = HashSet() - - 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() - val server = Server( - UUID.randomUUID(), - name, - emptyMap(), - flavor, - image, - ServerState.BUILD, - ServiceRegistry(), - events - ) - availableMemory -= requiredMemory - val vm = VirtualMachine(server, events, hypervisor.createMachine(ctx.machine)) - vms.add(vm) - vmStarted(vm) - eventFlow.emit(HypervisorEvent.VmsUpdated(this, vms.size, availableMemory)) - return server - } - - 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, machine: SimMachine) { - val performanceInterferenceModel: PerformanceInterferenceModel? = server.image.tags[IMAGE_PERF_INTERFERENCE_MODEL] as? PerformanceInterferenceModel? - - val job = coroutineScope.launch { - val workload = object : SimWorkload { - override suspend fun run(ctx: SimExecutionContext) { - val wrappedCtx = object : ComputeSimExecutionContext, SimExecutionContext by ctx { - override val server: Server - get() = this@VirtualMachine.server - } - (server.image as SimWorkloadImage).workload.run(wrappedCtx) - } - } - - delay(1) // TODO Introduce boot time - init() - try { - machine.run(workload) - exit(null) - } catch (cause: Throwable) { - exit(cause) - } - } - - 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() - } - } - - public suspend fun run() { - hypervisor.run(ctx) - } -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriverWorkload.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriverWorkload.kt deleted file mode 100644 index 91d400a4..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/SimVirtDriverWorkload.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.virt.driver - -import kotlinx.coroutines.coroutineScope -import org.opendc.simulator.compute.SimExecutionContext -import org.opendc.simulator.compute.workload.SimWorkload - -public class SimVirtDriverWorkload : SimWorkload { - public lateinit var driver: SimVirtDriver - - override suspend fun run(ctx: SimExecutionContext) { - coroutineScope { - driver = SimVirtDriver(this, ctx.clock, ctx) - driver.run() - } - } -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/VirtDriver.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/VirtDriver.kt deleted file mode 100644 index 54728eb6..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/driver/VirtDriver.kt +++ /dev/null @@ -1,58 +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.virt.driver - -import kotlinx.coroutines.flow.Flow -import org.opendc.compute.core.Flavor -import org.opendc.compute.core.Server -import org.opendc.compute.core.image.Image -import org.opendc.compute.virt.HypervisorEvent -import org.opendc.core.services.AbstractServiceKey -import java.util.UUID - -/** - * A driver interface for a hypervisor running on some host server and communicating with the central compute service to - * provide virtualization for that particular resource. - */ -public interface VirtDriver { - /** - * The events emitted by the driver. - */ - public val events: Flow - - /** - * Spawn the given [Image] on the compute resource of this driver. - * - * @param name The name of the server to spawn. - * @param image The image to deploy. - * @param flavor The flavor of the server which this driver is controlling. - * @return The virtual server spawned by this method. - */ - public suspend fun spawn( - name: String, - image: Image, - flavor: org.opendc.compute.core.Flavor - ): Server - - public companion object Key : AbstractServiceKey(UUID.randomUUID(), "virtual-driver") -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/HypervisorView.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/HypervisorView.kt deleted file mode 100644 index cf6b4487..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/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.virt.service - -import org.opendc.compute.core.Server -import org.opendc.compute.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/src/main/kotlin/org/opendc/compute/virt/service/SimpleVirtProvisioningService.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/SimpleVirtProvisioningService.kt deleted file mode 100644 index e7c2b8cc..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/SimpleVirtProvisioningService.kt +++ /dev/null @@ -1,390 +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.virt.service - -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.image.SimWorkloadImage -import org.opendc.compute.metal.service.ProvisioningService -import org.opendc.compute.virt.HypervisorEvent -import org.opendc.compute.virt.driver.InsufficientMemoryOnServerException -import org.opendc.compute.virt.driver.SimVirtDriver -import org.opendc.compute.virt.driver.SimVirtDriverWorkload -import org.opendc.compute.virt.driver.VirtDriver -import org.opendc.compute.virt.service.allocation.AllocationPolicy -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 - -private val logger = KotlinLogging.logger {} - -@OptIn(ExperimentalCoroutinesApi::class) -public class SimpleVirtProvisioningService( - private val coroutineScope: CoroutineScope, - private val clock: Clock, - private val provisioningService: ProvisioningService, - override val allocationPolicy: AllocationPolicy -) : VirtProvisioningService { - /** - * The hypervisors that have been launched by the service. - */ - private val hypervisors: MutableMap = mutableMapOf() - - /** - * The available hypervisors. - */ - private val availableHypervisors: MutableSet = mutableSetOf() - - /** - * The incoming images to be processed by the provisioner. - */ - private val incomingImages: MutableSet = mutableSetOf() - - /** - * The active images in the system. - */ - private val activeImages: MutableSet = 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() - - override val events: Flow = eventFlow - - init { - coroutineScope.launch { - val provisionedNodes = provisioningService.nodes() - provisionedNodes.forEach { node -> - val workload = SimVirtDriverWorkload() - val hypervisorImage = SimWorkloadImage(UUID.randomUUID(), "vmm", emptyMap(), workload) - launch { - var init = false - val deployedNode = provisioningService.deploy(node, hypervisorImage) - deployedNode.server!!.events.onEach { event -> - when (event) { - is ServerEvent.StateChanged -> { - if (!init) { - init = true - } - stateChanged(event.server) - } - } - }.launchIn(this) - - delay(1) - onHypervisorAvailable(deployedNode.server, workload.driver) - } - } - } - } - - override suspend fun drivers(): Set { - return availableHypervisors.map { it.driver }.toSet() - } - - override suspend fun deploy( - name: String, - image: Image, - flavor: Flavor - ): Server { - eventFlow.emit( - VirtProvisioningEvent.MetricsAvailable( - this@SimpleVirtProvisioningService, - hypervisors.size, - availableHypervisors.size, - ++submittedVms, - runningVms, - finishedVms, - ++queuedVms, - unscheduledVms - ) - ) - - return suspendCancellableCoroutine { 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 var call: Job? = null - - private fun requestCycle() { - if (call != null) { - return - } - - val quantum = 300000 // 5 minutes in milliseconds - // 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 = quantum - (clock.millis() % quantum) - - val call = coroutineScope.launch { - delay(delay) - this@SimpleVirtProvisioningService.call = null - schedule() - } - this.call = call - } - - private suspend fun schedule() { - val imagesToBeScheduled = incomingImages.toSet() - - for (imageInstance in imagesToBeScheduled) { - val requiredMemory = imageInstance.image.tags["required-memory"] as Long - val selectedHv = allocationLogic.select(availableHypervisors, imageInstance) - - if (selectedHv == null) { - if (requiredMemory > maxMemory || imageInstance.flavor.cpuCount > maxCores) { - eventFlow.emit( - VirtProvisioningEvent.MetricsAvailable( - this@SimpleVirtProvisioningService, - hypervisors.size, - availableHypervisors.size, - submittedVms, - runningVms, - finishedVms, - queuedVms, - ++unscheduledVms - ) - ) - - incomingImages -= imageInstance - - 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 -= imageInstance - - // 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) - - eventFlow.emit( - VirtProvisioningEvent.MetricsAvailable( - this@SimpleVirtProvisioningService, - 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." } - - eventFlow.emit( - VirtProvisioningEvent.MetricsAvailable( - this@SimpleVirtProvisioningService, - 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 - } - - eventFlow.emit( - VirtProvisioningEvent.MetricsAvailable( - this@SimpleVirtProvisioningService, - 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 - - eventFlow.emit( - VirtProvisioningEvent.MetricsAvailable( - this@SimpleVirtProvisioningService, - 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 - - eventFlow.emit( - VirtProvisioningEvent.MetricsAvailable( - this@SimpleVirtProvisioningService, - 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, - public var server: Server? = null - ) -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/VirtProvisioningEvent.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/VirtProvisioningEvent.kt deleted file mode 100644 index 31f5d2c9..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/VirtProvisioningEvent.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * MIT License - * - * 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.virt.service - -/** - * An event that is emitted by the [VirtProvisioningService]. - */ -public sealed class VirtProvisioningEvent { - /** - * The service that has emitted the event. - */ - public abstract val provisioner: VirtProvisioningService - - /** - * An event emitted for writing metrics. - */ - public data class MetricsAvailable( - override val provisioner: VirtProvisioningService, - public val totalHostCount: Int, - public val availableHostCount: Int, - public val totalVmCount: Int, - public val activeVmCount: Int, - public val inactiveVmCount: Int, - public val waitingVmCount: Int, - public val failedVmCount: Int - ) : VirtProvisioningEvent() -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/VirtProvisioningService.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/VirtProvisioningService.kt deleted file mode 100644 index 13dfd640..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/VirtProvisioningService.kt +++ /dev/null @@ -1,68 +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.virt.service - -import kotlinx.coroutines.flow.Flow -import org.opendc.compute.core.Flavor -import org.opendc.compute.core.Server -import org.opendc.compute.core.image.Image -import org.opendc.compute.virt.driver.VirtDriver -import org.opendc.compute.virt.service.allocation.AllocationPolicy - -/** - * A service for VM provisioning on a cloud. - */ -public interface VirtProvisioningService { - /** - * The policy used for allocating a VM on the available hypervisors. - */ - public val allocationPolicy: AllocationPolicy - - /** - * The events emitted by the service. - */ - public val events: Flow - - /** - * Obtain the active hypervisors for this provisioner. - */ - public suspend fun drivers(): Set - - /** - * Submit the specified [Image] to the provisioning service. - * - * @param name The name of the server to deploy. - * @param image The image to be deployed. - * @param flavor The flavor of the machine instance to run this [image] on. - */ - public suspend fun deploy( - name: String, - image: Image, - flavor: org.opendc.compute.core.Flavor - ): Server - - /** - * Terminate the provisioning service releasing all the leased bare-metal machines. - */ - public suspend fun terminate() -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/AllocationPolicy.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/AllocationPolicy.kt deleted file mode 100644 index 099d1740..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/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.virt.service.allocation - -import org.opendc.compute.metal.Node -import org.opendc.compute.virt.service.HypervisorView -import org.opendc.compute.virt.service.SimpleVirtProvisioningService - -/** - * 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, - image: SimpleVirtProvisioningService.ImageView - ): HypervisorView? - } - - /** - * Builds the logic of the policy. - */ - public operator fun invoke(): Logic -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/AvailableCoreMemoryAllocationPolicy.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/AvailableCoreMemoryAllocationPolicy.kt deleted file mode 100644 index cfbf3cd3..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/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.virt.service.allocation - -import org.opendc.compute.virt.service.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 = - compareBy { -it.availableMemory / it.server.flavor.cpuCount } - .run { if (reversed) reversed() else this } - } -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/AvailableMemoryAllocationPolicy.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/AvailableMemoryAllocationPolicy.kt deleted file mode 100644 index 48172765..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/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.virt.service.allocation - -import org.opendc.compute.virt.service.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 = compareBy { -it.availableMemory } - .run { if (reversed) reversed() else this } - } -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/ComparableAllocationPolicyLogic.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/ComparableAllocationPolicyLogic.kt deleted file mode 100644 index c971cd7e..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/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.virt.service.allocation - -import org.opendc.compute.virt.service.HypervisorView -import org.opendc.compute.virt.service.SimpleVirtProvisioningService - -/** - * 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 - - override fun select( - hypervisors: Set, - image: SimpleVirtProvisioningService.ImageView - ): HypervisorView? { - return hypervisors.asSequence() - .filter { hv -> - val fitsMemory = hv.availableMemory >= (image.image.tags["required-memory"] as Long) - val fitsCpu = hv.server.flavor.cpuCount >= image.flavor.cpuCount - fitsMemory && fitsCpu - } - .minWith(comparator.thenBy { it.server.uid }) - } -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/NumberOfActiveServersAllocationPolicy.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/NumberOfActiveServersAllocationPolicy.kt deleted file mode 100644 index 63ba64ce..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/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.virt.service.allocation - -import org.opendc.compute.virt.service.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 = compareBy { it.numberOfActiveServers } - .run { if (reversed) reversed() else this } - } -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/ProvisionedCoresAllocationPolicy.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/ProvisionedCoresAllocationPolicy.kt deleted file mode 100644 index d76e9c82..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/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.virt.service.allocation - -import org.opendc.compute.virt.service.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 = - compareBy { it.provisionedCores / it.server.flavor.cpuCount } - .run { if (reversed) reversed() else this } - } -} diff --git a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/RandomAllocationPolicy.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/RandomAllocationPolicy.kt deleted file mode 100644 index 26e61673..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/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.virt.service.allocation - -import org.opendc.compute.virt.service.HypervisorView -import org.opendc.compute.virt.service.SimpleVirtProvisioningService -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, - image: SimpleVirtProvisioningService.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/src/main/kotlin/org/opendc/compute/virt/service/allocation/ReplayAllocationPolicy.kt b/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/allocation/ReplayAllocationPolicy.kt deleted file mode 100644 index a623633d..00000000 --- a/simulator/opendc-compute/src/main/kotlin/org/opendc/compute/virt/service/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.virt.service.allocation - -import mu.KotlinLogging -import org.opendc.compute.virt.service.HypervisorView -import org.opendc.compute.virt.service.SimpleVirtProvisioningService - -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) : AllocationPolicy { - override fun invoke(): AllocationPolicy.Logic = object : AllocationPolicy.Logic { - override fun select( - hypervisors: Set, - image: SimpleVirtProvisioningService.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.maxBy { it.availableMemory } - } - - return machinesInCluster.maxBy { it.availableMemory } - ?: throw IllegalStateException("Cloud not find any machine and could not randomly assign") - } - } -} diff --git a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriverTest.kt b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriverTest.kt deleted file mode 100644 index 584dcef4..00000000 --- a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/driver/SimBareMetalDriverTest.kt +++ /dev/null @@ -1,90 +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.metal.driver - -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.TestCoroutineScope -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.opendc.compute.core.image.SimWorkloadImage -import org.opendc.simulator.compute.SimMachineModel -import org.opendc.simulator.compute.model.MemoryUnit -import org.opendc.simulator.compute.model.ProcessingNode -import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.compute.workload.SimFlopsWorkload -import org.opendc.simulator.utils.DelayControllerClockAdapter -import java.util.UUID - -@OptIn(ExperimentalCoroutinesApi::class) -internal class SimBareMetalDriverTest { - private lateinit var machineModel: SimMachineModel - - @BeforeEach - fun setUp() { - val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 4) - - machineModel = SimMachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 2000.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } - ) - } - - @Test - fun testFlopsWorkload() { - val testScope = TestCoroutineScope() - val clock = DelayControllerClockAdapter(testScope) - - var finalState: ServerState = ServerState.BUILD - var finalTime = 0L - - testScope.launch { - val driver = SimBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), machineModel) - val image = SimWorkloadImage(UUID.randomUUID(), "", emptyMap(), SimFlopsWorkload(4_000, 2, utilization = 1.0)) - - // Batch driver commands - withContext(coroutineContext) { - driver.init() - driver.setImage(image) - val server = driver.start().server!! - server.events.collect { event -> - when (event) { - is ServerEvent.StateChanged -> { - finalState = event.server.state - finalTime = clock.millis() - } - } - } - } - } - - testScope.advanceUntilIdle() - assertEquals(ServerState.SHUTOFF, finalState) - assertEquals(1001, finalTime) - } -} diff --git a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt deleted file mode 100644 index 6f13e557..00000000 --- a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt +++ /dev/null @@ -1,81 +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.metal.service - -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.collect -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.image.SimWorkloadImage -import org.opendc.compute.metal.driver.SimBareMetalDriver -import org.opendc.simulator.compute.SimMachineModel -import org.opendc.simulator.compute.model.MemoryUnit -import org.opendc.simulator.compute.model.ProcessingNode -import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.compute.workload.SimFlopsWorkload -import org.opendc.simulator.utils.DelayControllerClockAdapter -import java.util.UUID - -/** - * Test suite for the [SimpleProvisioningService]. - */ -@OptIn(ExperimentalCoroutinesApi::class) -internal class SimpleProvisioningServiceTest { - private lateinit var machineModel: SimMachineModel - - @BeforeEach - fun setUp() { - val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 4) - - machineModel = SimMachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 2000.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } - ) - } - - /** - * A basic smoke test. - */ - @Test - fun testSmoke() { - val testScope = TestCoroutineScope() - val clock = DelayControllerClockAdapter(testScope) - - testScope.launch { - val image = SimWorkloadImage(UUID.randomUUID(), "", emptyMap(), SimFlopsWorkload(1000, 2)) - val driver = SimBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), machineModel) - - val provisioner = SimpleProvisioningService() - provisioner.create(driver) - delay(5) - val nodes = provisioner.nodes() - val node = provisioner.deploy(nodes.first(), image) - node.server!!.events.collect { println(it) } - } - - testScope.advanceUntilIdle() - } -} diff --git a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/SimHypervisorTest.kt b/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/SimHypervisorTest.kt deleted file mode 100644 index 9d33d395..00000000 --- a/simulator/opendc-compute/src/test/kotlin/org/opendc/compute/virt/SimHypervisorTest.kt +++ /dev/null @@ -1,147 +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.virt - -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.TestCoroutineScope -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.image.SimWorkloadImage -import org.opendc.compute.metal.driver.SimBareMetalDriver -import org.opendc.compute.virt.driver.SimVirtDriverWorkload -import org.opendc.simulator.compute.SimMachineModel -import org.opendc.simulator.compute.model.MemoryUnit -import org.opendc.simulator.compute.model.ProcessingNode -import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.compute.workload.SimTraceWorkload -import org.opendc.simulator.utils.DelayControllerClockAdapter -import java.time.Clock -import java.util.UUID - -/** - * Basic test-suite for the hypervisor. - */ -@OptIn(ExperimentalCoroutinesApi::class) -internal class SimHypervisorTest { - private lateinit var scope: TestCoroutineScope - private lateinit var clock: Clock - private lateinit var machineModel: SimMachineModel - - @BeforeEach - fun setUp() { - scope = TestCoroutineScope() - clock = DelayControllerClockAdapter(scope) - - val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) - - machineModel = SimMachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } - ) - } - - /** - * Test overcommissioning of a hypervisor. - */ - @Test - fun overcommission() { - var requestedBurst = 0L - var grantedBurst = 0L - var overcommissionedBurst = 0L - - scope.launch { - val virtDriverWorkload = SimVirtDriverWorkload() - val vmm = SimWorkloadImage(UUID.randomUUID(), "vmm", emptyMap(), virtDriverWorkload) - val duration = 5 * 60L - val vmImageA = SimWorkloadImage( - UUID.randomUUID(), - "", - emptyMap(), - SimTraceWorkload( - sequenceOf( - SimTraceWorkload.Fragment(0, 28L * duration, duration * 1000, 28.0, 2), - SimTraceWorkload.Fragment(0, 3500L * duration, duration * 1000, 3500.0, 2), - SimTraceWorkload.Fragment(0, 0, duration * 1000, 0.0, 2), - SimTraceWorkload.Fragment(0, 183L * duration, duration * 1000, 183.0, 2) - ), - ) - ) - val vmImageB = SimWorkloadImage( - UUID.randomUUID(), - "", - emptyMap(), - SimTraceWorkload( - sequenceOf( - SimTraceWorkload.Fragment(0, 28L * duration, duration * 1000, 28.0, 2), - SimTraceWorkload.Fragment(0, 3100L * duration, duration * 1000, 3100.0, 2), - SimTraceWorkload.Fragment(0, 0, duration * 1000, 0.0, 2), - SimTraceWorkload.Fragment(0, 73L * duration, duration * 1000, 73.0, 2) - ) - ), - ) - - val metalDriver = - SimBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), machineModel) - - metalDriver.init() - metalDriver.setImage(vmm) - metalDriver.start() - - delay(5) - - val flavor = Flavor(2, 0) - val vmDriver = virtDriverWorkload.driver - vmDriver.events - .onEach { event -> - when (event) { - is HypervisorEvent.SliceFinished -> { - requestedBurst += event.requestedBurst - grantedBurst += event.grantedBurst - overcommissionedBurst += event.overcommissionedBurst - } - } - } - .launchIn(this) - - vmDriver.spawn("a", vmImageA, flavor) - vmDriver.spawn("b", vmImageB, flavor) - } - - scope.advanceUntilIdle() - - assertAll( - { assertEquals(emptyList(), scope.uncaughtExceptions, "No errors") }, - { assertEquals(2073600, requestedBurst, "Requested Burst does not match") }, - { assertEquals(2013600, grantedBurst, "Granted Burst does not match") }, - { assertEquals(60000, overcommissionedBurst, "Overcommissioned Burst does not match") }, - { assertEquals(1200007, scope.currentTime) } - ) - } -} diff --git a/simulator/opendc-experiments/opendc-experiments-sc18/src/main/kotlin/org/opendc/experiments/sc18/TestExperiment.kt b/simulator/opendc-experiments/opendc-experiments-sc18/src/main/kotlin/org/opendc/experiments/sc18/TestExperiment.kt index 4aef5be3..3786eebf 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc18/src/main/kotlin/org/opendc/experiments/sc18/TestExperiment.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc18/src/main/kotlin/org/opendc/experiments/sc18/TestExperiment.kt @@ -27,7 +27,7 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.test.TestCoroutineScope -import org.opendc.compute.metal.service.ProvisioningService +import org.opendc.compute.core.metal.service.ProvisioningService import org.opendc.format.environment.sc18.Sc18EnvironmentReader import org.opendc.format.trace.gwf.GwfTraceReader import org.opendc.simulator.utils.DelayControllerClockAdapter diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts b/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts index c0992fc9..d93c1713 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts +++ b/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts @@ -38,6 +38,7 @@ dependencies { implementation(project(":opendc-format")) implementation(project(":opendc-simulator:opendc-simulator-core")) implementation(project(":opendc-simulator:opendc-simulator-compute")) + implementation(project(":opendc-compute:opendc-compute-simulator")) implementation("com.github.ajalt:clikt:2.6.0") implementation("me.tongfei:progressbar:0.8.1") diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt index 3ff21558..805e34ee 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt @@ -34,15 +34,15 @@ import kotlinx.coroutines.launch import mu.KotlinLogging import org.opendc.compute.core.Flavor import org.opendc.compute.core.ServerEvent +import org.opendc.compute.core.metal.NODE_CLUSTER +import org.opendc.compute.core.metal.driver.BareMetalDriver +import org.opendc.compute.core.metal.service.ProvisioningService +import org.opendc.compute.core.virt.HypervisorEvent +import org.opendc.compute.core.virt.service.VirtProvisioningEvent import org.opendc.compute.core.workload.VmWorkload -import org.opendc.compute.metal.NODE_CLUSTER -import org.opendc.compute.metal.driver.BareMetalDriver -import org.opendc.compute.metal.service.ProvisioningService -import org.opendc.compute.virt.HypervisorEvent -import org.opendc.compute.virt.driver.SimVirtDriver -import org.opendc.compute.virt.service.SimpleVirtProvisioningService -import org.opendc.compute.virt.service.VirtProvisioningEvent -import org.opendc.compute.virt.service.allocation.AllocationPolicy +import org.opendc.compute.simulator.SimVirtDriver +import org.opendc.compute.simulator.SimVirtProvisioningService +import org.opendc.compute.simulator.allocation.AllocationPolicy import org.opendc.core.failure.CorrelatedFaultInjector import org.opendc.core.failure.FailureDomain import org.opendc.core.failure.FaultInjector @@ -140,14 +140,14 @@ public suspend fun createProvisioner( clock: Clock, environmentReader: EnvironmentReader, allocationPolicy: AllocationPolicy -): Pair { +): Pair { val environment = environmentReader.use { it.construct(coroutineScope, clock) } val bareMetalProvisioner = environment.platforms[0].zones[0].services[ProvisioningService] // Wait for the bare metal nodes to be spawned delay(10) - val scheduler = SimpleVirtProvisioningService(coroutineScope, clock, bareMetalProvisioner, allocationPolicy) + val scheduler = SimVirtProvisioningService(coroutineScope, clock, bareMetalProvisioner, allocationPolicy) // Wait for the hypervisors to be spawned delay(10) @@ -162,7 +162,7 @@ public suspend fun createProvisioner( public suspend fun attachMonitor( coroutineScope: CoroutineScope, clock: Clock, - scheduler: SimpleVirtProvisioningService, + scheduler: SimVirtProvisioningService, monitor: ExperimentMonitor ) { @@ -224,7 +224,7 @@ public suspend fun processTrace( coroutineScope: CoroutineScope, clock: Clock, reader: TraceReader, - scheduler: SimpleVirtProvisioningService, + scheduler: SimVirtProvisioningService, chan: Channel, monitor: ExperimentMonitor, vmPlacements: Map = emptyMap() diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/Run.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/Run.kt index 95987d07..7a5b84e0 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/Run.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/Run.kt @@ -28,7 +28,7 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestCoroutineScope import mu.KotlinLogging -import org.opendc.compute.virt.service.allocation.* +import org.opendc.compute.simulator.allocation.* import org.opendc.experiments.sc20.experiment.model.CompositeWorkload import org.opendc.experiments.sc20.experiment.monitor.ParquetExperimentMonitor import org.opendc.experiments.sc20.runner.TrialExperimentDescriptor diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/monitor/ExperimentMonitor.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/monitor/ExperimentMonitor.kt index 0ac532a4..18ba2c33 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/monitor/ExperimentMonitor.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/monitor/ExperimentMonitor.kt @@ -23,8 +23,8 @@ package org.opendc.experiments.sc20.experiment.monitor import org.opendc.compute.core.Server -import org.opendc.compute.virt.driver.VirtDriver -import org.opendc.compute.virt.service.VirtProvisioningEvent +import org.opendc.compute.core.virt.driver.VirtDriver +import org.opendc.compute.core.virt.service.VirtProvisioningEvent import java.io.Closeable /** diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/monitor/ParquetExperimentMonitor.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/monitor/ParquetExperimentMonitor.kt index bb512ef7..6ff3b518 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/monitor/ParquetExperimentMonitor.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/monitor/ParquetExperimentMonitor.kt @@ -24,8 +24,8 @@ package org.opendc.experiments.sc20.experiment.monitor import mu.KotlinLogging import org.opendc.compute.core.Server -import org.opendc.compute.virt.driver.VirtDriver -import org.opendc.compute.virt.service.VirtProvisioningEvent +import org.opendc.compute.core.virt.driver.VirtDriver +import org.opendc.compute.core.virt.service.VirtProvisioningEvent import org.opendc.experiments.sc20.telemetry.HostEvent import org.opendc.experiments.sc20.telemetry.ProvisionerEvent import org.opendc.experiments.sc20.telemetry.parquet.ParquetHostEventWriter diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20ParquetTraceReader.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20ParquetTraceReader.kt index 6ee53aec..d735ea4b 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20ParquetTraceReader.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20ParquetTraceReader.kt @@ -22,8 +22,8 @@ package org.opendc.experiments.sc20.trace -import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.core.workload.VmWorkload +import org.opendc.compute.simulator.SimWorkloadImage import org.opendc.experiments.sc20.experiment.model.CompositeWorkload import org.opendc.experiments.sc20.experiment.model.Workload import org.opendc.format.trace.TraceEntry diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20RawParquetTraceReader.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20RawParquetTraceReader.kt index b40cad7c..9bc1a58e 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20RawParquetTraceReader.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20RawParquetTraceReader.kt @@ -26,8 +26,8 @@ import mu.KotlinLogging import org.apache.avro.generic.GenericData import org.apache.hadoop.fs.Path import org.apache.parquet.avro.AvroParquetReader -import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.core.workload.VmWorkload +import org.opendc.compute.simulator.SimWorkloadImage import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20StreamingParquetTraceReader.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20StreamingParquetTraceReader.kt index 0afdc5fd..edef276c 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20StreamingParquetTraceReader.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20StreamingParquetTraceReader.kt @@ -31,8 +31,8 @@ import org.apache.parquet.filter2.predicate.FilterApi import org.apache.parquet.filter2.predicate.Statistics import org.apache.parquet.filter2.predicate.UserDefinedPredicate import org.apache.parquet.io.api.Binary -import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.core.workload.VmWorkload +import org.opendc.compute.simulator.SimWorkloadImage import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/WorkloadSampler.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/WorkloadSampler.kt index e8cc2c36..fa225cb0 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/WorkloadSampler.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/WorkloadSampler.kt @@ -23,8 +23,8 @@ package org.opendc.experiments.sc20.trace import mu.KotlinLogging -import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.core.workload.VmWorkload +import org.opendc.compute.simulator.SimWorkloadImage import org.opendc.experiments.sc20.experiment.model.CompositeWorkload import org.opendc.experiments.sc20.experiment.model.SamplingStrategy import org.opendc.experiments.sc20.experiment.model.Workload diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/test/kotlin/org/opendc/experiments/sc20/Sc20IntegrationTest.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/test/kotlin/org/opendc/experiments/sc20/Sc20IntegrationTest.kt index ff6e4bb9..9c44edfc 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/test/kotlin/org/opendc/experiments/sc20/Sc20IntegrationTest.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/test/kotlin/org/opendc/experiments/sc20/Sc20IntegrationTest.kt @@ -34,8 +34,8 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll import org.opendc.compute.core.Server import org.opendc.compute.core.workload.VmWorkload -import org.opendc.compute.virt.service.SimpleVirtProvisioningService -import org.opendc.compute.virt.service.allocation.AvailableCoreMemoryAllocationPolicy +import org.opendc.compute.simulator.SimVirtProvisioningService +import org.opendc.compute.simulator.allocation.AvailableCoreMemoryAllocationPolicy import org.opendc.experiments.sc20.experiment.attachMonitor import org.opendc.experiments.sc20.experiment.createFailureDomain import org.opendc.experiments.sc20.experiment.createProvisioner @@ -96,7 +96,7 @@ class Sc20IntegrationTest { val allocationPolicy = AvailableCoreMemoryAllocationPolicy() val traceReader = createTestTraceReader() val environmentReader = createTestEnvironmentReader() - lateinit var scheduler: SimpleVirtProvisioningService + lateinit var scheduler: SimVirtProvisioningService testScope.launch { val res = createProvisioner( @@ -159,7 +159,7 @@ class Sc20IntegrationTest { val allocationPolicy = AvailableCoreMemoryAllocationPolicy() val traceReader = createTestTraceReader(0.5, seed) val environmentReader = createTestEnvironmentReader("single") - lateinit var scheduler: SimpleVirtProvisioningService + lateinit var scheduler: SimVirtProvisioningService testScope.launch { val res = createProvisioner( diff --git a/simulator/opendc-format/build.gradle.kts b/simulator/opendc-format/build.gradle.kts index 3ab704bd..30aafc90 100644 --- a/simulator/opendc-format/build.gradle.kts +++ b/simulator/opendc-format/build.gradle.kts @@ -29,9 +29,10 @@ plugins { dependencies { api(project(":opendc-core")) - api(project(":opendc-compute")) + api(project(":opendc-compute:opendc-compute-core")) api(project(":opendc-workflows")) implementation(project(":opendc-simulator:opendc-simulator-compute")) + implementation(project(":opendc-compute:opendc-compute-simulator")) implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8") { exclude("org.jetbrains.kotlin", module = "kotlin-reflect") } diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc18/Sc18EnvironmentReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc18/Sc18EnvironmentReader.kt index 3fde1a6c..2e3e4a73 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc18/Sc18EnvironmentReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc18/Sc18EnvironmentReader.kt @@ -26,9 +26,9 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue import kotlinx.coroutines.CoroutineScope -import org.opendc.compute.metal.driver.SimBareMetalDriver -import org.opendc.compute.metal.service.ProvisioningService -import org.opendc.compute.metal.service.SimpleProvisioningService +import org.opendc.compute.core.metal.service.ProvisioningService +import org.opendc.compute.core.metal.service.SimpleProvisioningService +import org.opendc.compute.simulator.SimBareMetalDriver import org.opendc.core.Environment import org.opendc.core.Platform import org.opendc.core.Zone diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20ClusterEnvironmentReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20ClusterEnvironmentReader.kt index 63539d61..b4c71c4e 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20ClusterEnvironmentReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20ClusterEnvironmentReader.kt @@ -23,11 +23,11 @@ package org.opendc.format.environment.sc20 import kotlinx.coroutines.CoroutineScope -import org.opendc.compute.metal.NODE_CLUSTER -import org.opendc.compute.metal.driver.SimBareMetalDriver -import org.opendc.compute.metal.power.LinearLoadPowerModel -import org.opendc.compute.metal.service.ProvisioningService -import org.opendc.compute.metal.service.SimpleProvisioningService +import org.opendc.compute.core.metal.NODE_CLUSTER +import org.opendc.compute.core.metal.service.ProvisioningService +import org.opendc.compute.core.metal.service.SimpleProvisioningService +import org.opendc.compute.simulator.SimBareMetalDriver +import org.opendc.compute.simulator.power.LinearLoadPowerModel import org.opendc.core.Environment import org.opendc.core.Platform import org.opendc.core.Zone diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20EnvironmentReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20EnvironmentReader.kt index 36136888..94237cb9 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20EnvironmentReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/environment/sc20/Sc20EnvironmentReader.kt @@ -26,10 +26,10 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue import kotlinx.coroutines.CoroutineScope -import org.opendc.compute.metal.driver.SimBareMetalDriver -import org.opendc.compute.metal.power.LinearLoadPowerModel -import org.opendc.compute.metal.service.ProvisioningService -import org.opendc.compute.metal.service.SimpleProvisioningService +import org.opendc.compute.core.metal.service.ProvisioningService +import org.opendc.compute.core.metal.service.SimpleProvisioningService +import org.opendc.compute.simulator.SimBareMetalDriver +import org.opendc.compute.simulator.power.LinearLoadPowerModel import org.opendc.core.Environment import org.opendc.core.Platform import org.opendc.core.Zone diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/bitbrains/BitbrainsTraceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/bitbrains/BitbrainsTraceReader.kt index 6292a9cc..9353ef28 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/bitbrains/BitbrainsTraceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/bitbrains/BitbrainsTraceReader.kt @@ -22,8 +22,8 @@ package org.opendc.format.trace.bitbrains -import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.core.workload.VmWorkload +import org.opendc.compute.simulator.SimWorkloadImage import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/gwf/GwfTraceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/gwf/GwfTraceReader.kt index 53bc3956..a20b4f29 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/gwf/GwfTraceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/gwf/GwfTraceReader.kt @@ -22,7 +22,7 @@ package org.opendc.format.trace.gwf -import org.opendc.compute.core.image.SimWorkloadImage +import org.opendc.compute.simulator.SimWorkloadImage import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt index 558003a7..6d854fef 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt @@ -22,8 +22,8 @@ package org.opendc.format.trace.sc20 -import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.core.workload.VmWorkload +import org.opendc.compute.simulator.SimWorkloadImage import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/swf/SwfTraceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/swf/SwfTraceReader.kt index 5f312d01..f7b8a072 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/swf/SwfTraceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/swf/SwfTraceReader.kt @@ -22,8 +22,8 @@ package org.opendc.format.trace.swf -import org.opendc.compute.core.image.SimWorkloadImage import org.opendc.compute.core.workload.VmWorkload +import org.opendc.compute.simulator.SimWorkloadImage import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/wtf/WtfTraceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/wtf/WtfTraceReader.kt index 09f2764b..e43d68d8 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/wtf/WtfTraceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/wtf/WtfTraceReader.kt @@ -25,7 +25,7 @@ package org.opendc.format.trace.wtf import org.apache.avro.generic.GenericRecord import org.apache.hadoop.fs.Path import org.apache.parquet.avro.AvroParquetReader -import org.opendc.compute.core.image.SimWorkloadImage +import org.opendc.compute.simulator.SimWorkloadImage import org.opendc.core.User import org.opendc.format.trace.TraceEntry import org.opendc.format.trace.TraceReader diff --git a/simulator/opendc-format/src/test/kotlin/org/opendc/format/trace/swf/SwfTraceReaderTest.kt b/simulator/opendc-format/src/test/kotlin/org/opendc/format/trace/swf/SwfTraceReaderTest.kt index 78066c61..8db2ab40 100644 --- a/simulator/opendc-format/src/test/kotlin/org/opendc/format/trace/swf/SwfTraceReaderTest.kt +++ b/simulator/opendc-format/src/test/kotlin/org/opendc/format/trace/swf/SwfTraceReaderTest.kt @@ -24,7 +24,7 @@ package org.opendc.format.trace.swf import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test -import org.opendc.compute.core.image.SimWorkloadImage +import org.opendc.compute.simulator.SimWorkloadImage import org.opendc.simulator.compute.workload.SimTraceWorkload import java.io.File diff --git a/simulator/opendc-runner-web/build.gradle.kts b/simulator/opendc-runner-web/build.gradle.kts index 9e2c5277..cf437843 100644 --- a/simulator/opendc-runner-web/build.gradle.kts +++ b/simulator/opendc-runner-web/build.gradle.kts @@ -34,7 +34,7 @@ application { dependencies { api(project(":opendc-core")) - implementation(project(":opendc-compute")) + implementation(project(":opendc-compute:opendc-compute-simulator")) implementation(project(":opendc-format")) implementation(project(":opendc-experiments:opendc-experiments-sc20")) implementation(project(":opendc-simulator:opendc-simulator-core")) diff --git a/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/Main.kt b/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/Main.kt index 2d4272ab..ca500770 100644 --- a/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/Main.kt +++ b/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/Main.kt @@ -38,7 +38,7 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.test.TestCoroutineScope import mu.KotlinLogging import org.bson.Document -import org.opendc.compute.virt.service.allocation.* +import org.opendc.compute.simulator.allocation.* import org.opendc.experiments.sc20.experiment.attachMonitor import org.opendc.experiments.sc20.experiment.createFailureDomain import org.opendc.experiments.sc20.experiment.createProvisioner diff --git a/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/TopologyParser.kt b/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/TopologyParser.kt index cdcf1b08..244f2a3d 100644 --- a/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/TopologyParser.kt +++ b/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/TopologyParser.kt @@ -31,11 +31,11 @@ import com.mongodb.client.model.Projections import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import org.bson.Document -import org.opendc.compute.metal.NODE_CLUSTER -import org.opendc.compute.metal.driver.SimBareMetalDriver -import org.opendc.compute.metal.power.LinearLoadPowerModel -import org.opendc.compute.metal.service.ProvisioningService -import org.opendc.compute.metal.service.SimpleProvisioningService +import org.opendc.compute.core.metal.NODE_CLUSTER +import org.opendc.compute.core.metal.service.ProvisioningService +import org.opendc.compute.core.metal.service.SimpleProvisioningService +import org.opendc.compute.simulator.SimBareMetalDriver +import org.opendc.compute.simulator.power.LinearLoadPowerModel import org.opendc.core.Environment import org.opendc.core.Platform import org.opendc.core.Zone diff --git a/simulator/opendc-workflows/build.gradle.kts b/simulator/opendc-workflows/build.gradle.kts index b30ecd39..f61bdac6 100644 --- a/simulator/opendc-workflows/build.gradle.kts +++ b/simulator/opendc-workflows/build.gradle.kts @@ -29,7 +29,7 @@ plugins { dependencies { api(project(":opendc-core")) - api(project(":opendc-compute")) + api(project(":opendc-compute:opendc-compute-core")) implementation(project(":opendc-utils")) testImplementation(project(":opendc-simulator:opendc-simulator-core")) diff --git a/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/StageWorkflowService.kt b/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/StageWorkflowService.kt index bc08b22d..3b4e6eab 100644 --- a/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/StageWorkflowService.kt +++ b/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/StageWorkflowService.kt @@ -31,8 +31,8 @@ import kotlinx.coroutines.launch import org.opendc.compute.core.Server import org.opendc.compute.core.ServerEvent import org.opendc.compute.core.ServerState -import org.opendc.compute.metal.Node -import org.opendc.compute.metal.service.ProvisioningService +import org.opendc.compute.core.metal.Node +import org.opendc.compute.core.metal.service.ProvisioningService import org.opendc.utils.flow.EventFlow import org.opendc.workflows.service.stage.job.JobAdmissionPolicy import org.opendc.workflows.service.stage.job.JobOrderPolicy diff --git a/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/TaskState.kt b/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/TaskState.kt index ddfbeae3..ed023c82 100644 --- a/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/TaskState.kt +++ b/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/TaskState.kt @@ -22,7 +22,7 @@ package org.opendc.workflows.service -import org.opendc.compute.metal.Node +import org.opendc.compute.core.metal.Node import org.opendc.workflows.workload.Task public class TaskState(public val job: JobState, public val task: Task) { diff --git a/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/FirstFitResourceSelectionPolicy.kt b/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/FirstFitResourceSelectionPolicy.kt index 38b37140..8dc323ec 100644 --- a/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/FirstFitResourceSelectionPolicy.kt +++ b/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/FirstFitResourceSelectionPolicy.kt @@ -22,7 +22,7 @@ package org.opendc.workflows.service.stage.resource -import org.opendc.compute.metal.Node +import org.opendc.compute.core.metal.Node import org.opendc.workflows.service.StageWorkflowService /** diff --git a/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/FunctionalResourceFilterPolicy.kt b/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/FunctionalResourceFilterPolicy.kt index 9381ffd7..ac79a9ce 100644 --- a/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/FunctionalResourceFilterPolicy.kt +++ b/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/FunctionalResourceFilterPolicy.kt @@ -22,7 +22,7 @@ package org.opendc.workflows.service.stage.resource -import org.opendc.compute.metal.Node +import org.opendc.compute.core.metal.Node import org.opendc.workflows.service.StageWorkflowService import org.opendc.workflows.service.TaskState diff --git a/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/RandomResourceSelectionPolicy.kt b/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/RandomResourceSelectionPolicy.kt index b31a6217..caf87c70 100644 --- a/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/RandomResourceSelectionPolicy.kt +++ b/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/RandomResourceSelectionPolicy.kt @@ -22,7 +22,7 @@ package org.opendc.workflows.service.stage.resource -import org.opendc.compute.metal.Node +import org.opendc.compute.core.metal.Node import org.opendc.workflows.service.StageWorkflowService import java.util.* diff --git a/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/ResourceFilterPolicy.kt b/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/ResourceFilterPolicy.kt index 9f44fddf..4923a34b 100644 --- a/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/ResourceFilterPolicy.kt +++ b/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/ResourceFilterPolicy.kt @@ -22,7 +22,7 @@ package org.opendc.workflows.service.stage.resource -import org.opendc.compute.metal.Node +import org.opendc.compute.core.metal.Node import org.opendc.workflows.service.TaskState import org.opendc.workflows.service.stage.StagePolicy diff --git a/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/ResourceSelectionPolicy.kt b/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/ResourceSelectionPolicy.kt index 3682844a..990b990a 100644 --- a/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/ResourceSelectionPolicy.kt +++ b/simulator/opendc-workflows/src/main/kotlin/org/opendc/workflows/service/stage/resource/ResourceSelectionPolicy.kt @@ -22,7 +22,7 @@ package org.opendc.workflows.service.stage.resource -import org.opendc.compute.metal.Node +import org.opendc.compute.core.metal.Node import org.opendc.workflows.service.stage.StagePolicy /** diff --git a/simulator/opendc-workflows/src/test/kotlin/org/opendc/workflows/service/StageWorkflowSchedulerIntegrationTest.kt b/simulator/opendc-workflows/src/test/kotlin/org/opendc/workflows/service/StageWorkflowSchedulerIntegrationTest.kt index 6e9e7430..90cf5b99 100644 --- a/simulator/opendc-workflows/src/test/kotlin/org/opendc/workflows/service/StageWorkflowSchedulerIntegrationTest.kt +++ b/simulator/opendc-workflows/src/test/kotlin/org/opendc/workflows/service/StageWorkflowSchedulerIntegrationTest.kt @@ -35,7 +35,7 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotEquals import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -import org.opendc.compute.metal.service.ProvisioningService +import org.opendc.compute.core.metal.service.ProvisioningService import org.opendc.format.environment.sc18.Sc18EnvironmentReader import org.opendc.format.trace.gwf.GwfTraceReader import org.opendc.simulator.utils.DelayControllerClockAdapter diff --git a/simulator/settings.gradle.kts b/simulator/settings.gradle.kts index 3de05fe1..418b00fb 100644 --- a/simulator/settings.gradle.kts +++ b/simulator/settings.gradle.kts @@ -22,7 +22,8 @@ rootProject.name = "opendc-simulator" include(":opendc-core") -include(":opendc-compute") +include(":opendc-compute:opendc-compute-core") +include(":opendc-compute:opendc-compute-simulator") include(":opendc-workflows") include(":opendc-format") include(":opendc-experiments:opendc-experiments-sc18") -- cgit v1.2.3 From e06c953d1c71c704b351c81611e2e2bececf8e67 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Mon, 5 Oct 2020 12:12:32 +0200 Subject: Move failure models to separate module --- .../compute/core/metal/driver/BareMetalDriver.kt | 3 +- .../opendc-compute-simulator/build.gradle.kts | 1 + .../opendc/compute/simulator/SimBareMetalDriver.kt | 3 +- .../opendc/core/failure/CorrelatedFaultInjector.kt | 129 --------------------- .../org/opendc/core/failure/FailureDomain.kt | 47 -------- .../org/opendc/core/failure/FaultInjector.kt | 33 ------ .../core/failure/UncorrelatedFaultInjector.kt | 61 ---------- .../opendc-experiments-sc20/build.gradle.kts | 1 + .../sc20/experiment/ExperimentHelpers.kt | 6 +- .../opendc-simulator-failures/build.gradle.kts | 31 +++++ .../simulator/failures/CorrelatedFaultInjector.kt | 129 +++++++++++++++++++++ .../org/opendc/simulator/failures/FailureDomain.kt | 47 ++++++++ .../org/opendc/simulator/failures/FaultInjector.kt | 33 ++++++ .../failures/UncorrelatedFaultInjector.kt | 61 ++++++++++ simulator/settings.gradle.kts | 1 + 15 files changed, 310 insertions(+), 276 deletions(-) delete mode 100644 simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/CorrelatedFaultInjector.kt delete mode 100644 simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/FailureDomain.kt delete mode 100644 simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/FaultInjector.kt delete mode 100644 simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/UncorrelatedFaultInjector.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-failures/build.gradle.kts create mode 100644 simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/CorrelatedFaultInjector.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/FailureDomain.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/FaultInjector.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/UncorrelatedFaultInjector.kt diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/driver/BareMetalDriver.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/driver/BareMetalDriver.kt index 6f58aa20..8697a553 100644 --- a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/driver/BareMetalDriver.kt +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/driver/BareMetalDriver.kt @@ -26,7 +26,6 @@ import kotlinx.coroutines.flow.Flow import org.opendc.compute.core.Server import org.opendc.compute.core.image.Image import org.opendc.compute.core.metal.Node -import org.opendc.core.failure.FailureDomain import org.opendc.core.power.Powerable import org.opendc.core.services.AbstractServiceKey import java.util.UUID @@ -34,7 +33,7 @@ import java.util.UUID /** * A driver interface for the management interface of a bare-metal compute node. */ -public interface BareMetalDriver : Powerable, FailureDomain { +public interface BareMetalDriver : Powerable { /** * The [Node] that is controlled by this driver. */ diff --git a/simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts b/simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts index e1ec6421..d7570e54 100644 --- a/simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts +++ b/simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { implementation(project(":opendc-utils")) implementation("io.github.microutils:kotlin-logging:1.7.9") implementation(project(":opendc-simulator:opendc-simulator-compute")) + api(project(":opendc-simulator:opendc-simulator-failures")) testImplementation(project(":opendc-simulator:opendc-simulator-core")) testRuntimeOnly("org.slf4j:slf4j-simple:${Library.SLF4J}") 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 08bb7e79..7b1f0af6 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 @@ -41,6 +41,7 @@ import org.opendc.simulator.compute.SimBareMetalMachine import org.opendc.simulator.compute.SimExecutionContext import org.opendc.simulator.compute.SimMachineModel 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 @@ -67,7 +68,7 @@ public class SimBareMetalDriver( metadata: Map, machine: SimMachineModel, powerModel: PowerModel = ConstantPowerModel(0.0) -) : BareMetalDriver { +) : BareMetalDriver, FailureDomain { /** * The flavor that corresponds to this machine. */ diff --git a/simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/CorrelatedFaultInjector.kt b/simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/CorrelatedFaultInjector.kt deleted file mode 100644 index f3bd7f5a..00000000 --- a/simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/CorrelatedFaultInjector.kt +++ /dev/null @@ -1,129 +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.core.failure - -import kotlinx.coroutines.* -import java.time.Clock -import kotlin.math.exp -import kotlin.math.max -import kotlin.random.Random -import kotlin.random.asJavaRandom - -/** - * A [FaultInjector] that injects fault in the system which are correlated to each other. Failures do not occur in - * isolation, but will trigger other faults. - */ -public class CorrelatedFaultInjector( - private val coroutineScope: CoroutineScope, - private val clock: Clock, - private val iatScale: Double, - private val iatShape: Double, - private val sizeScale: Double, - private val sizeShape: Double, - private val dScale: Double, - private val dShape: Double, - random: Random = Random(0) -) : FaultInjector { - /** - * The active failure domains that have been registered. - */ - private val active = mutableSetOf() - - /** - * The [Job] that awaits the nearest fault in the system. - */ - private var job: Job? = null - - /** - * The [Random] instance to use. - */ - private val random: java.util.Random = random.asJavaRandom() - - /** - * Enqueue the specified [FailureDomain] to fail some time in the future. - */ - override fun enqueue(domain: FailureDomain) { - active += domain - - // Clean up the domain if it finishes - domain.scope.coroutineContext[Job]!!.invokeOnCompletion { - this@CorrelatedFaultInjector.coroutineScope.launch { - active -= domain - - if (active.isEmpty()) { - job?.cancel() - job = null - } - } - } - - if (job != null) { - return - } - - job = this.coroutineScope.launch { - while (active.isNotEmpty()) { - ensureActive() - - // Make sure to convert delay from hours to milliseconds - val d = lognvariate(iatScale, iatShape) * 3.6e6 - - // Handle long overflow - if (clock.millis() + d <= 0) { - return@launch - } - - delay(d.toLong()) - - val n = lognvariate(sizeScale, sizeShape).toInt() - val targets = active.shuffled(random).take(n) - - for (failureDomain in targets) { - active -= failureDomain - failureDomain.fail() - } - - val df = max(lognvariate(dScale, dShape) * 6e4, 15 * 6e4) - - // Handle long overflow - if (clock.millis() + df <= 0) { - return@launch - } - - delay(df.toLong()) - - for (failureDomain in targets) { - failureDomain.recover() - - // Re-enqueue machine to be failed - enqueue(failureDomain) - } - } - - job = null - } - } - - // XXX We should extract this in some common package later on. - private fun lognvariate(scale: Double, shape: Double) = exp(scale + shape * random.nextGaussian()) -} diff --git a/simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/FailureDomain.kt b/simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/FailureDomain.kt deleted file mode 100644 index 0795b09a..00000000 --- a/simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/FailureDomain.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * MIT License - * - * 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.core.failure - -import kotlinx.coroutines.CoroutineScope - -/** - * A logical or physical component in a computing environment which may fail. - */ -public interface FailureDomain { - /** - * The lifecycle of the failure domain to which a [FaultInjector] will attach. - */ - public val scope: CoroutineScope - - /** - * Fail the domain externally. - */ - public suspend fun fail() - - /** - * Resume the failure domain. - */ - public suspend fun recover() -} diff --git a/simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/FaultInjector.kt b/simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/FaultInjector.kt deleted file mode 100644 index ad776641..00000000 --- a/simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/FaultInjector.kt +++ /dev/null @@ -1,33 +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.core.failure - -/** - * An interface for stochastically injecting faults into a running system. - */ -public interface FaultInjector { - /** - * Enqueue the specified [FailureDomain] into the queue as candidate for failure injection in the future. - */ - public fun enqueue(domain: FailureDomain) -} diff --git a/simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/UncorrelatedFaultInjector.kt b/simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/UncorrelatedFaultInjector.kt deleted file mode 100644 index f64b8815..00000000 --- a/simulator/opendc-core/src/main/kotlin/org/opendc/core/failure/UncorrelatedFaultInjector.kt +++ /dev/null @@ -1,61 +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.core.failure - -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import java.time.Clock -import kotlin.math.ln1p -import kotlin.math.pow -import kotlin.random.Random - -/** - * A [FaultInjector] that injects uncorrelated faults into the system, meaning that failures of the subsystems are - * independent. - */ -public class UncorrelatedFaultInjector( - private val clock: Clock, - private val alpha: Double, - private val beta: Double, - private val random: Random = Random(0) -) : FaultInjector { - /** - * Enqueue the specified [FailureDomain] to fail some time in the future. - */ - override fun enqueue(domain: FailureDomain) { - domain.scope.launch { - val d = random.weibull(alpha, beta) * 1e3 // Make sure to convert delay to milliseconds - - // Handle long overflow - if (clock.millis() + d <= 0) { - return@launch - } - - delay(d.toLong()) - domain.fail() - } - } - - // XXX We should extract this in some common package later on. - private fun Random.weibull(alpha: Double, beta: Double) = (beta * (-ln1p(-nextDouble())).pow(1.0 / alpha)) -} diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts b/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts index d93c1713..3b682668 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts +++ b/simulator/opendc-experiments/opendc-experiments-sc20/build.gradle.kts @@ -38,6 +38,7 @@ dependencies { implementation(project(":opendc-format")) implementation(project(":opendc-simulator:opendc-simulator-core")) implementation(project(":opendc-simulator:opendc-simulator-compute")) + implementation(project(":opendc-simulator:opendc-simulator-failures")) implementation(project(":opendc-compute:opendc-compute-simulator")) implementation("com.github.ajalt:clikt:2.6.0") diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt index 805e34ee..59498c5b 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt @@ -43,14 +43,14 @@ import org.opendc.compute.core.workload.VmWorkload import org.opendc.compute.simulator.SimVirtDriver import org.opendc.compute.simulator.SimVirtProvisioningService import org.opendc.compute.simulator.allocation.AllocationPolicy -import org.opendc.core.failure.CorrelatedFaultInjector -import org.opendc.core.failure.FailureDomain -import org.opendc.core.failure.FaultInjector import org.opendc.experiments.sc20.experiment.monitor.ExperimentMonitor import org.opendc.experiments.sc20.trace.Sc20StreamingParquetTraceReader import org.opendc.format.environment.EnvironmentReader import org.opendc.format.trace.TraceReader import org.opendc.simulator.compute.interference.PerformanceInterferenceModel +import org.opendc.simulator.failures.CorrelatedFaultInjector +import org.opendc.simulator.failures.FailureDomain +import org.opendc.simulator.failures.FaultInjector import java.io.File import java.time.Clock import kotlin.math.ln diff --git a/simulator/opendc-simulator/opendc-simulator-failures/build.gradle.kts b/simulator/opendc-simulator/opendc-simulator-failures/build.gradle.kts new file mode 100644 index 00000000..1c30506f --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-failures/build.gradle.kts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +description = "Failure models for OpenDC" + +plugins { + `kotlin-library-convention` +} + +dependencies { + api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Library.KOTLINX_COROUTINES}") +} diff --git a/simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/CorrelatedFaultInjector.kt b/simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/CorrelatedFaultInjector.kt new file mode 100644 index 00000000..0e15f338 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/CorrelatedFaultInjector.kt @@ -0,0 +1,129 @@ +/* + * 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.simulator.failures + +import kotlinx.coroutines.* +import java.time.Clock +import kotlin.math.exp +import kotlin.math.max +import kotlin.random.Random +import kotlin.random.asJavaRandom + +/** + * A [FaultInjector] that injects fault in the system which are correlated to each other. Failures do not occur in + * isolation, but will trigger other faults. + */ +public class CorrelatedFaultInjector( + private val coroutineScope: CoroutineScope, + private val clock: Clock, + private val iatScale: Double, + private val iatShape: Double, + private val sizeScale: Double, + private val sizeShape: Double, + private val dScale: Double, + private val dShape: Double, + random: Random = Random(0) +) : FaultInjector { + /** + * The active failure domains that have been registered. + */ + private val active = mutableSetOf() + + /** + * The [Job] that awaits the nearest fault in the system. + */ + private var job: Job? = null + + /** + * The [Random] instance to use. + */ + private val random: java.util.Random = random.asJavaRandom() + + /** + * Enqueue the specified [FailureDomain] to fail some time in the future. + */ + override fun enqueue(domain: FailureDomain) { + active += domain + + // Clean up the domain if it finishes + domain.scope.coroutineContext[Job]!!.invokeOnCompletion { + this@CorrelatedFaultInjector.coroutineScope.launch { + active -= domain + + if (active.isEmpty()) { + job?.cancel() + job = null + } + } + } + + if (job != null) { + return + } + + job = this.coroutineScope.launch { + while (active.isNotEmpty()) { + ensureActive() + + // Make sure to convert delay from hours to milliseconds + val d = lognvariate(iatScale, iatShape) * 3.6e6 + + // Handle long overflow + if (clock.millis() + d <= 0) { + return@launch + } + + delay(d.toLong()) + + val n = lognvariate(sizeScale, sizeShape).toInt() + val targets = active.shuffled(random).take(n) + + for (failureDomain in targets) { + active -= failureDomain + failureDomain.fail() + } + + val df = max(lognvariate(dScale, dShape) * 6e4, 15 * 6e4) + + // Handle long overflow + if (clock.millis() + df <= 0) { + return@launch + } + + delay(df.toLong()) + + for (failureDomain in targets) { + failureDomain.recover() + + // Re-enqueue machine to be failed + enqueue(failureDomain) + } + } + + job = null + } + } + + // XXX We should extract this in some common package later on. + private fun lognvariate(scale: Double, shape: Double) = exp(scale + shape * random.nextGaussian()) +} diff --git a/simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/FailureDomain.kt b/simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/FailureDomain.kt new file mode 100644 index 00000000..dc3006e8 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/FailureDomain.kt @@ -0,0 +1,47 @@ +/* + * MIT License + * + * 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.simulator.failures + +import kotlinx.coroutines.CoroutineScope + +/** + * A logical or physical component in a computing environment which may fail. + */ +public interface FailureDomain { + /** + * The lifecycle of the failure domain to which a [FaultInjector] will attach. + */ + public val scope: CoroutineScope + + /** + * Fail the domain externally. + */ + public suspend fun fail() + + /** + * Resume the failure domain. + */ + public suspend fun recover() +} diff --git a/simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/FaultInjector.kt b/simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/FaultInjector.kt new file mode 100644 index 00000000..a866260c --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/FaultInjector.kt @@ -0,0 +1,33 @@ +/* + * 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.simulator.failures + +/** + * An interface for stochastically injecting faults into a running system. + */ +public interface FaultInjector { + /** + * Enqueue the specified [FailureDomain] into the queue as candidate for failure injection in the future. + */ + public fun enqueue(domain: FailureDomain) +} diff --git a/simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/UncorrelatedFaultInjector.kt b/simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/UncorrelatedFaultInjector.kt new file mode 100644 index 00000000..b3bd737e --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/UncorrelatedFaultInjector.kt @@ -0,0 +1,61 @@ +/* + * 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.simulator.failures + +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import java.time.Clock +import kotlin.math.ln1p +import kotlin.math.pow +import kotlin.random.Random + +/** + * A [FaultInjector] that injects uncorrelated faults into the system, meaning that failures of the subsystems are + * independent. + */ +public class UncorrelatedFaultInjector( + private val clock: Clock, + private val alpha: Double, + private val beta: Double, + private val random: Random = Random(0) +) : FaultInjector { + /** + * Enqueue the specified [FailureDomain] to fail some time in the future. + */ + override fun enqueue(domain: FailureDomain) { + domain.scope.launch { + val d = random.weibull(alpha, beta) * 1e3 // Make sure to convert delay to milliseconds + + // Handle long overflow + if (clock.millis() + d <= 0) { + return@launch + } + + delay(d.toLong()) + domain.fail() + } + } + + // XXX We should extract this in some common package later on. + private fun Random.weibull(alpha: Double, beta: Double) = (beta * (-ln1p(-nextDouble())).pow(1.0 / alpha)) +} diff --git a/simulator/settings.gradle.kts b/simulator/settings.gradle.kts index 418b00fb..935a18d0 100644 --- a/simulator/settings.gradle.kts +++ b/simulator/settings.gradle.kts @@ -31,4 +31,5 @@ include(":opendc-experiments:opendc-experiments-sc20") include(":opendc-runner-web") include(":opendc-simulator:opendc-simulator-core") include(":opendc-simulator:opendc-simulator-compute") +include(":opendc-simulator:opendc-simulator-failures") include(":opendc-utils") -- cgit v1.2.3 From feda2ade4069f0582feed0873c9a0d0a2e5401f6 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Mon, 5 Oct 2020 12:16:57 +0200 Subject: Move power models outside opendc-core --- .../compute/core/metal/driver/BareMetalDriver.kt | 3 +- .../opendc/compute/simulator/SimBareMetalDriver.kt | 5 +-- .../opendc/compute/simulator/power/PowerModel.kt | 32 +++++++++++++++++++ .../opendc/compute/simulator/power/PowerModels.kt | 1 - .../opendc/compute/simulator/power/Powerable.kt | 37 ++++++++++++++++++++++ .../kotlin/org/opendc/core/power/PowerModel.kt | 32 ------------------- .../main/kotlin/org/opendc/core/power/Powerable.kt | 37 ---------------------- .../sc20/experiment/ExperimentHelpers.kt | 3 +- 8 files changed, 75 insertions(+), 75 deletions(-) create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/PowerModel.kt create mode 100644 simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/Powerable.kt delete mode 100644 simulator/opendc-core/src/main/kotlin/org/opendc/core/power/PowerModel.kt delete mode 100644 simulator/opendc-core/src/main/kotlin/org/opendc/core/power/Powerable.kt diff --git a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/driver/BareMetalDriver.kt b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/driver/BareMetalDriver.kt index 8697a553..9db57127 100644 --- a/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/driver/BareMetalDriver.kt +++ b/simulator/opendc-compute/opendc-compute-core/src/main/kotlin/org/opendc/compute/core/metal/driver/BareMetalDriver.kt @@ -26,14 +26,13 @@ import kotlinx.coroutines.flow.Flow import org.opendc.compute.core.Server import org.opendc.compute.core.image.Image import org.opendc.compute.core.metal.Node -import org.opendc.core.power.Powerable import org.opendc.core.services.AbstractServiceKey import java.util.UUID /** * A driver interface for the management interface of a bare-metal compute node. */ -public interface BareMetalDriver : Powerable { +public interface BareMetalDriver { /** * The [Node] that is controlled by this driver. */ 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 7b1f0af6..97f550ba 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 @@ -35,7 +35,8 @@ 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.simulator.power.ConstantPowerModel -import org.opendc.core.power.PowerModel +import org.opendc.compute.simulator.power.PowerModel +import org.opendc.compute.simulator.power.Powerable import org.opendc.core.services.ServiceRegistry import org.opendc.simulator.compute.SimBareMetalMachine import org.opendc.simulator.compute.SimExecutionContext @@ -68,7 +69,7 @@ public class SimBareMetalDriver( metadata: Map, machine: SimMachineModel, powerModel: PowerModel = ConstantPowerModel(0.0) -) : BareMetalDriver, FailureDomain { +) : BareMetalDriver, FailureDomain, Powerable { /** * The flavor that corresponds to this machine. */ diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/PowerModel.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/PowerModel.kt new file mode 100644 index 00000000..174a510b --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/PowerModel.kt @@ -0,0 +1,32 @@ +/* + * MIT License + * + * 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.power + +import kotlinx.coroutines.flow.Flow + +/** + * A model for computing the power draw based on some value. + */ +public typealias PowerModel = (T) -> Flow diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/PowerModels.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/PowerModels.kt index 09651720..651eba6e 100644 --- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/PowerModels.kt +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/PowerModels.kt @@ -25,7 +25,6 @@ package org.opendc.compute.simulator.power import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import org.opendc.compute.core.metal.driver.BareMetalDriver -import org.opendc.core.power.PowerModel /** * A power model which emits a single value. diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/Powerable.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/Powerable.kt new file mode 100644 index 00000000..dcf74468 --- /dev/null +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/Powerable.kt @@ -0,0 +1,37 @@ +/* + * MIT License + * + * 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.power + +import kotlinx.coroutines.flow.Flow + +/** + * An entity that is uses power from some power source. + */ +public interface Powerable { + /** + * The power draw at the device's power supply in watts (W).w + */ + public val powerDraw: Flow +} diff --git a/simulator/opendc-core/src/main/kotlin/org/opendc/core/power/PowerModel.kt b/simulator/opendc-core/src/main/kotlin/org/opendc/core/power/PowerModel.kt deleted file mode 100644 index e93023d8..00000000 --- a/simulator/opendc-core/src/main/kotlin/org/opendc/core/power/PowerModel.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * MIT License - * - * 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.core.power - -import kotlinx.coroutines.flow.Flow - -/** - * A model for computing the power draw based on some value. - */ -public typealias PowerModel = (T) -> Flow diff --git a/simulator/opendc-core/src/main/kotlin/org/opendc/core/power/Powerable.kt b/simulator/opendc-core/src/main/kotlin/org/opendc/core/power/Powerable.kt deleted file mode 100644 index 4b73ad92..00000000 --- a/simulator/opendc-core/src/main/kotlin/org/opendc/core/power/Powerable.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * MIT License - * - * 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.core.power - -import kotlinx.coroutines.flow.Flow - -/** - * An entity that is uses power from some power source. - */ -public interface Powerable { - /** - * The power draw at the device's power supply in watts (W).w - */ - public val powerDraw: Flow -} diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt index 59498c5b..37e5d176 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt @@ -40,6 +40,7 @@ import org.opendc.compute.core.metal.service.ProvisioningService import org.opendc.compute.core.virt.HypervisorEvent import org.opendc.compute.core.virt.service.VirtProvisioningEvent import org.opendc.compute.core.workload.VmWorkload +import org.opendc.compute.simulator.SimBareMetalDriver import org.opendc.compute.simulator.SimVirtDriver import org.opendc.compute.simulator.SimVirtProvisioningService import org.opendc.compute.simulator.allocation.AllocationPolicy @@ -201,7 +202,7 @@ public suspend fun attachMonitor( } .launchIn(coroutineScope) - val driver = hypervisor.server.services[BareMetalDriver.Key] + val driver = hypervisor.server.services[BareMetalDriver.Key] as SimBareMetalDriver driver.powerDraw .onEach { monitor.reportPowerConsumption(hypervisor.server, it) } .launchIn(coroutineScope) -- cgit v1.2.3 From 374960cd15a2893a124c42975167f8bf6e45e868 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Mon, 5 Oct 2020 12:57:31 +0200 Subject: Address compilation warnings --- .../allocation/ComparableAllocationPolicyLogic.kt | 2 +- .../simulator/allocation/ReplayAllocationPolicy.kt | 4 ++-- .../kotlin/org/opendc/experiments/sc20/Main.kt | 3 +-- .../sc20/experiment/ExperimentHelpers.kt | 3 +-- .../org/opendc/experiments/sc20/experiment/Run.kt | 3 +-- .../experiment/monitor/ParquetExperimentMonitor.kt | 2 +- .../experiments/sc20/trace/Sc20TraceConverter.kt | 14 +++++++------- .../experiments/sc20/trace/WorkloadSampler.kt | 1 - .../opendc/format/trace/sc20/Sc20TraceReader.kt | 1 - .../org/opendc/format/trace/swf/SwfTraceReader.kt | 22 +++++++++++----------- .../org/opendc/format/trace/wtf/WtfTraceReader.kt | 2 -- .../src/main/kotlin/org/opendc/runner/web/Main.kt | 3 +-- .../kotlin/org/opendc/runner/web/TopologyParser.kt | 1 - 13 files changed, 26 insertions(+), 35 deletions(-) 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 index 6aff2bab..8defe8b7 100644 --- 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 @@ -44,6 +44,6 @@ public interface ComparableAllocationPolicyLogic : AllocationPolicy.Logic { val fitsCpu = hv.server.flavor.cpuCount >= image.flavor.cpuCount fitsMemory && fitsCpu } - .minWith(comparator.thenBy { it.server.uid }) + .minWithOrNull(comparator.thenBy { it.server.uid }) } } 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 index fda863bb..5312f4da 100644 --- 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 @@ -46,10 +46,10 @@ public class ReplayAllocationPolicy(private val vmPlacements: Map, scheduler: SimVirtProvisioningService, chan: Channel, - monitor: ExperimentMonitor, - vmPlacements: Map = emptyMap() + monitor: ExperimentMonitor ) { try { var submitted = 0 diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/Run.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/Run.kt index 7a5b84e0..660fc882 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/Run.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/Run.kt @@ -131,8 +131,7 @@ public data class Run(override val parent: Scenario, val id: Int, val seed: Int) trace, scheduler, chan, - monitor, - experiment.vmPlacements + monitor ) logger.debug("SUBMIT=${scheduler.submittedVms}") diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/monitor/ParquetExperimentMonitor.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/monitor/ParquetExperimentMonitor.kt index 6ff3b518..3eb9362c 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/monitor/ParquetExperimentMonitor.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/monitor/ParquetExperimentMonitor.kt @@ -57,7 +57,7 @@ public class ParquetExperimentMonitor(base: File, partition: String, bufferSize: startTime = time // Update timestamp of initial event - currentHostEvent.replaceAll { k, v -> v.copy(timestamp = startTime) } + currentHostEvent.replaceAll { _, v -> v.copy(timestamp = startTime) } } } diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20TraceConverter.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20TraceConverter.kt index dd6b15d0..bb2a75ee 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20TraceConverter.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/Sc20TraceConverter.kt @@ -187,11 +187,11 @@ public class SolvinityConversion : TraceConversion("Solvinity") { .filterNot { it.isDirectory } .filter { it.extension == "csv" || it.extension == "txt" } .toList() - .forEach { vmFile -> + .forEach file@{ vmFile -> BufferedReader(FileReader(vmFile)).use { reader -> reader.lineSequence() .chunked(128) - .forEachIndexed { idx, lines -> + .forEach { lines -> for (line in lines) { // Ignore comments in the trace if (line.startsWith("#") || line.isBlank()) { @@ -212,7 +212,7 @@ public class SolvinityConversion : TraceConversion("Solvinity") { if (timestamp < minTimestamp) { minTimestamp = timestamp } - return@forEach + return@file } } } @@ -229,13 +229,13 @@ public class SolvinityConversion : TraceConversion("Solvinity") { .filterNot { it.isDirectory } .filter { it.extension == "csv" || it.extension == "txt" } .toList() - .forEachIndexed { idx, vmFile -> + .forEach { vmFile -> println(vmFile) var vmId = "" var maxCores = -1 var requiredMemory = -1L - var cores = -1 + var cores: Int var minTime = Long.MAX_VALUE val flopsFragments = sequence { @@ -353,13 +353,13 @@ public class BitbrainsConversion : TraceConversion("Bitbrains") { .filterNot { it.isDirectory } .filter { it.extension == "csv" || it.extension == "txt" } .toList() - .forEachIndexed { idx, vmFile -> + .forEach { vmFile -> println(vmFile) var vmId = "" var maxCores = -1 var requiredMemory = -1L - var cores = -1 + var cores: Int var minTime = Long.MAX_VALUE val flopsFragments = sequence { diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/WorkloadSampler.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/WorkloadSampler.kt index fa225cb0..a8b83aef 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/WorkloadSampler.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/trace/WorkloadSampler.kt @@ -143,7 +143,6 @@ public fun sampleHpcWorkload( if (sampleOnLoad) { var currentLoad = 0.0 - var i = 0 for (entry in hpcSequence) { val entryLoad = entry.workload.image.tags.getValue("total-load") as Double if ((currentLoad + entryLoad) / totalLoad > fraction) { diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt index 6d854fef..66efbcd0 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/sc20/Sc20TraceReader.kt @@ -64,7 +64,6 @@ public class Sc20TraceReader( val timestampCol = 0 val cpuUsageCol = 1 val coreCol = 12 - val vmIdCol = 19 val provisionedMemoryCol = 20 val traceInterval = 5 * 60 * 1000L diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/swf/SwfTraceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/swf/SwfTraceReader.kt index f7b8a072..52d41c44 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/swf/SwfTraceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/swf/SwfTraceReader.kt @@ -64,17 +64,17 @@ public class SwfTraceReader( val sliceDuration = 5 * 60L - var jobNumber = -1L - var submitTime = -1L - var waitTime = -1L - var runTime = -1L - var cores = -1 - var memory = -1L - var slicedWaitTime = -1L - var flopsPerSecond = -1L - var flopsPartialSlice = -1L - var flopsFullSlice = -1L - var runtimePartialSliceRemainder = -1L + var jobNumber: Long + var submitTime: Long + var waitTime: Long + var runTime: Long + var cores: Int + var memory: Long + var slicedWaitTime: Long + var flopsPerSecond: Long + var flopsPartialSlice: Long + var flopsFullSlice: Long + var runtimePartialSliceRemainder: Long BufferedReader(FileReader(file)).use { reader -> reader.lineSequence() diff --git a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/wtf/WtfTraceReader.kt b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/wtf/WtfTraceReader.kt index e43d68d8..b2931468 100644 --- a/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/wtf/WtfTraceReader.kt +++ b/simulator/opendc-format/src/main/kotlin/org/opendc/format/trace/wtf/WtfTraceReader.kt @@ -70,8 +70,6 @@ public class WtfTraceReader(path: String) : TraceReader { val dependencies = (nextRecord.get("parents") as ArrayList).map { it.get("item") as Long } - val inputSize: Long = 0 - val outputSize: Long = 0 val flops: Long = 4100 * (runtime / 1000) * cores diff --git a/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/Main.kt b/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/Main.kt index ca500770..26577ef2 100644 --- a/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/Main.kt +++ b/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/Main.kt @@ -277,8 +277,7 @@ public class RunnerCli : CliktCommand(name = "runner") { trace, scheduler, chan, - monitor, - emptyMap() + monitor ) logger.debug("SUBMIT=${scheduler.submittedVms}") diff --git a/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/TopologyParser.kt b/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/TopologyParser.kt index 244f2a3d..5e483271 100644 --- a/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/TopologyParser.kt +++ b/simulator/opendc-runner-web/src/main/kotlin/org/opendc/runner/web/TopologyParser.kt @@ -60,7 +60,6 @@ public class TopologyParser(private val collection: MongoCollection, p val random = Random(0) for (machine in fetchMachines(id)) { - val machineId = machine.getString("_id") val clusterId = machine.getString("rack_id") val position = machine.getInteger("position") -- cgit v1.2.3