diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 11a42d60e..2f4db50c0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,10 +14,10 @@ jobs: strategy: matrix: os: [ ubuntu-22.04 ] - java: [ 17, 19 ] + java: [ 19 ] include: - os: windows-2022 - java: 17 + java: 19 steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a1fafe44e..e2b095503 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -95,7 +95,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 17 + java-version: 19 - name: Prepare id: prep run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 38b81b2d3..5b3357f44 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 17 + java-version: 19 - name: Publish with Gradle uses: gradle/gradle-build-action@v2 with: diff --git a/buildSrc/src/main/kotlin/spotless-conventions.gradle.kts b/buildSrc/src/main/kotlin/spotless-conventions.gradle.kts index 11131cc85..7a2b0a1c6 100644 --- a/buildSrc/src/main/kotlin/spotless-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/spotless-conventions.gradle.kts @@ -42,7 +42,6 @@ spotless { pluginManager.withPlugin("org.jetbrains.kotlin.jvm") { kotlin { ktlint() - .setUseExperimental(false) trimTrailingWhitespace() endWithNewline() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cff1c3302..bec3c35c8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,32 +1,32 @@ [versions] -calcite = "1.34.0" +calcite = "1.36.0" clikt = "3.5.2" commons-math3 = "3.6.1" -dokka = "1.8.10" +dokka = "1.9.10" gradle-node = "3.5.1" -hadoop = "3.3.5" -hypersistence-utils = "3.2.0" -jackson = "2.14.2" +hadoop = "3.3.6" +hypersistence-utils = "3.7.3" +jackson = "2.16.1" jandex-gradle = "1.1.0" -java = "17" -jline = "3.23.0" +java = "19" +jline = "3.25.1" jmh-gradle = "0.7.0" -jakarta-validation = "2.0.2" -junit-jupiter = "5.9.1" -kotlin = "1.8.10" +jakarta = "3.0.2" +junit-jupiter = "5.10.2" +kotlin = "1.9.22" kotlin-logging = "3.0.5" -kotlinx-coroutines = "1.6.4" -log4j = "2.20.0" -microprofile-openapi = "3.0" -microprofile-config = "3.0.1" -mockk = "1.13.4" +kotlinx-coroutines = "1.8.0" +log4j = "2.23.0" +microprofile-openapi = "3.1" +microprofile-config = "3.1" +mockk = "1.13.9" node = "18.15.0" -parquet = "1.12.3" -progressbar = "0.9.5" -quarkus = "2.16.5.Final" -sentry = "6.16.0" +parquet = "1.13.1" +progressbar = "0.10.0" +quarkus = "3.8.0" +sentry = "7.4.0" slf4j = "2.0.7" -spotless = "6.12.0" +spotless = "6.25.0" [libraries] # Kotlin @@ -36,6 +36,7 @@ kotlin-noarg = { module = "org.jetbrains.kotlin:kotlin-noarg", version.ref = "ko kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } # Logging +#kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version.ref = "kotlin-logging" } kotlin-logging = { module = "io.github.microutils:kotlin-logging", version.ref = "kotlin-logging" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" } @@ -84,7 +85,7 @@ quarkus-hibernate-validator = { module = "io.quarkus:quarkus-hibernate-validator quarkus-jdbc-h2 = { module = "io.quarkus:quarkus-jdbc-h2" } quarkus-jdbc-postgresql = { module = "io.quarkus:quarkus-jdbc-postgresql" } quarkus-flyway = { module = "io.quarkus:quarkus-flyway" } -hypersistence-utils-hibernate = { module = "io.hypersistence:hypersistence-utils-hibernate-55", version.ref = "hypersistence-utils" } +hypersistence-utils-hibernate = { module = "io.hypersistence:hypersistence-utils-hibernate-60", version.ref = "hypersistence-utils" } # Quarkus (Testing) quarkus-junit5-core = { module = "io.quarkus:quarkus-junit5" } @@ -98,7 +99,8 @@ calcite-core = { module = "org.apache.calcite:calcite-core", version.ref = "calc jline = { module = "org.jline:jline", version.ref = "jline" } # Other -jakarta-validation = { module = "jakarta.validation:jakarta.validation-api", version.ref = "jakarta-validation" } +jakarta-validation = { module = "jakarta.validation:jakarta.validation-api", version.ref = "jakarta" } +jakarta-ws-rs = { module = "jakarta.ws.rs:jakarta.ws.rs-api", version.ref = "jakarta" } hadoop-common = { module = "org.apache.hadoop:hadoop-common", version.ref = "hadoop" } hadoop-mapreduce-client-core = { module = "org.apache.hadoop:hadoop-mapreduce-client-core", version.ref = "hadoop" } commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } diff --git a/opendc-common/build.gradle.kts b/opendc-common/build.gradle.kts index f1c22f61a..e0524f3c1 100644 --- a/opendc-common/build.gradle.kts +++ b/opendc-common/build.gradle.kts @@ -23,7 +23,7 @@ group = "org.opendc" description = "Common functionality used across OpenDC modules" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-common/src/main/kotlin/org/opendc/common/DispatcherCoroutineDispatcher.kt b/opendc-common/src/main/kotlin/org/opendc/common/DispatcherCoroutineDispatcher.kt index 63744ef9a..d33e370d9 100644 --- a/opendc-common/src/main/kotlin/org/opendc/common/DispatcherCoroutineDispatcher.kt +++ b/opendc-common/src/main/kotlin/org/opendc/common/DispatcherCoroutineDispatcher.kt @@ -37,20 +37,33 @@ import kotlin.coroutines.CoroutineContext */ @OptIn(InternalCoroutinesApi::class) internal class DispatcherCoroutineDispatcher(private val dispatcher: Dispatcher) : CoroutineDispatcher(), Delay, DispatcherProvider { - override fun dispatch(context: CoroutineContext, block: Runnable) { + override fun dispatch( + context: CoroutineContext, + block: Runnable, + ) { block.run() } - override fun dispatchYield(context: CoroutineContext, block: Runnable) { + override fun dispatchYield( + context: CoroutineContext, + block: Runnable, + ) { dispatcher.schedule(block) } @OptIn(ExperimentalCoroutinesApi::class) - override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) { + override fun scheduleResumeAfterDelay( + timeMillis: Long, + continuation: CancellableContinuation, + ) { dispatcher.schedule(timeMillis, CancellableContinuationRunnable(continuation) { resumeUndispatched(Unit) }) } - override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle { + override fun invokeOnTimeout( + timeMillis: Long, + block: Runnable, + context: CoroutineContext, + ): DisposableHandle { val handle = dispatcher.scheduleCancellable(timeMillis, block) return DisposableHandle { handle.cancel() } } @@ -67,7 +80,7 @@ internal class DispatcherCoroutineDispatcher(private val dispatcher: Dispatcher) */ private class CancellableContinuationRunnable( @JvmField val continuation: CancellableContinuation, - private val block: CancellableContinuation.() -> Unit + private val block: CancellableContinuation.() -> Unit, ) : Runnable { override fun run() = continuation.block() } diff --git a/opendc-common/src/test/kotlin/org/opendc/common/DispatcherCoroutineDispatcherTest.kt b/opendc-common/src/test/kotlin/org/opendc/common/DispatcherCoroutineDispatcherTest.kt index 01b3d2fc2..43faba648 100644 --- a/opendc-common/src/test/kotlin/org/opendc/common/DispatcherCoroutineDispatcherTest.kt +++ b/opendc-common/src/test/kotlin/org/opendc/common/DispatcherCoroutineDispatcherTest.kt @@ -36,44 +36,46 @@ import org.opendc.simulator.kotlin.runSimulation * Test suite for [DispatcherCoroutineDispatcher]. */ class DispatcherCoroutineDispatcherTest { - /** * Tests if a dispatcher yields the correct time */ @Test - fun testYield() = runSimulation { - withContext(dispatcher.asCoroutineDispatcher()) { - val startTime = dispatcher.currentTime - yield() - assertEquals(startTime, dispatcher.currentTime) + fun testYield() = + runSimulation { + withContext(dispatcher.asCoroutineDispatcher()) { + val startTime = dispatcher.currentTime + yield() + assertEquals(startTime, dispatcher.currentTime) + } } - } /** * Tests if a dispatcher correctly delays */ @Test - fun testDelay() = runSimulation { - withContext(dispatcher.asCoroutineDispatcher()) { - val startTime = dispatcher.currentTime - delay(10) - assertEquals(startTime + 10, dispatcher.currentTime) + fun testDelay() = + runSimulation { + withContext(dispatcher.asCoroutineDispatcher()) { + val startTime = dispatcher.currentTime + delay(10) + assertEquals(startTime + 10, dispatcher.currentTime) + } } - } /** * Tests if a dispatcher correctly times out */ @Test - fun testTimeout() = runSimulation { - withContext(dispatcher.asCoroutineDispatcher()) { - assertThrows { - withTimeout(10) { - delay(1000) + fun testTimeout() = + runSimulation { + withContext(dispatcher.asCoroutineDispatcher()) { + assertThrows { + withTimeout(10) { + delay(1000) + } } - } - assertEquals(10, dispatcher.currentTime) + assertEquals(10, dispatcher.currentTime) + } } - } } diff --git a/opendc-common/src/test/kotlin/org/opendc/common/util/PacerTest.kt b/opendc-common/src/test/kotlin/org/opendc/common/util/PacerTest.kt index 3235b0463..539403b97 100644 --- a/opendc-common/src/test/kotlin/org/opendc/common/util/PacerTest.kt +++ b/opendc-common/src/test/kotlin/org/opendc/common/util/PacerTest.kt @@ -39,9 +39,10 @@ class PacerTest { var count = 0 runSimulation { - val pacer = Pacer(dispatcher, /*quantum*/ 100) { - count++ - } + val pacer = + Pacer(dispatcher, 100) { + count++ + } pacer.enqueue() } @@ -54,9 +55,10 @@ class PacerTest { var count = 0 runSimulation { - val pacer = Pacer(dispatcher, /*quantum*/ 100) { - count++ - } + val pacer = + Pacer(dispatcher, 100) { + count++ + } pacer.enqueue() pacer.enqueue() @@ -72,9 +74,10 @@ class PacerTest { var count = 0 runSimulation { - val pacer = Pacer(dispatcher, /*quantum*/ 100) { - count++ - } + val pacer = + Pacer(dispatcher, 100) { + count++ + } pacer.enqueue() pacer.cancel() @@ -90,9 +93,10 @@ class PacerTest { var count = 0 runSimulation { - val pacer = Pacer(dispatcher, /*quantum*/ 100) { - count++ - } + val pacer = + Pacer(dispatcher, 100) { + count++ + } assertFalse(pacer.isPending) assertDoesNotThrow { pacer.cancel() } @@ -108,9 +112,10 @@ class PacerTest { var count = 0 runSimulation { - val pacer = Pacer(dispatcher, /*quantum*/ 100) { - count++ - } + val pacer = + Pacer(dispatcher, 100) { + count++ + } pacer.enqueue() delay(100) diff --git a/opendc-compute/opendc-compute-api/build.gradle.kts b/opendc-compute/opendc-compute-api/build.gradle.kts index 2ac7e64c1..f9b04299a 100644 --- a/opendc-compute/opendc-compute-api/build.gradle.kts +++ b/opendc-compute/opendc-compute-api/build.gradle.kts @@ -22,7 +22,7 @@ description = "API interface for the OpenDC Compute service" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/ComputeClient.kt b/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/ComputeClient.kt index c26d0b8b8..09cfe6f52 100644 --- a/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/ComputeClient.kt +++ b/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/ComputeClient.kt @@ -54,7 +54,7 @@ public interface ComputeClient : AutoCloseable { cpuCount: Int, memorySize: Long, labels: Map = emptyMap(), - meta: Map = emptyMap() + meta: Map = emptyMap(), ): Flavor /** @@ -79,7 +79,7 @@ public interface ComputeClient : AutoCloseable { public fun newImage( name: String, labels: Map = emptyMap(), - meta: Map = emptyMap() + meta: Map = emptyMap(), ): Image /** @@ -110,7 +110,7 @@ public interface ComputeClient : AutoCloseable { flavor: Flavor, labels: Map = emptyMap(), meta: Map = emptyMap(), - start: Boolean = true + start: Boolean = true, ): Server /** diff --git a/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/InsufficientServerCapacityException.kt b/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/InsufficientServerCapacityException.kt index 8fbb73087..497d52669 100644 --- a/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/InsufficientServerCapacityException.kt +++ b/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/InsufficientServerCapacityException.kt @@ -26,4 +26,6 @@ package org.opendc.compute.api * This exception is thrown to indicate that the compute service does not have enough capacity at the moment to * fulfill a launch request. */ -public class InsufficientServerCapacityException(override val cause: Throwable? = null) : Exception("There was insufficient capacity available to satisfy the launch request") +public class InsufficientServerCapacityException( + override val cause: Throwable? = null, +) : Exception("There was insufficient capacity available to satisfy the launch request") diff --git a/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/ServerState.kt b/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/ServerState.kt index 2b5aebb11..a4d7d7d7d 100644 --- a/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/ServerState.kt +++ b/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/ServerState.kt @@ -49,5 +49,5 @@ public enum class ServerState { /** * The server has been deleted and cannot be started later on. */ - DELETED + DELETED, } diff --git a/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/ServerWatcher.kt b/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/ServerWatcher.kt index cf995fc30..3229e101e 100644 --- a/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/ServerWatcher.kt +++ b/opendc-compute/opendc-compute-api/src/main/kotlin/org/opendc/compute/api/ServerWatcher.kt @@ -32,5 +32,8 @@ public interface ServerWatcher { * @param server The server whose state has changed. * @param newState The new state of the server. */ - public fun onStateChanged(server: Server, newState: ServerState) {} + public fun onStateChanged( + server: Server, + newState: ServerState, + ) {} } diff --git a/opendc-compute/opendc-compute-service/build.gradle.kts b/opendc-compute/opendc-compute-service/build.gradle.kts index 1a73201e3..0efdb05f9 100644 --- a/opendc-compute/opendc-compute-service/build.gradle.kts +++ b/opendc-compute/opendc-compute-service/build.gradle.kts @@ -22,7 +22,7 @@ description = "OpenDC Compute Service implementation" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } @@ -33,6 +33,7 @@ dependencies { implementation(libs.kotlin.logging) testImplementation(projects.opendcSimulator.opendcSimulatorCore) + testImplementation(libs.log4j.slf4j) testRuntimeOnly(libs.log4j.core) testRuntimeOnly(libs.log4j.slf4j) } diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ComputeSchedulers.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ComputeSchedulers.kt index 2f071c131..189471463 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ComputeSchedulers.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ComputeSchedulers.kt @@ -37,48 +37,61 @@ import java.util.random.RandomGenerator /** * Create a [ComputeScheduler] for the experiment. */ -public fun createComputeScheduler(name: String, seeder: RandomGenerator, placements: Map = emptyMap()): ComputeScheduler { +public fun createComputeScheduler( + name: String, + seeder: RandomGenerator, + placements: Map = emptyMap(), +): ComputeScheduler { val cpuAllocationRatio = 16.0 val ramAllocationRatio = 1.5 return when (name) { - "mem" -> FilterScheduler( - filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), - weighers = listOf(RamWeigher(multiplier = 1.0)) - ) - "mem-inv" -> FilterScheduler( - filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), - weighers = listOf(RamWeigher(multiplier = -1.0)) - ) - "core-mem" -> FilterScheduler( - filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), - weighers = listOf(CoreRamWeigher(multiplier = 1.0)) - ) - "core-mem-inv" -> FilterScheduler( - filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), - weighers = listOf(CoreRamWeigher(multiplier = -1.0)) - ) - "active-servers" -> FilterScheduler( - filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), - weighers = listOf(InstanceCountWeigher(multiplier = -1.0)) - ) - "active-servers-inv" -> FilterScheduler( - filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), - weighers = listOf(InstanceCountWeigher(multiplier = 1.0)) - ) - "provisioned-cores" -> FilterScheduler( - filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), - weighers = listOf(VCpuWeigher(cpuAllocationRatio, multiplier = 1.0)) - ) - "provisioned-cores-inv" -> FilterScheduler( - filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), - weighers = listOf(VCpuWeigher(cpuAllocationRatio, multiplier = -1.0)) - ) - "random" -> FilterScheduler( - filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), - weighers = emptyList(), - subsetSize = Int.MAX_VALUE, - random = SplittableRandom(seeder.nextLong()) - ) + "mem" -> + FilterScheduler( + filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), + weighers = listOf(RamWeigher(multiplier = 1.0)), + ) + "mem-inv" -> + FilterScheduler( + filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), + weighers = listOf(RamWeigher(multiplier = -1.0)), + ) + "core-mem" -> + FilterScheduler( + filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), + weighers = listOf(CoreRamWeigher(multiplier = 1.0)), + ) + "core-mem-inv" -> + FilterScheduler( + filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), + weighers = listOf(CoreRamWeigher(multiplier = -1.0)), + ) + "active-servers" -> + FilterScheduler( + filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), + weighers = listOf(InstanceCountWeigher(multiplier = -1.0)), + ) + "active-servers-inv" -> + FilterScheduler( + filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), + weighers = listOf(InstanceCountWeigher(multiplier = 1.0)), + ) + "provisioned-cores" -> + FilterScheduler( + filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), + weighers = listOf(VCpuWeigher(cpuAllocationRatio, multiplier = 1.0)), + ) + "provisioned-cores-inv" -> + FilterScheduler( + filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), + weighers = listOf(VCpuWeigher(cpuAllocationRatio, multiplier = -1.0)), + ) + "random" -> + FilterScheduler( + filters = listOf(ComputeFilter(), VCpuFilter(cpuAllocationRatio), RamFilter(ramAllocationRatio)), + weighers = emptyList(), + subsetSize = Int.MAX_VALUE, + random = SplittableRandom(seeder.nextLong()), + ) "replay" -> ReplayScheduler(placements) else -> throw IllegalArgumentException("Unknown policy $name") } diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/FilterScheduler.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/FilterScheduler.kt index 18a319e99..cdcd1af06 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/FilterScheduler.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/FilterScheduler.kt @@ -46,7 +46,7 @@ public class FilterScheduler( private val filters: List, private val weighers: List, private val subsetSize: Int = 1, - private val random: RandomGenerator = SplittableRandom(0) + private val random: RandomGenerator = SplittableRandom(0), ) : ComputeScheduler { /** * The pool of hosts available to the scheduler. @@ -69,36 +69,37 @@ public class FilterScheduler( val hosts = hosts val filteredHosts = hosts.filter { host -> filters.all { filter -> filter.test(host, server) } } - val subset = if (weighers.isNotEmpty()) { - val results = weighers.map { it.getWeights(filteredHosts, server) } - val weights = DoubleArray(filteredHosts.size) + val subset = + if (weighers.isNotEmpty()) { + val results = weighers.map { it.getWeights(filteredHosts, server) } + val weights = DoubleArray(filteredHosts.size) - for (result in results) { - val min = result.min - val range = (result.max - min) + for (result in results) { + val min = result.min + val range = (result.max - min) - // Skip result if all weights are the same - if (range == 0.0) { - continue - } + // Skip result if all weights are the same + if (range == 0.0) { + continue + } - val multiplier = result.multiplier - val factor = multiplier / range + val multiplier = result.multiplier + val factor = multiplier / range - for ((i, weight) in result.weights.withIndex()) { - weights[i] += factor * (weight - min) + for ((i, weight) in result.weights.withIndex()) { + weights[i] += factor * (weight - min) + } } - } - weights.indices - .asSequence() - .sortedByDescending { weights[it] } - .map { filteredHosts[it] } - .take(subsetSize) - .toList() - } else { - filteredHosts - } + weights.indices + .asSequence() + .sortedByDescending { weights[it] } + .map { filteredHosts[it] } + .take(subsetSize) + .toList() + } else { + filteredHosts + } return when (val maxSize = min(subsetSize, subset.size)) { 0 -> null diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ReplayScheduler.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ReplayScheduler.kt index 4339b3dea..a6703c893 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ReplayScheduler.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ReplayScheduler.kt @@ -49,8 +49,9 @@ public class ReplayScheduler(private val vmPlacements: Map) : Co } override fun select(server: Server): HostView? { - val clusterName = vmPlacements[server.name] - ?: throw IllegalStateException("Could not find placement data in VM placement file for VM ${server.name}") + val clusterName = + vmPlacements[server.name] + ?: throw IllegalStateException("Could not find placement data in VM placement file for VM ${server.name}") val machinesInCluster = hosts.filter { it.host.name.contains(clusterName) } if (machinesInCluster.isEmpty()) { diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/ComputeFilter.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/ComputeFilter.kt index b562f8386..23590c133 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/ComputeFilter.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/ComputeFilter.kt @@ -30,7 +30,10 @@ import org.opendc.compute.service.driver.HostState * A [HostFilter] that filters on active hosts. */ public class ComputeFilter : HostFilter { - override fun test(host: HostView, server: Server): Boolean { + override fun test( + host: HostView, + server: Server, + ): Boolean { return host.host.state == HostState.UP } diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/DifferentHostFilter.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/DifferentHostFilter.kt index 4a9f41c55..df67a19fd 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/DifferentHostFilter.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/DifferentHostFilter.kt @@ -30,7 +30,10 @@ import java.util.UUID * A [HostFilter] that ensures an instance is scheduled on a different host from a set of instances. */ public class DifferentHostFilter : HostFilter { - override fun test(host: HostView, server: Server): Boolean { + override fun test( + host: HostView, + server: Server, + ): Boolean { @Suppress("UNCHECKED_CAST") val affinityUUIDs = server.meta["scheduler_hint:different_host"] as? Set ?: return true return host.host.instances.none { it.uid in affinityUUIDs } diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/HostFilter.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/HostFilter.kt index 78010fee4..902c760e2 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/HostFilter.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/HostFilter.kt @@ -34,5 +34,8 @@ public fun interface HostFilter { * Test whether the specified [host] should be included in the selection * for scheduling the specified [server]. */ - public fun test(host: HostView, server: Server): Boolean + public fun test( + host: HostView, + server: Server, + ): Boolean } diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/InstanceCountFilter.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/InstanceCountFilter.kt index 5aa38a887..d9348802f 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/InstanceCountFilter.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/InstanceCountFilter.kt @@ -31,7 +31,10 @@ import org.opendc.compute.service.HostView * @param limit The maximum number of instances on the host. */ public class InstanceCountFilter(private val limit: Int) : HostFilter { - override fun test(host: HostView, server: Server): Boolean { + override fun test( + host: HostView, + server: Server, + ): Boolean { return host.instanceCount < limit } diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/RamFilter.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/RamFilter.kt index 275e8f1c1..4792a7a0d 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/RamFilter.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/RamFilter.kt @@ -31,7 +31,10 @@ import org.opendc.compute.service.HostView * @param allocationRatio Virtual RAM to physical RAM allocation ratio. */ public class RamFilter(private val allocationRatio: Double) : HostFilter { - override fun test(host: HostView, server: Server): Boolean { + override fun test( + host: HostView, + server: Server, + ): Boolean { val requested = server.flavor.memorySize val available = host.availableMemory val total = host.host.model.memoryCapacity diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/SameHostFilter.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/SameHostFilter.kt index c37538663..4c31c66ab 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/SameHostFilter.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/SameHostFilter.kt @@ -30,7 +30,10 @@ import java.util.UUID * A [HostFilter] that ensures an instance is scheduled on the same host as all other instances in a set of instances. */ public class SameHostFilter : HostFilter { - override fun test(host: HostView, server: Server): Boolean { + override fun test( + host: HostView, + server: Server, + ): Boolean { @Suppress("UNCHECKED_CAST") val affinityUUIDs = server.meta["scheduler_hint:same_host"] as? Set ?: return true return host.host.instances.any { it.uid in affinityUUIDs } diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/VCpuCapacityFilter.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/VCpuCapacityFilter.kt index d4dff76b3..e3397e503 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/VCpuCapacityFilter.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/VCpuCapacityFilter.kt @@ -30,7 +30,10 @@ import org.opendc.compute.service.HostView * capacity on the host. */ public class VCpuCapacityFilter : HostFilter { - override fun test(host: HostView, server: Server): Boolean { + override fun test( + host: HostView, + server: Server, + ): Boolean { val requiredCapacity = server.flavor.meta["cpu-capacity"] as? Double val hostModel = host.host.model val availableCapacity = hostModel.cpuCapacity / hostModel.cpuCount diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/VCpuFilter.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/VCpuFilter.kt index 448a6189d..5d02873f5 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/VCpuFilter.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/VCpuFilter.kt @@ -31,7 +31,10 @@ import org.opendc.compute.service.HostView * @param allocationRatio Virtual CPU to physical CPU allocation ratio. */ public class VCpuFilter(private val allocationRatio: Double) : HostFilter { - override fun test(host: HostView, server: Server): Boolean { + override fun test( + host: HostView, + server: Server, + ): Boolean { val requested = server.flavor.cpuCount val total = host.host.model.cpuCount val limit = total * allocationRatio diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/CoreRamWeigher.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/CoreRamWeigher.kt index f79d6d883..d6aafbc7f 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/CoreRamWeigher.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/CoreRamWeigher.kt @@ -33,7 +33,10 @@ import org.opendc.compute.service.HostView * memory. */ public class CoreRamWeigher(override val multiplier: Double = 1.0) : HostWeigher { - override fun getWeight(host: HostView, server: Server): Double { + override fun getWeight( + host: HostView, + server: Server, + ): Double { return host.availableMemory.toDouble() / host.host.model.cpuCount } diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/HostWeigher.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/HostWeigher.kt index 017991220..825cfff9d 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/HostWeigher.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/HostWeigher.kt @@ -38,12 +38,18 @@ public interface HostWeigher { /** * Obtain the weight of the specified [host] when scheduling the specified [server]. */ - public fun getWeight(host: HostView, server: Server): Double + public fun getWeight( + host: HostView, + server: Server, + ): Double /** * Obtain the weights for [hosts] when scheduling the specified [server]. */ - public fun getWeights(hosts: List, server: Server): Result { + public fun getWeights( + hosts: List, + server: Server, + ): Result { val weights = DoubleArray(hosts.size) var min = Double.MAX_VALUE var max = Double.MIN_VALUE @@ -70,6 +76,6 @@ public interface HostWeigher { public val weights: DoubleArray, public val min: Double, public val max: Double, - public val multiplier: Double + public val multiplier: Double, ) } diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/InstanceCountWeigher.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/InstanceCountWeigher.kt index bfb583a24..9e0a95171 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/InstanceCountWeigher.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/InstanceCountWeigher.kt @@ -29,7 +29,10 @@ import org.opendc.compute.service.HostView * A [HostWeigher] that weighs the hosts based on the number of instances on the host. */ public class InstanceCountWeigher(override val multiplier: Double = 1.0) : HostWeigher { - override fun getWeight(host: HostView, server: Server): Double { + override fun getWeight( + host: HostView, + server: Server, + ): Double { return host.instanceCount.toDouble() } diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/RamWeigher.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/RamWeigher.kt index bb837fbea..fca2e8933 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/RamWeigher.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/RamWeigher.kt @@ -32,7 +32,10 @@ import org.opendc.compute.service.HostView * available memory, and a negative number will result in the scheduler preferring hosts with less memory. */ public class RamWeigher(override val multiplier: Double = 1.0) : HostWeigher { - override fun getWeight(host: HostView, server: Server): Double { + override fun getWeight( + host: HostView, + server: Server, + ): Double { return host.availableMemory.toDouble() } diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/VCpuCapacityWeigher.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/VCpuCapacityWeigher.kt index f15f60c9e..2912ce498 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/VCpuCapacityWeigher.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/VCpuCapacityWeigher.kt @@ -29,8 +29,10 @@ import org.opendc.compute.service.HostView * A [HostWeigher] that weighs the hosts based on the difference required vCPU capacity and the available CPU capacity. */ public class VCpuCapacityWeigher(override val multiplier: Double = 1.0) : HostWeigher { - - override fun getWeight(host: HostView, server: Server): Double { + override fun getWeight( + host: HostView, + server: Server, + ): Double { val model = host.host.model val requiredCapacity = server.flavor.meta["cpu-capacity"] as? Double ?: 0.0 return model.cpuCapacity / model.cpuCount - requiredCapacity / server.flavor.cpuCount diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/VCpuWeigher.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/VCpuWeigher.kt index 169ad8cb5..be93458ff 100644 --- a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/VCpuWeigher.kt +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/weights/VCpuWeigher.kt @@ -31,12 +31,14 @@ import org.opendc.compute.service.HostView * @param allocationRatio Virtual CPU to physical CPU allocation ratio. */ public class VCpuWeigher(private val allocationRatio: Double, override val multiplier: Double = 1.0) : HostWeigher { - init { require(allocationRatio > 0.0) { "Allocation ratio must be greater than zero" } } - override fun getWeight(host: HostView, server: Server): Double { + override fun getWeight( + host: HostView, + server: Server, + ): Double { return host.host.model.cpuCount * allocationRatio - host.provisionedCores } diff --git a/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/ComputeServiceTest.kt b/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/ComputeServiceTest.kt index 4dc1cfa84..52caea0cd 100644 --- a/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/ComputeServiceTest.kt +++ b/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/ComputeServiceTest.kt @@ -63,309 +63,324 @@ internal class ComputeServiceTest { @BeforeEach fun setUp() { scope = SimulationCoroutineScope() - val computeScheduler = FilterScheduler( - filters = listOf(ComputeFilter(), VCpuFilter(allocationRatio = 1.0), RamFilter(allocationRatio = 1.0)), - weighers = listOf(RamWeigher()) - ) + val computeScheduler = + FilterScheduler( + filters = listOf(ComputeFilter(), VCpuFilter(allocationRatio = 1.0), RamFilter(allocationRatio = 1.0)), + weighers = listOf(RamWeigher()), + ) service = ComputeService(scope.dispatcher, computeScheduler, Duration.ofMinutes(5)) } @Test - fun testClientClose() = scope.runSimulation { - val client = service.newClient() + fun testClientClose() = + scope.runSimulation { + val client = service.newClient() - assertEquals(emptyList(), client.queryFlavors()) - assertEquals(emptyList(), client.queryImages()) - assertEquals(emptyList(), client.queryServers()) + assertEquals(emptyList(), client.queryFlavors()) + assertEquals(emptyList(), client.queryImages()) + assertEquals(emptyList(), client.queryServers()) - client.close() + client.close() - assertThrows { client.queryFlavors() } - assertThrows { client.queryImages() } - assertThrows { client.queryServers() } + assertThrows { client.queryFlavors() } + assertThrows { client.queryImages() } + assertThrows { client.queryServers() } - assertThrows { client.findFlavor(UUID.randomUUID()) } - assertThrows { client.findImage(UUID.randomUUID()) } - assertThrows { client.findServer(UUID.randomUUID()) } + assertThrows { client.findFlavor(UUID.randomUUID()) } + assertThrows { client.findImage(UUID.randomUUID()) } + assertThrows { client.findServer(UUID.randomUUID()) } - assertThrows { client.newFlavor("test", 1, 2) } - assertThrows { client.newImage("test") } - assertThrows { client.newServer("test", mockk(), mockk()) } - } + assertThrows { client.newFlavor("test", 1, 2) } + assertThrows { client.newImage("test") } + assertThrows { client.newServer("test", mockk(), mockk()) } + } @Test - fun testClientCreate() = scope.runSimulation { - val client = service.newClient() - - val flavor = client.newFlavor("test", 1, 1024) - assertEquals(listOf(flavor), client.queryFlavors()) - assertEquals(flavor, client.findFlavor(flavor.uid)) - val image = client.newImage("test") - assertEquals(listOf(image), client.queryImages()) - assertEquals(image, client.findImage(image.uid)) - val server = client.newServer("test", image, flavor, start = false) - assertEquals(listOf(server), client.queryServers()) - assertEquals(server, client.findServer(server.uid)) - - server.delete() - assertNull(client.findServer(server.uid)) - - image.delete() - assertNull(client.findImage(image.uid)) - - flavor.delete() - assertNull(client.findFlavor(flavor.uid)) - - assertThrows { server.start() } - } + fun testClientCreate() = + scope.runSimulation { + val client = service.newClient() + + val flavor = client.newFlavor("test", 1, 1024) + assertEquals(listOf(flavor), client.queryFlavors()) + assertEquals(flavor, client.findFlavor(flavor.uid)) + val image = client.newImage("test") + assertEquals(listOf(image), client.queryImages()) + assertEquals(image, client.findImage(image.uid)) + val server = client.newServer("test", image, flavor, start = false) + assertEquals(listOf(server), client.queryServers()) + assertEquals(server, client.findServer(server.uid)) + + server.delete() + assertNull(client.findServer(server.uid)) + + image.delete() + assertNull(client.findImage(image.uid)) + + flavor.delete() + assertNull(client.findFlavor(flavor.uid)) + + assertThrows { server.start() } + } @Test - fun testClientOnClose() = scope.runSimulation { - service.close() - assertThrows { - service.newClient() + fun testClientOnClose() = + scope.runSimulation { + service.close() + assertThrows { + service.newClient() + } } - } @Test - fun testAddHost() = scope.runSimulation { - val host = mockk(relaxUnitFun = true) + fun testAddHost() = + scope.runSimulation { + val host = mockk(relaxUnitFun = true) - every { host.model } returns HostModel(4 * 2600.0, 4, 2048) - every { host.state } returns HostState.UP + every { host.model } returns HostModel(4 * 2600.0, 4, 2048) + every { host.state } returns HostState.UP - assertEquals(emptySet(), service.hosts) + assertEquals(emptySet(), service.hosts) - service.addHost(host) + service.addHost(host) - verify(exactly = 1) { host.addListener(any()) } + verify(exactly = 1) { host.addListener(any()) } - assertEquals(1, service.hosts.size) + assertEquals(1, service.hosts.size) - service.removeHost(host) + service.removeHost(host) - verify(exactly = 1) { host.removeListener(any()) } - } + verify(exactly = 1) { host.removeListener(any()) } + } @Test - fun testAddHostDouble() = scope.runSimulation { - val host = mockk(relaxUnitFun = true) + fun testAddHostDouble() = + scope.runSimulation { + val host = mockk(relaxUnitFun = true) - every { host.model } returns HostModel(4 * 2600.0, 4, 2048) - every { host.state } returns HostState.DOWN + every { host.model } returns HostModel(4 * 2600.0, 4, 2048) + every { host.state } returns HostState.DOWN - assertEquals(emptySet(), service.hosts) + assertEquals(emptySet(), service.hosts) - service.addHost(host) - service.addHost(host) + service.addHost(host) + service.addHost(host) - verify(exactly = 1) { host.addListener(any()) } - } + verify(exactly = 1) { host.addListener(any()) } + } @Test - fun testServerStartWithoutEnoughCpus() = scope.runSimulation { - val client = service.newClient() - val flavor = client.newFlavor("test", 1, 0) - val image = client.newImage("test") - val server = client.newServer("test", image, flavor, start = false) - - server.start() - delay(5L * 60 * 1000) - server.reload() - assertEquals(ServerState.TERMINATED, server.state) - } + fun testServerStartWithoutEnoughCpus() = + scope.runSimulation { + val client = service.newClient() + val flavor = client.newFlavor("test", 1, 0) + val image = client.newImage("test") + val server = client.newServer("test", image, flavor, start = false) + + server.start() + delay(5L * 60 * 1000) + server.reload() + assertEquals(ServerState.TERMINATED, server.state) + } @Test - fun testServerStartWithoutEnoughMemory() = scope.runSimulation { - val client = service.newClient() - val flavor = client.newFlavor("test", 0, 1024) - val image = client.newImage("test") - val server = client.newServer("test", image, flavor, start = false) - - server.start() - delay(5L * 60 * 1000) - server.reload() - assertEquals(ServerState.TERMINATED, server.state) - } + fun testServerStartWithoutEnoughMemory() = + scope.runSimulation { + val client = service.newClient() + val flavor = client.newFlavor("test", 0, 1024) + val image = client.newImage("test") + val server = client.newServer("test", image, flavor, start = false) + + server.start() + delay(5L * 60 * 1000) + server.reload() + assertEquals(ServerState.TERMINATED, server.state) + } @Test - fun testServerStartWithoutEnoughResources() = scope.runSimulation { - val client = service.newClient() - val flavor = client.newFlavor("test", 1, 1024) - val image = client.newImage("test") - val server = client.newServer("test", image, flavor, start = false) - - server.start() - delay(5L * 60 * 1000) - server.reload() - assertEquals(ServerState.TERMINATED, server.state) - } + fun testServerStartWithoutEnoughResources() = + scope.runSimulation { + val client = service.newClient() + val flavor = client.newFlavor("test", 1, 1024) + val image = client.newImage("test") + val server = client.newServer("test", image, flavor, start = false) + + server.start() + delay(5L * 60 * 1000) + server.reload() + assertEquals(ServerState.TERMINATED, server.state) + } @Test - fun testServerCancelRequest() = scope.runSimulation { - val client = service.newClient() - val flavor = client.newFlavor("test", 1, 1024) - val image = client.newImage("test") - val server = client.newServer("test", image, flavor, start = false) - - server.start() - server.stop() - delay(5L * 60 * 1000) - server.reload() - assertEquals(ServerState.TERMINATED, server.state) - } + fun testServerCancelRequest() = + scope.runSimulation { + val client = service.newClient() + val flavor = client.newFlavor("test", 1, 1024) + val image = client.newImage("test") + val server = client.newServer("test", image, flavor, start = false) + + server.start() + server.stop() + delay(5L * 60 * 1000) + server.reload() + assertEquals(ServerState.TERMINATED, server.state) + } @Test - fun testServerCannotFitOnHost() = scope.runSimulation { - val host = mockk(relaxUnitFun = true) + fun testServerCannotFitOnHost() = + scope.runSimulation { + val host = mockk(relaxUnitFun = true) - every { host.model } returns HostModel(4 * 2600.0, 4, 2048) - every { host.state } returns HostState.UP - every { host.canFit(any()) } returns false + every { host.model } returns HostModel(4 * 2600.0, 4, 2048) + every { host.state } returns HostState.UP + every { host.canFit(any()) } returns false - service.addHost(host) + service.addHost(host) - val client = service.newClient() - val flavor = client.newFlavor("test", 1, 1024) - val image = client.newImage("test") - val server = client.newServer("test", image, flavor, start = false) + val client = service.newClient() + val flavor = client.newFlavor("test", 1, 1024) + val image = client.newImage("test") + val server = client.newServer("test", image, flavor, start = false) - server.start() - delay(10L * 60 * 1000) - server.reload() - assertEquals(ServerState.PROVISIONING, server.state) + server.start() + delay(10L * 60 * 1000) + server.reload() + assertEquals(ServerState.PROVISIONING, server.state) - verify { host.canFit(server) } - } + verify { host.canFit(server) } + } @Test - fun testHostAvailableAfterSomeTime() = scope.runSimulation { - val host = mockk(relaxUnitFun = true) - val listeners = mutableListOf() + fun testHostAvailableAfterSomeTime() = + scope.runSimulation { + val host = mockk(relaxUnitFun = true) + val listeners = mutableListOf() - every { host.uid } returns UUID.randomUUID() - every { host.model } returns HostModel(4 * 2600.0, 4, 2048) - every { host.state } returns HostState.DOWN - every { host.addListener(any()) } answers { listeners.add(it.invocation.args[0] as HostListener) } - every { host.canFit(any()) } returns false + every { host.uid } returns UUID.randomUUID() + every { host.model } returns HostModel(4 * 2600.0, 4, 2048) + every { host.state } returns HostState.DOWN + every { host.addListener(any()) } answers { listeners.add(it.invocation.args[0] as HostListener) } + every { host.canFit(any()) } returns false - service.addHost(host) + service.addHost(host) - val client = service.newClient() - val flavor = client.newFlavor("test", 1, 1024) - val image = client.newImage("test") - val server = client.newServer("test", image, flavor, start = false) + val client = service.newClient() + val flavor = client.newFlavor("test", 1, 1024) + val image = client.newImage("test") + val server = client.newServer("test", image, flavor, start = false) - server.start() - delay(5L * 60 * 1000) + server.start() + delay(5L * 60 * 1000) - every { host.state } returns HostState.UP - listeners.forEach { it.onStateChanged(host, HostState.UP) } + every { host.state } returns HostState.UP + listeners.forEach { it.onStateChanged(host, HostState.UP) } - delay(5L * 60 * 1000) - server.reload() - assertEquals(ServerState.PROVISIONING, server.state) + delay(5L * 60 * 1000) + server.reload() + assertEquals(ServerState.PROVISIONING, server.state) - verify { host.canFit(server) } - } + verify { host.canFit(server) } + } @Test - fun testHostUnavailableAfterSomeTime() = scope.runSimulation { - val host = mockk(relaxUnitFun = true) - val listeners = mutableListOf() + fun testHostUnavailableAfterSomeTime() = + scope.runSimulation { + val host = mockk(relaxUnitFun = true) + val listeners = mutableListOf() - every { host.uid } returns UUID.randomUUID() - every { host.model } returns HostModel(4 * 2600.0, 4, 2048) - every { host.state } returns HostState.UP - every { host.addListener(any()) } answers { listeners.add(it.invocation.args[0] as HostListener) } - every { host.canFit(any()) } returns false + every { host.uid } returns UUID.randomUUID() + every { host.model } returns HostModel(4 * 2600.0, 4, 2048) + every { host.state } returns HostState.UP + every { host.addListener(any()) } answers { listeners.add(it.invocation.args[0] as HostListener) } + every { host.canFit(any()) } returns false - service.addHost(host) + service.addHost(host) - val client = service.newClient() - val flavor = client.newFlavor("test", 1, 1024) - val image = client.newImage("test") - val server = client.newServer("test", image, flavor, start = false) + val client = service.newClient() + val flavor = client.newFlavor("test", 1, 1024) + val image = client.newImage("test") + val server = client.newServer("test", image, flavor, start = false) - delay(5L * 60 * 1000) + delay(5L * 60 * 1000) - every { host.state } returns HostState.DOWN - listeners.forEach { it.onStateChanged(host, HostState.DOWN) } + every { host.state } returns HostState.DOWN + listeners.forEach { it.onStateChanged(host, HostState.DOWN) } - server.start() - delay(5L * 60 * 1000) - server.reload() - assertEquals(ServerState.PROVISIONING, server.state) + server.start() + delay(5L * 60 * 1000) + server.reload() + assertEquals(ServerState.PROVISIONING, server.state) - verify(exactly = 0) { host.canFit(server) } - } + verify(exactly = 0) { host.canFit(server) } + } @Test - fun testServerDeploy() = scope.runSimulation { - val host = mockk(relaxUnitFun = true) - val listeners = mutableListOf() + fun testServerDeploy() = + scope.runSimulation { + val host = mockk(relaxUnitFun = true) + val listeners = mutableListOf() - every { host.uid } returns UUID.randomUUID() - every { host.model } returns HostModel(4 * 2600.0, 4, 2048) - every { host.state } returns HostState.UP - every { host.canFit(any()) } returns true - every { host.addListener(any()) } answers { listeners.add(it.invocation.args[0] as HostListener) } + every { host.uid } returns UUID.randomUUID() + every { host.model } returns HostModel(4 * 2600.0, 4, 2048) + every { host.state } returns HostState.UP + every { host.canFit(any()) } returns true + every { host.addListener(any()) } answers { listeners.add(it.invocation.args[0] as HostListener) } - service.addHost(host) + service.addHost(host) - val client = service.newClient() - val flavor = client.newFlavor("test", 1, 1024) - val image = client.newImage("test") - val server = client.newServer("test", image, flavor, start = false) - val slot = slot() + val client = service.newClient() + val flavor = client.newFlavor("test", 1, 1024) + val image = client.newImage("test") + val server = client.newServer("test", image, flavor, start = false) + val slot = slot() - val watcher = mockk(relaxUnitFun = true) - server.watch(watcher) + val watcher = mockk(relaxUnitFun = true) + server.watch(watcher) - // Start server - server.start() - delay(5L * 60 * 1000) - coVerify { host.spawn(capture(slot)) } + // Start server + server.start() + delay(5L * 60 * 1000) + coVerify { host.spawn(capture(slot)) } - listeners.forEach { it.onStateChanged(host, slot.captured, ServerState.RUNNING) } + listeners.forEach { it.onStateChanged(host, slot.captured, ServerState.RUNNING) } - server.reload() - assertEquals(ServerState.RUNNING, server.state) + server.reload() + assertEquals(ServerState.RUNNING, server.state) - verify { watcher.onStateChanged(server, ServerState.RUNNING) } + verify { watcher.onStateChanged(server, ServerState.RUNNING) } - // Stop server - listeners.forEach { it.onStateChanged(host, slot.captured, ServerState.TERMINATED) } + // Stop server + listeners.forEach { it.onStateChanged(host, slot.captured, ServerState.TERMINATED) } - server.reload() - assertEquals(ServerState.TERMINATED, server.state) + server.reload() + assertEquals(ServerState.TERMINATED, server.state) - verify { watcher.onStateChanged(server, ServerState.TERMINATED) } - } + verify { watcher.onStateChanged(server, ServerState.TERMINATED) } + } @Test - fun testServerDeployFailure() = scope.runSimulation { - val host = mockk(relaxUnitFun = true) - val listeners = mutableListOf() - - every { host.uid } returns UUID.randomUUID() - every { host.model } returns HostModel(4 * 2600.0, 4, 2048) - every { host.state } returns HostState.UP - every { host.canFit(any()) } returns true - every { host.addListener(any()) } answers { listeners.add(it.invocation.args[0] as HostListener) } - coEvery { host.spawn(any()) } throws IllegalStateException() - - service.addHost(host) - - val client = service.newClient() - val flavor = client.newFlavor("test", 1, 1024) - val image = client.newImage("test") - val server = client.newServer("test", image, flavor, start = false) - - server.start() - delay(5L * 60 * 1000) - - server.reload() - assertEquals(ServerState.PROVISIONING, server.state) - } + fun testServerDeployFailure() = + scope.runSimulation { + val host = mockk(relaxUnitFun = true) + val listeners = mutableListOf() + + every { host.uid } returns UUID.randomUUID() + every { host.model } returns HostModel(4 * 2600.0, 4, 2048) + every { host.state } returns HostState.UP + every { host.canFit(any()) } returns true + every { host.addListener(any()) } answers { listeners.add(it.invocation.args[0] as HostListener) } + coEvery { host.spawn(any()) } throws IllegalStateException() + + service.addHost(host) + + val client = service.newClient() + val flavor = client.newFlavor("test", 1, 1024) + val image = client.newImage("test") + val server = client.newServer("test", image, flavor, start = false) + + server.start() + delay(5L * 60 * 1000) + + server.reload() + assertEquals(ServerState.PROVISIONING, server.state) + } } diff --git a/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/ServiceServerTest.kt b/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/ServiceServerTest.kt index f9fcd27b3..6e0f11b37 100644 --- a/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/ServiceServerTest.kt +++ b/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/ServiceServerTest.kt @@ -80,193 +80,205 @@ class ServiceServerTest { } @Test - fun testStartTerminatedServer() = runSimulation { - val service = mockk() - val uid = UUID.randomUUID() - val flavor = mockFlavor() - val image = mockImage() - val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) + fun testStartTerminatedServer() = + runSimulation { + val service = mockk() + val uid = UUID.randomUUID() + val flavor = mockFlavor() + val image = mockImage() + val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) - every { service.schedule(any()) } answers { ComputeService.SchedulingRequest(it.invocation.args[0] as ServiceServer, 0) } + every { service.schedule(any()) } answers { ComputeService.SchedulingRequest(it.invocation.args[0] as ServiceServer, 0) } - server.start() + server.start() - verify(exactly = 1) { service.schedule(server) } - assertEquals(ServerState.PROVISIONING, server.state) - } + verify(exactly = 1) { service.schedule(server) } + assertEquals(ServerState.PROVISIONING, server.state) + } @Test - fun testStartDeletedServer() = runSimulation { - val service = mockk() - val uid = UUID.randomUUID() - val flavor = mockFlavor() - val image = mockImage() - val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) + fun testStartDeletedServer() = + runSimulation { + val service = mockk() + val uid = UUID.randomUUID() + val flavor = mockFlavor() + val image = mockImage() + val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) - server.setState(ServerState.DELETED) + server.setState(ServerState.DELETED) - assertThrows { server.start() } - } + assertThrows { server.start() } + } @Test - fun testStartProvisioningServer() = runSimulation { - val service = mockk() - val uid = UUID.randomUUID() - val flavor = mockFlavor() - val image = mockImage() - val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) + fun testStartProvisioningServer() = + runSimulation { + val service = mockk() + val uid = UUID.randomUUID() + val flavor = mockFlavor() + val image = mockImage() + val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) - server.setState(ServerState.PROVISIONING) + server.setState(ServerState.PROVISIONING) - server.start() + server.start() - assertEquals(ServerState.PROVISIONING, server.state) - } + assertEquals(ServerState.PROVISIONING, server.state) + } @Test - fun testStartRunningServer() = runSimulation { - val service = mockk() - val uid = UUID.randomUUID() - val flavor = mockFlavor() - val image = mockImage() - val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) + fun testStartRunningServer() = + runSimulation { + val service = mockk() + val uid = UUID.randomUUID() + val flavor = mockFlavor() + val image = mockImage() + val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) - server.setState(ServerState.RUNNING) + server.setState(ServerState.RUNNING) - server.start() + server.start() - assertEquals(ServerState.RUNNING, server.state) - } + assertEquals(ServerState.RUNNING, server.state) + } @Test - fun testStopProvisioningServer() = runSimulation { - val service = mockk() - val uid = UUID.randomUUID() - val flavor = mockFlavor() - val image = mockImage() - val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) - val request = ComputeService.SchedulingRequest(server, 0) + fun testStopProvisioningServer() = + runSimulation { + val service = mockk() + val uid = UUID.randomUUID() + val flavor = mockFlavor() + val image = mockImage() + val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) + val request = ComputeService.SchedulingRequest(server, 0) - every { service.schedule(any()) } returns request + every { service.schedule(any()) } returns request - server.start() - server.stop() + server.start() + server.stop() - assertTrue(request.isCancelled) - assertEquals(ServerState.TERMINATED, server.state) - } + assertTrue(request.isCancelled) + assertEquals(ServerState.TERMINATED, server.state) + } @Test - fun testStopTerminatedServer() = runSimulation { - val service = mockk() - val uid = UUID.randomUUID() - val flavor = mockFlavor() - val image = mockImage() - val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) + fun testStopTerminatedServer() = + runSimulation { + val service = mockk() + val uid = UUID.randomUUID() + val flavor = mockFlavor() + val image = mockImage() + val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) - server.setState(ServerState.TERMINATED) - server.stop() + server.setState(ServerState.TERMINATED) + server.stop() - assertEquals(ServerState.TERMINATED, server.state) - } + assertEquals(ServerState.TERMINATED, server.state) + } @Test - fun testStopDeletedServer() = runSimulation { - val service = mockk() - val uid = UUID.randomUUID() - val flavor = mockFlavor() - val image = mockImage() - val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) + fun testStopDeletedServer() = + runSimulation { + val service = mockk() + val uid = UUID.randomUUID() + val flavor = mockFlavor() + val image = mockImage() + val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) - server.setState(ServerState.DELETED) - server.stop() + server.setState(ServerState.DELETED) + server.stop() - assertEquals(ServerState.DELETED, server.state) - } + assertEquals(ServerState.DELETED, server.state) + } @Test - fun testStopRunningServer() = runSimulation { - val service = mockk() - val uid = UUID.randomUUID() - val flavor = mockFlavor() - val image = mockImage() - val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) - val host = mockk(relaxUnitFun = true) - - server.setState(ServerState.RUNNING) - server.host = host - server.stop() - yield() - - verify { host.stop(server) } - } + fun testStopRunningServer() = + runSimulation { + val service = mockk() + val uid = UUID.randomUUID() + val flavor = mockFlavor() + val image = mockImage() + val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) + val host = mockk(relaxUnitFun = true) + + server.setState(ServerState.RUNNING) + server.host = host + server.stop() + yield() + + verify { host.stop(server) } + } @Test - fun testDeleteProvisioningServer() = runSimulation { - val service = mockk(relaxUnitFun = true) - val uid = UUID.randomUUID() - val flavor = mockFlavor() - val image = mockImage() - val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) - val request = ComputeService.SchedulingRequest(server, 0) + fun testDeleteProvisioningServer() = + runSimulation { + val service = mockk(relaxUnitFun = true) + val uid = UUID.randomUUID() + val flavor = mockFlavor() + val image = mockImage() + val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) + val request = ComputeService.SchedulingRequest(server, 0) - every { service.schedule(any()) } returns request + every { service.schedule(any()) } returns request - server.start() - server.delete() + server.start() + server.delete() - assertTrue(request.isCancelled) - assertEquals(ServerState.DELETED, server.state) - verify { service.delete(server) } - } + assertTrue(request.isCancelled) + assertEquals(ServerState.DELETED, server.state) + verify { service.delete(server) } + } @Test - fun testDeleteTerminatedServer() = runSimulation { - val service = mockk(relaxUnitFun = true) - val uid = UUID.randomUUID() - val flavor = mockFlavor() - val image = mockImage() - val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) + fun testDeleteTerminatedServer() = + runSimulation { + val service = mockk(relaxUnitFun = true) + val uid = UUID.randomUUID() + val flavor = mockFlavor() + val image = mockImage() + val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) - server.setState(ServerState.TERMINATED) - server.delete() + server.setState(ServerState.TERMINATED) + server.delete() - assertEquals(ServerState.DELETED, server.state) + assertEquals(ServerState.DELETED, server.state) - verify { service.delete(server) } - } + verify { service.delete(server) } + } @Test - fun testDeleteDeletedServer() = runSimulation { - val service = mockk(relaxUnitFun = true) - val uid = UUID.randomUUID() - val flavor = mockFlavor() - val image = mockImage() - val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) + fun testDeleteDeletedServer() = + runSimulation { + val service = mockk(relaxUnitFun = true) + val uid = UUID.randomUUID() + val flavor = mockFlavor() + val image = mockImage() + val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) - server.setState(ServerState.DELETED) - server.delete() + server.setState(ServerState.DELETED) + server.delete() - assertEquals(ServerState.DELETED, server.state) - } + assertEquals(ServerState.DELETED, server.state) + } @Test - fun testDeleteRunningServer() = runSimulation { - val service = mockk(relaxUnitFun = true) - val uid = UUID.randomUUID() - val flavor = mockFlavor() - val image = mockImage() - val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) - val host = mockk(relaxUnitFun = true) - - server.setState(ServerState.RUNNING) - server.host = host - server.delete() - yield() - - verify { host.delete(server) } - verify { service.delete(server) } - } + fun testDeleteRunningServer() = + runSimulation { + val service = mockk(relaxUnitFun = true) + val uid = UUID.randomUUID() + val flavor = mockFlavor() + val image = mockImage() + val server = ServiceServer(service, uid, "test", flavor, image, mutableMapOf(), mutableMapOf()) + val host = mockk(relaxUnitFun = true) + + server.setState(ServerState.RUNNING) + server.host = host + server.delete() + yield() + + verify { host.delete(server) } + verify { service.delete(server) } + } private fun mockFlavor(): ServiceFlavor { val flavor = mockk() diff --git a/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/scheduler/FilterSchedulerTest.kt b/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/scheduler/FilterSchedulerTest.kt index 4af6f7ec7..a48052a16 100644 --- a/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/scheduler/FilterSchedulerTest.kt +++ b/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/scheduler/FilterSchedulerTest.kt @@ -57,7 +57,7 @@ internal class FilterSchedulerTest { FilterScheduler( filters = emptyList(), weighers = emptyList(), - subsetSize = 0 + subsetSize = 0, ) } @@ -65,17 +65,18 @@ internal class FilterSchedulerTest { FilterScheduler( filters = emptyList(), weighers = emptyList(), - subsetSize = -2 + subsetSize = -2, ) } } @Test fun testNoHosts() { - val scheduler = FilterScheduler( - filters = emptyList(), - weighers = emptyList() - ) + val scheduler = + FilterScheduler( + filters = emptyList(), + weighers = emptyList(), + ) val server = mockk() every { server.flavor.cpuCount } returns 2 @@ -86,10 +87,11 @@ internal class FilterSchedulerTest { @Test fun testNoFiltersAndSchedulers() { - val scheduler = FilterScheduler( - filters = emptyList(), - weighers = emptyList() - ) + val scheduler = + FilterScheduler( + filters = emptyList(), + weighers = emptyList(), + ) val hostA = mockk() every { hostA.host.state } returns HostState.DOWN @@ -107,18 +109,19 @@ internal class FilterSchedulerTest { // Make sure we get the first host both times assertAll( { assertEquals(hostA, scheduler.select(server)) }, - { assertEquals(hostA, scheduler.select(server)) } + { assertEquals(hostA, scheduler.select(server)) }, ) } @Test fun testNoFiltersAndSchedulersRandom() { - val scheduler = FilterScheduler( - filters = emptyList(), - weighers = emptyList(), - subsetSize = Int.MAX_VALUE, - random = Random(1) - ) + val scheduler = + FilterScheduler( + filters = emptyList(), + weighers = emptyList(), + subsetSize = Int.MAX_VALUE, + random = Random(1), + ) val hostA = mockk() every { hostA.host.state } returns HostState.DOWN @@ -136,16 +139,17 @@ internal class FilterSchedulerTest { // Make sure we get the first host both times assertAll( { assertEquals(hostB, scheduler.select(server)) }, - { assertEquals(hostA, scheduler.select(server)) } + { assertEquals(hostA, scheduler.select(server)) }, ) } @Test fun testHostIsDown() { - val scheduler = FilterScheduler( - filters = listOf(ComputeFilter()), - weighers = emptyList() - ) + val scheduler = + FilterScheduler( + filters = listOf(ComputeFilter()), + weighers = emptyList(), + ) val host = mockk() every { host.host.state } returns HostState.DOWN @@ -161,10 +165,11 @@ internal class FilterSchedulerTest { @Test fun testHostIsUp() { - val scheduler = FilterScheduler( - filters = listOf(ComputeFilter()), - weighers = emptyList() - ) + val scheduler = + FilterScheduler( + filters = listOf(ComputeFilter()), + weighers = emptyList(), + ) val host = mockk() every { host.host.state } returns HostState.UP @@ -180,10 +185,11 @@ internal class FilterSchedulerTest { @Test fun testRamFilter() { - val scheduler = FilterScheduler( - filters = listOf(RamFilter(1.0)), - weighers = emptyList() - ) + val scheduler = + FilterScheduler( + filters = listOf(RamFilter(1.0)), + weighers = emptyList(), + ) val hostA = mockk() every { hostA.host.state } returns HostState.UP @@ -207,10 +213,11 @@ internal class FilterSchedulerTest { @Test fun testRamFilterOvercommit() { - val scheduler = FilterScheduler( - filters = listOf(RamFilter(1.5)), - weighers = emptyList() - ) + val scheduler = + FilterScheduler( + filters = listOf(RamFilter(1.5)), + weighers = emptyList(), + ) val host = mockk() every { host.host.state } returns HostState.UP @@ -228,10 +235,11 @@ internal class FilterSchedulerTest { @Test fun testVCpuFilter() { - val scheduler = FilterScheduler( - filters = listOf(VCpuFilter(1.0)), - weighers = emptyList() - ) + val scheduler = + FilterScheduler( + filters = listOf(VCpuFilter(1.0)), + weighers = emptyList(), + ) val hostA = mockk() every { hostA.host.state } returns HostState.UP @@ -255,10 +263,11 @@ internal class FilterSchedulerTest { @Test fun testVCpuFilterOvercommit() { - val scheduler = FilterScheduler( - filters = listOf(VCpuFilter(16.0)), - weighers = emptyList() - ) + val scheduler = + FilterScheduler( + filters = listOf(VCpuFilter(16.0)), + weighers = emptyList(), + ) val host = mockk() every { host.host.state } returns HostState.UP @@ -276,10 +285,11 @@ internal class FilterSchedulerTest { @Test fun testVCpuCapacityFilter() { - val scheduler = FilterScheduler( - filters = listOf(VCpuCapacityFilter()), - weighers = emptyList() - ) + val scheduler = + FilterScheduler( + filters = listOf(VCpuCapacityFilter()), + weighers = emptyList(), + ) val hostA = mockk() every { hostA.host.state } returns HostState.UP @@ -304,10 +314,11 @@ internal class FilterSchedulerTest { @Test fun testInstanceCountFilter() { - val scheduler = FilterScheduler( - filters = listOf(InstanceCountFilter(limit = 2)), - weighers = emptyList() - ) + val scheduler = + FilterScheduler( + filters = listOf(InstanceCountFilter(limit = 2)), + weighers = emptyList(), + ) val hostA = mockk() every { hostA.host.state } returns HostState.UP @@ -331,10 +342,11 @@ internal class FilterSchedulerTest { @Test fun testAffinityFilter() { - val scheduler = FilterScheduler( - filters = listOf(SameHostFilter()), - weighers = emptyList() - ) + val scheduler = + FilterScheduler( + filters = listOf(SameHostFilter()), + weighers = emptyList(), + ) val serverA = mockk() every { serverA.uid } returns UUID.randomUUID() @@ -370,10 +382,11 @@ internal class FilterSchedulerTest { @Test fun testAntiAffinityFilter() { - val scheduler = FilterScheduler( - filters = listOf(DifferentHostFilter()), - weighers = emptyList() - ) + val scheduler = + FilterScheduler( + filters = listOf(DifferentHostFilter()), + weighers = emptyList(), + ) val serverA = mockk() every { serverA.uid } returns UUID.randomUUID() @@ -409,10 +422,11 @@ internal class FilterSchedulerTest { @Test fun testRamWeigher() { - val scheduler = FilterScheduler( - filters = emptyList(), - weighers = listOf(RamWeigher(1.5)) - ) + val scheduler = + FilterScheduler( + filters = emptyList(), + weighers = listOf(RamWeigher(1.5)), + ) val hostA = mockk() every { hostA.host.state } returns HostState.UP @@ -436,10 +450,11 @@ internal class FilterSchedulerTest { @Test fun testCoreRamWeigher() { - val scheduler = FilterScheduler( - filters = emptyList(), - weighers = listOf(CoreRamWeigher(1.5)) - ) + val scheduler = + FilterScheduler( + filters = emptyList(), + weighers = listOf(CoreRamWeigher(1.5)), + ) val hostA = mockk() every { hostA.host.state } returns HostState.UP @@ -463,10 +478,11 @@ internal class FilterSchedulerTest { @Test fun testVCpuWeigher() { - val scheduler = FilterScheduler( - filters = emptyList(), - weighers = listOf(VCpuWeigher(16.0)) - ) + val scheduler = + FilterScheduler( + filters = emptyList(), + weighers = listOf(VCpuWeigher(16.0)), + ) val hostA = mockk() every { hostA.host.state } returns HostState.UP @@ -490,10 +506,11 @@ internal class FilterSchedulerTest { @Test fun testInstanceCountWeigher() { - val scheduler = FilterScheduler( - filters = emptyList(), - weighers = listOf(InstanceCountWeigher(multiplier = -1.0)) - ) + val scheduler = + FilterScheduler( + filters = emptyList(), + weighers = listOf(InstanceCountWeigher(multiplier = -1.0)), + ) val hostA = mockk() every { hostA.host.state } returns HostState.UP diff --git a/opendc-compute/opendc-compute-simulator/build.gradle.kts b/opendc-compute/opendc-compute-simulator/build.gradle.kts index 625f278bd..9692f6baf 100644 --- a/opendc-compute/opendc-compute-simulator/build.gradle.kts +++ b/opendc-compute/opendc-compute-simulator/build.gradle.kts @@ -22,7 +22,7 @@ description = "Simulator for OpenDC Compute" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/MutableServiceRegistry.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/MutableServiceRegistry.kt index 49b3688eb..ca72c9103 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/MutableServiceRegistry.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/MutableServiceRegistry.kt @@ -34,7 +34,11 @@ public interface MutableServiceRegistry : ServiceRegistry { * @param type The interface provided by the service. * @param service A reference to the actual implementation of the service. */ - public fun register(name: String, type: Class, service: T) + public fun register( + name: String, + type: Class, + service: T, + ) /** * Remove the service with [name] and [type] from this registry. @@ -42,7 +46,10 @@ public interface MutableServiceRegistry : ServiceRegistry { * @param name The name of the service to remove, which should follow the rules for domain names as defined by DNS. * @param type The type of the service to remove. */ - public fun remove(name: String, type: Class<*>) + public fun remove( + name: String, + type: Class<*>, + ) /** * Remove all services registered with [name]. diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/ServiceRegistry.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/ServiceRegistry.kt index d3af3f010..5a4bced1b 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/ServiceRegistry.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/ServiceRegistry.kt @@ -36,7 +36,10 @@ public interface ServiceRegistry { * @param type The type of the service to resolve, identified by the interface that is implemented by the service. * @return The service with specified [name] and implementing [type] or `null` if it does not exist. */ - public fun resolve(name: String, type: Class): T? + public fun resolve( + name: String, + type: Class, + ): T? /** * Create a copy of the registry. diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/ServiceRegistryImpl.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/ServiceRegistryImpl.kt index a9d05844d..bf3ee43f6 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/ServiceRegistryImpl.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/ServiceRegistryImpl.kt @@ -27,14 +27,21 @@ package org.opendc.compute.simulator */ internal class ServiceRegistryImpl(private val registry: MutableMap, Any>> = mutableMapOf()) : MutableServiceRegistry { - override fun resolve(name: String, type: Class): T? { + override fun resolve( + name: String, + type: Class, + ): T? { val servicesForName = registry[name] ?: return null @Suppress("UNCHECKED_CAST") return servicesForName[type] as T? } - override fun register(name: String, type: Class, service: T) { + override fun register( + name: String, + type: Class, + service: T, + ) { val services = registry.computeIfAbsent(name) { mutableMapOf() } if (type in services) { @@ -44,7 +51,10 @@ internal class ServiceRegistryImpl(private val registry: MutableMap) { + override fun remove( + name: String, + type: Class<*>, + ) { val services = registry[name] ?: return services.remove(type) } diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt index 16ded689e..47650f5d1 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt @@ -73,9 +73,8 @@ public class SimHost( private val hypervisor: SimHypervisor, private val mapper: SimWorkloadMapper = DefaultWorkloadMapper, private val bootModel: Supplier = Supplier { null }, - private val optimize: Boolean = false + private val optimize: Boolean = false, ) : Host, AutoCloseable { - /** * The event listeners registered with this host. */ @@ -85,9 +84,9 @@ public class SimHost( * The virtual machines running on the hypervisor. */ private val guests = HashMap() - private val _guests = mutableListOf() + private val temporaryGuests = mutableListOf() // TODO: Determine a better naming for this - private var _state: HostState = HostState.DOWN + private var localState: HostState = HostState.DOWN set(value) { if (value != field) { listeners.forEach { it.onStateChanged(this, value) } @@ -95,24 +94,26 @@ public class SimHost( field = value } - private val model: HostModel = HostModel( - machine.model.cpus.sumOf { it.frequency }, - machine.model.cpus.size, - machine.model.memory.sumOf { it.size } - ) + private val model: HostModel = + HostModel( + machine.model.cpus.sumOf { it.frequency }, + machine.model.cpus.size, + machine.model.memory.sumOf { it.size }, + ) /** * The [GuestListener] that listens for guest events. */ - private val guestListener = object : GuestListener { - override fun onStart(guest: Guest) { - listeners.forEach { it.onStateChanged(this@SimHost, guest.server, guest.state) } - } + private val guestListener = + object : GuestListener { + override fun onStart(guest: Guest) { + listeners.forEach { it.onStateChanged(this@SimHost, guest.server, guest.state) } + } - override fun onStop(guest: Guest) { - listeners.forEach { it.onStateChanged(this@SimHost, guest.server, guest.state) } + override fun onStop(guest: Guest) { + listeners.forEach { it.onStateChanged(this@SimHost, guest.server, guest.state) } + } } - } init { launch() @@ -135,7 +136,7 @@ public class SimHost( } override fun getState(): HostState { - return _state + return localState } override fun getInstances(): Set { @@ -155,17 +156,18 @@ public class SimHost( require(canFit(key)) { "Server does not fit" } val machine = hypervisor.newMachine(key.flavor.toMachineModel()) - val newGuest = Guest( - clock, - this, - hypervisor, - mapper, - guestListener, - server, - machine - ) - - _guests.add(newGuest) + val newGuest = + Guest( + clock, + this, + hypervisor, + mapper, + guestListener, + server, + machine, + ) + + temporaryGuests.add(newGuest) newGuest } } @@ -210,7 +212,7 @@ public class SimHost( var error = 0 var invalid = 0 - val guests = _guests.listIterator() + val guests = temporaryGuests.listIterator() for (guest in guests) { when (guest.state) { ServerState.TERMINATED -> terminated++ @@ -226,15 +228,15 @@ public class SimHost( } return HostSystemStats( - Duration.ofMillis(_uptime), - Duration.ofMillis(_downtime), - _bootTime, + Duration.ofMillis(localUptime), + Duration.ofMillis(localDowntime), + localBootTime, machine.psu.powerDraw, machine.psu.energyUsage, terminated, running, error, - invalid + invalid, ) } @@ -255,7 +257,7 @@ public class SimHost( hypervisor.cpuCapacity, hypervisor.cpuDemand, hypervisor.cpuUsage, - hypervisor.cpuUsage / _cpuLimit + hypervisor.cpuUsage / localCpuLimit, ) } @@ -275,7 +277,7 @@ public class SimHost( public fun fail() { reset(HostState.ERROR) - for (guest in _guests) { + for (guest in temporaryGuests) { guest.fail() } } @@ -299,31 +301,33 @@ public class SimHost( val bootWorkload = bootModel.get() val hypervisor = hypervisor - val hypervisorWorkload = object : SimWorkload by hypervisor { - override fun onStart(ctx: SimMachineContext) { - try { - _bootTime = clock.instant() - _state = HostState.UP - hypervisor.onStart(ctx) - - // Recover the guests that were running on the hypervisor. - for (guest in _guests) { - guest.recover() + val hypervisorWorkload = + object : SimWorkload by hypervisor { + override fun onStart(ctx: SimMachineContext) { + try { + localBootTime = clock.instant() + localState = HostState.UP + hypervisor.onStart(ctx) + + // Recover the guests that were running on the hypervisor. + for (guest in temporaryGuests) { + guest.recover() + } + } catch (cause: Throwable) { + localState = HostState.ERROR + throw cause } - } catch (cause: Throwable) { - _state = HostState.ERROR - throw cause } } - } val workload = if (bootWorkload != null) SimWorkloads.chain(bootWorkload, hypervisorWorkload) else hypervisorWorkload // Launch hypervisor onto machine - ctx = machine.startWorkload(workload, emptyMap()) { cause -> - _state = if (cause != null) HostState.ERROR else HostState.DOWN - ctx = null - } + ctx = + machine.startWorkload(workload, emptyMap()) { cause -> + localState = if (cause != null) HostState.ERROR else HostState.DOWN + ctx = null + } } /** @@ -334,7 +338,7 @@ public class SimHost( // Stop the hypervisor ctx?.shutdown() - _state = state + localState = state } /** @@ -352,28 +356,28 @@ public class SimHost( return if (optimize) model.optimize() else model } - private var _lastReport = clock.millis() - private var _uptime = 0L - private var _downtime = 0L - private var _bootTime: Instant? = null - private val _cpuLimit = machine.model.cpus.sumOf { it.frequency } + private var localLastReport = clock.millis() + private var localUptime = 0L + private var localDowntime = 0L + private var localBootTime: Instant? = null + private val localCpuLimit = machine.model.cpus.sumOf { it.frequency } /** * Helper function to track the uptime of a machine. */ private fun updateUptime() { val now = clock.millis() - val duration = now - _lastReport - _lastReport = now + val duration = now - localLastReport + localLastReport = now - if (_state == HostState.UP) { - _uptime += duration - } else if (_state == HostState.ERROR) { + if (localState == HostState.UP) { + localUptime += duration + } else if (localState == HostState.ERROR) { // Only increment downtime if the machine is in a failure state - _downtime += duration + localDowntime += duration } - val guests = _guests + val guests = temporaryGuests for (i in guests.indices) { guests[i].updateUptime() } diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/FailureModel.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/FailureModel.kt index 5e94830cd..9511017f1 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/FailureModel.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/FailureModel.kt @@ -38,6 +38,6 @@ public interface FailureModel { context: CoroutineContext, clock: InstantSource, service: ComputeService, - random: RandomGenerator + random: RandomGenerator, ): HostFaultInjector } diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/FailureModels.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/FailureModels.kt index 337f3c60b..b8887627b 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/FailureModels.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/FailureModels.kt @@ -46,7 +46,7 @@ public fun grid5000(failureInterval: Duration): FailureModel { context: CoroutineContext, clock: InstantSource, service: ComputeService, - random: RandomGenerator + random: RandomGenerator, ): HostFaultInjector { val rng = Well19937c(random.nextLong()) val hosts = service.hosts.map { it as SimHost }.toSet() @@ -59,7 +59,7 @@ public fun grid5000(failureInterval: Duration): FailureModel { hosts, iat = LogNormalDistribution(rng, ln(failureInterval.toHours().toDouble()), 1.03), selector = StochasticVictimSelector(LogNormalDistribution(rng, 1.88, 1.25), random), - fault = StartStopHostFault(LogNormalDistribution(rng, 8.89, 2.71)) + fault = StartStopHostFault(LogNormalDistribution(rng, 8.89, 2.71)), ) } diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/HostFault.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/HostFault.kt index d34f70d78..faf536ad8 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/HostFault.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/HostFault.kt @@ -32,5 +32,8 @@ public interface HostFault { /** * Apply the fault to the specified [victims]. */ - public suspend fun apply(clock: InstantSource, victims: List) + public suspend fun apply( + clock: InstantSource, + victims: List, + ) } diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/HostFaultInjector.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/HostFaultInjector.kt index afbb99d26..26084a1bf 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/HostFaultInjector.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/HostFaultInjector.kt @@ -60,7 +60,7 @@ public interface HostFaultInjector : AutoCloseable { hosts: Set, iat: RealDistribution, selector: VictimSelector, - fault: HostFault + fault: HostFault, ): HostFaultInjector = HostFaultInjectorImpl(context, clock, hosts, iat, selector, fault) } } diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/StartStopHostFault.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/StartStopHostFault.kt index 8bd25391b..45545f3bc 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/StartStopHostFault.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/StartStopHostFault.kt @@ -32,7 +32,10 @@ import kotlin.math.roundToLong * A type of [HostFault] where the hosts are stopped and recover after some random amount of time. */ public class StartStopHostFault(private val duration: RealDistribution) : HostFault { - override suspend fun apply(clock: InstantSource, victims: List) { + override suspend fun apply( + clock: InstantSource, + victims: List, + ) { for (host in victims) { host.fail() } diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/StochasticVictimSelector.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/StochasticVictimSelector.kt index 4aba0e914..93463cdb0 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/StochasticVictimSelector.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/failure/StochasticVictimSelector.kt @@ -34,9 +34,8 @@ import kotlin.math.roundToInt */ public class StochasticVictimSelector( private val size: RealDistribution, - private val random: RandomGenerator = SplittableRandom(0) + private val random: RandomGenerator = SplittableRandom(0), ) : VictimSelector { - override fun select(hosts: Set): List { val n = size.sample().roundToInt() val result = ArrayList(n) diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/Guest.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/Guest.kt index 354eb3d07..e268c506c 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/Guest.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/Guest.kt @@ -47,7 +47,7 @@ internal class Guest( private val mapper: SimWorkloadMapper, private val listener: GuestListener, val server: Server, - val machine: SimVirtualMachine + val machine: SimVirtualMachine, ) { /** * The state of the [Guest]. @@ -132,9 +132,9 @@ internal class Guest( updateUptime() return GuestSystemStats( - Duration.ofMillis(_uptime), - Duration.ofMillis(_downtime), - _bootTime + Duration.ofMillis(localUptime), + Duration.ofMillis(localDowntime), + localBootTime, ) } @@ -152,7 +152,7 @@ internal class Guest( counters.cpuLostTime / 1000L, machine.cpuCapacity, machine.cpuUsage, - machine.cpuUsage / _cpuLimit + machine.cpuUsage / localCpuLimit, ) } @@ -173,10 +173,11 @@ internal class Guest( val workload: SimWorkload = mapper.createWorkload(server) workload.setOffset(clock.millis()) val meta = mapOf("driver" to host, "server" to server) + server.meta - ctx = machine.startWorkload(workload, meta) { cause -> - onStop(if (cause != null) ServerState.ERROR else ServerState.TERMINATED) - ctx = null - } + ctx = + machine.startWorkload(workload, meta) { cause -> + onStop(if (cause != null) ServerState.ERROR else ServerState.TERMINATED) + ctx = null + } } /** @@ -201,7 +202,7 @@ internal class Guest( * This method is invoked when the guest was started on the host and has booted into a running state. */ private fun onStart() { - _bootTime = clock.instant() + localBootTime = clock.instant() state = ServerState.RUNNING listener.onStart(this) } @@ -216,24 +217,24 @@ internal class Guest( listener.onStop(this) } - private var _uptime = 0L - private var _downtime = 0L - private var _lastReport = clock.millis() - private var _bootTime: Instant? = null - private val _cpuLimit = machine.model.cpus.sumOf { it.frequency } + private var localUptime = 0L + private var localDowntime = 0L + private var localLastReport = clock.millis() + private var localBootTime: Instant? = null + private val localCpuLimit = machine.model.cpus.sumOf { it.frequency } /** * Helper function to track the uptime and downtime of the guest. */ fun updateUptime() { val now = clock.millis() - val duration = now - _lastReport - _lastReport = now + val duration = now - localLastReport + localLastReport = now if (state == ServerState.RUNNING) { - _uptime += duration + localUptime += duration } else if (state == ServerState.ERROR) { - _downtime += duration + localDowntime += duration } } diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/HostFaultInjectorImpl.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/HostFaultInjectorImpl.kt index afc0b0d42..c75ce5287 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/HostFaultInjectorImpl.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/HostFaultInjectorImpl.kt @@ -52,7 +52,7 @@ internal class HostFaultInjectorImpl( private val hosts: Set, private val iat: RealDistribution, private val selector: VictimSelector, - private val fault: HostFault + private val fault: HostFault, ) : HostFaultInjector { /** * The scope in which the injector runs. @@ -72,10 +72,11 @@ internal class HostFaultInjectorImpl( return } - job = scope.launch { - runInjector() - job = null - } + job = + scope.launch { + runInjector() + job = null + } } /** diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ComputeMonitorProvisioningStep.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ComputeMonitorProvisioningStep.kt index 50e7bd0d5..753cde165 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ComputeMonitorProvisioningStep.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ComputeMonitorProvisioningStep.kt @@ -34,10 +34,13 @@ import java.time.Duration public class ComputeMonitorProvisioningStep( private val serviceDomain: String, private val monitor: ComputeMonitor, - private val exportInterval: Duration + private val exportInterval: Duration, ) : ProvisioningStep { override fun apply(ctx: ProvisioningContext): AutoCloseable { - val service = requireNotNull(ctx.registry.resolve(serviceDomain, ComputeService::class.java)) { "Compute service $serviceDomain does not exist" } + val service = + requireNotNull( + ctx.registry.resolve(serviceDomain, ComputeService::class.java), + ) { "Compute service $serviceDomain does not exist" } val metricReader = ComputeMetricReader(ctx.dispatcher, service, monitor, exportInterval) return AutoCloseable { metricReader.close() } } diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ComputeServiceProvisioningStep.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ComputeServiceProvisioningStep.kt index fc555016e..484ae7ca0 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ComputeServiceProvisioningStep.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ComputeServiceProvisioningStep.kt @@ -36,12 +36,13 @@ import java.time.Duration public class ComputeServiceProvisioningStep internal constructor( private val serviceDomain: String, private val scheduler: (ProvisioningContext) -> ComputeScheduler, - private val schedulingQuantum: Duration + private val schedulingQuantum: Duration, ) : ProvisioningStep { override fun apply(ctx: ProvisioningContext): AutoCloseable { - val service = ComputeService.builder(ctx.dispatcher, scheduler(ctx)) - .withQuantum(schedulingQuantum) - .build() + val service = + ComputeService.builder(ctx.dispatcher, scheduler(ctx)) + .withQuantum(schedulingQuantum) + .build() ctx.registry.register(serviceDomain, ComputeService::class.java, service) return AutoCloseable { service.close() } diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ComputeSteps.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ComputeSteps.kt index 93f8fa4fe..53294b1b9 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ComputeSteps.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ComputeSteps.kt @@ -40,7 +40,7 @@ import java.time.Duration public fun setupComputeService( serviceDomain: String, scheduler: (ProvisioningContext) -> ComputeScheduler, - schedulingQuantum: Duration = Duration.ofMinutes(5) + schedulingQuantum: Duration = Duration.ofMinutes(5), ): ProvisioningStep { return ComputeServiceProvisioningStep(serviceDomain, scheduler, schedulingQuantum) } @@ -56,7 +56,7 @@ public fun setupComputeService( public fun registerComputeMonitor( serviceDomain: String, monitor: ComputeMonitor, - exportInterval: Duration = Duration.ofMinutes(5) + exportInterval: Duration = Duration.ofMinutes(5), ): ProvisioningStep { return ComputeMonitorProvisioningStep(serviceDomain, monitor, exportInterval) } @@ -69,6 +69,10 @@ public fun registerComputeMonitor( * @param specs A list of [HostSpec] objects describing the simulated hosts to provision. * @param optimize A flag to indicate that the CPU resources of the host should be merged into a single CPU resource. */ -public fun setupHosts(serviceDomain: String, specs: List, optimize: Boolean = false): ProvisioningStep { +public fun setupHosts( + serviceDomain: String, + specs: List, + optimize: Boolean = false, +): ProvisioningStep { return HostsProvisioningStep(serviceDomain, specs, optimize) } diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/HostsProvisioningStep.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/HostsProvisioningStep.kt index 3104ccbe7..d9c5e7a6b 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/HostsProvisioningStep.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/HostsProvisioningStep.kt @@ -40,10 +40,13 @@ import java.util.SplittableRandom public class HostsProvisioningStep internal constructor( private val serviceDomain: String, private val specs: List, - private val optimize: Boolean + private val optimize: Boolean, ) : ProvisioningStep { override fun apply(ctx: ProvisioningContext): AutoCloseable { - val service = requireNotNull(ctx.registry.resolve(serviceDomain, ComputeService::class.java)) { "Compute service $serviceDomain does not exist" } + val service = + requireNotNull( + ctx.registry.resolve(serviceDomain, ComputeService::class.java), + ) { "Compute service $serviceDomain does not exist" } val engine = FlowEngine.create(ctx.dispatcher) val graph = engine.newGraph() val hosts = mutableSetOf() @@ -52,15 +55,16 @@ public class HostsProvisioningStep internal constructor( val machine = SimBareMetalMachine.create(graph, spec.model, spec.psuFactory) val hypervisor = SimHypervisor.create(spec.multiplexerFactory, SplittableRandom(ctx.seeder.nextLong())) - val host = SimHost( - spec.uid, - spec.name, - spec.meta, - ctx.dispatcher.timeSource, - machine, - hypervisor, - optimize = optimize - ) + val host = + SimHost( + spec.uid, + spec.name, + spec.meta, + ctx.dispatcher.timeSource, + machine, + hypervisor, + optimize = optimize, + ) require(hosts.add(host)) { "Host with uid ${spec.uid} already exists" } service.addHost(host) diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/Provisioner.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/Provisioner.kt index 275378e74..58d3a8c29 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/Provisioner.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/Provisioner.kt @@ -43,13 +43,14 @@ public class Provisioner(dispatcher: Dispatcher, seed: Long) : AutoCloseable { /** * Implementation of [ProvisioningContext]. */ - private val context = object : ProvisioningContext { - override val dispatcher: Dispatcher = dispatcher - override val seeder: SplittableRandom = SplittableRandom(seed) - override val registry: MutableServiceRegistry = ServiceRegistryImpl() + private val context = + object : ProvisioningContext { + override val dispatcher: Dispatcher = dispatcher + override val seeder: SplittableRandom = SplittableRandom(seed) + override val registry: MutableServiceRegistry = ServiceRegistryImpl() - override fun toString(): String = "Provisioner.ProvisioningContext" - } + override fun toString(): String = "Provisioner.ProvisioningContext" + } /** * The stack of handles to run during the clean-up process. diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ProvisioningStep.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ProvisioningStep.kt index 0226a7047..c5b2be72a 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ProvisioningStep.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/provisioner/ProvisioningStep.kt @@ -56,6 +56,9 @@ public fun interface ProvisioningStep { * @param config The external configuration of the experiment runner. * @return The [ProvisioningStep] constructed according to [spec]. */ - public abstract fun create(spec: S, config: Config): ProvisioningStep + public abstract fun create( + spec: S, + config: Config, + ): ProvisioningStep } } diff --git a/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt b/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt index e9bac8dba..3a9854863 100644 --- a/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt +++ b/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt @@ -62,243 +62,274 @@ internal class SimHostTest { fun setUp() { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) - machineModel = MachineModel( - /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, - /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } - ) + machineModel = + MachineModel( + // cpus + List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + // memory + List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, + ) } /** * Test a single virtual machine hosted by the hypervisor. */ @Test - fun testSingle() = runSimulation { - val duration = 5 * 60L - - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create(graph, machineModel) - val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1)) - - val host = SimHost( - uid = UUID.randomUUID(), - name = "test", - meta = emptyMap(), - timeSource, - machine, - hypervisor - ) - val vmImage = MockImage( - UUID.randomUUID(), - "", - emptyMap(), - mapOf( - "workload" to - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 2 * 28.0, 2), - SimTraceFragment(duration * 1000, duration * 1000, 2 * 3500.0, 2), - SimTraceFragment(duration * 2000, duration * 1000, 0.0, 2), - SimTraceFragment(duration * 3000, duration * 1000, 2 * 183.0, 2) - ).createWorkload(1) - ) - ) - - val flavor = MockFlavor(2, 0) + fun testSingle() = + runSimulation { + val duration = 5 * 60L + + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1)) + + val host = + SimHost( + uid = UUID.randomUUID(), + name = "test", + meta = emptyMap(), + timeSource, + machine, + hypervisor, + ) + val vmImage = + MockImage( + UUID.randomUUID(), + "", + emptyMap(), + mapOf( + "workload" to + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 2 * 28.0, 2), + SimTraceFragment(duration * 1000, duration * 1000, 2 * 3500.0, 2), + SimTraceFragment(duration * 2000, duration * 1000, 0.0, 2), + SimTraceFragment(duration * 3000, duration * 1000, 2 * 183.0, 2), + ).createWorkload(1), + ), + ) + + val flavor = MockFlavor(2, 0) - suspendCancellableCoroutine { cont -> - host.addListener(object : HostListener { - private var finished = 0 - - override fun onStateChanged(host: Host, server: Server, newState: ServerState) { - if (newState == ServerState.TERMINATED && ++finished == 1) { - cont.resume(Unit) - } - } - }) - val server = MockServer(UUID.randomUUID(), "a", flavor, vmImage) - host.spawn(server) - host.start(server) - } + suspendCancellableCoroutine { cont -> + host.addListener( + object : HostListener { + private var finished = 0 + + override fun onStateChanged( + host: Host, + server: Server, + newState: ServerState, + ) { + if (newState == ServerState.TERMINATED && ++finished == 1) { + cont.resume(Unit) + } + } + }, + ) + val server = MockServer(UUID.randomUUID(), "a", flavor, vmImage) + host.spawn(server) + host.start(server) + } - // Ensure last cycle is collected - delay(1000L * duration) - host.close() + // Ensure last cycle is collected + delay(1000L * duration) + host.close() - val cpuStats = host.getCpuStats() + val cpuStats = host.getCpuStats() - assertAll( - { assertEquals(639564, cpuStats.activeTime, "Active time does not match") }, - { assertEquals(2360433, cpuStats.idleTime, "Idle time does not match") }, - { assertEquals(56251, cpuStats.stealTime, "Steal time does not match") }, - { assertEquals(1499999, timeSource.millis()) } - ) - } + assertAll( + { assertEquals(639564, cpuStats.activeTime, "Active time does not match") }, + { assertEquals(2360433, cpuStats.idleTime, "Idle time does not match") }, + { assertEquals(56251, cpuStats.stealTime, "Steal time does not match") }, + { assertEquals(1499999, timeSource.millis()) }, + ) + } /** * Test overcommitting of resources by the hypervisor. */ @Test - fun testOvercommitted() = runSimulation { - val duration = 5 * 60L - - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create(graph, machineModel) - val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1)) - - val host = SimHost( - uid = UUID.randomUUID(), - name = "test", - meta = emptyMap(), - timeSource, - machine, - hypervisor - ) - val vmImageA = MockImage( - UUID.randomUUID(), - "", - emptyMap(), - mapOf( - "workload" to - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 2 * 28.0, 2), - SimTraceFragment(duration * 1000, duration * 1000, 2 * 3500.0, 2), - SimTraceFragment(duration * 2000, duration * 1000, 0.0, 2), - SimTraceFragment(duration * 3000, duration * 1000, 2 * 183.0, 2) - ).createWorkload(1) - ) - ) - val vmImageB = MockImage( - UUID.randomUUID(), - "", - emptyMap(), - mapOf( - "workload" to - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 2 * 28.0, 2), - SimTraceFragment(duration * 1000, duration * 1000, 2 * 3100.0, 2), - SimTraceFragment(duration * 2000, duration * 1000, 0.0, 2), - SimTraceFragment(duration * 3000, duration * 1000, 2 * 73.0, 2) - ).createWorkload(1) - ) - ) - - val flavor = MockFlavor(2, 0) - - coroutineScope { - suspendCancellableCoroutine { cont -> - host.addListener(object : HostListener { - private var finished = 0 - - override fun onStateChanged(host: Host, server: Server, newState: ServerState) { - if (newState == ServerState.TERMINATED && ++finished == 2) { - cont.resume(Unit) - } - } - }) - val serverA = MockServer(UUID.randomUUID(), "a", flavor, vmImageA) - host.spawn(serverA) - val serverB = MockServer(UUID.randomUUID(), "b", flavor, vmImageB) - host.spawn(serverB) - - host.start(serverA) - host.start(serverB) + fun testOvercommitted() = + runSimulation { + val duration = 5 * 60L + + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1)) + + val host = + SimHost( + uid = UUID.randomUUID(), + name = "test", + meta = emptyMap(), + timeSource, + machine, + hypervisor, + ) + val vmImageA = + MockImage( + UUID.randomUUID(), + "", + emptyMap(), + mapOf( + "workload" to + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 2 * 28.0, 2), + SimTraceFragment(duration * 1000, duration * 1000, 2 * 3500.0, 2), + SimTraceFragment(duration * 2000, duration * 1000, 0.0, 2), + SimTraceFragment(duration * 3000, duration * 1000, 2 * 183.0, 2), + ).createWorkload(1), + ), + ) + val vmImageB = + MockImage( + UUID.randomUUID(), + "", + emptyMap(), + mapOf( + "workload" to + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 2 * 28.0, 2), + SimTraceFragment(duration * 1000, duration * 1000, 2 * 3100.0, 2), + SimTraceFragment(duration * 2000, duration * 1000, 0.0, 2), + SimTraceFragment(duration * 3000, duration * 1000, 2 * 73.0, 2), + ).createWorkload(1), + ), + ) + + val flavor = MockFlavor(2, 0) + + coroutineScope { + suspendCancellableCoroutine { cont -> + host.addListener( + object : HostListener { + private var finished = 0 + + override fun onStateChanged( + host: Host, + server: Server, + newState: ServerState, + ) { + if (newState == ServerState.TERMINATED && ++finished == 2) { + cont.resume(Unit) + } + } + }, + ) + val serverA = MockServer(UUID.randomUUID(), "a", flavor, vmImageA) + host.spawn(serverA) + val serverB = MockServer(UUID.randomUUID(), "b", flavor, vmImageB) + host.spawn(serverB) + + host.start(serverA) + host.start(serverB) + } } - } - // Ensure last cycle is collected - delay(1000L * duration) - host.close() + // Ensure last cycle is collected + delay(1000L * duration) + host.close() - val cpuStats = host.getCpuStats() + val cpuStats = host.getCpuStats() - assertAll( - { assertEquals(658502, cpuStats.activeTime, "Active time does not match") }, - { assertEquals(2341496, cpuStats.idleTime, "Idle time does not match") }, - { assertEquals(637504, cpuStats.stealTime, "Steal time does not match") }, - { assertEquals(1499999, timeSource.millis()) } - ) - } + assertAll( + { assertEquals(658502, cpuStats.activeTime, "Active time does not match") }, + { assertEquals(2341496, cpuStats.idleTime, "Idle time does not match") }, + { assertEquals(637504, cpuStats.stealTime, "Steal time does not match") }, + { assertEquals(1499999, timeSource.millis()) }, + ) + } /** * Test failure of the host. */ @Test - fun testFailure() = runSimulation { - val duration = 5 * 60L - - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create(graph, machineModel) - val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1)) - val host = SimHost( - uid = UUID.randomUUID(), - name = "test", - meta = emptyMap(), - timeSource, - machine, - hypervisor - ) - val image = MockImage( - UUID.randomUUID(), - "", - emptyMap(), - mapOf( - "workload" to - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 2 * 28.0, 2), - SimTraceFragment(duration * 1000L, duration * 1000, 2 * 3500.0, 2), - SimTraceFragment(duration * 2000L, duration * 1000, 0.0, 2), - SimTraceFragment(duration * 3000L, duration * 1000, 2 * 183.0, 2) - ).createWorkload(1) - ) - ) - val flavor = MockFlavor(2, 0) - val server = MockServer(UUID.randomUUID(), "a", flavor, image) - - coroutineScope { - host.spawn(server) - host.start(server) - delay(5000L) - host.fail() - delay(duration * 1000) - host.recover() - - suspendCancellableCoroutine { cont -> - host.addListener(object : HostListener { - override fun onStateChanged(host: Host, server: Server, newState: ServerState) { - if (newState == ServerState.TERMINATED) { - cont.resume(Unit) - } - } - }) + fun testFailure() = + runSimulation { + val duration = 5 * 60L + + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1)) + val host = + SimHost( + uid = UUID.randomUUID(), + name = "test", + meta = emptyMap(), + timeSource, + machine, + hypervisor, + ) + val image = + MockImage( + UUID.randomUUID(), + "", + emptyMap(), + mapOf( + "workload" to + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 2 * 28.0, 2), + SimTraceFragment(duration * 1000L, duration * 1000, 2 * 3500.0, 2), + SimTraceFragment(duration * 2000L, duration * 1000, 0.0, 2), + SimTraceFragment(duration * 3000L, duration * 1000, 2 * 183.0, 2), + ).createWorkload(1), + ), + ) + val flavor = MockFlavor(2, 0) + val server = MockServer(UUID.randomUUID(), "a", flavor, image) + + coroutineScope { + host.spawn(server) + host.start(server) + delay(5000L) + host.fail() + delay(duration * 1000) + host.recover() + + suspendCancellableCoroutine { cont -> + host.addListener( + object : HostListener { + override fun onStateChanged( + host: Host, + server: Server, + newState: ServerState, + ) { + if (newState == ServerState.TERMINATED) { + cont.resume(Unit) + } + } + }, + ) + } } - } - host.close() - // Ensure last cycle is collected - delay(1000L * duration) - - val cpuStats = host.getCpuStats() - val sysStats = host.getSystemStats() - val guestSysStats = host.getSystemStats(server) - - assertAll( - { assertEquals(1770344, cpuStats.idleTime, "Idle time does not match") }, - { assertEquals(639653, cpuStats.activeTime, "Active time does not match") }, - { assertEquals(1204999, sysStats.uptime.toMillis(), "Uptime does not match") }, - { assertEquals(300000, sysStats.downtime.toMillis(), "Downtime does not match") }, - { assertEquals(1204999, guestSysStats.uptime.toMillis(), "Guest uptime does not match") }, - { assertEquals(300000, guestSysStats.downtime.toMillis(), "Guest downtime does not match") } - ) - } + host.close() + // Ensure last cycle is collected + delay(1000L * duration) + + val cpuStats = host.getCpuStats() + val sysStats = host.getSystemStats() + val guestSysStats = host.getSystemStats(server) + + assertAll( + { assertEquals(1770344, cpuStats.idleTime, "Idle time does not match") }, + { assertEquals(639653, cpuStats.activeTime, "Active time does not match") }, + { assertEquals(1204999, sysStats.uptime.toMillis(), "Uptime does not match") }, + { assertEquals(300000, sysStats.downtime.toMillis(), "Downtime does not match") }, + { assertEquals(1204999, guestSysStats.uptime.toMillis(), "Guest uptime does not match") }, + { assertEquals(300000, guestSysStats.downtime.toMillis(), "Guest downtime does not match") }, + ) + } private class MockFlavor( override val cpuCount: Int, - override val memorySize: Long + override val memorySize: Long, ) : Flavor { override val uid: UUID = UUID.randomUUID() override val name: String = "test" @@ -318,7 +349,7 @@ internal class SimHostTest { override val uid: UUID, override val name: String, override val labels: Map, - override val meta: Map + override val meta: Map, ) : Image { override fun delete() { throw NotImplementedError() @@ -333,7 +364,7 @@ internal class SimHostTest { override val uid: UUID, override val name: String, override val flavor: Flavor, - override val image: Image + override val image: Image, ) : Server { override val labels: Map = emptyMap() diff --git a/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/failure/HostFaultInjectorTest.kt b/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/failure/HostFaultInjectorTest.kt index 29d0b5e74..690bf4724 100644 --- a/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/failure/HostFaultInjectorTest.kt +++ b/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/failure/HostFaultInjectorTest.kt @@ -43,64 +43,72 @@ class HostFaultInjectorTest { * Simple test case to test that nothing happens when the injector is not started. */ @Test - fun testInjectorNotStarted() = runSimulation { - val host = mockk(relaxUnitFun = true) + fun testInjectorNotStarted() = + runSimulation { + val host = mockk(relaxUnitFun = true) - val injector = createSimpleInjector(coroutineContext, timeSource, setOf(host)) + val injector = createSimpleInjector(coroutineContext, timeSource, setOf(host)) - coVerify(exactly = 0) { host.fail() } - coVerify(exactly = 0) { host.recover() } + coVerify(exactly = 0) { host.fail() } + coVerify(exactly = 0) { host.recover() } - injector.close() - } + injector.close() + } /** * Simple test case to test a start stop fault where the machine is stopped and started after some time. */ @Test - fun testInjectorStopsMachine() = runSimulation { - val host = mockk(relaxUnitFun = true) + fun testInjectorStopsMachine() = + runSimulation { + val host = mockk(relaxUnitFun = true) - val injector = createSimpleInjector(coroutineContext, timeSource, setOf(host)) + val injector = createSimpleInjector(coroutineContext, timeSource, setOf(host)) - injector.start() + injector.start() - delay(Duration.ofDays(55).toMillis()) + delay(Duration.ofDays(55).toMillis()) - injector.close() + injector.close() - coVerify(exactly = 1) { host.fail() } - coVerify(exactly = 1) { host.recover() } - } + coVerify(exactly = 1) { host.fail() } + coVerify(exactly = 1) { host.recover() } + } /** * Simple test case to test a start stop fault where multiple machines are stopped. */ @Test - fun testInjectorStopsMultipleMachines() = runSimulation { - val hosts = listOf( - mockk(relaxUnitFun = true), - mockk(relaxUnitFun = true) - ) + fun testInjectorStopsMultipleMachines() = + runSimulation { + val hosts = + listOf( + mockk(relaxUnitFun = true), + mockk(relaxUnitFun = true), + ) - val injector = createSimpleInjector(coroutineContext, timeSource, hosts.toSet()) + val injector = createSimpleInjector(coroutineContext, timeSource, hosts.toSet()) - injector.start() + injector.start() - delay(Duration.ofDays(55).toMillis()) + delay(Duration.ofDays(55).toMillis()) - injector.close() + injector.close() - coVerify(exactly = 1) { hosts[0].fail() } - coVerify(exactly = 1) { hosts[1].fail() } - coVerify(exactly = 1) { hosts[0].recover() } - coVerify(exactly = 1) { hosts[1].recover() } - } + coVerify(exactly = 1) { hosts[0].fail() } + coVerify(exactly = 1) { hosts[1].fail() } + coVerify(exactly = 1) { hosts[0].recover() } + coVerify(exactly = 1) { hosts[1].recover() } + } /** * Create a simple start stop fault injector. */ - private fun createSimpleInjector(context: CoroutineContext, clock: InstantSource, hosts: Set): HostFaultInjector { + private fun createSimpleInjector( + context: CoroutineContext, + clock: InstantSource, + hosts: Set, + ): HostFaultInjector { val rng = Well19937c(0) val iat = LogNormalDistribution(rng, ln(24 * 7.0), 1.03) val selector = StochasticVictimSelector(LogNormalDistribution(rng, 1.88, 1.25)) diff --git a/opendc-compute/opendc-compute-telemetry/build.gradle.kts b/opendc-compute/opendc-compute-telemetry/build.gradle.kts index c403ccb9f..f7af3877a 100644 --- a/opendc-compute/opendc-compute-telemetry/build.gradle.kts +++ b/opendc-compute/opendc-compute-telemetry/build.gradle.kts @@ -22,7 +22,7 @@ description = "OpenDC Compute Service implementation" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/ComputeMetricReader.kt b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/ComputeMetricReader.kt index db8754491..830101ef3 100644 --- a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/ComputeMetricReader.kt +++ b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/ComputeMetricReader.kt @@ -53,7 +53,7 @@ public class ComputeMetricReader( dispatcher: Dispatcher, private val service: ComputeService, private val monitor: ComputeMonitor, - private val exportInterval: Duration = Duration.ofMinutes(5) + private val exportInterval: Duration = Duration.ofMinutes(5), ) : AutoCloseable { private val logger = KotlinLogging.logger {} private val scope = CoroutineScope(dispatcher.asCoroutineDispatcher()) @@ -77,22 +77,23 @@ public class ComputeMetricReader( /** * The background job that is responsible for collecting the metrics every cycle. */ - private val job = scope.launch { - val intervalMs = exportInterval.toMillis() - try { - while (isActive) { - delay(intervalMs) - + private val job = + scope.launch { + val intervalMs = exportInterval.toMillis() + try { + while (isActive) { + delay(intervalMs) + + loggState() + } + } finally { loggState() - } - } finally { - loggState() - if (monitor is AutoCloseable) { - monitor.close() + if (monitor is AutoCloseable) { + monitor.close() + } } } - } private fun loggState() { try { @@ -127,7 +128,6 @@ public class ComputeMetricReader( * An aggregator for service metrics before they are reported. */ private class ServiceTableReaderImpl(private val service: ComputeService) : ServiceTableReader { - override fun copy(): ServiceTableReader { val newServiceTable = ServiceTableReaderImpl(service) newServiceTable.setValues(this) @@ -402,16 +402,17 @@ public class ComputeMetricReader( /** * The static information about this server. */ - override val server = ServerInfo( - server.uid.toString(), - server.name, - "vm", - "x86", - server.image.uid.toString(), - server.image.name, - server.flavor.cpuCount, - server.flavor.memorySize - ) + override val server = + ServerInfo( + server.uid.toString(), + server.name, + "vm", + "x86", + server.image.uid.toString(), + server.image.name, + server.flavor.cpuCount, + server.flavor.memorySize, + ) /** * The [HostInfo] of the host on which the server is hosted. diff --git a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetComputeMonitor.kt b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetComputeMonitor.kt index f60fbe6dd..1c9104975 100644 --- a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetComputeMonitor.kt +++ b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetComputeMonitor.kt @@ -32,20 +32,23 @@ import java.io.File * A [ComputeMonitor] that logs the events to a Parquet file. */ public class ParquetComputeMonitor(base: File, partition: String, bufferSize: Int) : ComputeMonitor, AutoCloseable { - private val serverWriter = ParquetServerDataWriter( - File(base, "$partition/server.parquet").also { it.parentFile.mkdirs() }, - bufferSize - ) + private val serverWriter = + ParquetServerDataWriter( + File(base, "$partition/server.parquet").also { it.parentFile.mkdirs() }, + bufferSize, + ) - private val hostWriter = ParquetHostDataWriter( - File(base, "$partition/host.parquet").also { it.parentFile.mkdirs() }, - bufferSize - ) + private val hostWriter = + ParquetHostDataWriter( + File(base, "$partition/host.parquet").also { it.parentFile.mkdirs() }, + bufferSize, + ) - private val serviceWriter = ParquetServiceDataWriter( - File(base, "$partition/service.parquet").also { it.parentFile.mkdirs() }, - bufferSize - ) + private val serviceWriter = + ParquetServiceDataWriter( + File(base, "$partition/service.parquet").also { it.parentFile.mkdirs() }, + bufferSize, + ) override fun record(reader: ServerTableReader) { serverWriter.write(reader) diff --git a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetDataWriter.kt b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetDataWriter.kt index 34a75d75c..b96ee28b5 100644 --- a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetDataWriter.kt +++ b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetDataWriter.kt @@ -43,7 +43,7 @@ import kotlin.concurrent.thread public abstract class ParquetDataWriter( path: File, private val writeSupport: WriteSupport, - bufferSize: Int = 4096 + bufferSize: Int = 4096, ) : AutoCloseable { /** * The logging instance to use. @@ -63,41 +63,44 @@ public abstract class ParquetDataWriter( /** * The thread that is responsible for writing the Parquet records. */ - private val writerThread = thread(start = false, name = this.toString()) { - val writer = let { - val builder = LocalParquetWriter.builder(path.toPath(), writeSupport) - .withWriterVersion(ParquetProperties.WriterVersion.PARQUET_2_0) - .withCompressionCodec(CompressionCodecName.ZSTD) - .withWriteMode(ParquetFileWriter.Mode.OVERWRITE) - buildWriter(builder) - } + private val writerThread = + thread(start = false, name = this.toString()) { + val writer = + let { + val builder = + LocalParquetWriter.builder(path.toPath(), writeSupport) + .withWriterVersion(ParquetProperties.WriterVersion.PARQUET_2_0) + .withCompressionCodec(CompressionCodecName.ZSTD) + .withWriteMode(ParquetFileWriter.Mode.OVERWRITE) + buildWriter(builder) + } - val queue = queue - val buf = mutableListOf() - var shouldStop = false + val queue = queue + val buf = mutableListOf() + var shouldStop = false - try { - while (!shouldStop) { - try { - writer.write(queue.take()) - } catch (e: InterruptedException) { - shouldStop = true - } + try { + while (!shouldStop) { + try { + writer.write(queue.take()) + } catch (e: InterruptedException) { + shouldStop = true + } - if (queue.drainTo(buf) > 0) { - for (data in buf) { - writer.write(data) + if (queue.drainTo(buf) > 0) { + for (data in buf) { + writer.write(data) + } + buf.clear() } - buf.clear() } + } catch (e: Throwable) { + logger.error(e) { "Failure in Parquet data writer" } + exception = e + } finally { + writer.close() } - } catch (e: Throwable) { - logger.error(e) { "Failure in Parquet data writer" } - exception = e - } finally { - writer.close() } - } /** * Build the [ParquetWriter] used to write the Parquet files. diff --git a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetHostDataWriter.kt b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetHostDataWriter.kt index a6799ef81..b789e44f2 100644 --- a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetHostDataWriter.kt +++ b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetHostDataWriter.kt @@ -40,7 +40,6 @@ import java.io.File */ public class ParquetHostDataWriter(path: File, bufferSize: Int) : ParquetDataWriter(path, HostDataWriteSupport(), bufferSize) { - override fun buildWriter(builder: LocalParquetWriter.Builder): ParquetWriter { return builder .withDictionaryEncoding("host_id", true) @@ -67,7 +66,10 @@ public class ParquetHostDataWriter(path: File, bufferSize: Int) : write(recordConsumer, record) } - private fun write(consumer: RecordConsumer, data: HostTableReader) { + private fun write( + consumer: RecordConsumer, + data: HostTableReader, + ) { consumer.startMessage() consumer.startField("timestamp", 0) @@ -165,76 +167,77 @@ public class ParquetHostDataWriter(path: File, bufferSize: Int) : /** * The schema of the host data. */ - val SCHEMA: MessageType = Types - .buildMessage() - .addFields( - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) + val SCHEMA: MessageType = + Types + .buildMessage() + .addFields( + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) // .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("timestamp"), - Types - .required(PrimitiveType.PrimitiveTypeName.BINARY) - .`as`(LogicalTypeAnnotation.stringType()) - .named("host_id"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("cpu_count"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("mem_capacity"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("guests_terminated"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("guests_running"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("guests_error"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("guests_invalid"), - Types - .required(PrimitiveType.PrimitiveTypeName.DOUBLE) - .named("cpu_limit"), - Types - .required(PrimitiveType.PrimitiveTypeName.DOUBLE) - .named("cpu_usage"), - Types - .required(PrimitiveType.PrimitiveTypeName.DOUBLE) - .named("cpu_demand"), - Types - .required(PrimitiveType.PrimitiveTypeName.DOUBLE) - .named("cpu_utilization"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("cpu_time_active"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("cpu_time_idle"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("cpu_time_steal"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("cpu_time_lost"), - Types - .required(PrimitiveType.PrimitiveTypeName.DOUBLE) - .named("power_draw"), - Types - .required(PrimitiveType.PrimitiveTypeName.DOUBLE) - .named("energy_usage"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("uptime"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("downtime"), - Types - .optional(PrimitiveType.PrimitiveTypeName.INT64) + .named("timestamp"), + Types + .required(PrimitiveType.PrimitiveTypeName.BINARY) + .`as`(LogicalTypeAnnotation.stringType()) + .named("host_id"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("cpu_count"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("mem_capacity"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("guests_terminated"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("guests_running"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("guests_error"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("guests_invalid"), + Types + .required(PrimitiveType.PrimitiveTypeName.DOUBLE) + .named("cpu_limit"), + Types + .required(PrimitiveType.PrimitiveTypeName.DOUBLE) + .named("cpu_usage"), + Types + .required(PrimitiveType.PrimitiveTypeName.DOUBLE) + .named("cpu_demand"), + Types + .required(PrimitiveType.PrimitiveTypeName.DOUBLE) + .named("cpu_utilization"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("cpu_time_active"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("cpu_time_idle"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("cpu_time_steal"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("cpu_time_lost"), + Types + .required(PrimitiveType.PrimitiveTypeName.DOUBLE) + .named("power_draw"), + Types + .required(PrimitiveType.PrimitiveTypeName.DOUBLE) + .named("energy_usage"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("uptime"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("downtime"), + Types + .optional(PrimitiveType.PrimitiveTypeName.INT64) // .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("boot_time") - ) - .named("host") + .named("boot_time"), + ) + .named("host") } } diff --git a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetServerDataWriter.kt b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetServerDataWriter.kt index e8a280162..bcae68056 100644 --- a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetServerDataWriter.kt +++ b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetServerDataWriter.kt @@ -40,7 +40,6 @@ import java.io.File */ public class ParquetServerDataWriter(path: File, bufferSize: Int) : ParquetDataWriter(path, ServerDataWriteSupport(), bufferSize) { - override fun buildWriter(builder: LocalParquetWriter.Builder): ParquetWriter { return builder .withDictionaryEncoding("server_id", true) @@ -68,7 +67,10 @@ public class ParquetServerDataWriter(path: File, bufferSize: Int) : write(recordConsumer, record) } - private fun write(consumer: RecordConsumer, data: ServerTableReader) { + private fun write( + consumer: RecordConsumer, + data: ServerTableReader, + ) { consumer.startMessage() consumer.startField("timestamp", 0) @@ -148,61 +150,61 @@ public class ParquetServerDataWriter(path: File, bufferSize: Int) : /** * The schema of the server data. */ - val SCHEMA: MessageType = Types.buildMessage() - .addFields( - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) + val SCHEMA: MessageType = + Types.buildMessage() + .addFields( + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) // .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("timestamp"), - Types - .required(PrimitiveType.PrimitiveTypeName.BINARY) - .`as`(LogicalTypeAnnotation.stringType()) - .named("server_id"), - Types - .required(PrimitiveType.PrimitiveTypeName.BINARY) - .`as`(LogicalTypeAnnotation.stringType()) - .named("server_name"), - Types - .optional(PrimitiveType.PrimitiveTypeName.BINARY) - .`as`(LogicalTypeAnnotation.stringType()) - .named("host_id"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("mem_capacity"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("cpu_count"), - Types - .required(PrimitiveType.PrimitiveTypeName.DOUBLE) - .named("cpu_limit"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("cpu_time_active"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("cpu_time_idle"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("cpu_time_steal"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("cpu_time_lost"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("uptime"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("downtime"), - Types - .optional(PrimitiveType.PrimitiveTypeName.INT64) + .named("timestamp"), + Types + .required(PrimitiveType.PrimitiveTypeName.BINARY) + .`as`(LogicalTypeAnnotation.stringType()) + .named("server_id"), + Types + .required(PrimitiveType.PrimitiveTypeName.BINARY) + .`as`(LogicalTypeAnnotation.stringType()) + .named("server_name"), + Types + .optional(PrimitiveType.PrimitiveTypeName.BINARY) + .`as`(LogicalTypeAnnotation.stringType()) + .named("host_id"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("mem_capacity"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("cpu_count"), + Types + .required(PrimitiveType.PrimitiveTypeName.DOUBLE) + .named("cpu_limit"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("cpu_time_active"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("cpu_time_idle"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("cpu_time_steal"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("cpu_time_lost"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("uptime"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("downtime"), + Types + .optional(PrimitiveType.PrimitiveTypeName.INT64) // .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("provision_time"), - Types - .optional(PrimitiveType.PrimitiveTypeName.INT64) + .named("provision_time"), + Types + .optional(PrimitiveType.PrimitiveTypeName.INT64) // .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("boot_time") - - ) - .named("server") + .named("boot_time"), + ) + .named("server") } } diff --git a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetServiceDataWriter.kt b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetServiceDataWriter.kt index a487203ed..21247ef3b 100644 --- a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetServiceDataWriter.kt +++ b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/export/parquet/ParquetServiceDataWriter.kt @@ -36,7 +36,6 @@ import java.io.File */ public class ParquetServiceDataWriter(path: File, bufferSize: Int) : ParquetDataWriter(path, ServiceDataWriteSupport(), bufferSize) { - override fun toString(): String = "service-writer" /** @@ -57,7 +56,10 @@ public class ParquetServiceDataWriter(path: File, bufferSize: Int) : write(recordConsumer, record) } - private fun write(consumer: RecordConsumer, data: ServiceTableReader) { + private fun write( + consumer: RecordConsumer, + data: ServiceTableReader, + ) { consumer.startMessage() consumer.startField("timestamp", 0) @@ -97,34 +99,35 @@ public class ParquetServiceDataWriter(path: File, bufferSize: Int) : } private companion object { - private val SCHEMA: MessageType = Types.buildMessage() - .addFields( - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) + private val SCHEMA: MessageType = + Types.buildMessage() + .addFields( + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) // .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("timestamp"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("hosts_up"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("hosts_down"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("servers_pending"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("servers_active"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("attempts_success"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("attempts_failure"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("attempts_error") - ) - .named("service") + .named("timestamp"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("hosts_up"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("hosts_down"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("servers_pending"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("servers_active"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("attempts_success"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("attempts_failure"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("attempts_error"), + ) + .named("service") } } diff --git a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/HostTableReader.kt b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/HostTableReader.kt index bfe2f281b..f9fff3e50 100644 --- a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/HostTableReader.kt +++ b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/HostTableReader.kt @@ -28,7 +28,6 @@ import java.time.Instant * An interface that is used to read a row of a host trace entry. */ public interface HostTableReader { - public fun copy(): HostTableReader public fun setValues(table: HostTableReader) diff --git a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServerInfo.kt b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServerInfo.kt index 96c5bb13b..fb83bf064 100644 --- a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServerInfo.kt +++ b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServerInfo.kt @@ -33,5 +33,5 @@ public data class ServerInfo( val imageId: String, val imageName: String, val cpuCount: Int, - val memCapacity: Long + val memCapacity: Long, ) diff --git a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServerTableReader.kt b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServerTableReader.kt index ec9743d8b..0ebf9d2f3 100644 --- a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServerTableReader.kt +++ b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServerTableReader.kt @@ -28,7 +28,6 @@ import java.time.Instant * An interface that is used to read a row of a server trace entry. */ public interface ServerTableReader { - public fun copy(): ServerTableReader public fun setValues(table: ServerTableReader) diff --git a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServiceData.kt b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServiceData.kt index 0d8b2abd3..ad4b3d49c 100644 --- a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServiceData.kt +++ b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServiceData.kt @@ -36,7 +36,7 @@ public data class ServiceData( val serversActive: Int, val attemptsSuccess: Int, val attemptsFailure: Int, - val attemptsError: Int + val attemptsError: Int, ) /** @@ -52,6 +52,6 @@ public fun ServiceTableReader.toServiceData(): ServiceData { serversActive, attemptsSuccess, attemptsFailure, - attemptsError + attemptsError, ) } diff --git a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServiceTableReader.kt b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServiceTableReader.kt index 501e317c8..10757a276 100644 --- a/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServiceTableReader.kt +++ b/opendc-compute/opendc-compute-telemetry/src/main/kotlin/org/opendc/compute/telemetry/table/ServiceTableReader.kt @@ -28,7 +28,6 @@ import java.time.Instant * An interface that is used to read a row of a service trace entry. */ public interface ServiceTableReader { - public fun copy(): ServiceTableReader public fun setValues(table: ServiceTableReader) diff --git a/opendc-compute/opendc-compute-topology/build.gradle.kts b/opendc-compute/opendc-compute-topology/build.gradle.kts index d4c084c00..0dedf8a9c 100644 --- a/opendc-compute/opendc-compute-topology/build.gradle.kts +++ b/opendc-compute/opendc-compute-topology/build.gradle.kts @@ -22,7 +22,7 @@ description = "OpenDC Compute Topology implementation" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/ClusterSpec.kt b/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/ClusterSpec.kt index e36c4e1ec..7a8a121c0 100644 --- a/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/ClusterSpec.kt +++ b/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/ClusterSpec.kt @@ -42,5 +42,5 @@ public data class ClusterSpec( val memCapacity: Double, val hostCount: Int, val memCapacityPerHost: Double, - val cpuCountPerHost: Int + val cpuCountPerHost: Int, ) diff --git a/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/ClusterSpecReader.kt b/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/ClusterSpecReader.kt index a1e9bc3d3..13314f7d0 100644 --- a/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/ClusterSpecReader.kt +++ b/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/ClusterSpecReader.kt @@ -65,16 +65,17 @@ public class ClusterSpecReader { val result = mutableListOf() for (entry in it) { - val def = ClusterSpec( - entry.id, - entry.name, - entry.cpuCount, - entry.cpuSpeed * 1000, // Convert to MHz - entry.memCapacity * 1000, // Convert to MiB - entry.hostCount, - entry.memCapacityPerHost * 1000, - entry.cpuCountPerHost - ) + val def = + ClusterSpec( + entry.id, + entry.name, + entry.cpuCount, + entry.cpuSpeed * 1000, + entry.memCapacity * 1000, + entry.hostCount, + entry.memCapacityPerHost * 1000, + entry.cpuCountPerHost, + ) result.add(def) } @@ -97,25 +98,26 @@ public class ClusterSpecReader { @JsonProperty("memoryCapacityPerHost") val memCapacityPerHost: Double, @JsonProperty("coreCountPerHost") - val cpuCountPerHost: Int + val cpuCountPerHost: Int, ) public companion object { /** * The [CsvSchema] that is used to parse the trace. */ - private val schema = CsvSchema.builder() - .addColumn("ClusterID", CsvSchema.ColumnType.STRING) - .addColumn("ClusterName", CsvSchema.ColumnType.STRING) - .addColumn("Cores", CsvSchema.ColumnType.NUMBER) - .addColumn("Speed", CsvSchema.ColumnType.NUMBER) - .addColumn("Memory", CsvSchema.ColumnType.NUMBER) - .addColumn("numberOfHosts", CsvSchema.ColumnType.NUMBER) - .addColumn("memoryCapacityPerHost", CsvSchema.ColumnType.NUMBER) - .addColumn("coreCountPerHost", CsvSchema.ColumnType.NUMBER) - .setAllowComments(true) - .setColumnSeparator(';') - .setUseHeader(true) - .build() + private val schema = + CsvSchema.builder() + .addColumn("ClusterID", CsvSchema.ColumnType.STRING) + .addColumn("ClusterName", CsvSchema.ColumnType.STRING) + .addColumn("Cores", CsvSchema.ColumnType.NUMBER) + .addColumn("Speed", CsvSchema.ColumnType.NUMBER) + .addColumn("Memory", CsvSchema.ColumnType.NUMBER) + .addColumn("numberOfHosts", CsvSchema.ColumnType.NUMBER) + .addColumn("memoryCapacityPerHost", CsvSchema.ColumnType.NUMBER) + .addColumn("coreCountPerHost", CsvSchema.ColumnType.NUMBER) + .setAllowComments(true) + .setColumnSeparator(';') + .setUseHeader(true) + .build() } } diff --git a/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/HostSpec.kt b/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/HostSpec.kt index 596121b02..ffaa093eb 100644 --- a/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/HostSpec.kt +++ b/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/HostSpec.kt @@ -44,5 +44,5 @@ public data class HostSpec( val meta: Map, val model: MachineModel, val psuFactory: SimPsuFactory = SimPsuFactories.noop(), - val multiplexerFactory: FlowMultiplexerFactory = FlowMultiplexerFactory.maxMinMultiplexer() + val multiplexerFactory: FlowMultiplexerFactory = FlowMultiplexerFactory.maxMinMultiplexer(), ) diff --git a/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/TopologyFactories.kt b/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/TopologyFactories.kt index 5f0fe5116..aadf52a62 100644 --- a/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/TopologyFactories.kt +++ b/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/TopologyFactories.kt @@ -49,7 +49,7 @@ private val reader = ClusterSpecReader() public fun clusterTopology( file: File, powerModel: CpuPowerModel = CpuPowerModels.linear(350.0, 200.0), - random: RandomGenerator = SplittableRandom(0) + random: RandomGenerator = SplittableRandom(0), ): List { return clusterTopology(reader.read(file), powerModel, random) } @@ -60,7 +60,7 @@ public fun clusterTopology( public fun clusterTopology( input: InputStream, powerModel: CpuPowerModel = CpuPowerModels.linear(350.0, 200.0), - random: RandomGenerator = SplittableRandom(0) + random: RandomGenerator = SplittableRandom(0), ): List { return clusterTopology(reader.read(input), powerModel, random) } @@ -68,23 +68,31 @@ public fun clusterTopology( /** * Construct a topology from the given list of [clusters]. */ -public fun clusterTopology(clusters: List, powerModel: CpuPowerModel, random: RandomGenerator = SplittableRandom(0)): List { +public fun clusterTopology( + clusters: List, + powerModel: CpuPowerModel, + random: RandomGenerator = SplittableRandom(0), +): List { return clusters.flatMap { it.toHostSpecs(random, powerModel) } } /** * Helper method to convert a [ClusterSpec] into a list of [HostSpec]s. */ -private fun ClusterSpec.toHostSpecs(random: RandomGenerator, powerModel: CpuPowerModel): List { +private fun ClusterSpec.toHostSpecs( + random: RandomGenerator, + powerModel: CpuPowerModel, +): List { val cpuSpeed = cpuSpeed val memoryPerHost = memCapacityPerHost.roundToLong() val unknownProcessingNode = ProcessingNode("unknown", "unknown", "unknown", cpuCountPerHost) val unknownMemoryUnit = MemoryUnit("unknown", "unknown", -1.0, memoryPerHost) - val machineModel = MachineModel( - List(cpuCountPerHost) { coreId -> ProcessingUnit(unknownProcessingNode, coreId, cpuSpeed) }, - listOf(unknownMemoryUnit) - ) + val machineModel = + MachineModel( + List(cpuCountPerHost) { coreId -> ProcessingUnit(unknownProcessingNode, coreId, cpuSpeed) }, + listOf(unknownMemoryUnit), + ) return List(hostCount) { HostSpec( @@ -92,7 +100,7 @@ private fun ClusterSpec.toHostSpecs(random: RandomGenerator, powerModel: CpuPowe "node-$name-$it", mapOf("cluster" to id), machineModel, - SimPsuFactories.simple(powerModel) + SimPsuFactories.simple(powerModel), ) } } diff --git a/opendc-compute/opendc-compute-workload/build.gradle.kts b/opendc-compute/opendc-compute-workload/build.gradle.kts index 905f905cd..58b7bc865 100644 --- a/opendc-compute/opendc-compute-workload/build.gradle.kts +++ b/opendc-compute/opendc-compute-workload/build.gradle.kts @@ -22,7 +22,7 @@ description = "OpenDC Compute Service implementation" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeWorkload.kt b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeWorkload.kt index a802afdb7..c9f784ff3 100644 --- a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeWorkload.kt +++ b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeWorkload.kt @@ -31,5 +31,8 @@ public interface ComputeWorkload { /** * Resolve the workload into a list of [VirtualMachine]s to simulate. */ - public fun resolve(loader: ComputeWorkloadLoader, random: RandomGenerator): List + public fun resolve( + loader: ComputeWorkloadLoader, + random: RandomGenerator, + ): List } diff --git a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeWorkloadLoader.kt b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeWorkloadLoader.kt index c5fb3e56a..2202f851f 100644 --- a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeWorkloadLoader.kt +++ b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeWorkloadLoader.kt @@ -29,18 +29,18 @@ import org.opendc.trace.Trace import org.opendc.trace.conv.INTERFERENCE_GROUP_MEMBERS import org.opendc.trace.conv.INTERFERENCE_GROUP_SCORE import org.opendc.trace.conv.INTERFERENCE_GROUP_TARGET -import org.opendc.trace.conv.RESOURCE_CPU_CAPACITY -import org.opendc.trace.conv.RESOURCE_CPU_COUNT -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_MEM_CAPACITY -import org.opendc.trace.conv.RESOURCE_START_TIME -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE -import org.opendc.trace.conv.RESOURCE_STATE_DURATION -import org.opendc.trace.conv.RESOURCE_STATE_TIMESTAMP -import org.opendc.trace.conv.RESOURCE_STOP_TIME import org.opendc.trace.conv.TABLE_INTERFERENCE_GROUPS import org.opendc.trace.conv.TABLE_RESOURCES import org.opendc.trace.conv.TABLE_RESOURCE_STATES +import org.opendc.trace.conv.resourceCpuCapacity +import org.opendc.trace.conv.resourceCpuCount +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceMemCapacity +import org.opendc.trace.conv.resourceStartTime +import org.opendc.trace.conv.resourceStateCpuUsage +import org.opendc.trace.conv.resourceStateDuration +import org.opendc.trace.conv.resourceStateTimestamp +import org.opendc.trace.conv.resourceStopTime import java.io.File import java.lang.ref.SoftReference import java.time.Duration @@ -71,11 +71,11 @@ public class ComputeWorkloadLoader(private val baseDir: File) { private fun parseFragments(trace: Trace): Map { val reader = checkNotNull(trace.getTable(TABLE_RESOURCE_STATES)).newReader() - val idCol = reader.resolve(RESOURCE_ID) - val timestampCol = reader.resolve(RESOURCE_STATE_TIMESTAMP) - val durationCol = reader.resolve(RESOURCE_STATE_DURATION) - val coresCol = reader.resolve(RESOURCE_CPU_COUNT) - val usageCol = reader.resolve(RESOURCE_STATE_CPU_USAGE) + val idCol = reader.resolve(resourceID) + val timestampCol = reader.resolve(resourceStateTimestamp) + val durationCol = reader.resolve(resourceStateDuration) + val coresCol = reader.resolve(resourceCpuCount) + val usageCol = reader.resolve(resourceStateCpuUsage) val fragments = mutableMapOf() @@ -100,15 +100,19 @@ public class ComputeWorkloadLoader(private val baseDir: File) { /** * Read the metadata into a workload. */ - private fun parseMeta(trace: Trace, fragments: Map, interferenceModel: VmInterferenceModel): List { + private fun parseMeta( + trace: Trace, + fragments: Map, + interferenceModel: VmInterferenceModel, + ): List { val reader = checkNotNull(trace.getTable(TABLE_RESOURCES)).newReader() - val idCol = reader.resolve(RESOURCE_ID) - val startTimeCol = reader.resolve(RESOURCE_START_TIME) - val stopTimeCol = reader.resolve(RESOURCE_STOP_TIME) - val cpuCountCol = reader.resolve(RESOURCE_CPU_COUNT) - val cpuCapacityCol = reader.resolve(RESOURCE_CPU_CAPACITY) - val memCol = reader.resolve(RESOURCE_MEM_CAPACITY) + val idCol = reader.resolve(resourceID) + val startTimeCol = reader.resolve(resourceStartTime) + val stopTimeCol = reader.resolve(resourceStopTime) + val cpuCountCol = reader.resolve(resourceCpuCount) + val cpuCapacityCol = reader.resolve(resourceCpuCapacity) + val memCol = reader.resolve(resourceMemCapacity) var counter = 0 val entries = mutableListOf() @@ -141,8 +145,8 @@ public class ComputeWorkloadLoader(private val baseDir: File) { submissionTime, endTime, builder.build(), - interferenceModel.getProfile(id) - ) + interferenceModel.getProfile(id), + ), ) } @@ -189,24 +193,28 @@ public class ComputeWorkloadLoader(private val baseDir: File) { /** * Load the trace with the specified [name] and [format]. */ - public fun get(name: String, format: String): List { - val ref = cache.compute(name) { key, oldVal -> - val inst = oldVal?.get() - if (inst == null) { - val path = baseDir.resolve(key) - - logger.info { "Loading trace $key at $path" } - - val trace = Trace.open(path, format) - val fragments = parseFragments(trace) - val interferenceModel = parseInterferenceModel(trace) - val vms = parseMeta(trace, fragments, interferenceModel) - - SoftReference(vms) - } else { - oldVal + public fun get( + name: String, + format: String, + ): List { + val ref = + cache.compute(name) { key, oldVal -> + val inst = oldVal?.get() + if (inst == null) { + val path = baseDir.resolve(key) + + logger.info { "Loading trace $key at $path" } + + val trace = Trace.open(path, format) + val fragments = parseFragments(trace) + val interferenceModel = parseInterferenceModel(trace) + val vms = parseMeta(trace, fragments, interferenceModel) + + SoftReference(vms) + } else { + oldVal + } } - } return checkNotNull(ref?.get()) { "Memory pressure" } } @@ -245,7 +253,12 @@ public class ComputeWorkloadLoader(private val baseDir: File) { * @param usage CPU usage of this fragment. * @param cores Number of cores used. */ - fun add(deadline: Instant, duration: Duration, usage: Double, cores: Int) { + fun add( + deadline: Instant, + duration: Duration, + usage: Double, + cores: Int, + ) { val startTimeMs = (deadline - duration).toEpochMilli() totalLoad += (usage * duration.toMillis()) / 1000.0 // avg MHz * duration = MFLOPs diff --git a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeWorkloads.kt b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeWorkloads.kt index 61a6e3a01..8723f88ba 100644 --- a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeWorkloads.kt +++ b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeWorkloads.kt @@ -32,7 +32,10 @@ import org.opendc.compute.workload.internal.TraceComputeWorkload /** * Construct a workload from a trace. */ -public fun trace(name: String, format: String = "opendc-vm"): ComputeWorkload = TraceComputeWorkload(name, format) +public fun trace( + name: String, + format: String = "opendc-vm", +): ComputeWorkload = TraceComputeWorkload(name, format) /** * Construct a composite workload with the specified fractions. diff --git a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/VirtualMachine.kt b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/VirtualMachine.kt index 622b3c554..deb50f5c8 100644 --- a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/VirtualMachine.kt +++ b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/VirtualMachine.kt @@ -50,5 +50,5 @@ public data class VirtualMachine( val startTime: Instant, val stopTime: Instant, val trace: SimTrace, - val interferenceProfile: VmInterferenceProfile? + val interferenceProfile: VmInterferenceProfile?, ) diff --git a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/CompositeComputeWorkload.kt b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/CompositeComputeWorkload.kt index 1ac5f4ad1..aba493b62 100644 --- a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/CompositeComputeWorkload.kt +++ b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/CompositeComputeWorkload.kt @@ -37,7 +37,10 @@ internal class CompositeComputeWorkload(val sources: Map { + override fun resolve( + loader: ComputeWorkloadLoader, + random: RandomGenerator, + ): List { val traces = sources.map { (source, fraction) -> fraction to source.resolve(loader, random) } val totalLoad = traces.sumOf { (_, vms) -> vms.sumOf { it.totalLoad } } diff --git a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/HpcSampledComputeWorkload.kt b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/HpcSampledComputeWorkload.kt index fdb599c1b..4207b2bef 100644 --- a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/HpcSampledComputeWorkload.kt +++ b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/HpcSampledComputeWorkload.kt @@ -35,7 +35,11 @@ import java.util.random.RandomGenerator * @param fraction The fraction of load/virtual machines to sample * @param sampleLoad A flag to indicate that the sampling should be based on the total load or on the number of VMs. */ -internal class HpcSampledComputeWorkload(val source: ComputeWorkload, val fraction: Double, val sampleLoad: Boolean = false) : ComputeWorkload { +internal class HpcSampledComputeWorkload( + val source: ComputeWorkload, + val fraction: Double, + val sampleLoad: Boolean = false, +) : ComputeWorkload { /** * The logging instance of this class. */ @@ -46,29 +50,35 @@ internal class HpcSampledComputeWorkload(val source: ComputeWorkload, val fracti */ private val pattern = Regex("^(ComputeNode|cn).*") - override fun resolve(loader: ComputeWorkloadLoader, random: RandomGenerator): List { + override fun resolve( + loader: ComputeWorkloadLoader, + random: RandomGenerator, + ): List { val vms = source.resolve(loader, random) - val (hpc, nonHpc) = vms.partition { entry -> - val name = entry.name - name.matches(pattern) - } - - val hpcSequence = generateSequence(0) { it + 1 } - .map { index -> - val res = mutableListOf() - hpc.mapTo(res) { sample(it, index) } - res + val (hpc, nonHpc) = + vms.partition { entry -> + val name = entry.name + name.matches(pattern) } - .flatten() - val nonHpcSequence = generateSequence(0) { it + 1 } - .map { index -> - val res = mutableListOf() - nonHpc.mapTo(res) { sample(it, index) } - res - } - .flatten() + val hpcSequence = + generateSequence(0) { it + 1 } + .map { index -> + val res = mutableListOf() + hpc.mapTo(res) { sample(it, index) } + res + } + .flatten() + + val nonHpcSequence = + generateSequence(0) { it + 1 } + .map { index -> + val res = mutableListOf() + nonHpc.mapTo(res) { sample(it, index) } + res + } + .flatten() logger.debug { "Found ${hpc.size} HPC workloads and ${nonHpc.size} non-HPC workloads" } @@ -135,7 +145,10 @@ internal class HpcSampledComputeWorkload(val source: ComputeWorkload, val fracti /** * Sample a random trace entry. */ - private fun sample(entry: VirtualMachine, i: Int): VirtualMachine { + private fun sample( + entry: VirtualMachine, + i: Int, + ): VirtualMachine { val uid = UUID.nameUUIDFromBytes("${entry.uid}-$i".toByteArray()) return entry.copy(uid = uid) } diff --git a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/LoadSampledComputeWorkload.kt b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/LoadSampledComputeWorkload.kt index 6014f37a2..c89507fa9 100644 --- a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/LoadSampledComputeWorkload.kt +++ b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/LoadSampledComputeWorkload.kt @@ -37,7 +37,10 @@ internal class LoadSampledComputeWorkload(val source: ComputeWorkload, val fract */ private val logger = KotlinLogging.logger {} - override fun resolve(loader: ComputeWorkloadLoader, random: RandomGenerator): List { + override fun resolve( + loader: ComputeWorkloadLoader, + random: RandomGenerator, + ): List { val vms = source.resolve(loader, random) val res = mutableListOf() diff --git a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/TraceComputeWorkload.kt b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/TraceComputeWorkload.kt index ff88fa3eb..39255c593 100644 --- a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/TraceComputeWorkload.kt +++ b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/internal/TraceComputeWorkload.kt @@ -31,7 +31,10 @@ import java.util.random.RandomGenerator * A [ComputeWorkload] from a trace. */ internal class TraceComputeWorkload(val name: String, val format: String) : ComputeWorkload { - override fun resolve(loader: ComputeWorkloadLoader, random: RandomGenerator): List { + override fun resolve( + loader: ComputeWorkloadLoader, + random: RandomGenerator, + ): List { return loader.get(name, format) } } diff --git a/opendc-experiments/opendc-experiments-base/build.gradle.kts b/opendc-experiments/opendc-experiments-base/build.gradle.kts index b30e468a1..8aa82b67f 100644 --- a/opendc-experiments/opendc-experiments-base/build.gradle.kts +++ b/opendc-experiments/opendc-experiments-base/build.gradle.kts @@ -22,7 +22,7 @@ description = "Support library for simulating VM-based workloads with OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` `testing-conventions` diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/Scenario.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/Scenario.kt index 66fc76e41..cf0f53207 100644 --- a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/Scenario.kt +++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/Scenario.kt @@ -36,5 +36,5 @@ public data class Scenario( val workload: Workload, val operationalPhenomena: OperationalPhenomena, val allocationPolicy: String, - val partitions: Map = emptyMap() + val partitions: Map = emptyMap(), ) diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/TraceHelpers.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/TraceHelpers.kt index 2afbd8a57..ddfa35cc7 100644 --- a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/TraceHelpers.kt +++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/TraceHelpers.kt @@ -49,19 +49,22 @@ public class RunningServerWatcher : ServerWatcher { // TODO: make this changeable private val unlockStates: List = listOf(ServerState.TERMINATED, ServerState.ERROR, ServerState.DELETED) - private val _mutex: Mutex = Mutex() + private val mutex: Mutex = Mutex() public suspend fun lock() { - _mutex.lock() + mutex.lock() } public suspend fun wait() { this.lock() } - override fun onStateChanged(server: Server, newState: ServerState) { + override fun onStateChanged( + server: Server, + newState: ServerState, + ) { if (unlockStates.contains(newState)) { - _mutex.unlock() + mutex.unlock() } } } @@ -82,7 +85,7 @@ public suspend fun ComputeService.replay( seed: Long, submitImmediately: Boolean = false, failureModel: FailureModel? = null, - interference: Boolean = false + interference: Boolean = false, ) { val injector = failureModel?.createInjector(coroutineContext, clock, this, Random(seed)) val client = newClient() @@ -123,17 +126,18 @@ public suspend fun ComputeService.replay( } launch { - val server = client.newServer( - entry.name, - image, - client.newFlavor( + val server = + client.newServer( entry.name, - entry.cpuCount, - entry.memCapacity, - meta = if (entry.cpuCapacity > 0.0) mapOf("cpu-capacity" to entry.cpuCapacity) else emptyMap() - ), - meta = meta - ) + image, + client.newFlavor( + entry.name, + entry.cpuCount, + entry.memCapacity, + meta = if (entry.cpuCapacity > 0.0) mapOf("cpu-capacity" to entry.cpuCapacity) else emptyMap(), + ), + meta = meta, + ) val serverWatcher = RunningServerWatcher() serverWatcher.lock() diff --git a/opendc-experiments/opendc-experiments-capelin/build.gradle.kts b/opendc-experiments/opendc-experiments-capelin/build.gradle.kts index 642303871..af37e352f 100644 --- a/opendc-experiments/opendc-experiments-capelin/build.gradle.kts +++ b/opendc-experiments/opendc-experiments-capelin/build.gradle.kts @@ -22,7 +22,7 @@ description = "Experiments for the Capelin work" -/* Build configuration */ +// Build configuration plugins { `kotlin-conventions` `testing-conventions` @@ -56,10 +56,10 @@ val createCapelinApp by tasks.creating(CreateStartScripts::class) { applicationName = "capelin" mainClass.set("org.opendc.experiments.capelin.CapelinCli") classpath = tasks.jar.get().outputs.files + configurations["runtimeClasspath"] - outputDir = project.buildDir.resolve("scripts") + outputDir = project.layout.buildDirectory.get().asFile.resolve("scripts") } -/* Create custom Capelin distribution */ +// Create custom Capelin distribution distributions { main { distributionBaseName.set("capelin") diff --git a/opendc-experiments/opendc-experiments-capelin/src/jmh/kotlin/org/opendc/experiments/capelin/CapelinBenchmarks.kt b/opendc-experiments/opendc-experiments-capelin/src/jmh/kotlin/org/opendc/experiments/capelin/CapelinBenchmarks.kt index 06f8265c0..f0084ec5c 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/jmh/kotlin/org/opendc/experiments/capelin/CapelinBenchmarks.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/jmh/kotlin/org/opendc/experiments/capelin/CapelinBenchmarks.kt @@ -72,22 +72,24 @@ class CapelinBenchmarks { } @Benchmark - fun benchmarkCapelin() = runSimulation { - val serviceDomain = "compute.opendc.org" + fun benchmarkCapelin() = + runSimulation { + val serviceDomain = "compute.opendc.org" - Provisioner(dispatcher, seed = 0).use { provisioner -> - val computeScheduler = FilterScheduler( - filters = listOf(ComputeFilter(), VCpuFilter(16.0), RamFilter(1.0)), - weighers = listOf(CoreRamWeigher(multiplier = 1.0)) - ) + Provisioner(dispatcher, seed = 0).use { provisioner -> + val computeScheduler = + FilterScheduler( + filters = listOf(ComputeFilter(), VCpuFilter(16.0), RamFilter(1.0)), + weighers = listOf(CoreRamWeigher(multiplier = 1.0)), + ) - provisioner.runSteps( - setupComputeService(serviceDomain, { computeScheduler }), - setupHosts(serviceDomain, topology, optimize = isOptimized) - ) + provisioner.runSteps( + setupComputeService(serviceDomain, { computeScheduler }), + setupHosts(serviceDomain, topology, optimize = isOptimized), + ) - val service = provisioner.registry.resolve(serviceDomain, ComputeService::class.java)!! - service.replay(timeSource, vms, 0L, interference = true) + val service = provisioner.registry.resolve(serviceDomain, ComputeService::class.java)!! + service.replay(timeSource, vms, 0L, interference = true) + } } - } } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinCli.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinCli.kt index ac0bd5060..5bec8c6de 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinCli.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinCli.kt @@ -114,7 +114,7 @@ internal class CapelinCommand : CliktCommand(name = "capelin") { "hor-ver" to { HorVerPortfolio() }, "more-hpc" to { MoreHpcPortfolio() }, "more-velocity" to { MoreVelocityPortfolio() }, - "op-phen" to { OperationalPhenomenaPortfolio() } + "op-phen" to { OperationalPhenomenaPortfolio() }, ) /** @@ -140,12 +140,17 @@ internal class CapelinCommand : CliktCommand(name = "capelin") { /** * Run a single scenario. */ - private fun runScenario(runner: CapelinRunner, pool: ForkJoinPool, scenario: Scenario) { - val pb = ProgressBarBuilder() - .setInitialMax(repeats.toLong()) - .setStyle(ProgressBarStyle.ASCII) - .setTaskName("Simulating...") - .build() + private fun runScenario( + runner: CapelinRunner, + pool: ForkJoinPool, + scenario: Scenario, + ) { + val pb = + ProgressBarBuilder() + .setInitialMax(repeats.toLong()) + .setStyle(ProgressBarStyle.ASCII) + .setTaskName("Simulating...") + .build() pool.submit { LongStream.range(0, repeats.toLong()) diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinRunner.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinRunner.kt index b97b7f94b..0de72afa7 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinRunner.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinRunner.kt @@ -50,7 +50,7 @@ import kotlin.math.roundToLong public class CapelinRunner( private val envPath: File, tracePath: File, - private val outputPath: File? + private val outputPath: File?, ) { /** * The [ComputeWorkloadLoader] to use for loading the traces. @@ -60,14 +60,17 @@ public class CapelinRunner( /** * Run a single [scenario] with the specified seed. */ - fun runScenario(scenario: Scenario, seed: Long) = runSimulation { + fun runScenario( + scenario: Scenario, + seed: Long, + ) = runSimulation { val serviceDomain = "compute.opendc.org" val topology = clusterTopology(File(envPath, "${scenario.topology.name}.txt")) Provisioner(dispatcher, seed).use { provisioner -> provisioner.runSteps( setupComputeService(serviceDomain, { createComputeScheduler(scenario.allocationPolicy, Random(it.seeder.nextLong())) }), - setupHosts(serviceDomain, topology, optimize = true) + setupHosts(serviceDomain, topology, optimize = true), ) if (outputPath != null) { @@ -80,9 +83,9 @@ public class CapelinRunner( ParquetComputeMonitor( outputPath, partition, - bufferSize = 4096 - ) - ) + bufferSize = 4096, + ), + ), ) } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/CompositeWorkloadPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/CompositeWorkloadPortfolio.kt index 40e3801f6..140f0480a 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/CompositeWorkloadPortfolio.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/CompositeWorkloadPortfolio.kt @@ -34,46 +34,49 @@ import org.opendc.experiments.base.portfolio.model.Workload * A [Portfolio] that explores the effect of a composite workload. */ public class CompositeWorkloadPortfolio : Portfolio { - private val topologies = listOf( - Topology("base"), - Topology("exp-vol-hor-hom"), - Topology("exp-vol-ver-hom"), - Topology("exp-vel-ver-hom") - ) - private val workloads = listOf( - Workload( - "all-azure", - composite(trace("solvinity-short") to 0.0, trace("azure") to 1.0) - ), - Workload( - "solvinity-25-azure-75", - composite(trace("solvinity-short") to 0.25, trace("azure") to 0.75) - ), - Workload( - "solvinity-50-azure-50", - composite(trace("solvinity-short") to 0.5, trace("azure") to 0.5) - ), - Workload( - "solvinity-75-azure-25", - composite(trace("solvinity-short") to 0.75, trace("azure") to 0.25) - ), - Workload( - "all-solvinity", - composite(trace("solvinity-short") to 1.0, trace("azure") to 0.0) + private val topologies = + listOf( + Topology("base"), + Topology("exp-vol-hor-hom"), + Topology("exp-vol-ver-hom"), + Topology("exp-vel-ver-hom"), + ) + private val workloads = + listOf( + Workload( + "all-azure", + composite(trace("solvinity-short") to 0.0, trace("azure") to 1.0), + ), + Workload( + "solvinity-25-azure-75", + composite(trace("solvinity-short") to 0.25, trace("azure") to 0.75), + ), + Workload( + "solvinity-50-azure-50", + composite(trace("solvinity-short") to 0.5, trace("azure") to 0.5), + ), + Workload( + "solvinity-75-azure-25", + composite(trace("solvinity-short") to 0.75, trace("azure") to 0.25), + ), + Workload( + "all-solvinity", + composite(trace("solvinity-short") to 1.0, trace("azure") to 0.0), + ), ) - ) private val operationalPhenomena = OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = false) private val allocationPolicy = "active-servers" - override val scenarios: Iterable = topologies.flatMap { topology -> - workloads.map { workload -> - Scenario( - topology, - workload, - operationalPhenomena, - allocationPolicy, - mapOf("topology" to topology.name, "workload" to workload.name) - ) + override val scenarios: Iterable = + topologies.flatMap { topology -> + workloads.map { workload -> + Scenario( + topology, + workload, + operationalPhenomena, + allocationPolicy, + mapOf("topology" to topology.name, "workload" to workload.name), + ) + } } - } } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/HorVerPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/HorVerPortfolio.kt index 1d68836ca..da884f35e 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/HorVerPortfolio.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/HorVerPortfolio.kt @@ -34,36 +34,39 @@ import org.opendc.experiments.base.portfolio.model.Workload * A [Portfolio] that explores the difference between horizontal and vertical scaling. */ public class HorVerPortfolio : Portfolio { - private val topologies = listOf( - Topology("base"), - Topology("rep-vol-hor-hom"), - Topology("rep-vol-hor-het"), - Topology("rep-vol-ver-hom"), - Topology("rep-vol-ver-het"), - Topology("exp-vol-hor-hom"), - Topology("exp-vol-hor-het"), - Topology("exp-vol-ver-hom"), - Topology("exp-vol-ver-het") - ) + private val topologies = + listOf( + Topology("base"), + Topology("rep-vol-hor-hom"), + Topology("rep-vol-hor-het"), + Topology("rep-vol-ver-hom"), + Topology("rep-vol-ver-het"), + Topology("exp-vol-hor-hom"), + Topology("exp-vol-hor-het"), + Topology("exp-vol-ver-hom"), + Topology("exp-vol-ver-het"), + ) - private val workloads = listOf( - Workload("solvinity-10%", trace("solvinity").sampleByLoad(0.1)), - Workload("solvinity-25%", trace("solvinity").sampleByLoad(0.25)), - Workload("solvinity-50%", trace("solvinity").sampleByLoad(0.5)), - Workload("solvinity-100%", trace("solvinity").sampleByLoad(1.0)) - ) + private val workloads = + listOf( + Workload("solvinity-10%", trace("solvinity").sampleByLoad(0.1)), + Workload("solvinity-25%", trace("solvinity").sampleByLoad(0.25)), + Workload("solvinity-50%", trace("solvinity").sampleByLoad(0.5)), + Workload("solvinity-100%", trace("solvinity").sampleByLoad(1.0)), + ) private val operationalPhenomena = OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true) private val allocationPolicy = "active-servers" - override val scenarios: Iterable = topologies.flatMap { topology -> - workloads.map { workload -> - Scenario( - topology, - workload, - operationalPhenomena, - allocationPolicy, - mapOf("topology" to topology.name, "workload" to workload.name) - ) + override val scenarios: Iterable = + topologies.flatMap { topology -> + workloads.map { workload -> + Scenario( + topology, + workload, + operationalPhenomena, + allocationPolicy, + mapOf("topology" to topology.name, "workload" to workload.name), + ) + } } - } } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreHpcPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreHpcPortfolio.kt index 1c222ae8c..e060ff14c 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreHpcPortfolio.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreHpcPortfolio.kt @@ -35,34 +35,37 @@ import org.opendc.experiments.base.portfolio.model.Workload * A [Portfolio] to explore the effect of HPC workloads. */ public class MoreHpcPortfolio : Portfolio { - private val topologies = listOf( - Topology("base"), - Topology("exp-vol-hor-hom"), - Topology("exp-vol-ver-hom"), - Topology("exp-vel-ver-hom") - ) - private val workloads = listOf( - Workload("hpc-0%", trace("solvinity").sampleByHpc(0.0)), - Workload("hpc-25%", trace("solvinity").sampleByHpc(0.25)), - Workload("hpc-50%", trace("solvinity").sampleByHpc(0.5)), - Workload("hpc-100%", trace("solvinity").sampleByHpc(1.0)), - Workload("hpc-load-25%", trace("solvinity").sampleByHpcLoad(0.25)), - Workload("hpc-load-50%", trace("solvinity").sampleByHpcLoad(0.5)), - Workload("hpc-load-100%", trace("solvinity").sampleByHpcLoad(1.0)) - ) + private val topologies = + listOf( + Topology("base"), + Topology("exp-vol-hor-hom"), + Topology("exp-vol-ver-hom"), + Topology("exp-vel-ver-hom"), + ) + private val workloads = + listOf( + Workload("hpc-0%", trace("solvinity").sampleByHpc(0.0)), + Workload("hpc-25%", trace("solvinity").sampleByHpc(0.25)), + Workload("hpc-50%", trace("solvinity").sampleByHpc(0.5)), + Workload("hpc-100%", trace("solvinity").sampleByHpc(1.0)), + Workload("hpc-load-25%", trace("solvinity").sampleByHpcLoad(0.25)), + Workload("hpc-load-50%", trace("solvinity").sampleByHpcLoad(0.5)), + Workload("hpc-load-100%", trace("solvinity").sampleByHpcLoad(1.0)), + ) private val operationalPhenomena = OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true) private val allocationPolicy: String = "active-servers" - override val scenarios: Iterable = topologies.flatMap { topology -> - workloads.map { workload -> - Scenario( - topology, - workload, - operationalPhenomena, - allocationPolicy, - mapOf("topology" to topology.name, "workload" to workload.name) - ) + override val scenarios: Iterable = + topologies.flatMap { topology -> + workloads.map { workload -> + Scenario( + topology, + workload, + operationalPhenomena, + allocationPolicy, + mapOf("topology" to topology.name, "workload" to workload.name), + ) + } } - } } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreVelocityPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreVelocityPortfolio.kt index b2a751a39..0d6e190cb 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreVelocityPortfolio.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreVelocityPortfolio.kt @@ -34,33 +34,36 @@ import org.opendc.experiments.base.portfolio.model.Workload * A [Portfolio] that explores the effect of adding more velocity to a cluster (e.g., faster machines). */ public class MoreVelocityPortfolio : Portfolio { - private val topologies = listOf( - Topology("base"), - Topology("rep-vel-ver-hom"), - Topology("rep-vel-ver-het"), - Topology("exp-vel-ver-hom"), - Topology("exp-vel-ver-het") - ) + private val topologies = + listOf( + Topology("base"), + Topology("rep-vel-ver-hom"), + Topology("rep-vel-ver-het"), + Topology("exp-vel-ver-hom"), + Topology("exp-vel-ver-het"), + ) - private val workloads = listOf( - Workload("solvinity-10%", trace("solvinity").sampleByLoad(0.1)), - Workload("solvinity-25%", trace("solvinity").sampleByLoad(0.25)), - Workload("solvinity-50%", trace("solvinity").sampleByLoad(0.5)), - Workload("solvinity-100%", trace("solvinity").sampleByLoad(1.0)) - ) + private val workloads = + listOf( + Workload("solvinity-10%", trace("solvinity").sampleByLoad(0.1)), + Workload("solvinity-25%", trace("solvinity").sampleByLoad(0.25)), + Workload("solvinity-50%", trace("solvinity").sampleByLoad(0.5)), + Workload("solvinity-100%", trace("solvinity").sampleByLoad(1.0)), + ) private val operationalPhenomena = OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true) private val allocationPolicy = "active-servers" - override val scenarios: Iterable = topologies.flatMap { topology -> - workloads.map { workload -> - Scenario( - topology, - workload, - operationalPhenomena, - allocationPolicy, - mapOf("topology" to topology.name, "workload" to workload.name) - ) + override val scenarios: Iterable = + topologies.flatMap { topology -> + workloads.map { workload -> + Scenario( + topology, + workload, + operationalPhenomena, + allocationPolicy, + mapOf("topology" to topology.name, "workload" to workload.name), + ) + } } - } } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/OperationalPhenomenaPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/OperationalPhenomenaPortfolio.kt index b8c60b67d..17c8bb48b 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/OperationalPhenomenaPortfolio.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/OperationalPhenomenaPortfolio.kt @@ -35,29 +35,32 @@ import org.opendc.experiments.base.portfolio.model.Workload */ public class OperationalPhenomenaPortfolio : Portfolio { private val topology = Topology("base") - private val workloads = listOf( - Workload("solvinity-10%", trace("solvinity").sampleByLoad(0.1)), - Workload("solvinity-25%", trace("solvinity").sampleByLoad(0.25)), - Workload("solvinity-50%", trace("solvinity").sampleByLoad(0.5)), - Workload("solvinity-100%", trace("solvinity").sampleByLoad(1.0)) - ) + private val workloads = + listOf( + Workload("solvinity-10%", trace("solvinity").sampleByLoad(0.1)), + Workload("solvinity-25%", trace("solvinity").sampleByLoad(0.25)), + Workload("solvinity-50%", trace("solvinity").sampleByLoad(0.5)), + Workload("solvinity-100%", trace("solvinity").sampleByLoad(1.0)), + ) - private val phenomenas = listOf( - OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true), - OperationalPhenomena(failureFrequency = 0.0, hasInterference = true), - OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = false), - OperationalPhenomena(failureFrequency = 0.0, hasInterference = false) - ) + private val phenomenas = + listOf( + OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true), + OperationalPhenomena(failureFrequency = 0.0, hasInterference = true), + OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = false), + OperationalPhenomena(failureFrequency = 0.0, hasInterference = false), + ) - private val allocationPolicies = listOf( - "mem", - "mem-inv", - "core-mem", - "core-mem-inv", - "active-servers", - "active-servers-inv", - "random" - ) + private val allocationPolicies = + listOf( + "mem", + "mem-inv", + "core-mem", + "core-mem-inv", + "active-servers", + "active-servers-inv", + "random", + ) override val scenarios: Iterable = workloads.flatMap { workload -> @@ -68,7 +71,7 @@ public class OperationalPhenomenaPortfolio : Portfolio { workload, operationalPhenomena, allocationPolicy, - mapOf("workload" to workload.name, "scheduler" to allocationPolicy, "phenomena" to index.toString()) + mapOf("workload" to workload.name, "scheduler" to allocationPolicy, "phenomena" to index.toString()), ) } } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/TestPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/TestPortfolio.kt index f73148024..729fb017c 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/TestPortfolio.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/TestPortfolio.kt @@ -34,12 +34,13 @@ import org.opendc.experiments.base.portfolio.model.Workload * A [Portfolio] to perform a simple test run. */ public class TestPortfolio : Portfolio { - override val scenarios: Iterable = listOf( - Scenario( - Topology("single"), - Workload("bitbrains-small", trace("trace").sampleByLoad(1.0)), - OperationalPhenomena(failureFrequency = 0.0, hasInterference = true), - "active-servers" + override val scenarios: Iterable = + listOf( + Scenario( + Topology("single"), + Workload("bitbrains-small", trace("trace").sampleByLoad(1.0)), + OperationalPhenomena(failureFrequency = 0.0, hasInterference = true), + "active-servers", + ), ) - ) } diff --git a/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinIntegrationTest.kt b/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinIntegrationTest.kt index 26cdb36e1..6b5382401 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinIntegrationTest.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinIntegrationTest.kt @@ -77,10 +77,11 @@ class CapelinIntegrationTest { @BeforeEach fun setUp() { monitor = TestComputeMonitor() - computeScheduler = FilterScheduler( - filters = listOf(ComputeFilter(), VCpuFilter(16.0), RamFilter(1.0)), - weighers = listOf(CoreRamWeigher(multiplier = 1.0)) - ) + computeScheduler = + FilterScheduler( + filters = listOf(ComputeFilter(), VCpuFilter(16.0), RamFilter(1.0)), + weighers = listOf(CoreRamWeigher(multiplier = 1.0)), + ) workloadLoader = ComputeWorkloadLoader(File("src/test/resources/trace")) } @@ -88,159 +89,166 @@ class CapelinIntegrationTest { * Test a large simulation setup. */ @Test - fun testLarge() = runSimulation { - val seed = 0L - val workload = createTestWorkload(1.0, seed) - val topology = createTopology() - val monitor = monitor + fun testLarge() = + runSimulation { + val seed = 0L + val workload = createTestWorkload(1.0, seed) + val topology = createTopology() + val monitor = monitor - Provisioner(dispatcher, seed).use { provisioner -> - provisioner.runSteps( - setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), - registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), - setupHosts(serviceDomain = "compute.opendc.org", topology) - ) + Provisioner(dispatcher, seed).use { provisioner -> + provisioner.runSteps( + setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), + registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), + setupHosts(serviceDomain = "compute.opendc.org", topology), + ) - val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! - service.replay(timeSource, workload, seed) - } + val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! + service.replay(timeSource, workload, seed) + } - println( - "Scheduler " + - "Success=${monitor.attemptsSuccess} " + - "Failure=${monitor.attemptsFailure} " + - "Error=${monitor.attemptsError} " + - "Pending=${monitor.serversPending} " + - "Active=${monitor.serversActive}" - ) + println( + "Scheduler " + + "Success=${monitor.attemptsSuccess} " + + "Failure=${monitor.attemptsFailure} " + + "Error=${monitor.attemptsError} " + + "Pending=${monitor.serversPending} " + + "Active=${monitor.serversActive}", + ) - // Note that these values have been verified beforehand - assertAll( - { assertEquals(50, monitor.attemptsSuccess, "The scheduler should schedule 50 VMs") }, - { assertEquals(0, monitor.serversActive, "All VMs should finish after a run") }, - { assertEquals(0, monitor.attemptsFailure, "No VM should be unscheduled") }, - { assertEquals(0, monitor.serversPending, "No VM should not be in the queue") }, - { assertEquals(223379991650, monitor.idleTime) { "Incorrect idle time" } }, - { assertEquals(66977091124, monitor.activeTime) { "Incorrect active time" } }, - { assertEquals(3160267873, monitor.stealTime) { "Incorrect steal time" } }, - { assertEquals(0, monitor.lostTime) { "Incorrect lost time" } }, - { assertEquals(5.8407E9, monitor.energyUsage, 1E4) { "Incorrect power draw" } } - ) - } + // Note that these values have been verified beforehand + assertAll( + { assertEquals(50, monitor.attemptsSuccess, "The scheduler should schedule 50 VMs") }, + { assertEquals(0, monitor.serversActive, "All VMs should finish after a run") }, + { assertEquals(0, monitor.attemptsFailure, "No VM should be unscheduled") }, + { assertEquals(0, monitor.serversPending, "No VM should not be in the queue") }, + { assertEquals(223379991650, monitor.idleTime) { "Incorrect idle time" } }, + { assertEquals(66977091124, monitor.activeTime) { "Incorrect active time" } }, + { assertEquals(3160267873, monitor.stealTime) { "Incorrect steal time" } }, + { assertEquals(0, monitor.lostTime) { "Incorrect lost time" } }, + { assertEquals(5.8407E9, monitor.energyUsage, 1E4) { "Incorrect power draw" } }, + ) + } /** * Test a small simulation setup. */ @Test - fun testSmall() = runSimulation { - val seed = 1L - val workload = createTestWorkload(0.25, seed) - val topology = createTopology("single") - val monitor = monitor + fun testSmall() = + runSimulation { + val seed = 1L + val workload = createTestWorkload(0.25, seed) + val topology = createTopology("single") + val monitor = monitor - Provisioner(dispatcher, seed).use { provisioner -> - provisioner.runSteps( - setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), - registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), - setupHosts(serviceDomain = "compute.opendc.org", topology) - ) + Provisioner(dispatcher, seed).use { provisioner -> + provisioner.runSteps( + setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), + registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), + setupHosts(serviceDomain = "compute.opendc.org", topology), + ) - val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! - service.replay(timeSource, workload, seed) - } + val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! + service.replay(timeSource, workload, seed) + } - println( - "Scheduler " + - "Success=${monitor.attemptsSuccess} " + - "Failure=${monitor.attemptsFailure} " + - "Error=${monitor.attemptsError} " + - "Pending=${monitor.serversPending} " + - "Active=${monitor.serversActive}" - ) + println( + "Scheduler " + + "Success=${monitor.attemptsSuccess} " + + "Failure=${monitor.attemptsFailure} " + + "Error=${monitor.attemptsError} " + + "Pending=${monitor.serversPending} " + + "Active=${monitor.serversActive}", + ) - // Note that these values have been verified beforehand - assertAll( - { assertEquals(10996730092, monitor.idleTime) { "Idle time incorrect" } }, - { assertEquals(9741285381, monitor.activeTime) { "Active time incorrect" } }, - { assertEquals(152, monitor.stealTime) { "Steal time incorrect" } }, - { assertEquals(0, monitor.lostTime) { "Lost time incorrect" } }, - { assertEquals(7.0109E8, monitor.energyUsage, 1E4) { "Incorrect power draw" } } - ) - } + // Note that these values have been verified beforehand + assertAll( + { assertEquals(10996730092, monitor.idleTime) { "Idle time incorrect" } }, + { assertEquals(9741285381, monitor.activeTime) { "Active time incorrect" } }, + { assertEquals(152, monitor.stealTime) { "Steal time incorrect" } }, + { assertEquals(0, monitor.lostTime) { "Lost time incorrect" } }, + { assertEquals(7.0109E8, monitor.energyUsage, 1E4) { "Incorrect power draw" } }, + ) + } /** * Test a small simulation setup with interference. */ @Test - fun testInterference() = runSimulation { - val seed = 0L - val workload = createTestWorkload(1.0, seed) - val topology = createTopology("single") + fun testInterference() = + runSimulation { + val seed = 0L + val workload = createTestWorkload(1.0, seed) + val topology = createTopology("single") - Provisioner(dispatcher, seed).use { provisioner -> - provisioner.runSteps( - setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), - registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), - setupHosts(serviceDomain = "compute.opendc.org", topology) - ) + Provisioner(dispatcher, seed).use { provisioner -> + provisioner.runSteps( + setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), + registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), + setupHosts(serviceDomain = "compute.opendc.org", topology), + ) - val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! - service.replay(timeSource, workload, seed, interference = true) - } + val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! + service.replay(timeSource, workload, seed, interference = true) + } - println( - "Scheduler " + - "Success=${monitor.attemptsSuccess} " + - "Failure=${monitor.attemptsFailure} " + - "Error=${monitor.attemptsError} " + - "Pending=${monitor.serversPending} " + - "Active=${monitor.serversActive}" - ) + println( + "Scheduler " + + "Success=${monitor.attemptsSuccess} " + + "Failure=${monitor.attemptsFailure} " + + "Error=${monitor.attemptsError} " + + "Pending=${monitor.serversPending} " + + "Active=${monitor.serversActive}", + ) - // Note that these values have been verified beforehand - assertAll( - { assertEquals(42814948316, monitor.idleTime) { "Idle time incorrect" } }, - { assertEquals(40138266225, monitor.activeTime) { "Active time incorrect" } }, - { assertEquals(23489356981, monitor.stealTime) { "Steal time incorrect" } }, - { assertEquals(424267131, monitor.lostTime) { "Lost time incorrect" } } - ) - } + // Note that these values have been verified beforehand + assertAll( + { assertEquals(42814948316, monitor.idleTime) { "Idle time incorrect" } }, + { assertEquals(40138266225, monitor.activeTime) { "Active time incorrect" } }, + { assertEquals(23489356981, monitor.stealTime) { "Steal time incorrect" } }, + { assertEquals(424267131, monitor.lostTime) { "Lost time incorrect" } }, + ) + } /** * Test a small simulation setup with failures. */ @Test - fun testFailures() = runSimulation { - val seed = 0L - val topology = createTopology("single") - val workload = createTestWorkload(0.25, seed) - val monitor = monitor + fun testFailures() = + runSimulation { + val seed = 0L + val topology = createTopology("single") + val workload = createTestWorkload(0.25, seed) + val monitor = monitor - Provisioner(dispatcher, seed).use { provisioner -> - provisioner.runSteps( - setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), - registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), - setupHosts(serviceDomain = "compute.opendc.org", topology) - ) + Provisioner(dispatcher, seed).use { provisioner -> + provisioner.runSteps( + setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), + registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), + setupHosts(serviceDomain = "compute.opendc.org", topology), + ) - val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! - service.replay(timeSource, workload, seed, failureModel = grid5000(Duration.ofDays(7))) - } + val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! + service.replay(timeSource, workload, seed, failureModel = grid5000(Duration.ofDays(7))) + } - // Note that these values have been verified beforehand - assertAll( - { assertEquals(1404277711, monitor.idleTime) { "Idle time incorrect" } }, - { assertEquals(1478675712, monitor.activeTime) { "Active time incorrect" } }, - { assertEquals(152, monitor.stealTime) { "Steal time incorrect" } }, - { assertEquals(0, monitor.lostTime) { "Lost time incorrect" } }, - { assertEquals(360369187, monitor.uptime) { "Uptime incorrect" } } - ) - } + // Note that these values have been verified beforehand + assertAll( + { assertEquals(1404277711, monitor.idleTime) { "Idle time incorrect" } }, + { assertEquals(1478675712, monitor.activeTime) { "Active time incorrect" } }, + { assertEquals(152, monitor.stealTime) { "Steal time incorrect" } }, + { assertEquals(0, monitor.lostTime) { "Lost time incorrect" } }, + { assertEquals(360369187, monitor.uptime) { "Uptime incorrect" } }, + ) + } /** * Obtain the trace reader for the test. */ - private fun createTestWorkload(fraction: Double, seed: Long): List { + private fun createTestWorkload( + fraction: Double, + seed: Long, + ): List { val source = trace("bitbrains-small").sampleByLoad(fraction) return source.resolve(workloadLoader, Random(seed)) } diff --git a/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinRunnerTest.kt b/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinRunnerTest.kt index 7354e7a51..32d53aeef 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinRunnerTest.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinRunnerTest.kt @@ -46,20 +46,20 @@ class CapelinRunnerTest { private val tracePath = File("src/test/resources/trace") /** - * Smoke test with output. + * Smoke test with output. fixme: Fix failures and enable Test */ -// @Test // fixme: Fix failures and enable fun testSmoke() { val outputPath = Files.createTempDirectory("output").toFile() try { val runner = CapelinRunner(envPath, tracePath, outputPath) - val scenario = Scenario( - Topology("topology"), - Workload("bitbrains-small", trace("bitbrains-small")), - OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true), - "active-servers" - ) + val scenario = + Scenario( + Topology("topology"), + Workload("bitbrains-small", trace("bitbrains-small")), + OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true), + "active-servers", + ) assertDoesNotThrow { runner.runScenario(scenario, seed = 0L) } } finally { @@ -68,17 +68,17 @@ class CapelinRunnerTest { } /** - * Smoke test without output. + * Smoke test without output. fixme: Fix failures and enable Test */ -// @Test // fixme: Fix failures and enable fun testSmokeNoOutput() { val runner = CapelinRunner(envPath, tracePath, null) - val scenario = Scenario( - Topology("topology"), - Workload("bitbrains-small", trace("bitbrains-small")), - OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true), - "active-servers" - ) + val scenario = + Scenario( + Topology("topology"), + Workload("bitbrains-small", trace("bitbrains-small")), + OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true), + "active-servers", + ) assertDoesNotThrow { runner.runScenario(scenario, seed = 0L) } } diff --git a/opendc-experiments/opendc-experiments-faas/build.gradle.kts b/opendc-experiments/opendc-experiments-faas/build.gradle.kts index 3cabbbf2e..d217f320d 100644 --- a/opendc-experiments/opendc-experiments-faas/build.gradle.kts +++ b/opendc-experiments/opendc-experiments-faas/build.gradle.kts @@ -22,7 +22,7 @@ description = "Support library for simulating FaaS workloads with OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` `testing-conventions` diff --git a/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FaaSServiceProvisioningStep.kt b/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FaaSServiceProvisioningStep.kt index 548abc9ab..6c8cc0a2e 100644 --- a/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FaaSServiceProvisioningStep.kt +++ b/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FaaSServiceProvisioningStep.kt @@ -48,21 +48,23 @@ public class FaaSServiceProvisioningStep internal constructor( private val routingPolicy: (ProvisioningContext) -> RoutingPolicy, private val terminationPolicy: (ProvisioningContext) -> FunctionTerminationPolicy, private val machineModel: MachineModel, - private val coldStartModel: ColdStartModel? + private val coldStartModel: ColdStartModel?, ) : ProvisioningStep { override fun apply(ctx: ProvisioningContext): AutoCloseable { - val delayInjector = if (coldStartModel != null) { - StochasticDelayInjector(coldStartModel, Random(ctx.seeder.nextLong())) - } else { - ZeroDelayInjector - } + val delayInjector = + if (coldStartModel != null) { + StochasticDelayInjector(coldStartModel, Random(ctx.seeder.nextLong())) + } else { + ZeroDelayInjector + } val deployer = SimFunctionDeployer(ctx.dispatcher, machineModel, delayInjector) - val service = FaaSService( - ctx.dispatcher, - deployer, - routingPolicy(ctx), - terminationPolicy(ctx) - ) + val service = + FaaSService( + ctx.dispatcher, + deployer, + routingPolicy(ctx), + terminationPolicy(ctx), + ) ctx.registry.register(serviceDomain, FaaSService::class.java, service) diff --git a/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FaaSSteps.kt b/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FaaSSteps.kt index ce76da0d0..a84fe0924 100644 --- a/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FaaSSteps.kt +++ b/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FaaSSteps.kt @@ -46,7 +46,7 @@ public fun setupFaaSService( routingPolicy: (ProvisioningContext) -> RoutingPolicy, terminationPolicy: (ProvisioningContext) -> FunctionTerminationPolicy, machineModel: MachineModel, - coldStartModel: ColdStartModel? = null + coldStartModel: ColdStartModel? = null, ): ProvisioningStep { return FaaSServiceProvisioningStep(serviceDomain, routingPolicy, terminationPolicy, machineModel, coldStartModel) } diff --git a/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FunctionSample.kt b/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FunctionSample.kt index 4ce2b136c..4ca84da77 100644 --- a/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FunctionSample.kt +++ b/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FunctionSample.kt @@ -40,5 +40,5 @@ public data class FunctionSample( val provisionedCpu: Int, val provisionedMem: Int, val cpuUsage: Double, - val memUsage: Double + val memUsage: Double, ) diff --git a/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FunctionTraceWorkload.kt b/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FunctionTraceWorkload.kt index 71a2536c1..1592e629e 100644 --- a/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FunctionTraceWorkload.kt +++ b/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/FunctionTraceWorkload.kt @@ -31,6 +31,11 @@ import org.opendc.simulator.compute.workload.SimWorkload * A [SimFaaSWorkload] for a [FunctionTrace]. */ public class FunctionTraceWorkload(trace: FunctionTrace) : - SimFaaSWorkload, SimWorkload by SimTrace.ofFragments(trace.samples.map { SimTraceFragment(it.timestamp, it.duration, it.cpuUsage, 1) }).createWorkload(0) { + SimFaaSWorkload, + SimWorkload by SimTrace.ofFragments( + trace.samples.map { + SimTraceFragment(it.timestamp, it.duration, it.cpuUsage, 1) + }, + ).createWorkload(0) { override suspend fun invoke() {} } diff --git a/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/ServerlessTraceReader.kt b/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/ServerlessTraceReader.kt index 7b6b3ef7d..09412961c 100644 --- a/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/ServerlessTraceReader.kt +++ b/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/ServerlessTraceReader.kt @@ -42,9 +42,10 @@ public class ServerlessTraceReader { /** * The [CsvFactory] used to create the parser. */ - private val factory = CsvFactory() - .enable(CsvParser.Feature.ALLOW_COMMENTS) - .enable(CsvParser.Feature.TRIM_SPACES) + private val factory = + CsvFactory() + .enable(CsvParser.Feature.ALLOW_COMMENTS) + .enable(CsvParser.Feature.TRIM_SPACES) /** * Parse the traces at the specified [path]. @@ -120,17 +121,18 @@ public class ServerlessTraceReader { /** * The [CsvSchema] that is used to parse the trace. */ - val schema = CsvSchema.builder() - .addColumn("Timestamp [ms]", CsvSchema.ColumnType.NUMBER) - .addColumn("Invocations", CsvSchema.ColumnType.NUMBER) - .addColumn("Avg Exec time per Invocation", CsvSchema.ColumnType.NUMBER) - .addColumn("Provisioned CPU [Mhz]", CsvSchema.ColumnType.NUMBER) - .addColumn("Provisioned Memory [mb]", CsvSchema.ColumnType.NUMBER) - .addColumn("Avg cpu usage per Invocation [Mhz]", CsvSchema.ColumnType.NUMBER) - .addColumn("Avg mem usage per Invocation [mb]", CsvSchema.ColumnType.NUMBER) - .addColumn("name", CsvSchema.ColumnType.STRING) - .setAllowComments(true) - .setUseHeader(true) - .build() + val schema = + CsvSchema.builder() + .addColumn("Timestamp [ms]", CsvSchema.ColumnType.NUMBER) + .addColumn("Invocations", CsvSchema.ColumnType.NUMBER) + .addColumn("Avg Exec time per Invocation", CsvSchema.ColumnType.NUMBER) + .addColumn("Provisioned CPU [Mhz]", CsvSchema.ColumnType.NUMBER) + .addColumn("Provisioned Memory [mb]", CsvSchema.ColumnType.NUMBER) + .addColumn("Avg cpu usage per Invocation [Mhz]", CsvSchema.ColumnType.NUMBER) + .addColumn("Avg mem usage per Invocation [mb]", CsvSchema.ColumnType.NUMBER) + .addColumn("name", CsvSchema.ColumnType.STRING) + .setAllowComments(true) + .setUseHeader(true) + .build() } } diff --git a/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/TraceHelpers.kt b/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/TraceHelpers.kt index 7a354d696..faa13fa25 100644 --- a/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/TraceHelpers.kt +++ b/opendc-experiments/opendc-experiments-faas/src/main/kotlin/org/opendc/experiments/faas/TraceHelpers.kt @@ -37,7 +37,10 @@ import kotlin.math.max * @param clock An [InstantSource] instance tracking simulation time. * @param trace The trace to simulate. */ -public suspend fun FaaSService.replay(clock: InstantSource, trace: List) { +public suspend fun FaaSService.replay( + clock: InstantSource, + trace: List, +) { val client = newClient() try { coroutineScope { diff --git a/opendc-experiments/opendc-experiments-faas/src/test/kotlin/org/opendc/experiments/faas/FaaSExperiment.kt b/opendc-experiments/opendc-experiments-faas/src/test/kotlin/org/opendc/experiments/faas/FaaSExperiment.kt index 9a3dba13a..346059a89 100644 --- a/opendc-experiments/opendc-experiments-faas/src/test/kotlin/org/opendc/experiments/faas/FaaSExperiment.kt +++ b/opendc-experiments/opendc-experiments-faas/src/test/kotlin/org/opendc/experiments/faas/FaaSExperiment.kt @@ -46,34 +46,35 @@ class FaaSExperiment { * Smoke test that simulates a small trace. */ @Test - fun testSmoke() = runSimulation { - val faasService = "faas.opendc.org" + fun testSmoke() = + runSimulation { + val faasService = "faas.opendc.org" - Provisioner(dispatcher, seed = 0L).use { provisioner -> - provisioner.runStep( - setupFaaSService( - faasService, - { RandomRoutingPolicy() }, - { FunctionTerminationPolicyFixed(it.dispatcher, timeout = Duration.ofMinutes(10)) }, - createMachineModel(), - coldStartModel = ColdStartModel.GOOGLE + Provisioner(dispatcher, seed = 0L).use { provisioner -> + provisioner.runStep( + setupFaaSService( + faasService, + { RandomRoutingPolicy() }, + { FunctionTerminationPolicyFixed(it.dispatcher, timeout = Duration.ofMinutes(10)) }, + createMachineModel(), + coldStartModel = ColdStartModel.GOOGLE, + ), ) - ) - val service = provisioner.registry.resolve(faasService, FaaSService::class.java)!! + val service = provisioner.registry.resolve(faasService, FaaSService::class.java)!! - val trace = ServerlessTraceReader().parse(File("src/test/resources/trace")) - service.replay(timeSource, trace) + val trace = ServerlessTraceReader().parse(File("src/test/resources/trace")) + service.replay(timeSource, trace) - val stats = service.getSchedulerStats() + val stats = service.getSchedulerStats() - assertAll( - { assertEquals(14, stats.totalInvocations) }, - { assertEquals(2, stats.timelyInvocations) }, - { assertEquals(12, stats.delayedInvocations) } - ) + assertAll( + { assertEquals(14, stats.totalInvocations) }, + { assertEquals(2, stats.timelyInvocations) }, + { assertEquals(12, stats.delayedInvocations) }, + ) + } } - } /** * Construct the machine model to test with. @@ -82,8 +83,10 @@ class FaaSExperiment { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) return MachineModel( - /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, - /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + // cpus + List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, + // memory + List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, ) } } diff --git a/opendc-experiments/opendc-experiments-faas/src/test/kotlin/org/opendc/experiments/faas/ServerlessTraceReaderTest.kt b/opendc-experiments/opendc-experiments-faas/src/test/kotlin/org/opendc/experiments/faas/ServerlessTraceReaderTest.kt index 54071791a..bc4f54573 100644 --- a/opendc-experiments/opendc-experiments-faas/src/test/kotlin/org/opendc/experiments/faas/ServerlessTraceReaderTest.kt +++ b/opendc-experiments/opendc-experiments-faas/src/test/kotlin/org/opendc/experiments/faas/ServerlessTraceReaderTest.kt @@ -39,7 +39,7 @@ class ServerlessTraceReaderTest { assertAll( { assertEquals(2, trace.size) }, { assertEquals("004c1ea5eb15978682b00ab659aed21e2835d5287668da8d5267f751fdfbdd78", trace[0].id) }, - { assertEquals(256, trace[0].maxMemory) } + { assertEquals(256, trace[0].maxMemory) }, ) } } diff --git a/opendc-experiments/opendc-experiments-greenifier/build.gradle.kts b/opendc-experiments/opendc-experiments-greenifier/build.gradle.kts index 74fa249ce..456725459 100644 --- a/opendc-experiments/opendc-experiments-greenifier/build.gradle.kts +++ b/opendc-experiments/opendc-experiments-greenifier/build.gradle.kts @@ -22,7 +22,7 @@ description = "Experiments for the Greenifier work" -/* Build configuration */ +// Build configuration plugins { `kotlin-conventions` `testing-conventions` @@ -56,10 +56,10 @@ val createGreenifierApp by tasks.creating(CreateStartScripts::class) { applicationName = "greenifier" mainClass.set("org.opendc.experiments.greenifier.GreenifierCli") classpath = tasks.jar.get().outputs.files + configurations["runtimeClasspath"] - outputDir = project.buildDir.resolve("scripts") + outputDir = project.layout.buildDirectory.get().asFile.resolve("scripts") } -/* Create custom Greenifier distribution */ +// Create custom Greenifier distribution distributions { main { distributionBaseName.set("greenifier") diff --git a/opendc-experiments/opendc-experiments-greenifier/src/jmh/kotlin/org/opendc/experiments/greenifier/GreenifierBenchmarks.kt b/opendc-experiments/opendc-experiments-greenifier/src/jmh/kotlin/org/opendc/experiments/greenifier/GreenifierBenchmarks.kt index 7997d01c1..6cc6df365 100644 --- a/opendc-experiments/opendc-experiments-greenifier/src/jmh/kotlin/org/opendc/experiments/greenifier/GreenifierBenchmarks.kt +++ b/opendc-experiments/opendc-experiments-greenifier/src/jmh/kotlin/org/opendc/experiments/greenifier/GreenifierBenchmarks.kt @@ -72,22 +72,24 @@ class GreenifierBenchmarks { } @Benchmark - fun benchmarkGreenifier() = runSimulation { - val serviceDomain = "compute.opendc.org" + fun benchmarkGreenifier() = + runSimulation { + val serviceDomain = "compute.opendc.org" - Provisioner(dispatcher, seed = 0).use { provisioner -> - val computeScheduler = FilterScheduler( - filters = listOf(ComputeFilter(), VCpuFilter(16.0), RamFilter(1.0)), - weighers = listOf(CoreRamWeigher(multiplier = 1.0)) - ) + Provisioner(dispatcher, seed = 0).use { provisioner -> + val computeScheduler = + FilterScheduler( + filters = listOf(ComputeFilter(), VCpuFilter(16.0), RamFilter(1.0)), + weighers = listOf(CoreRamWeigher(multiplier = 1.0)), + ) - provisioner.runSteps( - setupComputeService(serviceDomain, { computeScheduler }), - setupHosts(serviceDomain, topology, optimize = isOptimized) - ) + provisioner.runSteps( + setupComputeService(serviceDomain, { computeScheduler }), + setupHosts(serviceDomain, topology, optimize = isOptimized), + ) - val service = provisioner.registry.resolve(serviceDomain, ComputeService::class.java)!! - service.replay(timeSource, vms, 0L, interference = true) + val service = provisioner.registry.resolve(serviceDomain, ComputeService::class.java)!! + service.replay(timeSource, vms, 0L, interference = true) + } } - } } diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierCli.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierCli.kt index efdc96cdd..935575004 100644 --- a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierCli.kt +++ b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierCli.kt @@ -104,7 +104,7 @@ internal class GreenifierCommand : CliktCommand(name = "greenifier") { */ private val portfolio by argument(help = "portfolio to replay") .choice( - "greenifier" to { GreenifierPortfolio() } + "greenifier" to { GreenifierPortfolio() }, ) .default({ GreenifierPortfolio() }) @@ -131,12 +131,17 @@ internal class GreenifierCommand : CliktCommand(name = "greenifier") { /** * Run a single scenario. */ - private fun runScenario(runner: GreenifierRunner, pool: ForkJoinPool, scenario: Scenario) { - val pb = ProgressBarBuilder() - .setInitialMax(repeats.toLong()) - .setStyle(ProgressBarStyle.ASCII) - .setTaskName("Simulating...") - .build() + private fun runScenario( + runner: GreenifierRunner, + pool: ForkJoinPool, + scenario: Scenario, + ) { + val pb = + ProgressBarBuilder() + .setInitialMax(repeats.toLong()) + .setStyle(ProgressBarStyle.ASCII) + .setTaskName("Simulating...") + .build() pool.submit { LongStream.range(0, repeats.toLong()) diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierPortfolio.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierPortfolio.kt index eee30b816..f7fd204fc 100644 --- a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierPortfolio.kt +++ b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierPortfolio.kt @@ -34,26 +34,29 @@ import org.opendc.experiments.base.portfolio.model.Workload * A [Portfolio] that explores the difference between horizontal and vertical scaling. */ public class GreenifierPortfolio : Portfolio { - private val topologies = listOf( - Topology("single"), - Topology("multi") - ) + private val topologies = + listOf( + Topology("single"), + Topology("multi"), + ) - private val workloads = listOf( - Workload("bitbrains-small", trace("trace").sampleByLoad(1.0)) - ) + private val workloads = + listOf( + Workload("bitbrains-small", trace("trace").sampleByLoad(1.0)), + ) private val operationalPhenomena = OperationalPhenomena(0.0, false) private val allocationPolicy = "active-servers" - override val scenarios: Iterable = topologies.flatMap { topology -> - workloads.map { workload -> - Scenario( - topology, - workload, - operationalPhenomena, - allocationPolicy, - mapOf("topology" to topology.name, "workload" to workload.name) - ) + override val scenarios: Iterable = + topologies.flatMap { topology -> + workloads.map { workload -> + Scenario( + topology, + workload, + operationalPhenomena, + allocationPolicy, + mapOf("topology" to topology.name, "workload" to workload.name), + ) + } } - } } diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierRunner.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierRunner.kt index 2c2962f34..6da35cd15 100644 --- a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierRunner.kt +++ b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierRunner.kt @@ -50,7 +50,7 @@ import kotlin.math.roundToLong public class GreenifierRunner( private val envPath: File, tracePath: File, - private val outputPath: File? + private val outputPath: File?, ) { /** * The [ComputeWorkloadLoader] to use for loading the traces. @@ -60,14 +60,17 @@ public class GreenifierRunner( /** * Run a single [scenario] with the specified seed. */ - fun runScenario(scenario: Scenario, seed: Long) = runSimulation { + fun runScenario( + scenario: Scenario, + seed: Long, + ) = runSimulation { val serviceDomain = "compute.opendc.org" val topology = clusterTopology(File(envPath, "${scenario.topology.name}.txt")) Provisioner(dispatcher, seed).use { provisioner -> provisioner.runSteps( setupComputeService(serviceDomain, { createComputeScheduler(scenario.allocationPolicy, Random(it.seeder.nextLong())) }), - setupHosts(serviceDomain, topology, optimize = true) + setupHosts(serviceDomain, topology, optimize = true), ) if (outputPath != null) { @@ -80,9 +83,9 @@ public class GreenifierRunner( ParquetComputeMonitor( outputPath, partition, - bufferSize = 4096 - ) - ) + bufferSize = 4096, + ), + ), ) } diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierIntegrationTest.kt b/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierIntegrationTest.kt index dbf840a45..36b15ee0b 100644 --- a/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierIntegrationTest.kt +++ b/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierIntegrationTest.kt @@ -77,10 +77,11 @@ class GreenifierIntegrationTest { @BeforeEach fun setUp() { monitor = TestComputeMonitor() - computeScheduler = FilterScheduler( - filters = listOf(ComputeFilter(), VCpuFilter(16.0), RamFilter(1.0)), - weighers = listOf(CoreRamWeigher(multiplier = 1.0)) - ) + computeScheduler = + FilterScheduler( + filters = listOf(ComputeFilter(), VCpuFilter(16.0), RamFilter(1.0)), + weighers = listOf(CoreRamWeigher(multiplier = 1.0)), + ) workloadLoader = ComputeWorkloadLoader(File("src/test/resources/trace")) } @@ -88,159 +89,166 @@ class GreenifierIntegrationTest { * Test a large simulation setup. */ @Test - fun testLarge() = runSimulation { - val seed = 0L - val workload = createTestWorkload(1.0, seed) - val topology = createTopology() - val monitor = monitor + fun testLarge() = + runSimulation { + val seed = 0L + val workload = createTestWorkload(1.0, seed) + val topology = createTopology() + val monitor = monitor - Provisioner(dispatcher, seed).use { provisioner -> - provisioner.runSteps( - setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), - registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), - setupHosts(serviceDomain = "compute.opendc.org", topology) - ) + Provisioner(dispatcher, seed).use { provisioner -> + provisioner.runSteps( + setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), + registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), + setupHosts(serviceDomain = "compute.opendc.org", topology), + ) - val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! - service.replay(timeSource, workload, seed) - } + val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! + service.replay(timeSource, workload, seed) + } - println( - "Scheduler " + - "Success=${monitor.attemptsSuccess} " + - "Failure=${monitor.attemptsFailure} " + - "Error=${monitor.attemptsError} " + - "Pending=${monitor.serversPending} " + - "Active=${monitor.serversActive}" - ) + println( + "Scheduler " + + "Success=${monitor.attemptsSuccess} " + + "Failure=${monitor.attemptsFailure} " + + "Error=${monitor.attemptsError} " + + "Pending=${monitor.serversPending} " + + "Active=${monitor.serversActive}", + ) - // Note that these values have been verified beforehand - assertAll( - { assertEquals(50, monitor.attemptsSuccess, "The scheduler should schedule 50 VMs") }, - { assertEquals(0, monitor.serversActive, "All VMs should finish after a run") }, - { assertEquals(0, monitor.attemptsFailure, "No VM should be unscheduled") }, - { assertEquals(0, monitor.serversPending, "No VM should not be in the queue") }, - { assertEquals(223379991650, monitor.idleTime) { "Incorrect idle time" } }, - { assertEquals(66977091124, monitor.activeTime) { "Incorrect active time" } }, - { assertEquals(3160267873, monitor.stealTime) { "Incorrect steal time" } }, - { assertEquals(0, monitor.lostTime) { "Incorrect lost time" } }, - { assertEquals(5.8407E9, monitor.energyUsage, 1E4) { "Incorrect power draw" } } - ) - } + // Note that these values have been verified beforehand + assertAll( + { assertEquals(50, monitor.attemptsSuccess, "The scheduler should schedule 50 VMs") }, + { assertEquals(0, monitor.serversActive, "All VMs should finish after a run") }, + { assertEquals(0, monitor.attemptsFailure, "No VM should be unscheduled") }, + { assertEquals(0, monitor.serversPending, "No VM should not be in the queue") }, + { assertEquals(223379991650, monitor.idleTime) { "Incorrect idle time" } }, + { assertEquals(66977091124, monitor.activeTime) { "Incorrect active time" } }, + { assertEquals(3160267873, monitor.stealTime) { "Incorrect steal time" } }, + { assertEquals(0, monitor.lostTime) { "Incorrect lost time" } }, + { assertEquals(5.8407E9, monitor.energyUsage, 1E4) { "Incorrect power draw" } }, + ) + } /** * Test a small simulation setup. */ @Test - fun testSmall() = runSimulation { - val seed = 1L - val workload = createTestWorkload(0.25, seed) - val topology = createTopology("single") - val monitor = monitor + fun testSmall() = + runSimulation { + val seed = 1L + val workload = createTestWorkload(0.25, seed) + val topology = createTopology("single") + val monitor = monitor - Provisioner(dispatcher, seed).use { provisioner -> - provisioner.runSteps( - setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), - registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), - setupHosts(serviceDomain = "compute.opendc.org", topology) - ) + Provisioner(dispatcher, seed).use { provisioner -> + provisioner.runSteps( + setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), + registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), + setupHosts(serviceDomain = "compute.opendc.org", topology), + ) - val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! - service.replay(timeSource, workload, seed) - } + val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! + service.replay(timeSource, workload, seed) + } - println( - "Scheduler " + - "Success=${monitor.attemptsSuccess} " + - "Failure=${monitor.attemptsFailure} " + - "Error=${monitor.attemptsError} " + - "Pending=${monitor.serversPending} " + - "Active=${monitor.serversActive}" - ) + println( + "Scheduler " + + "Success=${monitor.attemptsSuccess} " + + "Failure=${monitor.attemptsFailure} " + + "Error=${monitor.attemptsError} " + + "Pending=${monitor.serversPending} " + + "Active=${monitor.serversActive}", + ) - // Note that these values have been verified beforehand - assertAll( - { assertEquals(10996730092, monitor.idleTime) { "Idle time incorrect" } }, - { assertEquals(9741285381, monitor.activeTime) { "Active time incorrect" } }, - { assertEquals(152, monitor.stealTime) { "Steal time incorrect" } }, - { assertEquals(0, monitor.lostTime) { "Lost time incorrect" } }, - { assertEquals(7.0109E8, monitor.energyUsage, 1E4) { "Incorrect power draw" } } - ) - } + // Note that these values have been verified beforehand + assertAll( + { assertEquals(10996730092, monitor.idleTime) { "Idle time incorrect" } }, + { assertEquals(9741285381, monitor.activeTime) { "Active time incorrect" } }, + { assertEquals(152, monitor.stealTime) { "Steal time incorrect" } }, + { assertEquals(0, monitor.lostTime) { "Lost time incorrect" } }, + { assertEquals(7.0109E8, monitor.energyUsage, 1E4) { "Incorrect power draw" } }, + ) + } /** * Test a small simulation setup with interference. */ @Test - fun testInterference() = runSimulation { - val seed = 0L - val workload = createTestWorkload(1.0, seed) - val topology = createTopology("single") + fun testInterference() = + runSimulation { + val seed = 0L + val workload = createTestWorkload(1.0, seed) + val topology = createTopology("single") - Provisioner(dispatcher, seed).use { provisioner -> - provisioner.runSteps( - setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), - registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), - setupHosts(serviceDomain = "compute.opendc.org", topology) - ) + Provisioner(dispatcher, seed).use { provisioner -> + provisioner.runSteps( + setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), + registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), + setupHosts(serviceDomain = "compute.opendc.org", topology), + ) - val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! - service.replay(timeSource, workload, seed, interference = true) - } + val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! + service.replay(timeSource, workload, seed, interference = true) + } - println( - "Scheduler " + - "Success=${monitor.attemptsSuccess} " + - "Failure=${monitor.attemptsFailure} " + - "Error=${monitor.attemptsError} " + - "Pending=${monitor.serversPending} " + - "Active=${monitor.serversActive}" - ) + println( + "Scheduler " + + "Success=${monitor.attemptsSuccess} " + + "Failure=${monitor.attemptsFailure} " + + "Error=${monitor.attemptsError} " + + "Pending=${monitor.serversPending} " + + "Active=${monitor.serversActive}", + ) - // Note that these values have been verified beforehand - assertAll( - { assertEquals(42814948316, monitor.idleTime) { "Idle time incorrect" } }, - { assertEquals(40138266225, monitor.activeTime) { "Active time incorrect" } }, - { assertEquals(23489356981, monitor.stealTime) { "Steal time incorrect" } }, - { assertEquals(424267131, monitor.lostTime) { "Lost time incorrect" } } - ) - } + // Note that these values have been verified beforehand + assertAll( + { assertEquals(42814948316, monitor.idleTime) { "Idle time incorrect" } }, + { assertEquals(40138266225, monitor.activeTime) { "Active time incorrect" } }, + { assertEquals(23489356981, monitor.stealTime) { "Steal time incorrect" } }, + { assertEquals(424267131, monitor.lostTime) { "Lost time incorrect" } }, + ) + } /** * Test a small simulation setup with failures. */ @Test - fun testFailures() = runSimulation { - val seed = 0L - val topology = createTopology("single") - val workload = createTestWorkload(0.25, seed) - val monitor = monitor + fun testFailures() = + runSimulation { + val seed = 0L + val topology = createTopology("single") + val workload = createTestWorkload(0.25, seed) + val monitor = monitor - Provisioner(dispatcher, seed).use { provisioner -> - provisioner.runSteps( - setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), - registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), - setupHosts(serviceDomain = "compute.opendc.org", topology) - ) + Provisioner(dispatcher, seed).use { provisioner -> + provisioner.runSteps( + setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }), + registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor), + setupHosts(serviceDomain = "compute.opendc.org", topology), + ) - val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! - service.replay(timeSource, workload, seed, failureModel = grid5000(Duration.ofDays(7))) - } + val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!! + service.replay(timeSource, workload, seed, failureModel = grid5000(Duration.ofDays(7))) + } - // Note that these values have been verified beforehand - assertAll( - { assertEquals(1404277711, monitor.idleTime) { "Idle time incorrect" } }, - { assertEquals(1478675712, monitor.activeTime) { "Active time incorrect" } }, - { assertEquals(152, monitor.stealTime) { "Steal time incorrect" } }, - { assertEquals(0, monitor.lostTime) { "Lost time incorrect" } }, - { assertEquals(360369187, monitor.uptime) { "Uptime incorrect" } } - ) - } + // Note that these values have been verified beforehand + assertAll( + { assertEquals(1404277711, monitor.idleTime) { "Idle time incorrect" } }, + { assertEquals(1478675712, monitor.activeTime) { "Active time incorrect" } }, + { assertEquals(152, monitor.stealTime) { "Steal time incorrect" } }, + { assertEquals(0, monitor.lostTime) { "Lost time incorrect" } }, + { assertEquals(360369187, monitor.uptime) { "Uptime incorrect" } }, + ) + } /** * Obtain the trace reader for the test. */ - private fun createTestWorkload(fraction: Double, seed: Long): List { + private fun createTestWorkload( + fraction: Double, + seed: Long, + ): List { val source = trace("bitbrains-small").sampleByLoad(fraction) return source.resolve(workloadLoader, Random(seed)) } diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierRunnerTest.kt b/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierRunnerTest.kt index ad3113e16..b6d6a6e93 100644 --- a/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierRunnerTest.kt +++ b/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierRunnerTest.kt @@ -46,20 +46,20 @@ class GreenifierRunnerTest { private val tracePath = File("src/test/resources/trace") /** - * Smoke test with output. + * Smoke test with output. fixme: Fix failures and enable Test */ -// @Test // fixme: Fix failures and enable fun testSmoke() { val outputPath = Files.createTempDirectory("output").toFile() try { val runner = GreenifierRunner(envPath, tracePath, outputPath) - val scenario = Scenario( - Topology("topology"), - Workload("bitbrains-small", trace("bitbrains-small")), - OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true), - "active-servers" - ) + val scenario = + Scenario( + Topology("topology"), + Workload("bitbrains-small", trace("bitbrains-small")), + OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true), + "active-servers", + ) assertDoesNotThrow { runner.runScenario(scenario, seed = 0L) } } finally { @@ -68,17 +68,17 @@ class GreenifierRunnerTest { } /** - * Smoke test without output. + * Smoke test without output. fixme: Fix failures and enable Test */ -// @Test // fixme: Fix failures and enable fun testSmokeNoOutput() { val runner = GreenifierRunner(envPath, tracePath, null) - val scenario = Scenario( - Topology("topology"), - Workload("bitbrains-small", trace("bitbrains-small")), - OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true), - "active-servers" - ) + val scenario = + Scenario( + Topology("topology"), + Workload("bitbrains-small", trace("bitbrains-small")), + OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true), + "active-servers", + ) assertDoesNotThrow { runner.runScenario(scenario, seed = 0L) } } diff --git a/opendc-experiments/opendc-experiments-tf20/build.gradle.kts b/opendc-experiments/opendc-experiments-tf20/build.gradle.kts index 7b3b084f5..b66958ca0 100644 --- a/opendc-experiments/opendc-experiments-tf20/build.gradle.kts +++ b/opendc-experiments/opendc-experiments-tf20/build.gradle.kts @@ -22,7 +22,7 @@ description = "TensorFlow application model in OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-conventions` `testing-conventions` diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/Models.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/Models.kt index be166bd5f..78a63df83 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/Models.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/Models.kt @@ -35,7 +35,7 @@ import org.opendc.experiments.tf20.keras.layer.regularization.Dropout /** * Construct an AlexNet model with the given batch size. */ -fun AlexNet(batchSize: Long): TrainableModel { +fun getAlexNet(batchSize: Long): TrainableModel { return Sequential( Input(batchSize, 227, 227, 3, name = "Input"), Conv2D(longArrayOf(11, 11, 3, 96), longArrayOf(1, 4, 4, 1), padding = ConvPadding.VALID, name = "conv1"), @@ -51,14 +51,14 @@ fun AlexNet(batchSize: Long): TrainableModel { Conv2D(longArrayOf(1, 1, 4096, 4096), longArrayOf(1, 1, 1, 1), padding = ConvPadding.SAME, name = "fc7"), Dropout(0.5f, name = "dropout7"), Conv2D(longArrayOf(1, 1, 4096, 1000), longArrayOf(1, 1, 1, 1), padding = ConvPadding.SAME, name = "f8"), - ActivationLayer(Activation.Softmax, name = "softmax") + ActivationLayer(Activation.Softmax, name = "softmax"), ) } /** * Construct an VGG16 model with the given batch size. */ -fun VGG16(batchSize: Long = 128): TrainableModel { +fun getVGG16(batchSize: Long = 128): TrainableModel { return Sequential( Input(batchSize, 224, 224, 3, name = "Input"), Conv2D(longArrayOf(3, 3, 3, 64), longArrayOf(1, 1, 1, 1), padding = ConvPadding.SAME, name = "conv1-1"), @@ -84,6 +84,6 @@ fun VGG16(batchSize: Long = 128): TrainableModel { Conv2D(longArrayOf(1, 1, 4096, 4096), longArrayOf(1, 1, 1, 1), padding = ConvPadding.SAME, name = "fc7"), Dropout(0.5f, name = "dropout7"), Conv2D(longArrayOf(1, 1, 4096, 1000), longArrayOf(1, 1, 1, 1), padding = ConvPadding.SAME, name = "f8"), - ActivationLayer(Activation.Softmax, name = "softmax") + ActivationLayer(Activation.Softmax, name = "softmax"), ) } diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/core/SimTFDevice.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/core/SimTFDevice.kt index a1fc3fba3..b14e499c8 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/core/SimTFDevice.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/core/SimTFDevice.kt @@ -54,102 +54,107 @@ public class SimTFDevice( dispatcher: Dispatcher, pu: ProcessingUnit, private val memory: MemoryUnit, - powerModel: CpuPowerModel + powerModel: CpuPowerModel, ) : TFDevice { /** * The [SimMachine] representing the device. */ - private val machine = SimBareMetalMachine.create( - FlowEngine.create(dispatcher).newGraph(), - MachineModel(listOf(pu), listOf(memory)), - SimPsuFactories.simple(powerModel) - ) + private val machine = + SimBareMetalMachine.create( + FlowEngine.create(dispatcher).newGraph(), + MachineModel(listOf(pu), listOf(memory)), + SimPsuFactories.simple(powerModel), + ) /** * The workload that will be run by the device. */ - private val workload = object : SimWorkload, FlowStageLogic { - /** - * The [FlowStage] of the workload. - */ - var stage: FlowStage? = null - - /** - * The output of the workload. - */ - private var output: OutPort? = null - - /** - * The queue of work to run. - */ - val queue = ArrayDeque() - - /** - * A flag to indicate that the workload is idle. - */ - val isIdle - get() = activeWork == null - - /** - * The active work of the workload. - */ - private var activeWork: Work? = null - - /** - * The timestamp of the last pull. - */ - private var lastPull: Long = 0L - - override fun onStart(ctx: SimMachineContext) { - val stage = ctx.graph.newStage(this) - this.stage = stage - output = stage.getOutlet("out") - lastPull = ctx.graph.engine.clock.millis() - - ctx.graph.connect(output, ctx.cpus[0].input) - } - - override fun onStop(ctx: SimMachineContext) { - stage?.close() - stage = null - output = null - } - - override fun setOffset(now: Long) {} + private val workload = + object : SimWorkload, FlowStageLogic { + /** + * The [FlowStage] of the workload. + */ + var stage: FlowStage? = null + + /** + * The output of the workload. + */ + private var output: OutPort? = null + + /** + * The queue of work to run. + */ + val queue = ArrayDeque() + + /** + * A flag to indicate that the workload is idle. + */ + val isIdle + get() = activeWork == null + + /** + * The active work of the workload. + */ + private var activeWork: Work? = null + + /** + * The timestamp of the last pull. + */ + private var lastPull: Long = 0L + + override fun onStart(ctx: SimMachineContext) { + val stage = ctx.graph.newStage(this) + this.stage = stage + output = stage.getOutlet("out") + lastPull = ctx.graph.engine.clock.millis() + + ctx.graph.connect(output, ctx.cpus[0].input) + } - override fun snapshot(): SimWorkload = throw UnsupportedOperationException() + override fun onStop(ctx: SimMachineContext) { + stage?.close() + stage = null + output = null + } - override fun onUpdate(ctx: FlowStage, now: Long): Long { - val output = output ?: return Long.MAX_VALUE - val lastPull = lastPull - this.lastPull = now - val delta = (now - lastPull).coerceAtLeast(0) - val consumedWork = output.rate * delta / 1000.0 + override fun setOffset(now: Long) {} + + override fun snapshot(): SimWorkload = throw UnsupportedOperationException() + + override fun onUpdate( + ctx: FlowStage, + now: Long, + ): Long { + val output = output ?: return Long.MAX_VALUE + val lastPull = lastPull + this.lastPull = now + val delta = (now - lastPull).coerceAtLeast(0) + val consumedWork = output.rate * delta / 1000.0 + + val activeWork = activeWork + if (activeWork != null) { + if (activeWork.consume(consumedWork)) { + this.activeWork = null + } else { + val duration = ceil(activeWork.flops / output.capacity * 1000).toLong() + output.push(output.capacity) + return now + duration + } + } - val activeWork = activeWork - if (activeWork != null) { - if (activeWork.consume(consumedWork)) { - this.activeWork = null - } else { - val duration = ceil(activeWork.flops / output.capacity * 1000).toLong() + val queue = queue + val head = queue.poll() + return if (head != null) { + this.activeWork = head + val duration = (head.flops / output.capacity * 1000).roundToLong() output.push(output.capacity) - return now + duration + now + duration + } else { + output.push(0.0f) + Long.MAX_VALUE } } - - val queue = queue - val head = queue.poll() - return if (head != null) { - this.activeWork = head - val duration = (head.flops / output.capacity * 1000).roundToLong() - output.push(output.capacity) - now + duration - } else { - output.push(0.0f) - Long.MAX_VALUE - } } - } init { machine.startWorkload(workload, emptyMap()) {} @@ -160,12 +165,13 @@ public class SimTFDevice( delay(duration.toLong()) } - override suspend fun compute(flops: Double) = suspendCancellableCoroutine { cont -> - workload.queue.add(Work(flops, cont)) - if (workload.isIdle) { - workload.stage?.invalidate() + override suspend fun compute(flops: Double) = + suspendCancellableCoroutine { cont -> + workload.queue.add(Work(flops, cont)) + if (workload.isIdle) { + workload.stage?.invalidate() + } } - } override fun getDeviceStats(): TFDeviceStats { return TFDeviceStats(machine.cpuUsage, machine.psu.powerDraw, machine.psu.energyUsage) diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/core/TFDeviceStats.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/core/TFDeviceStats.kt index 3fea44da7..c40982f8a 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/core/TFDeviceStats.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/core/TFDeviceStats.kt @@ -32,5 +32,5 @@ package org.opendc.experiments.tf20.core data class TFDeviceStats( val resourceUsage: Double, val powerDraw: Double, - val energyUsage: Double + val energyUsage: Double, ) diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/distribute/MirroredStrategy.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/distribute/MirroredStrategy.kt index 8caa7ec92..69d180a97 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/distribute/MirroredStrategy.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/distribute/MirroredStrategy.kt @@ -32,7 +32,11 @@ import org.opendc.experiments.tf20.core.TFDevice * It creates one replica per GPU device. Each variable in the model is mirrored across all the replicas. */ public class MirroredStrategy(val devices: List) : Strategy { - override suspend fun run(forward: Double, backward: Double, batchSize: Int) = coroutineScope { + override suspend fun run( + forward: Double, + backward: Double, + batchSize: Int, + ) = coroutineScope { for (device in devices) { launch { device.compute(forward * batchSize / devices.size + backward) } } diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/distribute/OneDeviceStrategy.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/distribute/OneDeviceStrategy.kt index 271fab987..05235b121 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/distribute/OneDeviceStrategy.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/distribute/OneDeviceStrategy.kt @@ -28,7 +28,11 @@ import org.opendc.experiments.tf20.core.TFDevice * A distribution [Strategy] that places all variables and computation on a single specified device. */ public class OneDeviceStrategy(val device: TFDevice) : Strategy { - override suspend fun run(forward: Double, backward: Double, batchSize: Int) { + override suspend fun run( + forward: Double, + backward: Double, + batchSize: Int, + ) { device.compute(forward * batchSize + backward) } } diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/distribute/Strategy.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/distribute/Strategy.kt index 3e755b56c..d5da628a1 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/distribute/Strategy.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/distribute/Strategy.kt @@ -29,5 +29,9 @@ public interface Strategy { /** * Converge the specified batch using the given strategy. */ - public suspend fun run(forward: Double, backward: Double, batchSize: Int) + public suspend fun run( + forward: Double, + backward: Double, + batchSize: Int, + ) } diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/TrainableModel.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/TrainableModel.kt index 2cac6cbcd..2d621d16c 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/TrainableModel.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/TrainableModel.kt @@ -110,7 +110,10 @@ public abstract class TrainableModel(vararg layers: Layer) : AutoCloseable { * @param [epochs] Number of epochs to train the model. An epoch is an iteration over the entire x and y data provided. * @param [batchSize] Number of samples per gradient update. */ - public suspend fun fit(epochs: Int = 5, batchSize: Int = 32) { + public suspend fun fit( + epochs: Int = 5, + batchSize: Int = 32, + ) { check(isCompiled) { "Model not yet compiled." } val forwardFlops = forward() diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/activations/Activation.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/activations/Activation.kt index 403acfc02..cb3b778ef 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/activations/Activation.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/activations/Activation.kt @@ -194,5 +194,5 @@ public enum class Activation { * * @see Ramachandran et al., 2017 */ - Swish; + Swish, } diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/conv/Conv2D.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/conv/Conv2D.kt index 74124bbdb..f89c47c63 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/conv/Conv2D.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/conv/Conv2D.kt @@ -35,13 +35,12 @@ import kotlin.math.ceil * Finally, if `activation` is applied to the outputs as well. */ public class Conv2D( - public val filter: LongArray = LongArray(4), // [H, W, channel_in, channel_out] - public val strides: LongArray = LongArray(4), // [1, stride_h, stride_w, 1] + public val filter: LongArray = LongArray(4), + public val strides: LongArray = LongArray(4), public val activation: Activation = Activation.Relu, public val padding: ConvPadding = ConvPadding.VALID, - name: String = "" + name: String = "", ) : Layer(name) { - private var padHeight: Double = 0.0 private var padWidth: Double = 0.0 diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/conv/ConvPadding.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/conv/ConvPadding.kt index 03ae62828..a47c435a1 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/conv/ConvPadding.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/conv/ConvPadding.kt @@ -35,5 +35,5 @@ public enum class ConvPadding { /** * No padding. */ - VALID + VALID, } diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/core/ActivationLayer.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/core/ActivationLayer.kt index 60b0f7543..000401b91 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/core/ActivationLayer.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/core/ActivationLayer.kt @@ -31,9 +31,8 @@ import org.opendc.experiments.tf20.keras.shape.TensorShape */ public class ActivationLayer( public val activation: Activation = Activation.Relu, - name: String = "" + name: String = "", ) : Layer(name) { - override fun build(inputShape: TensorShape) { // Intentionally left empty } diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/pool/Pool2D.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/pool/Pool2D.kt index 3c6b15bb7..a9a549380 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/pool/Pool2D.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/pool/Pool2D.kt @@ -40,9 +40,8 @@ public class Pool2D( public val poolSize: IntArray = intArrayOf(1, 2, 2, 1), public val strides: IntArray = intArrayOf(1, 2, 2, 1), public val padding: ConvPadding = ConvPadding.VALID, - name: String + name: String, ) : Layer(name) { - private var padHeight = 0L private var padWidth = 0L diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/regularization/Dropout.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/regularization/Dropout.kt index ff5f7711e..8198f98cd 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/regularization/Dropout.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/layer/regularization/Dropout.kt @@ -38,7 +38,7 @@ import org.opendc.experiments.tf20.keras.shape.TensorShape */ public class Dropout( public val keepProbability: Float = 0.1f, - name: String + name: String, ) : Layer(name) { override fun build(inputShape: TensorShape) {} diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/shape/TensorShape.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/shape/TensorShape.kt index 7affcb631..67e00e242 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/shape/TensorShape.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/keras/shape/TensorShape.kt @@ -33,7 +33,7 @@ public class TensorShape(vararg dims: Long) { /** * The dimensions of the tensor represented as [LongArray]. */ - private val _dims: LongArray = dims + private val localDims: LongArray = dims /** * Return amount of elements in Tensor with the given shape. @@ -42,7 +42,7 @@ public class TensorShape(vararg dims: Long) { get() { var prod = 1L for (i in 0 until rank) { - prod *= abs(_dims[i]) + prod *= abs(localDims[i]) } return prod } @@ -51,7 +51,7 @@ public class TensorShape(vararg dims: Long) { * Returns the rank of this shape. */ public val rank: Int - get() = _dims.size + get() = localDims.size /** * Returns the value of a dimension @@ -60,7 +60,7 @@ public class TensorShape(vararg dims: Long) { * @return The size of dimension i */ public operator fun get(i: Int): Long { - return _dims[i] + return localDims[i] } /** @@ -70,7 +70,7 @@ public class TensorShape(vararg dims: Long) { * @return Whether dimension i is unknown (equal to -1) */ private fun isKnown(i: Int): Boolean { - return _dims[i] != -1L + return localDims[i] != -1L } /** @@ -80,21 +80,21 @@ public class TensorShape(vararg dims: Long) { * @return The size of dimension i */ public fun size(i: Int): Long { - return _dims[i] + return localDims[i] } /** * Clone the [TensorShape] and return a new instance. */ public fun clone(): TensorShape { - return TensorShape(*_dims) + return TensorShape(*localDims) } /** * Create a string representation of this [TensorShape]. */ override fun toString(): String { - return _dims.contentToString().replace("-1", "None") + return localDims.contentToString().replace("-1", "None") } override fun equals(other: Any?): Boolean { @@ -103,12 +103,12 @@ public class TensorShape(vararg dims: Long) { other as TensorShape - if (!_dims.contentEquals(other._dims)) return false + if (!localDims.contentEquals(other.localDims)) return false return true } override fun hashCode(): Int { - return _dims.contentHashCode() + return localDims.contentHashCode() } } diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/network/Message.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/network/Message.kt index d63608733..fddcc7795 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/network/Message.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/network/Message.kt @@ -35,5 +35,5 @@ public data class Message( val to: NetworkNode, val type: MessageType, val dataSize: Long, - val iterations: Int + val iterations: Int, ) diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/network/MessageType.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/network/MessageType.kt index 8be162615..d71301373 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/network/MessageType.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/network/MessageType.kt @@ -27,5 +27,5 @@ package org.opendc.experiments.tf20.network */ public enum class MessageType { REQUEST, - WEIGHTS + WEIGHTS, } diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/network/NetworkController.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/network/NetworkController.kt index 5b408fb35..a4e79b4e0 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/network/NetworkController.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/network/NetworkController.kt @@ -63,7 +63,11 @@ public class NetworkController(dispatcher: Dispatcher) : AutoCloseable { /** * Add a connection between two links. */ - public fun addConnection(node1: NetworkNode, node2: NetworkNode, bandwidth: Long) { + public fun addConnection( + node1: NetworkNode, + node2: NetworkNode, + bandwidth: Long, + ) { bandwidthMatrix[Pair(node1, node2)] = bandwidth } diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/util/MLEnvironmentReader.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/util/MLEnvironmentReader.kt index 2a7578b3f..077bcc047 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/util/MLEnvironmentReader.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/util/MLEnvironmentReader.kt @@ -53,56 +53,58 @@ public class MLEnvironmentReader { var isGpuFlag = true var maxPower = 350.0 var minPower = 200.0 - val cores = machine.cpus.flatMap { id -> - when (id) { - 1 -> { - // ref: https://www.guru3d.com/articles-pages/nvidia-geforce-gtx-titan-x-review,8.html#:~:text=GeForce%20GTX%20Titan%20X%20%2D%20On,power%20supply%20unit%20as%20minimum. - maxPower = 334.0 - minPower = 90.0 - val node = ProcessingNode("NVidia", "TITAN X", "Pascal", 4992) - List(node.coreCount) { ProcessingUnit(node, it, 824.0) } + val cores = + machine.cpus.flatMap { id -> + when (id) { + 1 -> { + // ref: https://www.guru3d.com/articles-pages/nvidia-geforce-gtx-titan-x-review,8.html#:~:text=GeForce%20GTX%20Titan%20X%20%2D%20On,power%20supply%20unit%20as%20minimum. + maxPower = 334.0 + minPower = 90.0 + val node = ProcessingNode("NVidia", "TITAN X", "Pascal", 4992) + List(node.coreCount) { ProcessingUnit(node, it, 824.0) } + } + 2 -> { + // ref: https://www.microway.com/hpc-tech-tips/nvidia-tesla-p100-pci-e-16gb-gpu-accelerator-pascal-gp100-close/ + maxPower = 250.0 + minPower = 125.0 + val node = ProcessingNode("NVIDIA", "Tesla P100", "Pascal", 3584) + List(node.coreCount) { ProcessingUnit(node, it, 1190.0) } + } + 3 -> { + // ref: https://www.anandtech.com/show/10923/openpower-saga-tyans-1u-power8-gt75/7 + minPower = 84.0 + maxPower = 135.0 + val node = ProcessingNode("Intel", "E5-2690v3 Haswell24", "amd64", 24) + isGpuFlag = false + List(node.coreCount) { ProcessingUnit(node, it, 3498.0) } + } + 4 -> { + minPower = 130.0 + maxPower = 190.0 + val node = ProcessingNode("IBM", "POWER8", "RISC", 10) + isGpuFlag = false + List(node.coreCount) { ProcessingUnit(node, it, 143000.0) } // 28600.0 3690 + } + else -> throw IllegalArgumentException("The cpu id $id is not recognized") } - 2 -> { - // ref: https://www.microway.com/hpc-tech-tips/nvidia-tesla-p100-pci-e-16gb-gpu-accelerator-pascal-gp100-close/ - maxPower = 250.0 - minPower = 125.0 - val node = ProcessingNode("NVIDIA", "Tesla P100", "Pascal", 3584) - List(node.coreCount) { ProcessingUnit(node, it, 1190.0) } - } - 3 -> { - // ref: https://www.anandtech.com/show/10923/openpower-saga-tyans-1u-power8-gt75/7 - minPower = 84.0 - maxPower = 135.0 - val node = ProcessingNode("Intel", "E5-2690v3 Haswell24", "amd64", 24) - isGpuFlag = false - List(node.coreCount) { ProcessingUnit(node, it, 3498.0) } - } - 4 -> { - minPower = 130.0 - maxPower = 190.0 - val node = ProcessingNode("IBM", "POWER8", "RISC", 10) - isGpuFlag = false - List(node.coreCount) { ProcessingUnit(node, it, 143000.0) } // 28600.0 3690 - } - else -> throw IllegalArgumentException("The cpu id $id is not recognized") } - } - val memories = machine.memories.map { id -> - when (id) { - 1 -> MemoryUnit("NVidia", "GDDR5X", 480.0, 24L) - 2 -> MemoryUnit("NVidia", "GDDR5X", 720.0, 16L) - 3 -> MemoryUnit("IBM", "GDDR5X", 115.0, 160L) - 4 -> MemoryUnit("Inter", "GDDR5X", 68.0, 512L) - else -> throw IllegalArgumentException("The cpu id $id is not recognized") + val memories = + machine.memories.map { id -> + when (id) { + 1 -> MemoryUnit("NVidia", "GDDR5X", 480.0, 24L) + 2 -> MemoryUnit("NVidia", "GDDR5X", 720.0, 16L) + 3 -> MemoryUnit("IBM", "GDDR5X", 115.0, 160L) + 4 -> MemoryUnit("Inter", "GDDR5X", 68.0, 512L) + else -> throw IllegalArgumentException("The cpu id $id is not recognized") + } } - } MachineDef( UUID(0, counter.toLong()), "node-${counter++}", mapOf("gpu" to isGpuFlag), MachineModel(cores, memories), - CpuPowerModels.linear(maxPower, minPower) + CpuPowerModels.linear(maxPower, minPower), ) } } diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/util/MachineDef.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/util/MachineDef.kt index 6b72e1555..7ff917974 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/util/MachineDef.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/util/MachineDef.kt @@ -34,5 +34,5 @@ public data class MachineDef( val name: String, val meta: Map, val model: MachineModel, - val powerModel: CpuPowerModel + val powerModel: CpuPowerModel, ) diff --git a/opendc-experiments/opendc-experiments-tf20/src/test/kotlin/org/opendc/experiments/tf20/TensorFlowTest.kt b/opendc-experiments/opendc-experiments-tf20/src/test/kotlin/org/opendc/experiments/tf20/TensorFlowTest.kt index 899aafc0c..e38141759 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/test/kotlin/org/opendc/experiments/tf20/TensorFlowTest.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/test/kotlin/org/opendc/experiments/tf20/TensorFlowTest.kt @@ -41,114 +41,121 @@ class TensorFlowTest { * Smoke test that tests the capabilities of the TensorFlow application model in OpenDC. */ @Test - fun testSmokeAlexNet() = runSimulation { - val envInput = checkNotNull(TensorFlowTest::class.java.getResourceAsStream("/kth.json")) - val def = MLEnvironmentReader().readEnvironment(envInput).first() - - val device = SimTFDevice( - def.uid, - def.meta["gpu"] as Boolean, - dispatcher, - def.model.cpus[0], - def.model.memory[0], - CpuPowerModels.linear(250.0, 60.0) - ) - val strategy = OneDeviceStrategy(device) - val batchSize = 32 - val model = AlexNet(batchSize.toLong()) - model.use { - it.compile(strategy) - - it.fit(epochs = 9088 / batchSize, batchSize = batchSize) + fun testSmokeAlexNet() = + runSimulation { + val envInput = checkNotNull(TensorFlowTest::class.java.getResourceAsStream("/kth.json")) + val def = MLEnvironmentReader().readEnvironment(envInput).first() + + val device = + SimTFDevice( + def.uid, + def.meta["gpu"] as Boolean, + dispatcher, + def.model.cpus[0], + def.model.memory[0], + CpuPowerModels.linear(250.0, 60.0), + ) + val strategy = OneDeviceStrategy(device) + val batchSize = 32 + val model = getAlexNet(batchSize.toLong()) + model.use { + it.compile(strategy) + + it.fit(epochs = 9088 / batchSize, batchSize = batchSize) + } + + device.close() + + val stats = device.getDeviceStats() + assertAll( + { assertEquals(3309694252, timeSource.millis()) }, + { assertEquals(8.27423563E8, stats.energyUsage) }, + ) } - device.close() - - val stats = device.getDeviceStats() - assertAll( - { assertEquals(3309694252, timeSource.millis()) }, - { assertEquals(8.27423563E8, stats.energyUsage) } - ) - } - /** * Smoke test that tests the capabilities of the TensorFlow application model in OpenDC. */ @Test - fun testSmokeVGG() = runSimulation { - val envInput = checkNotNull(TensorFlowTest::class.java.getResourceAsStream("/kth.json")) - val def = MLEnvironmentReader().readEnvironment(envInput).first() - - val device = SimTFDevice( - def.uid, - def.meta["gpu"] as Boolean, - dispatcher, - def.model.cpus[0], - def.model.memory[0], - CpuPowerModels.linear(250.0, 60.0) - ) - val strategy = OneDeviceStrategy(device) - val batchSize = 128 - val model = VGG16(batchSize.toLong()) - model.use { - it.compile(strategy) - - it.fit(epochs = 9088 / batchSize, batchSize = batchSize) + fun testSmokeVGG() = + runSimulation { + val envInput = checkNotNull(TensorFlowTest::class.java.getResourceAsStream("/kth.json")) + val def = MLEnvironmentReader().readEnvironment(envInput).first() + + val device = + SimTFDevice( + def.uid, + def.meta["gpu"] as Boolean, + dispatcher, + def.model.cpus[0], + def.model.memory[0], + CpuPowerModels.linear(250.0, 60.0), + ) + val strategy = OneDeviceStrategy(device) + val batchSize = 128 + val model = getVGG16(batchSize.toLong()) + model.use { + it.compile(strategy) + + it.fit(epochs = 9088 / batchSize, batchSize = batchSize) + } + + device.close() + + val stats = device.getDeviceStats() + assertAll( + { assertEquals(176230328513, timeSource.millis()) }, + { assertEquals(4.405758212825E10, stats.energyUsage) }, + ) } - device.close() - - val stats = device.getDeviceStats() - assertAll( - { assertEquals(176230328513, timeSource.millis()) }, - { assertEquals(4.405758212825E10, stats.energyUsage) } - ) - } - /** * Smoke test that tests the capabilities of the TensorFlow application model in OpenDC. */ @Test - fun testSmokeDistribute() = runSimulation { - val envInput = checkNotNull(TensorFlowTest::class.java.getResourceAsStream("/kth.json")) - val def = MLEnvironmentReader().readEnvironment(envInput).first() - - val deviceA = SimTFDevice( - def.uid, - def.meta["gpu"] as Boolean, - dispatcher, - def.model.cpus[0], - def.model.memory[0], - CpuPowerModels.linear(250.0, 60.0) - ) - - val deviceB = SimTFDevice( - UUID.randomUUID(), - def.meta["gpu"] as Boolean, - dispatcher, - def.model.cpus[0], - def.model.memory[0], - CpuPowerModels.linear(250.0, 60.0) - ) - - val strategy = MirroredStrategy(listOf(deviceA, deviceB)) - val batchSize = 32 - val model = AlexNet(batchSize.toLong()) - model.use { - it.compile(strategy) - - it.fit(epochs = 9088 / batchSize, batchSize = batchSize) + fun testSmokeDistribute() = + runSimulation { + val envInput = checkNotNull(TensorFlowTest::class.java.getResourceAsStream("/kth.json")) + val def = MLEnvironmentReader().readEnvironment(envInput).first() + + val deviceA = + SimTFDevice( + def.uid, + def.meta["gpu"] as Boolean, + dispatcher, + def.model.cpus[0], + def.model.memory[0], + CpuPowerModels.linear(250.0, 60.0), + ) + + val deviceB = + SimTFDevice( + UUID.randomUUID(), + def.meta["gpu"] as Boolean, + dispatcher, + def.model.cpus[0], + def.model.memory[0], + CpuPowerModels.linear(250.0, 60.0), + ) + + val strategy = MirroredStrategy(listOf(deviceA, deviceB)) + val batchSize = 32 + val model = getAlexNet(batchSize.toLong()) + model.use { + it.compile(strategy) + + it.fit(epochs = 9088 / batchSize, batchSize = batchSize) + } + + deviceA.close() + deviceB.close() + + val statsA = deviceA.getDeviceStats() + val statsB = deviceB.getDeviceStats() + assertAll( + { assertEquals(1704994000, timeSource.millis()) }, + { assertEquals(4.262485E8, statsA.energyUsage) }, + { assertEquals(4.262485E8, statsB.energyUsage) }, + ) } - - deviceA.close() - deviceB.close() - - val statsA = deviceA.getDeviceStats() - val statsB = deviceB.getDeviceStats() - assertAll( - { assertEquals(1704994000, timeSource.millis()) }, - { assertEquals(4.262485E8, statsA.energyUsage) }, - { assertEquals(4.262485E8, statsB.energyUsage) } - ) - } } diff --git a/opendc-experiments/opendc-experiments-tf20/src/test/kotlin/org/opendc/experiments/tf20/core/SimTFDeviceTest.kt b/opendc-experiments/opendc-experiments-tf20/src/test/kotlin/org/opendc/experiments/tf20/core/SimTFDeviceTest.kt index 549c6f3e1..764738684 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/test/kotlin/org/opendc/experiments/tf20/core/SimTFDeviceTest.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/test/kotlin/org/opendc/experiments/tf20/core/SimTFDeviceTest.kt @@ -39,36 +39,38 @@ import java.util.UUID */ internal class SimTFDeviceTest { @Test - fun testSmoke() = runSimulation { - val puNode = ProcessingNode("NVIDIA", "Tesla V100", "unknown", 1) - val pu = ProcessingUnit(puNode, 0, 960 * 1230.0) - val memory = MemoryUnit("NVIDIA", "Tesla V100", 877.0, 32_000) + fun testSmoke() = + runSimulation { + val puNode = ProcessingNode("NVIDIA", "Tesla V100", "unknown", 1) + val pu = ProcessingUnit(puNode, 0, 960 * 1230.0) + val memory = MemoryUnit("NVIDIA", "Tesla V100", 877.0, 32_000) - val device = SimTFDevice( - UUID.randomUUID(), - isGpu = true, - dispatcher, - pu, - memory, - CpuPowerModels.linear(250.0, 100.0) - ) + val device = + SimTFDevice( + UUID.randomUUID(), + isGpu = true, + dispatcher, + pu, + memory, + CpuPowerModels.linear(250.0, 100.0), + ) - // Load 1 GiB into GPU memory - device.load(1000) - assertEquals(1140, timeSource.millis()) + // Load 1 GiB into GPU memory + device.load(1000) + assertEquals(1140, timeSource.millis()) - coroutineScope { - launch { device.compute(1e6) } - launch { device.compute(2e6) } - } + coroutineScope { + launch { device.compute(1e6) } + launch { device.compute(2e6) } + } - device.close() + device.close() - val stats = device.getDeviceStats() + val stats = device.getDeviceStats() - assertAll( - { assertEquals(3681, timeSource.millis()) }, - { assertEquals(749.25, stats.energyUsage) } - ) - } + assertAll( + { assertEquals(3681, timeSource.millis()) }, + { assertEquals(749.25, stats.energyUsage) }, + ) + } } diff --git a/opendc-experiments/opendc-experiments-workflow/build.gradle.kts b/opendc-experiments/opendc-experiments-workflow/build.gradle.kts index a5a2ea54d..ff5144c56 100644 --- a/opendc-experiments/opendc-experiments-workflow/build.gradle.kts +++ b/opendc-experiments/opendc-experiments-workflow/build.gradle.kts @@ -22,7 +22,7 @@ description = "Support library for simulating workflows with OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` `testing-conventions` diff --git a/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/TraceHelpers.kt b/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/TraceHelpers.kt index 2037dad4b..e396901c4 100644 --- a/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/TraceHelpers.kt +++ b/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/TraceHelpers.kt @@ -66,30 +66,35 @@ public fun Trace.toJobs(): List { val workflow = jobs.computeIfAbsent(workflowId) { id -> Job(UUID(0L, id), "", HashSet(), HashMap()) } val id = reader.getString(TASK_ID)!!.toLong() - val grantedCpus = if (reader.resolve(TASK_ALLOC_NCPUS) != 0) { - reader.getInt(TASK_ALLOC_NCPUS) - } else { - reader.getInt(TASK_REQ_NCPUS) - } + val grantedCpus = + if (reader.resolve(TASK_ALLOC_NCPUS) != 0) { + reader.getInt(TASK_ALLOC_NCPUS) + } else { + reader.getInt(TASK_REQ_NCPUS) + } val submitTime = reader.getInstant(TASK_SUBMIT_TIME)!! val runtime = reader.getDuration(TASK_RUNTIME)!! val flops: Long = 4000 * runtime.seconds * grantedCpus val workload = SimWorkloads.flops(flops, 1.0) - val task = Task( - UUID(0L, id), - "", - HashSet(), - mapOf( - "workload" to workload, - WORKFLOW_TASK_CORES to grantedCpus, - WORKFLOW_TASK_DEADLINE to runtime.toMillis() + val task = + Task( + UUID(0L, id), + "", + HashSet(), + mapOf( + "workload" to workload, + WORKFLOW_TASK_CORES to grantedCpus, + WORKFLOW_TASK_DEADLINE to runtime.toMillis(), + ), ) - ) tasks[id] = task taskDependencies[task] = reader.getSet(TASK_PARENTS, String::class.java)!!.map { it.toLong() }.toSet() - (workflow.metadata as MutableMap).merge("WORKFLOW_SUBMIT_TIME", submitTime.toEpochMilli()) { a, b -> min(a as Long, b as Long) } + (workflow.metadata as MutableMap).merge( + "WORKFLOW_SUBMIT_TIME", + submitTime.toEpochMilli(), + ) { a, b -> min(a as Long, b as Long) } (workflow.tasks as MutableSet).add(task) } @@ -110,7 +115,10 @@ public fun Trace.toJobs(): List { /** * Helper method to replay the specified list of [jobs] and suspend execution util all jobs have finished. */ -public suspend fun WorkflowService.replay(clock: InstantSource, jobs: List) { +public suspend fun WorkflowService.replay( + clock: InstantSource, + jobs: List, +) { // Sort jobs by their arrival time val orderedJobs = jobs.sortedBy { it.metadata.getOrDefault("WORKFLOW_SUBMIT_TIME", Long.MAX_VALUE) as Long } if (orderedJobs.isEmpty()) { diff --git a/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/WorkflowSchedulerSpec.kt b/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/WorkflowSchedulerSpec.kt index 8bd087e7a..cb8056a71 100644 --- a/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/WorkflowSchedulerSpec.kt +++ b/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/WorkflowSchedulerSpec.kt @@ -36,5 +36,5 @@ public data class WorkflowSchedulerSpec( val jobAdmissionPolicy: JobAdmissionPolicy, val jobOrderPolicy: JobOrderPolicy, val taskEligibilityPolicy: TaskEligibilityPolicy, - val taskOrderPolicy: TaskOrderPolicy + val taskOrderPolicy: TaskOrderPolicy, ) diff --git a/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/WorkflowServiceProvisioningStep.kt b/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/WorkflowServiceProvisioningStep.kt index 862ebf3de..af2a48719 100644 --- a/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/WorkflowServiceProvisioningStep.kt +++ b/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/WorkflowServiceProvisioningStep.kt @@ -40,21 +40,25 @@ public class WorkflowServiceProvisioningStep internal constructor( private val serviceDomain: String, private val computeService: String, private val scheduler: WorkflowSchedulerSpec, - private val schedulingQuantum: Duration + private val schedulingQuantum: Duration, ) : ProvisioningStep { override fun apply(ctx: ProvisioningContext): AutoCloseable { - val computeService = requireNotNull(ctx.registry.resolve(computeService, ComputeService::class.java)) { "Compute service $computeService does not exist" } + val computeService = + requireNotNull( + ctx.registry.resolve(computeService, ComputeService::class.java), + ) { "Compute service $computeService does not exist" } val client = computeService.newClient() - val service = WorkflowService( - ctx.dispatcher, - client, - scheduler.schedulingQuantum, - jobAdmissionPolicy = scheduler.jobAdmissionPolicy, - jobOrderPolicy = scheduler.jobOrderPolicy, - taskEligibilityPolicy = scheduler.taskEligibilityPolicy, - taskOrderPolicy = scheduler.taskOrderPolicy - ) + val service = + WorkflowService( + ctx.dispatcher, + client, + scheduler.schedulingQuantum, + jobAdmissionPolicy = scheduler.jobAdmissionPolicy, + jobOrderPolicy = scheduler.jobOrderPolicy, + taskEligibilityPolicy = scheduler.taskEligibilityPolicy, + taskOrderPolicy = scheduler.taskOrderPolicy, + ) ctx.registry.register(serviceDomain, WorkflowService::class.java, service) return AutoCloseable { diff --git a/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/WorkflowSteps.kt b/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/WorkflowSteps.kt index efcbf8896..bfcf3734e 100644 --- a/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/WorkflowSteps.kt +++ b/opendc-experiments/opendc-experiments-workflow/src/main/kotlin/org/opendc/experiments/workflow/WorkflowSteps.kt @@ -35,7 +35,7 @@ public fun setupWorkflowService( serviceDomain: String, computeService: String, scheduler: WorkflowSchedulerSpec, - schedulingQuantum: Duration = Duration.ofMinutes(5) + schedulingQuantum: Duration = Duration.ofMinutes(5), ): ProvisioningStep { return WorkflowServiceProvisioningStep(serviceDomain, computeService, scheduler, schedulingQuantum) } diff --git a/opendc-faas/opendc-faas-api/build.gradle.kts b/opendc-faas/opendc-faas-api/build.gradle.kts index 8a295acd1..2e4b5776c 100644 --- a/opendc-faas/opendc-faas-api/build.gradle.kts +++ b/opendc-faas/opendc-faas-api/build.gradle.kts @@ -22,7 +22,7 @@ description = "API for the OpenDC FaaS platform" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-faas/opendc-faas-api/src/main/kotlin/org/opendc/faas/api/FaaSClient.kt b/opendc-faas/opendc-faas-api/src/main/kotlin/org/opendc/faas/api/FaaSClient.kt index d3abb7f18..297d30651 100644 --- a/opendc-faas/opendc-faas-api/src/main/kotlin/org/opendc/faas/api/FaaSClient.kt +++ b/opendc-faas/opendc-faas-api/src/main/kotlin/org/opendc/faas/api/FaaSClient.kt @@ -59,7 +59,7 @@ public interface FaaSClient : AutoCloseable { name: String, memorySize: Long, labels: Map = emptyMap(), - meta: Map = emptyMap() + meta: Map = emptyMap(), ): FaaSFunction /** diff --git a/opendc-faas/opendc-faas-service/build.gradle.kts b/opendc-faas/opendc-faas-service/build.gradle.kts index 8b3719981..90cb8f56d 100644 --- a/opendc-faas/opendc-faas-service/build.gradle.kts +++ b/opendc-faas/opendc-faas-service/build.gradle.kts @@ -22,7 +22,7 @@ description = "FaaS service for OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/FaaSService.kt b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/FaaSService.kt index 96619cdb5..e9634ccc7 100644 --- a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/FaaSService.kt +++ b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/FaaSService.kt @@ -72,7 +72,7 @@ public interface FaaSService : AutoCloseable { deployer: FunctionDeployer, routingPolicy: RoutingPolicy, terminationPolicy: FunctionTerminationPolicy, - quantum: Duration = Duration.ofMillis(100) + quantum: Duration = Duration.ofMillis(100), ): FaaSService { return FaaSServiceImpl(dispatcher, deployer, routingPolicy, terminationPolicy, quantum) } diff --git a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/FunctionObject.kt b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/FunctionObject.kt index 091e82a8c..0ed96b966 100644 --- a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/FunctionObject.kt +++ b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/FunctionObject.kt @@ -35,21 +35,23 @@ public class FunctionObject( name: String, allocatedMemory: Long, labels: Map, - meta: Map + meta: Map, ) : AutoCloseable { /** * Metrics tracked per function. */ - private var _invocations = 0L - private var _timelyInvocations = 0L - private var _delayedInvocations = 0L - private var _failedInvocations = 0L - private var _activeInstances = 0 - private var _idleInstances = 0 - private val _waitTime = DescriptiveStatistics() - .apply { windowSize = 100 } - private val _activeTime = DescriptiveStatistics() - .apply { windowSize = 100 } + private var localInvocations = 0L + private var localTimelyInvocations = 0L + private var localDelayedInvocations = 0L + private var localFailedInvocations = 0L + private var localActiveInstances = 0 + private var localIdleInstances = 0 + private val localWaitTime = + DescriptiveStatistics() + .apply { windowSize = 100 } + private val localActiveTime = + DescriptiveStatistics() + .apply { windowSize = 100 } /** * The instances associated with this function. @@ -70,7 +72,7 @@ public class FunctionObject( * Report a scheduled invocation. */ internal fun reportSubmission() { - _invocations++ + localInvocations++ } /** @@ -78,38 +80,41 @@ public class FunctionObject( */ internal fun reportDeployment(isDelayed: Boolean) { if (isDelayed) { - _delayedInvocations++ - _idleInstances++ + localDelayedInvocations++ + localIdleInstances++ } else { - _timelyInvocations++ + localTimelyInvocations++ } } /** * Report the start of a function invocation. */ - internal fun reportStart(start: Long, submitTime: Long) { + internal fun reportStart( + start: Long, + submitTime: Long, + ) { val wait = start - submitTime - _waitTime.addValue(wait.toDouble()) + localWaitTime.addValue(wait.toDouble()) - _idleInstances-- - _activeInstances++ + localIdleInstances-- + localActiveInstances++ } /** * Report the failure of a function invocation. */ internal fun reportFailure() { - _failedInvocations++ + localFailedInvocations++ } /** * Report the end of a function invocation. */ internal fun reportEnd(duration: Long) { - _activeTime.addValue(duration.toDouble()) - _idleInstances++ - _activeInstances-- + localActiveTime.addValue(duration.toDouble()) + localIdleInstances++ + localActiveInstances-- } /** @@ -117,14 +122,14 @@ public class FunctionObject( */ internal fun getStats(): FunctionStats { return FunctionStats( - _invocations, - _timelyInvocations, - _delayedInvocations, - _failedInvocations, - _activeInstances, - _idleInstances, - _waitTime.copy(), - _activeTime.copy() + localInvocations, + localTimelyInvocations, + localDelayedInvocations, + localFailedInvocations, + localActiveInstances, + localIdleInstances, + localWaitTime.copy(), + localActiveTime.copy(), ) } diff --git a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/autoscaler/FunctionTerminationPolicyFixed.kt b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/autoscaler/FunctionTerminationPolicyFixed.kt index a2c371e16..9edb8c1d5 100644 --- a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/autoscaler/FunctionTerminationPolicyFixed.kt +++ b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/autoscaler/FunctionTerminationPolicyFixed.kt @@ -35,7 +35,7 @@ import java.time.Duration */ public class FunctionTerminationPolicyFixed( dispatcher: Dispatcher, - public val timeout: Duration + public val timeout: Duration, ) : FunctionTerminationPolicy { /** * The [TimerScheduler] used to schedule the function terminations. @@ -47,7 +47,10 @@ public class FunctionTerminationPolicyFixed( scheduler.cancel(instance) } - override fun onStateChanged(instance: FunctionInstance, newState: FunctionInstanceState) { + override fun onStateChanged( + instance: FunctionInstance, + newState: FunctionInstanceState, + ) { when (newState) { FunctionInstanceState.Active -> scheduler.cancel(instance) FunctionInstanceState.Idle -> schedule(instance) diff --git a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/deployer/FunctionDeployer.kt b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/deployer/FunctionDeployer.kt index 18d16d069..13d48fbfc 100644 --- a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/deployer/FunctionDeployer.kt +++ b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/deployer/FunctionDeployer.kt @@ -39,5 +39,8 @@ public interface FunctionDeployer { /** * Deploy the specified [function]. */ - public fun deploy(function: FunctionObject, listener: FunctionInstanceListener): FunctionInstance + public fun deploy( + function: FunctionObject, + listener: FunctionInstanceListener, + ): FunctionInstance } diff --git a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/deployer/FunctionInstanceListener.kt b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/deployer/FunctionInstanceListener.kt index 20e280a2d..e88b71042 100644 --- a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/deployer/FunctionInstanceListener.kt +++ b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/deployer/FunctionInstanceListener.kt @@ -29,5 +29,8 @@ public interface FunctionInstanceListener { /** * This method is invoked when the state of a [FunctionInstance] has changed. */ - public fun onStateChanged(instance: FunctionInstance, newState: FunctionInstanceState) {} + public fun onStateChanged( + instance: FunctionInstance, + newState: FunctionInstanceState, + ) {} } diff --git a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/deployer/FunctionInstanceState.kt b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/deployer/FunctionInstanceState.kt index 2b6b6eba6..0c310e6bb 100644 --- a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/deployer/FunctionInstanceState.kt +++ b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/deployer/FunctionInstanceState.kt @@ -44,5 +44,5 @@ public enum class FunctionInstanceState { /** * The function instance is released and cannot be used anymore. */ - Deleted + Deleted, } diff --git a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/internal/FaaSFunctionImpl.kt b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/internal/FaaSFunctionImpl.kt index 36532aa83..7cc85e40e 100644 --- a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/internal/FaaSFunctionImpl.kt +++ b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/internal/FaaSFunctionImpl.kt @@ -31,7 +31,7 @@ import java.util.UUID */ internal class FaaSFunctionImpl( private val service: FaaSServiceImpl, - private val state: FunctionObject + private val state: FunctionObject, ) : FaaSFunction { override val uid: UUID = state.uid diff --git a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/internal/FaaSServiceImpl.kt b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/internal/FaaSServiceImpl.kt index b1e6b3f51..397b0e7de 100644 --- a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/internal/FaaSServiceImpl.kt +++ b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/internal/FaaSServiceImpl.kt @@ -61,7 +61,7 @@ internal class FaaSServiceImpl( private val deployer: FunctionDeployer, private val routingPolicy: RoutingPolicy, private val terminationPolicy: FunctionTerminationPolicy, - quantum: Duration + quantum: Duration, ) : FaaSService, FunctionInstanceListener { /** * The logger instance of this server. @@ -134,19 +134,20 @@ internal class FaaSServiceImpl( name: String, memorySize: Long, labels: Map, - meta: Map + meta: Map, ): FaaSFunction { check(!isClosed) { "Client is already closed" } require(name !in functionsByName) { "Function with same name exists" } val uid = UUID(clock.millis(), random.nextLong()) - val function = FunctionObject( - uid, - name, - memorySize, - labels, - meta - ) + val function = + FunctionObject( + uid, + name, + memorySize, + labels, + meta, + ) functionsByName[name] = function functions[uid] = function @@ -200,27 +201,29 @@ internal class FaaSServiceImpl( val instances = function.instances // Check if there exists an instance of the function - val activeInstance = if (instances.isNotEmpty()) { - routingPolicy.select(instances, function) - } else { - null - } + val activeInstance = + if (instances.isNotEmpty()) { + routingPolicy.select(instances, function) + } else { + null + } - val instance = if (activeInstance != null) { - timelyInvocations++ - function.reportDeployment(isDelayed = false) + val instance = + if (activeInstance != null) { + timelyInvocations++ + function.reportDeployment(isDelayed = false) - activeInstance - } else { - val instance = deployer.deploy(function, this) - instances.add(instance) - terminationPolicy.enqueue(instance) + activeInstance + } else { + val instance = deployer.deploy(function, this) + instances.add(instance) + terminationPolicy.enqueue(instance) - delayedInvocations++ - function.reportDeployment(isDelayed = true) + delayedInvocations++ + function.reportDeployment(isDelayed = true) - instance - } + instance + } suspend { val start = clock.millis() @@ -268,7 +271,10 @@ internal class FaaSServiceImpl( } } - override fun onStateChanged(instance: FunctionInstance, newState: FunctionInstanceState) { + override fun onStateChanged( + instance: FunctionInstance, + newState: FunctionInstanceState, + ) { terminationPolicy.onStateChanged(instance, newState) if (newState == FunctionInstanceState.Deleted) { diff --git a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/router/RandomRoutingPolicy.kt b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/router/RandomRoutingPolicy.kt index 22bf72661..1eb03e5ad 100644 --- a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/router/RandomRoutingPolicy.kt +++ b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/router/RandomRoutingPolicy.kt @@ -31,7 +31,10 @@ import java.util.random.RandomGenerator * A [RoutingPolicy] that selects a random function instance. */ public class RandomRoutingPolicy(private val random: RandomGenerator = SplittableRandom(0)) : RoutingPolicy { - override fun select(instances: List, function: FunctionObject): FunctionInstance { + override fun select( + instances: List, + function: FunctionObject, + ): FunctionInstance { val idx = random.nextInt(instances.size) return instances.elementAt(idx) } diff --git a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/router/RoutingPolicy.kt b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/router/RoutingPolicy.kt index e99e329a8..c8ea37fc5 100644 --- a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/router/RoutingPolicy.kt +++ b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/router/RoutingPolicy.kt @@ -32,5 +32,8 @@ public interface RoutingPolicy { /** * Select the instance to which the request should be routed to. */ - public fun select(instances: List, function: FunctionObject): FunctionInstance? + public fun select( + instances: List, + function: FunctionObject, + ): FunctionInstance? } diff --git a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/telemetry/FunctionStats.kt b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/telemetry/FunctionStats.kt index 497ee4234..db6db6c13 100644 --- a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/telemetry/FunctionStats.kt +++ b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/telemetry/FunctionStats.kt @@ -44,5 +44,5 @@ public data class FunctionStats( val activeInstances: Int, val idleInstances: Int, val waitTime: DescriptiveStatistics, - val activeTime: DescriptiveStatistics + val activeTime: DescriptiveStatistics, ) diff --git a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/telemetry/SchedulerStats.kt b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/telemetry/SchedulerStats.kt index cabb1d562..b65dfb031 100644 --- a/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/telemetry/SchedulerStats.kt +++ b/opendc-faas/opendc-faas-service/src/main/kotlin/org/opendc/faas/service/telemetry/SchedulerStats.kt @@ -32,5 +32,5 @@ package org.opendc.faas.service.telemetry public data class SchedulerStats( val totalInvocations: Long, val timelyInvocations: Long, - val delayedInvocations: Long + val delayedInvocations: Long, ) diff --git a/opendc-faas/opendc-faas-service/src/test/kotlin/org/opendc/faas/service/FaaSServiceTest.kt b/opendc-faas/opendc-faas-service/src/test/kotlin/org/opendc/faas/service/FaaSServiceTest.kt index 9676744b3..72a5f2c88 100644 --- a/opendc-faas/opendc-faas-service/src/test/kotlin/org/opendc/faas/service/FaaSServiceTest.kt +++ b/opendc-faas/opendc-faas-service/src/test/kotlin/org/opendc/faas/service/FaaSServiceTest.kt @@ -41,136 +41,145 @@ import java.util.UUID * Test suite for the [FaaSService] implementation. */ internal class FaaSServiceTest { - @Test - fun testClientState() = runSimulation { - val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) - - val client = assertDoesNotThrow { service.newClient() } - assertDoesNotThrow { client.close() } - - assertThrows { client.queryFunctions() } - assertThrows { client.newFunction("test", 128) } - assertThrows { client.invoke("test") } - assertThrows { client.findFunction(UUID.randomUUID()) } - assertThrows { client.findFunction("name") } - } + fun testClientState() = + runSimulation { + val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) + + val client = assertDoesNotThrow { service.newClient() } + assertDoesNotThrow { client.close() } + + assertThrows { client.queryFunctions() } + assertThrows { client.newFunction("test", 128) } + assertThrows { client.invoke("test") } + assertThrows { client.findFunction(UUID.randomUUID()) } + assertThrows { client.findFunction("name") } + } @Test - fun testClientInvokeUnknown() = runSimulation { - val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) + fun testClientInvokeUnknown() = + runSimulation { + val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) - val client = service.newClient() + val client = service.newClient() - assertThrows { client.invoke("test") } - } + assertThrows { client.invoke("test") } + } @Test - fun testClientFunctionCreation() = runSimulation { - val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) + fun testClientFunctionCreation() = + runSimulation { + val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) - val client = service.newClient() + val client = service.newClient() - val function = client.newFunction("test", 128) + val function = client.newFunction("test", 128) - assertEquals("test", function.name) - } + assertEquals("test", function.name) + } @Test - fun testClientFunctionQuery() = runSimulation { - val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) + fun testClientFunctionQuery() = + runSimulation { + val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) - val client = service.newClient() + val client = service.newClient() - assertEquals(emptyList(), client.queryFunctions()) + assertEquals(emptyList(), client.queryFunctions()) - val function = client.newFunction("test", 128) + val function = client.newFunction("test", 128) - assertEquals(listOf(function), client.queryFunctions()) - } + assertEquals(listOf(function), client.queryFunctions()) + } @Test - fun testClientFunctionFindById() = runSimulation { - val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) + fun testClientFunctionFindById() = + runSimulation { + val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) - val client = service.newClient() + val client = service.newClient() - assertEquals(emptyList(), client.queryFunctions()) + assertEquals(emptyList(), client.queryFunctions()) - val function = client.newFunction("test", 128) + val function = client.newFunction("test", 128) - assertNotNull(client.findFunction(function.uid)) - } + assertNotNull(client.findFunction(function.uid)) + } @Test - fun testClientFunctionFindByName() = runSimulation { - val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) + fun testClientFunctionFindByName() = + runSimulation { + val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) - val client = service.newClient() + val client = service.newClient() - assertEquals(emptyList(), client.queryFunctions()) + assertEquals(emptyList(), client.queryFunctions()) - val function = client.newFunction("test", 128) + val function = client.newFunction("test", 128) - assertNotNull(client.findFunction(function.name)) - } + assertNotNull(client.findFunction(function.name)) + } @Test - fun testClientFunctionDuplicateName() = runSimulation { - val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) + fun testClientFunctionDuplicateName() = + runSimulation { + val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) - val client = service.newClient() + val client = service.newClient() - client.newFunction("test", 128) + client.newFunction("test", 128) - assertThrows { client.newFunction("test", 128) } - } + assertThrows { client.newFunction("test", 128) } + } @Test - fun testClientFunctionDelete() = runSimulation { - val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) - - val client = service.newClient() - val function = client.newFunction("test", 128) - assertNotNull(client.findFunction(function.uid)) - function.delete() - assertNull(client.findFunction(function.uid)) - - // Delete should be idempotent - function.delete() - } + fun testClientFunctionDelete() = + runSimulation { + val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) + + val client = service.newClient() + val function = client.newFunction("test", 128) + assertNotNull(client.findFunction(function.uid)) + function.delete() + assertNull(client.findFunction(function.uid)) + + // Delete should be idempotent + function.delete() + } @Test - fun testClientFunctionCannotInvokeDeleted() = runSimulation { - val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) + fun testClientFunctionCannotInvokeDeleted() = + runSimulation { + val service = FaaSService(dispatcher, mockk(), mockk(), mockk()) - val client = service.newClient() - val function = client.newFunction("test", 128) - assertNotNull(client.findFunction(function.uid)) - function.delete() + val client = service.newClient() + val function = client.newFunction("test", 128) + assertNotNull(client.findFunction(function.uid)) + function.delete() - assertThrows { function.invoke() } - } + assertThrows { function.invoke() } + } @Test - fun testClientFunctionInvoke() = runSimulation { - val deployer = mockk() - val service = FaaSService(dispatcher, deployer, mockk(), mockk(relaxUnitFun = true)) + fun testClientFunctionInvoke() = + runSimulation { + val deployer = mockk() + val service = FaaSService(dispatcher, deployer, mockk(), mockk(relaxUnitFun = true)) - every { deployer.deploy(any(), any()) } answers { - object : FunctionInstance { - override val state: FunctionInstanceState = FunctionInstanceState.Idle - override val function: FunctionObject = it.invocation.args[0] as FunctionObject + every { deployer.deploy(any(), any()) } answers { + object : FunctionInstance { + override val state: FunctionInstanceState = FunctionInstanceState.Idle + override val function: FunctionObject = it.invocation.args[0] as FunctionObject - override suspend fun invoke() {} + override suspend fun invoke() {} - override fun close() {} + override fun close() {} + } } - } - val client = service.newClient() - val function = client.newFunction("test", 128) + val client = service.newClient() + val function = client.newFunction("test", 128) - function.invoke() - } + function.invoke() + } } diff --git a/opendc-faas/opendc-faas-simulator/build.gradle.kts b/opendc-faas/opendc-faas-simulator/build.gradle.kts index 5f8c86671..203743240 100644 --- a/opendc-faas/opendc-faas-simulator/build.gradle.kts +++ b/opendc-faas/opendc-faas-simulator/build.gradle.kts @@ -22,7 +22,7 @@ description = "Simulator for the OpenDC FaaS platform" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-faas/opendc-faas-simulator/src/main/kotlin/org/opendc/faas/simulator/SimFunctionDeployer.kt b/opendc-faas/opendc-faas-simulator/src/main/kotlin/org/opendc/faas/simulator/SimFunctionDeployer.kt index 47b4d4fad..c81dc5231 100644 --- a/opendc-faas/opendc-faas-simulator/src/main/kotlin/org/opendc/faas/simulator/SimFunctionDeployer.kt +++ b/opendc-faas/opendc-faas-simulator/src/main/kotlin/org/opendc/faas/simulator/SimFunctionDeployer.kt @@ -58,14 +58,17 @@ public class SimFunctionDeployer( private val dispatcher: Dispatcher, private val model: MachineModel, private val delayInjector: DelayInjector, - private val mapper: SimFaaSWorkloadMapper = SimMetaFaaSWorkloadMapper() + private val mapper: SimFaaSWorkloadMapper = SimMetaFaaSWorkloadMapper(), ) : FunctionDeployer, AutoCloseable { /** * The [CoroutineScope] of this deployer. */ private val scope = CoroutineScope(dispatcher.asCoroutineDispatcher() + Job()) - override fun deploy(function: FunctionObject, listener: FunctionInstanceListener): Instance { + override fun deploy( + function: FunctionObject, + listener: FunctionInstanceListener, + ): Instance { val instance = Instance(function, listener) instance.start() return instance @@ -84,10 +87,11 @@ public class SimFunctionDeployer( /** * The machine that will execute the workloads. */ - public val machine: SimMachine = SimBareMetalMachine.create( - FlowEngine.create(dispatcher).newGraph(), - model - ) + public val machine: SimMachine = + SimBareMetalMachine.create( + FlowEngine.create(dispatcher).newGraph(), + model, + ) /** * The job associated with the lifecycle of the instance. @@ -134,38 +138,39 @@ public class SimFunctionDeployer( */ internal fun start() { check(state == FunctionInstanceState.Provisioning) { "Invalid state of function instance" } - job = scope.launch { - delay(delayInjector.getColdStartDelay(this@Instance)) - - launch { - try { - machine.runWorkload(workload) - } finally { - state = FunctionInstanceState.Deleted - } - } + job = + scope.launch { + delay(delayInjector.getColdStartDelay(this@Instance)) - while (isActive) { - if (queue.isEmpty()) { - chan.receive() + launch { + try { + machine.runWorkload(workload) + } finally { + state = FunctionInstanceState.Deleted + } } - state = FunctionInstanceState.Active - while (queue.isNotEmpty()) { - val request = queue.poll() - try { - workload.invoke() - request.cont.resume(Unit) - } catch (cause: CancellationException) { - request.cont.resumeWithException(cause) - throw cause - } catch (cause: Throwable) { - request.cont.resumeWithException(cause) + while (isActive) { + if (queue.isEmpty()) { + chan.receive() + } + + state = FunctionInstanceState.Active + while (queue.isNotEmpty()) { + val request = queue.poll() + try { + workload.invoke() + request.cont.resume(Unit) + } catch (cause: CancellationException) { + request.cont.resumeWithException(cause) + throw cause + } catch (cause: Throwable) { + request.cont.resumeWithException(cause) + } } + state = FunctionInstanceState.Idle } - state = FunctionInstanceState.Idle } - } } /** diff --git a/opendc-faas/opendc-faas-simulator/src/main/kotlin/org/opendc/faas/simulator/delay/ColdStartModel.kt b/opendc-faas/opendc-faas-simulator/src/main/kotlin/org/opendc/faas/simulator/delay/ColdStartModel.kt index 624067bef..f5035ca26 100644 --- a/opendc-faas/opendc-faas-simulator/src/main/kotlin/org/opendc/faas/simulator/delay/ColdStartModel.kt +++ b/opendc-faas/opendc-faas-simulator/src/main/kotlin/org/opendc/faas/simulator/delay/ColdStartModel.kt @@ -60,7 +60,7 @@ public enum class ColdStartModel { else -> Pair(0.0, 1.0) } } - }; + }, ; /** * Obtain the stochastic parameters for the cold start models. diff --git a/opendc-faas/opendc-faas-simulator/src/test/kotlin/org/opendc/faas/simulator/SimFaaSServiceTest.kt b/opendc-faas/opendc-faas-simulator/src/test/kotlin/org/opendc/faas/simulator/SimFaaSServiceTest.kt index ee9114b5d..f68860e36 100644 --- a/opendc-faas/opendc-faas-simulator/src/test/kotlin/org/opendc/faas/simulator/SimFaaSServiceTest.kt +++ b/opendc-faas/opendc-faas-simulator/src/test/kotlin/org/opendc/faas/simulator/SimFaaSServiceTest.kt @@ -50,57 +50,63 @@ import java.util.Random * A test suite for the [FaaSService] implementation under simulated conditions. */ internal class SimFaaSServiceTest { - private lateinit var machineModel: MachineModel @BeforeEach fun setUp() { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) - machineModel = MachineModel( - /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, - /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } - ) + machineModel = + MachineModel( + List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, + List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, + ) } @Test - fun testSmoke() = runSimulation { - val random = Random(0) - val workload = spyk(object : SimFaaSWorkload, SimWorkload by SimWorkloads.runtime(1000, 1.0) { - override suspend fun invoke() { - delay(random.nextInt(1000).toLong()) - } - }) + fun testSmoke() = + runSimulation { + val random = Random(0) + val workload = + spyk( + object : SimFaaSWorkload, SimWorkload by SimWorkloads.runtime(1000, 1.0) { + override suspend fun invoke() { + delay(random.nextInt(1000).toLong()) + } + }, + ) - val delayInjector = StochasticDelayInjector(ColdStartModel.GOOGLE, random) - val deployer = SimFunctionDeployer(dispatcher, machineModel, delayInjector) { workload } - val service = FaaSService( - dispatcher, - deployer, - RandomRoutingPolicy(), - FunctionTerminationPolicyFixed(dispatcher, timeout = Duration.ofMillis(10000)) - ) + val delayInjector = StochasticDelayInjector(ColdStartModel.GOOGLE, random) + val deployer = SimFunctionDeployer(dispatcher, machineModel, delayInjector) { workload } + val service = + FaaSService( + dispatcher, + deployer, + RandomRoutingPolicy(), + FunctionTerminationPolicyFixed(dispatcher, timeout = Duration.ofMillis(10000)), + ) - val client = service.newClient() + val client = service.newClient() - val function = client.newFunction("test", 128) - function.invoke() - delay(2000) + val function = client.newFunction("test", 128) + function.invoke() + delay(2000) - service.close() - deployer.close() + service.close() + deployer.close() - yield() + yield() - val funcStats = service.getFunctionStats(function) + val funcStats = service.getFunctionStats(function) - assertAll( - { coVerify { workload.invoke() } }, - { assertEquals(1, funcStats.totalInvocations) }, - { assertEquals(1, funcStats.delayedInvocations) }, - { assertEquals(0, funcStats.failedInvocations) }, - { assertEquals(0.0, funcStats.waitTime.mean) }, // fixme: this is probably wrong, and should be 100 - { assertEquals(1285.0, funcStats.activeTime.mean) } - ) - } + // fixme: waitTime is probably wrong, and should be 100 + assertAll( + { coVerify { workload.invoke() } }, + { assertEquals(1, funcStats.totalInvocations) }, + { assertEquals(1, funcStats.delayedInvocations) }, + { assertEquals(0, funcStats.failedInvocations) }, + { assertEquals(0.0, funcStats.waitTime.mean) }, + { assertEquals(1285.0, funcStats.activeTime.mean) }, + ) + } } diff --git a/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt b/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt index eea46b950..3707b6019 100644 --- a/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt +++ b/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt @@ -56,10 +56,13 @@ class SimMachineBenchmarks { fun setUp() { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) - machineModel = MachineModel( - /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, - /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } - ) + machineModel = + MachineModel( + // cpus + List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, + // memory + List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, + ) val random = ThreadLocalRandom.current() val builder = SimTrace.builder() diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/Coroutines.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/Coroutines.kt index b354caffc..63af20489 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/Coroutines.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/Coroutines.kt @@ -35,7 +35,10 @@ import kotlin.coroutines.resumeWithException * @return A [SimMachineContext] that represents the execution context for the workload. * @throws IllegalStateException if a workload is already active on the machine or if the machine is closed. */ -public suspend fun SimMachine.runWorkload(workload: SimWorkload, meta: Map = emptyMap()) { +public suspend fun SimMachine.runWorkload( + workload: SimWorkload, + meta: Map = emptyMap(), +) { return suspendCancellableCoroutine { cont -> cont.invokeOnCancellation { this@runWorkload.cancel() } diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt index 28acaef41..f427e3a72 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt @@ -62,369 +62,419 @@ class SimMachineTest { fun setUp() { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) - machineModel = MachineModel( - /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, - /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, - /*net*/ listOf(NetworkAdapter("Mellanox", "ConnectX-5", 25000.0)), - /*storage*/ listOf(StorageDevice("Samsung", "EVO", 1000.0, 250.0, 250.0)) - ) + machineModel = + MachineModel( + List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, + List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, + listOf(NetworkAdapter("Mellanox", "ConnectX-5", 25000.0)), + listOf(StorageDevice("Samsung", "EVO", 1000.0, 250.0, 250.0)), + ) } @Test - fun testFlopsWorkload() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) + fun testFlopsWorkload() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - machine.runWorkload(SimWorkloads.flops(2_000, /*utilization*/ 1.0)) + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) - // Two cores execute 1000 MFlOps per second (1000 ms) - assertEquals(1000, timeSource.millis()) - } + machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) - @Test - fun testTraceWorkload() = runSimulation { - val random = ThreadLocalRandom.current() - val builder = SimTrace.builder() - repeat(1000000) { - val timestamp = it.toLong() * 1000 - val deadline = timestamp + 1000 - builder.add(deadline, random.nextDouble(0.0, 4500.0), 1) + // Two cores execute 1000 MFlOps per second (1000 ms) + assertEquals(1000, timeSource.millis()) } - val trace = builder.build() - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) + @Test + fun testTraceWorkload() = + runSimulation { + val random = ThreadLocalRandom.current() + val builder = SimTrace.builder() + repeat(1000000) { + val timestamp = it.toLong() * 1000 + val deadline = timestamp + 1000 + builder.add(deadline, random.nextDouble(0.0, 4500.0), 1) + } + val trace = builder.build() - machine.runWorkload(trace.createWorkload(0)) + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) - // Two cores execute 1000 MFlOps per second (1000 ms) - assertEquals(1000000000, timeSource.millis()) - } + machine.runWorkload(trace.createWorkload(0)) - @Test - fun testDualSocketMachine() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val cpuNode = machineModel.cpus[0].node - val machineModel = MachineModel( - /*cpus*/ List(cpuNode.coreCount * 2) { ProcessingUnit(cpuNode, it % 2, 1000.0) }, - /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } - ) - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - machine.runWorkload(SimWorkloads.flops(2_000, /*utilization*/ 1.0)) - - // Two sockets with two cores execute 2000 MFlOps per second (500 ms) - assertEquals(500, timeSource.millis()) - } + // Two cores execute 1000 MFlOps per second (1000 ms) + assertEquals(1000000000, timeSource.millis()) + } @Test - fun testPower() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val machine = SimBareMetalMachine.create( - graph, - machineModel, - SimPsuFactories.simple(CpuPowerModels.linear(100.0, 50.0)) - ) - val source = SimPowerSource(graph, /*capacity*/ 1000.0f) - source.connect(machine.psu) - - coroutineScope { - launch { machine.runWorkload(SimWorkloads.flops(2_000, /*utilization*/ 1.0)) } - - yield() - assertAll( - { assertEquals(100.0, machine.psu.powerDraw) }, - { assertEquals(100.0f, source.powerDraw) } - ) + fun testDualSocketMachine() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val cpuNode = machineModel.cpus[0].node + val machineModel = + MachineModel( + List(cpuNode.coreCount * 2) { ProcessingUnit(cpuNode, it % 2, 1000.0) }, + List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, + ) + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) + + machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) + + // Two sockets with two cores execute 2000 MFlOps per second (500 ms) + assertEquals(500, timeSource.millis()) } - } @Test - fun testCapacityClamp() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) + fun testPower() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + SimPsuFactories.simple(CpuPowerModels.linear(100.0, 50.0)), + ) + val source = SimPowerSource(graph, 1000.0f) + source.connect(machine.psu) - machine.runWorkload(object : SimWorkload { - override fun onStart(ctx: SimMachineContext) { - val cpu = ctx.cpus[0] - - cpu.frequency = (cpu.model.frequency + 1000.0) - assertEquals(cpu.model.frequency, cpu.frequency) - cpu.frequency = -1.0 - assertEquals(0.0, cpu.frequency) + coroutineScope { + launch { machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) } - ctx.shutdown() + yield() + assertAll( + { assertEquals(100.0, machine.psu.powerDraw) }, + { assertEquals(100.0f, source.powerDraw) }, + ) } - - override fun setOffset(now: Long) {} - - override fun onStop(ctx: SimMachineContext) {} - - override fun snapshot(): SimWorkload = TODO() - }) - } + } @Test - fun testMemory() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - machine.runWorkload(object : SimWorkload { - override fun onStart(ctx: SimMachineContext) { - assertEquals(32_000 * 4.0, ctx.memory.capacity) - ctx.shutdown() - } + fun testCapacityClamp() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - override fun setOffset(now: Long) {} + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) - override fun onStop(ctx: SimMachineContext) {} + machine.runWorkload( + object : SimWorkload { + override fun onStart(ctx: SimMachineContext) { + val cpu = ctx.cpus[0] - override fun snapshot(): SimWorkload = TODO() - }) - } + cpu.frequency = (cpu.model.frequency + 1000.0) + assertEquals(cpu.model.frequency, cpu.frequency) + cpu.frequency = -1.0 + assertEquals(0.0, cpu.frequency) - @Test - fun testMemoryUsage() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - machine.runWorkload(object : SimWorkload { - override fun onStart(ctx: SimMachineContext) { - val source = SimpleFlowSource(ctx.graph, ctx.memory.capacity.toFloat(), 1.0f) { ctx.shutdown() } - ctx.graph.connect(source.output, ctx.memory.input) - } + ctx.shutdown() + } - override fun setOffset(now: Long) {} + override fun setOffset(now: Long) {} - override fun onStop(ctx: SimMachineContext) {} + override fun onStop(ctx: SimMachineContext) {} - override fun snapshot(): SimWorkload = TODO() - }) - - assertEquals(1000, timeSource.millis()) - } + override fun snapshot(): SimWorkload = TODO() + }, + ) + } @Test - fun testNetUsage() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - val adapter = (machine.peripherals[0] as SimNetworkAdapter) - adapter.connect(SimNetworkSink(graph, adapter.bandwidth.toFloat())) - - machine.runWorkload(object : SimWorkload { - override fun onStart(ctx: SimMachineContext) { - val iface = ctx.networkInterfaces[0] - val source = SimpleFlowSource(ctx.graph, 800.0f, 0.8f) { ctx.shutdown(); it.close(); } - ctx.graph.connect(source.output, iface.tx) - } + fun testMemory() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) + + machine.runWorkload( + object : SimWorkload { + override fun onStart(ctx: SimMachineContext) { + assertEquals(32_000 * 4.0, ctx.memory.capacity) + ctx.shutdown() + } + + override fun setOffset(now: Long) {} + + override fun onStop(ctx: SimMachineContext) {} + + override fun snapshot(): SimWorkload = TODO() + }, + ) + } - override fun setOffset(now: Long) {} + @Test + fun testMemoryUsage() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) + + machine.runWorkload( + object : SimWorkload { + override fun onStart(ctx: SimMachineContext) { + val source = SimpleFlowSource(ctx.graph, ctx.memory.capacity.toFloat(), 1.0f) { ctx.shutdown() } + ctx.graph.connect(source.output, ctx.memory.input) + } + + override fun setOffset(now: Long) {} + + override fun onStop(ctx: SimMachineContext) {} + + override fun snapshot(): SimWorkload = TODO() + }, + ) - override fun onStop(ctx: SimMachineContext) {} + assertEquals(1000, timeSource.millis()) + } - override fun snapshot(): SimWorkload = TODO() - }) + @Test + fun testNetUsage() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) + + val adapter = (machine.peripherals[0] as SimNetworkAdapter) + adapter.connect(SimNetworkSink(graph, adapter.bandwidth.toFloat())) + + machine.runWorkload( + object : SimWorkload { + override fun onStart(ctx: SimMachineContext) { + val iface = ctx.networkInterfaces[0] + val source = + SimpleFlowSource(ctx.graph, 800.0f, 0.8f) { + ctx.shutdown() + it.close() + } + ctx.graph.connect(source.output, iface.tx) + } + + override fun setOffset(now: Long) {} + + override fun onStop(ctx: SimMachineContext) {} + + override fun snapshot(): SimWorkload = TODO() + }, + ) - assertEquals(40, timeSource.millis()) - } + assertEquals(40, timeSource.millis()) + } @Test - fun testDiskReadUsage() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - machine.runWorkload(object : SimWorkload { - override fun onStart(ctx: SimMachineContext) { - val disk = ctx.storageInterfaces[0] - val source = SimpleFlowSource(ctx.graph, 800.0f, 0.8f) { ctx.shutdown() } - ctx.graph.connect(source.output, disk.read) - } - - override fun setOffset(now: Long) {} + fun testDiskReadUsage() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) + + machine.runWorkload( + object : SimWorkload { + override fun onStart(ctx: SimMachineContext) { + val disk = ctx.storageInterfaces[0] + val source = SimpleFlowSource(ctx.graph, 800.0f, 0.8f) { ctx.shutdown() } + ctx.graph.connect(source.output, disk.read) + } + + override fun setOffset(now: Long) {} + + override fun onStop(ctx: SimMachineContext) {} + + override fun snapshot(): SimWorkload = TODO() + }, + ) - override fun onStop(ctx: SimMachineContext) {} + assertEquals(4000, timeSource.millis()) + } - override fun snapshot(): SimWorkload = TODO() - }) + @Test + fun testDiskWriteUsage() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) + + machine.runWorkload( + object : SimWorkload { + override fun onStart(ctx: SimMachineContext) { + val disk = ctx.storageInterfaces[0] + val source = SimpleFlowSource(ctx.graph, 800.0f, 0.8f) { ctx.shutdown() } + ctx.graph.connect(source.output, disk.write) + } + + override fun setOffset(now: Long) {} + + override fun onStop(ctx: SimMachineContext) {} + + override fun snapshot(): SimWorkload = TODO() + }, + ) - assertEquals(4000, timeSource.millis()) - } + assertEquals(4000, timeSource.millis()) + } @Test - fun testDiskWriteUsage() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - machine.runWorkload(object : SimWorkload { - override fun onStart(ctx: SimMachineContext) { - val disk = ctx.storageInterfaces[0] - val source = SimpleFlowSource(ctx.graph, 800.0f, 0.8f) { ctx.shutdown() } - ctx.graph.connect(source.output, disk.write) + fun testCancellation() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) + + try { + coroutineScope { + launch { machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) } + cancel() + } + } catch (_: CancellationException) { + // Ignore } - override fun setOffset(now: Long) {} - - override fun onStop(ctx: SimMachineContext) {} - - override fun snapshot(): SimWorkload = TODO() - }) - - assertEquals(4000, timeSource.millis()) - } + assertEquals(0, timeSource.millis()) + } @Test - fun testCancellation() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testConcurrentRuns() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) - try { coroutineScope { - launch { machine.runWorkload(SimWorkloads.flops(2_000, /*utilization*/ 1.0)) } - cancel() - } - } catch (_: CancellationException) { - // Ignore - } - - assertEquals(0, timeSource.millis()) - } + launch { + machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) + } - @Test - fun testConcurrentRuns() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - coroutineScope { - launch { - machine.runWorkload(SimWorkloads.flops(2_000, /*utilization*/ 1.0)) - } - - assertThrows { - machine.runWorkload(SimWorkloads.flops(2_000, /*utilization*/ 1.0)) + assertThrows { + machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) + } } } - } @Test - fun testCatchStartFailure() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testCatchStartFailure() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) - val workload = mockk() - every { workload.onStart(any()) } throws IllegalStateException() + val workload = mockk() + every { workload.onStart(any()) } throws IllegalStateException() - assertThrows { machine.runWorkload(workload) } - } + assertThrows { machine.runWorkload(workload) } + } @Test - fun testCatchStopFailure() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - val workload = mockk() - every { workload.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown() } - every { workload.onStop(any()) } throws IllegalStateException() - - assertThrows { machine.runWorkload(workload) } - } + fun testCatchStopFailure() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) + + val workload = mockk() + every { workload.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown() } + every { workload.onStop(any()) } throws IllegalStateException() + + assertThrows { machine.runWorkload(workload) } + } @Test - fun testCatchShutdownFailure() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testCatchShutdownFailure() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) - val workload = mockk() - every { workload.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown(IllegalStateException()) } + val workload = mockk() + every { workload.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown(IllegalStateException()) } - assertThrows { machine.runWorkload(workload) } - } + assertThrows { machine.runWorkload(workload) } + } @Test - fun testCatchNestedFailure() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - val workload = mockk() - every { workload.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown(IllegalStateException()) } - every { workload.onStop(any()) } throws IllegalStateException() - - val exc = assertThrows { machine.runWorkload(workload) } - assertEquals(1, exc.cause!!.suppressedExceptions.size) - } + fun testCatchNestedFailure() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) + + val workload = mockk() + every { workload.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown(IllegalStateException()) } + every { workload.onStop(any()) } throws IllegalStateException() + + val exc = assertThrows { machine.runWorkload(workload) } + assertEquals(1, exc.cause!!.suppressedExceptions.size) + } } diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt index 99f47b2fb..bef226997 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt @@ -54,173 +54,202 @@ internal class SimFairShareHypervisorTest { @BeforeEach fun setUp() { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 1) - model = MachineModel( - /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, - /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } - ) + model = + MachineModel( + // cpus + List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + // memory + List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, + ) } /** * Test overcommitting of resources via the hypervisor with a single VM. */ @Test - fun testOvercommittedSingle() = runSimulation { - val duration = 5 * 60L - val workloadA = - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) - ).createWorkload(0) - - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create(graph, model) - val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(0L), ScalingGovernors.performance()) - - launch { machine.runWorkload(hypervisor) } - yield() - - val vm = hypervisor.newMachine(model) - vm.runWorkload(workloadA) - - yield() - machine.cancel() - - assertAll( - { assertEquals(319781, hypervisor.counters.cpuActiveTime, "Active time does not match") }, - { assertEquals(880219, hypervisor.counters.cpuIdleTime, "Idle time does not match") }, - { assertEquals(28125, hypervisor.counters.cpuStealTime, "Steal time does not match") }, - { assertEquals(1200000, timeSource.millis()) { "Current time is correct" } } - ) - } + fun testOvercommittedSingle() = + runSimulation { + val duration = 5 * 60L + val workloadA = + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1), + ).createWorkload(0) + + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, model) + val hypervisor = + SimHypervisor.create( + FlowMultiplexerFactory.maxMinMultiplexer(), + SplittableRandom(0L), + ScalingGovernors.performance(), + ) + + launch { machine.runWorkload(hypervisor) } + yield() + + val vm = hypervisor.newMachine(model) + vm.runWorkload(workloadA) + + yield() + machine.cancel() + + assertAll( + { assertEquals(319781, hypervisor.counters.cpuActiveTime, "Active time does not match") }, + { assertEquals(880219, hypervisor.counters.cpuIdleTime, "Idle time does not match") }, + { assertEquals(28125, hypervisor.counters.cpuStealTime, "Steal time does not match") }, + { assertEquals(1200000, timeSource.millis()) { "Current time is correct" } }, + ) + } /** * Test overcommitting of resources via the hypervisor with two VMs. */ @Test - fun testOvercommittedDual() = runSimulation { - val duration = 5 * 60L - val workloadA = - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) - ).createWorkload(0) - val workloadB = - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 3100.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 73.0, 1) - ).createWorkload(0) - - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create(graph, model) - val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(0L), ScalingGovernors.performance()) - - launch { machine.runWorkload(hypervisor) } - - yield() - coroutineScope { - launch { + fun testOvercommittedDual() = + runSimulation { + val duration = 5 * 60L + val workloadA = + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1), + ).createWorkload(0) + val workloadB = + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 3100.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 73.0, 1), + ).createWorkload(0) + + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, model) + val hypervisor = + SimHypervisor.create( + FlowMultiplexerFactory.maxMinMultiplexer(), + SplittableRandom(0L), + ScalingGovernors.performance(), + ) + + launch { machine.runWorkload(hypervisor) } + + yield() + coroutineScope { + launch { + val vm = hypervisor.newMachine(model) + vm.runWorkload(workloadA) + hypervisor.removeMachine(vm) + } val vm = hypervisor.newMachine(model) - vm.runWorkload(workloadA) + vm.runWorkload(workloadB) hypervisor.removeMachine(vm) } - val vm = hypervisor.newMachine(model) - vm.runWorkload(workloadB) - hypervisor.removeMachine(vm) + yield() + machine.cancel() + yield() + + assertAll( + { assertEquals(329250, hypervisor.counters.cpuActiveTime, "Active time does not match") }, + { assertEquals(870750, hypervisor.counters.cpuIdleTime, "Idle time does not match") }, + { assertEquals(318750, hypervisor.counters.cpuStealTime, "Steal time does not match") }, + { assertEquals(1200000, timeSource.millis()) }, + ) } - yield() - machine.cancel() - yield() - - assertAll( - { assertEquals(329250, hypervisor.counters.cpuActiveTime, "Active time does not match") }, - { assertEquals(870750, hypervisor.counters.cpuIdleTime, "Idle time does not match") }, - { assertEquals(318750, hypervisor.counters.cpuStealTime, "Steal time does not match") }, - { assertEquals(1200000, timeSource.millis()) } - ) - } @Test - fun testMultipleCPUs() = runSimulation { - val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) - val model = MachineModel( - /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, - /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } - ) - - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create(graph, model) - val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(0L), ScalingGovernors.performance()) + fun testMultipleCPUs() = + runSimulation { + val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) + val model = + MachineModel( + // cpus + List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + // memory + List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, + ) + + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, model) + val hypervisor = + SimHypervisor.create( + FlowMultiplexerFactory.maxMinMultiplexer(), + SplittableRandom(0L), + ScalingGovernors.performance(), + ) + + assertDoesNotThrow { + launch { machine.runWorkload(hypervisor) } + } - assertDoesNotThrow { - launch { machine.runWorkload(hypervisor) } + machine.cancel() } - machine.cancel() - } - @Test - fun testInterference() = runSimulation { - val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) - val model = MachineModel( - /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, - /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } - ) - - val interferenceModel = VmInterferenceModel.builder() - .addGroup(setOf("a", "b"), 0.0, 0.9) - .addGroup(setOf("a", "c"), 0.0, 0.6) - .addGroup(setOf("a", "n"), 0.1, 0.8) - .build() - - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create(graph, model) - val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(0L)) - - val duration = 5 * 60L - val workloadA = - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 3500.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) - ).createWorkload(0) - val workloadB = - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 3100.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 73.0, 1) - ).createWorkload(0) - - launch { - machine.runWorkload(hypervisor) - } + fun testInterference() = + runSimulation { + val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) + val model = + MachineModel( + // cpus + List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + // memory + List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, + ) + + val interferenceModel = + VmInterferenceModel.builder() + .addGroup(setOf("a", "b"), 0.0, 0.9) + .addGroup(setOf("a", "c"), 0.0, 0.6) + .addGroup(setOf("a", "n"), 0.1, 0.8) + .build() + + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, model) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(0L)) + + val duration = 5 * 60L + val workloadA = + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 3500.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1), + ).createWorkload(0) + val workloadB = + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 3100.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 73.0, 1), + ).createWorkload(0) - coroutineScope { launch { + machine.runWorkload(hypervisor) + } + + coroutineScope { + launch { + val vm = hypervisor.newMachine(model) + vm.runWorkload(workloadA, meta = mapOf("interference-model" to interferenceModel.getProfile("a")!!)) + hypervisor.removeMachine(vm) + } val vm = hypervisor.newMachine(model) - vm.runWorkload(workloadA, meta = mapOf("interference-model" to interferenceModel.getProfile("a")!!)) + vm.runWorkload(workloadB, meta = mapOf("interference-model" to interferenceModel.getProfile("b")!!)) hypervisor.removeMachine(vm) } - val vm = hypervisor.newMachine(model) - vm.runWorkload(workloadB, meta = mapOf("interference-model" to interferenceModel.getProfile("b")!!)) - hypervisor.removeMachine(vm) - } - machine.cancel() - } + machine.cancel() + } } diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt index 93b67aa36..b762acea6 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt @@ -55,167 +55,176 @@ internal class SimSpaceSharedHypervisorTest { @BeforeEach fun setUp() { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 1) - machineModel = MachineModel( - /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, - /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } - ) + machineModel = + MachineModel( + // cpus + List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + // memory + List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, + ) } /** * Test a trace workload. */ @Test - fun testTrace() = runSimulation { - val duration = 5 * 60L - val workloadA = - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) - ).createWorkload(0) - - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create(graph, machineModel) - val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) - - launch { machine.runWorkload(hypervisor) } - val vm = hypervisor.newMachine(machineModel) - vm.runWorkload(workloadA) - yield() - - hypervisor.removeMachine(vm) - machine.cancel() - - assertEquals(5 * 60L * 4000, timeSource.millis()) { "Took enough time" } - } + fun testTrace() = + runSimulation { + val duration = 5 * 60L + val workloadA = + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1), + ).createWorkload(0) + + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) + + launch { machine.runWorkload(hypervisor) } + val vm = hypervisor.newMachine(machineModel) + vm.runWorkload(workloadA) + yield() + + hypervisor.removeMachine(vm) + machine.cancel() + + assertEquals(5 * 60L * 4000, timeSource.millis()) { "Took enough time" } + } /** * Test runtime workload on hypervisor. */ @Test - fun testRuntimeWorkload() = runSimulation { - val duration = 5 * 60L * 1000 - val workload = SimWorkloads.runtime(duration, 1.0) - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testRuntimeWorkload() = + runSimulation { + val duration = 5 * 60L * 1000 + val workload = SimWorkloads.runtime(duration, 1.0) + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val machine = SimBareMetalMachine.create(graph, machineModel) - val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) - launch { machine.runWorkload(hypervisor) } - yield() - val vm = hypervisor.newMachine(machineModel) - vm.runWorkload(workload) - hypervisor.removeMachine(vm) + launch { machine.runWorkload(hypervisor) } + yield() + val vm = hypervisor.newMachine(machineModel) + vm.runWorkload(workload) + hypervisor.removeMachine(vm) - machine.cancel() + machine.cancel() - assertEquals(duration, timeSource.millis()) { "Took enough time" } - } + assertEquals(duration, timeSource.millis()) { "Took enough time" } + } /** * Test FLOPs workload on hypervisor. */ @Test - fun testFlopsWorkload() = runSimulation { - val duration = 5 * 60L * 1000 - val workload = SimWorkloads.flops((duration * 3.2).toLong(), 1.0) - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create(graph, machineModel) - val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) - - launch { machine.runWorkload(hypervisor) } - yield() - val vm = hypervisor.newMachine(machineModel) - vm.runWorkload(workload) - machine.cancel() - - assertEquals(duration, timeSource.millis()) { "Took enough time" } - } + fun testFlopsWorkload() = + runSimulation { + val duration = 5 * 60L * 1000 + val workload = SimWorkloads.flops((duration * 3.2).toLong(), 1.0) + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) + + launch { machine.runWorkload(hypervisor) } + yield() + val vm = hypervisor.newMachine(machineModel) + vm.runWorkload(workload) + machine.cancel() + + assertEquals(duration, timeSource.millis()) { "Took enough time" } + } /** * Test two workloads running sequentially. */ @Test - fun testTwoWorkloads() = runSimulation { - val duration = 5 * 60L * 1000 - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testTwoWorkloads() = + runSimulation { + val duration = 5 * 60L * 1000 + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val machine = SimBareMetalMachine.create(graph, machineModel) - val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) - launch { machine.runWorkload(hypervisor) } - yield() + launch { machine.runWorkload(hypervisor) } + yield() - val vm = hypervisor.newMachine(machineModel) - vm.runWorkload(SimWorkloads.runtime(duration, 1.0)) - hypervisor.removeMachine(vm) + val vm = hypervisor.newMachine(machineModel) + vm.runWorkload(SimWorkloads.runtime(duration, 1.0)) + hypervisor.removeMachine(vm) - yield() + yield() - val vm2 = hypervisor.newMachine(machineModel) - vm2.runWorkload(SimWorkloads.runtime(duration, 1.0)) - hypervisor.removeMachine(vm2) + val vm2 = hypervisor.newMachine(machineModel) + vm2.runWorkload(SimWorkloads.runtime(duration, 1.0)) + hypervisor.removeMachine(vm2) - machine.cancel() + machine.cancel() - assertEquals(duration * 2, timeSource.millis()) { "Took enough time" } - } + assertEquals(duration * 2, timeSource.millis()) { "Took enough time" } + } /** * Test concurrent workloads on the machine. */ @Test - fun testConcurrentWorkloadFails() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testConcurrentWorkloadFails() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val machine = SimBareMetalMachine.create(graph, machineModel) - val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) - launch { machine.runWorkload(hypervisor) } - yield() + launch { machine.runWorkload(hypervisor) } + yield() - val vm = hypervisor.newMachine(machineModel) - launch { vm.runWorkload(SimWorkloads.runtime(10_000, 1.0)) } - yield() + val vm = hypervisor.newMachine(machineModel) + launch { vm.runWorkload(SimWorkloads.runtime(10_000, 1.0)) } + yield() - assertAll( - { assertFalse(hypervisor.canFit(machineModel)) }, - { assertThrows { hypervisor.newMachine(machineModel) } } - ) + assertAll( + { assertFalse(hypervisor.canFit(machineModel)) }, + { assertThrows { hypervisor.newMachine(machineModel) } }, + ) - machine.cancel() - vm.cancel() - } + machine.cancel() + vm.cancel() + } /** * Test concurrent workloads on the machine. */ @Test - fun testConcurrentWorkloadSucceeds() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testConcurrentWorkloadSucceeds() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val machine = SimBareMetalMachine.create(graph, machineModel) - val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) - launch { machine.runWorkload(hypervisor) } - yield() + launch { machine.runWorkload(hypervisor) } + yield() - hypervisor.removeMachine(hypervisor.newMachine(machineModel)) + hypervisor.removeMachine(hypervisor.newMachine(machineModel)) - assertAll( - { assertTrue(hypervisor.canFit(machineModel)) }, - { assertDoesNotThrow { hypervisor.newMachine(machineModel) } } - ) + assertAll( + { assertTrue(hypervisor.canFit(machineModel)) }, + { assertDoesNotThrow { hypervisor.newMachine(machineModel) } }, + ) - machine.cancel() - } + machine.cancel() + } } diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt index 6b182f4c4..4a930df64 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt @@ -51,10 +51,10 @@ internal class ConservativeScalingGovernorTest { logic.onStart() logic.onLimit(0.5) - /* Upwards scaling */ + // Upwards scaling logic.onLimit(defaultThreshold + 0.2) - /* Downwards scaling */ + // Downwards scaling logic.onLimit(defaultThreshold + 0.1) verify(exactly = 2) { policy.target = minSpeed } @@ -82,10 +82,10 @@ internal class ConservativeScalingGovernorTest { logic.onStart() logic.onLimit(0.5) - /* Upwards scaling */ + // Upwards scaling logic.onLimit(threshold + 0.2) - /* Downwards scaling */ + // Downwards scaling logic.onLimit(threshold + 0.1) verify(exactly = 2) { policy.target = minSpeed } diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt index 9a6263c59..e85758ae5 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt @@ -36,34 +36,35 @@ internal class PowerModelTest { private val cpuUtil = 0.9 @ParameterizedTest - @MethodSource("MachinePowerModelArgs") + @MethodSource("machinePowerModelArgs") fun `compute power consumption given CPU loads`( powerModel: CpuPowerModel, - expectedPowerConsumption: Double + expectedPowerConsumption: Double, ) { val computedPowerConsumption = powerModel.computePower(cpuUtil) assertEquals(expectedPowerConsumption, computedPowerConsumption, epsilon) } @ParameterizedTest - @MethodSource("MachinePowerModelArgs") + @MethodSource("machinePowerModelArgs") fun `ignore idle power when computing power consumptions`( powerModel: CpuPowerModel, - expectedPowerConsumption: Double + expectedPowerConsumption: Double, ) { val zeroPowerModel = CpuPowerModels.zeroIdle(powerModel) assertAll( { assertEquals(expectedPowerConsumption, zeroPowerModel.computePower(cpuUtil), epsilon) }, - { assertEquals(0.0, zeroPowerModel.computePower(0.0)) } + { assertEquals(0.0, zeroPowerModel.computePower(0.0)) }, ) } @Test fun `compute power draw by the SPEC benchmark model`() { - val powerModel = CpuPowerModels.interpolate( - 58.4, 98.0, 109.0, 118.0, 128.0, 140.0, 153.0, 170.0, 189.0, 205.0, 222.0 - ) + val powerModel = + CpuPowerModels.interpolate( + 58.4, 98.0, 109.0, 118.0, 128.0, 140.0, 153.0, 170.0, 189.0, 205.0, 222.0, + ) assertAll( { assertEquals(58.4, powerModel.computePower(0.0)) }, @@ -73,22 +74,23 @@ internal class PowerModelTest { { assertEquals(189.0, powerModel.computePower(0.8)) }, { assertEquals(189.0 + 0.7 * 10 * (205 - 189) / 10, powerModel.computePower(0.87)) }, { assertEquals(205.0, powerModel.computePower(0.9)) }, - { assertEquals(222.0, powerModel.computePower(1.0)) } + { assertEquals(222.0, powerModel.computePower(1.0)) }, ) } @Suppress("unused") private companion object { @JvmStatic - fun MachinePowerModelArgs(): Stream = Stream.of( - Arguments.of(CpuPowerModels.constant(0.0), 0.0), - Arguments.of(CpuPowerModels.linear(350.0, 200.0), 335.0), - Arguments.of(CpuPowerModels.square(350.0, 200.0), 321.5), - Arguments.of(CpuPowerModels.cubic(350.0, 200.0), 309.35), - Arguments.of(CpuPowerModels.sqrt(350.0, 200.0), 342.302), - Arguments.of(CpuPowerModels.mse(350.0, 200.0, 1.4), 340.571), - Arguments.of(CpuPowerModels.asymptotic(350.0, 200.0, 0.3, false), 338.765), - Arguments.of(CpuPowerModels.asymptotic(350.0, 200.0, 0.3, true), 323.072) - ) + fun machinePowerModelArgs(): Stream = + Stream.of( + Arguments.of(CpuPowerModels.constant(0.0), 0.0), + Arguments.of(CpuPowerModels.linear(350.0, 200.0), 335.0), + Arguments.of(CpuPowerModels.square(350.0, 200.0), 321.5), + Arguments.of(CpuPowerModels.cubic(350.0, 200.0), 309.35), + Arguments.of(CpuPowerModels.sqrt(350.0, 200.0), 342.302), + Arguments.of(CpuPowerModels.mse(350.0, 200.0, 1.4), 340.571), + Arguments.of(CpuPowerModels.asymptotic(350.0, 200.0, 0.3, false), 338.765), + Arguments.of(CpuPowerModels.asymptotic(350.0, 200.0, 0.3, true), 323.072), + ) } } diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimChainWorkloadTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimChainWorkloadTest.kt index 08bb6509c..9ea9d300a 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimChainWorkloadTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimChainWorkloadTest.kt @@ -51,235 +51,255 @@ class SimChainWorkloadTest { fun setUp() { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) - machineModel = MachineModel( - /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, - /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } - ) + machineModel = + MachineModel( + // cpus + List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, + // memory + List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, + ) } @Test - fun testMultipleWorkloads() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - val workload = - SimWorkloads.chain( - SimWorkloads.runtime(1000, 1.0), - SimWorkloads.runtime(1000, 1.0) - ) + fun testMultipleWorkloads() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - machine.runWorkload(workload) + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) - assertEquals(2000, timeSource.millis()) - } + val workload = + SimWorkloads.chain( + SimWorkloads.runtime(1000, 1.0), + SimWorkloads.runtime(1000, 1.0), + ) + + machine.runWorkload(workload) + + assertEquals(2000, timeSource.millis()) + } @Test - fun testStartFailure() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - val workloadA = mockk() - every { workloadA.onStart(any()) } throws IllegalStateException("Staged") - every { workloadA.onStop(any()) } returns Unit - - val workload = - SimWorkloads.chain( - workloadA, - SimWorkloads.runtime(1000, 1.0) - ) + fun testStartFailure() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - assertThrows { machine.runWorkload(workload) } + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) - assertEquals(0, timeSource.millis()) - } + val workloadA = mockk() + every { workloadA.onStart(any()) } throws IllegalStateException("Staged") + every { workloadA.onStop(any()) } returns Unit - @Test - fun testStartFailureSecond() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - val workloadA = mockk() - every { workloadA.onStart(any()) } throws IllegalStateException("Staged") - every { workloadA.onStop(any()) } returns Unit - - val workload = - SimWorkloads.chain( - SimWorkloads.runtime(1000, 1.0), - workloadA, - SimWorkloads.runtime(1000, 1.0) - ) + val workload = + SimWorkloads.chain( + workloadA, + SimWorkloads.runtime(1000, 1.0), + ) - assertThrows { machine.runWorkload(workload) } + assertThrows { machine.runWorkload(workload) } - assertEquals(1000, timeSource.millis()) - } + assertEquals(0, timeSource.millis()) + } @Test - fun testStopFailure() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - val workloadA = spyk(SimRuntimeWorkload(1000, 1.0)) - every { workloadA.onStop(any()) } throws IllegalStateException("Staged") - - val workload = - SimWorkloads.chain( - workloadA, - SimWorkloads.runtime(1000, 1.0) - ) + fun testStartFailureSecond() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - assertThrows { machine.runWorkload(workload) } + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) - assertEquals(1000, timeSource.millis()) - } + val workloadA = mockk() + every { workloadA.onStart(any()) } throws IllegalStateException("Staged") + every { workloadA.onStop(any()) } returns Unit + + val workload = + SimWorkloads.chain( + SimWorkloads.runtime(1000, 1.0), + workloadA, + SimWorkloads.runtime(1000, 1.0), + ) + + assertThrows { machine.runWorkload(workload) } + + assertEquals(1000, timeSource.millis()) + } @Test - fun testStopFailureSecond() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - val workloadA = spyk(SimRuntimeWorkload(1000, 1.0)) - every { workloadA.onStop(any()) } throws IllegalStateException("Staged") - - val workload = - SimWorkloads.chain( - SimWorkloads.runtime(1000, 1.0), - workloadA, - SimWorkloads.runtime(1000, 1.0) - ) + fun testStopFailure() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - assertThrows { machine.runWorkload(workload) } + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) - assertEquals(2000, timeSource.millis()) - } + val workloadA = spyk(SimRuntimeWorkload(1000, 1.0)) + every { workloadA.onStop(any()) } throws IllegalStateException("Staged") + + val workload = + SimWorkloads.chain( + workloadA, + SimWorkloads.runtime(1000, 1.0), + ) + + assertThrows { machine.runWorkload(workload) } + + assertEquals(1000, timeSource.millis()) + } @Test - fun testStartAndStopFailure() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - val workloadA = mockk() - every { workloadA.onStart(any()) } throws IllegalStateException() - every { workloadA.onStop(any()) } throws IllegalStateException() - - val workload = - SimWorkloads.chain( - SimRuntimeWorkload(1000, 1.0), - workloadA - ) + fun testStopFailureSecond() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val exc = assertThrows { machine.runWorkload(workload) } + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) - assertEquals(2, exc.cause!!.suppressedExceptions.size) - assertEquals(1000, timeSource.millis()) - } + val workloadA = spyk(SimRuntimeWorkload(1000, 1.0)) + every { workloadA.onStop(any()) } throws IllegalStateException("Staged") + + val workload = + SimWorkloads.chain( + SimWorkloads.runtime(1000, 1.0), + workloadA, + SimWorkloads.runtime(1000, 1.0), + ) + + assertThrows { machine.runWorkload(workload) } + + assertEquals(2000, timeSource.millis()) + } @Test - fun testShutdownAndStopFailure() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - val workloadA = mockk() - every { workloadA.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown(IllegalStateException()) } - every { workloadA.onStop(any()) } throws IllegalStateException() - - val workload = - SimWorkloads.chain( - SimRuntimeWorkload(1000, 1.0), - workloadA - ) + fun testStartAndStopFailure() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val exc = assertThrows { machine.runWorkload(workload) } + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) - assertEquals(1, exc.cause!!.suppressedExceptions.size) - assertEquals(1000, timeSource.millis()) - } + val workloadA = mockk() + every { workloadA.onStart(any()) } throws IllegalStateException() + every { workloadA.onStop(any()) } throws IllegalStateException() + + val workload = + SimWorkloads.chain( + SimRuntimeWorkload(1000, 1.0), + workloadA, + ) + + val exc = assertThrows { machine.runWorkload(workload) } + + assertEquals(2, exc.cause!!.suppressedExceptions.size) + assertEquals(1000, timeSource.millis()) + } @Test - fun testShutdownAndStartFailure() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - val workloadA = mockk(relaxUnitFun = true) - every { workloadA.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown(IllegalStateException()) } - - val workloadB = mockk(relaxUnitFun = true) - every { workloadB.onStart(any()) } throws IllegalStateException() - - val workload = - SimWorkloads.chain( - SimRuntimeWorkload(1000, 1.0), - workloadA, - workloadB - ) + fun testShutdownAndStopFailure() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val exc = assertThrows { machine.runWorkload(workload) } - assertEquals(1, exc.cause!!.suppressedExceptions.size) - assertEquals(1000, timeSource.millis()) - } + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) + + val workloadA = mockk() + every { workloadA.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown(IllegalStateException()) } + every { workloadA.onStop(any()) } throws IllegalStateException() + + val workload = + SimWorkloads.chain( + SimRuntimeWorkload(1000, 1.0), + workloadA, + ) + + val exc = assertThrows { machine.runWorkload(workload) } + + assertEquals(1, exc.cause!!.suppressedExceptions.size) + assertEquals(1000, timeSource.millis()) + } @Test - fun testSnapshot() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create(graph, machineModel) - val workload = - SimWorkloads.chain( - SimWorkloads.runtime(1000, 1.0), - SimWorkloads.runtime(1000, 1.0) - ) + fun testShutdownAndStartFailure() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) + + val workloadA = mockk(relaxUnitFun = true) + every { workloadA.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown(IllegalStateException()) } + + val workloadB = mockk(relaxUnitFun = true) + every { workloadB.onStart(any()) } throws IllegalStateException() + + val workload = + SimWorkloads.chain( + SimRuntimeWorkload(1000, 1.0), + workloadA, + workloadB, + ) + + val exc = assertThrows { machine.runWorkload(workload) } + assertEquals(1, exc.cause!!.suppressedExceptions.size) + assertEquals(1000, timeSource.millis()) + } - val job = launch { machine.runWorkload(workload) } - delay(500L) - val snapshot = workload.snapshot() + @Test + fun testSnapshot() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - job.join() + val machine = SimBareMetalMachine.create(graph, machineModel) + val workload = + SimWorkloads.chain( + SimWorkloads.runtime(1000, 1.0), + SimWorkloads.runtime(1000, 1.0), + ) - assertEquals(2000, timeSource.millis()) + val job = launch { machine.runWorkload(workload) } + delay(500L) + val snapshot = workload.snapshot() - machine.runWorkload(snapshot) + job.join() - assertEquals(3500, timeSource.millis()) - } + assertEquals(2000, timeSource.millis()) + + machine.runWorkload(snapshot) + + assertEquals(3500, timeSource.millis()) + } } diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt index b87fd8ba2..e40d4f8b3 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt @@ -45,102 +45,113 @@ class SimTraceWorkloadTest { fun setUp() { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) - machineModel = MachineModel( - /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, - /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } - ) + machineModel = + MachineModel( + // cpus + List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, + // memory + List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, + ) } @Test - fun testSmoke() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - val workload = - SimTrace.ofFragments( - SimTraceFragment(0, 1000, 2 * 28.0, 2), - SimTraceFragment(1000, 1000, 2 * 3100.0, 2), - SimTraceFragment(2000, 1000, 0.0, 2), - SimTraceFragment(3000, 1000, 2 * 73.0, 2) - ).createWorkload(0) - - machine.runWorkload(workload) - - assertEquals(4000, timeSource.millis()) - } + fun testSmoke() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) + + val workload = + SimTrace.ofFragments( + SimTraceFragment(0, 1000, 2 * 28.0, 2), + SimTraceFragment(1000, 1000, 2 * 3100.0, 2), + SimTraceFragment(2000, 1000, 0.0, 2), + SimTraceFragment(3000, 1000, 2 * 73.0, 2), + ).createWorkload(0) + + machine.runWorkload(workload) + + assertEquals(4000, timeSource.millis()) + } // @Test // fixme: Fix delayed start and enable test - fun testOffset() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - val workload = - SimTrace.ofFragments( - SimTraceFragment(0, 1000, 2 * 28.0, 2), - SimTraceFragment(1000, 1000, 2 * 3100.0, 2), - SimTraceFragment(2000, 1000, 0.0, 2), - SimTraceFragment(3000, 1000, 2 * 73.0, 2) - ).createWorkload(1000) - - machine.runWorkload(workload) - - assertEquals(5000, timeSource.millis()) // fixme: should be 5000 but this is 4000 for now to make all tests succeed - } + fun testOffset() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) + + val workload = + SimTrace.ofFragments( + SimTraceFragment(0, 1000, 2 * 28.0, 2), + SimTraceFragment(1000, 1000, 2 * 3100.0, 2), + SimTraceFragment(2000, 1000, 0.0, 2), + SimTraceFragment(3000, 1000, 2 * 73.0, 2), + ).createWorkload(1000) + + machine.runWorkload(workload) + + assertEquals(5000, timeSource.millis()) // fixme: should be 5000 but this is 4000 for now to make all tests succeed + } @Test - fun testSkipFragment() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - val workload = - SimTrace.ofFragments( - SimTraceFragment(0, 1000, 2 * 28.0, 2), - SimTraceFragment(1000, 1000, 2 * 3100.0, 2), - SimTraceFragment(2000, 1000, 0.0, 2), - SimTraceFragment(3000, 1000, 2 * 73.0, 2) - ).createWorkload(0) - - delay(1000L) - machine.runWorkload(workload) - - assertEquals(4000, timeSource.millis()) - } + fun testSkipFragment() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) + + val workload = + SimTrace.ofFragments( + SimTraceFragment(0, 1000, 2 * 28.0, 2), + SimTraceFragment(1000, 1000, 2 * 3100.0, 2), + SimTraceFragment(2000, 1000, 0.0, 2), + SimTraceFragment(3000, 1000, 2 * 73.0, 2), + ).createWorkload(0) + + delay(1000L) + machine.runWorkload(workload) + + assertEquals(4000, timeSource.millis()) + } @Test - fun testZeroCores() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create( - graph, - machineModel - ) - - val workload = - SimTrace.ofFragments( - SimTraceFragment(0, 1000, 2 * 28.0, 2), - SimTraceFragment(1000, 1000, 2 * 3100.0, 2), - SimTraceFragment(2000, 1000, 0.0, 0), - SimTraceFragment(3000, 1000, 2 * 73.0, 2) - ).createWorkload(0) - - machine.runWorkload(workload) - - assertEquals(4000, timeSource.millis()) - } + fun testZeroCores() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val machine = + SimBareMetalMachine.create( + graph, + machineModel, + ) + + val workload = + SimTrace.ofFragments( + SimTraceFragment(0, 1000, 2 * 28.0, 2), + SimTraceFragment(1000, 1000, 2 * 3100.0, 2), + SimTraceFragment(2000, 1000, 0.0, 0), + SimTraceFragment(3000, 1000, 2 * 73.0, 2), + ).createWorkload(0) + + machine.runWorkload(workload) + + assertEquals(4000, timeSource.millis()) + } } diff --git a/opendc-simulator/opendc-simulator-core/build.gradle.kts b/opendc-simulator/opendc-simulator-core/build.gradle.kts index 0ae95d428..027ce3e5a 100644 --- a/opendc-simulator/opendc-simulator-core/build.gradle.kts +++ b/opendc-simulator/opendc-simulator-core/build.gradle.kts @@ -22,7 +22,7 @@ description = "Simulation-specific code for use in OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationBuilders.kt b/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationBuilders.kt index 271b89e08..bc232ce05 100644 --- a/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationBuilders.kt +++ b/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationBuilders.kt @@ -66,14 +66,15 @@ import kotlin.coroutines.EmptyCoroutineContext public fun runSimulation( context: CoroutineContext = EmptyCoroutineContext, scheduler: SimulationDispatcher = SimulationDispatcher(), - body: suspend SimulationCoroutineScope.() -> Unit + body: suspend SimulationCoroutineScope.() -> Unit, ) { val (safeContext, job, dispatcher) = context.checkArguments(scheduler) val startingJobs = job.activeJobs() val scope = SimulationCoroutineScope(safeContext) - val deferred = scope.async { - body(scope) - } + val deferred = + scope.async { + body(scope) + } dispatcher.advanceUntilIdle() deferred.getCompletionExceptionOrNull()?.let { throw it @@ -105,9 +106,10 @@ private fun Job.activeJobs(): Set { * Convert a [ContinuationInterceptor] into a [SimulationDispatcher] if possible. */ internal fun ContinuationInterceptor.asSimulationDispatcher(): SimulationDispatcher { - val provider = this as? DispatcherProvider ?: throw IllegalArgumentException( - "DispatcherProvider such as SimulatorCoroutineDispatcher as the ContinuationInterceptor(Dispatcher) is required" - ) + val provider = + this as? DispatcherProvider ?: throw IllegalArgumentException( + "DispatcherProvider such as SimulatorCoroutineDispatcher as the ContinuationInterceptor(Dispatcher) is required", + ) return provider.dispatcher as? SimulationDispatcher ?: throw IllegalArgumentException("Active dispatcher is not a SimulationDispatcher") } diff --git a/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationCoroutineScope.kt b/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationCoroutineScope.kt index ca49fc53f..a29e94044 100644 --- a/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationCoroutineScope.kt +++ b/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationCoroutineScope.kt @@ -43,7 +43,7 @@ public interface SimulationCoroutineScope : CoroutineScope, SimulationController */ public fun SimulationCoroutineScope( context: CoroutineContext = EmptyCoroutineContext, - scheduler: SimulationDispatcher = SimulationDispatcher() + scheduler: SimulationDispatcher = SimulationDispatcher(), ): SimulationCoroutineScope { var safeContext = context val simulationDispatcher: SimulationDispatcher diff --git a/opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/TaskQueueTest.kt b/opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/TaskQueueTest.kt index 56dd83aa5..eaafedfde 100644 --- a/opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/TaskQueueTest.kt +++ b/opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/TaskQueueTest.kt @@ -47,7 +47,7 @@ class TaskQueueTest { fun testPollEmpty() { assertAll( { assertEquals(Long.MAX_VALUE, queue.peekDeadline()) }, - { assertNull(queue.poll()) } + { assertNull(queue.poll()) }, ) } @@ -63,7 +63,7 @@ class TaskQueueTest { assertAll( { assertEquals(100, queue.peekDeadline()) }, { assertEquals(entry, queue.poll()) }, - { assertNull(queue.poll()) } + { assertNull(queue.poll()) }, ) } @@ -86,7 +86,7 @@ class TaskQueueTest { { assertEquals(entryB, queue.poll()) }, { assertEquals(entryC, queue.poll()) }, { assertEquals(entryA, queue.poll()) }, - { assertNull(queue.poll()) } + { assertNull(queue.poll()) }, ) } @@ -109,7 +109,7 @@ class TaskQueueTest { { assertEquals(entryA, queue.poll()) }, { assertEquals(entryB, queue.poll()) }, { assertEquals(entryC, queue.poll()) }, - { assertNull(queue.poll()) } + { assertNull(queue.poll()) }, ) } @@ -136,7 +136,7 @@ class TaskQueueTest { { assertEquals(entryD, queue.poll()) }, { assertEquals(entryC, queue.poll()) }, { assertEquals(entryA, queue.poll()) }, - { assertNull(queue.poll()) } + { assertNull(queue.poll()) }, ) } @@ -160,7 +160,7 @@ class TaskQueueTest { { assertEquals(20, queue.peekDeadline()) }, { assertEquals(entryB, queue.poll()) }, { assertEquals(entryC, queue.poll()) }, - { assertNull(queue.poll()) } + { assertNull(queue.poll()) }, ) } @@ -184,7 +184,7 @@ class TaskQueueTest { { assertEquals(58, queue.peekDeadline()) }, { assertEquals(entryC, queue.poll()) }, { assertEquals(entryA, queue.poll()) }, - { assertNull(queue.poll()) } + { assertNull(queue.poll()) }, ) } @@ -208,7 +208,7 @@ class TaskQueueTest { { assertEquals(20, queue.peekDeadline()) }, { assertEquals(entryB, queue.poll()) }, { assertEquals(entryA, queue.poll()) }, - { assertNull(queue.poll()) } + { assertNull(queue.poll()) }, ) } @@ -228,7 +228,7 @@ class TaskQueueTest { assertAll( { assertFalse(queue.remove(10, 1)) }, - { assertFalse(queue.remove(58, 2)) } + { assertFalse(queue.remove(58, 2)) }, ) } } diff --git a/opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/kotlin/SimulationBuildersTest.kt b/opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/kotlin/SimulationBuildersTest.kt index 26419a506..b25025ef4 100644 --- a/opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/kotlin/SimulationBuildersTest.kt +++ b/opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/kotlin/SimulationBuildersTest.kt @@ -38,31 +38,33 @@ import org.junit.jupiter.api.assertThrows */ class SimulationBuildersTest { @Test - fun testDelay() = runSimulation { - assertEquals(0, currentTime) - delay(100) - assertEquals(100, currentTime) - } + fun testDelay() = + runSimulation { + assertEquals(0, currentTime) + delay(100) + assertEquals(100, currentTime) + } @Test - fun testController() = runSimulation { - var completed = false + fun testController() = + runSimulation { + var completed = false - launch { - delay(20) - completed = true - } + launch { + delay(20) + completed = true + } - advanceBy(10) - assertFalse(completed) - advanceBy(11) - assertTrue(completed) + advanceBy(10) + assertFalse(completed) + advanceBy(11) + assertTrue(completed) - completed = false - launch { completed = true } - runCurrent() - assertTrue(completed) - } + completed = false + launch { completed = true } + runCurrent() + assertTrue(completed) + } @Test fun testFailOnActiveJobs() { diff --git a/opendc-simulator/opendc-simulator-flow/src/jmh/kotlin/org/opendc/simulator/flow2/FlowBenchmarks.kt b/opendc-simulator/opendc-simulator-flow/src/jmh/kotlin/org/opendc/simulator/flow2/FlowBenchmarks.kt index 59dd3badc..6bf9c2a21 100644 --- a/opendc-simulator/opendc-simulator-flow/src/jmh/kotlin/org/opendc/simulator/flow2/FlowBenchmarks.kt +++ b/opendc-simulator/opendc-simulator-flow/src/jmh/kotlin/org/opendc/simulator/flow2/FlowBenchmarks.kt @@ -50,11 +50,12 @@ class FlowBenchmarks { fun setUp() { val random = ThreadLocalRandom.current() val traceSize = 10_000_000 - trace = TraceFlowSource.Trace( - LongArray(traceSize) { (it + 1) * 1000L }, - FloatArray(traceSize) { random.nextFloat(0.0f, 4500.0f) }, - traceSize - ) + trace = + TraceFlowSource.Trace( + LongArray(traceSize) { (it + 1) * 1000L }, + FloatArray(traceSize) { random.nextFloat(0.0f, 4500.0f) }, + traceSize, + ) } @Benchmark diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowEngineTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowEngineTest.kt index 467bf334e..413a58782 100644 --- a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowEngineTest.kt +++ b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowEngineTest.kt @@ -37,161 +37,174 @@ import org.opendc.simulator.kotlin.runSimulation */ class FlowEngineTest { @Test - fun testSmoke() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testSmoke() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val multiplexer = MaxMinFlowMultiplexer(graph) - val sink = SimpleFlowSink(graph, 2.0f) + val multiplexer = MaxMinFlowMultiplexer(graph) + val sink = SimpleFlowSink(graph, 2.0f) - graph.connect(multiplexer.newOutput(), sink.input) + graph.connect(multiplexer.newOutput(), sink.input) - val sourceA = SimpleFlowSource(graph, 2000.0f, 0.8f) - val sourceB = SimpleFlowSource(graph, 2000.0f, 0.8f) + val sourceA = SimpleFlowSource(graph, 2000.0f, 0.8f) + val sourceB = SimpleFlowSource(graph, 2000.0f, 0.8f) - graph.connect(sourceA.output, multiplexer.newInput()) - graph.connect(sourceB.output, multiplexer.newInput()) - } + graph.connect(sourceA.output, multiplexer.newInput()) + graph.connect(sourceB.output, multiplexer.newInput()) + } @Test - fun testConnectInvalidInlet() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testConnectInvalidInlet() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val inlet = mockk() - val source = SimpleFlowSource(graph, 2000.0f, 0.8f) - assertThrows { graph.connect(source.output, inlet) } - } + val inlet = mockk() + val source = SimpleFlowSource(graph, 2000.0f, 0.8f) + assertThrows { graph.connect(source.output, inlet) } + } @Test - fun testConnectInvalidOutlet() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testConnectInvalidOutlet() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val outlet = mockk() - val sink = SimpleFlowSink(graph, 2.0f) - assertThrows { graph.connect(outlet, sink.input) } - } + val outlet = mockk() + val sink = SimpleFlowSink(graph, 2.0f) + assertThrows { graph.connect(outlet, sink.input) } + } @Test - fun testConnectInletBelongsToDifferentGraph() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graphA = engine.newGraph() - val graphB = engine.newGraph() + fun testConnectInletBelongsToDifferentGraph() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graphA = engine.newGraph() + val graphB = engine.newGraph() - val sink = SimpleFlowSink(graphB, 2.0f) - val source = SimpleFlowSource(graphA, 2000.0f, 0.8f) + val sink = SimpleFlowSink(graphB, 2.0f) + val source = SimpleFlowSource(graphA, 2000.0f, 0.8f) - assertThrows { graphA.connect(source.output, sink.input) } - } + assertThrows { graphA.connect(source.output, sink.input) } + } @Test - fun testConnectOutletBelongsToDifferentGraph() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graphA = engine.newGraph() - val graphB = engine.newGraph() + fun testConnectOutletBelongsToDifferentGraph() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graphA = engine.newGraph() + val graphB = engine.newGraph() - val sink = SimpleFlowSink(graphA, 2.0f) - val source = SimpleFlowSource(graphB, 2000.0f, 0.8f) + val sink = SimpleFlowSink(graphA, 2.0f) + val source = SimpleFlowSource(graphB, 2000.0f, 0.8f) - assertThrows { graphA.connect(source.output, sink.input) } - } + assertThrows { graphA.connect(source.output, sink.input) } + } @Test - fun testConnectInletAlreadyConnected() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testConnectInletAlreadyConnected() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val sink = SimpleFlowSink(graph, 2.0f) - val sourceA = SimpleFlowSource(graph, 2000.0f, 0.8f) - val sourceB = SimpleFlowSource(graph, 2000.0f, 0.8f) + val sink = SimpleFlowSink(graph, 2.0f) + val sourceA = SimpleFlowSource(graph, 2000.0f, 0.8f) + val sourceB = SimpleFlowSource(graph, 2000.0f, 0.8f) - graph.connect(sourceA.output, sink.input) - assertThrows { graph.connect(sourceB.output, sink.input) } - } + graph.connect(sourceA.output, sink.input) + assertThrows { graph.connect(sourceB.output, sink.input) } + } @Test - fun testConnectOutletAlreadyConnected() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testConnectOutletAlreadyConnected() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val sinkA = SimpleFlowSink(graph, 2.0f) - val sinkB = SimpleFlowSink(graph, 2.0f) - val source = SimpleFlowSource(graph, 2000.0f, 0.8f) + val sinkA = SimpleFlowSink(graph, 2.0f) + val sinkB = SimpleFlowSink(graph, 2.0f) + val source = SimpleFlowSource(graph, 2000.0f, 0.8f) - graph.connect(source.output, sinkA.input) - assertThrows { graph.connect(source.output, sinkB.input) } - } + graph.connect(source.output, sinkA.input) + assertThrows { graph.connect(source.output, sinkB.input) } + } @Test - fun testDisconnectInletInvalid() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testDisconnectInletInvalid() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val inlet = mockk() - assertThrows { graph.disconnect(inlet) } - } + val inlet = mockk() + assertThrows { graph.disconnect(inlet) } + } @Test - fun testDisconnectOutletInvalid() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testDisconnectOutletInvalid() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val outlet = mockk() - assertThrows { graph.disconnect(outlet) } - } + val outlet = mockk() + assertThrows { graph.disconnect(outlet) } + } @Test - fun testDisconnectInletInvalidGraph() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graphA = engine.newGraph() - val graphB = engine.newGraph() + fun testDisconnectInletInvalidGraph() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graphA = engine.newGraph() + val graphB = engine.newGraph() - val sink = SimpleFlowSink(graphA, 2.0f) + val sink = SimpleFlowSink(graphA, 2.0f) - assertThrows { graphB.disconnect(sink.input) } - } + assertThrows { graphB.disconnect(sink.input) } + } @Test - fun testDisconnectOutletInvalidGraph() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graphA = engine.newGraph() - val graphB = engine.newGraph() + fun testDisconnectOutletInvalidGraph() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graphA = engine.newGraph() + val graphB = engine.newGraph() - val source = SimpleFlowSource(graphA, 2000.0f, 0.8f) + val source = SimpleFlowSource(graphA, 2000.0f, 0.8f) - assertThrows { graphB.disconnect(source.output) } - } + assertThrows { graphB.disconnect(source.output) } + } @Test - fun testInletEquality() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testInletEquality() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val sinkA = SimpleFlowSink(graph, 2.0f) - val sinkB = SimpleFlowSink(graph, 2.0f) + val sinkA = SimpleFlowSink(graph, 2.0f) + val sinkB = SimpleFlowSink(graph, 2.0f) - val multiplexer = MaxMinFlowMultiplexer(graph) + val multiplexer = MaxMinFlowMultiplexer(graph) - assertEquals(sinkA.input, sinkA.input) - assertNotEquals(sinkA.input, sinkB.input) + assertEquals(sinkA.input, sinkA.input) + assertNotEquals(sinkA.input, sinkB.input) - assertNotEquals(multiplexer.newInput(), multiplexer.newInput()) - } + assertNotEquals(multiplexer.newInput(), multiplexer.newInput()) + } @Test - fun testOutletEquality() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testOutletEquality() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val sourceA = SimpleFlowSource(graph, 2000.0f, 0.8f) - val sourceB = SimpleFlowSource(graph, 2000.0f, 0.8f) + val sourceA = SimpleFlowSource(graph, 2000.0f, 0.8f) + val sourceB = SimpleFlowSource(graph, 2000.0f, 0.8f) - val multiplexer = MaxMinFlowMultiplexer(graph) + val multiplexer = MaxMinFlowMultiplexer(graph) - assertEquals(sourceA.output, sourceA.output) - assertNotEquals(sourceA.output, sourceB.output) + assertEquals(sourceA.output, sourceA.output) + assertNotEquals(sourceA.output, sourceB.output) - assertNotEquals(multiplexer.newOutput(), multiplexer.newOutput()) - } + assertNotEquals(multiplexer.newOutput(), multiplexer.newOutput()) + } } diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowTimerQueueTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowTimerQueueTest.kt index 1824959c4..059bd5f56 100644 --- a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowTimerQueueTest.kt +++ b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowTimerQueueTest.kt @@ -47,7 +47,7 @@ class FlowTimerQueueTest { fun testPollEmpty() { assertAll( { assertEquals(Long.MAX_VALUE, queue.peekDeadline()) }, - { assertNull(queue.poll(100L)) } + { assertNull(queue.poll(100L)) }, ) } @@ -66,7 +66,7 @@ class FlowTimerQueueTest { { assertEquals(100, queue.peekDeadline()) }, { assertNull(queue.poll(10L)) }, { assertEquals(entry, queue.poll(200L)) }, - { assertNull(queue.poll(200L)) } + { assertNull(queue.poll(200L)) }, ) } @@ -98,7 +98,7 @@ class FlowTimerQueueTest { { assertEquals(entryB, queue.poll(100L)) }, { assertEquals(entryC, queue.poll(100L)) }, { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) } + { assertNull(queue.poll(100L)) }, ) } @@ -137,7 +137,7 @@ class FlowTimerQueueTest { { assertEquals(entryD, queue.poll(100L)) }, { assertEquals(entryC, queue.poll(100L)) }, { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) } + { assertNull(queue.poll(100L)) }, ) } @@ -172,7 +172,7 @@ class FlowTimerQueueTest { { assertEquals(entryA, queue.poll(100L)) }, { assertEquals(entryB, queue.poll(100L)) }, { assertEquals(entryC, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) } + { assertNull(queue.poll(100L)) }, ) } @@ -207,7 +207,7 @@ class FlowTimerQueueTest { { assertEquals(entryC, queue.poll(100L)) }, { assertEquals(entryB, queue.poll(100L)) }, { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) } + { assertNull(queue.poll(100L)) }, ) } @@ -242,7 +242,7 @@ class FlowTimerQueueTest { { assertEquals(entryB, queue.poll(100L)) }, { assertEquals(entryC, queue.poll(100L)) }, { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) } + { assertNull(queue.poll(100L)) }, ) } @@ -277,7 +277,7 @@ class FlowTimerQueueTest { { assertEquals(entryB, queue.poll(100L)) }, { assertEquals(entryC, queue.poll(100L)) }, { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) } + { assertNull(queue.poll(100L)) }, ) } @@ -311,7 +311,7 @@ class FlowTimerQueueTest { { assertEquals(20, queue.peekDeadline()) }, { assertEquals(entryB, queue.poll(100L)) }, { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) } + { assertNull(queue.poll(100L)) }, ) } @@ -345,7 +345,7 @@ class FlowTimerQueueTest { { assertEquals(58, queue.peekDeadline()) }, { assertEquals(entryC, queue.poll(100L)) }, { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) } + { assertNull(queue.poll(100L)) }, ) } @@ -379,7 +379,7 @@ class FlowTimerQueueTest { { assertEquals(20, queue.peekDeadline()) }, { assertEquals(entryB, queue.poll(100L)) }, { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) } + { assertNull(queue.poll(100L)) }, ) } } diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexerTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexerTest.kt index d17958418..2aef51743 100644 --- a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexerTest.kt +++ b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexerTest.kt @@ -39,33 +39,34 @@ class ForwardingFlowMultiplexerTest { * Test a trace workload. */ @Test - fun testTrace() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testTrace() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val switch = ForwardingFlowMultiplexer(graph) - val sink = SimpleFlowSink(graph, 3200.0f) - graph.connect(switch.newOutput(), sink.input) + val switch = ForwardingFlowMultiplexer(graph) + val sink = SimpleFlowSink(graph, 3200.0f) + graph.connect(switch.newOutput(), sink.input) - yield() + yield() - assertEquals(sink.capacity, switch.capacity) { "Capacity is not detected" } + assertEquals(sink.capacity, switch.capacity) { "Capacity is not detected" } - val workload = - TraceFlowSource( - graph, - TraceFlowSource.Trace( - longArrayOf(1000, 2000, 3000, 4000), - floatArrayOf(28.0f, 3500.0f, 0.0f, 183.0f), - 4 + val workload = + TraceFlowSource( + graph, + TraceFlowSource.Trace( + longArrayOf(1000, 2000, 3000, 4000), + floatArrayOf(28.0f, 3500.0f, 0.0f, 183.0f), + 4, + ), ) - ) - graph.connect(workload.output, switch.newInput()) + graph.connect(workload.output, switch.newInput()) - advanceUntilIdle() + advanceUntilIdle() - assertAll( - { assertEquals(4000, timeSource.millis()) { "Took enough time" } } - ) - } + assertAll( + { assertEquals(4000, timeSource.millis()) { "Took enough time" } }, + ) + } } diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexerTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexerTest.kt index ebae2d4e6..0bcf4a3fe 100644 --- a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexerTest.kt +++ b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexerTest.kt @@ -34,21 +34,22 @@ import org.opendc.simulator.kotlin.runSimulation */ class MaxMinFlowMultiplexerTest { @Test - fun testSmoke() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val switch = MaxMinFlowMultiplexer(graph) + fun testSmoke() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val switch = MaxMinFlowMultiplexer(graph) - val sinks = List(2) { SimpleFlowSink(graph, 2000.0f) } - for (source in sinks) { - graph.connect(switch.newOutput(), source.input) - } + val sinks = List(2) { SimpleFlowSink(graph, 2000.0f) } + for (source in sinks) { + graph.connect(switch.newOutput(), source.input) + } - val source = SimpleFlowSource(graph, 2000.0f, 1.0f) - graph.connect(source.output, switch.newInput()) + val source = SimpleFlowSource(graph, 2000.0f, 1.0f) + graph.connect(source.output, switch.newInput()) - advanceUntilIdle() + advanceUntilIdle() - assertEquals(500, timeSource.millis()) - } + assertEquals(500, timeSource.millis()) + } } diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/sink/FlowSinkTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/sink/FlowSinkTest.kt index ea516c63f..7085a4b90 100644 --- a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/sink/FlowSinkTest.kt +++ b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/sink/FlowSinkTest.kt @@ -36,82 +36,89 @@ import java.util.concurrent.ThreadLocalRandom */ class FlowSinkTest { @Test - fun testSmoke() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testSmoke() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val sink = SimpleFlowSink(graph, 1.0f) - val source = SimpleFlowSource(graph, 2.0f, 1.0f) + val sink = SimpleFlowSink(graph, 1.0f) + val source = SimpleFlowSource(graph, 2.0f, 1.0f) - graph.connect(source.output, sink.input) - advanceUntilIdle() + graph.connect(source.output, sink.input) + advanceUntilIdle() - assertEquals(2000, timeSource.millis()) - } + assertEquals(2000, timeSource.millis()) + } @Test - fun testAdjustCapacity() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testAdjustCapacity() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val sink = SimpleFlowSink(graph, 1.0f) - val source = SimpleFlowSource(graph, 2.0f, 1.0f) + val sink = SimpleFlowSink(graph, 1.0f) + val source = SimpleFlowSource(graph, 2.0f, 1.0f) - graph.connect(source.output, sink.input) + graph.connect(source.output, sink.input) - delay(1000) - sink.capacity = 0.5f + delay(1000) + sink.capacity = 0.5f - advanceUntilIdle() + advanceUntilIdle() - assertEquals(3000, timeSource.millis()) - } + assertEquals(3000, timeSource.millis()) + } @Test - fun testUtilization() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() + fun testUtilization() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() - val sink = SimpleFlowSink(graph, 1.0f) - val source = SimpleFlowSource(graph, 2.0f, 0.5f) + val sink = SimpleFlowSink(graph, 1.0f) + val source = SimpleFlowSource(graph, 2.0f, 0.5f) - graph.connect(source.output, sink.input) - advanceUntilIdle() + graph.connect(source.output, sink.input) + advanceUntilIdle() - assertEquals(4000, timeSource.millis()) - } + assertEquals(4000, timeSource.millis()) + } @Test - fun testFragments() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val sink = SimpleFlowSink(graph, 1.0f) - val trace = TraceFlowSource.Trace( - longArrayOf(1000, 2000, 3000, 4000), - floatArrayOf(1.0f, 0.5f, 2.0f, 1.0f), - 4 - ) - val source = TraceFlowSource( - graph, - trace - ) - - graph.connect(source.output, sink.input) - advanceUntilIdle() - - assertEquals(4000, timeSource.millis()) - } + fun testFragments() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + + val sink = SimpleFlowSink(graph, 1.0f) + val trace = + TraceFlowSource.Trace( + longArrayOf(1000, 2000, 3000, 4000), + floatArrayOf(1.0f, 0.5f, 2.0f, 1.0f), + 4, + ) + val source = + TraceFlowSource( + graph, + trace, + ) + + graph.connect(source.output, sink.input) + advanceUntilIdle() + + assertEquals(4000, timeSource.millis()) + } @Test fun benchmarkSink() { val random = ThreadLocalRandom.current() val traceSize = 10000000 - val trace = TraceFlowSource.Trace( - LongArray(traceSize) { it * 1000L }, - FloatArray(traceSize) { random.nextDouble(0.0, 4500.0).toFloat() }, - traceSize - ) + val trace = + TraceFlowSource.Trace( + LongArray(traceSize) { it * 1000L }, + FloatArray(traceSize) { random.nextDouble(0.0, 4500.0).toFloat() }, + traceSize, + ) return runSimulation { val engine = FlowEngine.create(dispatcher) diff --git a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt index 181d9a208..4655bfeaf 100644 --- a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt +++ b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt @@ -42,105 +42,112 @@ import org.opendc.simulator.kotlin.runSimulation */ class SimNetworkSinkTest { @Test - fun testInitialState() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, /*capacity*/ 100.0f) - - assertAll( - { assertFalse(sink.isConnected) }, - { assertNull(sink.link) }, - { assertEquals(100.0f, sink.capacity) } - ) - } + fun testInitialState() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val sink = SimNetworkSink(graph, 100.0f) + + assertAll( + { assertFalse(sink.isConnected) }, + { assertNull(sink.link) }, + { assertEquals(100.0f, sink.capacity) }, + ) + } @Test - fun testDisconnectIdempotent() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, /*capacity*/ 100.0f) - - assertDoesNotThrow { sink.disconnect() } - assertFalse(sink.isConnected) - } + fun testDisconnectIdempotent() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val sink = SimNetworkSink(graph, 100.0f) + + assertDoesNotThrow { sink.disconnect() } + assertFalse(sink.isConnected) + } @Test - fun testConnectCircular() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, /*capacity*/ 100.0f) - - assertThrows { - sink.connect(sink) + fun testConnectCircular() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val sink = SimNetworkSink(graph, 100.0f) + + assertThrows { + sink.connect(sink) + } } - } @Test - fun testConnectAlreadyConnectedTarget() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, /*capacity*/ 100.0f) - val source = mockk(relaxUnitFun = true) - every { source.isConnected } returns true - - assertThrows { - sink.connect(source) + fun testConnectAlreadyConnectedTarget() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val sink = SimNetworkSink(graph, 100.0f) + val source = mockk(relaxUnitFun = true) + every { source.isConnected } returns true + + assertThrows { + sink.connect(source) + } } - } @Test - fun testConnectAlreadyConnected() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, /*capacity*/ 100.0f) - val source1 = TestSource(graph) + fun testConnectAlreadyConnected() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val sink = SimNetworkSink(graph, 100.0f) + val source1 = TestSource(graph) - val source2 = mockk(relaxUnitFun = true) + val source2 = mockk(relaxUnitFun = true) - every { source2.isConnected } returns false + every { source2.isConnected } returns false - sink.connect(source1) - assertThrows { - sink.connect(source2) + sink.connect(source1) + assertThrows { + sink.connect(source2) + } } - } @Test - fun testConnect() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, /*capacity*/ 100.0f) - val source = TestSource(graph) + fun testConnect() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val sink = SimNetworkSink(graph, 100.0f) + val source = TestSource(graph) - sink.connect(source) + sink.connect(source) - yield() + yield() - assertAll( - { assertTrue(sink.isConnected) }, - { assertTrue(source.isConnected) }, - { assertEquals(100.0f, source.outlet.capacity) } - ) + assertAll( + { assertTrue(sink.isConnected) }, + { assertTrue(source.isConnected) }, + { assertEquals(100.0f, source.outlet.capacity) }, + ) - verify { source.logic.onUpdate(any(), any()) } - } + verify { source.logic.onUpdate(any(), any()) } + } @Test - fun testDisconnect() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, /*capacity*/ 100.0f) - val source = TestSource(graph) - - sink.connect(source) - sink.disconnect() - - yield() - - assertAll( - { assertFalse(sink.isConnected) }, - { assertFalse(source.isConnected) }, - { assertEquals(0.0f, source.outlet.capacity) } - ) - } + fun testDisconnect() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val sink = SimNetworkSink(graph, 100.0f) + val source = TestSource(graph) + + sink.connect(source) + sink.disconnect() + + yield() + + assertAll( + { assertFalse(sink.isConnected) }, + { assertFalse(source.isConnected) }, + { assertEquals(0.0f, source.outlet.capacity) }, + ) + } } diff --git a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt index 4a4894787..b5a00ffcd 100644 --- a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt +++ b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt @@ -37,39 +37,41 @@ import org.opendc.simulator.kotlin.runSimulation */ class SimNetworkSwitchVirtualTest { @Test - fun testConnect() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, /*capacity*/ 100.0f) - val source = TestSource(graph) - val switch = SimNetworkSwitchVirtual(graph) + fun testConnect() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val sink = SimNetworkSink(graph, 100.0f) + val source = TestSource(graph) + val switch = SimNetworkSwitchVirtual(graph) - switch.newPort().connect(sink) - switch.newPort().connect(source) + switch.newPort().connect(sink) + switch.newPort().connect(source) - yield() + yield() - assertAll( - { assertTrue(sink.isConnected) }, - { assertTrue(source.isConnected) }, - { assertEquals(100.0f, source.outlet.capacity) } - ) + assertAll( + { assertTrue(sink.isConnected) }, + { assertTrue(source.isConnected) }, + { assertEquals(100.0f, source.outlet.capacity) }, + ) - verify { source.logic.onUpdate(any(), any()) } - } + verify { source.logic.onUpdate(any(), any()) } + } @Test - fun testConnectClosedPort() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, /*capacity*/ 100.0f) - val switch = SimNetworkSwitchVirtual(graph) + fun testConnectClosedPort() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val sink = SimNetworkSink(graph, 100.0f) + val switch = SimNetworkSwitchVirtual(graph) - val port = switch.newPort() - port.close() + val port = switch.newPort() + port.close() - assertThrows { - port.connect(sink) + assertThrows { + port.connect(sink) + } } - } } diff --git a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/TestSource.kt b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/TestSource.kt index f69db7a2b..298a5d486 100644 --- a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/TestSource.kt +++ b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/TestSource.kt @@ -45,7 +45,10 @@ class TestSource(graph: FlowGraph) : SimNetworkPort(), FlowStageLogic { outlet.push(80.0f) } - override fun onUpdate(ctx: FlowStage, now: Long): Long = Long.MAX_VALUE + override fun onUpdate( + ctx: FlowStage, + now: Long, + ): Long = Long.MAX_VALUE override fun getOutlet(): Outlet = outlet diff --git a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPduTest.kt b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPduTest.kt index f596ca4ef..9df72c49f 100644 --- a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPduTest.kt +++ b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPduTest.kt @@ -34,94 +34,100 @@ import org.opendc.simulator.kotlin.runSimulation */ internal class SimPduTest { @Test - fun testZeroOutlets() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, /*capacity*/ 100.0f) - val pdu = SimPdu(graph) - source.connect(pdu) + fun testZeroOutlets() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source = SimPowerSource(graph, 100.0f) + val pdu = SimPdu(graph) + source.connect(pdu) - yield() + yield() - assertEquals(0.0f, source.powerDraw) - } + assertEquals(0.0f, source.powerDraw) + } @Test - fun testSingleOutlet() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, /*capacity*/ 100.0f) - val pdu = SimPdu(graph) - source.connect(pdu) - pdu.newOutlet().connect(TestInlet(graph)) - - yield() - - assertEquals(100.0f, source.powerDraw) - } + fun testSingleOutlet() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source = SimPowerSource(graph, 100.0f) + val pdu = SimPdu(graph) + source.connect(pdu) + pdu.newOutlet().connect(TestInlet(graph)) + + yield() + + assertEquals(100.0f, source.powerDraw) + } @Test - fun testDoubleOutlet() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, /*capacity*/ 200.0f) - val pdu = SimPdu(graph) - source.connect(pdu) + fun testDoubleOutlet() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source = SimPowerSource(graph, 200.0f) + val pdu = SimPdu(graph) + source.connect(pdu) - pdu.newOutlet().connect(TestInlet(graph)) - pdu.newOutlet().connect(TestInlet(graph)) + pdu.newOutlet().connect(TestInlet(graph)) + pdu.newOutlet().connect(TestInlet(graph)) - yield() + yield() - assertEquals(200.0f, source.powerDraw) - } + assertEquals(200.0f, source.powerDraw) + } @Test - fun testDisconnect() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, /*capacity*/ 300.0f) - val pdu = SimPdu(graph) - source.connect(pdu) - - val outlet = pdu.newOutlet() - outlet.connect(TestInlet(graph)) - outlet.disconnect() + fun testDisconnect() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source = SimPowerSource(graph, 300.0f) + val pdu = SimPdu(graph) + source.connect(pdu) + + val outlet = pdu.newOutlet() + outlet.connect(TestInlet(graph)) + outlet.disconnect() - yield() + yield() - assertEquals(0.0f, source.powerDraw) - } + assertEquals(0.0f, source.powerDraw) + } @Test - fun testLoss() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, /*capacity*/ 500.0f) - // https://download.schneider-electric.com/files?p_Doc_Ref=SPD_NRAN-66CK3D_EN - val pdu = SimPdu(graph, /*idlePower*/ 1.5f, /*lossCoefficient*/ 0.015f) - source.connect(pdu) - pdu.newOutlet().connect(TestInlet(graph)) - - yield() - - assertEquals(251.5f, source.powerDraw, 0.01f) - } + fun testLoss() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source = SimPowerSource(graph, 500.0f) + // https://download.schneider-electric.com/files?p_Doc_Ref=SPD_NRAN-66CK3D_EN + val pdu = SimPdu(graph, 1.5f, 0.015f) + source.connect(pdu) + pdu.newOutlet().connect(TestInlet(graph)) + + yield() + + assertEquals(251.5f, source.powerDraw, 0.01f) + } @Test - fun testOutletClose() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, /*capacity*/ 100.0f) - val pdu = SimPdu(graph) - source.connect(pdu) - val outlet = pdu.newOutlet() - outlet.close() - - yield() - - assertThrows { - outlet.connect(TestInlet(graph)) + fun testOutletClose() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source = SimPowerSource(graph, 100.0f) + val pdu = SimPdu(graph) + source.connect(pdu) + val outlet = pdu.newOutlet() + outlet.close() + + yield() + + assertThrows { + outlet.connect(TestInlet(graph)) + } } - } } diff --git a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPowerSourceTest.kt b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPowerSourceTest.kt index 03c942b4c..bbc9ad92d 100644 --- a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPowerSourceTest.kt +++ b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPowerSourceTest.kt @@ -41,108 +41,115 @@ import org.opendc.simulator.kotlin.runSimulation */ internal class SimPowerSourceTest { @Test - fun testInitialState() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, /*capacity*/ 100.0f) - - yield() - - assertAll( - { assertFalse(source.isConnected) }, - { assertNull(source.inlet) }, - { assertEquals(100.0f, source.capacity) } - ) - } + fun testInitialState() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source = SimPowerSource(graph, 100.0f) + + yield() + + assertAll( + { assertFalse(source.isConnected) }, + { assertNull(source.inlet) }, + { assertEquals(100.0f, source.capacity) }, + ) + } @Test - fun testDisconnectIdempotent() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, /*capacity*/ 100.0f) - - assertDoesNotThrow { source.disconnect() } - assertFalse(source.isConnected) - } + fun testDisconnectIdempotent() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source = SimPowerSource(graph, 100.0f) + + assertDoesNotThrow { source.disconnect() } + assertFalse(source.isConnected) + } @Test - fun testConnect() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, /*capacity*/ 100.0f) - val inlet = TestInlet(graph) + fun testConnect() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source = SimPowerSource(graph, 100.0f) + val inlet = TestInlet(graph) - source.connect(inlet) + source.connect(inlet) - yield() + yield() - assertAll( - { assertTrue(source.isConnected) }, - { assertEquals(inlet, source.inlet) }, - { assertTrue(inlet.isConnected) }, - { assertEquals(source, inlet.outlet) }, - { assertEquals(100.0f, source.powerDraw) } - ) - } + assertAll( + { assertTrue(source.isConnected) }, + { assertEquals(inlet, source.inlet) }, + { assertTrue(inlet.isConnected) }, + { assertEquals(source, inlet.outlet) }, + { assertEquals(100.0f, source.powerDraw) }, + ) + } @Test - fun testDisconnect() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, /*capacity*/ 100.0f) - val inlet = TestInlet(graph) + fun testDisconnect() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source = SimPowerSource(graph, 100.0f) + val inlet = TestInlet(graph) - source.connect(inlet) - source.disconnect() + source.connect(inlet) + source.disconnect() - yield() + yield() - assertEquals(0.0f, inlet.flowOutlet.capacity) - } + assertEquals(0.0f, inlet.flowOutlet.capacity) + } @Test - fun testDisconnectAssertion() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, /*capacity*/ 100.0f) + fun testDisconnectAssertion() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source = SimPowerSource(graph, 100.0f) - val inlet = mockk(relaxUnitFun = true) - every { inlet.isConnected } returns false - every { inlet.flowOutlet } returns TestInlet(graph).flowOutlet + val inlet = mockk(relaxUnitFun = true) + every { inlet.isConnected } returns false + every { inlet.flowOutlet } returns TestInlet(graph).flowOutlet - source.connect(inlet) - inlet.outlet = null + source.connect(inlet) + inlet.outlet = null - assertThrows { - source.disconnect() + assertThrows { + source.disconnect() + } } - } @Test - fun testOutletAlreadyConnected() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, /*capacity*/ 100.0f) - val inlet = TestInlet(graph) - - source.connect(inlet) - assertThrows { - source.connect(TestInlet(graph)) - } + fun testOutletAlreadyConnected() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source = SimPowerSource(graph, 100.0f) + val inlet = TestInlet(graph) - assertEquals(inlet, source.inlet) - } + source.connect(inlet) + assertThrows { + source.connect(TestInlet(graph)) + } + + assertEquals(inlet, source.inlet) + } @Test - fun testInletAlreadyConnected() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, /*capacity*/ 100.0f) - val inlet = mockk(relaxUnitFun = true) - every { inlet.isConnected } returns true - - assertThrows { - source.connect(inlet) + fun testInletAlreadyConnected() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source = SimPowerSource(graph, 100.0f) + val inlet = mockk(relaxUnitFun = true) + every { inlet.isConnected } returns true + + assertThrows { + source.connect(inlet) + } } - } } diff --git a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimUpsTest.kt b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimUpsTest.kt index 89fede63e..cbd23887c 100644 --- a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimUpsTest.kt +++ b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimUpsTest.kt @@ -34,71 +34,75 @@ import org.opendc.simulator.kotlin.runSimulation */ internal class SimUpsTest { @Test - fun testSingleInlet() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, /*capacity*/ 200.0f) - val ups = SimUps(graph) - source.connect(ups.newInlet()) - ups.connect(TestInlet(graph)) + fun testSingleInlet() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source = SimPowerSource(graph, 200.0f) + val ups = SimUps(graph) + source.connect(ups.newInlet()) + ups.connect(TestInlet(graph)) - yield() + yield() - assertEquals(100.0f, source.powerDraw) - } + assertEquals(100.0f, source.powerDraw) + } @Test - fun testDoubleInlet() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source1 = SimPowerSource(graph, /*capacity*/ 200.0f) - val source2 = SimPowerSource(graph, /*capacity*/ 200.0f) - val ups = SimUps(graph) - source1.connect(ups.newInlet()) - source2.connect(ups.newInlet()) - - ups.connect(TestInlet(graph)) - - yield() - - assertAll( - { assertEquals(50.0f, source1.powerDraw) }, - { assertEquals(50.0f, source2.powerDraw) } - ) - } + fun testDoubleInlet() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source1 = SimPowerSource(graph, 200.0f) + val source2 = SimPowerSource(graph, 200.0f) + val ups = SimUps(graph) + source1.connect(ups.newInlet()) + source2.connect(ups.newInlet()) + + ups.connect(TestInlet(graph)) + + yield() + + assertAll( + { assertEquals(50.0f, source1.powerDraw) }, + { assertEquals(50.0f, source2.powerDraw) }, + ) + } @Test - fun testLoss() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, /*capacity*/ 500.0f) - // https://download.schneider-electric.com/files?p_Doc_Ref=SPD_NRAN-66CK3D_EN - val ups = SimUps(graph, /*idlePower*/ 4.0f, /*lossCoefficient*/ 0.05f) - source.connect(ups.newInlet()) - ups.connect(TestInlet(graph)) + fun testLoss() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source = SimPowerSource(graph, 500.0f) + // https://download.schneider-electric.com/files?p_Doc_Ref=SPD_NRAN-66CK3D_EN + val ups = SimUps(graph, 4.0f, 0.05f) + source.connect(ups.newInlet()) + ups.connect(TestInlet(graph)) - yield() + yield() - assertEquals(109.0f, source.powerDraw, 0.01f) - } + assertEquals(109.0f, source.powerDraw, 0.01f) + } @Test - fun testDisconnect() = runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source1 = SimPowerSource(graph, /*capacity*/ 200.0f) - val source2 = SimPowerSource(graph, /*capacity*/ 200.0f) - val ups = SimUps(graph) - source1.connect(ups.newInlet()) - source2.connect(ups.newInlet()) + fun testDisconnect() = + runSimulation { + val engine = FlowEngine.create(dispatcher) + val graph = engine.newGraph() + val source1 = SimPowerSource(graph, 200.0f) + val source2 = SimPowerSource(graph, 200.0f) + val ups = SimUps(graph) + source1.connect(ups.newInlet()) + source2.connect(ups.newInlet()) - val inlet = TestInlet(graph) + val inlet = TestInlet(graph) - ups.connect(inlet) - ups.disconnect() + ups.connect(inlet) + ups.disconnect() - yield() + yield() - assertEquals(0.0f, inlet.flowOutlet.capacity) - } + assertEquals(0.0f, inlet.flowOutlet.capacity) + } } diff --git a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/TestInlet.kt b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/TestInlet.kt index d5f509e73..1c06acf4a 100644 --- a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/TestInlet.kt +++ b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/TestInlet.kt @@ -38,7 +38,10 @@ class TestInlet(graph: FlowGraph) : SimPowerInlet(), FlowStageLogic { flowOutlet.push(100.0f) } - override fun onUpdate(ctx: FlowStage, now: Long): Long = Long.MAX_VALUE + override fun onUpdate( + ctx: FlowStage, + now: Long, + ): Long = Long.MAX_VALUE override fun getFlowOutlet(): Outlet { return flowOutlet diff --git a/opendc-trace/opendc-trace-api/build.gradle.kts b/opendc-trace/opendc-trace-api/build.gradle.kts index 977eec0d3..514cd7772 100644 --- a/opendc-trace/opendc-trace-api/build.gradle.kts +++ b/opendc-trace/opendc-trace-api/build.gradle.kts @@ -22,7 +22,7 @@ description = "Workload trace library for OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/TableColumn.kt b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/TableColumn.kt index 0f75d8902..4e82e48a4 100644 --- a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/TableColumn.kt +++ b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/TableColumn.kt @@ -32,5 +32,5 @@ package org.opendc.trace public data class TableColumn( public val name: String, public val type: TableColumnType, - public val isNullable: Boolean = false + public val isNullable: Boolean = false, ) diff --git a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/TableReader.kt b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/TableReader.kt index 42b1c690b..95a589356 100644 --- a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/TableReader.kt +++ b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/TableReader.kt @@ -143,7 +143,10 @@ public interface TableReader : AutoCloseable { * @throws IllegalArgumentException if the column index is not valid for this reader or this type. * @return The value of the column as `List` or `null` if the column is null. */ - public fun getList(index: Int, elementType: Class): List? + public fun getList( + index: Int, + elementType: Class, + ): List? /** * Obtain the value of the column with the specified [index] as [Set]. @@ -153,7 +156,10 @@ public interface TableReader : AutoCloseable { * @throws IllegalArgumentException if the column index is not valid for this reader or this type. * @return The value of the column as `Set` or `null` if the column is null. */ - public fun getSet(index: Int, elementType: Class): Set? + public fun getSet( + index: Int, + elementType: Class, + ): Set? /** * Obtain the value of the column with the specified [index] as [Set]. @@ -164,7 +170,11 @@ public interface TableReader : AutoCloseable { * @throws IllegalArgumentException if the column index is not valid for this reader or this type. * @return The value of the column as `Map` or `null` if the column is null. */ - public fun getMap(index: Int, keyType: Class, valueType: Class): Map? + public fun getMap( + index: Int, + keyType: Class, + valueType: Class, + ): Map? /** * Determine whether a column named [name] has a `null` value for the current row. @@ -264,7 +274,10 @@ public interface TableReader : AutoCloseable { * @throws IllegalArgumentException if the column index is not valid for this reader or this type. * @return The value of the column as `List` or `null` if the column is null. */ - public fun getList(name: String, elementType: Class): List? = getList(resolve(name), elementType) + public fun getList( + name: String, + elementType: Class, + ): List? = getList(resolve(name), elementType) /** * Obtain the value of the column named [name] as [Set]. @@ -274,7 +287,10 @@ public interface TableReader : AutoCloseable { * @throws IllegalArgumentException if the column index is not valid for this reader or this type. * @return The value of the column as `Set` or `null` if the column is null. */ - public fun getSet(name: String, elementType: Class): Set? = getSet(resolve(name), elementType) + public fun getSet( + name: String, + elementType: Class, + ): Set? = getSet(resolve(name), elementType) /** * Obtain the value of the column named [name] as [Set]. @@ -285,8 +301,11 @@ public interface TableReader : AutoCloseable { * @throws IllegalArgumentException if the column index is not valid for this reader or this type. * @return The value of the column as `Map` or `null` if the column is null. */ - public fun getMap(name: String, keyType: Class, valueType: Class): Map? = - getMap(resolve(name), keyType, valueType) + public fun getMap( + name: String, + keyType: Class, + valueType: Class, + ): Map? = getMap(resolve(name), keyType, valueType) /** * Closes the reader so that no further iteration or data access can be made. diff --git a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/TableWriter.kt b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/TableWriter.kt index 3b02794d7..133bd01cd 100644 --- a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/TableWriter.kt +++ b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/TableWriter.kt @@ -55,7 +55,10 @@ public interface TableWriter : AutoCloseable { * @param value The boolean value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setBoolean(index: Int, value: Boolean) + public fun setBoolean( + index: Int, + value: Boolean, + ) /** * Set the column with index [index] to integer [value]. @@ -64,7 +67,10 @@ public interface TableWriter : AutoCloseable { * @param value The integer value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setInt(index: Int, value: Int) + public fun setInt( + index: Int, + value: Int, + ) /** * Set the column with index [index] to long [value]. @@ -73,7 +79,10 @@ public interface TableWriter : AutoCloseable { * @param value The long value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setLong(index: Int, value: Long) + public fun setLong( + index: Int, + value: Long, + ) /** * Set the column with index [index] to float [value]. @@ -82,7 +91,10 @@ public interface TableWriter : AutoCloseable { * @param value The float value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setFloat(index: Int, value: Float) + public fun setFloat( + index: Int, + value: Float, + ) /** * Set the column with index [index] to double [value]. @@ -91,7 +103,10 @@ public interface TableWriter : AutoCloseable { * @param value The double value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setDouble(index: Int, value: Double) + public fun setDouble( + index: Int, + value: Double, + ) /** * Set the column with index [index] to [String] [value]. @@ -100,7 +115,10 @@ public interface TableWriter : AutoCloseable { * @param value The [String] value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setString(index: Int, value: String) + public fun setString( + index: Int, + value: String, + ) /** * Set the column with index [index] to [UUID] [value]. @@ -109,7 +127,10 @@ public interface TableWriter : AutoCloseable { * @param value The [UUID] value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setUUID(index: Int, value: UUID) + public fun setUUID( + index: Int, + value: UUID, + ) /** * Set the column with index [index] to [Instant] [value]. @@ -118,7 +139,10 @@ public interface TableWriter : AutoCloseable { * @param value The [Instant] value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setInstant(index: Int, value: Instant) + public fun setInstant( + index: Int, + value: Instant, + ) /** * Set the column with index [index] to [Duration] [value]. @@ -127,7 +151,10 @@ public interface TableWriter : AutoCloseable { * @param value The [Duration] value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setDuration(index: Int, value: Duration) + public fun setDuration( + index: Int, + value: Duration, + ) /** * Set the column with index [index] to [List] [value]. @@ -136,7 +163,10 @@ public interface TableWriter : AutoCloseable { * @param value The [Map] value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setList(index: Int, value: List) + public fun setList( + index: Int, + value: List, + ) /** * Set the column with index [index] to [Set] [value]. @@ -145,7 +175,10 @@ public interface TableWriter : AutoCloseable { * @param value The [Set] value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setSet(index: Int, value: Set) + public fun setSet( + index: Int, + value: Set, + ) /** * Set the column with index [index] to [Map] [value]. @@ -154,7 +187,10 @@ public interface TableWriter : AutoCloseable { * @param value The [Map] value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setMap(index: Int, value: Map) + public fun setMap( + index: Int, + value: Map, + ) /** * Set the column named [name] to boolean [value]. @@ -163,7 +199,10 @@ public interface TableWriter : AutoCloseable { * @param value The boolean value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setBoolean(name: String, value: Boolean): Unit = setBoolean(resolve(name), value) + public fun setBoolean( + name: String, + value: Boolean, + ): Unit = setBoolean(resolve(name), value) /** * Set the column named [name] to integer [value]. @@ -172,7 +211,10 @@ public interface TableWriter : AutoCloseable { * @param value The integer value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setInt(name: String, value: Int): Unit = setInt(resolve(name), value) + public fun setInt( + name: String, + value: Int, + ): Unit = setInt(resolve(name), value) /** * Set the column named [name] to long [value]. @@ -181,7 +223,10 @@ public interface TableWriter : AutoCloseable { * @param value The long value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setLong(name: String, value: Long): Unit = setLong(resolve(name), value) + public fun setLong( + name: String, + value: Long, + ): Unit = setLong(resolve(name), value) /** * Set the column named [name] to float [value]. @@ -190,7 +235,10 @@ public interface TableWriter : AutoCloseable { * @param value The float value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setFloat(name: String, value: Float): Unit = setFloat(resolve(name), value) + public fun setFloat( + name: String, + value: Float, + ): Unit = setFloat(resolve(name), value) /** * Set the column named [name] to double [value]. @@ -199,7 +247,10 @@ public interface TableWriter : AutoCloseable { * @param value The double value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setDouble(name: String, value: Double): Unit = setDouble(resolve(name), value) + public fun setDouble( + name: String, + value: Double, + ): Unit = setDouble(resolve(name), value) /** * Set the column named [name] to [String] [value]. @@ -208,7 +259,10 @@ public interface TableWriter : AutoCloseable { * @param value The [String] value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setString(name: String, value: String): Unit = setString(resolve(name), value) + public fun setString( + name: String, + value: String, + ): Unit = setString(resolve(name), value) /** * Set the column named [name] to [UUID] [value]. @@ -217,7 +271,10 @@ public interface TableWriter : AutoCloseable { * @param value The [UUID] value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setUUID(name: String, value: UUID): Unit = setUUID(resolve(name), value) + public fun setUUID( + name: String, + value: UUID, + ): Unit = setUUID(resolve(name), value) /** * Set the column named [name] to [Instant] [value]. @@ -226,7 +283,10 @@ public interface TableWriter : AutoCloseable { * @param value The [Instant] value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setInstant(name: String, value: Instant): Unit = setInstant(resolve(name), value) + public fun setInstant( + name: String, + value: Instant, + ): Unit = setInstant(resolve(name), value) /** * Set the column named [name] to [Duration] [value]. @@ -235,7 +295,10 @@ public interface TableWriter : AutoCloseable { * @param value The [Duration] value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setDuration(name: String, value: Duration): Unit = setDuration(resolve(name), value) + public fun setDuration( + name: String, + value: Duration, + ): Unit = setDuration(resolve(name), value) /** * Set the column named [name] to [List] [value]. @@ -244,7 +307,10 @@ public interface TableWriter : AutoCloseable { * @param value The [List] value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setList(name: String, value: List): Unit = setList(resolve(name), value) + public fun setList( + name: String, + value: List, + ): Unit = setList(resolve(name), value) /** * Set the column named [name] to [Set] [value]. @@ -253,7 +319,10 @@ public interface TableWriter : AutoCloseable { * @param value The [Set] value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setSet(name: String, value: Set): Unit = setSet(resolve(name), value) + public fun setSet( + name: String, + value: Set, + ): Unit = setSet(resolve(name), value) /** * Set the column named [name] to [Map] [value]. @@ -262,7 +331,10 @@ public interface TableWriter : AutoCloseable { * @param value The [Map] value to set the column to. * @throws IllegalArgumentException if the column is not valid for this method. */ - public fun setMap(name: String, value: Map): Unit = setMap(resolve(name), value) + public fun setMap( + name: String, + value: Map, + ): Unit = setMap(resolve(name), value) /** * Flush any buffered content to the underlying target. diff --git a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/Trace.kt b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/Trace.kt index 64e8f2721..a1059e9e1 100644 --- a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/Trace.kt +++ b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/Trace.kt @@ -55,7 +55,10 @@ public interface Trace { * @throws IllegalArgumentException if [format] is not supported. */ @JvmStatic - public fun open(path: File, format: String): Trace = open(path.toPath(), format) + public fun open( + path: File, + format: String, + ): Trace = open(path.toPath(), format) /** * Open a [Trace] at the specified [path] in the given [format]. @@ -65,7 +68,10 @@ public interface Trace { * @throws IllegalArgumentException if [format] is not supported. */ @JvmStatic - public fun open(path: Path, format: String): Trace { + public fun open( + path: Path, + format: String, + ): Trace { val provider = requireNotNull(TraceFormat.byName(format)) { "Unknown format $format" } return TraceImpl(provider, path) } @@ -77,7 +83,10 @@ public interface Trace { * @param format The format of the trace to create. */ @JvmStatic - public fun create(path: File, format: String): Trace = create(path.toPath(), format) + public fun create( + path: File, + format: String, + ): Trace = create(path.toPath(), format) /** * Create a [Trace] at the specified [path] in the given [format]. @@ -86,7 +95,10 @@ public interface Trace { * @param format The format of the trace to create. */ @JvmStatic - public fun create(path: Path, format: String): Trace { + public fun create( + path: Path, + format: String, + ): Trace { val provider = requireNotNull(TraceFormat.byName(format)) { "Unknown format $format" } provider.create(path) return TraceImpl(provider, path) diff --git a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/conv/ResourceColumns.kt b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/conv/ResourceColumns.kt index 89f8dbc46..046dd13d6 100644 --- a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/conv/ResourceColumns.kt +++ b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/conv/ResourceColumns.kt @@ -28,40 +28,40 @@ package org.opendc.trace.conv * Identifier of the resource. */ @JvmField -public val RESOURCE_ID: String = "id" +public val resourceID: String = "id" /** * The cluster to which the resource belongs. */ @JvmField -public val RESOURCE_CLUSTER_ID: String = "cluster_id" +public val resourceClusterID: String = "cluster_id" /** * Start time for the resource. */ @JvmField -public val RESOURCE_START_TIME: String = "start_time" +public val resourceStartTime: String = "start_time" /** * End time for the resource. */ @JvmField -public val RESOURCE_STOP_TIME: String = "stop_time" +public val resourceStopTime: String = "stop_time" /** * Number of CPUs for the resource. */ @JvmField -public val RESOURCE_CPU_COUNT: String = "cpu_count" +public val resourceCpuCount: String = "cpu_count" /** * Total CPU capacity of the resource in MHz. */ @JvmField -public val RESOURCE_CPU_CAPACITY: String = "cpu_capacity" +public val resourceCpuCapacity: String = "cpu_capacity" /** * Memory capacity for the resource in KB. */ @JvmField -public val RESOURCE_MEM_CAPACITY: String = "mem_capacity" +public val resourceMemCapacity: String = "mem_capacity" diff --git a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/conv/ResourceStateColumns.kt b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/conv/ResourceStateColumns.kt index 5187d501c..eede6bd6c 100644 --- a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/conv/ResourceStateColumns.kt +++ b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/conv/ResourceStateColumns.kt @@ -28,70 +28,70 @@ package org.opendc.trace.conv * The timestamp at which the state was recorded. */ @JvmField -public val RESOURCE_STATE_TIMESTAMP: String = "timestamp" +public val resourceStateTimestamp: String = "timestamp" /** * Duration for the state. */ @JvmField -public val RESOURCE_STATE_DURATION: String = "duration" +public val resourceStateDuration: String = "duration" /** * A flag to indicate that the resource is powered on. */ @JvmField -public val RESOURCE_STATE_POWERED_ON: String = "powered_on" +public val resourceStatePoweredOn: String = "powered_on" /** * Total CPU usage of the resource in MHz. */ @JvmField -public val RESOURCE_STATE_CPU_USAGE: String = "cpu_usage" +public val resourceStateCpuUsage: String = "cpu_usage" /** * Total CPU usage of the resource in percentage. */ @JvmField -public val RESOURCE_STATE_CPU_USAGE_PCT: String = "cpu_usage_pct" +public val resourceStateCpuUsagePct: String = "cpu_usage_pct" /** * Total CPU demand of the resource in MHz. */ @JvmField -public val RESOURCE_STATE_CPU_DEMAND: String = "cpu_demand" +public val resourceStateCpuDemand: String = "cpu_demand" /** * CPU ready percentage. */ @JvmField -public val RESOURCE_STATE_CPU_READY_PCT: String = "cpu_ready_pct" +public val resourceStateCpuReadyPct: String = "cpu_ready_pct" /** * Memory usage of the resource in KB. */ @JvmField -public val RESOURCE_STATE_MEM_USAGE: String = "mem_usage" +public val resourceStateMemUsage: String = "mem_usage" /** * Disk read throughput of the resource in KB/s. */ @JvmField -public val RESOURCE_STATE_DISK_READ: String = "disk_read" +public val resourceStateDiskRead: String = "disk_read" /** * Disk write throughput of the resource in KB/s. */ @JvmField -public val RESOURCE_STATE_DISK_WRITE: String = "disk_write" +public val resourceStateDiskWrite: String = "disk_write" /** * Network receive throughput of the resource in KB/s. */ @JvmField -public val RESOURCE_STATE_NET_RX: String = "net_rx" +public val resourceStateNetRx: String = "net_rx" /** * Network transmit throughput of the resource in KB/s. */ @JvmField -public val RESOURCE_STATE_NET_TX: String = "net_tx" +public val resourceStateNetTx: String = "net_tx" diff --git a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/spi/TraceFormat.kt b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/spi/TraceFormat.kt index 46ef051d0..835378220 100644 --- a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/spi/TraceFormat.kt +++ b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/spi/TraceFormat.kt @@ -61,7 +61,10 @@ public interface TraceFormat { * @throws IllegalArgumentException If [table] does not exist. * @return The [TableDetails] for the specified [table]. */ - public fun getDetails(path: Path, table: String): TableDetails + public fun getDetails( + path: Path, + table: String, + ): TableDetails /** * Open a [TableReader] for the specified [table]. @@ -72,7 +75,11 @@ public interface TraceFormat { * @throws IllegalArgumentException If [table] does not exist. * @return A [TableReader] instance for the table. */ - public fun newReader(path: Path, table: String, projection: List?): TableReader + public fun newReader( + path: Path, + table: String, + projection: List?, + ): TableReader /** * Open a [TableWriter] for the specified [table]. @@ -83,7 +90,10 @@ public interface TraceFormat { * @throws UnsupportedOperationException If the format does not support writing. * @return A [TableWriter] instance for the table. */ - public fun newWriter(path: Path, table: String): TableWriter + public fun newWriter( + path: Path, + table: String, + ): TableWriter /** * A helper object for resolving providers. diff --git a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/util/CompositeTableReader.kt b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/util/CompositeTableReader.kt index 2fe820c46..4b9a0d955 100644 --- a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/util/CompositeTableReader.kt +++ b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/util/CompositeTableReader.kt @@ -122,17 +122,27 @@ public abstract class CompositeTableReader : TableReader { return delegate.getDuration(index) } - override fun getList(index: Int, elementType: Class): List? { + override fun getList( + index: Int, + elementType: Class, + ): List? { val delegate = checkNotNull(delegate) { "Invalid reader state" } return delegate.getList(index, elementType) } - override fun getSet(index: Int, elementType: Class): Set? { + override fun getSet( + index: Int, + elementType: Class, + ): Set? { val delegate = checkNotNull(delegate) { "Invalid reader state" } return delegate.getSet(index, elementType) } - override fun getMap(index: Int, keyType: Class, valueType: Class): Map? { + override fun getMap( + index: Int, + keyType: Class, + valueType: Class, + ): Map? { val delegate = checkNotNull(delegate) { "Invalid reader state" } return delegate.getMap(index, keyType, valueType) } diff --git a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/util/TableColumnConversion.kt b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/util/TableColumnConversion.kt index 26739e346..fda2bc542 100644 --- a/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/util/TableColumnConversion.kt +++ b/opendc-trace/opendc-trace-api/src/main/kotlin/org/opendc/trace/util/TableColumnConversion.kt @@ -32,7 +32,10 @@ import java.util.UUID /** * Helper method to convert a [List] into a [List] with elements of [targetElementType]. */ -public fun TableColumnType.List.convertTo(value: List<*>?, targetElementType: Class): List? { +public fun TableColumnType.List.convertTo( + value: List<*>?, + targetElementType: Class, +): List? { require(elementType.isCompatible(targetElementType)) { "Target element type is not compatible with $elementType" } @Suppress("UNCHECKED_CAST") return value as List? @@ -41,7 +44,10 @@ public fun TableColumnType.List.convertTo(value: List<*>?, targetElementType /** * Helper method to convert a [Set] into a [Set] with elements of [targetElementType]. */ -public fun TableColumnType.Set.convertTo(value: Set<*>?, targetElementType: Class): Set? { +public fun TableColumnType.Set.convertTo( + value: Set<*>?, + targetElementType: Class, +): Set? { require(elementType.isCompatible(targetElementType)) { "Target element type is not compatible with $elementType" } @Suppress("UNCHECKED_CAST") return value as Set? @@ -50,7 +56,11 @@ public fun TableColumnType.Set.convertTo(value: Set<*>?, targetElementType: /** * Helper method to convert a [Map] into a [Map] with [targetKeyType] keys and [targetValueType] values. */ -public fun TableColumnType.Map.convertTo(value: Map<*, *>?, targetKeyType: Class, targetValueType: Class): Map? { +public fun TableColumnType.Map.convertTo( + value: Map<*, *>?, + targetKeyType: Class, + targetValueType: Class, +): Map? { require(keyType.isCompatible(targetKeyType)) { "Target key type $targetKeyType is not compatible with $keyType" } require(valueType.isCompatible(targetValueType)) { "Target value type $targetValueType is not compatible with $valueType" } @Suppress("UNCHECKED_CAST") diff --git a/opendc-trace/opendc-trace-azure/build.gradle.kts b/opendc-trace/opendc-trace-azure/build.gradle.kts index ee53c5834..21b8b439e 100644 --- a/opendc-trace/opendc-trace-azure/build.gradle.kts +++ b/opendc-trace/opendc-trace-azure/build.gradle.kts @@ -22,7 +22,7 @@ description = "Support for Azure VM traces in OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` `benchmark-conventions` diff --git a/opendc-trace/opendc-trace-azure/src/jmh/kotlin/org/opendc/trace/azure/AzureTraceBenchmarks.kt b/opendc-trace/opendc-trace-azure/src/jmh/kotlin/org/opendc/trace/azure/AzureTraceBenchmarks.kt index 6759f38ae..bb3c24507 100644 --- a/opendc-trace/opendc-trace-azure/src/jmh/kotlin/org/opendc/trace/azure/AzureTraceBenchmarks.kt +++ b/opendc-trace/opendc-trace-azure/src/jmh/kotlin/org/opendc/trace/azure/AzureTraceBenchmarks.kt @@ -22,9 +22,9 @@ package org.opendc.trace.azure -import org.opendc.trace.conv.RESOURCE_ID import org.opendc.trace.conv.TABLE_RESOURCES import org.opendc.trace.conv.TABLE_RESOURCE_STATES +import org.opendc.trace.conv.resourceID import org.opendc.trace.spi.TraceFormat import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Fork @@ -58,7 +58,7 @@ class AzureTraceBenchmarks { fun benchmarkResourcesReader(bh: Blackhole) { val reader = format.newReader(path, TABLE_RESOURCES, null) try { - val idColumn = reader.resolve(RESOURCE_ID) + val idColumn = reader.resolve(resourceID) while (reader.nextRow()) { bh.consume(reader.getString(idColumn)) } @@ -71,7 +71,7 @@ class AzureTraceBenchmarks { fun benchmarkResourceStatesReader(bh: Blackhole) { val reader = format.newReader(path, TABLE_RESOURCE_STATES, null) try { - val idColumn = reader.resolve(RESOURCE_ID) + val idColumn = reader.resolve(resourceID) while (reader.nextRow()) { bh.consume(reader.getString(idColumn)) } diff --git a/opendc-trace/opendc-trace-azure/src/main/kotlin/org/opendc/trace/azure/AzureResourceStateTableReader.kt b/opendc-trace/opendc-trace-azure/src/main/kotlin/org/opendc/trace/azure/AzureResourceStateTableReader.kt index 0c60c75d8..bcf6ff523 100644 --- a/opendc-trace/opendc-trace-azure/src/main/kotlin/org/opendc/trace/azure/AzureResourceStateTableReader.kt +++ b/opendc-trace/opendc-trace-azure/src/main/kotlin/org/opendc/trace/azure/AzureResourceStateTableReader.kt @@ -26,9 +26,9 @@ import com.fasterxml.jackson.core.JsonToken import com.fasterxml.jackson.dataformat.csv.CsvParser import com.fasterxml.jackson.dataformat.csv.CsvSchema import org.opendc.trace.TableReader -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE_PCT -import org.opendc.trace.conv.RESOURCE_STATE_TIMESTAMP +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceStateCpuUsagePct +import org.opendc.trace.conv.resourceStateTimestamp import java.time.Duration import java.time.Instant import java.util.UUID @@ -74,21 +74,21 @@ internal class AzureResourceStateTableReader(private val parser: CsvParser) : Ta return true } - private val COL_ID = 0 - private val COL_TIMESTAMP = 1 - private val COL_CPU_USAGE_PCT = 2 + private val colID = 0 + private val colTimestamp = 1 + private val colCpuUsagePct = 2 override fun resolve(name: String): Int { return when (name) { - RESOURCE_ID -> COL_ID - RESOURCE_STATE_TIMESTAMP -> COL_TIMESTAMP - RESOURCE_STATE_CPU_USAGE_PCT -> COL_CPU_USAGE_PCT + resourceID -> colID + resourceStateTimestamp -> colTimestamp + resourceStateCpuUsagePct -> colCpuUsagePct else -> -1 } } override fun isNull(index: Int): Boolean { - require(index in 0..COL_CPU_USAGE_PCT) { "Invalid column index" } + require(index in 0..colCpuUsagePct) { "Invalid column index" } return false } @@ -111,7 +111,7 @@ internal class AzureResourceStateTableReader(private val parser: CsvParser) : Ta override fun getDouble(index: Int): Double { checkActive() return when (index) { - COL_CPU_USAGE_PCT -> cpuUsagePct + colCpuUsagePct -> cpuUsagePct else -> throw IllegalArgumentException("Invalid column") } } @@ -119,7 +119,7 @@ internal class AzureResourceStateTableReader(private val parser: CsvParser) : Ta override fun getString(index: Int): String? { checkActive() return when (index) { - COL_ID -> id + colID -> id else -> throw IllegalArgumentException("Invalid column") } } @@ -131,7 +131,7 @@ internal class AzureResourceStateTableReader(private val parser: CsvParser) : Ta override fun getInstant(index: Int): Instant? { checkActive() return when (index) { - COL_TIMESTAMP -> timestamp + colTimestamp -> timestamp else -> throw IllegalArgumentException("Invalid column") } } @@ -140,15 +140,25 @@ internal class AzureResourceStateTableReader(private val parser: CsvParser) : Ta throw IllegalArgumentException("Invalid column") } - override fun getList(index: Int, elementType: Class): List? { + override fun getList( + index: Int, + elementType: Class, + ): List? { throw IllegalArgumentException("Invalid column") } - override fun getMap(index: Int, keyType: Class, valueType: Class): Map? { + override fun getMap( + index: Int, + keyType: Class, + valueType: Class, + ): Map? { throw IllegalArgumentException("Invalid column") } - override fun getSet(index: Int, elementType: Class): Set? { + override fun getSet( + index: Int, + elementType: Class, + ): Set? { throw IllegalArgumentException("Invalid column") } @@ -196,13 +206,14 @@ internal class AzureResourceStateTableReader(private val parser: CsvParser) : Ta /** * The [CsvSchema] that is used to parse the trace. */ - private val schema = CsvSchema.builder() - .addColumn("timestamp", CsvSchema.ColumnType.NUMBER) - .addColumn("vm id", CsvSchema.ColumnType.STRING) - .addColumn("CPU min cpu", CsvSchema.ColumnType.NUMBER) - .addColumn("CPU max cpu", CsvSchema.ColumnType.NUMBER) - .addColumn("CPU avg cpu", CsvSchema.ColumnType.NUMBER) - .setAllowComments(true) - .build() + private val schema = + CsvSchema.builder() + .addColumn("timestamp", CsvSchema.ColumnType.NUMBER) + .addColumn("vm id", CsvSchema.ColumnType.STRING) + .addColumn("CPU min cpu", CsvSchema.ColumnType.NUMBER) + .addColumn("CPU max cpu", CsvSchema.ColumnType.NUMBER) + .addColumn("CPU avg cpu", CsvSchema.ColumnType.NUMBER) + .setAllowComments(true) + .build() } } diff --git a/opendc-trace/opendc-trace-azure/src/main/kotlin/org/opendc/trace/azure/AzureResourceTableReader.kt b/opendc-trace/opendc-trace-azure/src/main/kotlin/org/opendc/trace/azure/AzureResourceTableReader.kt index c0acb67a4..d86a04668 100644 --- a/opendc-trace/opendc-trace-azure/src/main/kotlin/org/opendc/trace/azure/AzureResourceTableReader.kt +++ b/opendc-trace/opendc-trace-azure/src/main/kotlin/org/opendc/trace/azure/AzureResourceTableReader.kt @@ -26,11 +26,11 @@ import com.fasterxml.jackson.core.JsonToken import com.fasterxml.jackson.dataformat.csv.CsvParser import com.fasterxml.jackson.dataformat.csv.CsvSchema import org.opendc.trace.TableReader -import org.opendc.trace.conv.RESOURCE_CPU_COUNT -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_MEM_CAPACITY -import org.opendc.trace.conv.RESOURCE_START_TIME -import org.opendc.trace.conv.RESOURCE_STOP_TIME +import org.opendc.trace.conv.resourceCpuCount +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceMemCapacity +import org.opendc.trace.conv.resourceStartTime +import org.opendc.trace.conv.resourceStopTime import java.time.Duration import java.time.Instant import java.util.UUID @@ -78,25 +78,25 @@ internal class AzureResourceTableReader(private val parser: CsvParser) : TableRe return true } - private val COL_ID = 0 - private val COL_START_TIME = 1 - private val COL_STOP_TIME = 2 - private val COL_CPU_COUNT = 3 - private val COL_MEM_CAPACITY = 4 + private val colID = 0 + private val colStartTime = 1 + private val colStopTime = 2 + private val colCpuCount = 3 + private val colMemCapacity = 4 override fun resolve(name: String): Int { return when (name) { - RESOURCE_ID -> COL_ID - RESOURCE_START_TIME -> COL_START_TIME - RESOURCE_STOP_TIME -> COL_STOP_TIME - RESOURCE_CPU_COUNT -> COL_CPU_COUNT - RESOURCE_MEM_CAPACITY -> COL_MEM_CAPACITY + resourceID -> colID + resourceStartTime -> colStartTime + resourceStopTime -> colStopTime + resourceCpuCount -> colCpuCount + resourceMemCapacity -> colMemCapacity else -> -1 } } override fun isNull(index: Int): Boolean { - require(index in 0..COL_MEM_CAPACITY) { "Invalid column index" } + require(index in 0..colMemCapacity) { "Invalid column index" } return false } @@ -107,7 +107,7 @@ internal class AzureResourceTableReader(private val parser: CsvParser) : TableRe override fun getInt(index: Int): Int { checkActive() return when (index) { - COL_CPU_COUNT -> cpuCores + colCpuCount -> cpuCores else -> throw IllegalArgumentException("Invalid column") } } @@ -115,7 +115,7 @@ internal class AzureResourceTableReader(private val parser: CsvParser) : TableRe override fun getLong(index: Int): Long { checkActive() return when (index) { - COL_CPU_COUNT -> cpuCores.toLong() + colCpuCount -> cpuCores.toLong() else -> throw IllegalArgumentException("Invalid column") } } @@ -127,7 +127,7 @@ internal class AzureResourceTableReader(private val parser: CsvParser) : TableRe override fun getDouble(index: Int): Double { checkActive() return when (index) { - COL_MEM_CAPACITY -> memCapacity + colMemCapacity -> memCapacity else -> throw IllegalArgumentException("Invalid column") } } @@ -135,7 +135,7 @@ internal class AzureResourceTableReader(private val parser: CsvParser) : TableRe override fun getString(index: Int): String? { checkActive() return when (index) { - COL_ID -> id + colID -> id else -> throw IllegalArgumentException("Invalid column") } } @@ -147,8 +147,8 @@ internal class AzureResourceTableReader(private val parser: CsvParser) : TableRe override fun getInstant(index: Int): Instant? { checkActive() return when (index) { - COL_START_TIME -> startTime - COL_STOP_TIME -> stopTime + colStartTime -> startTime + colStopTime -> stopTime else -> throw IllegalArgumentException("Invalid column") } } @@ -157,15 +157,25 @@ internal class AzureResourceTableReader(private val parser: CsvParser) : TableRe throw IllegalArgumentException("Invalid column") } - override fun getList(index: Int, elementType: Class): List? { + override fun getList( + index: Int, + elementType: Class, + ): List? { throw IllegalArgumentException("Invalid column") } - override fun getSet(index: Int, elementType: Class): Set? { + override fun getSet( + index: Int, + elementType: Class, + ): Set? { throw IllegalArgumentException("Invalid column") } - override fun getMap(index: Int, keyType: Class, valueType: Class): Map? { + override fun getMap( + index: Int, + keyType: Class, + valueType: Class, + ): Map? { throw IllegalArgumentException("Invalid column") } @@ -217,19 +227,20 @@ internal class AzureResourceTableReader(private val parser: CsvParser) : TableRe /** * The [CsvSchema] that is used to parse the trace. */ - private val schema = CsvSchema.builder() - .addColumn("vm id", CsvSchema.ColumnType.NUMBER) - .addColumn("subscription id", CsvSchema.ColumnType.STRING) - .addColumn("deployment id", CsvSchema.ColumnType.NUMBER) - .addColumn("timestamp vm created", CsvSchema.ColumnType.NUMBER) - .addColumn("timestamp vm deleted", CsvSchema.ColumnType.NUMBER) - .addColumn("max cpu", CsvSchema.ColumnType.NUMBER) - .addColumn("avg cpu", CsvSchema.ColumnType.NUMBER) - .addColumn("p95 cpu", CsvSchema.ColumnType.NUMBER) - .addColumn("vm category", CsvSchema.ColumnType.NUMBER) - .addColumn("vm virtual core count", CsvSchema.ColumnType.NUMBER) - .addColumn("vm memory", CsvSchema.ColumnType.NUMBER) - .setAllowComments(true) - .build() + private val schema = + CsvSchema.builder() + .addColumn("vm id", CsvSchema.ColumnType.NUMBER) + .addColumn("subscription id", CsvSchema.ColumnType.STRING) + .addColumn("deployment id", CsvSchema.ColumnType.NUMBER) + .addColumn("timestamp vm created", CsvSchema.ColumnType.NUMBER) + .addColumn("timestamp vm deleted", CsvSchema.ColumnType.NUMBER) + .addColumn("max cpu", CsvSchema.ColumnType.NUMBER) + .addColumn("avg cpu", CsvSchema.ColumnType.NUMBER) + .addColumn("p95 cpu", CsvSchema.ColumnType.NUMBER) + .addColumn("vm category", CsvSchema.ColumnType.NUMBER) + .addColumn("vm virtual core count", CsvSchema.ColumnType.NUMBER) + .addColumn("vm memory", CsvSchema.ColumnType.NUMBER) + .setAllowComments(true) + .build() } } diff --git a/opendc-trace/opendc-trace-azure/src/main/kotlin/org/opendc/trace/azure/AzureTraceFormat.kt b/opendc-trace/opendc-trace-azure/src/main/kotlin/org/opendc/trace/azure/AzureTraceFormat.kt index 3f64c6401..a75da9d91 100644 --- a/opendc-trace/opendc-trace-azure/src/main/kotlin/org/opendc/trace/azure/AzureTraceFormat.kt +++ b/opendc-trace/opendc-trace-azure/src/main/kotlin/org/opendc/trace/azure/AzureTraceFormat.kt @@ -28,15 +28,15 @@ import org.opendc.trace.TableColumn import org.opendc.trace.TableColumnType import org.opendc.trace.TableReader import org.opendc.trace.TableWriter -import org.opendc.trace.conv.RESOURCE_CPU_COUNT -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_MEM_CAPACITY -import org.opendc.trace.conv.RESOURCE_START_TIME -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE_PCT -import org.opendc.trace.conv.RESOURCE_STATE_TIMESTAMP -import org.opendc.trace.conv.RESOURCE_STOP_TIME import org.opendc.trace.conv.TABLE_RESOURCES import org.opendc.trace.conv.TABLE_RESOURCE_STATES +import org.opendc.trace.conv.resourceCpuCount +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceMemCapacity +import org.opendc.trace.conv.resourceStartTime +import org.opendc.trace.conv.resourceStateCpuUsagePct +import org.opendc.trace.conv.resourceStateTimestamp +import org.opendc.trace.conv.resourceStopTime import org.opendc.trace.spi.TableDetails import org.opendc.trace.spi.TraceFormat import org.opendc.trace.util.CompositeTableReader @@ -59,9 +59,10 @@ public class AzureTraceFormat : TraceFormat { /** * The [CsvFactory] used to create the parser. */ - private val factory = CsvFactory() - .enable(CsvParser.Feature.ALLOW_COMMENTS) - .enable(CsvParser.Feature.TRIM_SPACES) + private val factory = + CsvFactory() + .enable(CsvParser.Feature.ALLOW_COMMENTS) + .enable(CsvParser.Feature.TRIM_SPACES) override fun create(path: Path) { throw UnsupportedOperationException("Writing not supported for this format") @@ -69,29 +70,38 @@ public class AzureTraceFormat : TraceFormat { override fun getTables(path: Path): List = listOf(TABLE_RESOURCES, TABLE_RESOURCE_STATES) - override fun getDetails(path: Path, table: String): TableDetails { + override fun getDetails( + path: Path, + table: String, + ): TableDetails { return when (table) { - TABLE_RESOURCES -> TableDetails( - listOf( - TableColumn(RESOURCE_ID, TableColumnType.String), - TableColumn(RESOURCE_START_TIME, TableColumnType.Instant), - TableColumn(RESOURCE_STOP_TIME, TableColumnType.Instant), - TableColumn(RESOURCE_CPU_COUNT, TableColumnType.Int), - TableColumn(RESOURCE_MEM_CAPACITY, TableColumnType.Double) + TABLE_RESOURCES -> + TableDetails( + listOf( + TableColumn(resourceID, TableColumnType.String), + TableColumn(resourceStartTime, TableColumnType.Instant), + TableColumn(resourceStopTime, TableColumnType.Instant), + TableColumn(resourceCpuCount, TableColumnType.Int), + TableColumn(resourceMemCapacity, TableColumnType.Double), + ), ) - ) - TABLE_RESOURCE_STATES -> TableDetails( - listOf( - TableColumn(RESOURCE_ID, TableColumnType.String), - TableColumn(RESOURCE_STATE_TIMESTAMP, TableColumnType.Instant), - TableColumn(RESOURCE_STATE_CPU_USAGE_PCT, TableColumnType.Double) + TABLE_RESOURCE_STATES -> + TableDetails( + listOf( + TableColumn(resourceID, TableColumnType.String), + TableColumn(resourceStateTimestamp, TableColumnType.Instant), + TableColumn(resourceStateCpuUsagePct, TableColumnType.Double), + ), ) - ) else -> throw IllegalArgumentException("Table $table not supported") } } - override fun newReader(path: Path, table: String, projection: List?): TableReader { + override fun newReader( + path: Path, + table: String, + projection: List?, + ): TableReader { return when (table) { TABLE_RESOURCES -> { val stream = GZIPInputStream(path.resolve("vmtable/vmtable.csv.gz").inputStream()) @@ -102,7 +112,10 @@ public class AzureTraceFormat : TraceFormat { } } - override fun newWriter(path: Path, table: String): TableWriter { + override fun newWriter( + path: Path, + table: String, + ): TableWriter { throw UnsupportedOperationException("Writing not supported for this format") } @@ -110,10 +123,11 @@ public class AzureTraceFormat : TraceFormat { * Construct a [TableReader] for reading over all VM CPU readings. */ private fun newResourceStateReader(path: Path): TableReader { - val partitions = Files.walk(path.resolve("vm_cpu_readings"), 1) - .filter { !Files.isDirectory(it) && it.name.endsWith(".csv.gz") } - .collect(Collectors.toMap({ it.name.removeSuffix(".csv.gz") }, { it })) - .toSortedMap() + val partitions = + Files.walk(path.resolve("vm_cpu_readings"), 1) + .filter { !Files.isDirectory(it) && it.name.endsWith(".csv.gz") } + .collect(Collectors.toMap({ it.name.removeSuffix(".csv.gz") }, { it })) + .toSortedMap() val it = partitions.iterator() return object : CompositeTableReader() { diff --git a/opendc-trace/opendc-trace-azure/src/test/kotlin/org/opendc/trace/azure/AzureTraceFormatTest.kt b/opendc-trace/opendc-trace-azure/src/test/kotlin/org/opendc/trace/azure/AzureTraceFormatTest.kt index 00cdc1741..4fe96a8e6 100644 --- a/opendc-trace/opendc-trace-azure/src/test/kotlin/org/opendc/trace/azure/AzureTraceFormatTest.kt +++ b/opendc-trace/opendc-trace-azure/src/test/kotlin/org/opendc/trace/azure/AzureTraceFormatTest.kt @@ -33,13 +33,13 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.opendc.trace.TableColumn import org.opendc.trace.TableReader -import org.opendc.trace.conv.RESOURCE_CPU_COUNT -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_MEM_CAPACITY -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE_PCT -import org.opendc.trace.conv.RESOURCE_STATE_TIMESTAMP import org.opendc.trace.conv.TABLE_RESOURCES import org.opendc.trace.conv.TABLE_RESOURCE_STATES +import org.opendc.trace.conv.resourceCpuCount +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceMemCapacity +import org.opendc.trace.conv.resourceStateCpuUsagePct +import org.opendc.trace.conv.resourceStateTimestamp import org.opendc.trace.testkit.TableReaderTestKit import java.nio.file.Paths @@ -76,9 +76,9 @@ class AzureTraceFormatTest { val reader = format.newReader(path, TABLE_RESOURCES, null) assertAll( { assertTrue(reader.nextRow()) }, - { assertEquals("x/XsOfHO4ocsV99i4NluqKDuxctW2MMVmwqOPAlg4wp8mqbBOe3wxBlQo0+Qx+uf", reader.getString(RESOURCE_ID)) }, - { assertEquals(1, reader.getInt(RESOURCE_CPU_COUNT)) }, - { assertEquals(1750000.0, reader.getDouble(RESOURCE_MEM_CAPACITY)) } + { assertEquals("x/XsOfHO4ocsV99i4NluqKDuxctW2MMVmwqOPAlg4wp8mqbBOe3wxBlQo0+Qx+uf", reader.getString(resourceID)) }, + { assertEquals(1, reader.getInt(resourceCpuCount)) }, + { assertEquals(1750000.0, reader.getDouble(resourceMemCapacity)) }, ) reader.close() @@ -91,9 +91,9 @@ class AzureTraceFormatTest { assertAll( { assertTrue(reader.nextRow()) }, - { assertEquals("+ZcrOp5/c/fJ6mVgP5qMZlOAGDwyjaaDNM0WoWOt2IDb47gT0UwK9lFwkPQv3C7Q", reader.getString(RESOURCE_ID)) }, - { assertEquals(0, reader.getInstant(RESOURCE_STATE_TIMESTAMP)?.epochSecond) }, - { assertEquals(0.0286979, reader.getDouble(RESOURCE_STATE_CPU_USAGE_PCT), 0.01) } + { assertEquals("+ZcrOp5/c/fJ6mVgP5qMZlOAGDwyjaaDNM0WoWOt2IDb47gT0UwK9lFwkPQv3C7Q", reader.getString(resourceID)) }, + { assertEquals(0, reader.getInstant(resourceStateTimestamp)?.epochSecond) }, + { assertEquals(0.0286979, reader.getDouble(resourceStateCpuUsagePct), 0.01) }, ) reader.close() diff --git a/opendc-trace/opendc-trace-bitbrains/build.gradle.kts b/opendc-trace/opendc-trace-bitbrains/build.gradle.kts index 502b052a9..6ca40d3da 100644 --- a/opendc-trace/opendc-trace-bitbrains/build.gradle.kts +++ b/opendc-trace/opendc-trace-bitbrains/build.gradle.kts @@ -22,7 +22,7 @@ description = "Support for GWF traces in OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsExResourceStateTableReader.kt b/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsExResourceStateTableReader.kt index 511f02db0..8387d1ed2 100644 --- a/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsExResourceStateTableReader.kt +++ b/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsExResourceStateTableReader.kt @@ -23,18 +23,18 @@ package org.opendc.trace.bitbrains import org.opendc.trace.TableReader -import org.opendc.trace.conv.RESOURCE_CLUSTER_ID -import org.opendc.trace.conv.RESOURCE_CPU_CAPACITY -import org.opendc.trace.conv.RESOURCE_CPU_COUNT -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_MEM_CAPACITY -import org.opendc.trace.conv.RESOURCE_STATE_CPU_DEMAND -import org.opendc.trace.conv.RESOURCE_STATE_CPU_READY_PCT -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE_PCT -import org.opendc.trace.conv.RESOURCE_STATE_DISK_READ -import org.opendc.trace.conv.RESOURCE_STATE_DISK_WRITE -import org.opendc.trace.conv.RESOURCE_STATE_TIMESTAMP +import org.opendc.trace.conv.resourceClusterID +import org.opendc.trace.conv.resourceCpuCapacity +import org.opendc.trace.conv.resourceCpuCount +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceMemCapacity +import org.opendc.trace.conv.resourceStateCpuDemand +import org.opendc.trace.conv.resourceStateCpuReadyPct +import org.opendc.trace.conv.resourceStateCpuUsage +import org.opendc.trace.conv.resourceStateCpuUsagePct +import org.opendc.trace.conv.resourceStateDiskRead +import org.opendc.trace.conv.resourceStateDiskWrite +import org.opendc.trace.conv.resourceStateTimestamp import java.io.BufferedReader import java.time.Duration import java.time.Instant @@ -99,18 +99,18 @@ internal class BitbrainsExResourceStateTableReader(private val reader: BufferedR val field = line.subSequence(start, end) as String when (col++) { - COL_TIMESTAMP -> timestamp = Instant.ofEpochSecond(field.toLong(10)) - COL_CPU_USAGE -> cpuUsage = field.toDouble() - COL_CPU_DEMAND -> cpuDemand = field.toDouble() - COL_DISK_READ -> diskRead = field.toDouble() - COL_DISK_WRITE -> diskWrite = field.toDouble() - COL_CLUSTER_ID -> cluster = field.trim() - COL_NCPUS -> cpuCores = field.toInt(10) - COL_CPU_READY_PCT -> cpuReadyPct = field.toDouble() - COL_POWERED_ON -> poweredOn = field.toInt(10) == 1 - COL_CPU_CAPACITY -> cpuCapacity = field.toDouble() - COL_ID -> id = field.trim() - COL_MEM_CAPACITY -> memCapacity = field.toDouble() * 1000 // Convert from MB to KB + colTimestamp -> timestamp = Instant.ofEpochSecond(field.toLong(10)) + colCpuUsage -> cpuUsage = field.toDouble() + colCpuDemand -> cpuDemand = field.toDouble() + colDiskRead -> diskRead = field.toDouble() + colDiskWrite -> diskWrite = field.toDouble() + colClusterID -> cluster = field.trim() + colNcpus -> cpuCores = field.toInt(10) + colCpuReadyPct -> cpuReadyPct = field.toDouble() + colPoweredOn -> poweredOn = field.toInt(10) == 1 + colCpuCapacity -> cpuCapacity = field.toDouble() + colID -> id = field.trim() + colMemCapacity -> memCapacity = field.toDouble() * 1000 // Convert from MB to KB } } @@ -119,31 +119,31 @@ internal class BitbrainsExResourceStateTableReader(private val reader: BufferedR override fun resolve(name: String): Int { return when (name) { - RESOURCE_ID -> COL_ID - RESOURCE_CLUSTER_ID -> COL_CLUSTER_ID - RESOURCE_STATE_TIMESTAMP -> COL_TIMESTAMP - RESOURCE_CPU_COUNT -> COL_NCPUS - RESOURCE_CPU_CAPACITY -> COL_CPU_CAPACITY - RESOURCE_STATE_CPU_USAGE -> COL_CPU_USAGE - RESOURCE_STATE_CPU_USAGE_PCT -> COL_CPU_USAGE_PCT - RESOURCE_STATE_CPU_DEMAND -> COL_CPU_DEMAND - RESOURCE_STATE_CPU_READY_PCT -> COL_CPU_READY_PCT - RESOURCE_MEM_CAPACITY -> COL_MEM_CAPACITY - RESOURCE_STATE_DISK_READ -> COL_DISK_READ - RESOURCE_STATE_DISK_WRITE -> COL_DISK_WRITE + resourceID -> colID + resourceClusterID -> colClusterID + resourceStateTimestamp -> colTimestamp + resourceCpuCount -> colNcpus + resourceCpuCapacity -> colCpuCapacity + resourceStateCpuUsage -> colCpuUsage + resourceStateCpuUsagePct -> colCpuUsagePct + resourceStateCpuDemand -> colCpuDemand + resourceStateCpuReadyPct -> colCpuReadyPct + resourceMemCapacity -> colMemCapacity + resourceStateDiskRead -> colDiskRead + resourceStateDiskWrite -> colDiskWrite else -> -1 } } override fun isNull(index: Int): Boolean { - require(index in 0 until COL_MAX) { "Invalid column index" } + require(index in 0 until colMax) { "Invalid column index" } return false } override fun getBoolean(index: Int): Boolean { check(state == State.Active) { "No active row" } return when (index) { - COL_POWERED_ON -> poweredOn + colPoweredOn -> poweredOn else -> throw IllegalArgumentException("Invalid column") } } @@ -151,7 +151,7 @@ internal class BitbrainsExResourceStateTableReader(private val reader: BufferedR override fun getInt(index: Int): Int { check(state == State.Active) { "No active row" } return when (index) { - COL_NCPUS -> cpuCores + colNcpus -> cpuCores else -> throw IllegalArgumentException("Invalid column") } } @@ -167,14 +167,14 @@ internal class BitbrainsExResourceStateTableReader(private val reader: BufferedR override fun getDouble(index: Int): Double { check(state == State.Active) { "No active row" } return when (index) { - COL_CPU_CAPACITY -> cpuCapacity - COL_CPU_USAGE -> cpuUsage - COL_CPU_USAGE_PCT -> cpuUsage / cpuCapacity - COL_CPU_READY_PCT -> cpuReadyPct - COL_CPU_DEMAND -> cpuDemand - COL_MEM_CAPACITY -> memCapacity - COL_DISK_READ -> diskRead - COL_DISK_WRITE -> diskWrite + colCpuCapacity -> cpuCapacity + colCpuUsage -> cpuUsage + colCpuUsagePct -> cpuUsage / cpuCapacity + colCpuReadyPct -> cpuReadyPct + colCpuDemand -> cpuDemand + colMemCapacity -> memCapacity + colDiskRead -> diskRead + colDiskWrite -> diskWrite else -> throw IllegalArgumentException("Invalid column") } } @@ -182,8 +182,8 @@ internal class BitbrainsExResourceStateTableReader(private val reader: BufferedR override fun getString(index: Int): String? { check(state == State.Active) { "No active row" } return when (index) { - COL_ID -> id - COL_CLUSTER_ID -> cluster + colID -> id + colClusterID -> cluster else -> throw IllegalArgumentException("Invalid column") } } @@ -195,7 +195,7 @@ internal class BitbrainsExResourceStateTableReader(private val reader: BufferedR override fun getInstant(index: Int): Instant? { check(state == State.Active) { "No active row" } return when (index) { - COL_TIMESTAMP -> timestamp + colTimestamp -> timestamp else -> throw IllegalArgumentException("Invalid column") } } @@ -204,15 +204,25 @@ internal class BitbrainsExResourceStateTableReader(private val reader: BufferedR throw IllegalArgumentException("Invalid column") } - override fun getList(index: Int, elementType: Class): List? { + override fun getList( + index: Int, + elementType: Class, + ): List? { throw IllegalArgumentException("Invalid column") } - override fun getSet(index: Int, elementType: Class): Set? { + override fun getSet( + index: Int, + elementType: Class, + ): Set? { throw IllegalArgumentException("Invalid column") } - override fun getMap(index: Int, keyType: Class, valueType: Class): Map? { + override fun getMap( + index: Int, + keyType: Class, + valueType: Class, + ): Map? { throw IllegalArgumentException("Invalid column") } @@ -259,22 +269,24 @@ internal class BitbrainsExResourceStateTableReader(private val reader: BufferedR /** * Default column indices for the extended Bitbrains format. */ - private val COL_TIMESTAMP = 0 - private val COL_CPU_USAGE = 1 - private val COL_CPU_DEMAND = 2 - private val COL_DISK_READ = 4 - private val COL_DISK_WRITE = 6 - private val COL_CLUSTER_ID = 10 - private val COL_NCPUS = 12 - private val COL_CPU_READY_PCT = 13 - private val COL_POWERED_ON = 14 - private val COL_CPU_CAPACITY = 18 - private val COL_ID = 19 - private val COL_MEM_CAPACITY = 20 - private val COL_CPU_USAGE_PCT = 21 - private val COL_MAX = COL_CPU_USAGE_PCT + 1 + private val colTimestamp = 0 + private val colCpuUsage = 1 + private val colCpuDemand = 2 + private val colDiskRead = 4 + private val colDiskWrite = 6 + private val colClusterID = 10 + private val colNcpus = 12 + private val colCpuReadyPct = 13 + private val colPoweredOn = 14 + private val colCpuCapacity = 18 + private val colID = 19 + private val colMemCapacity = 20 + private val colCpuUsagePct = 21 + private val colMax = colCpuUsagePct + 1 private enum class State { - Pending, Active, Closed + Pending, + Active, + Closed, } } diff --git a/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsExTraceFormat.kt b/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsExTraceFormat.kt index d364694c9..6115953f1 100644 --- a/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsExTraceFormat.kt +++ b/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsExTraceFormat.kt @@ -26,19 +26,19 @@ import org.opendc.trace.TableColumn import org.opendc.trace.TableColumnType import org.opendc.trace.TableReader import org.opendc.trace.TableWriter -import org.opendc.trace.conv.RESOURCE_CLUSTER_ID -import org.opendc.trace.conv.RESOURCE_CPU_CAPACITY -import org.opendc.trace.conv.RESOURCE_CPU_COUNT -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_MEM_CAPACITY -import org.opendc.trace.conv.RESOURCE_STATE_CPU_DEMAND -import org.opendc.trace.conv.RESOURCE_STATE_CPU_READY_PCT -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE_PCT -import org.opendc.trace.conv.RESOURCE_STATE_DISK_READ -import org.opendc.trace.conv.RESOURCE_STATE_DISK_WRITE -import org.opendc.trace.conv.RESOURCE_STATE_TIMESTAMP import org.opendc.trace.conv.TABLE_RESOURCE_STATES +import org.opendc.trace.conv.resourceClusterID +import org.opendc.trace.conv.resourceCpuCapacity +import org.opendc.trace.conv.resourceCpuCount +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceMemCapacity +import org.opendc.trace.conv.resourceStateCpuDemand +import org.opendc.trace.conv.resourceStateCpuReadyPct +import org.opendc.trace.conv.resourceStateCpuUsage +import org.opendc.trace.conv.resourceStateCpuUsagePct +import org.opendc.trace.conv.resourceStateDiskRead +import org.opendc.trace.conv.resourceStateDiskWrite +import org.opendc.trace.conv.resourceStateTimestamp import org.opendc.trace.spi.TableDetails import org.opendc.trace.spi.TraceFormat import org.opendc.trace.util.CompositeTableReader @@ -64,36 +64,47 @@ public class BitbrainsExTraceFormat : TraceFormat { override fun getTables(path: Path): List = listOf(TABLE_RESOURCE_STATES) - override fun getDetails(path: Path, table: String): TableDetails { + override fun getDetails( + path: Path, + table: String, + ): TableDetails { return when (table) { - TABLE_RESOURCE_STATES -> TableDetails( - listOf( - TableColumn(RESOURCE_ID, TableColumnType.String), - TableColumn(RESOURCE_CLUSTER_ID, TableColumnType.String), - TableColumn(RESOURCE_STATE_TIMESTAMP, TableColumnType.Instant), - TableColumn(RESOURCE_CPU_COUNT, TableColumnType.Int), - TableColumn(RESOURCE_CPU_CAPACITY, TableColumnType.Double), - TableColumn(RESOURCE_STATE_CPU_USAGE, TableColumnType.Double), - TableColumn(RESOURCE_STATE_CPU_USAGE_PCT, TableColumnType.Double), - TableColumn(RESOURCE_STATE_CPU_DEMAND, TableColumnType.Double), - TableColumn(RESOURCE_STATE_CPU_READY_PCT, TableColumnType.Double), - TableColumn(RESOURCE_MEM_CAPACITY, TableColumnType.Double), - TableColumn(RESOURCE_STATE_DISK_READ, TableColumnType.Double), - TableColumn(RESOURCE_STATE_DISK_WRITE, TableColumnType.Double) + TABLE_RESOURCE_STATES -> + TableDetails( + listOf( + TableColumn(resourceID, TableColumnType.String), + TableColumn(resourceClusterID, TableColumnType.String), + TableColumn(resourceStateTimestamp, TableColumnType.Instant), + TableColumn(resourceCpuCount, TableColumnType.Int), + TableColumn(resourceCpuCapacity, TableColumnType.Double), + TableColumn(resourceStateCpuUsage, TableColumnType.Double), + TableColumn(resourceStateCpuUsagePct, TableColumnType.Double), + TableColumn(resourceStateCpuDemand, TableColumnType.Double), + TableColumn(resourceStateCpuReadyPct, TableColumnType.Double), + TableColumn(resourceMemCapacity, TableColumnType.Double), + TableColumn(resourceStateDiskRead, TableColumnType.Double), + TableColumn(resourceStateDiskWrite, TableColumnType.Double), + ), ) - ) else -> throw IllegalArgumentException("Table $table not supported") } } - override fun newReader(path: Path, table: String, projection: List?): TableReader { + override fun newReader( + path: Path, + table: String, + projection: List?, + ): TableReader { return when (table) { TABLE_RESOURCE_STATES -> newResourceStateReader(path) else -> throw IllegalArgumentException("Table $table not supported") } } - override fun newWriter(path: Path, table: String): TableWriter { + override fun newWriter( + path: Path, + table: String, + ): TableWriter { throw UnsupportedOperationException("Writing not supported for this format") } @@ -101,10 +112,11 @@ public class BitbrainsExTraceFormat : TraceFormat { * Construct a [TableReader] for reading over all resource state partitions. */ private fun newResourceStateReader(path: Path): TableReader { - val partitions = Files.walk(path, 1) - .filter { !Files.isDirectory(it) && it.extension == "txt" } - .collect(Collectors.toMap({ it.nameWithoutExtension }, { it })) - .toSortedMap() + val partitions = + Files.walk(path, 1) + .filter { !Files.isDirectory(it) && it.extension == "txt" } + .collect(Collectors.toMap({ it.nameWithoutExtension }, { it })) + .toSortedMap() val it = partitions.iterator() return object : CompositeTableReader() { diff --git a/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsResourceStateTableReader.kt b/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsResourceStateTableReader.kt index 65ca8a9c7..e264fccb7 100644 --- a/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsResourceStateTableReader.kt +++ b/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsResourceStateTableReader.kt @@ -27,18 +27,18 @@ import com.fasterxml.jackson.core.JsonToken import com.fasterxml.jackson.dataformat.csv.CsvParser import com.fasterxml.jackson.dataformat.csv.CsvSchema import org.opendc.trace.TableReader -import org.opendc.trace.conv.RESOURCE_CPU_CAPACITY -import org.opendc.trace.conv.RESOURCE_CPU_COUNT -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_MEM_CAPACITY -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE_PCT -import org.opendc.trace.conv.RESOURCE_STATE_DISK_READ -import org.opendc.trace.conv.RESOURCE_STATE_DISK_WRITE -import org.opendc.trace.conv.RESOURCE_STATE_MEM_USAGE -import org.opendc.trace.conv.RESOURCE_STATE_NET_RX -import org.opendc.trace.conv.RESOURCE_STATE_NET_TX -import org.opendc.trace.conv.RESOURCE_STATE_TIMESTAMP +import org.opendc.trace.conv.resourceCpuCapacity +import org.opendc.trace.conv.resourceCpuCount +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceMemCapacity +import org.opendc.trace.conv.resourceStateCpuUsage +import org.opendc.trace.conv.resourceStateCpuUsagePct +import org.opendc.trace.conv.resourceStateDiskRead +import org.opendc.trace.conv.resourceStateDiskWrite +import org.opendc.trace.conv.resourceStateMemUsage +import org.opendc.trace.conv.resourceStateNetRx +import org.opendc.trace.conv.resourceStateNetTx +import org.opendc.trace.conv.resourceStateTimestamp import java.text.NumberFormat import java.time.Duration import java.time.Instant @@ -103,20 +103,21 @@ internal class BitbrainsResourceStateTableReader(private val partition: String, when (parser.currentName) { "Timestamp [ms]" -> { - timestamp = when (timestampType) { - TimestampType.UNDECIDED -> { - try { - val res = LocalDateTime.parse(parser.text, formatter).toInstant(ZoneOffset.UTC) - timestampType = TimestampType.DATE_TIME - res - } catch (e: DateTimeParseException) { - timestampType = TimestampType.EPOCH_MILLIS - Instant.ofEpochSecond(parser.longValue) + timestamp = + when (timestampType) { + TimestampType.UNDECIDED -> { + try { + val res = LocalDateTime.parse(parser.text, formatter).toInstant(ZoneOffset.UTC) + timestampType = TimestampType.DATE_TIME + res + } catch (e: DateTimeParseException) { + timestampType = TimestampType.EPOCH_MILLIS + Instant.ofEpochSecond(parser.longValue) + } } + TimestampType.DATE_TIME -> LocalDateTime.parse(parser.text, formatter).toInstant(ZoneOffset.UTC) + TimestampType.EPOCH_MILLIS -> Instant.ofEpochSecond(parser.longValue) } - TimestampType.DATE_TIME -> LocalDateTime.parse(parser.text, formatter).toInstant(ZoneOffset.UTC) - TimestampType.EPOCH_MILLIS -> Instant.ofEpochSecond(parser.longValue) - } } "CPU cores" -> cpuCores = parser.intValue "CPU capacity provisioned [MHZ]" -> cpuCapacity = parseSafeDouble() @@ -134,39 +135,39 @@ internal class BitbrainsResourceStateTableReader(private val partition: String, return true } - private val COL_TIMESTAMP = 0 - private val COL_CPU_COUNT = 1 - private val COL_CPU_CAPACITY = 2 - private val COL_CPU_USAGE = 3 - private val COL_CPU_USAGE_PCT = 4 - private val COL_MEM_CAPACITY = 5 - private val COL_MEM_USAGE = 6 - private val COL_DISK_READ = 7 - private val COL_DISK_WRITE = 8 - private val COL_NET_RX = 9 - private val COL_NET_TX = 10 - private val COL_ID = 11 + private val colTimestamp = 0 + private val colCpuCount = 1 + private val colCpuCapacity = 2 + private val colCpuUsage = 3 + private val colCpuUsagePct = 4 + private val colMemCapacity = 5 + private val colMemUsage = 6 + private val colDiskRead = 7 + private val colDiskWrite = 8 + private val colNetRx = 9 + private val colNetTx = 10 + private val colID = 11 override fun resolve(name: String): Int { return when (name) { - RESOURCE_ID -> COL_ID - RESOURCE_STATE_TIMESTAMP -> COL_TIMESTAMP - RESOURCE_CPU_COUNT -> COL_CPU_COUNT - RESOURCE_CPU_CAPACITY -> COL_CPU_CAPACITY - RESOURCE_STATE_CPU_USAGE -> COL_CPU_USAGE - RESOURCE_STATE_CPU_USAGE_PCT -> COL_CPU_USAGE_PCT - RESOURCE_MEM_CAPACITY -> COL_MEM_CAPACITY - RESOURCE_STATE_MEM_USAGE -> COL_MEM_USAGE - RESOURCE_STATE_DISK_READ -> COL_DISK_READ - RESOURCE_STATE_DISK_WRITE -> COL_DISK_WRITE - RESOURCE_STATE_NET_RX -> COL_NET_RX - RESOURCE_STATE_NET_TX -> COL_NET_TX + resourceID -> colID + resourceStateTimestamp -> colTimestamp + resourceCpuCount -> colCpuCount + resourceCpuCapacity -> colCpuCapacity + resourceStateCpuUsage -> colCpuUsage + resourceStateCpuUsagePct -> colCpuUsagePct + resourceMemCapacity -> colMemCapacity + resourceStateMemUsage -> colMemUsage + resourceStateDiskRead -> colDiskRead + resourceStateDiskWrite -> colDiskWrite + resourceStateNetRx -> colNetRx + resourceStateNetTx -> colNetTx else -> -1 } } override fun isNull(index: Int): Boolean { - require(index in 0..COL_ID) { "Invalid column index" } + require(index in 0..colID) { "Invalid column index" } return false } @@ -177,7 +178,7 @@ internal class BitbrainsResourceStateTableReader(private val partition: String, override fun getInt(index: Int): Int { checkActive() return when (index) { - COL_CPU_COUNT -> cpuCores + colCpuCount -> cpuCores else -> throw IllegalArgumentException("Invalid column") } } @@ -193,15 +194,15 @@ internal class BitbrainsResourceStateTableReader(private val partition: String, override fun getDouble(index: Int): Double { checkActive() return when (index) { - COL_CPU_CAPACITY -> cpuCapacity - COL_CPU_USAGE -> cpuUsage - COL_CPU_USAGE_PCT -> cpuUsagePct - COL_MEM_CAPACITY -> memCapacity - COL_MEM_USAGE -> memUsage - COL_DISK_READ -> diskRead - COL_DISK_WRITE -> diskWrite - COL_NET_RX -> netReceived - COL_NET_TX -> netTransmitted + colCpuCapacity -> cpuCapacity + colCpuUsage -> cpuUsage + colCpuUsagePct -> cpuUsagePct + colMemCapacity -> memCapacity + colMemUsage -> memUsage + colDiskRead -> diskRead + colDiskWrite -> diskWrite + colNetRx -> netReceived + colNetTx -> netTransmitted else -> throw IllegalArgumentException("Invalid column") } } @@ -209,7 +210,7 @@ internal class BitbrainsResourceStateTableReader(private val partition: String, override fun getString(index: Int): String { checkActive() return when (index) { - COL_ID -> partition + colID -> partition else -> throw IllegalArgumentException("Invalid column") } } @@ -221,7 +222,7 @@ internal class BitbrainsResourceStateTableReader(private val partition: String, override fun getInstant(index: Int): Instant? { checkActive() return when (index) { - COL_TIMESTAMP -> timestamp + colTimestamp -> timestamp else -> throw IllegalArgumentException("Invalid column") } } @@ -230,15 +231,25 @@ internal class BitbrainsResourceStateTableReader(private val partition: String, throw IllegalArgumentException("Invalid column") } - override fun getList(index: Int, elementType: Class): List? { + override fun getList( + index: Int, + elementType: Class, + ): List? { throw IllegalArgumentException("Invalid column") } - override fun getSet(index: Int, elementType: Class): Set? { + override fun getSet( + index: Int, + elementType: Class, + ): Set? { throw IllegalArgumentException("Invalid column") } - override fun getMap(index: Int, keyType: Class, valueType: Class): Map? { + override fun getMap( + index: Int, + keyType: Class, + valueType: Class, + ): Map? { throw IllegalArgumentException("Invalid column") } @@ -322,30 +333,33 @@ internal class BitbrainsResourceStateTableReader(private val partition: String, * The type of the timestamp in the trace. */ private enum class TimestampType { - UNDECIDED, DATE_TIME, EPOCH_MILLIS + UNDECIDED, + DATE_TIME, + EPOCH_MILLIS, } companion object { /** * The [CsvSchema] that is used to parse the trace. */ - private val schema = CsvSchema.builder() - .addColumn("Timestamp [ms]", CsvSchema.ColumnType.NUMBER_OR_STRING) - .addColumn("CPU cores", CsvSchema.ColumnType.NUMBER) - .addColumn("CPU capacity provisioned [MHZ]", CsvSchema.ColumnType.NUMBER) - .addColumn("CPU usage [MHZ]", CsvSchema.ColumnType.NUMBER) - .addColumn("CPU usage [%]", CsvSchema.ColumnType.NUMBER) - .addColumn("Memory capacity provisioned [KB]", CsvSchema.ColumnType.NUMBER) - .addColumn("Memory usage [KB]", CsvSchema.ColumnType.NUMBER) - .addColumn("Memory usage [%]", CsvSchema.ColumnType.NUMBER) - .addColumn("Disk read throughput [KB/s]", CsvSchema.ColumnType.NUMBER) - .addColumn("Disk write throughput [KB/s]", CsvSchema.ColumnType.NUMBER) - .addColumn("Disk size [GB]", CsvSchema.ColumnType.NUMBER) - .addColumn("Network received throughput [KB/s]", CsvSchema.ColumnType.NUMBER) - .addColumn("Network transmitted throughput [KB/s]", CsvSchema.ColumnType.NUMBER) - .setAllowComments(true) - .setUseHeader(true) - .setColumnSeparator(';') - .build() + private val schema = + CsvSchema.builder() + .addColumn("Timestamp [ms]", CsvSchema.ColumnType.NUMBER_OR_STRING) + .addColumn("CPU cores", CsvSchema.ColumnType.NUMBER) + .addColumn("CPU capacity provisioned [MHZ]", CsvSchema.ColumnType.NUMBER) + .addColumn("CPU usage [MHZ]", CsvSchema.ColumnType.NUMBER) + .addColumn("CPU usage [%]", CsvSchema.ColumnType.NUMBER) + .addColumn("Memory capacity provisioned [KB]", CsvSchema.ColumnType.NUMBER) + .addColumn("Memory usage [KB]", CsvSchema.ColumnType.NUMBER) + .addColumn("Memory usage [%]", CsvSchema.ColumnType.NUMBER) + .addColumn("Disk read throughput [KB/s]", CsvSchema.ColumnType.NUMBER) + .addColumn("Disk write throughput [KB/s]", CsvSchema.ColumnType.NUMBER) + .addColumn("Disk size [GB]", CsvSchema.ColumnType.NUMBER) + .addColumn("Network received throughput [KB/s]", CsvSchema.ColumnType.NUMBER) + .addColumn("Network transmitted throughput [KB/s]", CsvSchema.ColumnType.NUMBER) + .setAllowComments(true) + .setUseHeader(true) + .setColumnSeparator(';') + .build() } } diff --git a/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsResourceTableReader.kt b/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsResourceTableReader.kt index 776a8f86c..a12785f0c 100644 --- a/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsResourceTableReader.kt +++ b/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsResourceTableReader.kt @@ -24,7 +24,7 @@ package org.opendc.trace.bitbrains import com.fasterxml.jackson.dataformat.csv.CsvFactory import org.opendc.trace.TableReader -import org.opendc.trace.conv.RESOURCE_ID +import org.opendc.trace.conv.resourceID import java.nio.file.Path import java.time.Duration import java.time.Instant @@ -56,7 +56,7 @@ internal class BitbrainsResourceTableReader(private val factory: CsvFactory, vms val parser = factory.createParser(path.toFile()) val reader = BitbrainsResourceStateTableReader(name, parser) - val idCol = reader.resolve(RESOURCE_ID) + val idCol = reader.resolve(resourceID) try { if (!reader.nextRow()) { @@ -74,17 +74,17 @@ internal class BitbrainsResourceTableReader(private val factory: CsvFactory, vms return false } - private val COL_ID = 0 + private val colID = 0 override fun resolve(name: String): Int { return when (name) { - RESOURCE_ID -> COL_ID + resourceID -> colID else -> -1 } } override fun isNull(index: Int): Boolean { - require(index in 0..COL_ID) { "Invalid column index" } + require(index in 0..colID) { "Invalid column index" } return false } @@ -111,7 +111,7 @@ internal class BitbrainsResourceTableReader(private val factory: CsvFactory, vms override fun getString(index: Int): String? { check(state == State.Active) { "No active row" } return when (index) { - COL_ID -> id + colID -> id else -> throw IllegalArgumentException("Invalid column") } } @@ -128,15 +128,25 @@ internal class BitbrainsResourceTableReader(private val factory: CsvFactory, vms throw IllegalArgumentException("Invalid column") } - override fun getList(index: Int, elementType: Class): List? { + override fun getList( + index: Int, + elementType: Class, + ): List? { throw IllegalArgumentException("Invalid column") } - override fun getSet(index: Int, elementType: Class): Set? { + override fun getSet( + index: Int, + elementType: Class, + ): Set? { throw IllegalArgumentException("Invalid column") } - override fun getMap(index: Int, keyType: Class, valueType: Class): Map? { + override fun getMap( + index: Int, + keyType: Class, + valueType: Class, + ): Map? { throw IllegalArgumentException("Invalid column") } @@ -158,6 +168,8 @@ internal class BitbrainsResourceTableReader(private val factory: CsvFactory, vms } private enum class State { - Pending, Active, Closed + Pending, + Active, + Closed, } } diff --git a/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsTraceFormat.kt b/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsTraceFormat.kt index b0809735c..238530776 100644 --- a/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsTraceFormat.kt +++ b/opendc-trace/opendc-trace-bitbrains/src/main/kotlin/org/opendc/trace/bitbrains/BitbrainsTraceFormat.kt @@ -28,20 +28,20 @@ import org.opendc.trace.TableColumn import org.opendc.trace.TableColumnType import org.opendc.trace.TableReader import org.opendc.trace.TableWriter -import org.opendc.trace.conv.RESOURCE_CPU_CAPACITY -import org.opendc.trace.conv.RESOURCE_CPU_COUNT -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_MEM_CAPACITY -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE_PCT -import org.opendc.trace.conv.RESOURCE_STATE_DISK_READ -import org.opendc.trace.conv.RESOURCE_STATE_DISK_WRITE -import org.opendc.trace.conv.RESOURCE_STATE_MEM_USAGE -import org.opendc.trace.conv.RESOURCE_STATE_NET_RX -import org.opendc.trace.conv.RESOURCE_STATE_NET_TX -import org.opendc.trace.conv.RESOURCE_STATE_TIMESTAMP import org.opendc.trace.conv.TABLE_RESOURCES import org.opendc.trace.conv.TABLE_RESOURCE_STATES +import org.opendc.trace.conv.resourceCpuCapacity +import org.opendc.trace.conv.resourceCpuCount +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceMemCapacity +import org.opendc.trace.conv.resourceStateCpuUsage +import org.opendc.trace.conv.resourceStateCpuUsagePct +import org.opendc.trace.conv.resourceStateDiskRead +import org.opendc.trace.conv.resourceStateDiskWrite +import org.opendc.trace.conv.resourceStateMemUsage +import org.opendc.trace.conv.resourceStateNetRx +import org.opendc.trace.conv.resourceStateNetTx +import org.opendc.trace.conv.resourceStateTimestamp import org.opendc.trace.spi.TableDetails import org.opendc.trace.spi.TraceFormat import org.opendc.trace.util.CompositeTableReader @@ -63,9 +63,10 @@ public class BitbrainsTraceFormat : TraceFormat { /** * The [CsvFactory] used to create the parser. */ - private val factory = CsvFactory() - .enable(CsvParser.Feature.ALLOW_COMMENTS) - .enable(CsvParser.Feature.TRIM_SPACES) + private val factory = + CsvFactory() + .enable(CsvParser.Feature.ALLOW_COMMENTS) + .enable(CsvParser.Feature.TRIM_SPACES) override fun create(path: Path) { throw UnsupportedOperationException("Writing not supported for this format") @@ -73,40 +74,50 @@ public class BitbrainsTraceFormat : TraceFormat { override fun getTables(path: Path): List = listOf(TABLE_RESOURCES, TABLE_RESOURCE_STATES) - override fun getDetails(path: Path, table: String): TableDetails { + override fun getDetails( + path: Path, + table: String, + ): TableDetails { return when (table) { - TABLE_RESOURCES -> TableDetails( - listOf( - TableColumn(RESOURCE_ID, TableColumnType.String) + TABLE_RESOURCES -> + TableDetails( + listOf( + TableColumn(resourceID, TableColumnType.String), + ), ) - ) - TABLE_RESOURCE_STATES -> TableDetails( - listOf( - TableColumn(RESOURCE_ID, TableColumnType.String), - TableColumn(RESOURCE_STATE_TIMESTAMP, TableColumnType.Instant), - TableColumn(RESOURCE_CPU_COUNT, TableColumnType.Int), - TableColumn(RESOURCE_CPU_CAPACITY, TableColumnType.Double), - TableColumn(RESOURCE_STATE_CPU_USAGE, TableColumnType.Double), - TableColumn(RESOURCE_STATE_CPU_USAGE_PCT, TableColumnType.Double), - TableColumn(RESOURCE_MEM_CAPACITY, TableColumnType.Double), - TableColumn(RESOURCE_STATE_MEM_USAGE, TableColumnType.Double), - TableColumn(RESOURCE_STATE_DISK_READ, TableColumnType.Double), - TableColumn(RESOURCE_STATE_DISK_WRITE, TableColumnType.Double), - TableColumn(RESOURCE_STATE_NET_RX, TableColumnType.Double), - TableColumn(RESOURCE_STATE_NET_TX, TableColumnType.Double) + TABLE_RESOURCE_STATES -> + TableDetails( + listOf( + TableColumn(resourceID, TableColumnType.String), + TableColumn(resourceStateTimestamp, TableColumnType.Instant), + TableColumn(resourceCpuCount, TableColumnType.Int), + TableColumn(resourceCpuCapacity, TableColumnType.Double), + TableColumn(resourceStateCpuUsage, TableColumnType.Double), + TableColumn(resourceStateCpuUsagePct, TableColumnType.Double), + TableColumn(resourceMemCapacity, TableColumnType.Double), + TableColumn(resourceStateMemUsage, TableColumnType.Double), + TableColumn(resourceStateDiskRead, TableColumnType.Double), + TableColumn(resourceStateDiskWrite, TableColumnType.Double), + TableColumn(resourceStateNetRx, TableColumnType.Double), + TableColumn(resourceStateNetTx, TableColumnType.Double), + ), ) - ) else -> throw IllegalArgumentException("Table $table not supported") } } - override fun newReader(path: Path, table: String, projection: List?): TableReader { + override fun newReader( + path: Path, + table: String, + projection: List?, + ): TableReader { return when (table) { TABLE_RESOURCES -> { - val vms = Files.walk(path, 1) - .filter { !Files.isDirectory(it) && it.extension == "csv" } - .collect(Collectors.toMap({ it.nameWithoutExtension }, { it })) - .toSortedMap() + val vms = + Files.walk(path, 1) + .filter { !Files.isDirectory(it) && it.extension == "csv" } + .collect(Collectors.toMap({ it.nameWithoutExtension }, { it })) + .toSortedMap() BitbrainsResourceTableReader(factory, vms) } TABLE_RESOURCE_STATES -> newResourceStateReader(path) @@ -114,7 +125,10 @@ public class BitbrainsTraceFormat : TraceFormat { } } - override fun newWriter(path: Path, table: String): TableWriter { + override fun newWriter( + path: Path, + table: String, + ): TableWriter { throw UnsupportedOperationException("Writing not supported for this format") } @@ -122,10 +136,11 @@ public class BitbrainsTraceFormat : TraceFormat { * Construct a [TableReader] for reading over all resource state partitions. */ private fun newResourceStateReader(path: Path): TableReader { - val partitions = Files.walk(path, 1) - .filter { !Files.isDirectory(it) && it.extension == "csv" } - .collect(Collectors.toMap({ it.nameWithoutExtension }, { it })) - .toSortedMap() + val partitions = + Files.walk(path, 1) + .filter { !Files.isDirectory(it) && it.extension == "csv" } + .collect(Collectors.toMap({ it.nameWithoutExtension }, { it })) + .toSortedMap() val it = partitions.iterator() return object : CompositeTableReader() { diff --git a/opendc-trace/opendc-trace-bitbrains/src/test/kotlin/org/opendc/trace/bitbrains/BitbrainsExTraceFormatTest.kt b/opendc-trace/opendc-trace-bitbrains/src/test/kotlin/org/opendc/trace/bitbrains/BitbrainsExTraceFormatTest.kt index e8c7094b2..18c59fb80 100644 --- a/opendc-trace/opendc-trace-bitbrains/src/test/kotlin/org/opendc/trace/bitbrains/BitbrainsExTraceFormatTest.kt +++ b/opendc-trace/opendc-trace-bitbrains/src/test/kotlin/org/opendc/trace/bitbrains/BitbrainsExTraceFormatTest.kt @@ -33,9 +33,9 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.opendc.trace.TableColumn import org.opendc.trace.TableReader -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE -import org.opendc.trace.conv.RESOURCE_STATE_TIMESTAMP import org.opendc.trace.conv.TABLE_RESOURCE_STATES +import org.opendc.trace.conv.resourceStateCpuUsage +import org.opendc.trace.conv.resourceStateTimestamp import org.opendc.trace.testkit.TableReaderTestKit import java.nio.file.Paths @@ -72,8 +72,8 @@ internal class BitbrainsExTraceFormatTest { assertAll( { assertTrue(reader.nextRow()) }, - { assertEquals(1631911500, reader.getInstant(RESOURCE_STATE_TIMESTAMP)?.epochSecond) }, - { assertEquals(21.2, reader.getDouble(RESOURCE_STATE_CPU_USAGE), 0.01) } + { assertEquals(1631911500, reader.getInstant(resourceStateTimestamp)?.epochSecond) }, + { assertEquals(21.2, reader.getDouble(resourceStateCpuUsage), 0.01) }, ) reader.close() diff --git a/opendc-trace/opendc-trace-bitbrains/src/test/kotlin/org/opendc/trace/bitbrains/BitbrainsTraceFormatTest.kt b/opendc-trace/opendc-trace-bitbrains/src/test/kotlin/org/opendc/trace/bitbrains/BitbrainsTraceFormatTest.kt index edab8747e..8ff138527 100644 --- a/opendc-trace/opendc-trace-bitbrains/src/test/kotlin/org/opendc/trace/bitbrains/BitbrainsTraceFormatTest.kt +++ b/opendc-trace/opendc-trace-bitbrains/src/test/kotlin/org/opendc/trace/bitbrains/BitbrainsTraceFormatTest.kt @@ -34,11 +34,11 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.opendc.trace.TableColumn import org.opendc.trace.TableReader -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE -import org.opendc.trace.conv.RESOURCE_STATE_TIMESTAMP import org.opendc.trace.conv.TABLE_RESOURCES import org.opendc.trace.conv.TABLE_RESOURCE_STATES +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceStateCpuUsage +import org.opendc.trace.conv.resourceStateTimestamp import org.opendc.trace.testkit.TableReaderTestKit import java.nio.file.Paths @@ -75,8 +75,8 @@ class BitbrainsTraceFormatTest { assertAll( { assertTrue(reader.nextRow()) }, - { assertEquals("bitbrains", reader.getString(RESOURCE_ID)) }, - { assertFalse(reader.nextRow()) } + { assertEquals("bitbrains", reader.getString(resourceID)) }, + { assertFalse(reader.nextRow()) }, ) reader.close() @@ -89,8 +89,8 @@ class BitbrainsTraceFormatTest { assertAll( { assertTrue(reader.nextRow()) }, - { assertEquals(1376314846, reader.getInstant(RESOURCE_STATE_TIMESTAMP)?.epochSecond) }, - { assertEquals(19.066, reader.getDouble(RESOURCE_STATE_CPU_USAGE), 0.01) } + { assertEquals(1376314846, reader.getInstant(resourceStateTimestamp)?.epochSecond) }, + { assertEquals(19.066, reader.getDouble(resourceStateCpuUsage), 0.01) }, ) reader.close() diff --git a/opendc-trace/opendc-trace-calcite/build.gradle.kts b/opendc-trace/opendc-trace-calcite/build.gradle.kts index 2ffdac3cb..848e00dac 100644 --- a/opendc-trace/opendc-trace-calcite/build.gradle.kts +++ b/opendc-trace/opendc-trace-calcite/build.gradle.kts @@ -22,7 +22,7 @@ description = "Apache Calcite (SQL) integration for the OpenDC trace library" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceReaderEnumerator.kt b/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceReaderEnumerator.kt index 74bd188b4..eed52ab3f 100644 --- a/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceReaderEnumerator.kt +++ b/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceReaderEnumerator.kt @@ -36,7 +36,7 @@ import java.util.concurrent.atomic.AtomicBoolean internal class TraceReaderEnumerator( private val reader: TableReader, private val columns: List, - private val cancelFlag: AtomicBoolean + private val cancelFlag: AtomicBoolean, ) : Enumerator { private val columnIndices = columns.map { reader.resolve(it.name) }.toIntArray() private var current: E? = null @@ -80,7 +80,11 @@ internal class TraceReaderEnumerator( return res } - private fun convertColumn(reader: TableReader, column: TableColumn, columnIndex: Int): Any? { + private fun convertColumn( + reader: TableReader, + column: TableColumn, + columnIndex: Int, + ): Any? { return when (column.type) { is TableColumnType.Boolean -> reader.getBoolean(columnIndex) is TableColumnType.Int -> reader.getInt(columnIndex) diff --git a/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceSchemaFactory.kt b/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceSchemaFactory.kt index 3c6badc84..cbf7ec43d 100644 --- a/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceSchemaFactory.kt +++ b/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceSchemaFactory.kt @@ -36,7 +36,11 @@ import java.nio.file.Paths * This factory allows users to include a schema that references a trace in a `model.json` file. */ public class TraceSchemaFactory : SchemaFactory { - override fun create(parentSchema: SchemaPlus, name: String, operand: Map): Schema { + override fun create( + parentSchema: SchemaPlus, + name: String, + operand: Map, + ): Schema { val base = operand[ModelHandler.ExtraOperand.BASE_DIRECTORY.camelName] as File? val pathParam = requireNotNull(operand["path"]) { "Trace path not specified" } as String val path = if (base != null) File(base, pathParam).toPath() else Paths.get(pathParam) diff --git a/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceTable.kt b/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceTable.kt index 2dd02710e..e74d2ee8d 100644 --- a/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceTable.kt +++ b/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceTable.kt @@ -71,7 +71,11 @@ internal class TraceTable(private val table: org.opendc.trace.Table) : return rowType } - override fun scan(root: DataContext, filters: MutableList, projects: IntArray?): Enumerable> { + override fun scan( + root: DataContext, + filters: MutableList, + projects: IntArray?, + ): Enumerable> { // Filters are currently not supported by the OpenDC trace API. By keeping the filters in the list, Calcite // assumes that they are declined and will perform the filters itself. @@ -130,14 +134,18 @@ internal class TraceTable(private val table: org.opendc.trace.Table) : return rowCount } - override fun asQueryable(queryProvider: QueryProvider, schema: SchemaPlus, tableName: String): Queryable { + override fun asQueryable( + queryProvider: QueryProvider, + schema: SchemaPlus, + tableName: String, + ): Queryable { return object : AbstractTableQueryable(queryProvider, schema, this@TraceTable, tableName) { override fun enumerator(): Enumerator { val cancelFlag = AtomicBoolean(false) return TraceReaderEnumerator( this@TraceTable.table.newReader(), this@TraceTable.table.columns, - cancelFlag + cancelFlag, ) } @@ -155,7 +163,7 @@ internal class TraceTable(private val table: org.opendc.trace.Table) : operation: TableModify.Operation, updateColumnList: MutableList?, sourceExpressionList: MutableList?, - flattened: Boolean + flattened: Boolean, ): TableModify { cluster.planner.addRule(TraceTableModifyRule.DEFAULT.toRule()) @@ -166,7 +174,7 @@ internal class TraceTable(private val table: org.opendc.trace.Table) : operation, updateColumnList, sourceExpressionList, - flattened + flattened, ) } @@ -184,7 +192,10 @@ internal class TraceTable(private val table: org.opendc.trace.Table) : return typeFactory.createStructType(types, names) } - private fun mapType(typeFactory: JavaTypeFactory, type: TableColumnType): RelDataType { + private fun mapType( + typeFactory: JavaTypeFactory, + type: TableColumnType, + ): RelDataType { return when (type) { is TableColumnType.Boolean -> typeFactory.createSqlType(SqlTypeName.BOOLEAN) is TableColumnType.Int -> typeFactory.createSqlType(SqlTypeName.INTEGER) diff --git a/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceTableModify.kt b/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceTableModify.kt index cc23854ff..eedff00d0 100644 --- a/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceTableModify.kt +++ b/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceTableModify.kt @@ -59,7 +59,7 @@ internal class TraceTableModify( operation: Operation, updateColumnList: List?, sourceExpressionList: List?, - flattened: Boolean + flattened: Boolean, ) : TableModify(cluster, traitSet, table, schema, input, operation, updateColumnList, sourceExpressionList, flattened), EnumerableRel { init { @@ -67,7 +67,10 @@ internal class TraceTableModify( table.unwrap(ModifiableTable::class.java) ?: throw AssertionError() // TODO: user error in validator } - override fun copy(traitSet: RelTraitSet, inputs: List?): RelNode { + override fun copy( + traitSet: RelTraitSet, + inputs: List?, + ): RelNode { return TraceTableModify( cluster, traitSet, @@ -77,40 +80,48 @@ internal class TraceTableModify( operation, updateColumnList, sourceExpressionList, - isFlattened + isFlattened, ) } - override fun computeSelfCost(planner: RelOptPlanner, mq: RelMetadataQuery?): RelOptCost { + override fun computeSelfCost( + planner: RelOptPlanner, + mq: RelMetadataQuery?, + ): RelOptCost { // Prefer this plan compared to the standard EnumerableTableModify. return super.computeSelfCost(planner, mq)!!.multiplyBy(.1) } - override fun implement(implementor: EnumerableRelImplementor, pref: Prefer): EnumerableRel.Result { + override fun implement( + implementor: EnumerableRelImplementor, + pref: Prefer, + ): EnumerableRel.Result { val builder = BlockBuilder() val result = implementor.visitChild(this, 0, getInput() as EnumerableRel, pref) val childExp = builder.append("child", result.block) - val convertedChildExpr = if (getInput().rowType != rowType) { - val typeFactory = cluster.typeFactory as JavaTypeFactory - val format = EnumerableTableScan.deduceFormat(table) - val physType = PhysTypeImpl.of(typeFactory, table.rowType, format) - val childPhysType = result.physType - val o = Expressions.parameter(childPhysType.javaRowType, "o") - val expressionList = List(childPhysType.rowType.fieldCount) { i -> - childPhysType.fieldReference(o, i, physType.getJavaFieldType(i)) - } + val convertedChildExpr = + if (getInput().rowType != rowType) { + val typeFactory = cluster.typeFactory as JavaTypeFactory + val format = EnumerableTableScan.deduceFormat(table) + val physType = PhysTypeImpl.of(typeFactory, table.rowType, format) + val childPhysType = result.physType + val o = Expressions.parameter(childPhysType.javaRowType, "o") + val expressionList = + List(childPhysType.rowType.fieldCount) { i -> + childPhysType.fieldReference(o, i, physType.getJavaFieldType(i)) + } - builder.append( - "convertedChild", - Expressions.call( - childExp, - BuiltInMethod.SELECT.method, - Expressions.lambda>(physType.record(expressionList), o) + builder.append( + "convertedChild", + Expressions.call( + childExp, + BuiltInMethod.SELECT.method, + Expressions.lambda>(physType.record(expressionList), o), + ), ) - ) - } else { - childExp - } + } else { + childExp + } if (!isInsert) { throw UnsupportedOperationException("Deletion and update not supported") @@ -126,10 +137,10 @@ internal class TraceTableModify( Long::class.java, expression, INSERT_METHOD, - convertedChildExpr - ) - ) - ) + convertedChildExpr, + ), + ), + ), ) val rowFormat = if (pref === Prefer.ARRAY) JavaRowFormat.ARRAY else JavaRowFormat.SCALAR diff --git a/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceTableModifyRule.kt b/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceTableModifyRule.kt index 7572e3814..9c5609849 100644 --- a/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceTableModifyRule.kt +++ b/opendc-trace/opendc-trace-calcite/src/main/kotlin/org/opendc/trace/calcite/TraceTableModifyRule.kt @@ -52,14 +52,20 @@ internal class TraceTableModifyRule(config: Config) : ConverterRule(config) { modify.operation, modify.updateColumnList, modify.sourceExpressionList, - modify.isFlattened + modify.isFlattened, ) } companion object { /** Default configuration. */ - val DEFAULT: Config = Config.INSTANCE - .withConversion(LogicalTableModify::class.java, Convention.NONE, EnumerableConvention.INSTANCE, "TraceTableModificationRule") - .withRuleFactory { config: Config -> TraceTableModifyRule(config) } + val DEFAULT: Config = + Config.INSTANCE + .withConversion( + LogicalTableModify::class.java, + Convention.NONE, + EnumerableConvention.INSTANCE, + "TraceTableModificationRule", + ) + .withRuleFactory { config: Config -> TraceTableModifyRule(config) } } } diff --git a/opendc-trace/opendc-trace-calcite/src/test/kotlin/org/opendc/trace/calcite/CalciteTest.kt b/opendc-trace/opendc-trace-calcite/src/test/kotlin/org/opendc/trace/calcite/CalciteTest.kt index 64bb31c95..93b15e5fb 100644 --- a/opendc-trace/opendc-trace-calcite/src/test/kotlin/org/opendc/trace/calcite/CalciteTest.kt +++ b/opendc-trace/opendc-trace-calcite/src/test/kotlin/org/opendc/trace/calcite/CalciteTest.kt @@ -71,7 +71,7 @@ class CalciteTest { { assertEquals("1052", rs.getString("id")) }, { assertTrue(rs.next()) }, { assertEquals("1073", rs.getString("id")) }, - { assertFalse(rs.next()) } + { assertFalse(rs.next()) }, ) } } @@ -86,7 +86,7 @@ class CalciteTest { { assertEquals(300000, rs.getLong("duration")) }, { assertEquals(0.0, rs.getDouble("cpu_usage")) }, { assertTrue(rs.next()) }, - { assertEquals("1019", rs.getString("id")) } + { assertEquals("1019", rs.getString("id")) }, ) } } @@ -98,7 +98,7 @@ class CalciteTest { { assertTrue(rs.next()) }, { assertArrayEquals(arrayOf("1019", "1023", "1052"), rs.getArray("members").array as Array<*>) }, { assertEquals(0.0, rs.getDouble("target")) }, - { assertEquals(0.8830158730158756, rs.getDouble("score")) } + { assertEquals(0.8830158730158756, rs.getDouble("score")) }, ) } } @@ -109,7 +109,7 @@ class CalciteTest { assertAll( { assertTrue(rs.next()) }, { assertEquals(249.59993808, rs.getDouble("max_cpu_usage")) }, - { assertEquals(5.387240309118493, rs.getDouble("avg_cpu_usage")) } + { assertEquals(5.387240309118493, rs.getDouble("avg_cpu_usage")) }, ) } } @@ -120,12 +120,13 @@ class CalciteTest { val newTrace = Trace.create(tmp, "opendc-vm") runStatement(newTrace) { stmt -> - val count = stmt.executeUpdate( - """ - INSERT INTO trace.resources (id, start_time, stop_time, cpu_count, cpu_capacity, mem_capacity) - VALUES (1234, '2013-08-12 13:35:46.0', '2013-09-11 13:39:58.0', 1, 2926.0, 1024.0) - """.trimIndent() - ) + val count = + stmt.executeUpdate( + """ + INSERT INTO trace.resources (id, start_time, stop_time, cpu_count, cpu_capacity, mem_capacity) + VALUES (1234, '2013-08-12 13:35:46.0', '2013-09-11 13:39:58.0', 1, 2926.0, 1024.0) + """.trimIndent(), + ) assertEquals(1, count) } @@ -136,7 +137,7 @@ class CalciteTest { { assertEquals(1, rs.getInt("cpu_count")) }, { assertEquals(Timestamp.valueOf("2013-08-12 13:35:46.0"), rs.getTimestamp("start_time")) }, { assertEquals(2926.0, rs.getDouble("cpu_capacity")) }, - { assertEquals(1024.0, rs.getDouble("mem_capacity")) } + { assertEquals(1024.0, rs.getDouble("mem_capacity")) }, ) } } @@ -145,9 +146,10 @@ class CalciteTest { fun testUUID() { val trace = mockk() every { trace.tables } returns listOf(TABLE_RESOURCES) - every { trace.getTable(TABLE_RESOURCES)!!.columns } returns listOf( - TableColumn("id", TableColumnType.UUID) - ) + every { trace.getTable(TABLE_RESOURCES)!!.columns } returns + listOf( + TableColumn("id", TableColumnType.UUID), + ) every { trace.getTable(TABLE_RESOURCES)!!.newReader() } answers { object : TableReader { override fun nextRow(): Boolean = true @@ -195,15 +197,25 @@ class CalciteTest { TODO("not implemented") } - override fun getList(index: Int, elementType: Class): List? { + override fun getList( + index: Int, + elementType: Class, + ): List? { TODO("not implemented") } - override fun getSet(index: Int, elementType: Class): Set? { + override fun getSet( + index: Int, + elementType: Class, + ): Set? { TODO("not implemented") } - override fun getMap(index: Int, keyType: Class, valueType: Class): Map? { + override fun getMap( + index: Int, + keyType: Class, + valueType: Class, + ): Map? { TODO("not implemented") } @@ -214,7 +226,7 @@ class CalciteTest { runQuery(trace, "SELECT id FROM trace.resources") { rs -> assertAll( { assertTrue(rs.next()) }, - { assertArrayEquals(byteArrayOf(0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2), rs.getBytes("id")) } + { assertArrayEquals(byteArrayOf(0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2), rs.getBytes("id")) }, ) } } @@ -222,7 +234,11 @@ class CalciteTest { /** * Helper function to run statement for the specified trace. */ - private fun runQuery(trace: Trace, query: String, block: (ResultSet) -> Unit) { + private fun runQuery( + trace: Trace, + query: String, + block: (ResultSet) -> Unit, + ) { runStatement(trace) { stmt -> val rs = stmt.executeQuery(query) rs.use { block(rs) } @@ -232,7 +248,10 @@ class CalciteTest { /** * Helper function to run statement for the specified trace. */ - private fun runStatement(trace: Trace, block: (Statement) -> Unit) { + private fun runStatement( + trace: Trace, + block: (Statement) -> Unit, + ) { val info = Properties() info.setProperty("lex", "JAVA") val connection = DriverManager.getConnection("jdbc:calcite:", info).unwrap(CalciteConnection::class.java) diff --git a/opendc-trace/opendc-trace-calcite/src/test/kotlin/org/opendc/trace/calcite/TraceSchemaFactoryTest.kt b/opendc-trace/opendc-trace-calcite/src/test/kotlin/org/opendc/trace/calcite/TraceSchemaFactoryTest.kt index 735cedce2..eb4bc769b 100644 --- a/opendc-trace/opendc-trace-calcite/src/test/kotlin/org/opendc/trace/calcite/TraceSchemaFactoryTest.kt +++ b/opendc-trace/opendc-trace-calcite/src/test/kotlin/org/opendc/trace/calcite/TraceSchemaFactoryTest.kt @@ -48,7 +48,7 @@ class TraceSchemaFactoryTest { { assertEquals("1019", rs.getString("id")) }, { assertEquals(1, rs.getInt("cpu_count")) }, { assertEquals(Timestamp.valueOf("2013-08-12 13:40:46.0"), rs.getTimestamp("start_time")) }, - { assertEquals(181352.0, rs.getDouble("mem_capacity")) } + { assertEquals(181352.0, rs.getDouble("mem_capacity")) }, ) } finally { rs.close() diff --git a/opendc-trace/opendc-trace-gwf/build.gradle.kts b/opendc-trace/opendc-trace-gwf/build.gradle.kts index 0c041439a..4d0bd7962 100644 --- a/opendc-trace/opendc-trace-gwf/build.gradle.kts +++ b/opendc-trace/opendc-trace-gwf/build.gradle.kts @@ -22,7 +22,7 @@ description = "Support for GWF traces in OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-trace/opendc-trace-gwf/src/main/kotlin/org/opendc/trace/gwf/GwfTaskTableReader.kt b/opendc-trace/opendc-trace-gwf/src/main/kotlin/org/opendc/trace/gwf/GwfTaskTableReader.kt index 78ce6ad4b..8a2a99cb7 100644 --- a/opendc-trace/opendc-trace-gwf/src/main/kotlin/org/opendc/trace/gwf/GwfTaskTableReader.kt +++ b/opendc-trace/opendc-trace-gwf/src/main/kotlin/org/opendc/trace/gwf/GwfTaskTableReader.kt @@ -88,19 +88,19 @@ internal class GwfTaskTableReader(private val parser: CsvParser) : TableReader { override fun resolve(name: String): Int { return when (name) { - TASK_ID -> COL_JOB_ID - TASK_WORKFLOW_ID -> COL_WORKFLOW_ID - TASK_SUBMIT_TIME -> COL_SUBMIT_TIME - TASK_RUNTIME -> COL_RUNTIME - TASK_ALLOC_NCPUS -> COL_NPROC - TASK_REQ_NCPUS -> COL_REQ_NPROC - TASK_PARENTS -> COL_DEPS + TASK_ID -> colJobID + TASK_WORKFLOW_ID -> colWorkflowID + TASK_SUBMIT_TIME -> colSubmitTime + TASK_RUNTIME -> colRuntime + TASK_ALLOC_NCPUS -> colNproc + TASK_REQ_NCPUS -> colReqNproc + TASK_PARENTS -> colDeps else -> -1 } } override fun isNull(index: Int): Boolean { - require(index in 0..COL_DEPS) { "Invalid column" } + require(index in 0..colDeps) { "Invalid column" } return false } @@ -111,8 +111,8 @@ internal class GwfTaskTableReader(private val parser: CsvParser) : TableReader { override fun getInt(index: Int): Int { checkActive() return when (index) { - COL_REQ_NPROC -> reqNProcs - COL_NPROC -> nProcs + colReqNproc -> reqNProcs + colNproc -> nProcs else -> throw IllegalArgumentException("Invalid column") } } @@ -132,8 +132,8 @@ internal class GwfTaskTableReader(private val parser: CsvParser) : TableReader { override fun getString(index: Int): String? { checkActive() return when (index) { - COL_JOB_ID -> jobId - COL_WORKFLOW_ID -> workflowId + colJobID -> jobId + colWorkflowID -> workflowId else -> throw IllegalArgumentException("Invalid column") } } @@ -145,7 +145,7 @@ internal class GwfTaskTableReader(private val parser: CsvParser) : TableReader { override fun getInstant(index: Int): Instant? { checkActive() return when (index) { - COL_SUBMIT_TIME -> submitTime + colSubmitTime -> submitTime else -> throw IllegalArgumentException("Invalid column") } } @@ -153,23 +153,33 @@ internal class GwfTaskTableReader(private val parser: CsvParser) : TableReader { override fun getDuration(index: Int): Duration? { checkActive() return when (index) { - COL_RUNTIME -> runtime + colRuntime -> runtime else -> throw IllegalArgumentException("Invalid column") } } - override fun getList(index: Int, elementType: Class): List? { + override fun getList( + index: Int, + elementType: Class, + ): List? { throw IllegalArgumentException("Invalid column") } - override fun getMap(index: Int, keyType: Class, valueType: Class): Map? { + override fun getMap( + index: Int, + keyType: Class, + valueType: Class, + ): Map? { throw IllegalArgumentException("Invalid column") } - override fun getSet(index: Int, elementType: Class): Set? { + override fun getSet( + index: Int, + elementType: Class, + ): Set? { checkActive() return when (index) { - COL_DEPS -> TYPE_DEPS.convertTo(dependencies, elementType) + colDeps -> typeDeps.convertTo(dependencies, elementType) else -> throw IllegalArgumentException("Invalid column") } } @@ -245,31 +255,32 @@ internal class GwfTaskTableReader(private val parser: CsvParser) : TableReader { dependencies = emptySet() } - private val COL_WORKFLOW_ID = 0 - private val COL_JOB_ID = 1 - private val COL_SUBMIT_TIME = 2 - private val COL_RUNTIME = 3 - private val COL_NPROC = 4 - private val COL_REQ_NPROC = 5 - private val COL_DEPS = 6 + private val colWorkflowID = 0 + private val colJobID = 1 + private val colSubmitTime = 2 + private val colRuntime = 3 + private val colNproc = 4 + private val colReqNproc = 5 + private val colDeps = 6 - private val TYPE_DEPS = TableColumnType.Set(TableColumnType.String) + private val typeDeps = TableColumnType.Set(TableColumnType.String) companion object { /** * The [CsvSchema] that is used to parse the trace. */ - private val schema = CsvSchema.builder() - .addColumn("WorkflowID", CsvSchema.ColumnType.NUMBER) - .addColumn("JobID", CsvSchema.ColumnType.NUMBER) - .addColumn("SubmitTime", CsvSchema.ColumnType.NUMBER) - .addColumn("RunTime", CsvSchema.ColumnType.NUMBER) - .addColumn("NProcs", CsvSchema.ColumnType.NUMBER) - .addColumn("ReqNProcs", CsvSchema.ColumnType.NUMBER) - .addColumn("Dependencies", CsvSchema.ColumnType.STRING) - .setAllowComments(true) - .setUseHeader(true) - .setColumnSeparator(',') - .build() + private val schema = + CsvSchema.builder() + .addColumn("WorkflowID", CsvSchema.ColumnType.NUMBER) + .addColumn("JobID", CsvSchema.ColumnType.NUMBER) + .addColumn("SubmitTime", CsvSchema.ColumnType.NUMBER) + .addColumn("RunTime", CsvSchema.ColumnType.NUMBER) + .addColumn("NProcs", CsvSchema.ColumnType.NUMBER) + .addColumn("ReqNProcs", CsvSchema.ColumnType.NUMBER) + .addColumn("Dependencies", CsvSchema.ColumnType.STRING) + .setAllowComments(true) + .setUseHeader(true) + .setColumnSeparator(',') + .build() } } diff --git a/opendc-trace/opendc-trace-gwf/src/main/kotlin/org/opendc/trace/gwf/GwfTraceFormat.kt b/opendc-trace/opendc-trace-gwf/src/main/kotlin/org/opendc/trace/gwf/GwfTraceFormat.kt index d2ded0ee1..097c55936 100644 --- a/opendc-trace/opendc-trace-gwf/src/main/kotlin/org/opendc/trace/gwf/GwfTraceFormat.kt +++ b/opendc-trace/opendc-trace-gwf/src/main/kotlin/org/opendc/trace/gwf/GwfTraceFormat.kt @@ -52,9 +52,10 @@ public class GwfTraceFormat : TraceFormat { /** * The [CsvFactory] used to create the parser. */ - private val factory = CsvFactory() - .enable(CsvParser.Feature.ALLOW_COMMENTS) - .enable(CsvParser.Feature.TRIM_SPACES) + private val factory = + CsvFactory() + .enable(CsvParser.Feature.ALLOW_COMMENTS) + .enable(CsvParser.Feature.TRIM_SPACES) override fun create(path: Path) { throw UnsupportedOperationException("Writing not supported for this format") @@ -62,31 +63,42 @@ public class GwfTraceFormat : TraceFormat { override fun getTables(path: Path): List = listOf(TABLE_TASKS) - override fun getDetails(path: Path, table: String): TableDetails { + override fun getDetails( + path: Path, + table: String, + ): TableDetails { return when (table) { - TABLE_TASKS -> TableDetails( - listOf( - TableColumn(TASK_WORKFLOW_ID, TableColumnType.String), - TableColumn(TASK_ID, TableColumnType.String), - TableColumn(TASK_SUBMIT_TIME, TableColumnType.Instant), - TableColumn(TASK_RUNTIME, TableColumnType.Duration), - TableColumn(TASK_REQ_NCPUS, TableColumnType.Int), - TableColumn(TASK_ALLOC_NCPUS, TableColumnType.Int), - TableColumn(TASK_PARENTS, TableColumnType.Set(TableColumnType.String)) + TABLE_TASKS -> + TableDetails( + listOf( + TableColumn(TASK_WORKFLOW_ID, TableColumnType.String), + TableColumn(TASK_ID, TableColumnType.String), + TableColumn(TASK_SUBMIT_TIME, TableColumnType.Instant), + TableColumn(TASK_RUNTIME, TableColumnType.Duration), + TableColumn(TASK_REQ_NCPUS, TableColumnType.Int), + TableColumn(TASK_ALLOC_NCPUS, TableColumnType.Int), + TableColumn(TASK_PARENTS, TableColumnType.Set(TableColumnType.String)), + ), ) - ) else -> throw IllegalArgumentException("Table $table not supported") } } - override fun newReader(path: Path, table: String, projection: List?): TableReader { + override fun newReader( + path: Path, + table: String, + projection: List?, + ): TableReader { return when (table) { TABLE_TASKS -> GwfTaskTableReader(factory.createParser(path.toFile())) else -> throw IllegalArgumentException("Table $table not supported") } } - override fun newWriter(path: Path, table: String): TableWriter { + override fun newWriter( + path: Path, + table: String, + ): TableWriter { throw UnsupportedOperationException("Writing not supported for this format") } } diff --git a/opendc-trace/opendc-trace-gwf/src/test/kotlin/org/opendc/trace/gwf/GwfTraceFormatTest.kt b/opendc-trace/opendc-trace-gwf/src/test/kotlin/org/opendc/trace/gwf/GwfTraceFormatTest.kt index c75e86df3..9c97547ac 100644 --- a/opendc-trace/opendc-trace-gwf/src/test/kotlin/org/opendc/trace/gwf/GwfTraceFormatTest.kt +++ b/opendc-trace/opendc-trace-gwf/src/test/kotlin/org/opendc/trace/gwf/GwfTraceFormatTest.kt @@ -82,7 +82,7 @@ internal class GwfTraceFormatTest { { assertEquals("1", reader.getString(TASK_ID)) }, { assertEquals(Instant.ofEpochSecond(16), reader.getInstant(TASK_SUBMIT_TIME)) }, { assertEquals(Duration.ofSeconds(11), reader.getDuration(TASK_RUNTIME)) }, - { assertEquals(emptySet(), reader.getSet(TASK_PARENTS, String::class.java)) } + { assertEquals(emptySet(), reader.getSet(TASK_PARENTS, String::class.java)) }, ) } @@ -101,7 +101,7 @@ internal class GwfTraceFormatTest { { assertEquals("7", reader.getString(TASK_ID)) }, { assertEquals(Instant.ofEpochSecond(87), reader.getInstant(TASK_SUBMIT_TIME)) }, { assertEquals(Duration.ofSeconds(11), reader.getDuration(TASK_RUNTIME)) }, - { assertEquals(setOf("4", "5", "6"), reader.getSet(TASK_PARENTS, String::class.java)) } + { assertEquals(setOf("4", "5", "6"), reader.getSet(TASK_PARENTS, String::class.java)) }, ) } diff --git a/opendc-trace/opendc-trace-opendc/build.gradle.kts b/opendc-trace/opendc-trace-opendc/build.gradle.kts index 18967136e..d9ed15f80 100644 --- a/opendc-trace/opendc-trace-opendc/build.gradle.kts +++ b/opendc-trace/opendc-trace-opendc/build.gradle.kts @@ -22,7 +22,7 @@ description = "Support for OpenDC-specific trace formats" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` `benchmark-conventions` diff --git a/opendc-trace/opendc-trace-opendc/src/jmh/kotlin/org/opendc/trace/opendc/OdcVmTraceBenchmarks.kt b/opendc-trace/opendc-trace-opendc/src/jmh/kotlin/org/opendc/trace/opendc/OdcVmTraceBenchmarks.kt index e504cf2fb..e179e2611 100644 --- a/opendc-trace/opendc-trace-opendc/src/jmh/kotlin/org/opendc/trace/opendc/OdcVmTraceBenchmarks.kt +++ b/opendc-trace/opendc-trace-opendc/src/jmh/kotlin/org/opendc/trace/opendc/OdcVmTraceBenchmarks.kt @@ -23,10 +23,10 @@ package org.opendc.trace.opendc import org.opendc.trace.conv.INTERFERENCE_GROUP_SCORE -import org.opendc.trace.conv.RESOURCE_ID import org.opendc.trace.conv.TABLE_INTERFERENCE_GROUPS import org.opendc.trace.conv.TABLE_RESOURCES import org.opendc.trace.conv.TABLE_RESOURCE_STATES +import org.opendc.trace.conv.resourceID import org.opendc.trace.spi.TraceFormat import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Fork @@ -60,7 +60,7 @@ class OdcVmTraceBenchmarks { fun benchmarkResourcesReader(bh: Blackhole) { val reader = format.newReader(path, TABLE_RESOURCES, null) try { - val idColumn = reader.resolve(RESOURCE_ID) + val idColumn = reader.resolve(resourceID) while (reader.nextRow()) { bh.consume(reader.getString(idColumn)) } @@ -73,7 +73,7 @@ class OdcVmTraceBenchmarks { fun benchmarkResourceStatesReader(bh: Blackhole) { val reader = format.newReader(path, TABLE_RESOURCE_STATES, null) try { - val idColumn = reader.resolve(RESOURCE_ID) + val idColumn = reader.resolve(resourceID) while (reader.nextRow()) { bh.consume(reader.getString(idColumn)) } diff --git a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmInterferenceJsonTableReader.kt b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmInterferenceJsonTableReader.kt index 3e1fca068..7bf48f1ab 100644 --- a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmInterferenceJsonTableReader.kt +++ b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmInterferenceJsonTableReader.kt @@ -65,24 +65,24 @@ internal class OdcVmInterferenceJsonTableReader(private val parser: JsonParser) } } - private val COL_MEMBERS = 0 - private val COL_TARGET = 1 - private val COL_SCORE = 2 + private val colMembers = 0 + private val colTarget = 1 + private val colScore = 2 - private val TYPE_MEMBERS = TableColumnType.Set(TableColumnType.String) + private val typeMembers = TableColumnType.Set(TableColumnType.String) override fun resolve(name: String): Int { return when (name) { - INTERFERENCE_GROUP_MEMBERS -> COL_MEMBERS - INTERFERENCE_GROUP_TARGET -> COL_TARGET - INTERFERENCE_GROUP_SCORE -> COL_SCORE + INTERFERENCE_GROUP_MEMBERS -> colMembers + INTERFERENCE_GROUP_TARGET -> colTarget + INTERFERENCE_GROUP_SCORE -> colScore else -> -1 } } override fun isNull(index: Int): Boolean { return when (index) { - COL_MEMBERS, COL_TARGET, COL_SCORE -> false + colMembers, colTarget, colScore -> false else -> throw IllegalArgumentException("Invalid column index $index") } } @@ -106,8 +106,8 @@ internal class OdcVmInterferenceJsonTableReader(private val parser: JsonParser) override fun getDouble(index: Int): Double { checkActive() return when (index) { - COL_TARGET -> targetLoad - COL_SCORE -> score + colTarget -> targetLoad + colScore -> score else -> throw IllegalArgumentException("Invalid column $index") } } @@ -128,19 +128,29 @@ internal class OdcVmInterferenceJsonTableReader(private val parser: JsonParser) throw IllegalArgumentException("Invalid column $index") } - override fun getList(index: Int, elementType: Class): List? { + override fun getList( + index: Int, + elementType: Class, + ): List? { throw IllegalArgumentException("Invalid column $index") } - override fun getSet(index: Int, elementType: Class): Set? { + override fun getSet( + index: Int, + elementType: Class, + ): Set? { checkActive() return when (index) { - COL_MEMBERS -> TYPE_MEMBERS.convertTo(members, elementType) + colMembers -> typeMembers.convertTo(members, elementType) else -> throw IllegalArgumentException("Invalid column $index") } } - override fun getMap(index: Int, keyType: Class, valueType: Class): Map? { + override fun getMap( + index: Int, + keyType: Class, + valueType: Class, + ): Map? { throw IllegalArgumentException("Invalid column $index") } @@ -196,7 +206,10 @@ internal class OdcVmInterferenceJsonTableReader(private val parser: JsonParser) /** * Parse the members of a group. */ - private fun parseGroupMembers(parser: JsonParser, members: MutableSet) { + private fun parseGroupMembers( + parser: JsonParser, + members: MutableSet, + ) { if (!parser.isExpectedStartArrayToken) { throw JsonParseException(parser, "Expected array for group members, but got ${parser.currentToken()}") } diff --git a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmInterferenceJsonTableWriter.kt b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmInterferenceJsonTableWriter.kt index c6905c5bc..93f5a9765 100644 --- a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmInterferenceJsonTableWriter.kt +++ b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmInterferenceJsonTableWriter.kt @@ -70,70 +70,106 @@ internal class OdcVmInterferenceJsonTableWriter(private val generator: JsonGener override fun resolve(name: String): Int { return when (name) { - INTERFERENCE_GROUP_MEMBERS -> COL_MEMBERS - INTERFERENCE_GROUP_TARGET -> COL_TARGET - INTERFERENCE_GROUP_SCORE -> COL_SCORE + INTERFERENCE_GROUP_MEMBERS -> colMembers + INTERFERENCE_GROUP_TARGET -> colTarget + INTERFERENCE_GROUP_SCORE -> colScore else -> -1 } } - override fun setBoolean(index: Int, value: Boolean) { + override fun setBoolean( + index: Int, + value: Boolean, + ) { throw IllegalArgumentException("Invalid column $index") } - override fun setInt(index: Int, value: Int) { + override fun setInt( + index: Int, + value: Int, + ) { throw IllegalArgumentException("Invalid column $index") } - override fun setLong(index: Int, value: Long) { + override fun setLong( + index: Int, + value: Long, + ) { throw IllegalArgumentException("Invalid column $index") } - override fun setFloat(index: Int, value: Float) { + override fun setFloat( + index: Int, + value: Float, + ) { throw IllegalArgumentException("Invalid column $index") } - override fun setDouble(index: Int, value: Double) { + override fun setDouble( + index: Int, + value: Double, + ) { check(isRowActive) { "No active row" } when (index) { - COL_TARGET -> targetLoad = (value as Number).toDouble() - COL_SCORE -> score = (value as Number).toDouble() + colTarget -> targetLoad = (value as Number).toDouble() + colScore -> score = (value as Number).toDouble() else -> throw IllegalArgumentException("Invalid column $index") } } - override fun setString(index: Int, value: String) { + override fun setString( + index: Int, + value: String, + ) { throw IllegalArgumentException("Invalid column $index") } - override fun setUUID(index: Int, value: UUID) { + override fun setUUID( + index: Int, + value: UUID, + ) { throw IllegalArgumentException("Invalid column $index") } - override fun setInstant(index: Int, value: Instant) { + override fun setInstant( + index: Int, + value: Instant, + ) { throw IllegalArgumentException("Invalid column $index") } - override fun setDuration(index: Int, value: Duration) { + override fun setDuration( + index: Int, + value: Duration, + ) { throw IllegalArgumentException("Invalid column $index") } - override fun setList(index: Int, value: List) { + override fun setList( + index: Int, + value: List, + ) { throw IllegalArgumentException("Invalid column $index") } - override fun setSet(index: Int, value: Set) { + override fun setSet( + index: Int, + value: Set, + ) { check(isRowActive) { "No active row" } @Suppress("UNCHECKED_CAST") when (index) { - COL_MEMBERS -> members = value as Set + colMembers -> members = value as Set else -> throw IllegalArgumentException("Invalid column index $index") } } - override fun setMap(index: Int, value: Map) { + override fun setMap( + index: Int, + value: Map, + ) { throw IllegalArgumentException("Invalid column $index") } @@ -146,9 +182,9 @@ internal class OdcVmInterferenceJsonTableWriter(private val generator: JsonGener generator.close() } - private val COL_MEMBERS = 0 - private val COL_TARGET = 1 - private val COL_SCORE = 2 + private val colMembers = 0 + private val colTarget = 1 + private val colScore = 2 private var members = emptySet() private var targetLoad = Double.POSITIVE_INFINITY diff --git a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmResourceStateTableReader.kt b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmResourceStateTableReader.kt index ff9a98d75..8e54f2b0c 100644 --- a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmResourceStateTableReader.kt +++ b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmResourceStateTableReader.kt @@ -23,11 +23,11 @@ package org.opendc.trace.opendc import org.opendc.trace.TableReader -import org.opendc.trace.conv.RESOURCE_CPU_COUNT -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE -import org.opendc.trace.conv.RESOURCE_STATE_DURATION -import org.opendc.trace.conv.RESOURCE_STATE_TIMESTAMP +import org.opendc.trace.conv.resourceCpuCount +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceStateCpuUsage +import org.opendc.trace.conv.resourceStateDuration +import org.opendc.trace.conv.resourceStateTimestamp import org.opendc.trace.opendc.parquet.ResourceState import org.opendc.trace.util.parquet.LocalParquetReader import java.time.Duration @@ -55,25 +55,25 @@ internal class OdcVmResourceStateTableReader(private val reader: LocalParquetRea } } - private val COL_ID = 0 - private val COL_TIMESTAMP = 1 - private val COL_DURATION = 2 - private val COL_CPU_COUNT = 3 - private val COL_CPU_USAGE = 4 + private val colID = 0 + private val colTimestamp = 1 + private val colDuration = 2 + private val colCpuCount = 3 + private val colCpuUsage = 4 override fun resolve(name: String): Int { return when (name) { - RESOURCE_ID -> COL_ID - RESOURCE_STATE_TIMESTAMP -> COL_TIMESTAMP - RESOURCE_STATE_DURATION -> COL_DURATION - RESOURCE_CPU_COUNT -> COL_CPU_COUNT - RESOURCE_STATE_CPU_USAGE -> COL_CPU_USAGE + resourceID -> colID + resourceStateTimestamp -> colTimestamp + resourceStateDuration -> colDuration + resourceCpuCount -> colCpuCount + resourceStateCpuUsage -> colCpuUsage else -> -1 } } override fun isNull(index: Int): Boolean { - require(index in 0..COL_CPU_USAGE) { "Invalid column index" } + require(index in 0..colCpuUsage) { "Invalid column index" } return false } @@ -84,7 +84,7 @@ internal class OdcVmResourceStateTableReader(private val reader: LocalParquetRea override fun getInt(index: Int): Int { val record = checkNotNull(record) { "Reader in invalid state" } return when (index) { - COL_CPU_COUNT -> record.cpuCount + colCpuCount -> record.cpuCount else -> throw IllegalArgumentException("Invalid column or type [index $index]") } } @@ -100,7 +100,7 @@ internal class OdcVmResourceStateTableReader(private val reader: LocalParquetRea override fun getDouble(index: Int): Double { val record = checkNotNull(record) { "Reader in invalid state" } return when (index) { - COL_CPU_USAGE -> record.cpuUsage + colCpuUsage -> record.cpuUsage else -> throw IllegalArgumentException("Invalid column or type [index $index]") } } @@ -109,7 +109,7 @@ internal class OdcVmResourceStateTableReader(private val reader: LocalParquetRea val record = checkNotNull(record) { "Reader in invalid state" } return when (index) { - COL_ID -> record.id + colID -> record.id else -> throw IllegalArgumentException("Invalid column index $index") } } @@ -122,7 +122,7 @@ internal class OdcVmResourceStateTableReader(private val reader: LocalParquetRea val record = checkNotNull(record) { "Reader in invalid state" } return when (index) { - COL_TIMESTAMP -> record.timestamp + colTimestamp -> record.timestamp else -> throw IllegalArgumentException("Invalid column index $index") } } @@ -131,20 +131,30 @@ internal class OdcVmResourceStateTableReader(private val reader: LocalParquetRea val record = checkNotNull(record) { "Reader in invalid state" } return when (index) { - COL_DURATION -> record.duration + colDuration -> record.duration else -> throw IllegalArgumentException("Invalid column index $index") } } - override fun getList(index: Int, elementType: Class): List? { + override fun getList( + index: Int, + elementType: Class, + ): List? { throw IllegalArgumentException("Invalid column or type [index $index]") } - override fun getSet(index: Int, elementType: Class): Set? { + override fun getSet( + index: Int, + elementType: Class, + ): Set? { throw IllegalArgumentException("Invalid column or type [index $index]") } - override fun getMap(index: Int, keyType: Class, valueType: Class): Map? { + override fun getMap( + index: Int, + keyType: Class, + valueType: Class, + ): Map? { throw IllegalArgumentException("Invalid column or type [index $index]") } diff --git a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmResourceStateTableWriter.kt b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmResourceStateTableWriter.kt index cf0a401b9..01cd13c84 100644 --- a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmResourceStateTableWriter.kt +++ b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmResourceStateTableWriter.kt @@ -24,11 +24,11 @@ package org.opendc.trace.opendc import org.apache.parquet.hadoop.ParquetWriter import org.opendc.trace.TableWriter -import org.opendc.trace.conv.RESOURCE_CPU_COUNT -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE -import org.opendc.trace.conv.RESOURCE_STATE_DURATION -import org.opendc.trace.conv.RESOURCE_STATE_TIMESTAMP +import org.opendc.trace.conv.resourceCpuCount +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceStateCpuUsage +import org.opendc.trace.conv.resourceStateDuration +import org.opendc.trace.conv.resourceStateTimestamp import org.opendc.trace.opendc.parquet.ResourceState import java.time.Duration import java.time.Instant @@ -41,113 +41,149 @@ internal class OdcVmResourceStateTableWriter(private val writer: ParquetWriter= lastTimestamp) { "Records need to be ordered by (id, timestamp)" } + check(lastId != localID || localTimestamp >= lastTimestamp) { "Records need to be ordered by (id, timestamp)" } - writer.write(ResourceState(_id, _timestamp, _duration, _cpuCount, _cpuUsage)) + writer.write(ResourceState(localID, localTimestamp, localDuration, localCpuCount, localCpuUsage)) - lastId = _id - lastTimestamp = _timestamp + lastId = localID + lastTimestamp = localTimestamp } override fun resolve(name: String): Int { return when (name) { - RESOURCE_ID -> COL_ID - RESOURCE_STATE_TIMESTAMP -> COL_TIMESTAMP - RESOURCE_STATE_DURATION -> COL_DURATION - RESOURCE_CPU_COUNT -> COL_CPU_COUNT - RESOURCE_STATE_CPU_USAGE -> COL_CPU_USAGE + resourceID -> colID + resourceStateTimestamp -> colTimestamp + resourceStateDuration -> colDuration + resourceCpuCount -> colCpuCount + resourceStateCpuUsage -> colCpuUsage else -> -1 } } - override fun setBoolean(index: Int, value: Boolean) { + override fun setBoolean( + index: Int, + value: Boolean, + ) { throw IllegalArgumentException("Invalid column or type [index $index]") } - override fun setInt(index: Int, value: Int) { - check(_isActive) { "No active row" } + override fun setInt( + index: Int, + value: Int, + ) { + check(localIsActive) { "No active row" } when (index) { - COL_CPU_COUNT -> _cpuCount = value + colCpuCount -> localCpuCount = value else -> throw IllegalArgumentException("Invalid column or type [index $index]") } } - override fun setLong(index: Int, value: Long) { + override fun setLong( + index: Int, + value: Long, + ) { throw IllegalArgumentException("Invalid column or type [index $index]") } - override fun setFloat(index: Int, value: Float) { + override fun setFloat( + index: Int, + value: Float, + ) { throw IllegalArgumentException("Invalid column or type [index $index]") } - override fun setDouble(index: Int, value: Double) { - check(_isActive) { "No active row" } + override fun setDouble( + index: Int, + value: Double, + ) { + check(localIsActive) { "No active row" } when (index) { - COL_CPU_USAGE -> _cpuUsage = value + colCpuUsage -> localCpuUsage = value else -> throw IllegalArgumentException("Invalid column or type [index $index]") } } - override fun setString(index: Int, value: String) { - check(_isActive) { "No active row" } + override fun setString( + index: Int, + value: String, + ) { + check(localIsActive) { "No active row" } when (index) { - COL_ID -> _id = value + colID -> localID = value else -> throw IllegalArgumentException("Invalid column or type [index $index]") } } - override fun setUUID(index: Int, value: UUID) { + override fun setUUID( + index: Int, + value: UUID, + ) { throw IllegalArgumentException("Invalid column or type [index $index]") } - override fun setInstant(index: Int, value: Instant) { - check(_isActive) { "No active row" } + override fun setInstant( + index: Int, + value: Instant, + ) { + check(localIsActive) { "No active row" } when (index) { - COL_TIMESTAMP -> _timestamp = value + colTimestamp -> localTimestamp = value else -> throw IllegalArgumentException("Invalid column or type [index $index]") } } - override fun setDuration(index: Int, value: Duration) { - check(_isActive) { "No active row" } + override fun setDuration( + index: Int, + value: Duration, + ) { + check(localIsActive) { "No active row" } when (index) { - COL_DURATION -> _duration = value + colDuration -> localDuration = value else -> throw IllegalArgumentException("Invalid column or type [index $index]") } } - override fun setList(index: Int, value: List) { + override fun setList( + index: Int, + value: List, + ) { throw IllegalArgumentException("Invalid column or type [index $index]") } - override fun setSet(index: Int, value: Set) { + override fun setSet( + index: Int, + value: Set, + ) { throw IllegalArgumentException("Invalid column or type [index $index]") } - override fun setMap(index: Int, value: Map) { + override fun setMap( + index: Int, + value: Map, + ) { throw IllegalArgumentException("Invalid column or type [index $index]") } @@ -165,9 +201,9 @@ internal class OdcVmResourceStateTableWriter(private val writer: ParquetWriter COL_ID - RESOURCE_START_TIME -> COL_START_TIME - RESOURCE_STOP_TIME -> COL_STOP_TIME - RESOURCE_CPU_COUNT -> COL_CPU_COUNT - RESOURCE_CPU_CAPACITY -> COL_CPU_CAPACITY - RESOURCE_MEM_CAPACITY -> COL_MEM_CAPACITY + resourceID -> colID + resourceStartTime -> colStartTime + resourceStopTime -> colStopTime + resourceCpuCount -> colCpuCount + resourceCpuCapacity -> colCpuCapacity + resourceMemCapacity -> colMemCapacity else -> -1 } } override fun isNull(index: Int): Boolean { - require(index in 0..COL_MEM_CAPACITY) { "Invalid column index" } + require(index in 0..colMemCapacity) { "Invalid column index" } return false } @@ -88,7 +88,7 @@ internal class OdcVmResourceTableReader(private val reader: LocalParquetReader record.cpuCount + colCpuCount -> record.cpuCount else -> throw IllegalArgumentException("Invalid column") } } @@ -105,8 +105,8 @@ internal class OdcVmResourceTableReader(private val reader: LocalParquetReader record.cpuCapacity - COL_MEM_CAPACITY -> record.memCapacity + colCpuCapacity -> record.cpuCapacity + colMemCapacity -> record.memCapacity else -> throw IllegalArgumentException("Invalid column") } } @@ -115,7 +115,7 @@ internal class OdcVmResourceTableReader(private val reader: LocalParquetReader record.id + colID -> record.id else -> throw IllegalArgumentException("Invalid column") } } @@ -128,8 +128,8 @@ internal class OdcVmResourceTableReader(private val reader: LocalParquetReader record.startTime - COL_STOP_TIME -> record.stopTime + colStartTime -> record.startTime + colStopTime -> record.stopTime else -> throw IllegalArgumentException("Invalid column") } } @@ -138,15 +138,25 @@ internal class OdcVmResourceTableReader(private val reader: LocalParquetReader getList(index: Int, elementType: Class): List? { + override fun getList( + index: Int, + elementType: Class, + ): List? { throw IllegalArgumentException("Invalid column") } - override fun getSet(index: Int, elementType: Class): Set? { + override fun getSet( + index: Int, + elementType: Class, + ): Set? { throw IllegalArgumentException("Invalid column") } - override fun getMap(index: Int, keyType: Class, valueType: Class): Map? { + override fun getMap( + index: Int, + keyType: Class, + valueType: Class, + ): Map? { throw IllegalArgumentException("Invalid column") } diff --git a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmResourceTableWriter.kt b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmResourceTableWriter.kt index 73a038910..5bbc2f3fc 100644 --- a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmResourceTableWriter.kt +++ b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/OdcVmResourceTableWriter.kt @@ -24,12 +24,12 @@ package org.opendc.trace.opendc import org.apache.parquet.hadoop.ParquetWriter import org.opendc.trace.TableWriter -import org.opendc.trace.conv.RESOURCE_CPU_CAPACITY -import org.opendc.trace.conv.RESOURCE_CPU_COUNT -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_MEM_CAPACITY -import org.opendc.trace.conv.RESOURCE_START_TIME -import org.opendc.trace.conv.RESOURCE_STOP_TIME +import org.opendc.trace.conv.resourceCpuCapacity +import org.opendc.trace.conv.resourceCpuCount +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceMemCapacity +import org.opendc.trace.conv.resourceStartTime +import org.opendc.trace.conv.resourceStopTime import org.opendc.trace.opendc.parquet.Resource import java.time.Duration import java.time.Instant @@ -42,105 +42,141 @@ internal class OdcVmResourceTableWriter(private val writer: ParquetWriter COL_ID - RESOURCE_START_TIME -> COL_START_TIME - RESOURCE_STOP_TIME -> COL_STOP_TIME - RESOURCE_CPU_COUNT -> COL_CPU_COUNT - RESOURCE_CPU_CAPACITY -> COL_CPU_CAPACITY - RESOURCE_MEM_CAPACITY -> COL_MEM_CAPACITY + resourceID -> colID + resourceStartTime -> colStartTime + resourceStopTime -> colStopTime + resourceCpuCount -> colCpuCount + resourceCpuCapacity -> colCpuCapacity + resourceMemCapacity -> colMemCapacity else -> -1 } } - override fun setBoolean(index: Int, value: Boolean) { + override fun setBoolean( + index: Int, + value: Boolean, + ) { throw IllegalArgumentException("Invalid column or type [index $index]") } - override fun setInt(index: Int, value: Int) { - check(_isActive) { "No active row" } + override fun setInt( + index: Int, + value: Int, + ) { + check(localIsActive) { "No active row" } when (index) { - COL_CPU_COUNT -> _cpuCount = value + colCpuCount -> localCpuCount = value else -> throw IllegalArgumentException("Invalid column or type [index $index]") } } - override fun setLong(index: Int, value: Long) { + override fun setLong( + index: Int, + value: Long, + ) { throw IllegalArgumentException("Invalid column or type [index $index]") } - override fun setFloat(index: Int, value: Float) { + override fun setFloat( + index: Int, + value: Float, + ) { throw IllegalArgumentException("Invalid column or type [index $index]") } - override fun setDouble(index: Int, value: Double) { - check(_isActive) { "No active row" } + override fun setDouble( + index: Int, + value: Double, + ) { + check(localIsActive) { "No active row" } when (index) { - COL_CPU_CAPACITY -> _cpuCapacity = value - COL_MEM_CAPACITY -> _memCapacity = value + colCpuCapacity -> localCpuCapacity = value + colMemCapacity -> localMemCapacity = value else -> throw IllegalArgumentException("Invalid column or type [index $index]") } } - override fun setString(index: Int, value: String) { - check(_isActive) { "No active row" } + override fun setString( + index: Int, + value: String, + ) { + check(localIsActive) { "No active row" } when (index) { - COL_ID -> _id = value + colID -> localId = value else -> throw IllegalArgumentException("Invalid column index $index") } } - override fun setUUID(index: Int, value: UUID) { + override fun setUUID( + index: Int, + value: UUID, + ) { throw IllegalArgumentException("Invalid column or type [index $index]") } - override fun setInstant(index: Int, value: Instant) { - check(_isActive) { "No active row" } + override fun setInstant( + index: Int, + value: Instant, + ) { + check(localIsActive) { "No active row" } when (index) { - COL_START_TIME -> _startTime = value - COL_STOP_TIME -> _stopTime = value + colStartTime -> localStartTime = value + colStopTime -> localStopTime = value else -> throw IllegalArgumentException("Invalid column index $index") } } - override fun setDuration(index: Int, value: Duration) { + override fun setDuration( + index: Int, + value: Duration, + ) { throw IllegalArgumentException("Invalid column or type [index $index]") } - override fun setList(index: Int, value: List) { + override fun setList( + index: Int, + value: List, + ) { throw IllegalArgumentException("Invalid column or type [index $index]") } - override fun setSet(index: Int, value: Set) { + override fun setSet( + index: Int, + value: Set, + ) { throw IllegalArgumentException("Invalid column or type [index $index]") } - override fun setMap(index: Int, value: Map) { + override fun setMap( + index: Int, + value: Map, + ) { throw IllegalArgumentException("Invalid column or type [index $index]") } @@ -152,10 +188,10 @@ internal class OdcVmResourceTableWriter(private val writer: ParquetWriter = listOf(TABLE_RESOURCES, TABLE_RESOURCE_STATES, TABLE_INTERFERENCE_GROUPS) - override fun getDetails(path: Path, table: String): TableDetails { + override fun getDetails( + path: Path, + table: String, + ): TableDetails { return when (table) { - TABLE_RESOURCES -> TableDetails( - listOf( - TableColumn(RESOURCE_ID, TableColumnType.String), - TableColumn(RESOURCE_START_TIME, TableColumnType.Instant), - TableColumn(RESOURCE_STOP_TIME, TableColumnType.Instant), - TableColumn(RESOURCE_CPU_COUNT, TableColumnType.Int), - TableColumn(RESOURCE_CPU_CAPACITY, TableColumnType.Double), - TableColumn(RESOURCE_MEM_CAPACITY, TableColumnType.Double) + TABLE_RESOURCES -> + TableDetails( + listOf( + TableColumn(resourceID, TableColumnType.String), + TableColumn(resourceStartTime, TableColumnType.Instant), + TableColumn(resourceStopTime, TableColumnType.Instant), + TableColumn(resourceCpuCount, TableColumnType.Int), + TableColumn(resourceCpuCapacity, TableColumnType.Double), + TableColumn(resourceMemCapacity, TableColumnType.Double), + ), ) - ) - TABLE_RESOURCE_STATES -> TableDetails( - listOf( - TableColumn(RESOURCE_ID, TableColumnType.String), - TableColumn(RESOURCE_STATE_TIMESTAMP, TableColumnType.Instant), - TableColumn(RESOURCE_STATE_DURATION, TableColumnType.Duration), - TableColumn(RESOURCE_CPU_COUNT, TableColumnType.Int), - TableColumn(RESOURCE_STATE_CPU_USAGE, TableColumnType.Double) + TABLE_RESOURCE_STATES -> + TableDetails( + listOf( + TableColumn(resourceID, TableColumnType.String), + TableColumn(resourceStateTimestamp, TableColumnType.Instant), + TableColumn(resourceStateDuration, TableColumnType.Duration), + TableColumn(resourceCpuCount, TableColumnType.Int), + TableColumn(resourceStateCpuUsage, TableColumnType.Double), + ), ) - ) - TABLE_INTERFERENCE_GROUPS -> TableDetails( - listOf( - TableColumn(INTERFERENCE_GROUP_MEMBERS, TableColumnType.Set(TableColumnType.String)), - TableColumn(INTERFERENCE_GROUP_TARGET, TableColumnType.Double), - TableColumn(INTERFERENCE_GROUP_SCORE, TableColumnType.Double) + TABLE_INTERFERENCE_GROUPS -> + TableDetails( + listOf( + TableColumn(INTERFERENCE_GROUP_MEMBERS, TableColumnType.Set(TableColumnType.String)), + TableColumn(INTERFERENCE_GROUP_TARGET, TableColumnType.Double), + TableColumn(INTERFERENCE_GROUP_SCORE, TableColumnType.Double), + ), ) - ) else -> throw IllegalArgumentException("Table $table not supported") } } - override fun newReader(path: Path, table: String, projection: List?): TableReader { + override fun newReader( + path: Path, + table: String, + projection: List?, + ): TableReader { return when (table) { TABLE_RESOURCES -> { val reader = LocalParquetReader(path.resolve("meta.parquet"), ResourceReadSupport(projection)) @@ -130,11 +140,12 @@ public class OdcVmTraceFormat : TraceFormat { } TABLE_INTERFERENCE_GROUPS -> { val modelPath = path.resolve("interference-model.json") - val parser = if (modelPath.exists()) { - jsonFactory.createParser(modelPath.toFile()) - } else { - jsonFactory.createParser("[]") // If model does not exist, return empty model - } + val parser = + if (modelPath.exists()) { + jsonFactory.createParser(modelPath.toFile()) + } else { + jsonFactory.createParser("[]") // If model does not exist, return empty model + } OdcVmInterferenceJsonTableReader(parser) } @@ -142,26 +153,31 @@ public class OdcVmTraceFormat : TraceFormat { } } - override fun newWriter(path: Path, table: String): TableWriter { + override fun newWriter( + path: Path, + table: String, + ): TableWriter { return when (table) { TABLE_RESOURCES -> { - val writer = LocalParquetWriter.builder(path.resolve("meta.parquet"), ResourceWriteSupport()) - .withCompressionCodec(CompressionCodecName.ZSTD) - .withPageWriteChecksumEnabled(true) - .withWriterVersion(ParquetProperties.WriterVersion.PARQUET_2_0) - .withWriteMode(ParquetFileWriter.Mode.OVERWRITE) - .build() + val writer = + LocalParquetWriter.builder(path.resolve("meta.parquet"), ResourceWriteSupport()) + .withCompressionCodec(CompressionCodecName.ZSTD) + .withPageWriteChecksumEnabled(true) + .withWriterVersion(ParquetProperties.WriterVersion.PARQUET_2_0) + .withWriteMode(ParquetFileWriter.Mode.OVERWRITE) + .build() OdcVmResourceTableWriter(writer) } TABLE_RESOURCE_STATES -> { - val writer = LocalParquetWriter.builder(path.resolve("trace.parquet"), ResourceStateWriteSupport()) - .withCompressionCodec(CompressionCodecName.ZSTD) - .withDictionaryEncoding("id", true) - .withBloomFilterEnabled("id", true) - .withPageWriteChecksumEnabled(true) - .withWriterVersion(ParquetProperties.WriterVersion.PARQUET_2_0) - .withWriteMode(ParquetFileWriter.Mode.OVERWRITE) - .build() + val writer = + LocalParquetWriter.builder(path.resolve("trace.parquet"), ResourceStateWriteSupport()) + .withCompressionCodec(CompressionCodecName.ZSTD) + .withDictionaryEncoding("id", true) + .withBloomFilterEnabled("id", true) + .withPageWriteChecksumEnabled(true) + .withWriterVersion(ParquetProperties.WriterVersion.PARQUET_2_0) + .withWriteMode(ParquetFileWriter.Mode.OVERWRITE) + .build() OdcVmResourceStateTableWriter(writer) } TABLE_INTERFERENCE_GROUPS -> { diff --git a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/Resource.kt b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/Resource.kt index c6db45b5e..13eefe722 100644 --- a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/Resource.kt +++ b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/Resource.kt @@ -33,5 +33,5 @@ internal data class Resource( val stopTime: Instant, val cpuCount: Int, val cpuCapacity: Double, - val memCapacity: Double + val memCapacity: Double, ) diff --git a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceReadSupport.kt b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceReadSupport.kt index 52911d5f7..8bada02ee 100644 --- a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceReadSupport.kt +++ b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceReadSupport.kt @@ -31,12 +31,12 @@ import org.apache.parquet.schema.MessageType import org.apache.parquet.schema.PrimitiveType import org.apache.parquet.schema.Types import org.opendc.trace.TableColumn -import org.opendc.trace.conv.RESOURCE_CPU_CAPACITY -import org.opendc.trace.conv.RESOURCE_CPU_COUNT -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_MEM_CAPACITY -import org.opendc.trace.conv.RESOURCE_START_TIME -import org.opendc.trace.conv.RESOURCE_STOP_TIME +import org.opendc.trace.conv.resourceCpuCapacity +import org.opendc.trace.conv.resourceCpuCount +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceMemCapacity +import org.opendc.trace.conv.resourceStartTime +import org.opendc.trace.conv.resourceStopTime /** * A [ReadSupport] instance for [Resource] objects. @@ -45,18 +45,19 @@ internal class ResourceReadSupport(private val projection: List?) : Read /** * Mapping from field names to [TableColumn]s. */ - private val fieldMap = mapOf( - "id" to RESOURCE_ID, - "submissionTime" to RESOURCE_START_TIME, - "start_time" to RESOURCE_START_TIME, - "endTime" to RESOURCE_STOP_TIME, - "stop_time" to RESOURCE_STOP_TIME, - "maxCores" to RESOURCE_CPU_COUNT, - "cpu_count" to RESOURCE_CPU_COUNT, - "cpu_capacity" to RESOURCE_CPU_CAPACITY, - "requiredMemory" to RESOURCE_MEM_CAPACITY, - "mem_capacity" to RESOURCE_MEM_CAPACITY - ) + private val fieldMap = + mapOf( + "id" to resourceID, + "submissionTime" to resourceStartTime, + "start_time" to resourceStartTime, + "endTime" to resourceStopTime, + "stop_time" to resourceStopTime, + "maxCores" to resourceCpuCount, + "cpu_count" to resourceCpuCount, + "cpu_capacity" to resourceCpuCapacity, + "requiredMemory" to resourceMemCapacity, + "mem_capacity" to resourceMemCapacity, + ) override fun init(context: InitContext): ReadContext { val projectedSchema = @@ -84,7 +85,7 @@ internal class ResourceReadSupport(private val projection: List?) : Read configuration: Configuration, keyValueMetaData: Map, fileSchema: MessageType, - readContext: ReadContext + readContext: ReadContext, ): RecordMaterializer = ResourceRecordMaterializer(readContext.requestedSchema) companion object { @@ -92,64 +93,67 @@ internal class ResourceReadSupport(private val projection: List?) : Read * Parquet read schema (version 2.0) for the "resources" table in the trace. */ @JvmStatic - val READ_SCHEMA_V2_0: MessageType = Types.buildMessage() - .addFields( - Types - .required(PrimitiveType.PrimitiveTypeName.BINARY) - .`as`(LogicalTypeAnnotation.stringType()) - .named("id"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("submissionTime"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("endTime"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("maxCores"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("requiredMemory") - ) - .named("resource") + val READ_SCHEMA_V2_0: MessageType = + Types.buildMessage() + .addFields( + Types + .required(PrimitiveType.PrimitiveTypeName.BINARY) + .`as`(LogicalTypeAnnotation.stringType()) + .named("id"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) + .named("submissionTime"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) + .named("endTime"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("maxCores"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("requiredMemory"), + ) + .named("resource") /** * Parquet read schema (version 2.1) for the "resources" table in the trace. */ @JvmStatic - val READ_SCHEMA_V2_1: MessageType = Types.buildMessage() - .addFields( - Types - .required(PrimitiveType.PrimitiveTypeName.BINARY) - .`as`(LogicalTypeAnnotation.stringType()) - .named("id"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("start_time"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("stop_time"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("cpu_count"), - Types - .required(PrimitiveType.PrimitiveTypeName.DOUBLE) - .named("cpu_capacity"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("mem_capacity") - ) - .named("resource") + val READ_SCHEMA_V2_1: MessageType = + Types.buildMessage() + .addFields( + Types + .required(PrimitiveType.PrimitiveTypeName.BINARY) + .`as`(LogicalTypeAnnotation.stringType()) + .named("id"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) + .named("start_time"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) + .named("stop_time"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("cpu_count"), + Types + .required(PrimitiveType.PrimitiveTypeName.DOUBLE) + .named("cpu_capacity"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("mem_capacity"), + ) + .named("resource") /** * Parquet read schema for the "resources" table in the trace. */ @JvmStatic - val READ_SCHEMA: MessageType = READ_SCHEMA_V2_0 - .union(READ_SCHEMA_V2_1) + val READ_SCHEMA: MessageType = + READ_SCHEMA_V2_0 + .union(READ_SCHEMA_V2_1) } } diff --git a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceRecordMaterializer.kt b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceRecordMaterializer.kt index 936a684a4..6e2afa7a5 100644 --- a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceRecordMaterializer.kt +++ b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceRecordMaterializer.kt @@ -37,75 +37,91 @@ internal class ResourceRecordMaterializer(schema: MessageType) : RecordMateriali /** * State of current record being read. */ - private var _id = "" - private var _startTime = Instant.MIN - private var _stopTime = Instant.MIN - private var _cpuCount = 0 - private var _cpuCapacity = 0.0 - private var _memCapacity = 0.0 + private var localId = "" + private var localStartTime = Instant.MIN + private var localStopTime = Instant.MIN + private var localCpuCount = 0 + private var localCpuCapacity = 0.0 + private var localMemCapacity = 0.0 /** * Root converter for the record. */ - private val root = object : GroupConverter() { - /** - * The converters for the columns of the schema. - */ - private val converters = schema.fields.map { type -> - when (type.name) { - "id" -> object : PrimitiveConverter() { - override fun addBinary(value: Binary) { - _id = value.toStringUsingUTF8() - } - } - "start_time", "submissionTime" -> object : PrimitiveConverter() { - override fun addLong(value: Long) { - _startTime = Instant.ofEpochMilli(value) - } - } - "stop_time", "endTime" -> object : PrimitiveConverter() { - override fun addLong(value: Long) { - _stopTime = Instant.ofEpochMilli(value) - } - } - "cpu_count", "maxCores" -> object : PrimitiveConverter() { - override fun addInt(value: Int) { - _cpuCount = value - } - } - "cpu_capacity" -> object : PrimitiveConverter() { - override fun addDouble(value: Double) { - _cpuCapacity = value - } - } - "mem_capacity", "requiredMemory" -> object : PrimitiveConverter() { - override fun addDouble(value: Double) { - _memCapacity = value - } + private val root = + object : GroupConverter() { + /** + * The converters for the columns of the schema. + */ + private val converters = + schema.fields.map { type -> + when (type.name) { + "id" -> + object : PrimitiveConverter() { + override fun addBinary(value: Binary) { + localId = value.toStringUsingUTF8() + } + } + "start_time", "submissionTime" -> + object : PrimitiveConverter() { + override fun addLong(value: Long) { + localStartTime = Instant.ofEpochMilli(value) + } + } + "stop_time", "endTime" -> + object : PrimitiveConverter() { + override fun addLong(value: Long) { + localStopTime = Instant.ofEpochMilli(value) + } + } + "cpu_count", "maxCores" -> + object : PrimitiveConverter() { + override fun addInt(value: Int) { + localCpuCount = value + } + } + "cpu_capacity" -> + object : PrimitiveConverter() { + override fun addDouble(value: Double) { + localCpuCapacity = value + } + } + "mem_capacity", "requiredMemory" -> + object : PrimitiveConverter() { + override fun addDouble(value: Double) { + localMemCapacity = value + } - override fun addLong(value: Long) { - _memCapacity = value.toDouble() + override fun addLong(value: Long) { + localMemCapacity = value.toDouble() + } + } + else -> error("Unknown column $type") } } - else -> error("Unknown column $type") - } - } - override fun start() { - _id = "" - _startTime = Instant.MIN - _stopTime = Instant.MIN - _cpuCount = 0 - _cpuCapacity = 0.0 - _memCapacity = 0.0 - } + override fun start() { + localId = "" + localStartTime = Instant.MIN + localStopTime = Instant.MIN + localCpuCount = 0 + localCpuCapacity = 0.0 + localMemCapacity = 0.0 + } - override fun end() {} + override fun end() {} - override fun getConverter(fieldIndex: Int): Converter = converters[fieldIndex] - } + override fun getConverter(fieldIndex: Int): Converter = converters[fieldIndex] + } - override fun getCurrentRecord(): Resource = Resource(_id, _startTime, _stopTime, _cpuCount, _cpuCapacity, _memCapacity) + override fun getCurrentRecord(): Resource = + Resource( + localId, + localStartTime, + localStopTime, + localCpuCount, + localCpuCapacity, + localMemCapacity, + ) override fun getRootConverter(): GroupConverter = root } diff --git a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceState.kt b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceState.kt index 9ad587648..483f444cb 100644 --- a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceState.kt +++ b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceState.kt @@ -30,5 +30,5 @@ internal class ResourceState( val timestamp: Instant, val duration: Duration, val cpuCount: Int, - val cpuUsage: Double + val cpuUsage: Double, ) diff --git a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceStateReadSupport.kt b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceStateReadSupport.kt index 56366cd82..21e206a9a 100644 --- a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceStateReadSupport.kt +++ b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceStateReadSupport.kt @@ -31,11 +31,11 @@ import org.apache.parquet.schema.MessageType import org.apache.parquet.schema.PrimitiveType import org.apache.parquet.schema.Types import org.opendc.trace.TableColumn -import org.opendc.trace.conv.RESOURCE_CPU_COUNT -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE -import org.opendc.trace.conv.RESOURCE_STATE_DURATION -import org.opendc.trace.conv.RESOURCE_STATE_TIMESTAMP +import org.opendc.trace.conv.resourceCpuCount +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceStateCpuUsage +import org.opendc.trace.conv.resourceStateDuration +import org.opendc.trace.conv.resourceStateTimestamp /** * A [ReadSupport] instance for [ResourceState] objects. @@ -44,16 +44,17 @@ internal class ResourceStateReadSupport(private val projection: List?) : /** * Mapping from field names to [TableColumn]s. */ - private val fieldMap = mapOf( - "id" to RESOURCE_ID, - "time" to RESOURCE_STATE_TIMESTAMP, - "timestamp" to RESOURCE_STATE_TIMESTAMP, - "duration" to RESOURCE_STATE_DURATION, - "cores" to RESOURCE_CPU_COUNT, - "cpu_count" to RESOURCE_CPU_COUNT, - "cpuUsage" to RESOURCE_STATE_CPU_USAGE, - "cpu_usage" to RESOURCE_STATE_CPU_USAGE - ) + private val fieldMap = + mapOf( + "id" to resourceID, + "time" to resourceStateTimestamp, + "timestamp" to resourceStateTimestamp, + "duration" to resourceStateDuration, + "cores" to resourceCpuCount, + "cpu_count" to resourceCpuCount, + "cpuUsage" to resourceStateCpuUsage, + "cpu_usage" to resourceStateCpuUsage, + ) override fun init(context: InitContext): ReadContext { val projectedSchema = @@ -81,7 +82,7 @@ internal class ResourceStateReadSupport(private val projection: List?) : configuration: Configuration, keyValueMetaData: Map, fileSchema: MessageType, - readContext: ReadContext + readContext: ReadContext, ): RecordMaterializer = ResourceStateRecordMaterializer(readContext.requestedSchema) companion object { @@ -89,53 +90,55 @@ internal class ResourceStateReadSupport(private val projection: List?) : * Parquet read schema (version 2.0) for the "resource states" table in the trace. */ @JvmStatic - val READ_SCHEMA_V2_0: MessageType = Types.buildMessage() - .addFields( - Types - .required(PrimitiveType.PrimitiveTypeName.BINARY) - .`as`(LogicalTypeAnnotation.stringType()) - .named("id"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("time"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("duration"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("cores"), - Types - .required(PrimitiveType.PrimitiveTypeName.DOUBLE) - .named("cpuUsage") - ) - .named("resource_state") + val READ_SCHEMA_V2_0: MessageType = + Types.buildMessage() + .addFields( + Types + .required(PrimitiveType.PrimitiveTypeName.BINARY) + .`as`(LogicalTypeAnnotation.stringType()) + .named("id"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) + .named("time"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("duration"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("cores"), + Types + .required(PrimitiveType.PrimitiveTypeName.DOUBLE) + .named("cpuUsage"), + ) + .named("resource_state") /** * Parquet read schema (version 2.1) for the "resource states" table in the trace. */ @JvmStatic - val READ_SCHEMA_V2_1: MessageType = Types.buildMessage() - .addFields( - Types - .required(PrimitiveType.PrimitiveTypeName.BINARY) - .`as`(LogicalTypeAnnotation.stringType()) - .named("id"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("timestamp"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("duration"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("cpu_count"), - Types - .required(PrimitiveType.PrimitiveTypeName.DOUBLE) - .named("cpu_usage") - ) - .named("resource_state") + val READ_SCHEMA_V2_1: MessageType = + Types.buildMessage() + .addFields( + Types + .required(PrimitiveType.PrimitiveTypeName.BINARY) + .`as`(LogicalTypeAnnotation.stringType()) + .named("id"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) + .named("timestamp"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("duration"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("cpu_count"), + Types + .required(PrimitiveType.PrimitiveTypeName.DOUBLE) + .named("cpu_usage"), + ) + .named("resource_state") /** * Parquet read schema for the "resource states" table in the trace. diff --git a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceStateRecordMaterializer.kt b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceStateRecordMaterializer.kt index a813a5af9..72d24e782 100644 --- a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceStateRecordMaterializer.kt +++ b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceStateRecordMaterializer.kt @@ -38,69 +38,77 @@ internal class ResourceStateRecordMaterializer(schema: MessageType) : RecordMate /** * State of current record being read. */ - private var _id = "" - private var _timestamp = Instant.MIN - private var _duration = Duration.ZERO - private var _cpuCount = 0 - private var _cpuUsage = 0.0 + private var localId = "" + private var localTimestamp = Instant.MIN + private var localDuration = Duration.ZERO + private var localCpuCount = 0 + private var localCpuUsage = 0.0 /** * Root converter for the record. */ - private val root = object : GroupConverter() { - /** - * The converters for the columns of the schema. - */ - private val converters = schema.fields.map { type -> - when (type.name) { - "id" -> object : PrimitiveConverter() { - override fun addBinary(value: Binary) { - _id = value.toStringUsingUTF8() + private val root = + object : GroupConverter() { + /** + * The converters for the columns of the schema. + */ + private val converters = + schema.fields.map { type -> + when (type.name) { + "id" -> + object : PrimitiveConverter() { + override fun addBinary(value: Binary) { + localId = value.toStringUsingUTF8() + } + } + "timestamp", "time" -> + object : PrimitiveConverter() { + override fun addLong(value: Long) { + localTimestamp = Instant.ofEpochMilli(value) + } + } + "duration" -> + object : PrimitiveConverter() { + override fun addLong(value: Long) { + localDuration = Duration.ofMillis(value) + } + } + "cpu_count", "cores" -> + object : PrimitiveConverter() { + override fun addInt(value: Int) { + localCpuCount = value + } + } + "cpu_usage", "cpuUsage" -> + object : PrimitiveConverter() { + override fun addDouble(value: Double) { + localCpuUsage = value + } + } + "flops" -> + object : PrimitiveConverter() { + override fun addLong(value: Long) { + // Ignore to support v1 format + } + } + else -> error("Unknown column $type") } } - "timestamp", "time" -> object : PrimitiveConverter() { - override fun addLong(value: Long) { - _timestamp = Instant.ofEpochMilli(value) - } - } - "duration" -> object : PrimitiveConverter() { - override fun addLong(value: Long) { - _duration = Duration.ofMillis(value) - } - } - "cpu_count", "cores" -> object : PrimitiveConverter() { - override fun addInt(value: Int) { - _cpuCount = value - } - } - "cpu_usage", "cpuUsage" -> object : PrimitiveConverter() { - override fun addDouble(value: Double) { - _cpuUsage = value - } - } - "flops" -> object : PrimitiveConverter() { - override fun addLong(value: Long) { - // Ignore to support v1 format - } - } - else -> error("Unknown column $type") - } - } - override fun start() { - _id = "" - _timestamp = Instant.MIN - _duration = Duration.ZERO - _cpuCount = 0 - _cpuUsage = 0.0 - } + override fun start() { + localId = "" + localTimestamp = Instant.MIN + localDuration = Duration.ZERO + localCpuCount = 0 + localCpuUsage = 0.0 + } - override fun end() {} + override fun end() {} - override fun getConverter(fieldIndex: Int): Converter = converters[fieldIndex] - } + override fun getConverter(fieldIndex: Int): Converter = converters[fieldIndex] + } - override fun getCurrentRecord(): ResourceState = ResourceState(_id, _timestamp, _duration, _cpuCount, _cpuUsage) + override fun getCurrentRecord(): ResourceState = ResourceState(localId, localTimestamp, localDuration, localCpuCount, localCpuUsage) override fun getRootConverter(): GroupConverter = root } diff --git a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceStateWriteSupport.kt b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceStateWriteSupport.kt index 0bbec4d25..2a6d8c12e 100644 --- a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceStateWriteSupport.kt +++ b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceStateWriteSupport.kt @@ -52,7 +52,10 @@ internal class ResourceStateWriteSupport : WriteSupport() { write(recordConsumer, record) } - private fun write(consumer: RecordConsumer, record: ResourceState) { + private fun write( + consumer: RecordConsumer, + record: ResourceState, + ) { consumer.startMessage() consumer.startField("id", 0) @@ -83,26 +86,27 @@ internal class ResourceStateWriteSupport : WriteSupport() { * Parquet schema for the "resource states" table in the trace. */ @JvmStatic - val WRITE_SCHEMA: MessageType = Types.buildMessage() - .addFields( - Types - .required(PrimitiveType.PrimitiveTypeName.BINARY) - .`as`(LogicalTypeAnnotation.stringType()) - .named("id"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("timestamp"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("duration"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("cpu_count"), - Types - .required(PrimitiveType.PrimitiveTypeName.DOUBLE) - .named("cpu_usage") - ) - .named("resource_state") + val WRITE_SCHEMA: MessageType = + Types.buildMessage() + .addFields( + Types + .required(PrimitiveType.PrimitiveTypeName.BINARY) + .`as`(LogicalTypeAnnotation.stringType()) + .named("id"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) + .named("timestamp"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("duration"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("cpu_count"), + Types + .required(PrimitiveType.PrimitiveTypeName.DOUBLE) + .named("cpu_usage"), + ) + .named("resource_state") } } diff --git a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceWriteSupport.kt b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceWriteSupport.kt index cd4287541..ed62e2ce4 100644 --- a/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceWriteSupport.kt +++ b/opendc-trace/opendc-trace-opendc/src/main/kotlin/org/opendc/trace/opendc/parquet/ResourceWriteSupport.kt @@ -53,7 +53,10 @@ internal class ResourceWriteSupport : WriteSupport() { write(recordConsumer, record) } - private fun write(consumer: RecordConsumer, record: Resource) { + private fun write( + consumer: RecordConsumer, + record: Resource, + ) { consumer.startMessage() consumer.startField("id", 0) @@ -88,30 +91,31 @@ internal class ResourceWriteSupport : WriteSupport() { * Parquet schema for the "resources" table in the trace. */ @JvmStatic - val WRITE_SCHEMA: MessageType = Types.buildMessage() - .addFields( - Types - .required(PrimitiveType.PrimitiveTypeName.BINARY) - .`as`(LogicalTypeAnnotation.stringType()) - .named("id"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("start_time"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("stop_time"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT32) - .named("cpu_count"), - Types - .required(PrimitiveType.PrimitiveTypeName.DOUBLE) - .named("cpu_capacity"), - Types - .required(PrimitiveType.PrimitiveTypeName.INT64) - .named("mem_capacity") - ) - .named("resource") + val WRITE_SCHEMA: MessageType = + Types.buildMessage() + .addFields( + Types + .required(PrimitiveType.PrimitiveTypeName.BINARY) + .`as`(LogicalTypeAnnotation.stringType()) + .named("id"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) + .named("start_time"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) + .named("stop_time"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT32) + .named("cpu_count"), + Types + .required(PrimitiveType.PrimitiveTypeName.DOUBLE) + .named("cpu_capacity"), + Types + .required(PrimitiveType.PrimitiveTypeName.INT64) + .named("mem_capacity"), + ) + .named("resource") } } diff --git a/opendc-trace/opendc-trace-opendc/src/test/kotlin/org/opendc/trace/opendc/OdcVmTraceFormatTest.kt b/opendc-trace/opendc-trace-opendc/src/test/kotlin/org/opendc/trace/opendc/OdcVmTraceFormatTest.kt index d3c3b35ba..c9fa21c30 100644 --- a/opendc-trace/opendc-trace-opendc/src/test/kotlin/org/opendc/trace/opendc/OdcVmTraceFormatTest.kt +++ b/opendc-trace/opendc-trace-opendc/src/test/kotlin/org/opendc/trace/opendc/OdcVmTraceFormatTest.kt @@ -40,17 +40,17 @@ import org.opendc.trace.TableWriter import org.opendc.trace.conv.INTERFERENCE_GROUP_MEMBERS import org.opendc.trace.conv.INTERFERENCE_GROUP_SCORE import org.opendc.trace.conv.INTERFERENCE_GROUP_TARGET -import org.opendc.trace.conv.RESOURCE_CPU_CAPACITY -import org.opendc.trace.conv.RESOURCE_CPU_COUNT -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_MEM_CAPACITY -import org.opendc.trace.conv.RESOURCE_START_TIME -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE -import org.opendc.trace.conv.RESOURCE_STATE_TIMESTAMP -import org.opendc.trace.conv.RESOURCE_STOP_TIME import org.opendc.trace.conv.TABLE_INTERFERENCE_GROUPS import org.opendc.trace.conv.TABLE_RESOURCES import org.opendc.trace.conv.TABLE_RESOURCE_STATES +import org.opendc.trace.conv.resourceCpuCapacity +import org.opendc.trace.conv.resourceCpuCount +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceMemCapacity +import org.opendc.trace.conv.resourceStartTime +import org.opendc.trace.conv.resourceStateCpuUsage +import org.opendc.trace.conv.resourceStateTimestamp +import org.opendc.trace.conv.resourceStopTime import org.opendc.trace.testkit.TableReaderTestKit import org.opendc.trace.testkit.TableWriterTestKit import java.nio.file.Files @@ -88,19 +88,19 @@ internal class OdcVmTraceFormatTest { @ValueSource(strings = ["trace-v2.0", "trace-v2.1"]) fun testResources(name: String) { val path = Paths.get("src/test/resources/$name") - val reader = format.newReader(path, TABLE_RESOURCES, listOf(RESOURCE_ID, RESOURCE_START_TIME)) + val reader = format.newReader(path, TABLE_RESOURCES, listOf(resourceID, resourceStartTime)) assertAll( { assertTrue(reader.nextRow()) }, - { assertEquals("1019", reader.getString(RESOURCE_ID)) }, - { assertEquals(Instant.ofEpochMilli(1376314846000), reader.getInstant(RESOURCE_START_TIME)) }, + { assertEquals("1019", reader.getString(resourceID)) }, + { assertEquals(Instant.ofEpochMilli(1376314846000), reader.getInstant(resourceStartTime)) }, { assertTrue(reader.nextRow()) }, - { assertEquals("1023", reader.getString(RESOURCE_ID)) }, + { assertEquals("1023", reader.getString(resourceID)) }, { assertTrue(reader.nextRow()) }, - { assertEquals("1052", reader.getString(RESOURCE_ID)) }, + { assertEquals("1052", reader.getString(resourceID)) }, { assertTrue(reader.nextRow()) }, - { assertEquals("1073", reader.getString(RESOURCE_ID)) }, - { assertFalse(reader.nextRow()) } + { assertEquals("1073", reader.getString(resourceID)) }, + { assertFalse(reader.nextRow()) }, ) reader.close() @@ -112,12 +112,12 @@ internal class OdcVmTraceFormatTest { val writer = format.newWriter(path, TABLE_RESOURCES) writer.startRow() - writer.setString(RESOURCE_ID, "1019") - writer.setInstant(RESOURCE_START_TIME, Instant.EPOCH) - writer.setInstant(RESOURCE_STOP_TIME, Instant.EPOCH) - writer.setInt(RESOURCE_CPU_COUNT, 1) - writer.setDouble(RESOURCE_CPU_CAPACITY, 1024.0) - writer.setDouble(RESOURCE_MEM_CAPACITY, 1024.0) + writer.setString(resourceID, "1019") + writer.setInstant(resourceStartTime, Instant.EPOCH) + writer.setInstant(resourceStopTime, Instant.EPOCH) + writer.setInt(resourceCpuCount, 1) + writer.setDouble(resourceCpuCapacity, 1024.0) + writer.setDouble(resourceMemCapacity, 1024.0) writer.endRow() writer.close() @@ -125,13 +125,13 @@ internal class OdcVmTraceFormatTest { assertAll( { assertTrue(reader.nextRow()) }, - { assertEquals("1019", reader.getString(RESOURCE_ID)) }, - { assertEquals(Instant.EPOCH, reader.getInstant(RESOURCE_START_TIME)) }, - { assertEquals(Instant.EPOCH, reader.getInstant(RESOURCE_STOP_TIME)) }, - { assertEquals(1, reader.getInt(RESOURCE_CPU_COUNT)) }, - { assertEquals(1024.0, reader.getDouble(RESOURCE_CPU_CAPACITY)) }, - { assertEquals(1024.0, reader.getDouble(RESOURCE_MEM_CAPACITY)) }, - { assertFalse(reader.nextRow()) } + { assertEquals("1019", reader.getString(resourceID)) }, + { assertEquals(Instant.EPOCH, reader.getInstant(resourceStartTime)) }, + { assertEquals(Instant.EPOCH, reader.getInstant(resourceStopTime)) }, + { assertEquals(1, reader.getInt(resourceCpuCount)) }, + { assertEquals(1024.0, reader.getDouble(resourceCpuCapacity)) }, + { assertEquals(1024.0, reader.getDouble(resourceMemCapacity)) }, + { assertFalse(reader.nextRow()) }, ) reader.close() @@ -141,17 +141,18 @@ internal class OdcVmTraceFormatTest { @ValueSource(strings = ["trace-v2.0", "trace-v2.1"]) fun testSmoke(name: String) { val path = Paths.get("src/test/resources/$name") - val reader = format.newReader( - path, - TABLE_RESOURCE_STATES, - listOf(RESOURCE_ID, RESOURCE_STATE_TIMESTAMP, RESOURCE_STATE_CPU_USAGE) - ) + val reader = + format.newReader( + path, + TABLE_RESOURCE_STATES, + listOf(resourceID, resourceStateTimestamp, resourceStateCpuUsage), + ) assertAll( { assertTrue(reader.nextRow()) }, - { assertEquals("1019", reader.getString(RESOURCE_ID)) }, - { assertEquals(1376314846, reader.getInstant(RESOURCE_STATE_TIMESTAMP)?.epochSecond) }, - { assertEquals(0.0, reader.getDouble(RESOURCE_STATE_CPU_USAGE), 0.01) } + { assertEquals("1019", reader.getString(resourceID)) }, + { assertEquals(1376314846, reader.getInstant(resourceStateTimestamp)?.epochSecond) }, + { assertEquals(0.0, reader.getDouble(resourceStateCpuUsage), 0.01) }, ) reader.close() @@ -163,10 +164,10 @@ internal class OdcVmTraceFormatTest { val writer = format.newWriter(path, TABLE_RESOURCE_STATES) writer.startRow() - writer.setString(RESOURCE_ID, "1019") - writer.setInstant(RESOURCE_STATE_TIMESTAMP, Instant.EPOCH) - writer.setDouble(RESOURCE_STATE_CPU_USAGE, 23.0) - writer.setInt(RESOURCE_CPU_COUNT, 1) + writer.setString(resourceID, "1019") + writer.setInstant(resourceStateTimestamp, Instant.EPOCH) + writer.setDouble(resourceStateCpuUsage, 23.0) + writer.setInt(resourceCpuCount, 1) writer.endRow() writer.close() @@ -174,11 +175,11 @@ internal class OdcVmTraceFormatTest { assertAll( { assertTrue(reader.nextRow()) }, - { assertEquals("1019", reader.getString(RESOURCE_ID)) }, - { assertEquals(Instant.EPOCH, reader.getInstant(RESOURCE_STATE_TIMESTAMP)) }, - { assertEquals(1, reader.getInt(RESOURCE_CPU_COUNT)) }, - { assertEquals(23.0, reader.getDouble(RESOURCE_STATE_CPU_USAGE)) }, - { assertFalse(reader.nextRow()) } + { assertEquals("1019", reader.getString(resourceID)) }, + { assertEquals(Instant.EPOCH, reader.getInstant(resourceStateTimestamp)) }, + { assertEquals(1, reader.getInt(resourceCpuCount)) }, + { assertEquals(23.0, reader.getDouble(resourceStateCpuUsage)) }, + { assertFalse(reader.nextRow()) }, ) reader.close() @@ -187,11 +188,12 @@ internal class OdcVmTraceFormatTest { @Test fun testInterferenceGroups() { val path = Paths.get("src/test/resources/trace-v2.1") - val reader = format.newReader( - path, - TABLE_INTERFERENCE_GROUPS, - listOf(INTERFERENCE_GROUP_MEMBERS, INTERFERENCE_GROUP_TARGET, INTERFERENCE_GROUP_SCORE) - ) + val reader = + format.newReader( + path, + TABLE_INTERFERENCE_GROUPS, + listOf(INTERFERENCE_GROUP_MEMBERS, INTERFERENCE_GROUP_TARGET, INTERFERENCE_GROUP_SCORE), + ) assertAll( { assertTrue(reader.nextRow()) }, @@ -202,7 +204,7 @@ internal class OdcVmTraceFormatTest { { assertEquals(setOf("1023", "1052", "1073"), reader.getSet(INTERFERENCE_GROUP_MEMBERS, String::class.java)) }, { assertEquals(0.0, reader.getDouble(INTERFERENCE_GROUP_TARGET)) }, { assertEquals(0.7133055555552751, reader.getDouble(INTERFERENCE_GROUP_SCORE)) }, - { assertFalse(reader.nextRow()) } + { assertFalse(reader.nextRow()) }, ) reader.close() @@ -247,7 +249,7 @@ internal class OdcVmTraceFormatTest { { assertEquals(setOf("a", "b", "d"), reader.getSet(INTERFERENCE_GROUP_MEMBERS, String::class.java)) }, { assertEquals(0.5, reader.getDouble(INTERFERENCE_GROUP_TARGET)) }, { assertEquals(0.9, reader.getDouble(INTERFERENCE_GROUP_SCORE)) }, - { assertFalse(reader.nextRow()) } + { assertFalse(reader.nextRow()) }, ) reader.close() diff --git a/opendc-trace/opendc-trace-parquet/build.gradle.kts b/opendc-trace/opendc-trace-parquet/build.gradle.kts index 2217a017c..4cdd43506 100644 --- a/opendc-trace/opendc-trace-parquet/build.gradle.kts +++ b/opendc-trace/opendc-trace-parquet/build.gradle.kts @@ -22,13 +22,13 @@ description = "Parquet helpers for traces in OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } dependencies { - /* This configuration is necessary for a slim dependency on Apache Parquet */ + // This configuration is necessary for a slim dependency on Apache Parquet api(libs.parquet) { exclude(group = "org.apache.hadoop") } diff --git a/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalInputFile.kt b/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalInputFile.kt index fd2e00cd9..a60b426ad 100644 --- a/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalInputFile.kt +++ b/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalInputFile.kt @@ -47,61 +47,66 @@ public class LocalInputFile(private val path: Path) : InputFile { override fun getLength(): Long = channel.size() - override fun newStream(): SeekableInputStream = object : SeekableInputStream() { - override fun read(buf: ByteBuffer): Int { - return channel.read(buf) - } + override fun newStream(): SeekableInputStream = + object : SeekableInputStream() { + override fun read(buf: ByteBuffer): Int { + return channel.read(buf) + } - override fun read(): Int { - val single = ByteBuffer.allocate(1) - var read: Int + override fun read(): Int { + val single = ByteBuffer.allocate(1) + var read: Int - // ReadableByteChannel#read might read zero bytes so continue until we read at least one byte - do { - read = channel.read(single) - } while (read == 0) + // ReadableByteChannel#read might read zero bytes so continue until we read at least one byte + do { + read = channel.read(single) + } while (read == 0) - return if (read == -1) { - read - } else { - single.get(0).toInt() and 0xff + return if (read == -1) { + read + } else { + single.get(0).toInt() and 0xff + } } - } - override fun getPos(): Long { - return channel.position() - } + override fun getPos(): Long { + return channel.position() + } - override fun seek(newPos: Long) { - channel.position(newPos) - } + override fun seek(newPos: Long) { + channel.position(newPos) + } - override fun readFully(bytes: ByteArray) { - readFully(ByteBuffer.wrap(bytes)) - } + override fun readFully(bytes: ByteArray) { + readFully(ByteBuffer.wrap(bytes)) + } - override fun readFully(bytes: ByteArray, start: Int, len: Int) { - readFully(ByteBuffer.wrap(bytes, start, len)) - } + override fun readFully( + bytes: ByteArray, + start: Int, + len: Int, + ) { + readFully(ByteBuffer.wrap(bytes, start, len)) + } - override fun readFully(buf: ByteBuffer) { - var remainder = buf.remaining() - while (remainder > 0) { - val read = channel.read(buf) - remainder -= read + override fun readFully(buf: ByteBuffer) { + var remainder = buf.remaining() + while (remainder > 0) { + val read = channel.read(buf) + remainder -= read - if (read == -1 && remainder > 0) { - throw EOFException() + if (read == -1 && remainder > 0) { + throw EOFException() + } } } - } - override fun close() { - channel.close() - } + override fun close() { + channel.close() + } - override fun toString(): String = "NioSeekableInputStream" - } + override fun toString(): String = "NioSeekableInputStream" + } override fun toString(): String = "LocalInputFile[path=$path]" } diff --git a/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalOutputFile.kt b/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalOutputFile.kt index 1b17ae5d2..24627b451 100644 --- a/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalOutputFile.kt +++ b/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalOutputFile.kt @@ -51,8 +51,7 @@ public class LocalOutputFile(private val path: Path) : OutputFile { override fun supportsBlockSize(): Boolean = false - override fun defaultBlockSize(): Long = - throw UnsupportedOperationException("Local filesystem does not have default block size") + override fun defaultBlockSize(): Long = throw UnsupportedOperationException("Local filesystem does not have default block size") override fun getPath(): String = path.toString() @@ -77,7 +76,11 @@ public class LocalOutputFile(private val path: Path) : OutputFile { _pos += b.size } - override fun write(b: ByteArray, off: Int, len: Int) { + override fun write( + b: ByteArray, + off: Int, + len: Int, + ) { output.write(b, off, len) _pos += len } diff --git a/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalParquetReader.kt b/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalParquetReader.kt index de8a56d05..b503254e6 100644 --- a/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalParquetReader.kt +++ b/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalParquetReader.kt @@ -43,20 +43,21 @@ import kotlin.io.path.isDirectory public class LocalParquetReader( path: Path, private val readSupport: ReadSupport, - private val strictTyping: Boolean = true + private val strictTyping: Boolean = true, ) : AutoCloseable { /** * The input files to process. */ - private val filesIterator = if (path.isDirectory()) { - Files.list(path) - .filter { !it.isDirectory() } - .sorted() - .map { LocalInputFile(it) } - .iterator() - } else { - listOf(LocalInputFile(path)).iterator() - } + private val filesIterator = + if (path.isDirectory()) { + Files.list(path) + .filter { !it.isDirectory() } + .sorted() + .map { LocalInputFile(it) } + .iterator() + } else { + listOf(LocalInputFile(path)).iterator() + } /** * The Parquet reader to use. @@ -104,11 +105,12 @@ public class LocalParquetReader( reader?.close() try { - this.reader = if (filesIterator.hasNext()) { - createReader(filesIterator.next()) - } else { - null - } + this.reader = + if (filesIterator.hasNext()) { + createReader(filesIterator.next()) + } else { + null + } } catch (e: Throwable) { this.reader = null throw e diff --git a/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalParquetWriter.kt b/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalParquetWriter.kt index b5eb1deb2..c7028fc3a 100644 --- a/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalParquetWriter.kt +++ b/opendc-trace/opendc-trace-parquet/src/main/kotlin/org/opendc/trace/util/parquet/LocalParquetWriter.kt @@ -37,7 +37,7 @@ public class LocalParquetWriter { */ public class Builder internal constructor( output: OutputFile, - private val writeSupport: WriteSupport + private val writeSupport: WriteSupport, ) : ParquetWriter.Builder>(output) { override fun self(): Builder = this @@ -49,7 +49,9 @@ public class LocalParquetWriter { * Create a [Builder] instance that writes a Parquet file at the specified [path]. */ @JvmStatic - public fun builder(path: Path, writeSupport: WriteSupport): Builder = - Builder(LocalOutputFile(path), writeSupport) + public fun builder( + path: Path, + writeSupport: WriteSupport, + ): Builder = Builder(LocalOutputFile(path), writeSupport) } } diff --git a/opendc-trace/opendc-trace-parquet/src/test/kotlin/org/opendc/trace/util/parquet/ParquetTest.kt b/opendc-trace/opendc-trace-parquet/src/test/kotlin/org/opendc/trace/util/parquet/ParquetTest.kt index b6c5a423a..fc90aded0 100644 --- a/opendc-trace/opendc-trace-parquet/src/test/kotlin/org/opendc/trace/util/parquet/ParquetTest.kt +++ b/opendc-trace/opendc-trace-parquet/src/test/kotlin/org/opendc/trace/util/parquet/ParquetTest.kt @@ -51,49 +51,52 @@ import java.nio.file.Path internal class ParquetTest { private lateinit var path: Path - private val schema = Types.buildMessage() - .addField( - Types.primitive(PrimitiveType.PrimitiveTypeName.INT32, Type.Repetition.REQUIRED) - .named("field") - ) - .named("test") - private val writeSupport = object : WriteSupport() { - lateinit var recordConsumer: RecordConsumer - - override fun init(configuration: Configuration): WriteContext { - return WriteContext(schema, emptyMap()) - } + private val schema = + Types.buildMessage() + .addField( + Types.primitive(PrimitiveType.PrimitiveTypeName.INT32, Type.Repetition.REQUIRED) + .named("field"), + ) + .named("test") + private val writeSupport = + object : WriteSupport() { + lateinit var recordConsumer: RecordConsumer + + override fun init(configuration: Configuration): WriteContext { + return WriteContext(schema, emptyMap()) + } - override fun prepareForWrite(recordConsumer: RecordConsumer) { - this.recordConsumer = recordConsumer - } + override fun prepareForWrite(recordConsumer: RecordConsumer) { + this.recordConsumer = recordConsumer + } - override fun write(record: Int) { - val consumer = recordConsumer + override fun write(record: Int) { + val consumer = recordConsumer - consumer.startMessage() - consumer.startField("field", 0) - consumer.addInteger(record) - consumer.endField("field", 0) - consumer.endMessage() + consumer.startMessage() + consumer.startField("field", 0) + consumer.addInteger(record) + consumer.endField("field", 0) + consumer.endMessage() + } } - } - private val readSupport = object : ReadSupport() { - @Suppress("OVERRIDE_DEPRECATION") - override fun init( - configuration: Configuration, - keyValueMetaData: Map, - fileSchema: MessageType - ): ReadContext = ReadContext(fileSchema) - - override fun prepareForRead( - configuration: Configuration, - keyValueMetaData: Map, - fileSchema: MessageType, - readContext: ReadContext - ): RecordMaterializer = TestRecordMaterializer() - } + private val readSupport = + object : ReadSupport() { + @Suppress("OVERRIDE_DEPRECATION") + override fun init( + configuration: Configuration, + keyValueMetaData: Map, + fileSchema: MessageType, + ): ReadContext = ReadContext(fileSchema) + + override fun prepareForRead( + configuration: Configuration, + keyValueMetaData: Map, + fileSchema: MessageType, + readContext: ReadContext, + ): RecordMaterializer = TestRecordMaterializer() + } /** * Set up the test @@ -117,9 +120,10 @@ internal class ParquetTest { @Test fun testSmoke() { val n = 4 - val writer = LocalParquetWriter.builder(path, writeSupport) - .withWriteMode(ParquetFileWriter.Mode.OVERWRITE) - .build() + val writer = + LocalParquetWriter.builder(path, writeSupport) + .withWriteMode(ParquetFileWriter.Mode.OVERWRITE) + .build() try { repeat(n) { i -> @@ -166,19 +170,23 @@ internal class ParquetTest { private class TestRecordMaterializer : RecordMaterializer() { private var current: Int = 0 - private val fieldConverter = object : PrimitiveConverter() { - override fun addInt(value: Int) { - current = value + private val fieldConverter = + object : PrimitiveConverter() { + override fun addInt(value: Int) { + current = value + } } - } - private val root = object : GroupConverter() { - override fun getConverter(fieldIndex: Int): Converter { - require(fieldIndex == 0) - return fieldConverter + private val root = + object : GroupConverter() { + override fun getConverter(fieldIndex: Int): Converter { + require(fieldIndex == 0) + return fieldConverter + } + + override fun start() {} + + override fun end() {} } - override fun start() {} - override fun end() {} - } override fun getCurrentRecord(): Int = current diff --git a/opendc-trace/opendc-trace-swf/build.gradle.kts b/opendc-trace/opendc-trace-swf/build.gradle.kts index d3bc5aa6a..2798cdb13 100644 --- a/opendc-trace/opendc-trace-swf/build.gradle.kts +++ b/opendc-trace/opendc-trace-swf/build.gradle.kts @@ -22,7 +22,7 @@ description = "Support for Standard Workload Format (SWF) traces in OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-trace/opendc-trace-swf/src/main/kotlin/org/opendc/trace/swf/SwfTaskTableReader.kt b/opendc-trace/opendc-trace-swf/src/main/kotlin/org/opendc/trace/swf/SwfTaskTableReader.kt index 2465fb47e..5a79fd6f8 100644 --- a/opendc-trace/opendc-trace-swf/src/main/kotlin/org/opendc/trace/swf/SwfTaskTableReader.kt +++ b/opendc-trace/opendc-trace-swf/src/main/kotlin/org/opendc/trace/swf/SwfTaskTableReader.kt @@ -99,22 +99,22 @@ internal class SwfTaskTableReader(private val reader: BufferedReader) : TableRea override fun resolve(name: String): Int { return when (name) { - TASK_ID -> COL_JOB_ID - TASK_SUBMIT_TIME -> COL_SUBMIT_TIME - TASK_WAIT_TIME -> COL_WAIT_TIME - TASK_RUNTIME -> COL_RUN_TIME - TASK_ALLOC_NCPUS -> COL_ALLOC_NCPUS - TASK_REQ_NCPUS -> COL_REQ_NCPUS - TASK_STATUS -> COL_STATUS - TASK_USER_ID -> COL_USER_ID - TASK_GROUP_ID -> COL_GROUP_ID - TASK_PARENTS -> COL_PARENT_JOB + TASK_ID -> colJobID + TASK_SUBMIT_TIME -> colSubmitTime + TASK_WAIT_TIME -> colWaitTime + TASK_RUNTIME -> colRunTime + TASK_ALLOC_NCPUS -> colAllocNcpus + TASK_REQ_NCPUS -> colReqNcpus + TASK_STATUS -> colStatus + TASK_USER_ID -> colUserID + TASK_GROUP_ID -> colGroupID + TASK_PARENTS -> colParentJob else -> -1 } } override fun isNull(index: Int): Boolean { - require(index in COL_JOB_ID..COL_PARENT_THINK_TIME) { "Invalid column index" } + require(index in colJobID..colParentThinkTime) { "Invalid column index" } return false } @@ -125,7 +125,7 @@ internal class SwfTaskTableReader(private val reader: BufferedReader) : TableRea override fun getInt(index: Int): Int { check(state == State.Active) { "No active row" } return when (index) { - COL_REQ_NCPUS, COL_ALLOC_NCPUS, COL_STATUS, COL_GROUP_ID, COL_USER_ID -> fields[index].toInt(10) + colReqNcpus, colAllocNcpus, colStatus, colGroupID, colUserID -> fields[index].toInt(10) else -> throw IllegalArgumentException("Invalid column") } } @@ -145,7 +145,7 @@ internal class SwfTaskTableReader(private val reader: BufferedReader) : TableRea override fun getString(index: Int): String { check(state == State.Active) { "No active row" } return when (index) { - COL_JOB_ID -> fields[index] + colJobID -> fields[index] else -> throw IllegalArgumentException("Invalid column") } } @@ -157,7 +157,7 @@ internal class SwfTaskTableReader(private val reader: BufferedReader) : TableRea override fun getInstant(index: Int): Instant? { check(state == State.Active) { "No active row" } return when (index) { - COL_SUBMIT_TIME -> Instant.ofEpochSecond(fields[index].toLong(10)) + colSubmitTime -> Instant.ofEpochSecond(fields[index].toLong(10)) else -> throw IllegalArgumentException("Invalid column") } } @@ -165,20 +165,26 @@ internal class SwfTaskTableReader(private val reader: BufferedReader) : TableRea override fun getDuration(index: Int): Duration? { check(state == State.Active) { "No active row" } return when (index) { - COL_WAIT_TIME, COL_RUN_TIME -> Duration.ofSeconds(fields[index].toLong(10)) + colWaitTime, colRunTime -> Duration.ofSeconds(fields[index].toLong(10)) else -> throw IllegalArgumentException("Invalid column") } } - override fun getList(index: Int, elementType: Class): List? { + override fun getList( + index: Int, + elementType: Class, + ): List? { throw IllegalArgumentException("Invalid column") } - override fun getSet(index: Int, elementType: Class): Set? { + override fun getSet( + index: Int, + elementType: Class, + ): Set? { check(state == State.Active) { "No active row" } @Suppress("UNCHECKED_CAST") return when (index) { - COL_PARENT_JOB -> { + colParentJob -> { require(elementType.isAssignableFrom(String::class.java)) val parent = fields[index].toLong(10) if (parent < 0) emptySet() else setOf(parent) @@ -187,7 +193,11 @@ internal class SwfTaskTableReader(private val reader: BufferedReader) : TableRea } as Set? } - override fun getMap(index: Int, keyType: Class, valueType: Class): Map? { + override fun getMap( + index: Int, + keyType: Class, + valueType: Class, + ): Map? { throw IllegalArgumentException("Invalid column") } @@ -199,26 +209,28 @@ internal class SwfTaskTableReader(private val reader: BufferedReader) : TableRea /** * Default column indices for the SWF format. */ - private val COL_JOB_ID = 0 - private val COL_SUBMIT_TIME = 1 - private val COL_WAIT_TIME = 2 - private val COL_RUN_TIME = 3 - private val COL_ALLOC_NCPUS = 4 - private val COL_AVG_CPU_TIME = 5 - private val COL_USED_MEM = 6 - private val COL_REQ_NCPUS = 7 - private val COL_REQ_TIME = 8 - private val COL_REQ_MEM = 9 - private val COL_STATUS = 10 - private val COL_USER_ID = 11 - private val COL_GROUP_ID = 12 - private val COL_EXEC_NUM = 13 - private val COL_QUEUE_NUM = 14 - private val COL_PART_NUM = 15 - private val COL_PARENT_JOB = 16 - private val COL_PARENT_THINK_TIME = 17 + private val colJobID = 0 + private val colSubmitTime = 1 + private val colWaitTime = 2 + private val colRunTime = 3 + private val colAllocNcpus = 4 + private val colAvgCpuTime = 5 + private val colUsedMem = 6 + private val colReqNcpus = 7 + private val colReqTime = 8 + private val colReqMem = 9 + private val colStatus = 10 + private val colUserID = 11 + private val colGroupID = 12 + private val colExecNum = 13 + private val colQueueNum = 14 + private val colPartNum = 15 + private val colParentJob = 16 + private val colParentThinkTime = 17 private enum class State { - Pending, Active, Closed + Pending, + Active, + Closed, } } diff --git a/opendc-trace/opendc-trace-swf/src/main/kotlin/org/opendc/trace/swf/SwfTraceFormat.kt b/opendc-trace/opendc-trace-swf/src/main/kotlin/org/opendc/trace/swf/SwfTraceFormat.kt index c51805d71..d59b07b4e 100644 --- a/opendc-trace/opendc-trace-swf/src/main/kotlin/org/opendc/trace/swf/SwfTraceFormat.kt +++ b/opendc-trace/opendc-trace-swf/src/main/kotlin/org/opendc/trace/swf/SwfTraceFormat.kt @@ -56,34 +56,45 @@ public class SwfTraceFormat : TraceFormat { override fun getTables(path: Path): List = listOf(TABLE_TASKS) - override fun getDetails(path: Path, table: String): TableDetails { + override fun getDetails( + path: Path, + table: String, + ): TableDetails { return when (table) { - TABLE_TASKS -> TableDetails( - listOf( - TableColumn(TASK_ID, TableColumnType.String), - TableColumn(TASK_SUBMIT_TIME, TableColumnType.Instant), - TableColumn(TASK_WAIT_TIME, TableColumnType.Duration), - TableColumn(TASK_RUNTIME, TableColumnType.Duration), - TableColumn(TASK_REQ_NCPUS, TableColumnType.Int), - TableColumn(TASK_ALLOC_NCPUS, TableColumnType.Int), - TableColumn(TASK_PARENTS, TableColumnType.Set(TableColumnType.String)), - TableColumn(TASK_STATUS, TableColumnType.Int), - TableColumn(TASK_GROUP_ID, TableColumnType.Int), - TableColumn(TASK_USER_ID, TableColumnType.Int) + TABLE_TASKS -> + TableDetails( + listOf( + TableColumn(TASK_ID, TableColumnType.String), + TableColumn(TASK_SUBMIT_TIME, TableColumnType.Instant), + TableColumn(TASK_WAIT_TIME, TableColumnType.Duration), + TableColumn(TASK_RUNTIME, TableColumnType.Duration), + TableColumn(TASK_REQ_NCPUS, TableColumnType.Int), + TableColumn(TASK_ALLOC_NCPUS, TableColumnType.Int), + TableColumn(TASK_PARENTS, TableColumnType.Set(TableColumnType.String)), + TableColumn(TASK_STATUS, TableColumnType.Int), + TableColumn(TASK_GROUP_ID, TableColumnType.Int), + TableColumn(TASK_USER_ID, TableColumnType.Int), + ), ) - ) else -> throw IllegalArgumentException("Table $table not supported") } } - override fun newReader(path: Path, table: String, projection: List?): TableReader { + override fun newReader( + path: Path, + table: String, + projection: List?, + ): TableReader { return when (table) { TABLE_TASKS -> SwfTaskTableReader(path.bufferedReader()) else -> throw IllegalArgumentException("Table $table not supported") } } - override fun newWriter(path: Path, table: String): TableWriter { + override fun newWriter( + path: Path, + table: String, + ): TableWriter { throw UnsupportedOperationException("Writing not supported for this format") } } diff --git a/opendc-trace/opendc-trace-swf/src/test/kotlin/org/opendc/trace/swf/SwfTraceFormatTest.kt b/opendc-trace/opendc-trace-swf/src/test/kotlin/org/opendc/trace/swf/SwfTraceFormatTest.kt index 71d6dee3f..436f25726 100644 --- a/opendc-trace/opendc-trace-swf/src/test/kotlin/org/opendc/trace/swf/SwfTraceFormatTest.kt +++ b/opendc-trace/opendc-trace-swf/src/test/kotlin/org/opendc/trace/swf/SwfTraceFormatTest.kt @@ -77,7 +77,7 @@ internal class SwfTraceFormatTest { { assertEquals(306, reader.getInt(TASK_ALLOC_NCPUS)) }, { assertTrue(reader.nextRow()) }, { assertEquals("2", reader.getString(TASK_ID)) }, - { assertEquals(17, reader.getInt(TASK_ALLOC_NCPUS)) } + { assertEquals(17, reader.getInt(TASK_ALLOC_NCPUS)) }, ) reader.close() diff --git a/opendc-trace/opendc-trace-testkit/build.gradle.kts b/opendc-trace/opendc-trace-testkit/build.gradle.kts index f6b7222c6..e75ffc8c5 100644 --- a/opendc-trace/opendc-trace-testkit/build.gradle.kts +++ b/opendc-trace/opendc-trace-testkit/build.gradle.kts @@ -22,7 +22,7 @@ description = "Reusable test suite for implementors" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-trace/opendc-trace-testkit/src/main/kotlin/org/opendc/trace/testkit/TableReaderTestKit.kt b/opendc-trace/opendc-trace-testkit/src/main/kotlin/org/opendc/trace/testkit/TableReaderTestKit.kt index 4624cba0e..e5808f810 100644 --- a/opendc-trace/opendc-trace-testkit/src/main/kotlin/org/opendc/trace/testkit/TableReaderTestKit.kt +++ b/opendc-trace/opendc-trace-testkit/src/main/kotlin/org/opendc/trace/testkit/TableReaderTestKit.kt @@ -89,7 +89,7 @@ public abstract class TableReaderTestKit { { assertThrows { reader.getDuration(-1) } }, { assertThrows { reader.getList(-1, Any::class.java) } }, { assertThrows { reader.getSet(-1, Any::class.java) } }, - { assertThrows { reader.getMap(-1, Any::class.java, Any::class.java) } } + { assertThrows { reader.getMap(-1, Any::class.java, Any::class.java) } }, ) } @@ -111,13 +111,25 @@ public abstract class TableReaderTestKit { is TableColumnType.String -> assertFalse(reader.isNull(column.name) && reader.getString(column.name) != null) is TableColumnType.UUID -> assertFalse(reader.isNull(column.name) && reader.getUUID(column.name) != null) is TableColumnType.Instant -> assertFalse(reader.isNull(column.name) && reader.getInstant(column.name) != null) - is TableColumnType.Duration -> assertFalse(reader.isNull(column.name) && reader.getDuration(column.name) != null) - is TableColumnType.List -> assertFalse(reader.isNull(column.name) && reader.getList(column.name, Any::class.java) != null) - is TableColumnType.Set -> assertFalse(reader.isNull(column.name) && reader.getSet(column.name, Any::class.java) != null) - is TableColumnType.Map -> assertFalse(reader.isNull(column.name) && reader.getMap(column.name, Any::class.java, Any::class.java) != null) + is TableColumnType.Duration -> + assertFalse( + reader.isNull(column.name) && reader.getDuration(column.name) != null, + ) + is TableColumnType.List -> + assertFalse( + reader.isNull(column.name) && reader.getList(column.name, Any::class.java) != null, + ) + is TableColumnType.Set -> + assertFalse( + reader.isNull(column.name) && reader.getSet(column.name, Any::class.java) != null, + ) + is TableColumnType.Map -> + assertFalse( + reader.isNull(column.name) && reader.getMap(column.name, Any::class.java, Any::class.java) != null, + ) } } - } + }, ) } } diff --git a/opendc-trace/opendc-trace-testkit/src/main/kotlin/org/opendc/trace/testkit/TableWriterTestKit.kt b/opendc-trace/opendc-trace-testkit/src/main/kotlin/org/opendc/trace/testkit/TableWriterTestKit.kt index 3cd05f50d..2b4adf19f 100644 --- a/opendc-trace/opendc-trace-testkit/src/main/kotlin/org/opendc/trace/testkit/TableWriterTestKit.kt +++ b/opendc-trace/opendc-trace-testkit/src/main/kotlin/org/opendc/trace/testkit/TableWriterTestKit.kt @@ -88,7 +88,7 @@ public abstract class TableWriterTestKit { { assertThrows { writer.setDuration(-1, Duration.ofMinutes(5)) } }, { assertThrows { writer.setList(-1, listOf("test")) } }, { assertThrows { writer.setSet(-1, setOf("test")) } }, - { assertThrows { writer.setMap(-1, mapOf("test" to "test")) } } + { assertThrows { writer.setMap(-1, mapOf("test" to "test")) } }, ) } @@ -117,7 +117,7 @@ public abstract class TableWriterTestKit { } } } - } + }, ) } diff --git a/opendc-trace/opendc-trace-tools/build.gradle.kts b/opendc-trace/opendc-trace-tools/build.gradle.kts index db11059be..002ab8ccd 100644 --- a/opendc-trace/opendc-trace-tools/build.gradle.kts +++ b/opendc-trace/opendc-trace-tools/build.gradle.kts @@ -22,7 +22,7 @@ description = "Tools for working with workload traces" -/* Build configuration */ +// Build configuration plugins { `kotlin-conventions` application diff --git a/opendc-trace/opendc-trace-tools/src/main/kotlin/org/opendc/trace/tools/ConvertCommand.kt b/opendc-trace/opendc-trace-tools/src/main/kotlin/org/opendc/trace/tools/ConvertCommand.kt index bf0e2e3b4..17ff0c90c 100644 --- a/opendc-trace/opendc-trace-tools/src/main/kotlin/org/opendc/trace/tools/ConvertCommand.kt +++ b/opendc-trace/opendc-trace-tools/src/main/kotlin/org/opendc/trace/tools/ConvertCommand.kt @@ -39,24 +39,23 @@ import com.github.ajalt.clikt.parameters.types.restrictTo import mu.KotlinLogging import org.opendc.trace.TableWriter import org.opendc.trace.Trace -import org.opendc.trace.conv.RESOURCE_CPU_CAPACITY -import org.opendc.trace.conv.RESOURCE_CPU_COUNT -import org.opendc.trace.conv.RESOURCE_ID -import org.opendc.trace.conv.RESOURCE_MEM_CAPACITY -import org.opendc.trace.conv.RESOURCE_START_TIME -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE -import org.opendc.trace.conv.RESOURCE_STATE_CPU_USAGE_PCT -import org.opendc.trace.conv.RESOURCE_STATE_DURATION -import org.opendc.trace.conv.RESOURCE_STATE_MEM_USAGE -import org.opendc.trace.conv.RESOURCE_STATE_TIMESTAMP -import org.opendc.trace.conv.RESOURCE_STOP_TIME import org.opendc.trace.conv.TABLE_RESOURCES import org.opendc.trace.conv.TABLE_RESOURCE_STATES +import org.opendc.trace.conv.resourceCpuCapacity +import org.opendc.trace.conv.resourceCpuCount +import org.opendc.trace.conv.resourceID +import org.opendc.trace.conv.resourceMemCapacity +import org.opendc.trace.conv.resourceStartTime +import org.opendc.trace.conv.resourceStateCpuUsage +import org.opendc.trace.conv.resourceStateCpuUsagePct +import org.opendc.trace.conv.resourceStateDuration +import org.opendc.trace.conv.resourceStateMemUsage +import org.opendc.trace.conv.resourceStateTimestamp +import org.opendc.trace.conv.resourceStopTime import java.io.File import java.time.Duration import java.time.Instant import java.util.Random -import kotlin.collections.HashMap import kotlin.math.abs import kotlin.math.max import kotlin.math.min @@ -105,7 +104,7 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b */ private val converter by option("-c", "--converter", help = "converter strategy to use").groupChoice( "default" to DefaultTraceConverter(), - "azure" to AzureTraceConverter() + "azure" to AzureTraceConverter(), ).defaultByName("default") override fun run() { @@ -174,7 +173,11 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b * @param samplingOptions The sampling options to use. * @return The map of resources that have been selected. */ - abstract fun convertResources(trace: Trace, writer: TableWriter, samplingOptions: SamplingOptions?): Map + abstract fun convertResources( + trace: Trace, + writer: TableWriter, + samplingOptions: SamplingOptions?, + ): Map /** * Convert the resource states table for the trace. @@ -184,7 +187,11 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b * @param selected The set of virtual machines that have been selected. * @return The number of rows written. */ - abstract fun convertResourceStates(trace: Trace, writer: TableWriter, selected: Map): Int + abstract fun convertResourceStates( + trace: Trace, + writer: TableWriter, + selected: Map, + ): Int /** * A resource in the resource table. @@ -195,7 +202,7 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b val stopTime: Instant, val cpuCount: Int, val cpuCapacity: Double, - val memCapacity: Double + val memCapacity: Double, ) } @@ -211,14 +218,18 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b /** * The interval at which the samples where taken. */ - private val SAMPLE_INTERVAL = Duration.ofMinutes(5) + private val sampleInterval = Duration.ofMinutes(5) /** * The difference in CPU usage for the algorithm to cascade samples. */ - private val SAMPLE_CASCADE_DIFF = 0.1 + private val sampleCascadeDiff = 0.1 - override fun convertResources(trace: Trace, writer: TableWriter, samplingOptions: SamplingOptions?): Map { + override fun convertResources( + trace: Trace, + writer: TableWriter, + samplingOptions: SamplingOptions?, + ): Map { val random = samplingOptions?.let { Random(it.seed) } val samplingFraction = samplingOptions?.fraction ?: 1.0 val reader = checkNotNull(trace.getTable(TABLE_RESOURCE_STATES)).newReader() @@ -226,12 +237,12 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b var hasNextRow = reader.nextRow() val selectedVms = mutableMapOf() - val idCol = reader.resolve(RESOURCE_ID) - val timestampCol = reader.resolve(RESOURCE_STATE_TIMESTAMP) - val cpuCountCol = reader.resolve(RESOURCE_CPU_COUNT) - val cpuCapacityCol = reader.resolve(RESOURCE_CPU_CAPACITY) - val memCapacityCol = reader.resolve(RESOURCE_MEM_CAPACITY) - val memUsageCol = reader.resolve(RESOURCE_STATE_MEM_USAGE) + val idCol = reader.resolve(resourceID) + val timestampCol = reader.resolve(resourceStateTimestamp) + val cpuCountCol = reader.resolve(resourceCpuCount) + val cpuCapacityCol = reader.resolve(resourceCpuCapacity) + val memCapacityCol = reader.resolve(resourceMemCapacity) + val memUsageCol = reader.resolve(resourceStateMemUsage) while (hasNextRow) { var id: String @@ -257,7 +268,7 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b } hasNextRow = reader.nextRow() - } while (hasNextRow && id == reader.getString(RESOURCE_ID)) + } while (hasNextRow && id == reader.getString(resourceID)) // Sample only a fraction of the VMs if (random != null && random.nextDouble() > samplingFraction) { @@ -266,7 +277,7 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b logger.info { "Selecting VM $id" } - val startInstant = Instant.ofEpochMilli(startTime) - SAMPLE_INTERVAL // Offset by sample interval + val startInstant = Instant.ofEpochMilli(startTime) - sampleInterval // Offset by sample interval val stopInstant = Instant.ofEpochMilli(stopTime) selectedVms.computeIfAbsent(id) { @@ -274,26 +285,30 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b } writer.startRow() - writer.setString(RESOURCE_ID, id) - writer.setInstant(RESOURCE_START_TIME, startInstant) - writer.setInstant(RESOURCE_STOP_TIME, stopInstant) - writer.setInt(RESOURCE_CPU_COUNT, cpuCount) - writer.setDouble(RESOURCE_CPU_CAPACITY, cpuCapacity) - writer.setDouble(RESOURCE_MEM_CAPACITY, max(memCapacity, memUsage)) + writer.setString(resourceID, id) + writer.setInstant(resourceStartTime, startInstant) + writer.setInstant(resourceStopTime, stopInstant) + writer.setInt(resourceCpuCount, cpuCount) + writer.setDouble(resourceCpuCapacity, cpuCapacity) + writer.setDouble(resourceMemCapacity, max(memCapacity, memUsage)) writer.endRow() } return selectedVms } - override fun convertResourceStates(trace: Trace, writer: TableWriter, selected: Map): Int { + override fun convertResourceStates( + trace: Trace, + writer: TableWriter, + selected: Map, + ): Int { val reader = checkNotNull(trace.getTable(TABLE_RESOURCE_STATES)).newReader() - val sampleInterval = SAMPLE_INTERVAL.toMillis() + val sampleInterval = sampleInterval.toMillis() - val idCol = reader.resolve(RESOURCE_ID) - val timestampCol = reader.resolve(RESOURCE_STATE_TIMESTAMP) - val cpuCountCol = reader.resolve(RESOURCE_CPU_COUNT) - val cpuUsageCol = reader.resolve(RESOURCE_STATE_CPU_USAGE) + val idCol = reader.resolve(resourceID) + val timestampCol = reader.resolve(resourceStateTimestamp) + val cpuCountCol = reader.resolve(resourceCpuCount) + val cpuUsageCol = reader.resolve(resourceStateCpuUsage) var hasNextRow = reader.nextRow() var count = 0 @@ -315,9 +330,10 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b // Attempt to cascade further samples into one if they share the same CPU usage while (reader.nextRow().also { hasNextRow = it }) { - val shouldCascade = id == reader.getString(idCol) && - abs(cpuUsage - reader.getDouble(cpuUsageCol)) < SAMPLE_CASCADE_DIFF && - cpuCount == reader.getInt(cpuCountCol) + val shouldCascade = + id == reader.getString(idCol) && + abs(cpuUsage - reader.getDouble(cpuUsageCol)) < sampleCascadeDiff && + cpuCount == reader.getInt(cpuCountCol) // Check whether the next sample can be cascaded with the current sample: // (1) The VM identifier of both samples matches @@ -339,11 +355,11 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b } writer.startRow() - writer.setString(RESOURCE_ID, id) - writer.setInstant(RESOURCE_STATE_TIMESTAMP, Instant.ofEpochMilli(timestamp)) - writer.setDuration(RESOURCE_STATE_DURATION, Duration.ofMillis(duration)) - writer.setInt(RESOURCE_CPU_COUNT, cpuCount) - writer.setDouble(RESOURCE_STATE_CPU_USAGE, cpuUsage) + writer.setString(resourceID, id) + writer.setInstant(resourceStateTimestamp, Instant.ofEpochMilli(timestamp)) + writer.setDuration(resourceStateDuration, Duration.ofMillis(duration)) + writer.setInt(resourceCpuCount, cpuCount) + writer.setDouble(resourceStateCpuUsage, cpuUsage) writer.endRow() count++ @@ -365,28 +381,32 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b /** * CPU capacity of the machines used by Azure. */ - private val CPU_CAPACITY = 2500.0 + private val cpuCapacity = 2500.0 /** * The interval at which the samples where taken. */ - private val SAMPLE_INTERVAL = Duration.ofMinutes(5) + private val sampleInterval = Duration.ofMinutes(5) /** * The difference in CPU usage for the algorithm to cascade samples. */ - private val SAMPLE_CASCADE_DIFF = 0.1 + private val sampleCascadeDiff = 0.1 - override fun convertResources(trace: Trace, writer: TableWriter, samplingOptions: SamplingOptions?): Map { + override fun convertResources( + trace: Trace, + writer: TableWriter, + samplingOptions: SamplingOptions?, + ): Map { val random = samplingOptions?.let { Random(it.seed) } val samplingFraction = samplingOptions?.fraction ?: 1.0 val reader = checkNotNull(trace.getTable(TABLE_RESOURCES)).newReader() - val idCol = reader.resolve(RESOURCE_ID) - val startTimeCol = reader.resolve(RESOURCE_START_TIME) - val stopTimeCol = reader.resolve(RESOURCE_STOP_TIME) - val cpuCountCol = reader.resolve(RESOURCE_CPU_COUNT) - val memCapacityCol = reader.resolve(RESOURCE_MEM_CAPACITY) + val idCol = reader.resolve(resourceID) + val startTimeCol = reader.resolve(resourceStartTime) + val stopTimeCol = reader.resolve(resourceStopTime) + val cpuCountCol = reader.resolve(resourceCpuCount) + val memCapacityCol = reader.resolve(resourceMemCapacity) val selectedVms = mutableMapOf() @@ -406,33 +426,37 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b val startInstant = Instant.ofEpochMilli(startTime) val stopInstant = Instant.ofEpochMilli(stopTime) - val cpuCapacity = cpuCount * CPU_CAPACITY + val cpuCapacity = cpuCount * cpuCapacity selectedVms.computeIfAbsent(id) { Resource(it, startInstant, stopInstant, cpuCount, cpuCapacity, memCapacity) } writer.startRow() - writer.setString(RESOURCE_ID, id) - writer.setInstant(RESOURCE_START_TIME, startInstant) - writer.setInstant(RESOURCE_STOP_TIME, stopInstant) - writer.setInt(RESOURCE_CPU_COUNT, cpuCount) - writer.setDouble(RESOURCE_CPU_CAPACITY, cpuCapacity) - writer.setDouble(RESOURCE_MEM_CAPACITY, memCapacity) + writer.setString(resourceID, id) + writer.setInstant(resourceStartTime, startInstant) + writer.setInstant(resourceStopTime, stopInstant) + writer.setInt(resourceCpuCount, cpuCount) + writer.setDouble(resourceCpuCapacity, cpuCapacity) + writer.setDouble(resourceMemCapacity, memCapacity) writer.endRow() } return selectedVms } - override fun convertResourceStates(trace: Trace, writer: TableWriter, selected: Map): Int { + override fun convertResourceStates( + trace: Trace, + writer: TableWriter, + selected: Map, + ): Int { val reader = checkNotNull(trace.getTable(TABLE_RESOURCE_STATES)).newReader() val states = HashMap() - val sampleInterval = SAMPLE_INTERVAL.toMillis() + val sampleInterval = sampleInterval.toMillis() - val idCol = reader.resolve(RESOURCE_ID) - val timestampCol = reader.resolve(RESOURCE_STATE_TIMESTAMP) - val cpuUsageCol = reader.resolve(RESOURCE_STATE_CPU_USAGE_PCT) + val idCol = reader.resolve(resourceID) + val timestampCol = reader.resolve(resourceStateTimestamp) + val cpuUsageCol = reader.resolve(resourceStateCpuUsagePct) var count = 0 @@ -448,7 +472,7 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b // Check whether the next sample can be cascaded with the current sample: // (1) The CPU usage is almost identical (lower than `SAMPLE_CASCADE_DIFF`) // (2) The interval between both samples is not higher than `SAMPLE_INTERVAL` - if (abs(cpuUsage - state.cpuUsage) <= SAMPLE_CASCADE_DIFF && delta <= sampleInterval) { + if (abs(cpuUsage - state.cpuUsage) <= sampleCascadeDiff && delta <= sampleInterval) { state.time = timestamp state.duration += delta continue @@ -470,7 +494,11 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b return count } - private class State(@JvmField val resource: Resource, @JvmField var cpuUsage: Double, @JvmField var duration: Long) { + private class State( + @JvmField val resource: Resource, + @JvmField var cpuUsage: Double, + @JvmField var duration: Long, + ) { @JvmField var time: Long = resource.startTime.toEpochMilli() private var lastWrite: Long = Long.MIN_VALUE @@ -482,11 +510,11 @@ internal class ConvertCommand : CliktCommand(name = "convert", help = "Convert b lastWrite = time writer.startRow() - writer.setString(RESOURCE_ID, resource.id) - writer.setInstant(RESOURCE_STATE_TIMESTAMP, Instant.ofEpochMilli(time)) - writer.setDuration(RESOURCE_STATE_DURATION, Duration.ofMillis(duration)) - writer.setDouble(RESOURCE_STATE_CPU_USAGE, cpuUsage) - writer.setInt(RESOURCE_CPU_COUNT, resource.cpuCount) + writer.setString(resourceID, resource.id) + writer.setInstant(resourceStateTimestamp, Instant.ofEpochMilli(time)) + writer.setDuration(resourceStateDuration, Duration.ofMillis(duration)) + writer.setDouble(resourceStateCpuUsage, cpuUsage) + writer.setInt(resourceCpuCount, resource.cpuCount) writer.endRow() } } diff --git a/opendc-trace/opendc-trace-tools/src/main/kotlin/org/opendc/trace/tools/QueryCommand.kt b/opendc-trace/opendc-trace-tools/src/main/kotlin/org/opendc/trace/tools/QueryCommand.kt index 98b4cdf51..7b7a2a641 100644 --- a/opendc-trace/opendc-trace-tools/src/main/kotlin/org/opendc/trace/tools/QueryCommand.kt +++ b/opendc-trace/opendc-trace-tools/src/main/kotlin/org/opendc/trace/tools/QueryCommand.kt @@ -67,11 +67,12 @@ internal class QueryCommand : CliktCommand(name = "query", help = "Query workloa /** * Access to the terminal. */ - private val terminal = TerminalBuilder.builder() - .system(false) - .streams(System.`in`, System.out) - .encoding(StandardCharsets.UTF_8) - .build() + private val terminal = + TerminalBuilder.builder() + .system(false) + .streams(System.`in`, System.out) + .encoding(StandardCharsets.UTF_8) + .build() /** * Helper class to print results to console. @@ -119,10 +120,11 @@ internal class QueryCommand : CliktCommand(name = "query", help = "Query workloa var count = 0 val meta: ResultSetMetaData = rs.metaData - val options = mapOf( - Printer.COLUMNS to List(meta.columnCount) { meta.getColumnName(it + 1) }, - Printer.BORDER to "|" - ) + val options = + mapOf( + Printer.COLUMNS to List(meta.columnCount) { meta.getColumnName(it + 1) }, + Printer.BORDER to "|", + ) val data = mutableListOf>() while (rs.next()) { @@ -146,7 +148,10 @@ internal class QueryCommand : CliktCommand(name = "query", help = "Query workloa private class QueryPrinter(private val terminal: Terminal) : DefaultPrinter(null) { override fun terminal(): Terminal = terminal - override fun highlightAndPrint(options: MutableMap, exception: Throwable) { + override fun highlightAndPrint( + options: MutableMap, + exception: Throwable, + ) { if (options.getOrDefault("exception", "stack") == "stack") { exception.printStackTrace() } else { diff --git a/opendc-trace/opendc-trace-wfformat/build.gradle.kts b/opendc-trace/opendc-trace-wfformat/build.gradle.kts index a0e22b161..57313a73f 100644 --- a/opendc-trace/opendc-trace-wfformat/build.gradle.kts +++ b/opendc-trace/opendc-trace-wfformat/build.gradle.kts @@ -22,7 +22,7 @@ description = "Support for WfCommons workload traces in OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-trace/opendc-trace-wfformat/src/main/kotlin/org/opendc/trace/wfformat/WfFormatTaskTableReader.kt b/opendc-trace/opendc-trace-wfformat/src/main/kotlin/org/opendc/trace/wfformat/WfFormatTaskTableReader.kt index e0cbd305f..8f84e51f6 100644 --- a/opendc-trace/opendc-trace-wfformat/src/main/kotlin/org/opendc/trace/wfformat/WfFormatTaskTableReader.kt +++ b/opendc-trace/opendc-trace-wfformat/src/main/kotlin/org/opendc/trace/wfformat/WfFormatTaskTableReader.kt @@ -81,13 +81,14 @@ internal class WfFormatTaskTableReader(private val parser: JsonParser) : TableRe } ParserLevel.WORKFLOW -> { // Seek for the jobs object in the file - level = if (!seekJobs()) { - ParserLevel.TRACE - } else if (!parser.isExpectedStartArrayToken) { - throw JsonParseException(parser, "Expected array", parser.currentLocation) - } else { - ParserLevel.JOB - } + level = + if (!seekJobs()) { + ParserLevel.TRACE + } else if (!parser.isExpectedStartArrayToken) { + throw JsonParseException(parser, "Expected array", parser.currentLocation) + } else { + ParserLevel.JOB + } } ParserLevel.JOB -> { when (parser.nextToken()) { @@ -108,18 +109,18 @@ internal class WfFormatTaskTableReader(private val parser: JsonParser) : TableRe override fun resolve(name: String): Int { return when (name) { - TASK_ID -> COL_ID - TASK_WORKFLOW_ID -> COL_WORKFLOW_ID - TASK_RUNTIME -> COL_RUNTIME - TASK_REQ_NCPUS -> COL_NPROC - TASK_PARENTS -> COL_PARENTS - TASK_CHILDREN -> COL_CHILDREN + TASK_ID -> colID + TASK_WORKFLOW_ID -> colWorkflowID + TASK_RUNTIME -> colRuntime + TASK_REQ_NCPUS -> colNproc + TASK_PARENTS -> colParents + TASK_CHILDREN -> colChildren else -> -1 } } override fun isNull(index: Int): Boolean { - require(index in 0..COL_CHILDREN) { "Invalid column value" } + require(index in 0..colChildren) { "Invalid column value" } return false } @@ -130,7 +131,7 @@ internal class WfFormatTaskTableReader(private val parser: JsonParser) : TableRe override fun getInt(index: Int): Int { checkActive() return when (index) { - COL_NPROC -> cores + colNproc -> cores else -> throw IllegalArgumentException("Invalid column") } } @@ -150,8 +151,8 @@ internal class WfFormatTaskTableReader(private val parser: JsonParser) : TableRe override fun getString(index: Int): String? { checkActive() return when (index) { - COL_ID -> id - COL_WORKFLOW_ID -> workflowId + colID -> id + colWorkflowID -> workflowId else -> throw IllegalArgumentException("Invalid column") } } @@ -167,25 +168,35 @@ internal class WfFormatTaskTableReader(private val parser: JsonParser) : TableRe override fun getDuration(index: Int): Duration? { checkActive() return when (index) { - COL_RUNTIME -> runtime + colRuntime -> runtime else -> throw IllegalArgumentException("Invalid column") } } - override fun getList(index: Int, elementType: Class): List? { + override fun getList( + index: Int, + elementType: Class, + ): List? { throw IllegalArgumentException("Invalid column") } - override fun getSet(index: Int, elementType: Class): Set? { + override fun getSet( + index: Int, + elementType: Class, + ): Set? { checkActive() return when (index) { - COL_PARENTS -> TYPE_PARENTS.convertTo(parents, elementType) - COL_CHILDREN -> TYPE_CHILDREN.convertTo(children, elementType) + colParents -> typeParents.convertTo(parents, elementType) + colChildren -> typeChildren.convertTo(children, elementType) else -> throw IllegalArgumentException("Invalid column") } } - override fun getMap(index: Int, keyType: Class, valueType: Class): Map? { + override fun getMap( + index: Int, + keyType: Class, + valueType: Class, + ): Map? { throw IllegalArgumentException("Invalid column") } @@ -267,7 +278,10 @@ internal class WfFormatTaskTableReader(private val parser: JsonParser) : TableRe } private enum class ParserLevel { - TOP, TRACE, WORKFLOW, JOB + TOP, + TRACE, + WORKFLOW, + JOB, } /** @@ -288,13 +302,13 @@ internal class WfFormatTaskTableReader(private val parser: JsonParser) : TableRe cores = -1 } - private val COL_ID = 0 - private val COL_WORKFLOW_ID = 1 - private val COL_RUNTIME = 3 - private val COL_NPROC = 4 - private val COL_PARENTS = 5 - private val COL_CHILDREN = 6 + private val colID = 0 + private val colWorkflowID = 1 + private val colRuntime = 3 + private val colNproc = 4 + private val colParents = 5 + private val colChildren = 6 - private val TYPE_PARENTS = TableColumnType.Set(TableColumnType.String) - private val TYPE_CHILDREN = TableColumnType.Set(TableColumnType.String) + private val typeParents = TableColumnType.Set(TableColumnType.String) + private val typeChildren = TableColumnType.Set(TableColumnType.String) } diff --git a/opendc-trace/opendc-trace-wfformat/src/main/kotlin/org/opendc/trace/wfformat/WfFormatTraceFormat.kt b/opendc-trace/opendc-trace-wfformat/src/main/kotlin/org/opendc/trace/wfformat/WfFormatTraceFormat.kt index 35fb883a1..2178fac6c 100644 --- a/opendc-trace/opendc-trace-wfformat/src/main/kotlin/org/opendc/trace/wfformat/WfFormatTraceFormat.kt +++ b/opendc-trace/opendc-trace-wfformat/src/main/kotlin/org/opendc/trace/wfformat/WfFormatTraceFormat.kt @@ -55,30 +55,41 @@ public class WfFormatTraceFormat : TraceFormat { override fun getTables(path: Path): List = listOf(TABLE_TASKS) - override fun getDetails(path: Path, table: String): TableDetails { + override fun getDetails( + path: Path, + table: String, + ): TableDetails { return when (table) { - TABLE_TASKS -> TableDetails( - listOf( - TableColumn(TASK_ID, TableColumnType.String), - TableColumn(TASK_WORKFLOW_ID, TableColumnType.String), - TableColumn(TASK_RUNTIME, TableColumnType.Duration), - TableColumn(TASK_REQ_NCPUS, TableColumnType.Int), - TableColumn(TASK_PARENTS, TableColumnType.Set(TableColumnType.String)), - TableColumn(TASK_CHILDREN, TableColumnType.Set(TableColumnType.String)) + TABLE_TASKS -> + TableDetails( + listOf( + TableColumn(TASK_ID, TableColumnType.String), + TableColumn(TASK_WORKFLOW_ID, TableColumnType.String), + TableColumn(TASK_RUNTIME, TableColumnType.Duration), + TableColumn(TASK_REQ_NCPUS, TableColumnType.Int), + TableColumn(TASK_PARENTS, TableColumnType.Set(TableColumnType.String)), + TableColumn(TASK_CHILDREN, TableColumnType.Set(TableColumnType.String)), + ), ) - ) else -> throw IllegalArgumentException("Table $table not supported") } } - override fun newReader(path: Path, table: String, projection: List?): TableReader { + override fun newReader( + path: Path, + table: String, + projection: List?, + ): TableReader { return when (table) { TABLE_TASKS -> WfFormatTaskTableReader(factory.createParser(path.toFile())) else -> throw IllegalArgumentException("Table $table not supported") } } - override fun newWriter(path: Path, table: String): TableWriter { + override fun newWriter( + path: Path, + table: String, + ): TableWriter { throw UnsupportedOperationException("Writing not supported for this format") } } diff --git a/opendc-trace/opendc-trace-wfformat/src/test/kotlin/org/opendc/trace/wfformat/WfFormatTaskTableReaderTest.kt b/opendc-trace/opendc-trace-wfformat/src/test/kotlin/org/opendc/trace/wfformat/WfFormatTaskTableReaderTest.kt index 0560d642c..618cdf7db 100644 --- a/opendc-trace/opendc-trace-wfformat/src/test/kotlin/org/opendc/trace/wfformat/WfFormatTaskTableReaderTest.kt +++ b/opendc-trace/opendc-trace-wfformat/src/test/kotlin/org/opendc/trace/wfformat/WfFormatTaskTableReaderTest.kt @@ -69,11 +69,12 @@ internal class WfFormatTaskTableReaderTest { @Test fun testNoWorkflow() { - val content = """ - { - "name": "eager-nextflow-chameleon" - } - """.trimIndent() + val content = + """ + { + "name": "eager-nextflow-chameleon" + } + """.trimIndent() val parser = factory.createParser(content) val reader = WfFormatTaskTableReader(parser) @@ -88,12 +89,13 @@ internal class WfFormatTaskTableReaderTest { @Test fun testWorkflowArrayType() { - val content = """ - { - "name": "eager-nextflow-chameleon", - "workflow": [] - } - """.trimIndent() + val content = + """ + { + "name": "eager-nextflow-chameleon", + "workflow": [] + } + """.trimIndent() val parser = factory.createParser(content) val reader = WfFormatTaskTableReader(parser) @@ -108,12 +110,13 @@ internal class WfFormatTaskTableReaderTest { @Test fun testWorkflowNullType() { - val content = """ - { - "name": "eager-nextflow-chameleon", - "workflow": null - } - """.trimIndent() + val content = + """ + { + "name": "eager-nextflow-chameleon", + "workflow": null + } + """.trimIndent() val parser = factory.createParser(content) val reader = WfFormatTaskTableReader(parser) @@ -128,14 +131,15 @@ internal class WfFormatTaskTableReaderTest { @Test fun testNoJobs() { - val content = """ - { - "name": "eager-nextflow-chameleon", - "workflow": { + val content = + """ + { + "name": "eager-nextflow-chameleon", + "workflow": { + } } - } - """.trimIndent() + """.trimIndent() val parser = factory.createParser(content) val reader = WfFormatTaskTableReader(parser) @@ -146,12 +150,13 @@ internal class WfFormatTaskTableReaderTest { @Test fun testJobsObjectType() { - val content = """ - { - "name": "eager-nextflow-chameleon", - "workflow": { "jobs": {} } - } - """.trimIndent() + val content = + """ + { + "name": "eager-nextflow-chameleon", + "workflow": { "jobs": {} } + } + """.trimIndent() val parser = factory.createParser(content) val reader = WfFormatTaskTableReader(parser) @@ -162,12 +167,13 @@ internal class WfFormatTaskTableReaderTest { @Test fun testJobsNullType() { - val content = """ - { - "name": "eager-nextflow-chameleon", - "workflow": { "jobs": null } - } - """.trimIndent() + val content = + """ + { + "name": "eager-nextflow-chameleon", + "workflow": { "jobs": null } + } + """.trimIndent() val parser = factory.createParser(content) val reader = WfFormatTaskTableReader(parser) @@ -178,14 +184,15 @@ internal class WfFormatTaskTableReaderTest { @Test fun testJobsInvalidChildType() { - val content = """ - { - "name": "eager-nextflow-chameleon", - "workflow": { - "jobs": [1] + val content = + """ + { + "name": "eager-nextflow-chameleon", + "workflow": { + "jobs": [1] + } } - } - """.trimIndent() + """.trimIndent() val parser = factory.createParser(content) val reader = WfFormatTaskTableReader(parser) @@ -196,18 +203,19 @@ internal class WfFormatTaskTableReaderTest { @Test fun testJobsValidChildType() { - val content = """ - { - "name": "eager-nextflow-chameleon", - "workflow": { - "jobs": [ - { - "name": "test" - } - ] + val content = + """ + { + "name": "eager-nextflow-chameleon", + "workflow": { + "jobs": [ + { + "name": "test" + } + ] + } } - } - """.trimIndent() + """.trimIndent() val parser = factory.createParser(content) val reader = WfFormatTaskTableReader(parser) @@ -220,19 +228,20 @@ internal class WfFormatTaskTableReaderTest { @Test fun testJobsInvalidParents() { - val content = """ - { - "name": "eager-nextflow-chameleon", - "workflow": { - "jobs": [ - { - "name": "test", - "parents": 1, - } - ] + val content = + """ + { + "name": "eager-nextflow-chameleon", + "workflow": { + "jobs": [ + { + "name": "test", + "parents": 1, + } + ] + } } - } - """.trimIndent() + """.trimIndent() val parser = factory.createParser(content) val reader = WfFormatTaskTableReader(parser) @@ -243,19 +252,20 @@ internal class WfFormatTaskTableReaderTest { @Test fun testJobsInvalidParentsItem() { - val content = """ - { - "name": "eager-nextflow-chameleon", - "workflow": { - "jobs": [ - { - "name": "test", - "parents": [1], - } - ] + val content = + """ + { + "name": "eager-nextflow-chameleon", + "workflow": { + "jobs": [ + { + "name": "test", + "parents": [1], + } + ] + } } - } - """.trimIndent() + """.trimIndent() val parser = factory.createParser(content) val reader = WfFormatTaskTableReader(parser) @@ -266,19 +276,20 @@ internal class WfFormatTaskTableReaderTest { @Test fun testJobsValidParents() { - val content = """ - { - "name": "eager-nextflow-chameleon", - "workflow": { - "jobs": [ - { - "name": "test", - "parents": ["1"] - } - ] + val content = + """ + { + "name": "eager-nextflow-chameleon", + "workflow": { + "jobs": [ + { + "name": "test", + "parents": ["1"] + } + ] + } } - } - """.trimIndent() + """.trimIndent() val parser = factory.createParser(content) val reader = WfFormatTaskTableReader(parser) @@ -291,19 +302,20 @@ internal class WfFormatTaskTableReaderTest { @Test fun testJobsInvalidSecondEntry() { - val content = """ - { - "workflow": { - "jobs": [ - { - "name": "test", - "parents": ["1"] - }, - "test" - ] + val content = + """ + { + "workflow": { + "jobs": [ + { + "name": "test", + "parents": ["1"] + }, + "test" + ] + } } - } - """.trimIndent() + """.trimIndent() val parser = factory.createParser(content) val reader = WfFormatTaskTableReader(parser) @@ -315,25 +327,26 @@ internal class WfFormatTaskTableReaderTest { @Test fun testDuplicateJobsArray() { - val content = """ - { - "name": "eager-nextflow-chameleon", - "workflow": { - "jobs": [ - { - "name": "test", - "parents": ["1"] - } - ], - "jobs": [ - { - "name": "test2", - "parents": ["test"] - } - ] + val content = + """ + { + "name": "eager-nextflow-chameleon", + "workflow": { + "jobs": [ + { + "name": "test", + "parents": ["1"] + } + ], + "jobs": [ + { + "name": "test2", + "parents": ["test"] + } + ] + } } - } - """.trimIndent() + """.trimIndent() val parser = factory.createParser(content) val reader = WfFormatTaskTableReader(parser) diff --git a/opendc-trace/opendc-trace-wfformat/src/test/kotlin/org/opendc/trace/wfformat/WfFormatTraceFormatTest.kt b/opendc-trace/opendc-trace-wfformat/src/test/kotlin/org/opendc/trace/wfformat/WfFormatTraceFormatTest.kt index 75f4b413c..80a9d80e2 100644 --- a/opendc-trace/opendc-trace-wfformat/src/test/kotlin/org/opendc/trace/wfformat/WfFormatTraceFormatTest.kt +++ b/opendc-trace/opendc-trace-wfformat/src/test/kotlin/org/opendc/trace/wfformat/WfFormatTraceFormatTest.kt @@ -81,7 +81,7 @@ class WfFormatTraceFormatTest { { assertEquals("makebwaindex_mammoth_mt_krause.fasta", reader.getString(TASK_ID)) }, { assertEquals("eager-nextflow-chameleon", reader.getString(TASK_WORKFLOW_ID)) }, { assertEquals(172000, reader.getDuration(TASK_RUNTIME)?.toMillis()) }, - { assertEquals(emptySet(), reader.getSet(TASK_PARENTS, String::class.java)) } + { assertEquals(emptySet(), reader.getSet(TASK_PARENTS, String::class.java)) }, ) assertAll( @@ -89,7 +89,7 @@ class WfFormatTraceFormatTest { { assertEquals("makeseqdict_mammoth_mt_krause.fasta", reader.getString(TASK_ID)) }, { assertEquals("eager-nextflow-chameleon", reader.getString(TASK_WORKFLOW_ID)) }, { assertEquals(175000, reader.getDuration(TASK_RUNTIME)?.toMillis()) }, - { assertEquals(setOf("makebwaindex_mammoth_mt_krause.fasta"), reader.getSet(TASK_PARENTS, String::class.java)) } + { assertEquals(setOf("makebwaindex_mammoth_mt_krause.fasta"), reader.getSet(TASK_PARENTS, String::class.java)) }, ) reader.close() diff --git a/opendc-trace/opendc-trace-wtf/build.gradle.kts b/opendc-trace/opendc-trace-wtf/build.gradle.kts index 599087e11..a3119e5e9 100644 --- a/opendc-trace/opendc-trace-wtf/build.gradle.kts +++ b/opendc-trace/opendc-trace-wtf/build.gradle.kts @@ -22,7 +22,7 @@ description = "Support for Workflow Trace Format (WTF) traces in OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/WtfTaskTableReader.kt b/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/WtfTaskTableReader.kt index 73c1b8a9a..955823884 100644 --- a/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/WtfTaskTableReader.kt +++ b/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/WtfTaskTableReader.kt @@ -62,38 +62,38 @@ internal class WtfTaskTableReader(private val reader: LocalParquetReader) } } - private val COL_ID = 0 - private val COL_WORKFLOW_ID = 1 - private val COL_SUBMIT_TIME = 2 - private val COL_WAIT_TIME = 3 - private val COL_RUNTIME = 4 - private val COL_REQ_NCPUS = 5 - private val COL_PARENTS = 6 - private val COL_CHILDREN = 7 - private val COL_GROUP_ID = 8 - private val COL_USER_ID = 9 - - private val TYPE_PARENTS = TableColumnType.Set(TableColumnType.String) - private val TYPE_CHILDREN = TableColumnType.Set(TableColumnType.String) + private val colID = 0 + private val colWorkflowID = 1 + private val colSubmitTime = 2 + private val colWaitTime = 3 + private val colRuntime = 4 + private val colReqNcpus = 5 + private val colParents = 6 + private val colChildren = 7 + private val colGroupID = 8 + private val colUserID = 9 + + private val typeParents = TableColumnType.Set(TableColumnType.String) + private val typeChildren = TableColumnType.Set(TableColumnType.String) override fun resolve(name: String): Int { return when (name) { - TASK_ID -> COL_ID - TASK_WORKFLOW_ID -> COL_WORKFLOW_ID - TASK_SUBMIT_TIME -> COL_SUBMIT_TIME - TASK_WAIT_TIME -> COL_WAIT_TIME - TASK_RUNTIME -> COL_RUNTIME - TASK_REQ_NCPUS -> COL_REQ_NCPUS - TASK_PARENTS -> COL_PARENTS - TASK_CHILDREN -> COL_CHILDREN - TASK_GROUP_ID -> COL_GROUP_ID - TASK_USER_ID -> COL_USER_ID + TASK_ID -> colID + TASK_WORKFLOW_ID -> colWorkflowID + TASK_SUBMIT_TIME -> colSubmitTime + TASK_WAIT_TIME -> colWaitTime + TASK_RUNTIME -> colRuntime + TASK_REQ_NCPUS -> colReqNcpus + TASK_PARENTS -> colParents + TASK_CHILDREN -> colChildren + TASK_GROUP_ID -> colGroupID + TASK_USER_ID -> colUserID else -> -1 } } override fun isNull(index: Int): Boolean { - require(index in COL_ID..COL_USER_ID) { "Invalid column index" } + require(index in colID..colUserID) { "Invalid column index" } return false } @@ -105,9 +105,9 @@ internal class WtfTaskTableReader(private val reader: LocalParquetReader) val record = checkNotNull(record) { "Reader in invalid state" } return when (index) { - COL_REQ_NCPUS -> record.requestedCpus - COL_GROUP_ID -> record.groupId - COL_USER_ID -> record.userId + colReqNcpus -> record.requestedCpus + colGroupID -> record.groupId + colUserID -> record.userId else -> throw IllegalArgumentException("Invalid column") } } @@ -127,8 +127,8 @@ internal class WtfTaskTableReader(private val reader: LocalParquetReader) override fun getString(index: Int): String { val record = checkNotNull(record) { "Reader in invalid state" } return when (index) { - COL_ID -> record.id - COL_WORKFLOW_ID -> record.workflowId + colID -> record.id + colWorkflowID -> record.workflowId else -> throw IllegalArgumentException("Invalid column") } } @@ -140,7 +140,7 @@ internal class WtfTaskTableReader(private val reader: LocalParquetReader) override fun getInstant(index: Int): Instant { val record = checkNotNull(record) { "Reader in invalid state" } return when (index) { - COL_SUBMIT_TIME -> record.submitTime + colSubmitTime -> record.submitTime else -> throw IllegalArgumentException("Invalid column") } } @@ -148,26 +148,36 @@ internal class WtfTaskTableReader(private val reader: LocalParquetReader) override fun getDuration(index: Int): Duration { val record = checkNotNull(record) { "Reader in invalid state" } return when (index) { - COL_WAIT_TIME -> record.waitTime - COL_RUNTIME -> record.runtime + colWaitTime -> record.waitTime + colRuntime -> record.runtime else -> throw IllegalArgumentException("Invalid column") } } - override fun getList(index: Int, elementType: Class): List? { + override fun getList( + index: Int, + elementType: Class, + ): List? { throw IllegalArgumentException("Invalid column") } - override fun getSet(index: Int, elementType: Class): Set? { + override fun getSet( + index: Int, + elementType: Class, + ): Set? { val record = checkNotNull(record) { "Reader in invalid state" } return when (index) { - COL_PARENTS -> TYPE_PARENTS.convertTo(record.parents, elementType) - COL_CHILDREN -> TYPE_CHILDREN.convertTo(record.children, elementType) + colParents -> typeParents.convertTo(record.parents, elementType) + colChildren -> typeChildren.convertTo(record.children, elementType) else -> throw IllegalArgumentException("Invalid column") } } - override fun getMap(index: Int, keyType: Class, valueType: Class): Map? { + override fun getMap( + index: Int, + keyType: Class, + valueType: Class, + ): Map? { throw IllegalArgumentException("Invalid column") } diff --git a/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/WtfTraceFormat.kt b/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/WtfTraceFormat.kt index c25b512c8..1386d2eff 100644 --- a/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/WtfTraceFormat.kt +++ b/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/WtfTraceFormat.kt @@ -55,27 +55,35 @@ public class WtfTraceFormat : TraceFormat { override fun getTables(path: Path): List = listOf(TABLE_TASKS) - override fun getDetails(path: Path, table: String): TableDetails { + override fun getDetails( + path: Path, + table: String, + ): TableDetails { return when (table) { - TABLE_TASKS -> TableDetails( - listOf( - TableColumn(TASK_ID, TableColumnType.String), - TableColumn(TASK_WORKFLOW_ID, TableColumnType.String), - TableColumn(TASK_SUBMIT_TIME, TableColumnType.Instant), - TableColumn(TASK_WAIT_TIME, TableColumnType.Duration), - TableColumn(TASK_RUNTIME, TableColumnType.Duration), - TableColumn(TASK_REQ_NCPUS, TableColumnType.Int), - TableColumn(TASK_PARENTS, TableColumnType.Set(TableColumnType.String)), - TableColumn(TASK_CHILDREN, TableColumnType.Set(TableColumnType.String)), - TableColumn(TASK_GROUP_ID, TableColumnType.Int), - TableColumn(TASK_USER_ID, TableColumnType.Int) + TABLE_TASKS -> + TableDetails( + listOf( + TableColumn(TASK_ID, TableColumnType.String), + TableColumn(TASK_WORKFLOW_ID, TableColumnType.String), + TableColumn(TASK_SUBMIT_TIME, TableColumnType.Instant), + TableColumn(TASK_WAIT_TIME, TableColumnType.Duration), + TableColumn(TASK_RUNTIME, TableColumnType.Duration), + TableColumn(TASK_REQ_NCPUS, TableColumnType.Int), + TableColumn(TASK_PARENTS, TableColumnType.Set(TableColumnType.String)), + TableColumn(TASK_CHILDREN, TableColumnType.Set(TableColumnType.String)), + TableColumn(TASK_GROUP_ID, TableColumnType.Int), + TableColumn(TASK_USER_ID, TableColumnType.Int), + ), ) - ) else -> throw IllegalArgumentException("Table $table not supported") } } - override fun newReader(path: Path, table: String, projection: List?): TableReader { + override fun newReader( + path: Path, + table: String, + projection: List?, + ): TableReader { return when (table) { TABLE_TASKS -> { val reader = LocalParquetReader(path.resolve("tasks/schema-1.0"), TaskReadSupport(projection), strictTyping = false) @@ -85,7 +93,10 @@ public class WtfTraceFormat : TraceFormat { } } - override fun newWriter(path: Path, table: String): TableWriter { + override fun newWriter( + path: Path, + table: String, + ): TableWriter { throw UnsupportedOperationException("Writing not supported for this format") } } diff --git a/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/parquet/Task.kt b/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/parquet/Task.kt index 71557f965..a1db0cab7 100644 --- a/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/parquet/Task.kt +++ b/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/parquet/Task.kt @@ -38,5 +38,5 @@ internal data class Task( val groupId: Int, val userId: Int, val parents: Set, - val children: Set + val children: Set, ) diff --git a/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/parquet/TaskReadSupport.kt b/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/parquet/TaskReadSupport.kt index 33be38d4b..1f9c506d9 100644 --- a/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/parquet/TaskReadSupport.kt +++ b/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/parquet/TaskReadSupport.kt @@ -51,18 +51,19 @@ internal class TaskReadSupport(private val projection: List?) : ReadSupp /** * Mapping of table columns to their Parquet column names. */ - private val colMap = mapOf( - TASK_ID to "id", - TASK_WORKFLOW_ID to "workflow_id", - TASK_SUBMIT_TIME to "ts_submit", - TASK_WAIT_TIME to "wait_time", - TASK_RUNTIME to "runtime", - TASK_REQ_NCPUS to "resource_amount_requested", - TASK_PARENTS to "parents", - TASK_CHILDREN to "children", - TASK_GROUP_ID to "group_id", - TASK_USER_ID to "user_id" - ) + private val colMap = + mapOf( + TASK_ID to "id", + TASK_WORKFLOW_ID to "workflow_id", + TASK_SUBMIT_TIME to "ts_submit", + TASK_WAIT_TIME to "wait_time", + TASK_RUNTIME to "runtime", + TASK_REQ_NCPUS to "resource_amount_requested", + TASK_PARENTS to "parents", + TASK_CHILDREN to "children", + TASK_GROUP_ID to "group_id", + TASK_USER_ID to "user_id", + ) override fun init(context: InitContext): ReadContext { val projectedSchema = @@ -87,7 +88,7 @@ internal class TaskReadSupport(private val projection: List?) : ReadSupp configuration: Configuration, keyValueMetaData: Map, fileSchema: MessageType, - readContext: ReadContext + readContext: ReadContext, ): RecordMaterializer = TaskRecordMaterializer(readContext.requestedSchema) companion object { @@ -95,52 +96,53 @@ internal class TaskReadSupport(private val projection: List?) : ReadSupp * Parquet read schema for the "tasks" table in the trace. */ @JvmStatic - val READ_SCHEMA: MessageType = Types.buildMessage() - .addFields( - Types - .optional(PrimitiveType.PrimitiveTypeName.INT64) - .named("id"), - Types - .optional(PrimitiveType.PrimitiveTypeName.INT64) - .named("workflow_id"), - Types - .optional(PrimitiveType.PrimitiveTypeName.INT64) - .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) - .named("ts_submit"), - Types - .optional(PrimitiveType.PrimitiveTypeName.INT64) - .named("wait_time"), - Types - .optional(PrimitiveType.PrimitiveTypeName.INT64) - .named("runtime"), - Types - .optional(PrimitiveType.PrimitiveTypeName.DOUBLE) - .named("resource_amount_requested"), - Types - .optional(PrimitiveType.PrimitiveTypeName.INT32) - .named("user_id"), - Types - .optional(PrimitiveType.PrimitiveTypeName.INT32) - .named("group_id"), - Types - .buildGroup(Type.Repetition.OPTIONAL) - .addField( - Types.repeatedGroup() - .addField(Types.optional(PrimitiveType.PrimitiveTypeName.INT64).named("item")) - .named("list") - ) - .`as`(LogicalTypeAnnotation.listType()) - .named("children"), - Types - .buildGroup(Type.Repetition.OPTIONAL) - .addField( - Types.repeatedGroup() - .addField(Types.optional(PrimitiveType.PrimitiveTypeName.INT64).named("item")) - .named("list") - ) - .`as`(LogicalTypeAnnotation.listType()) - .named("parents") - ) - .named("task") + val READ_SCHEMA: MessageType = + Types.buildMessage() + .addFields( + Types + .optional(PrimitiveType.PrimitiveTypeName.INT64) + .named("id"), + Types + .optional(PrimitiveType.PrimitiveTypeName.INT64) + .named("workflow_id"), + Types + .optional(PrimitiveType.PrimitiveTypeName.INT64) + .`as`(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.MILLIS)) + .named("ts_submit"), + Types + .optional(PrimitiveType.PrimitiveTypeName.INT64) + .named("wait_time"), + Types + .optional(PrimitiveType.PrimitiveTypeName.INT64) + .named("runtime"), + Types + .optional(PrimitiveType.PrimitiveTypeName.DOUBLE) + .named("resource_amount_requested"), + Types + .optional(PrimitiveType.PrimitiveTypeName.INT32) + .named("user_id"), + Types + .optional(PrimitiveType.PrimitiveTypeName.INT32) + .named("group_id"), + Types + .buildGroup(Type.Repetition.OPTIONAL) + .addField( + Types.repeatedGroup() + .addField(Types.optional(PrimitiveType.PrimitiveTypeName.INT64).named("item")) + .named("list"), + ) + .`as`(LogicalTypeAnnotation.listType()) + .named("children"), + Types + .buildGroup(Type.Repetition.OPTIONAL) + .addField( + Types.repeatedGroup() + .addField(Types.optional(PrimitiveType.PrimitiveTypeName.INT64).named("item")) + .named("list"), + ) + .`as`(LogicalTypeAnnotation.listType()) + .named("parents"), + ) + .named("task") } } diff --git a/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/parquet/TaskRecordMaterializer.kt b/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/parquet/TaskRecordMaterializer.kt index 055be0c37..412a4f8b9 100644 --- a/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/parquet/TaskRecordMaterializer.kt +++ b/opendc-trace/opendc-trace-wtf/src/main/kotlin/org/opendc/trace/wtf/parquet/TaskRecordMaterializer.kt @@ -39,102 +39,113 @@ internal class TaskRecordMaterializer(schema: MessageType) : RecordMaterializer< /** * State of current record being read. */ - private var _id = "" - private var _workflowId = "" - private var _submitTime = Instant.MIN - private var _waitTime = Duration.ZERO - private var _runtime = Duration.ZERO - private var _requestedCpus = 0 - private var _groupId = 0 - private var _userId = 0 - private var _parents = mutableSetOf() - private var _children = mutableSetOf() + private var localID = "" + private var localWorkflowID = "" + private var localSubmitTime = Instant.MIN + private var localWaitTime = Duration.ZERO + private var localRuntime = Duration.ZERO + private var localRequestedCpus = 0 + private var localGroupId = 0 + private var localUserId = 0 + private var localParents = mutableSetOf() + private var localChildren = mutableSetOf() /** * Root converter for the record. */ - private val root = object : GroupConverter() { - /** - * The converters for the columns of the schema. - */ - private val converters = schema.fields.map { type -> - when (type.name) { - "id" -> object : PrimitiveConverter() { - override fun addLong(value: Long) { - _id = value.toString() + private val root = + object : GroupConverter() { + /** + * The converters for the columns of the schema. + */ + private val converters = + schema.fields.map { type -> + when (type.name) { + "id" -> + object : PrimitiveConverter() { + override fun addLong(value: Long) { + localID = value.toString() + } + } + "workflow_id" -> + object : PrimitiveConverter() { + override fun addLong(value: Long) { + localWorkflowID = value.toString() + } + } + "ts_submit" -> + object : PrimitiveConverter() { + override fun addLong(value: Long) { + localSubmitTime = Instant.ofEpochMilli(value) + } + } + "wait_time" -> + object : PrimitiveConverter() { + override fun addLong(value: Long) { + localWaitTime = Duration.ofMillis(value) + } + } + "runtime" -> + object : PrimitiveConverter() { + override fun addLong(value: Long) { + localRuntime = Duration.ofMillis(value) + } + } + "resource_amount_requested" -> + object : PrimitiveConverter() { + override fun addDouble(value: Double) { + localRequestedCpus = value.roundToInt() + } + } + "group_id" -> + object : PrimitiveConverter() { + override fun addInt(value: Int) { + localGroupId = value + } + } + "user_id" -> + object : PrimitiveConverter() { + override fun addInt(value: Int) { + localUserId = value + } + } + "children" -> RelationConverter(localChildren) + "parents" -> RelationConverter(localParents) + else -> error("Unknown column $type") } } - "workflow_id" -> object : PrimitiveConverter() { - override fun addLong(value: Long) { - _workflowId = value.toString() - } - } - "ts_submit" -> object : PrimitiveConverter() { - override fun addLong(value: Long) { - _submitTime = Instant.ofEpochMilli(value) - } - } - "wait_time" -> object : PrimitiveConverter() { - override fun addLong(value: Long) { - _waitTime = Duration.ofMillis(value) - } - } - "runtime" -> object : PrimitiveConverter() { - override fun addLong(value: Long) { - _runtime = Duration.ofMillis(value) - } - } - "resource_amount_requested" -> object : PrimitiveConverter() { - override fun addDouble(value: Double) { - _requestedCpus = value.roundToInt() - } - } - "group_id" -> object : PrimitiveConverter() { - override fun addInt(value: Int) { - _groupId = value - } - } - "user_id" -> object : PrimitiveConverter() { - override fun addInt(value: Int) { - _userId = value - } - } - "children" -> RelationConverter(_children) - "parents" -> RelationConverter(_parents) - else -> error("Unknown column $type") - } - } - override fun start() { - _id = "" - _workflowId = "" - _submitTime = Instant.MIN - _waitTime = Duration.ZERO - _runtime = Duration.ZERO - _requestedCpus = 0 - _groupId = 0 - _userId = 0 - _parents.clear() - _children.clear() - } + override fun start() { + localID = "" + localWorkflowID = "" + localSubmitTime = Instant.MIN + localWaitTime = Duration.ZERO + localRuntime = Duration.ZERO + localRequestedCpus = 0 + localGroupId = 0 + localUserId = 0 + localParents.clear() + localChildren.clear() + } - override fun end() {} + override fun end() {} - override fun getConverter(fieldIndex: Int): Converter = converters[fieldIndex] - } + override fun getConverter(fieldIndex: Int): Converter = converters[fieldIndex] + } - override fun getCurrentRecord(): Task = Task( - _id, - _workflowId, - _submitTime, - _waitTime, - _runtime, - _requestedCpus, - _groupId, - _userId, - _parents.toSet(), - _children.toSet() - ) + override fun getCurrentRecord(): Task = + Task( + localID, + localWorkflowID, + localSubmitTime, + localWaitTime, + localRuntime, + localRequestedCpus, + localGroupId, + localUserId, + localParents.toSet(), + localChildren.toSet(), + ) override fun getRootConverter(): GroupConverter = root @@ -142,25 +153,28 @@ internal class TaskRecordMaterializer(schema: MessageType) : RecordMaterializer< * Helper class to convert parent and child relations and add them to [relations]. */ private class RelationConverter(private val relations: MutableSet) : GroupConverter() { - private val entryConverter = object : PrimitiveConverter() { - override fun addLong(value: Long) { - relations.add(value.toString()) - } + private val entryConverter = + object : PrimitiveConverter() { + override fun addLong(value: Long) { + relations.add(value.toString()) + } - override fun addDouble(value: Double) { - relations.add(value.roundToLong().toString()) + override fun addDouble(value: Double) { + relations.add(value.roundToLong().toString()) + } } - } - private val listConverter = object : GroupConverter() { - override fun getConverter(fieldIndex: Int): Converter { - require(fieldIndex == 0) - return entryConverter - } + private val listConverter = + object : GroupConverter() { + override fun getConverter(fieldIndex: Int): Converter { + require(fieldIndex == 0) + return entryConverter + } - override fun start() {} - override fun end() {} - } + override fun start() {} + + override fun end() {} + } override fun getConverter(fieldIndex: Int): Converter { require(fieldIndex == 0) @@ -168,6 +182,7 @@ internal class TaskRecordMaterializer(schema: MessageType) : RecordMaterializer< } override fun start() {} + override fun end() {} } } diff --git a/opendc-trace/opendc-trace-wtf/src/test/kotlin/org/opendc/trace/wtf/WtfTraceFormatTest.kt b/opendc-trace/opendc-trace-wtf/src/test/kotlin/org/opendc/trace/wtf/WtfTraceFormatTest.kt index 0457098c4..ad49cce0c 100644 --- a/opendc-trace/opendc-trace-wtf/src/test/kotlin/org/opendc/trace/wtf/WtfTraceFormatTest.kt +++ b/opendc-trace/opendc-trace-wtf/src/test/kotlin/org/opendc/trace/wtf/WtfTraceFormatTest.kt @@ -87,9 +87,9 @@ class WtfTraceFormatTest { { assertEquals( setOf("584055316413447529", "133113685133695608", "1008582348422865408"), - reader.getSet(TASK_PARENTS, String::class.java) + reader.getSet(TASK_PARENTS, String::class.java), ) - } + }, ) assertAll( @@ -101,9 +101,9 @@ class WtfTraceFormatTest { { assertEquals( setOf("584055316413447529", "133113685133695608", "1008582348422865408"), - reader.getSet(TASK_PARENTS, String::class.java) + reader.getSet(TASK_PARENTS, String::class.java), ) - } + }, ) reader.close() diff --git a/opendc-web/opendc-web-client/build.gradle.kts b/opendc-web/opendc-web-client/build.gradle.kts index 77a0afff7..55228ef94 100644 --- a/opendc-web/opendc-web-client/build.gradle.kts +++ b/opendc-web/opendc-web-client/build.gradle.kts @@ -22,7 +22,7 @@ description = "Client for the OpenDC web API" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/PortfolioResource.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/PortfolioResource.kt index 399804e85..f0e499732 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/PortfolioResource.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/PortfolioResource.kt @@ -40,19 +40,28 @@ public class PortfolioResource internal constructor(private val client: Transpor /** * Obtain the portfolio for [project] with [number]. */ - public fun get(project: Long, number: Int): Portfolio? = client.get("projects/$project/portfolios/$number") + public fun get( + project: Long, + number: Int, + ): Portfolio? = client.get("projects/$project/portfolios/$number") /** * Create a new portfolio for [project] with the specified [request]. */ - public fun create(project: Long, request: Portfolio.Create): Portfolio { + public fun create( + project: Long, + request: Portfolio.Create, + ): Portfolio { return checkNotNull(client.post("projects/$project/portfolios", request)) } /** * Delete the portfolio for [project] with [index]. */ - public fun delete(project: Long, index: Int): Portfolio { + public fun delete( + project: Long, + index: Int, + ): Portfolio { return requireNotNull(client.delete("projects/$project/portfolios/$index")) { "Unknown portfolio $index" } } } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/ScenarioResource.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/ScenarioResource.kt index 7055e7527..d43515a95 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/ScenarioResource.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/ScenarioResource.kt @@ -40,24 +40,37 @@ public class ScenarioResource internal constructor(private val client: Transport /** * List all scenarios that belong to the specified [portfolioNumber]. */ - public fun getAll(project: Long, portfolioNumber: Int): List = client.get("projects/$project/portfolios/$portfolioNumber/scenarios") ?: emptyList() + public fun getAll( + project: Long, + portfolioNumber: Int, + ): List = client.get("projects/$project/portfolios/$portfolioNumber/scenarios") ?: emptyList() /** * Obtain the scenario for [project] with [index]. */ - public fun get(project: Long, index: Int): Scenario? = client.get("projects/$project/scenarios/$index") + public fun get( + project: Long, + index: Int, + ): Scenario? = client.get("projects/$project/scenarios/$index") /** * Create a new scenario for [portfolio][portfolioNumber] with the specified [request]. */ - public fun create(project: Long, portfolioNumber: Int, request: Scenario.Create): Scenario { + public fun create( + project: Long, + portfolioNumber: Int, + request: Scenario.Create, + ): Scenario { return checkNotNull(client.post("projects/$project/portfolios/$portfolioNumber", request)) } /** * Delete the scenario for [project] with [index]. */ - public fun delete(project: Long, index: Int): Scenario { + public fun delete( + project: Long, + index: Int, + ): Scenario { return requireNotNull(client.delete("projects/$project/scenarios/$index")) { "Unknown scenario $index" } } } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/TopologyResource.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/TopologyResource.kt index c37ae8daa..34f5ea1bf 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/TopologyResource.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/TopologyResource.kt @@ -41,26 +41,39 @@ public class TopologyResource internal constructor(private val client: Transport /** * Obtain the topology for [project] with [index]. */ - public fun get(project: Long, index: Int): Topology? = client.get("projects/$project/topologies/$index") + public fun get( + project: Long, + index: Int, + ): Topology? = client.get("projects/$project/topologies/$index") /** * Create a new topology for [project] with [request]. */ - public fun create(project: Long, request: Topology.Create): Topology { + public fun create( + project: Long, + request: Topology.Create, + ): Topology { return checkNotNull(client.post("projects/$project/topologies", request)) } /** * Update the topology with [index] for [project] using the specified [request]. */ - public fun update(project: Long, index: Int, request: Topology.Update): Topology? { + public fun update( + project: Long, + index: Int, + request: Topology.Update, + ): Topology? { return client.put("projects/$project/topologies/$index", request) } /** * Delete the topology for [project] with [index]. */ - public fun delete(project: Long, index: Long): Topology { + public fun delete( + project: Long, + index: Long, + ): Topology { return requireNotNull(client.delete("projects/$project/topologies/$index")) { "Unknown topology $index" } } } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/auth/OpenIdAuthController.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/auth/OpenIdAuthController.kt index 7f9cbacd0..707dc138c 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/auth/OpenIdAuthController.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/auth/OpenIdAuthController.kt @@ -41,39 +41,40 @@ public class OpenIdAuthController( private val clientId: String, private val clientSecret: String, private val audience: String = "https://api.opendc.org/v2/", - private val client: HttpClient = HttpClient.newHttpClient() + private val client: HttpClient = HttpClient.newHttpClient(), ) : AuthController { /** * The Jackson object mapper to convert messages from/to JSON. */ - private val mapper = jacksonObjectMapper() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + private val mapper = + jacksonObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) /** * The cached [OpenIdConfiguration]. */ private val openidConfig: OpenIdConfiguration get() { - var openidConfig = _openidConfig + var openidConfig = localOpenidConfig if (openidConfig == null) { openidConfig = requestConfig() - _openidConfig = openidConfig + localOpenidConfig = openidConfig } return openidConfig } - private var _openidConfig: OpenIdConfiguration? = null + private var localOpenidConfig: OpenIdConfiguration? = null /** * The cached OAuth token. */ - private var _token: OAuthTokenResponse? = null + private var localToken: OAuthTokenResponse? = null override fun injectToken(request: HttpRequest.Builder) { - var token = _token + var token = localToken if (token == null) { token = requestToken() - _token = token + localToken = token } request.header("Authorization", "Bearer ${token.accessToken}") @@ -83,22 +84,23 @@ public class OpenIdAuthController( * Refresh the current access token. */ override fun refreshToken() { - val refreshToken = _token?.refreshToken + val refreshToken = localToken?.refreshToken if (refreshToken == null) { requestToken() return } - _token = refreshToken(openidConfig, refreshToken) + localToken = refreshToken(openidConfig, refreshToken) } /** * Request the OpenID configuration from the chosen auth domain */ private fun requestConfig(): OpenIdConfiguration { - val request = HttpRequest.newBuilder(URI("https://$domain/.well-known/openid-configuration")) - .GET() - .build() + val request = + HttpRequest.newBuilder(URI("https://$domain/.well-known/openid-configuration")) + .GET() + .build() val response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()) return mapper.readValue(response.body()) } @@ -108,10 +110,11 @@ public class OpenIdAuthController( */ private fun requestToken(openidConfig: OpenIdConfiguration): OAuthTokenResponse { val body = OAuthTokenRequest.ClientCredentials(audience, clientId, clientSecret) - val request = HttpRequest.newBuilder(openidConfig.tokenEndpoint) - .header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) - .build() + val request = + HttpRequest.newBuilder(openidConfig.tokenEndpoint) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) + .build() val response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()) return mapper.readValue(response.body()) } @@ -119,12 +122,16 @@ public class OpenIdAuthController( /** * Helper method to refresh the auth token. */ - private fun refreshToken(openidConfig: OpenIdConfiguration, refreshToken: String): OAuthTokenResponse { + private fun refreshToken( + openidConfig: OpenIdConfiguration, + refreshToken: String, + ): OAuthTokenResponse { val body = OAuthTokenRequest.RefreshToken(refreshToken, clientId, clientSecret) - val request = HttpRequest.newBuilder(openidConfig.tokenEndpoint) - .header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) - .build() + val request = + HttpRequest.newBuilder(openidConfig.tokenEndpoint) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) + .build() val response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()) return mapper.readValue(response.body()) } @@ -134,7 +141,7 @@ public class OpenIdAuthController( */ private fun requestToken(): OAuthTokenResponse { val token = requestToken(openidConfig) - _token = token + localToken = token return token } } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/ClientUtils.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/ClientUtils.kt index 29cf09dcb..1ffaa602a 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/ClientUtils.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/ClientUtils.kt @@ -35,14 +35,20 @@ internal inline fun TransportClient.get(path: String): T? { /** * Perform a POST request for resource at [path] and convert to type [T]. */ -internal inline fun TransportClient.post(path: String, body: B): T? { +internal inline fun TransportClient.post( + path: String, + body: B, +): T? { return post(path, body, object : TypeReference() {}) } /** * Perform a PUT request for resource at [path] and convert to type [T]. */ -internal inline fun TransportClient.put(path: String, body: B): T? { +internal inline fun TransportClient.put( + path: String, + body: B, +): T? { return put(path, body, object : TypeReference() {}) } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenRequest.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenRequest.kt index 253419954..1bb06c8fd 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenRequest.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenRequest.kt @@ -33,8 +33,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo @JsonSubTypes( value = [ JsonSubTypes.Type(value = OAuthTokenRequest.ClientCredentials::class, name = "client_credentials"), - JsonSubTypes.Type(value = OAuthTokenRequest.RefreshToken::class, name = "refresh_token") - ] + JsonSubTypes.Type(value = OAuthTokenRequest.RefreshToken::class, name = "refresh_token"), + ], ) internal sealed class OAuthTokenRequest { /** @@ -45,7 +45,7 @@ internal sealed class OAuthTokenRequest { @JsonProperty("client_id") val clientId: String, @JsonProperty("client_secret") - val clientSecret: String + val clientSecret: String, ) : OAuthTokenRequest() /** @@ -57,6 +57,6 @@ internal sealed class OAuthTokenRequest { @JsonProperty("client_id") val clientId: String, @JsonProperty("client_secret") - val clientSecret: String + val clientSecret: String, ) : OAuthTokenRequest() } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenResponse.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenResponse.kt index cd5ccab07..76fe007c2 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenResponse.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenResponse.kt @@ -36,5 +36,5 @@ internal data class OAuthTokenResponse( val tokenType: String, val scope: String = "", @JsonProperty("expires_in") - val expiresIn: Long + val expiresIn: Long, ) diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OpenIdConfiguration.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OpenIdConfiguration.kt index 23fbf3680..eac1607e9 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OpenIdConfiguration.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OpenIdConfiguration.kt @@ -39,5 +39,5 @@ internal data class OpenIdConfiguration( @JsonProperty("jwks_uri") val jwksUri: URI, @JsonProperty("scopes_supported") - val scopesSupported: Set + val scopesSupported: Set, ) diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/runner/JobResource.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/runner/JobResource.kt index ad3f1c9b3..e72f703c9 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/runner/JobResource.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/runner/JobResource.kt @@ -44,5 +44,8 @@ public class JobResource internal constructor(private val client: TransportClien /** * Update the job with [id]. */ - public fun update(id: Long, update: Job.Update): Job? = client.post("jobs/$id", update) + public fun update( + id: Long, + update: Job.Update, + ): Job? = client.post("jobs/$id", update) } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/HttpTransportClient.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/HttpTransportClient.kt index e407380b4..f6dca4d1b 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/HttpTransportClient.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/HttpTransportClient.kt @@ -43,23 +43,28 @@ import java.nio.file.Paths public class HttpTransportClient( private val baseUrl: URI, private val auth: AuthController?, - private val client: HttpClient = HttpClient.newHttpClient() + private val client: HttpClient = HttpClient.newHttpClient(), ) : TransportClient { /** * The Jackson object mapper to convert messages from/to JSON. */ - private val mapper = jacksonObjectMapper() - .registerModule(JavaTimeModule()) - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + private val mapper = + jacksonObjectMapper() + .registerModule(JavaTimeModule()) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) /** * Obtain a resource at [path] of [targetType]. */ - override fun get(path: String, targetType: TypeReference): T? { - val request = HttpRequest.newBuilder(buildUri(path)) - .GET() - .also { auth?.injectToken(it) } - .build() + override fun get( + path: String, + targetType: TypeReference, + ): T? { + val request = + HttpRequest.newBuilder(buildUri(path)) + .GET() + .also { auth?.injectToken(it) } + .build() val response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()) return when (val code = response.statusCode()) { @@ -81,12 +86,17 @@ public class HttpTransportClient( /** * Update a resource at [path] of [targetType]. */ - override fun post(path: String, body: B, targetType: TypeReference): T? { - val request = HttpRequest.newBuilder(buildUri(path)) - .POST(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) - .header("Content-Type", "application/json") - .also { auth?.injectToken(it) } - .build() + override fun post( + path: String, + body: B, + targetType: TypeReference, + ): T? { + val request = + HttpRequest.newBuilder(buildUri(path)) + .POST(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) + .header("Content-Type", "application/json") + .also { auth?.injectToken(it) } + .build() val response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()) return when (val code = response.statusCode()) { @@ -108,12 +118,17 @@ public class HttpTransportClient( /** * Replace a resource at [path] of [targetType]. */ - override fun put(path: String, body: B, targetType: TypeReference): T? { - val request = HttpRequest.newBuilder(buildUri(path)) - .PUT(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) - .header("Content-Type", "application/json") - .also { auth?.injectToken(it) } - .build() + override fun put( + path: String, + body: B, + targetType: TypeReference, + ): T? { + val request = + HttpRequest.newBuilder(buildUri(path)) + .PUT(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) + .header("Content-Type", "application/json") + .also { auth?.injectToken(it) } + .build() val response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()) return when (val code = response.statusCode()) { @@ -135,11 +150,15 @@ public class HttpTransportClient( /** * Delete a resource at [path] of [targetType]. */ - override fun delete(path: String, targetType: TypeReference): T? { - val request = HttpRequest.newBuilder(buildUri(path)) - .DELETE() - .also { auth?.injectToken(it) } - .build() + override fun delete( + path: String, + targetType: TypeReference, + ): T? { + val request = + HttpRequest.newBuilder(buildUri(path)) + .DELETE() + .also { auth?.injectToken(it) } + .build() val response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()) return when (val code = response.statusCode()) { diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/TransportClient.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/TransportClient.kt index af727ca78..ebf3402fa 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/TransportClient.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/TransportClient.kt @@ -31,20 +31,34 @@ public interface TransportClient { /** * Obtain a resource at [path] of [targetType]. */ - public fun get(path: String, targetType: TypeReference): T? + public fun get( + path: String, + targetType: TypeReference, + ): T? /** * Update a resource at [path] of [targetType]. */ - public fun post(path: String, body: B, targetType: TypeReference): T? + public fun post( + path: String, + body: B, + targetType: TypeReference, + ): T? /** * Replace a resource at [path] of [targetType]. */ - public fun put(path: String, body: B, targetType: TypeReference): T? + public fun put( + path: String, + body: B, + targetType: TypeReference, + ): T? /** * Delete a resource at [path] of [targetType]. */ - public fun delete(path: String, targetType: TypeReference): T? + public fun delete( + path: String, + targetType: TypeReference, + ): T? } diff --git a/opendc-web/opendc-web-proto/build.gradle.kts b/opendc-web/opendc-web-proto/build.gradle.kts index 4a566346a..9b3076553 100644 --- a/opendc-web/opendc-web-proto/build.gradle.kts +++ b/opendc-web/opendc-web-proto/build.gradle.kts @@ -22,7 +22,7 @@ description = "Web communication protocol for OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` id("org.kordamp.gradle.jandex") // Necessary for Quarkus to process annotations diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/JobState.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/JobState.kt index 38b8ca427..a8e67ec5e 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/JobState.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/JobState.kt @@ -49,5 +49,5 @@ public enum class JobState { /** * The job has failed. */ - FAILED; + FAILED, } diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Machine.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Machine.kt index f5c50cc3c..72163f519 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Machine.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Machine.kt @@ -36,5 +36,5 @@ public data class Machine( val memory: List = emptyList(), @JsonProperty("storages") val storage: List = emptyList(), - val rackId: String? = null + val rackId: String? = null, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/MemoryUnit.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/MemoryUnit.kt index 1fc604fad..00560ad69 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/MemoryUnit.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/MemoryUnit.kt @@ -30,5 +30,5 @@ public data class MemoryUnit( val name: String, val speedMbPerS: Double, val sizeMb: Double, - val energyConsumptionW: Double + val energyConsumptionW: Double, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/OperationalPhenomena.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/OperationalPhenomena.kt index f3164f647..28006d273 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/OperationalPhenomena.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/OperationalPhenomena.kt @@ -27,5 +27,5 @@ package org.opendc.web.proto */ public data class OperationalPhenomena( val failures: Boolean, - val interference: Boolean + val interference: Boolean, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/ProcessingUnit.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/ProcessingUnit.kt index 5f79d1bd6..86f405161 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/ProcessingUnit.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/ProcessingUnit.kt @@ -30,5 +30,5 @@ public data class ProcessingUnit( val name: String, val clockRateMhz: Double, val numberOfCores: Int, - val energyConsumptionW: Double + val energyConsumptionW: Double, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Rack.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Rack.kt index 131aa1846..c997e814d 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Rack.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Rack.kt @@ -30,5 +30,5 @@ public data class Rack( val name: String, val capacity: Int, val powerCapacityW: Double, - val machines: List + val machines: List, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Room.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Room.kt index c54991501..5b305168a 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Room.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Room.kt @@ -29,5 +29,5 @@ public data class Room( val id: String, val name: String, val tiles: Set, - val topologyId: String? = null + val topologyId: String? = null, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/RoomTile.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/RoomTile.kt index 53cb53cde..666d66ee9 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/RoomTile.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/RoomTile.kt @@ -30,5 +30,5 @@ public data class RoomTile( val positionX: Double, val positionY: Double, val rack: Rack? = null, - val roomId: String? = null + val roomId: String? = null, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Targets.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Targets.kt index a0100f72b..25516ff0b 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Targets.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Targets.kt @@ -22,7 +22,7 @@ package org.opendc.web.proto -import javax.validation.constraints.Min +import jakarta.validation.constraints.Min /** * The targets of a portfolio. @@ -33,5 +33,5 @@ import javax.validation.constraints.Min public data class Targets( val metrics: Set, @field:Min(1) - val repeats: Int = 1 + val repeats: Int = 1, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Trace.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Trace.kt index 1c086cd83..2952a273d 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Trace.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Trace.kt @@ -32,5 +32,5 @@ package org.opendc.web.proto public data class Trace( val id: String, val name: String, - val type: String + val type: String, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Workload.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Workload.kt index cc6e0ed8e..58daf817e 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Workload.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Workload.kt @@ -22,8 +22,8 @@ package org.opendc.web.proto -import javax.validation.constraints.DecimalMax -import javax.validation.constraints.DecimalMin +import jakarta.validation.constraints.DecimalMax +import jakarta.validation.constraints.DecimalMin /** * The workload to simulate for a scenario. @@ -39,6 +39,6 @@ public data class Workload(val trace: Trace, val samplingFraction: Double) { val trace: String, @DecimalMin(value = "0.001", message = "Sampling fraction must be non-zero") @DecimalMax(value = "1", message = "Sampling fraction cannot exceed one") - val samplingFraction: Double + val samplingFraction: Double, ) } diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Job.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Job.kt index 4f21f0bbe..346424366 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Job.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Job.kt @@ -37,7 +37,7 @@ public data class Job( val createdAt: Instant, val updatedAt: Instant, val runtime: Int, - val results: Map? = null + val results: Map? = null, ) { /** * A request to update the state of a job. diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Portfolio.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Portfolio.kt index 5faad5b3f..916d8cf07 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Portfolio.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Portfolio.kt @@ -39,5 +39,5 @@ public data class Portfolio( val id: Long, val number: Int, val name: String, - val targets: Targets + val targets: Targets, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Scenario.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Scenario.kt index aeffc4d7b..ebc10bb07 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Scenario.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Scenario.kt @@ -38,5 +38,5 @@ public data class Scenario( val workload: Workload, val topology: Topology, val phenomena: OperationalPhenomena, - val schedulerName: String + val schedulerName: String, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Topology.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Topology.kt index bc185aead..4bffdee9c 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Topology.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Topology.kt @@ -36,5 +36,5 @@ public data class Topology( val name: String, val rooms: List, val createdAt: Instant, - val updatedAt: Instant + val updatedAt: Instant, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Job.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Job.kt index de5f8de3b..dd2f209e2 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Job.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Job.kt @@ -36,5 +36,5 @@ public data class Job( val state: JobState, val createdAt: Instant, val updatedAt: Instant, - val results: Map? = null + val results: Map? = null, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Portfolio.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Portfolio.kt index 99d0f65e4..6f433a045 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Portfolio.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Portfolio.kt @@ -22,9 +22,9 @@ package org.opendc.web.proto.user +import jakarta.validation.constraints.NotBlank import org.eclipse.microprofile.openapi.annotations.media.Schema import org.opendc.web.proto.Targets -import javax.validation.constraints.NotBlank /** * A portfolio is the composition of multiple scenarios. @@ -42,7 +42,7 @@ public data class Portfolio( val project: Project, val name: String, val targets: Targets, - val scenarios: List + val scenarios: List, ) { /** * A request to create a new portfolio. @@ -51,7 +51,7 @@ public data class Portfolio( public data class Create( @field:NotBlank(message = "Name must not be empty") val name: String, - val targets: Targets + val targets: Targets, ) /** @@ -67,6 +67,6 @@ public data class Portfolio( val id: Long, val number: Int, val name: String, - val targets: Targets + val targets: Targets, ) } diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Project.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Project.kt index 3a2807ca6..635552a97 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Project.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Project.kt @@ -22,9 +22,9 @@ package org.opendc.web.proto.user +import jakarta.validation.constraints.NotBlank import org.eclipse.microprofile.openapi.annotations.media.Schema import java.time.Instant -import javax.validation.constraints.NotBlank /** * A project in OpenDC encapsulates all the datacenter designs and simulation runs for a set of users. @@ -34,11 +34,13 @@ public data class Project( val name: String, val createdAt: Instant, val updatedAt: Instant, - val role: ProjectRole + val role: ProjectRole, ) { /** * A request to create a new project. */ @Schema(name = "Project.Create") - public data class Create(@field:NotBlank(message = "Name must not be empty") val name: String) + public data class Create( + @field:NotBlank(message = "Name must not be empty") val name: String, + ) } diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/ProjectRole.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/ProjectRole.kt index ea6a30abc..0f6de1fc8 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/ProjectRole.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/ProjectRole.kt @@ -39,5 +39,5 @@ public enum class ProjectRole { /** * The user owns the project (so he can delete it). */ - OWNER + OWNER, } diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Scenario.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Scenario.kt index b9c7a4cfe..e0c790f51 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Scenario.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Scenario.kt @@ -22,10 +22,10 @@ package org.opendc.web.proto.user +import jakarta.validation.constraints.NotBlank import org.eclipse.microprofile.openapi.annotations.media.Schema import org.opendc.web.proto.OperationalPhenomena import org.opendc.web.proto.Workload -import javax.validation.constraints.NotBlank /** * A single scenario to be explored by the simulator. @@ -40,7 +40,7 @@ public data class Scenario( val topology: Topology.Summary, val phenomena: OperationalPhenomena, val schedulerName: String, - val jobs: List + val jobs: List, ) { /** * Create a new scenario. @@ -58,7 +58,7 @@ public data class Scenario( val workload: Workload.Spec, val topology: Long, val phenomena: OperationalPhenomena, - val schedulerName: String + val schedulerName: String, ) /** @@ -81,6 +81,6 @@ public data class Scenario( val topology: Topology.Summary, val phenomena: OperationalPhenomena, val schedulerName: String, - val jobs: List + val jobs: List, ) } diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Topology.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Topology.kt index 73748bb9b..0943eaf8b 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Topology.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Topology.kt @@ -22,10 +22,10 @@ package org.opendc.web.proto.user +import jakarta.validation.constraints.NotBlank import org.eclipse.microprofile.openapi.annotations.media.Schema import org.opendc.web.proto.Room import java.time.Instant -import javax.validation.constraints.NotBlank /** * Model for an OpenDC topology. @@ -37,7 +37,7 @@ public data class Topology( val name: String, val rooms: List, val createdAt: Instant, - val updatedAt: Instant + val updatedAt: Instant, ) { /** * Create a new topology for a project. @@ -46,7 +46,7 @@ public data class Topology( public data class Create( @field:NotBlank(message = "Name must not be empty") val name: String, - val rooms: List + val rooms: List, ) /** @@ -70,6 +70,6 @@ public data class Topology( val number: Int, val name: String, val createdAt: Instant, - val updatedAt: Instant + val updatedAt: Instant, ) } diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/User.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/User.kt index f18cda617..33dad4ffd 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/User.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/User.kt @@ -27,5 +27,5 @@ package org.opendc.web.proto.user */ public data class User( val userId: String, - val accounting: UserAccounting + val accounting: UserAccounting, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/UserAccounting.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/UserAccounting.kt index 2441983a5..970721eb7 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/UserAccounting.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/UserAccounting.kt @@ -30,5 +30,5 @@ import java.time.LocalDate public data class UserAccounting( val periodEnd: LocalDate, val simulationTime: Int, - val simulationTimeBudget: Int + val simulationTimeBudget: Int, ) diff --git a/opendc-web/opendc-web-runner-quarkus-deployment/build.gradle.kts b/opendc-web/opendc-web-runner-quarkus-deployment/build.gradle.kts index b3f1ec3b0..589337f43 100644 --- a/opendc-web/opendc-web-runner-quarkus-deployment/build.gradle.kts +++ b/opendc-web/opendc-web-runner-quarkus-deployment/build.gradle.kts @@ -22,7 +22,7 @@ description = "Quarkus extension for the OpenDC experiment runner" -/* Build configuration */ +// Build configuration plugins { `java-library-conventions` } diff --git a/opendc-web/opendc-web-runner-quarkus/src/main/java/org/opendc/web/runner/runtime/OpenDCRunnerRecorder.java b/opendc-web/opendc-web-runner-quarkus/src/main/java/org/opendc/web/runner/runtime/OpenDCRunnerRecorder.java index 76f2368fa..d5d524f10 100644 --- a/opendc-web/opendc-web-runner-quarkus/src/main/java/org/opendc/web/runner/runtime/OpenDCRunnerRecorder.java +++ b/opendc-web/opendc-web-runner-quarkus/src/main/java/org/opendc/web/runner/runtime/OpenDCRunnerRecorder.java @@ -25,8 +25,8 @@ import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; +import jakarta.enterprise.inject.spi.CDI; import java.io.File; -import javax.enterprise.inject.spi.CDI; import org.jboss.logging.Logger; import org.opendc.web.runner.JobManager; import org.opendc.web.runner.OpenDCRunner; diff --git a/opendc-web/opendc-web-runner/Dockerfile b/opendc-web/opendc-web-runner/Dockerfile index 3f3930556..22c36c658 100644 --- a/opendc-web/opendc-web-runner/Dockerfile +++ b/opendc-web/opendc-web-runner/Dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:17-slim +FROM openjdk:19-slim MAINTAINER OpenDC Maintainers # Obtain (cache) Gradle wrapper @@ -11,7 +11,7 @@ RUN ./gradlew --version COPY ./ /app/ RUN ./gradlew --no-daemon :opendc-web:opendc-web-runner:installDist -FROM openjdk:17-slim +FROM openjdk:19-slim COPY --from=0 /app/opendc-web/opendc-web-runner/build/install /opt/ COPY --from=0 /app/traces /opt/opendc/traces WORKDIR /opt/opendc diff --git a/opendc-web/opendc-web-runner/build.gradle.kts b/opendc-web/opendc-web-runner/build.gradle.kts index 0a6ce6589..38492929a 100644 --- a/opendc-web/opendc-web-runner/build.gradle.kts +++ b/opendc-web/opendc-web-runner/build.gradle.kts @@ -22,7 +22,7 @@ description = "Experiment runner for OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` distribution @@ -74,7 +74,7 @@ val createCli by tasks.creating(CreateStartScripts::class) { applicationName = "opendc-runner" mainClass.set("org.opendc.web.runner.cli.MainKt") classpath = cliJar.outputs.files + cliRuntimeClasspath - outputDir = project.buildDir.resolve("scripts") + outputDir = project.layout.buildDirectory.get().asFile.resolve("scripts") } distributions { diff --git a/opendc-web/opendc-web-runner/src/cli/kotlin/org/opendc/web/runner/Main.kt b/opendc-web/opendc-web-runner/src/cli/kotlin/org/opendc/web/runner/Main.kt index 299c4d09f..5d35fd98f 100644 --- a/opendc-web/opendc-web-runner/src/cli/kotlin/org/opendc/web/runner/Main.kt +++ b/opendc-web/opendc-web-runner/src/cli/kotlin/org/opendc/web/runner/Main.kt @@ -48,7 +48,7 @@ class RunnerCli : CliktCommand(name = "opendc-runner") { private val apiUrl by option( "--api-url", help = "url to the OpenDC API", - envvar = "OPENDC_API_URL" + envvar = "OPENDC_API_URL", ) .convert { URI(it) } .default(URI("https://api.opendc.org/v2")) @@ -59,7 +59,7 @@ class RunnerCli : CliktCommand(name = "opendc-runner") { private val authDomain by option( "--auth-domain", help = "auth domain of the OpenDC API", - envvar = "AUTH0_DOMAIN" + envvar = "AUTH0_DOMAIN", ) .required() @@ -69,7 +69,7 @@ class RunnerCli : CliktCommand(name = "opendc-runner") { private val authAudience by option( "--auth-audience", help = "auth audience of the OpenDC API", - envvar = "AUTH0_AUDIENCE" + envvar = "AUTH0_AUDIENCE", ) .required() @@ -79,7 +79,7 @@ class RunnerCli : CliktCommand(name = "opendc-runner") { private val authClientId by option( "--auth-id", help = "auth client id of the OpenDC API", - envvar = "AUTH0_CLIENT_ID" + envvar = "AUTH0_CLIENT_ID", ) .required() @@ -89,7 +89,7 @@ class RunnerCli : CliktCommand(name = "opendc-runner") { private val authClientSecret by option( "--auth-secret", help = "auth client secret of the OpenDC API", - envvar = "AUTH0_CLIENT_SECRET" + envvar = "AUTH0_CLIENT_SECRET", ) .required() @@ -99,7 +99,7 @@ class RunnerCli : CliktCommand(name = "opendc-runner") { private val tracePath by option( "--traces", help = "path to the directory containing the traces", - envvar = "OPENDC_TRACES" + envvar = "OPENDC_TRACES", ) .file(canBeFile = false) .defaultLazy { File("traces/") } @@ -109,7 +109,7 @@ class RunnerCli : CliktCommand(name = "opendc-runner") { */ private val parallelism by option( "--parallelism", - help = "maximum number of threads for simulations" + help = "maximum number of threads for simulations", ) .int() .default(Runtime.getRuntime().availableProcessors() - 1) diff --git a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/JobManager.kt b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/JobManager.kt index d6c06889a..a517f3b4a 100644 --- a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/JobManager.kt +++ b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/JobManager.kt @@ -47,17 +47,27 @@ public interface JobManager { * @param runtime The total runtime of the job. * @return `true` if the job can continue, `false` if the job has been cancelled. */ - public fun heartbeat(id: Long, runtime: Int): Boolean + public fun heartbeat( + id: Long, + runtime: Int, + ): Boolean /** * Mark the job as failed. */ - public fun fail(id: Long, runtime: Int) + public fun fail( + id: Long, + runtime: Int, + ) /** * Persist the specified results for the specified job. */ - public fun finish(id: Long, runtime: Int, results: Map) + public fun finish( + id: Long, + runtime: Int, + results: Map, + ) public companion object { /** diff --git a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/OpenDCRunner.kt b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/OpenDCRunner.kt index 4351f3c1c..eee340cfd 100644 --- a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/OpenDCRunner.kt +++ b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/OpenDCRunner.kt @@ -76,7 +76,7 @@ public class OpenDCRunner( parallelism: Int = Runtime.getRuntime().availableProcessors(), private val jobTimeout: Duration = Duration.ofMinutes(10), private val pollInterval: Duration = Duration.ofSeconds(30), - private val heartbeatInterval: Duration = Duration.ofMinutes(1) + private val heartbeatInterval: Duration = Duration.ofMinutes(1), ) : Runnable { /** * Logging instance for this runner. @@ -149,26 +149,28 @@ public class OpenDCRunner( val startTime = Instant.now() val currentThread = Thread.currentThread() - val heartbeat = scheduler.scheduleWithFixedDelay( - { - if (!manager.heartbeat(id, startTime.secondsSince())) { - currentThread.interrupt() - } - }, - 0, - heartbeatInterval.toMillis(), - TimeUnit.MILLISECONDS - ) + val heartbeat = + scheduler.scheduleWithFixedDelay( + { + if (!manager.heartbeat(id, startTime.secondsSince())) { + currentThread.interrupt() + } + }, + 0, + heartbeatInterval.toMillis(), + TimeUnit.MILLISECONDS, + ) try { val topology = convertTopology(scenario.topology) - val jobs = (0 until scenario.portfolio.targets.repeats).map { repeat -> - SimulationTask( - scenario, - repeat, - topology - ) - } + val jobs = + (0 until scenario.portfolio.targets.repeats).map { repeat -> + SimulationTask( + scenario, + repeat, + topology, + ) + } val results = invokeAll(jobs).map { it.rawResult } heartbeat.cancel(true) @@ -194,8 +196,8 @@ public class OpenDCRunner( "total_vms_submitted" to results.map { it.totalVmsSubmitted }, "total_vms_queued" to results.map { it.totalVmsQueued }, "total_vms_finished" to results.map { it.totalVmsFinished }, - "total_vms_failed" to results.map { it.totalVmsFailed } - ) + "total_vms_failed" to results.map { it.totalVmsFailed }, + ), ) } catch (e: Exception) { // Check whether the job failed due to exceeding its time budget @@ -232,7 +234,7 @@ public class OpenDCRunner( private inner class SimulationTask( private val scenario: Scenario, private val repeat: Int, - private val topology: List + private val topology: List, ) : RecursiveTask() { override fun compute(): WebComputeMonitor.Results { val monitor = WebComputeMonitor() @@ -254,50 +256,51 @@ public class OpenDCRunner( /** * Run a single simulation of the scenario. */ - private fun runSimulation(monitor: WebComputeMonitor) = runSimulation { - val serviceDomain = "compute.opendc.org" - val seed = repeat.toLong() - - val scenario = scenario - - Provisioner(dispatcher, seed).use { provisioner -> - provisioner.runSteps( - setupComputeService( - serviceDomain, - { createComputeScheduler(scenario.schedulerName, Random(it.seeder.nextLong())) } - ), - registerComputeMonitor(serviceDomain, monitor), - setupHosts(serviceDomain, topology) - ) - - val service = provisioner.registry.resolve(serviceDomain, ComputeService::class.java)!! - - val workload = - trace(scenario.workload.trace.id).sampleByLoad(scenario.workload.samplingFraction) - val vms = workload.resolve(workloadLoader, Random(seed)) + private fun runSimulation(monitor: WebComputeMonitor) = + runSimulation { + val serviceDomain = "compute.opendc.org" + val seed = repeat.toLong() + + val scenario = scenario + + Provisioner(dispatcher, seed).use { provisioner -> + provisioner.runSteps( + setupComputeService( + serviceDomain, + { createComputeScheduler(scenario.schedulerName, Random(it.seeder.nextLong())) }, + ), + registerComputeMonitor(serviceDomain, monitor), + setupHosts(serviceDomain, topology), + ) - val phenomena = scenario.phenomena - val failureModel = - if (phenomena.failures) { - grid5000(Duration.ofDays(7)) - } else { - null + val service = provisioner.registry.resolve(serviceDomain, ComputeService::class.java)!! + + val workload = + trace(scenario.workload.trace.id).sampleByLoad(scenario.workload.samplingFraction) + val vms = workload.resolve(workloadLoader, Random(seed)) + + val phenomena = scenario.phenomena + val failureModel = + if (phenomena.failures) { + grid5000(Duration.ofDays(7)) + } else { + null + } + + // Run workload trace + service.replay(timeSource, vms, seed, failureModel = failureModel, interference = phenomena.interference) + + val serviceMetrics = service.getSchedulerStats() + logger.debug { + "Scheduler " + + "Success=${serviceMetrics.attemptsSuccess} " + + "Failure=${serviceMetrics.attemptsFailure} " + + "Error=${serviceMetrics.attemptsError} " + + "Pending=${serviceMetrics.serversPending} " + + "Active=${serviceMetrics.serversActive}" } - - // Run workload trace - service.replay(timeSource, vms, seed, failureModel = failureModel, interference = phenomena.interference) - - val serviceMetrics = service.getSchedulerStats() - logger.debug { - "Scheduler " + - "Success=${serviceMetrics.attemptsSuccess} " + - "Failure=${serviceMetrics.attemptsFailure} " + - "Error=${serviceMetrics.attemptsError} " + - "Pending=${serviceMetrics.serversPending} " + - "Active=${serviceMetrics.serversActive}" } } - } } /** @@ -307,46 +310,50 @@ public class OpenDCRunner( val res = mutableListOf() val random = Random(0) - val machines = topology.rooms.asSequence() - .flatMap { room -> - room.tiles.flatMap { tile -> - val rack = tile.rack - rack?.machines?.map { machine -> rack to machine } ?: emptyList() + val machines = + topology.rooms.asSequence() + .flatMap { room -> + room.tiles.flatMap { tile -> + val rack = tile.rack + rack?.machines?.map { machine -> rack to machine } ?: emptyList() + } } - } for ((rack, machine) in machines) { val clusterId = rack.id val position = machine.position - val processors = machine.cpus.flatMap { cpu -> - val cores = cpu.numberOfCores - val speed = cpu.clockRateMhz - // TODO Remove hard coding of vendor - val node = ProcessingNode("Intel", "amd64", cpu.name, cores) - List(cores) { coreId -> - ProcessingUnit(node, coreId, speed) + val processors = + machine.cpus.flatMap { cpu -> + val cores = cpu.numberOfCores + val speed = cpu.clockRateMhz + // TODO Remove hard coding of vendor + val node = ProcessingNode("Intel", "amd64", cpu.name, cores) + List(cores) { coreId -> + ProcessingUnit(node, coreId, speed) + } + } + val memoryUnits = + machine.memory.map { memory -> + MemoryUnit( + "Samsung", + memory.name, + memory.speedMbPerS, + memory.sizeMb.toLong(), + ) } - } - val memoryUnits = machine.memory.map { memory -> - MemoryUnit( - "Samsung", - memory.name, - memory.speedMbPerS, - memory.sizeMb.toLong() - ) - } val energyConsumptionW = machine.cpus.sumOf { it.energyConsumptionW } val powerModel = CpuPowerModels.linear(2 * energyConsumptionW, energyConsumptionW * 0.5) - val spec = HostSpec( - UUID(random.nextLong(), random.nextLong()), - "node-$clusterId-$position", - mapOf("cluster" to clusterId), - MachineModel(processors, memoryUnits), - SimPsuFactories.simple(powerModel) - ) + val spec = + HostSpec( + UUID(random.nextLong(), random.nextLong()), + "node-$clusterId-$position", + mapOf("cluster" to clusterId), + MachineModel(processors, memoryUnits), + SimPsuFactories.simple(powerModel), + ) res += spec } @@ -358,10 +365,11 @@ public class OpenDCRunner( * A custom [ForkJoinWorkerThreadFactory] that uses the [ClassLoader] of specified by the runner. */ private class RunnerThreadFactory(private val classLoader: ClassLoader) : ForkJoinWorkerThreadFactory { - override fun newThread(pool: ForkJoinPool): ForkJoinWorkerThread = object : ForkJoinWorkerThread(pool) { - init { - contextClassLoader = classLoader + override fun newThread(pool: ForkJoinPool): ForkJoinWorkerThread = + object : ForkJoinWorkerThread(pool) { + init { + contextClassLoader = classLoader + } } - } } } diff --git a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/JobManagerImpl.kt b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/JobManagerImpl.kt index 5b1b71325..7081041c7 100644 --- a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/JobManagerImpl.kt +++ b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/JobManagerImpl.kt @@ -44,16 +44,26 @@ internal class JobManagerImpl(private val client: OpenDCRunnerClient) : JobManag } } - override fun heartbeat(id: Long, runtime: Int): Boolean { + override fun heartbeat( + id: Long, + runtime: Int, + ): Boolean { val res = client.jobs.update(id, Job.Update(JobState.RUNNING, runtime)) return res?.state != JobState.FAILED } - override fun fail(id: Long, runtime: Int) { + override fun fail( + id: Long, + runtime: Int, + ) { client.jobs.update(id, Job.Update(JobState.FAILED, runtime)) } - override fun finish(id: Long, runtime: Int, results: Map) { + override fun finish( + id: Long, + runtime: Int, + results: Map, + ) { client.jobs.update(id, Job.Update(JobState.FINISHED, runtime)) } } diff --git a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/WebComputeMonitor.kt b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/WebComputeMonitor.kt index 774689c99..4576a4631 100644 --- a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/WebComputeMonitor.kt +++ b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/WebComputeMonitor.kt @@ -34,31 +34,32 @@ import kotlin.math.roundToLong */ internal class WebComputeMonitor : ComputeMonitor { override fun record(reader: HostTableReader) { - val slices = reader.downtime / SLICE_LENGTH + val slices = reader.downtime / sliceLength - hostAggregateMetrics = AggregateHostMetrics( - hostAggregateMetrics.totalActiveTime + reader.cpuActiveTime, - hostAggregateMetrics.totalIdleTime + reader.cpuIdleTime, - hostAggregateMetrics.totalStealTime + reader.cpuStealTime, - hostAggregateMetrics.totalLostTime + reader.cpuLostTime, - hostAggregateMetrics.totalPowerDraw + reader.energyUsage, - hostAggregateMetrics.totalFailureSlices + slices, - hostAggregateMetrics.totalFailureVmSlices + reader.guestsRunning * slices - ) + hostAggregateMetrics = + AggregateHostMetrics( + hostAggregateMetrics.totalActiveTime + reader.cpuActiveTime, + hostAggregateMetrics.totalIdleTime + reader.cpuIdleTime, + hostAggregateMetrics.totalStealTime + reader.cpuStealTime, + hostAggregateMetrics.totalLostTime + reader.cpuLostTime, + hostAggregateMetrics.totalPowerDraw + reader.energyUsage, + hostAggregateMetrics.totalFailureSlices + slices, + hostAggregateMetrics.totalFailureVmSlices + reader.guestsRunning * slices, + ) hostMetrics.compute(reader.host.id) { _, prev -> HostMetrics( reader.cpuUsage + (prev?.cpuUsage ?: 0.0), reader.cpuDemand + (prev?.cpuDemand ?: 0.0), reader.guestsRunning + (prev?.instanceCount ?: 0), - 1 + (prev?.count ?: 0) + 1 + (prev?.count ?: 0), ) } } private var hostAggregateMetrics: AggregateHostMetrics = AggregateHostMetrics() private val hostMetrics: MutableMap = mutableMapOf() - private val SLICE_LENGTH: Long = 5 * 60L + private val sliceLength: Long = 5 * 60L private data class AggregateHostMetrics( val totalActiveTime: Long = 0L, @@ -67,14 +68,14 @@ internal class WebComputeMonitor : ComputeMonitor { val totalLostTime: Long = 0L, val totalPowerDraw: Double = 0.0, val totalFailureSlices: Double = 0.0, - val totalFailureVmSlices: Double = 0.0 + val totalFailureVmSlices: Double = 0.0, ) private data class HostMetrics( val cpuUsage: Double, val cpuDemand: Double, val instanceCount: Long, - val count: Long + val count: Long, ) private lateinit var serviceData: ServiceData @@ -106,7 +107,7 @@ internal class WebComputeMonitor : ComputeMonitor { serviceData.serversTotal, serviceData.serversPending, serviceData.serversTotal - serviceData.serversPending - serviceData.serversActive, - serviceData.attemptsError + serviceData.attemptsFailure + serviceData.attemptsError + serviceData.attemptsFailure, ) } @@ -128,6 +129,6 @@ internal class WebComputeMonitor : ComputeMonitor { val totalVmsSubmitted: Int, val totalVmsQueued: Int, val totalVmsFinished: Int, - val totalVmsFailed: Int + val totalVmsFailed: Int, ) } diff --git a/opendc-web/opendc-web-server/Dockerfile b/opendc-web/opendc-web-server/Dockerfile index 8aa542918..bcdb831e5 100644 --- a/opendc-web/opendc-web-server/Dockerfile +++ b/opendc-web/opendc-web-server/Dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:17-slim +FROM openjdk:19-slim MAINTAINER OpenDC Maintainers # Obtain (cache) Gradle wrapper @@ -19,7 +19,7 @@ ENV OPENDC_AUTH0_DOCS_CLIENT_ID=$OPENDC_AUTH0_DOCS_CLIENT_ID COPY ./ /app/ RUN ./gradlew --no-daemon :opendc-web:opendc-web-server:quarkusBuild -Dquarkus.profile=docker -FROM openjdk:17-slim +FROM openjdk:19-slim COPY --from=0 /app/opendc-web/opendc-web-server/build/quarkus-app /opt/opendc WORKDIR /opt/opendc CMD java -jar quarkus-run.jar diff --git a/opendc-web/opendc-web-server/build.gradle.kts b/opendc-web/opendc-web-server/build.gradle.kts index 8a6d8c0b0..484e98c00 100644 --- a/opendc-web/opendc-web-server/build.gradle.kts +++ b/opendc-web/opendc-web-server/build.gradle.kts @@ -22,7 +22,7 @@ description = "Web server of OpenDC" -/* Build configuration */ +// Build configuration plugins { `quarkus-conventions` distribution @@ -34,7 +34,7 @@ dependencies { implementation(projects.opendcWeb.opendcWebProto) testImplementation("junit:junit:4.13.1") testImplementation("junit:junit:4.13.1") - compileOnly(projects.opendcWeb.opendcWebUiQuarkusDeployment) /* Temporary fix for Quarkus/Gradle issues */ + compileOnly(projects.opendcWeb.opendcWebUiQuarkusDeployment) // Temporary fix for Quarkus/Gradle issues compileOnly(projects.opendcWeb.opendcWebRunnerQuarkusDeployment) implementation(projects.opendcWeb.opendcWebUiQuarkus) implementation(projects.opendcWeb.opendcWebRunnerQuarkus) @@ -68,7 +68,7 @@ val createStartScripts by tasks.creating(CreateStartScripts::class) { applicationName = "opendc-server" mainClass.set("io.quarkus.bootstrap.runner.QuarkusEntryPoint") classpath = files("lib/quarkus-run.jar") - outputDir = project.buildDir.resolve("scripts") + outputDir = project.layout.buildDirectory.get().asFile.resolve("scripts") } distributions { diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java index c5fb208e5..a0ac390f9 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java @@ -22,23 +22,14 @@ package org.opendc.web.server.model; +import io.hypersistence.utils.hibernate.type.json.JsonType; import io.quarkus.hibernate.orm.panache.Panache; -import io.quarkus.hibernate.orm.panache.PanacheEntity; +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import io.quarkus.hibernate.orm.panache.PanacheQuery; import io.quarkus.panache.common.Parameters; +import jakarta.persistence.*; import java.time.Instant; import java.util.Map; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; import org.hibernate.annotations.Type; import org.opendc.web.proto.JobState; @@ -46,7 +37,7 @@ * A simulation job to be run by the simulator. */ @Entity -@Table(name = "jobs") +@Table @NamedQueries({ @NamedQuery( name = "Job.updateOne", @@ -57,7 +48,16 @@ WHERE j.id = :id AND j.state = :oldState """) }) -public class Job extends PanacheEntity { +public class Job extends PanacheEntityBase { + /** + * The main ID of a project. + * The value starts at 6 to account for the other 5 projects already made by the loading script. + */ + @Id + @SequenceGenerator(name = "jobSeq", sequenceName = "job_id_seq", allocationSize = 1, initialValue = 3) + @GeneratedValue(generator = "jobSeq") + public Long id; + @ManyToOne(optional = false, fetch = FetchType.EAGER) @JoinColumn(name = "scenario_id", foreignKey = @ForeignKey(name = "fk_jobs_scenario"), nullable = false) public Scenario scenario; @@ -83,9 +83,8 @@ public class Job extends PanacheEntity { /** * The state of the job. */ - @Type(type = "io.hypersistence.utils.hibernate.type.basic.PostgreSQLEnumType") - @Column(nullable = false, columnDefinition = "enum") @Enumerated(EnumType.STRING) + @Column(nullable = false) public JobState state = JobState.PENDING; /** @@ -97,8 +96,8 @@ public class Job extends PanacheEntity { /** * Experiment results in JSON */ - @Type(type = "io.hypersistence.utils.hibernate.type.json.JsonType") @Column(columnDefinition = "jsonb") + @Type(JsonType.class) public Map results = null; /** diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java index 3a406683b..c26951922 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java @@ -22,23 +22,27 @@ package org.opendc.web.server.model; -import io.quarkus.hibernate.orm.panache.PanacheEntity; +import io.hypersistence.utils.hibernate.type.json.JsonType; +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import io.quarkus.hibernate.orm.panache.PanacheQuery; import io.quarkus.panache.common.Parameters; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; import java.util.HashSet; import java.util.Set; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToMany; -import javax.persistence.OrderBy; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; import org.hibernate.annotations.Type; import org.opendc.web.proto.Targets; @@ -47,7 +51,6 @@ */ @Entity @Table( - name = "portfolios", uniqueConstraints = { @UniqueConstraint( name = "uk_portfolios_number", @@ -60,7 +63,17 @@ name = "Portfolio.findOneByProject", query = "SELECT p FROM Portfolio p WHERE p.project.id = :projectId AND p.number = :number") }) -public class Portfolio extends PanacheEntity { +public class Portfolio extends PanacheEntityBase { + + /** + * The main ID of a project. + * The value starts at 6 to account for the other 5 projects already made by the loading script. + */ + @Id + @SequenceGenerator(name = "portfolioSeq", sequenceName = "portfolio_id_seq", allocationSize = 1, initialValue = 4) + @GeneratedValue(generator = "portfolioSeq") + public Long id; + /** * The {@link Project} this portfolio belongs to. */ @@ -83,8 +96,8 @@ public class Portfolio extends PanacheEntity { /** * The portfolio targets (metrics, repetitions). */ - @Type(type = "io.hypersistence.utils.hibernate.type.json.JsonType") @Column(columnDefinition = "jsonb", nullable = false, updatable = false) + @Type(JsonType.class) public Targets targets; /** diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java index 5836e33f0..f4e5305d2 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java @@ -23,25 +23,28 @@ package org.opendc.web.server.model; import io.quarkus.hibernate.orm.panache.Panache; -import io.quarkus.hibernate.orm.panache.PanacheEntity; +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import io.quarkus.panache.common.Parameters; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import java.time.Instant; import java.util.HashSet; import java.util.Set; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToMany; -import javax.persistence.OrderBy; -import javax.persistence.Table; /** * A project in OpenDC encapsulates all the datacenter designs and simulation runs for a set of users. */ @Entity -@Table(name = "projects") +@Table @NamedQueries({ @NamedQuery( name = "Project.findByUser", @@ -49,7 +52,7 @@ """ SELECT a FROM ProjectAuthorization a - WHERE a.key.userId = :userId + WHERE a.key.userName = :userName """), @NamedQuery( name = "Project.allocatePortfolio", @@ -76,7 +79,17 @@ WHERE p.id = :id AND p.scenariosCreated = :oldState """) }) -public class Project extends PanacheEntity { +public class Project extends PanacheEntityBase { + + /** + * The main ID of a project. + * The value starts at 6 to account for the other 5 projects already made by the loading script. + */ + @Id + @SequenceGenerator(name = "projectSeq", sequenceName = "project_id_seq", allocationSize = 1, initialValue = 7) + @GeneratedValue(generator = "projectSeq") + public Long id; + /** * The name of the project. */ diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java index 1238f58de..3776ae12d 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java @@ -25,30 +25,29 @@ import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import io.quarkus.hibernate.orm.panache.PanacheQuery; import io.quarkus.panache.common.Parameters; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.Table; import java.io.Serializable; import java.util.Objects; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.EmbeddedId; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.MapsId; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; -import org.hibernate.annotations.Type; import org.opendc.web.proto.user.ProjectRole; /** * An authorization for some user to participate in a project. */ @Entity -@Table(name = "project_authorizations") +@Table @NamedQueries({ @NamedQuery( name = "ProjectAuthorization.findByUser", @@ -56,7 +55,7 @@ """ SELECT a FROM ProjectAuthorization a - WHERE a.key.userId = :userId + WHERE a.key.userName = :userName """), }) public class ProjectAuthorization extends PanacheEntityBase { @@ -82,16 +81,15 @@ public class ProjectAuthorization extends PanacheEntityBase { /** * The role of the user in the project. */ - @Type(type = "io.hypersistence.utils.hibernate.type.basic.PostgreSQLEnumType") - @Column(nullable = false, columnDefinition = "enum") + @Column(nullable = false) @Enumerated(EnumType.STRING) public ProjectRole role; /** * Construct a {@link ProjectAuthorization} object. */ - public ProjectAuthorization(Project project, String userId, ProjectRole role) { - this.key = new ProjectAuthorization.Key(project.id, userId); + public ProjectAuthorization(Project project, String userName, ProjectRole role) { + this.key = new ProjectAuthorization.Key(project.id, userName); this.project = project; this.role = role; } @@ -102,25 +100,25 @@ public ProjectAuthorization(Project project, String userId, ProjectRole role) { protected ProjectAuthorization() {} /** - * List all projects for the user with the specified userId. + * List all projects for the user with the specified userName. * - * @param userId The identifier of the user that is requesting the list of projects. + * @param userName The identifier of the user that is requesting the list of projects. * @return A query returning projects that the user has received authorization for. */ - public static PanacheQuery findByUser(String userId) { - return find("#ProjectAuthorization.findByUser", Parameters.with("userId", userId)); + public static PanacheQuery findByUser(String userName) { + return find("#ProjectAuthorization.findByUser", Parameters.with("userName", userName)); } /** - * Find the project with id for the user with the specified userId. + * Find the project with id for the user with the specified userName. * - * @param userId The identifier of the user that is requesting the list of projects. - * @param id The unique identifier of the project. + * @param userName The identifier of the user that is requesting the list of projects. + * @param project_id The unique identifier of the project. * @return The project with the specified identifier or null if it does not exist or is not accessible * to the user with the specified identifier. */ - public static ProjectAuthorization findByUser(String userId, long id) { - return findById(new ProjectAuthorization.Key(id, userId)); + public static ProjectAuthorization findByUser(String userName, long project_id) { + return findById(new ProjectAuthorization.Key(project_id, userName)); } /** @@ -148,12 +146,12 @@ public static class Key implements Serializable { @Column(name = "project_id", nullable = false) public long projectId; - @Column(name = "user_id", nullable = false) - public String userId; + @Column(name = "user_name", nullable = false) + public String userName; - public Key(long projectId, String userId) { + public Key(long projectId, String userName) { this.projectId = projectId; - this.userId = userId; + this.userName = userName; } protected Key() {} @@ -163,12 +161,12 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Key key = (Key) o; - return projectId == key.projectId && userId.equals(key.userId); + return projectId == key.projectId && userName.equals(key.userName); } @Override public int hashCode() { - return Objects.hash(projectId, userId); + return Objects.hash(projectId, userName); } } } diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java index 016e931b9..c79ef5bbb 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java @@ -22,25 +22,13 @@ package org.opendc.web.server.model; -import io.quarkus.hibernate.orm.panache.PanacheEntity; +import io.hypersistence.utils.hibernate.type.json.JsonType; +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import io.quarkus.hibernate.orm.panache.PanacheQuery; import io.quarkus.panache.common.Parameters; +import jakarta.persistence.*; import java.util.ArrayList; import java.util.List; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Embedded; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToMany; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; import org.hibernate.annotations.Type; import org.opendc.web.proto.OperationalPhenomena; @@ -49,7 +37,6 @@ */ @Entity @Table( - name = "scenarios", uniqueConstraints = { @UniqueConstraint( name = "uk_scenarios_number", @@ -71,7 +58,16 @@ name = "Scenario.findOneByProject", query = "SELECT s FROM Scenario s WHERE s.project.id = :projectId AND s.number = :number") }) -public class Scenario extends PanacheEntity { +public class Scenario extends PanacheEntityBase { + /** + * The main ID of a Scenario. + * The value starts at 3 to account for the other 2 scenarios already made by the loading script. + */ + @Id + @SequenceGenerator(name = "scenarioSeq", sequenceName = "scenario_id_seq", allocationSize = 1, initialValue = 3) + @GeneratedValue(generator = "scenarioSeq") + public Long id; + /** * The {@link Project} to which this scenario belongs. */ @@ -113,9 +109,11 @@ public class Scenario extends PanacheEntity { /** * Operational phenomena activated in the scenario. + * @Column(columnDefinition = "jsonb", nullable = false, updatable = false) + * @Type(JsonType.class) */ - @Type(type = "io.hypersistence.utils.hibernate.type.json.JsonType") @Column(columnDefinition = "jsonb", nullable = false, updatable = false) + @Type(JsonType.class) public OperationalPhenomena phenomena; /** diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java index 05a1ac12b..8a4e2ae22 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java @@ -22,20 +22,24 @@ package org.opendc.web.server.model; -import io.quarkus.hibernate.orm.panache.PanacheEntity; +import io.hypersistence.utils.hibernate.type.json.JsonType; +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import io.quarkus.hibernate.orm.panache.PanacheQuery; import io.quarkus.panache.common.Parameters; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; import java.time.Instant; import java.util.List; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; import org.hibernate.annotations.Type; import org.opendc.web.proto.Room; @@ -44,7 +48,6 @@ */ @Entity @Table( - name = "topologies", uniqueConstraints = { @UniqueConstraint( name = "uk_topologies_number", @@ -57,7 +60,16 @@ name = "Topology.findOneByProject", query = "SELECT t FROM Topology t WHERE t.project.id = :projectId AND t.number = :number") }) -public class Topology extends PanacheEntity { +public class Topology extends PanacheEntityBase { + /** + * The main ID of a project. + * The value starts at 6 to account for the other 5 projects already made by the loading script. + */ + @Id + @SequenceGenerator(name = "topologySeq", sequenceName = "topology_id_seq", allocationSize = 1, initialValue = 5) + @GeneratedValue(generator = "topologySeq") + public Long id; + /** * The {@link Project} to which the topology belongs. */ @@ -91,9 +103,11 @@ public class Topology extends PanacheEntity { /** * Datacenter design in JSON + * @Column(columnDefinition = "jsonb", nullable = false) + * @Type(JsonType.class) */ - @Type(type = "io.hypersistence.utils.hibernate.type.json.JsonType") @Column(columnDefinition = "jsonb", nullable = false) + @Type(JsonType.class) public List rooms; /** diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Trace.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Trace.java index 36d27abcb..71c647bc0 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Trace.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Trace.java @@ -23,16 +23,16 @@ package org.opendc.web.server.model; import io.quarkus.hibernate.orm.panache.PanacheEntityBase; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; /** * A workload trace available for simulation. */ @Entity -@Table(name = "traces") +@Table public class Trace extends PanacheEntityBase { /** * The unique identifier of the trace. diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/UserAccounting.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/UserAccounting.java index fda4302fb..10a10ef93 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/UserAccounting.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/UserAccounting.java @@ -24,19 +24,19 @@ import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import io.quarkus.panache.common.Parameters; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.Table; import java.time.LocalDate; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; /** * Entity to track the number of simulation minutes used by a user. */ @Entity -@Table(name = "user_accounting") +@Table @NamedQueries({ @NamedQuery( name = "UserAccounting.consumeBudget", diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Workload.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Workload.java index 129fb0c50..fd7010d2e 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Workload.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Workload.java @@ -22,9 +22,9 @@ package org.opendc.web.server.model; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.ManyToOne; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.ManyToOne; /** * Specification of the workload for a {@link Scenario} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/SchedulerResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/SchedulerResource.java index 0fd581829..d7bb8f69b 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/SchedulerResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/SchedulerResource.java @@ -22,10 +22,10 @@ package org.opendc.web.server.rest; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; import java.util.List; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; /** * A resource representing the available schedulers that can be used during experiments. diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/TraceResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/TraceResource.java index 7316c93fc..daec01cd6 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/TraceResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/TraceResource.java @@ -22,13 +22,13 @@ package org.opendc.web.server.rest; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; import java.util.List; import java.util.stream.Stream; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import org.opendc.web.server.model.Trace; /** diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java index 3b6be42e5..345acdfe7 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java @@ -23,10 +23,10 @@ package org.opendc.web.server.rest.error; import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; import org.opendc.web.proto.ProtocolError; /** diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.java index ad1bb05e2..e027e5597 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.java @@ -22,11 +22,11 @@ package org.opendc.web.server.rest.error; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; import org.opendc.web.proto.ProtocolError; /** diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java index dff525261..4dde8654e 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java @@ -22,17 +22,17 @@ package org.opendc.web.server.rest.runner; +import jakarta.annotation.security.RolesAllowed; +import jakarta.transaction.Transactional; +import jakarta.validation.Valid; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; import java.util.List; -import javax.annotation.security.RolesAllowed; -import javax.transaction.Transactional; -import javax.validation.Valid; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import org.opendc.web.proto.JobState; import org.opendc.web.server.model.Job; import org.opendc.web.server.service.JobService; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java index d1fc980d5..2a3a40f47 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java @@ -23,19 +23,19 @@ package org.opendc.web.server.rest.user; import io.quarkus.security.identity.SecurityIdentity; +import jakarta.annotation.security.RolesAllowed; +import jakarta.transaction.Transactional; +import jakarta.validation.Valid; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; import java.time.Instant; import java.util.List; -import javax.annotation.security.RolesAllowed; -import javax.transaction.Transactional; -import javax.validation.Valid; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import org.opendc.web.server.model.Portfolio; import org.opendc.web.server.model.ProjectAuthorization; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java index a058cd313..789808c80 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java @@ -23,18 +23,18 @@ package org.opendc.web.server.rest.user; import io.quarkus.security.identity.SecurityIdentity; +import jakarta.annotation.security.RolesAllowed; +import jakarta.transaction.Transactional; +import jakarta.validation.Valid; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; import java.time.Instant; import java.util.List; -import javax.annotation.security.RolesAllowed; -import javax.transaction.Transactional; -import javax.validation.Valid; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import org.opendc.web.proto.JobState; import org.opendc.web.server.model.Job; import org.opendc.web.server.model.Portfolio; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java index da47c3ff2..ae1c959e1 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java @@ -23,19 +23,19 @@ package org.opendc.web.server.rest.user; import io.quarkus.security.identity.SecurityIdentity; +import jakarta.annotation.security.RolesAllowed; +import jakarta.transaction.Transactional; +import jakarta.validation.Valid; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; import java.time.Instant; import java.util.List; -import javax.annotation.security.RolesAllowed; -import javax.transaction.Transactional; -import javax.validation.Valid; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import org.opendc.web.proto.user.ProjectRole; import org.opendc.web.server.model.Project; import org.opendc.web.server.model.ProjectAuthorization; @@ -96,9 +96,9 @@ public org.opendc.web.proto.user.Project create(@Valid org.opendc.web.proto.user */ @GET @Path("{project}") - public org.opendc.web.proto.user.Project get(@PathParam("project") long id) { + public org.opendc.web.proto.user.Project get(@PathParam("project") long project_id) { ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), id); + ProjectAuthorization.findByUser(identity.getPrincipal().getName(), project_id); if (auth == null) { throw new WebApplicationException("Project not found", 404); diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java index cf933c322..bb3eb89bf 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java @@ -23,15 +23,15 @@ package org.opendc.web.server.rest.user; import io.quarkus.security.identity.SecurityIdentity; +import jakarta.annotation.security.RolesAllowed; +import jakarta.transaction.Transactional; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; import java.util.List; -import javax.annotation.security.RolesAllowed; -import javax.transaction.Transactional; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import org.opendc.web.server.model.ProjectAuthorization; import org.opendc.web.server.model.Scenario; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java index 71491801d..b8c542d3d 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java @@ -24,21 +24,21 @@ import io.quarkus.hibernate.orm.panache.Panache; import io.quarkus.security.identity.SecurityIdentity; +import jakarta.annotation.security.RolesAllowed; +import jakarta.persistence.PersistenceException; +import jakarta.transaction.Transactional; +import jakarta.validation.Valid; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; import java.time.Instant; import java.util.List; -import javax.annotation.security.RolesAllowed; -import javax.persistence.PersistenceException; -import javax.transaction.Transactional; -import javax.validation.Valid; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import org.opendc.web.server.model.Project; import org.opendc.web.server.model.ProjectAuthorization; import org.opendc.web.server.model.Topology; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserResource.java index c3fb28665..c8cda2b76 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserResource.java @@ -23,10 +23,10 @@ package org.opendc.web.server.rest.user; import io.quarkus.security.identity.SecurityIdentity; -import javax.annotation.security.RolesAllowed; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; import org.opendc.web.proto.user.User; import org.opendc.web.proto.user.UserAccounting; import org.opendc.web.server.service.UserAccountingService; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java index ed0eaf9c8..709335205 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java @@ -22,9 +22,9 @@ package org.opendc.web.server.service; +import jakarta.enterprise.context.ApplicationScoped; import java.time.Instant; import java.util.Map; -import javax.enterprise.context.ApplicationScoped; import org.opendc.web.proto.JobState; import org.opendc.web.server.model.Job; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/UserAccountingService.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/UserAccountingService.java index e5003cb4a..73fa2a3e1 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/UserAccountingService.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/UserAccountingService.java @@ -22,11 +22,11 @@ package org.opendc.web.server.service; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.persistence.EntityExistsException; import java.time.Duration; import java.time.LocalDate; import java.time.temporal.TemporalAdjusters; -import javax.enterprise.context.ApplicationScoped; -import javax.persistence.EntityExistsException; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.opendc.web.server.model.UserAccounting; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/DevSecurityOverrideFilter.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/DevSecurityOverrideFilter.java index de4478cb6..103f868d5 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/DevSecurityOverrideFilter.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/DevSecurityOverrideFilter.java @@ -23,12 +23,12 @@ package org.opendc.web.server.util; import io.quarkus.arc.properties.IfBuildProperty; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.container.ContainerRequestFilter; +import jakarta.ws.rs.container.PreMatching; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.ext.Provider; import java.security.Principal; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerRequestFilter; -import javax.ws.rs.container.PreMatching; -import javax.ws.rs.core.SecurityContext; -import javax.ws.rs.ext.Provider; /** * Helper class to disable security for the OpenDC web API when in development mode. diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/KotlinModuleCustomizer.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/KotlinModuleCustomizer.java index c30edcbff..ff3ba1cd1 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/KotlinModuleCustomizer.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/KotlinModuleCustomizer.java @@ -25,7 +25,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.module.kotlin.KotlinModule; import io.quarkus.jackson.ObjectMapperCustomizer; -import javax.inject.Singleton; +import jakarta.inject.Singleton; /** * Helper class to register the Kotlin Jackson module. diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/QuarkusObjectMapperSupplier.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/QuarkusObjectMapperSupplier.java index e46c74ed5..60ca77e5e 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/QuarkusObjectMapperSupplier.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/QuarkusObjectMapperSupplier.java @@ -25,7 +25,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.hypersistence.utils.hibernate.type.util.ObjectMapperSupplier; import io.quarkus.runtime.annotations.RegisterForReflection; -import javax.enterprise.inject.spi.CDI; +import jakarta.enterprise.inject.spi.CDI; /** * A supplier for an {@link ObjectMapper} used by the Hypersistence utilities. diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/runner/QuarkusJobManager.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/runner/QuarkusJobManager.java index 0331eacfa..47d397f33 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/runner/QuarkusJobManager.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/runner/QuarkusJobManager.java @@ -22,9 +22,9 @@ package org.opendc.web.server.util.runner; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.transaction.Transactional; import java.util.Map; -import javax.enterprise.context.ApplicationScoped; -import javax.transaction.Transactional; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.opendc.web.proto.JobState; diff --git a/opendc-web/opendc-web-server/src/main/resources/application-test.properties b/opendc-web/opendc-web-server/src/main/resources/application-test.properties index bee172219..4e3063e4d 100644 --- a/opendc-web/opendc-web-server/src/main/resources/application-test.properties +++ b/opendc-web/opendc-web-server/src/main/resources/application-test.properties @@ -37,3 +37,7 @@ quarkus.swagger-ui.enable=false # Disable OpenDC web UI and runner quarkus.opendc-ui.include=false quarkus.opendc-runner.include=false + +# Create new tables and fill them +quarkus.hibernate-orm.database.generation=drop-and-create +quarkus.hibernate-orm.sql-load-script=load_data.sql diff --git a/opendc-web/opendc-web-server/src/main/resources/db/migration/V3.0__core.sql b/opendc-web/opendc-web-server/src/main/resources/db/migration/V3.0__core.sql deleted file mode 100644 index 40654b6b7..000000000 --- a/opendc-web/opendc-web-server/src/main/resources/db/migration/V3.0__core.sql +++ /dev/null @@ -1,160 +0,0 @@ --- Hibernate sequence for unique identifiers -create sequence hibernate_sequence start with 1 increment by 1; - --- Projects -create table projects -( - id bigint not null, - created_at timestamp not null, - name varchar(255) not null, - portfolios_created integer not null default 0, - scenarios_created integer not null default 0, - topologies_created integer not null default 0, - updated_at timestamp not null, - primary key (id) -); - -create type project_role as enum ('OWNER', 'EDITOR', 'VIEWER'); - --- Project authorizations authorize users specific permissions to a project. -create table project_authorizations -( - project_id bigint not null, - user_id varchar(255) not null, - role project_role not null, - primary key (project_id, user_id) -); - --- Topologies represent the datacenter designs created by users. -create table topologies -( - id bigint not null, - created_at timestamp not null, - name varchar(255) not null, - number integer not null, - rooms jsonb not null, - updated_at timestamp not null, - project_id bigint not null, - primary key (id) -); - --- Portfolios -create table portfolios -( - id bigint not null, - name varchar(255) not null, - number integer not null, - targets jsonb not null, - project_id bigint not null, - primary key (id) -); - -create table scenarios -( - id bigint not null, - name varchar(255) not null, - number integer not null, - phenomena jsonb not null, - scheduler_name varchar(255) not null, - sampling_fraction double precision not null, - portfolio_id bigint not null, - project_id bigint not null, - topology_id bigint not null, - trace_id varchar(255) not null, - primary key (id) -); - -create type job_state as enum ('PENDING', 'CLAIMED', 'RUNNING', 'FINISHED', 'FAILED'); - -create table jobs -( - id bigint not null, - created_by varchar(255) not null, - created_at timestamp not null, - repeats integer not null, - results jsonb, - state job_state not null default 'PENDING', - runtime integer not null default 0, - updated_at timestamp not null, - scenario_id bigint not null, - primary key (id) -); - --- User accounting -create table user_accounting -( - user_id varchar(255) not null, - period_end date not null, - simulation_time integer not null, - simulation_time_budget integer not null, - primary key (user_id) -); - --- Workload traces available to the user. -create table traces -( - id varchar(255) not null, - name varchar(255) not null, - type varchar(255) not null, - primary key (id) -); - --- Relations -alter table project_authorizations - add constraint fk_project_authorizations - foreign key (project_id) - references projects; - -create index ux_topologies_number on topologies (project_id, number); - -alter table topologies - add constraint uk_topologies_number unique (project_id, number); - -alter table topologies - add constraint fk_topologies_project - foreign key (project_id) - references projects; - -create index ux_portfolios_number on portfolios (project_id, number); - -alter table portfolios - add constraint fk_portfolios_project - foreign key (project_id) - references projects; - -alter table portfolios - add constraint uk_portfolios_number unique (project_id, number); - -create index ux_scenarios_number on scenarios (project_id, number); - -alter table scenarios - add constraint uk_scenarios_number unique (project_id, number); - -alter table scenarios - add constraint fk_scenarios_project - foreign key (project_id) - references projects; - -alter table scenarios - add constraint fk_scenarios_topology - foreign key (topology_id) - references topologies; - -alter table scenarios - add constraint fk_scenarios_portfolio - foreign key (portfolio_id) - references portfolios; - -alter table scenarios - add constraint fk_scenarios_trace - foreign key (trace_id) - references traces; - -alter table jobs - add constraint fk_scenarios_job - foreign key (scenario_id) - references scenarios; - --- Initial data -insert into traces (id, name, type) -values ('bitbrains-small', 'Bitbrains Small', 'vm'); diff --git a/opendc-web/opendc-web-server/src/main/resources/db/testing/V3.0.1__entities.sql b/opendc-web/opendc-web-server/src/main/resources/db/testing/V3.0.1__entities.sql deleted file mode 100644 index 1b702f4e0..000000000 --- a/opendc-web/opendc-web-server/src/main/resources/db/testing/V3.0.1__entities.sql +++ /dev/null @@ -1,24 +0,0 @@ --- Test entities - -alter sequence hibernate_sequence restart with 500; - -insert into projects (id, created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at) -values (1, current_timestamp(), 'Test Project', 1, 2, 1, current_timestamp()); -insert into project_authorizations (project_id, user_id, role) -values (1, 'owner', 'OWNER'), - (1, 'editor', 'EDITOR'), - (1, 'viewer', 'VIEWER'); - -insert into portfolios (id, name, number, targets, project_id) -values (1, 'Test Portfolio', 1, '{ "metrics": [] }' format json, 1); - -insert into topologies (id, created_at, name, number, rooms, updated_at, project_id) -values (1, current_timestamp(), 'Test Topology', 1, '[]' format json, current_timestamp(), 1); - -insert into scenarios (id, name, number, phenomena, scheduler_name, sampling_fraction, portfolio_id, project_id, topology_id, trace_id) -values (1, 'Test Scenario', 1, '{ "failures": false, "interference": false }' format json, 'mem', 1.0, 1, 1, 1, 'bitbrains-small'), - (2, 'Test Scenario', 2, '{ "failures": false, "interference": false }' format json, 'mem', 1.0, 1, 1, 1, 'bitbrains-small'); - -insert into jobs (id, created_by, created_at, repeats, updated_at, scenario_id) -values (1, 'owner', current_timestamp(), 1, current_timestamp(), 1), - (2, 'owner', current_timestamp(), 1, current_timestamp(), 2); diff --git a/opendc-web/opendc-web-server/src/main/resources/load_data.sql b/opendc-web/opendc-web-server/src/main/resources/load_data.sql new file mode 100644 index 000000000..72396cefe --- /dev/null +++ b/opendc-web/opendc-web-server/src/main/resources/load_data.sql @@ -0,0 +1,124 @@ + +-- Insert data + +INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) + VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project 1', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 1); + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('OWNER', 1, 'test_user_1'); + +-- Add test user 2 as a viewer for project 1 + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('VIEWER', 1, 'test_user_2'); + +-- Add test user 3 as an editor for project 1 + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('EDITOR', 1, 'test_user_3'); + +-- Create a project for test user 2 + +INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project 2', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 2); + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('OWNER', 2, 'test_user_2'); + +-- Create three projects for test user 3. User 3 has multiple projects to test getAll + +INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project 3', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 3); + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('OWNER', 3, 'test_user_3'); + +INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project 4', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 4); + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('OWNER', 4, 'test_user_3'); + +INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project 5', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 5); + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('OWNER', 5, 'test_user_3'); + +-- Project to delete + +INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project Delete', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 6); + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('OWNER', 6, 'test_user_1'); + +-- -------------------------------------------------------------------------------- +-- PortFolios +-- -------------------------------------------------------------------------------- + +-- Add Portfolio to project 1 +INSERT INTO PORTFOLIO (name, number, project_id, targets, id) +VALUES ('Test PortFolio Base', 1, 1, '{"metrics": [], "repeats":1}' FORMAT JSON, 1); + +INSERT INTO PORTFOLIO (name, number, project_id, targets, id) +VALUES ('Test PortFolio Delete', 2, 1, '{"metrics": [], "repeats":1}' FORMAT JSON, 2); + +INSERT INTO PORTFOLIO (name, number, project_id, targets, id) +VALUES ('Test PortFolio DeleteEditor', 3, 1, '{"metrics": [], "repeats":1}' FORMAT JSON, 3); + +UPDATE Project p +SET p.portfolios_created = 3, p.updated_at = '2024-03-01T15:31:41.579969Z' +WHERE p.id = 1; + +-- -------------------------------------------------------------------------------- +-- Topologies +-- -------------------------------------------------------------------------------- + +INSERT INTO TOPOLOGY (created_at, name, number, project_id, rooms, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Topology testUpdate', 1, 1, '[]' FORMAT JSON, '2024-03-01T15:31:41.579969Z', 1); + +INSERT INTO TOPOLOGY (created_at, name, number, project_id, rooms, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Topology testDeleteAsEditor', 2, 1, '[]' FORMAT JSON, '2024-03-01T15:31:41.579969Z', 2); + +INSERT INTO TOPOLOGY (created_at, name, number, project_id, rooms, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Topology testDelete', 3, 1, '[]' FORMAT JSON, '2024-03-01T15:31:41.579969Z', 3); + +INSERT INTO TOPOLOGY (created_at, name, number, project_id, rooms, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Topology testDeleteUsed', 4, 1, '[]' FORMAT JSON, '2024-03-01T15:31:41.579969Z', 4); + +UPDATE Project p +SET p.topologies_created = 4, p.updated_at = '2024-03-01T15:31:41.579969Z' +WHERE p.id = 1; + +-- -------------------------------------------------------------------------------- +-- Traces +-- -------------------------------------------------------------------------------- + +INSERT INTO TRACE (id, name, type) +VALUES ('bitbrains-small', 'Bitbrains Small', 'small'); + +-- -------------------------------------------------------------------------------- +-- Scenario +-- -------------------------------------------------------------------------------- + +INSERT INTO SCENARIO (name, number, phenomena, portfolio_id, project_id, scheduler_name, topology_id, sampling_fraction, trace_id, id) +VALUES ('Test Scenario testDelete', 1, '{"failures": false, "interference": false}' FORMAT JSON, 1, 1, 'test', 1, 1.0, 'bitbrains-small', 1); + +INSERT INTO SCENARIO (name, number, phenomena, portfolio_id, project_id, scheduler_name, topology_id, sampling_fraction, trace_id, id) +VALUES ('Test Scenario testDeleteUsed', 2, '{"failures": false, "interference": false}' FORMAT JSON, 1, 1, 'test', 4, 1.0, 'bitbrains-small', 2); + + +UPDATE Project p +SET p.scenarios_created = 2, p.updated_at = '2024-03-01T15:31:41.579969Z' +WHERE p.id = 1; + +-- -------------------------------------------------------------------------------- +-- Job +-- -------------------------------------------------------------------------------- + +INSERT INTO JOB (scenario_id, created_by, created_at, repeats, updated_at, state, runtime, results, id) +VALUES (1, 'test_user_1', '2024-03-01T15:31:41.579969Z', 1, '2024-03-01T15:31:41.579969Z', 'PENDING', 1, '{}' FORMAT JSON, 1); + +INSERT INTO JOB (scenario_id, created_by, created_at, repeats, updated_at, state, runtime, results, id) +VALUES (1, 'test_user_1', '2024-03-01T15:31:41.579969Z', 1, '2024-03-01T15:31:41.579969Z', 'PENDING', 1, '{}' FORMAT JSON, 2); diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java index feeac4d35..f52ede3a5 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java @@ -22,11 +22,10 @@ package org.opendc.web.server.rest; -import static io.restassured.RestAssured.when; +import static io.restassured.RestAssured.given; import io.quarkus.test.common.http.TestHTTPEndpoint; import io.quarkus.test.junit.QuarkusTest; -import io.restassured.http.ContentType; import org.junit.jupiter.api.Test; /** @@ -40,6 +39,6 @@ public final class SchedulerResourceTest { */ @Test public void testGetSchedulers() { - when().get().then().statusCode(200).contentType(ContentType.JSON); + given().get().then().statusCode(200); } } diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java index 5c5976db5..9da260594 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java @@ -41,7 +41,7 @@ public final class TraceResourceTest { */ @Test public void testGetAllEmpty() { - when().get().then().statusCode(200).contentType(ContentType.JSON); + when().get().then().statusCode(200); } /** @@ -49,7 +49,7 @@ public void testGetAllEmpty() { */ @Test public void testGetNonExisting() { - when().get("/unknown").then().statusCode(404).contentType(ContentType.JSON); + when().get("/unknown").then().statusCode(404); } /** diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java index 94b2cef03..09f60c0ac 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java @@ -23,7 +23,6 @@ package org.opendc.web.server.rest.runner; import static io.restassured.RestAssured.given; -import static io.restassured.RestAssured.when; import static org.hamcrest.Matchers.equalTo; import io.quarkus.test.common.http.TestHTTPEndpoint; @@ -44,7 +43,7 @@ public final class JobResourceTest { */ @Test public void testQueryWithoutToken() { - when().get().then().statusCode(401); + given().get().then().statusCode(401); } /** @@ -52,10 +51,10 @@ public void testQueryWithoutToken() { */ @Test @TestSecurity( - user = "test", + user = "test_user_1", roles = {"openid"}) public void testQueryInvalidScope() { - when().get().then().statusCode(403); + given().get().then().statusCode(403); } /** @@ -63,10 +62,10 @@ public void testQueryInvalidScope() { */ @Test @TestSecurity( - user = "test", + user = "test_user_1", roles = {"runner"}) public void testQuery() { - when().get().then().statusCode(200).contentType(ContentType.JSON).body("get(0).state", equalTo("PENDING")); + given().get().then().statusCode(200).contentType(ContentType.JSON).body("get(0).state", equalTo("PENDING")); } /** @@ -74,10 +73,10 @@ public void testQuery() { */ @Test @TestSecurity( - user = "test", + user = "test_user_1", roles = {"runner"}) public void testGetNonExisting() { - when().get("/0").then().statusCode(404).contentType(ContentType.JSON); + given().get("/0").then().statusCode(404); } /** @@ -85,10 +84,10 @@ public void testGetNonExisting() { */ @Test @TestSecurity( - user = "test", + user = "test_user_1", roles = {"runner"}) public void testGetExisting() { - when().get("/1").then().statusCode(200).contentType(ContentType.JSON).body("id", equalTo(1)); + given().get("/1").then().statusCode(200).contentType(ContentType.JSON).body("id", equalTo(1)); } /** @@ -96,7 +95,7 @@ public void testGetExisting() { */ @Test @TestSecurity( - user = "test", + user = "test_user_1", roles = {"runner"}) public void testUpdateNonExistent() { given().body(new org.opendc.web.proto.runner.Job.Update(JobState.PENDING, 0, null)) @@ -113,7 +112,7 @@ public void testUpdateNonExistent() { */ @Test @TestSecurity( - user = "test", + user = "test_user_1", roles = {"runner"}) public void testUpdateState() { given().body(new org.opendc.web.proto.runner.Job.Update(JobState.CLAIMED, 0, null)) @@ -131,7 +130,7 @@ public void testUpdateState() { */ @Test @TestSecurity( - user = "test", + user = "test_user_1", roles = {"runner"}) public void testUpdateInvalidInput() { given().body("{ \"test\": \"test\" }") diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java index a952d83f1..f23b4fc40 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java @@ -40,33 +40,55 @@ @TestHTTPEndpoint(PortfolioResource.class) public final class PortfolioResourceTest { /** - * Test that tries to obtain the list of portfolios belonging to a project. + * Test that tries to obtain the list of all portfolios belonging to a project. */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) - public void testGetForProject() { - given().pathParam("project", 1).when().get().then().statusCode(200).contentType(ContentType.JSON); + public void testGetAllForProject() { + given().pathParam("project", 1).when().get().then().statusCode(200); } /** - * Test that tries to obtain the list of portfolios belonging to a project without authorization. + * Test that tries to obtain the list of all portfolios belonging to a project + * without authorization. + * + * TODO: Why is this an empty list, and not a 403 message? */ @Test @TestSecurity( - user = "unknown", + user = "test_user_1", roles = {"openid"}) - public void testGetForProjectNoAuthorization() { - given().pathParam("project", 1).when().get().then().statusCode(200).contentType(ContentType.JSON); + public void testGetAllForProjectNoAuthorization() { + given().pathParam("project", 1).when().get().then().statusCode(200); } /** - * Test that tries to create a topology for a project. + * Test that tries to create a portfolio for a project that exists and user has permission. */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", + roles = {"openid"}) + public void testCreate() { + given().pathParam("project", "1") + .body(new org.opendc.web.proto.user.Portfolio.Create("Test Portfolio New", new Targets(Set.of(), 1))) + .contentType(ContentType.JSON) + .when() + .post() + .then() + .statusCode(200) + .contentType(ContentType.JSON) + .body("name", equalTo("Test Portfolio New")); + } + + /** + * Test that tries to create a topology for a project that does not exist. + */ + @Test + @TestSecurity( + user = "test_user_1", roles = {"openid"}) public void testCreateNonExistent() { given().pathParam("project", "0") @@ -75,45 +97,42 @@ public void testCreateNonExistent() { .when() .post() .then() - .statusCode(404) - .contentType(ContentType.JSON); + .statusCode(404); } /** - * Test that tries to create a topology for a project. + * Test that tries to create a portfolio for a project that does exist but the user does not have permission. */ @Test @TestSecurity( - user = "viewer", + user = "test_user_2", roles = {"openid"}) - public void testCreateNotPermitted() { + public void testCreateViewer() { given().pathParam("project", "1") .body(new org.opendc.web.proto.user.Portfolio.Create("test", new Targets(Set.of(), 1))) .contentType(ContentType.JSON) .when() .post() .then() - .statusCode(403) - .contentType(ContentType.JSON); + .statusCode(403); } /** - * Test that tries to create a portfolio for a project. + * Test that tries to create a portfolio for a project that does exist but the user does not have permission. + * TODO: This should return 403 but does not because there is no user class */ @Test @TestSecurity( - user = "editor", + user = "test_user_1", roles = {"openid"}) - public void testCreate() { - given().pathParam("project", "1") + public void testCreateNotPermitted() { + given().pathParam("project", "3") .body(new org.opendc.web.proto.user.Portfolio.Create("test", new Targets(Set.of(), 1))) .contentType(ContentType.JSON) .when() .post() .then() - .statusCode(200) - .contentType(ContentType.JSON) - .body("name", equalTo("test")); + .statusCode(404); } /** @@ -121,7 +140,7 @@ public void testCreate() { */ @Test @TestSecurity( - user = "editor", + user = "test_user_1", roles = {"openid"}) public void testCreateEmpty() { given().pathParam("project", "1") @@ -130,8 +149,7 @@ public void testCreateEmpty() { .when() .post() .then() - .statusCode(400) - .contentType(ContentType.JSON); + .statusCode(400); } /** @@ -139,7 +157,7 @@ public void testCreateEmpty() { */ @Test @TestSecurity( - user = "editor", + user = "test_user_1", roles = {"openid"}) public void testCreateBlankName() { given().pathParam("project", "1") @@ -148,8 +166,7 @@ public void testCreateBlankName() { .when() .post() .then() - .statusCode(400) - .contentType(ContentType.JSON); + .statusCode(400); } /** @@ -165,7 +182,7 @@ public void testGetWithoutToken() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"runner"}) public void testGetInvalidToken() { given().pathParam("project", "1").when().get("/1").then().statusCode(403); @@ -176,15 +193,10 @@ public void testGetInvalidToken() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetNonExisting() { - given().pathParam("project", "1") - .when() - .get("/0") - .then() - .statusCode(404) - .contentType(ContentType.JSON); + given().pathParam("project", "1").when().get("/0").then().statusCode(404); } /** @@ -192,15 +204,10 @@ public void testGetNonExisting() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetNonExistingProject() { - given().pathParam("project", "0") - .when() - .get("/1") - .then() - .statusCode(404) - .contentType(ContentType.JSON); + given().pathParam("project", "0").when().get("/1").then().statusCode(404); } /** @@ -208,7 +215,7 @@ public void testGetNonExistingProject() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetExisting() { given().pathParam("project", "1") @@ -225,7 +232,7 @@ public void testGetExisting() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDeleteNonExistent() { given().pathParam("project", "1").when().delete("/0").then().statusCode(404); @@ -236,7 +243,7 @@ public void testDeleteNonExistent() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDeleteNonExistentProject() { given().pathParam("project", "0").when().delete("/1").then().statusCode(404); @@ -247,26 +254,21 @@ public void testDeleteNonExistentProject() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDelete() { - int number = given().pathParam("project", "1") - .body(new org.opendc.web.proto.user.Portfolio.Create("Delete Portfolio", new Targets(Set.of(), 1))) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .extract() - .path("number"); + given().pathParam("project", "1").when().delete("/2").then().statusCode(200); + } - given().pathParam("project", "1") - .when() - .delete("/" + number) - .then() - .statusCode(200) - .contentType(ContentType.JSON); + /** + * Test to delete a portfolio as an editor. + */ + @Test + @TestSecurity( + user = "test_user_3", + roles = {"openid"}) + public void testDeleteEditor() { + given().pathParam("project", "1").when().delete("/3").then().statusCode(200); } /** @@ -274,14 +276,9 @@ public void testDelete() { */ @Test @TestSecurity( - user = "viewer", + user = "test_user_2", roles = {"openid"}) public void testDeleteAsViewer() { - given().pathParam("project", "1") - .when() - .delete("/1") - .then() - .statusCode(403) - .contentType(ContentType.JSON); + given().pathParam("project", "1").when().delete("/1").then().statusCode(403); } } diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java index 58042833b..270dbae9b 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java @@ -43,7 +43,7 @@ public final class PortfolioScenarioResourceTest { /** * Test that tries to obtain a portfolio without token. */ - // @Test + @Test public void testGetWithoutToken() { given().pathParam("project", "1") .pathParam("portfolio", "1") @@ -58,7 +58,7 @@ public void testGetWithoutToken() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"runner"}) public void testGetInvalidToken() { given().pathParam("project", "1") @@ -72,12 +72,12 @@ public void testGetInvalidToken() { /** * Test that tries to obtain a scenario without authorization. */ - // @Test - // @TestSecurity( - // user = "unknown", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testGetUnauthorized() { - given().pathParam("project", "1") + given().pathParam("project", "2") .pathParam("portfolio", "1") .when() .get() @@ -88,28 +88,28 @@ public void testGetUnauthorized() { /** * Test that tries to obtain a scenario. + * TODO: shouldn't this be all scenarios? */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testGet() { given().pathParam("project", "1") .pathParam("portfolio", "1") .when() .get() .then() - .statusCode(200) - .contentType(ContentType.JSON); + .statusCode(200); } /** - * Test that tries to create a scenario for a portfolio. + * Test that tries to create a scenario for a portfolio that does not exist in a project that can be accessed. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateNonExistent() { given().pathParam("project", "1") .pathParam("portfolio", "0") @@ -126,13 +126,13 @@ public void testCreateNonExistent() { /** * Test that tries to create a scenario for a portfolio without authorization. */ - // @Test - // @TestSecurity( - // user = "unknown", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateUnauthorized() { - given().pathParam("project", "1") - .pathParam("portfolio", "0") + given().pathParam("project", "2") + .pathParam("portfolio", "1") .body(new Scenario.Create( "test", new Workload.Spec("test", 1.0), 1, new OperationalPhenomena(false, false), "test")) .contentType(ContentType.JSON) @@ -146,13 +146,13 @@ public void testCreateUnauthorized() { /** * Test that tries to create a scenario for a portfolio as a viewer. */ - // @Test - // @TestSecurity( - // user = "viewer", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_2", + roles = {"openid"}) public void testCreateAsViewer() { given().pathParam("project", "1") - .pathParam("portfolio", "0") + .pathParam("portfolio", "1") .body(new Scenario.Create( "test", new Workload.Spec("test", 1.0), 1, new OperationalPhenomena(false, false), "test")) .contentType(ContentType.JSON) @@ -166,15 +166,15 @@ public void testCreateAsViewer() { /** * Test that tries to create a scenario for a portfolio. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreate() { given().pathParam("project", "1") .pathParam("portfolio", "1") .body(new Scenario.Create( - "test", + "Test Scenario New", new Workload.Spec("bitbrains-small", 1.0), 1, new OperationalPhenomena(false, false), @@ -185,16 +185,16 @@ public void testCreate() { .then() .statusCode(200) .contentType(ContentType.JSON) - .body("name", equalTo("test")); + .body("name", equalTo("Test Scenario New")); } /** * Test to create a project with an empty body. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateEmpty() { given().pathParam("project", "1") .pathParam("portfolio", "1") @@ -210,10 +210,10 @@ public void testCreateEmpty() { /** * Test to create a project with a blank name. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateBlankName() { given().pathParam("project", "1") .pathParam("portfolio", "1") @@ -228,12 +228,12 @@ public void testCreateBlankName() { } /** - * Test that tries to create a scenario for a portfolio. + * Test that tries to create a scenario for a portfolio with an unknown Topology. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateUnknownTopology() { given().pathParam("project", "1") .pathParam("portfolio", "1") @@ -252,12 +252,12 @@ public void testCreateUnknownTopology() { } /** - * Test that tries to create a scenario for a portfolio. + * Test that tries to create a scenario for a portfolio with an unknown Trace. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateUnknownTrace() { given().pathParam("project", "1") .pathParam("portfolio", "1") diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java index bd7cff9b9..450c0c0c5 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java @@ -24,7 +24,7 @@ import static io.restassured.RestAssured.given; import static io.restassured.RestAssured.when; -import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.*; import io.quarkus.test.common.http.TestHTTPEndpoint; import io.quarkus.test.junit.QuarkusTest; @@ -41,7 +41,7 @@ public final class ProjectResourceTest { /** * Test that tries to obtain all projects without token. */ - // @Test + @Test public void testGetAllWithoutToken() { when().get().then().statusCode(401); } @@ -51,30 +51,41 @@ public void testGetAllWithoutToken() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"runner"}) public void testGetAllWithInvalidScope() { when().get().then().statusCode(403); } + /** + * Test that tries to obtain when no projects have yet been made. + */ + @Test + @TestSecurity( + user = "test_user_4", + roles = {"openid"}) + public void testGetAllWithNoAvailableProjects() { + when().get().then().statusCode(200).contentType(ContentType.JSON).body("", empty()); + } + /** * Test that tries to obtain all project for a user. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_3", + roles = {"openid"}) public void testGetAll() { - when().get().then().statusCode(200).contentType(ContentType.JSON).body("get(0).name", equalTo("Test Project")); + given().get().then().statusCode(200).contentType(ContentType.JSON).body("", hasSize(4)); } /** * Test that tries to obtain a non-existent project. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testGetNonExisting() { when().get("/0").then().statusCode(404).contentType(ContentType.JSON); } @@ -82,106 +93,104 @@ public void testGetNonExisting() { /** * Test that tries to obtain a project. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testGetExisting() { - when().get("/1").then().statusCode(200).contentType(ContentType.JSON).body("id", equalTo(1)); + // Try to get the project + given().get("/1").then().statusCode(200).contentType(ContentType.JSON).body("id", equalTo(1)); } /** * Test that tries to create a project. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreate() { - given().body(new org.opendc.web.proto.user.Project.Create("test")) + given().body(new org.opendc.web.proto.user.Project.Create("Test Project New")) .contentType(ContentType.JSON) .when() .post() .then() .statusCode(200) .contentType(ContentType.JSON) - .body("name", equalTo("test")); + .body("name", equalTo("Test Project New")); } /** * Test to create a project with an empty body. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateEmpty() { - given().body("{}") - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(400) - .contentType(ContentType.JSON); + given().body("{}").contentType(ContentType.JSON).when().post().then().statusCode(400); } /** * Test to create a project with a blank name. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateBlankName() { given().body(new org.opendc.web.proto.user.Project.Create("")) .contentType(ContentType.JSON) .when() .post() .then() - .statusCode(400) - .contentType(ContentType.JSON); + .statusCode(400); + } + + /** + * Test to delete a project that is owned by the user. + */ + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) + public void testDelete() { + given().delete("/6").then().statusCode(200); } /** * Test to delete a non-existent project. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testDeleteNonExistent() { - when().delete("/0").then().statusCode(404).contentType(ContentType.JSON); + when().delete("/0").then().statusCode(404); } /** - * Test to delete a project. + * Test to delete a project which is not connected to the user. + * test_user_3 is not connected to project 1. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) - public void testDelete() { - int id = given().body(new org.opendc.web.proto.user.Project.Create("Delete Project")) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .extract() - .path("id"); - - when().delete("/" + id).then().statusCode(200).contentType(ContentType.JSON); + @Test + @TestSecurity( + user = "test_user_3", + roles = {"openid"}) + public void testDeleteNotConnected() { + when().delete("/1").then().statusCode(403); } /** * Test to delete a project which the user does not own. + * project 1 is owned by test_user_1, test_user_2 is a viewer + * should not be able to delete it */ - // @Test - // @TestSecurity( - // user = "viewer", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_2", + roles = {"openid"}) public void testDeleteNonOwner() { - when().delete("/1").then().statusCode(403).contentType(ContentType.JSON); + when().delete("/1").then().statusCode(403); } } diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java index a980e4e26..d81f9655c 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java @@ -28,13 +28,8 @@ import io.quarkus.test.common.http.TestHTTPEndpoint; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.security.TestSecurity; -import io.restassured.builder.RequestSpecBuilder; import io.restassured.http.ContentType; -import io.restassured.specification.RequestSpecification; import org.junit.jupiter.api.Test; -import org.opendc.web.proto.OperationalPhenomena; -import org.opendc.web.proto.Workload; -import org.opendc.web.proto.user.Scenario; /** * Test suite for {@link ScenarioResource}. @@ -47,10 +42,10 @@ public final class ScenarioResourceTest { */ @Test @TestSecurity( - user = "unknown", + user = "test_user_1", roles = {"openid"}) public void testGetAllUnauthorized() { - given().pathParam("project", "1").when().get().then().statusCode(404).contentType(ContentType.JSON); + given().pathParam("project", "2").when().get().then().statusCode(404); } /** @@ -58,10 +53,10 @@ public void testGetAllUnauthorized() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetAll() { - given().pathParam("project", "1").when().get().then().statusCode(200).contentType(ContentType.JSON); + given().pathParam("project", "1").when().get().then().statusCode(200); } /** @@ -77,7 +72,7 @@ public void testGetWithoutToken() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"runner"}) public void testGetInvalidToken() { given().pathParam("project", "1").when().get("/1").then().statusCode(403); @@ -88,7 +83,7 @@ public void testGetInvalidToken() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetNonExisting() { given().pathParam("project", "1") @@ -100,14 +95,14 @@ public void testGetNonExisting() { } /** - * Test that tries to obtain a scenario. + * Test that tries to obtain a scenario when it does not have authority to get to the project. */ @Test @TestSecurity( - user = "unknown", + user = "test_user_1", roles = {"openid"}) public void testGetExistingUnauthorized() { - given().pathParam("project", "1") + given().pathParam("project", "2") .when() .get("/1") .then() @@ -120,7 +115,7 @@ public void testGetExistingUnauthorized() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetExisting() { given().pathParam("project", "1") @@ -137,7 +132,7 @@ public void testGetExisting() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDeleteNonExistent() { given().pathParam("project", "1").when().delete("/0").then().statusCode(404); @@ -148,10 +143,10 @@ public void testDeleteNonExistent() { */ @Test @TestSecurity( - user = "unknown", + user = "test_user_1", roles = {"openid"}) public void testDeleteUnauthorized() { - given().pathParam("project", "1").when().delete("/1").then().statusCode(404); + given().pathParam("project", "2").when().delete("/1").then().statusCode(404); } /** @@ -159,7 +154,7 @@ public void testDeleteUnauthorized() { */ @Test @TestSecurity( - user = "viewer", + user = "test_user_2", roles = {"openid"}) public void testDeleteAsViewer() { given().pathParam("project", "1").when().delete("/1").then().statusCode(403); @@ -170,32 +165,12 @@ public void testDeleteAsViewer() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDelete() { - RequestSpecification spec = new RequestSpecBuilder() - .setBasePath("/projects/1/portfolios/1/scenarios") - .build(); - - int number = given(spec) - .body(new Scenario.Create( - "test", - new Workload.Spec("bitbrains-small", 1.0), - 1, - new OperationalPhenomena(false, false), - "test")) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .extract() - .path("number"); - given().pathParam("project", "1") .when() - .delete("/" + number) + .delete("/1") .then() .statusCode(200) .contentType(ContentType.JSON); diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java index c0746e7ae..277376e53 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java @@ -44,7 +44,7 @@ public final class TopologyResourceTest { */ @Test @TestSecurity( - user = "unknown", + user = "test_user_4", roles = {"openid"}) public void testGetAllWithoutAuth() { given().pathParam("project", "1") @@ -58,21 +58,22 @@ public void testGetAllWithoutAuth() { /** * Test that tries to obtain the list of topologies belonging to a project. + * TODO: check if any topology comes back */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetAll() { - given().pathParam("project", "1").when().get().then().statusCode(200).contentType(ContentType.JSON); + given().pathParam("project", "1").when().get().then().statusCode(200); } /** - * Test that tries to create a topology for a project. + * Test that tries to create a topology for a project that does not exist. */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testCreateNonExistent() { given().pathParam("project", "0") @@ -81,26 +82,25 @@ public void testCreateNonExistent() { .when() .post() .then() - .statusCode(404) - .contentType(ContentType.JSON); + .statusCode(404); } /** - * Test that tries to create a topology for a project as viewer. + * Test that tries to create a topology for a project while not authorized. + * TODO: should probably return 403, but this does not work in the current system */ @Test @TestSecurity( - user = "viewer", + user = "test_user_1", roles = {"openid"}) public void testCreateUnauthorized() { - given().pathParam("project", "1") + given().pathParam("project", "2") .body(new Topology.Create("test", List.of())) .contentType(ContentType.JSON) .when() .post() .then() - .statusCode(403) - .contentType(ContentType.JSON); + .statusCode(404); } /** @@ -108,18 +108,18 @@ public void testCreateUnauthorized() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testCreate() { given().pathParam("project", "1") - .body(new Topology.Create("test", List.of())) + .body(new Topology.Create("Test Topology New", List.of())) .contentType(ContentType.JSON) .when() .post() .then() .statusCode(200) .contentType(ContentType.JSON) - .body("name", equalTo("test")); + .body("name", equalTo("Test Topology New")); } /** @@ -127,7 +127,7 @@ public void testCreate() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testCreateEmpty() { given().pathParam("project", "1") @@ -136,8 +136,7 @@ public void testCreateEmpty() { .when() .post() .then() - .statusCode(400) - .contentType(ContentType.JSON); + .statusCode(400); } /** @@ -145,7 +144,7 @@ public void testCreateEmpty() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testCreateBlankName() { given().pathParam("project", "1") @@ -154,8 +153,7 @@ public void testCreateBlankName() { .when() .post() .then() - .statusCode(400) - .contentType(ContentType.JSON); + .statusCode(400); } /** @@ -171,7 +169,7 @@ public void testGetWithoutToken() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"runner"}) public void testGetInvalidToken() { given().pathParam("project", "1").when().get("/1").then().statusCode(403); @@ -182,15 +180,10 @@ public void testGetInvalidToken() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetNonExisting() { - given().pathParam("project", "1") - .when() - .get("/0") - .then() - .statusCode(404) - .contentType(ContentType.JSON); + given().pathParam("project", "1").when().get("/0").then().statusCode(404); } /** @@ -198,15 +191,10 @@ public void testGetNonExisting() { */ @Test @TestSecurity( - user = "unknown", + user = "test_user_1", roles = {"openid"}) public void testGetUnauthorized() { - given().pathParam("project", "1") - .when() - .get("/1") - .then() - .statusCode(404) - .contentType(ContentType.JSON); + given().pathParam("project", "2").when().get("/1").then().statusCode(404); } /** @@ -214,7 +202,7 @@ public void testGetUnauthorized() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetExisting() { given().pathParam("project", "1") @@ -231,7 +219,7 @@ public void testGetExisting() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testUpdateNonExistent() { given().pathParam("project", "1") @@ -248,10 +236,10 @@ public void testUpdateNonExistent() { */ @Test @TestSecurity( - user = "unknown", + user = "test_user_1", roles = {"openid"}) public void testUpdateUnauthorized() { - given().pathParam("project", "1") + given().pathParam("project", "2") .body(new Topology.Update(List.of())) .contentType(ContentType.JSON) .when() @@ -262,10 +250,11 @@ public void testUpdateUnauthorized() { /** * Test to update a topology as a viewer. + * TODO: should return 403, but currently returns 404 */ @Test @TestSecurity( - user = "viewer", + user = "test_user_2", roles = {"openid"}) public void testUpdateAsViewer() { given().pathParam("project", "1") @@ -283,7 +272,7 @@ public void testUpdateAsViewer() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testUpdate() { given().pathParam("project", "1") @@ -292,8 +281,7 @@ public void testUpdate() { .when() .put("/1") .then() - .statusCode(200) - .contentType(ContentType.JSON); + .statusCode(200); } /** @@ -301,7 +289,7 @@ public void testUpdate() { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDeleteNonExistent() { given().pathParam("project", "1").when().delete("/0").then().statusCode(404); @@ -312,10 +300,10 @@ public void testDeleteNonExistent() { */ @Test @TestSecurity( - user = "unknown", + user = "test_user_1", roles = {"openid"}) public void testDeleteUnauthorized() { - given().pathParam("project", "1").when().delete("/1").then().statusCode(404); + given().pathParam("project", "2").when().delete("/1").then().statusCode(404); } /** @@ -323,50 +311,46 @@ public void testDeleteUnauthorized() { */ @Test @TestSecurity( - user = "viewer", + user = "test_user_2", roles = {"openid"}) public void testDeleteAsViewer() { given().pathParam("project", "1").when().delete("/1").then().statusCode(403); } + /** + * Test to delete a topology as a viewer. + */ + @Test + @TestSecurity( + user = "test_user_3", + roles = {"openid"}) + public void testDeleteAsEditor() { + given().pathParam("project", "1").when().delete("/2").then().statusCode(200); + } + /** * Test to delete a topology. */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDelete() { - int number = given().pathParam("project", "1") - .body(new Topology.Create("Delete Topology", List.of())) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .extract() - .path("number"); - - given().pathParam("project", "1") - .when() - .delete("/" + number) - .then() - .statusCode(200) - .contentType(ContentType.JSON); + given().pathParam("project", "1").when().delete("/3").then().statusCode(200); } /** * Test to delete a topology that is still being used by a scenario. + * TODO: fix later */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDeleteUsed() { given().pathParam("project", "1") .when() - .delete("/1") // Topology 1 is still used by scenario 1 and 2 + .delete("/4") // Topology 1 is still used by scenario 1 and 2 .then() .statusCode(403) .contentType(ContentType.JSON); diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java index d1d820979..91e3eb661 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java @@ -33,9 +33,9 @@ import io.quarkus.panache.mock.PanacheMock; import io.quarkus.test.junit.QuarkusTest; +import jakarta.persistence.EntityExistsException; import java.time.Duration; import java.time.LocalDate; -import javax.persistence.EntityExistsException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; diff --git a/opendc-web/opendc-web-ui-quarkus-deployment/build.gradle.kts b/opendc-web/opendc-web-ui-quarkus-deployment/build.gradle.kts index 5a42aaea9..6be49e221 100644 --- a/opendc-web/opendc-web-ui-quarkus-deployment/build.gradle.kts +++ b/opendc-web/opendc-web-ui-quarkus-deployment/build.gradle.kts @@ -22,7 +22,7 @@ description = "Quarkus extension for serving OpenDC web interface" -/* Build configuration */ +// Build configuration plugins { `java-library-conventions` } diff --git a/opendc-web/opendc-web-ui/build.gradle.kts b/opendc-web/opendc-web-ui/build.gradle.kts index 79160a2e7..777098d4d 100644 --- a/opendc-web/opendc-web-ui/build.gradle.kts +++ b/opendc-web/opendc-web-ui/build.gradle.kts @@ -43,27 +43,29 @@ node { version.set(libs.versions.node.get()) } -val formatTask = tasks.register("prettierFormat") { - group = "formatting" - description = "Use Prettier to format the JavaScript codebase" - - args.set(listOf("run", "format")) - dependsOn(tasks.npmInstall) - inputs.dir("src") - inputs.files("package.json", "next.config.js", ".prettierrc.yaml") - outputs.upToDateWhen { true } -} +val formatTask = + tasks.register("prettierFormat") { + group = "formatting" + description = "Use Prettier to format the JavaScript codebase" + + args.set(listOf("run", "format")) + dependsOn(tasks.npmInstall) + inputs.dir("src") + inputs.files("package.json", "next.config.js", ".prettierrc.yaml") + outputs.upToDateWhen { true } + } -val lintTask = tasks.register("nextLint") { - group = "verification" - description = "Use ESLint to check for problems" +val lintTask = + tasks.register("nextLint") { + group = "verification" + description = "Use ESLint to check for problems" - args.set(listOf("run", "lint")) - dependsOn(tasks.npmInstall) - inputs.dir("src") - inputs.files("package.json", "next.config.js", ".eslintrc") - outputs.upToDateWhen { true } -} + args.set(listOf("run", "lint")) + dependsOn(tasks.npmInstall) + inputs.dir("src") + inputs.files("package.json", "next.config.js", ".eslintrc") + outputs.upToDateWhen { true } + } tasks.register("nextDev") { group = "build" @@ -77,30 +79,32 @@ tasks.register("nextDev") { outputs.upToDateWhen { true } } -val buildTask = tasks.register("nextBuild") { - group = "build" - description = "Build the Next.js project" - - args.set(listOf("run", "build")) - - val env = listOf( - "NEXT_PUBLIC_API_BASE_URL", - "NEXT_PUBLIC_SENTRY_DSN", - "NEXT_PUBLIC_AUTH0_DOMAIN", - "NEXT_PUBLIC_AUTH0_CLIENT_ID", - "NEXT_PUBLIC_AUTH0_AUDIENCE" - ) - for (envvar in env) { - environment.put(envvar, "%%$envvar%%") +val buildTask = + tasks.register("nextBuild") { + group = "build" + description = "Build the Next.js project" + + args.set(listOf("run", "build")) + + val env = + listOf( + "NEXT_PUBLIC_API_BASE_URL", + "NEXT_PUBLIC_SENTRY_DSN", + "NEXT_PUBLIC_AUTH0_DOMAIN", + "NEXT_PUBLIC_AUTH0_CLIENT_ID", + "NEXT_PUBLIC_AUTH0_AUDIENCE", + ) + for (envvar in env) { + environment.put(envvar, "%%$envvar%%") + } + + dependsOn(tasks.npmInstall) + inputs.dir(project.fileTree("src")) + inputs.dir("node_modules") + inputs.files("package.json", "next.config.js") + outputs.dir(layout.buildDirectory.dir("next")) } - dependsOn(tasks.npmInstall) - inputs.dir(project.fileTree("src")) - inputs.dir("node_modules") - inputs.files("package.json", "next.config.js") - outputs.dir(layout.buildDirectory.dir("next")) -} - tasks.register("nextStart") { group = "build" description = "Build the Next.js project" diff --git a/opendc-workflow/opendc-workflow-api/build.gradle.kts b/opendc-workflow/opendc-workflow-api/build.gradle.kts index 03569d8ca..ac94082b6 100644 --- a/opendc-workflow/opendc-workflow-api/build.gradle.kts +++ b/opendc-workflow/opendc-workflow-api/build.gradle.kts @@ -22,7 +22,7 @@ description = "Workflow orchestration service API for OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-workflow/opendc-workflow-api/src/main/kotlin/org/opendc/workflow/api/Job.kt b/opendc-workflow/opendc-workflow-api/src/main/kotlin/org/opendc/workflow/api/Job.kt index b59ad6dab..92df6be6d 100644 --- a/opendc-workflow/opendc-workflow-api/src/main/kotlin/org/opendc/workflow/api/Job.kt +++ b/opendc-workflow/opendc-workflow-api/src/main/kotlin/org/opendc/workflow/api/Job.kt @@ -36,7 +36,7 @@ public data class Job( val uid: UUID, val name: String, val tasks: Set, - val metadata: Map = emptyMap() + val metadata: Map = emptyMap(), ) { override fun equals(other: Any?): Boolean = other is Job && uid == other.uid diff --git a/opendc-workflow/opendc-workflow-api/src/main/kotlin/org/opendc/workflow/api/Task.kt b/opendc-workflow/opendc-workflow-api/src/main/kotlin/org/opendc/workflow/api/Task.kt index f805c2108..a14cfd110 100644 --- a/opendc-workflow/opendc-workflow-api/src/main/kotlin/org/opendc/workflow/api/Task.kt +++ b/opendc-workflow/opendc-workflow-api/src/main/kotlin/org/opendc/workflow/api/Task.kt @@ -36,7 +36,7 @@ public data class Task( val uid: UUID, val name: String, val dependencies: Set, - val metadata: Map = emptyMap() + val metadata: Map = emptyMap(), ) { override fun equals(other: Any?): Boolean = other is Task && uid == other.uid diff --git a/opendc-workflow/opendc-workflow-service/build.gradle.kts b/opendc-workflow/opendc-workflow-service/build.gradle.kts index e9e31656f..fdfbf82af 100644 --- a/opendc-workflow/opendc-workflow-service/build.gradle.kts +++ b/opendc-workflow/opendc-workflow-service/build.gradle.kts @@ -22,7 +22,7 @@ description = "Workflow orchestration service for OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/WorkflowService.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/WorkflowService.kt index 07b43b6dc..4efc79536 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/WorkflowService.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/WorkflowService.kt @@ -73,7 +73,7 @@ public interface WorkflowService : AutoCloseable { jobAdmissionPolicy: JobAdmissionPolicy, jobOrderPolicy: JobOrderPolicy, taskEligibilityPolicy: TaskEligibilityPolicy, - taskOrderPolicy: TaskOrderPolicy + taskOrderPolicy: TaskOrderPolicy, ): WorkflowService { return WorkflowServiceImpl( dispatcher, @@ -82,7 +82,7 @@ public interface WorkflowService : AutoCloseable { jobAdmissionPolicy, jobOrderPolicy, taskEligibilityPolicy, - taskOrderPolicy + taskOrderPolicy, ) } } diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/internal/TaskStatus.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/internal/TaskStatus.kt index fe941d097..e5475ee62 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/internal/TaskStatus.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/internal/TaskStatus.kt @@ -29,5 +29,5 @@ public enum class TaskStatus { CREATED, READY, ACTIVE, - FINISHED + FINISHED, } diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/internal/WorkflowServiceImpl.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/internal/WorkflowServiceImpl.kt index d54584b31..93a55c3da 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/internal/WorkflowServiceImpl.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/internal/WorkflowServiceImpl.kt @@ -59,7 +59,7 @@ public class WorkflowServiceImpl( jobAdmissionPolicy: JobAdmissionPolicy, jobOrderPolicy: JobOrderPolicy, taskEligibilityPolicy: TaskEligibilityPolicy, - taskOrderPolicy: TaskOrderPolicy + taskOrderPolicy: TaskOrderPolicy, ) : WorkflowService, ServerWatcher { /** * The [CoroutineScope] of the service bounded by the lifecycle of the service. @@ -109,47 +109,48 @@ public class WorkflowServiceImpl( /** * The root listener of this scheduler. */ - private val rootListener = object : WorkflowSchedulerListener { - /** - * The listeners to delegate to. - */ - val listeners = mutableListOf() - - override fun jobSubmitted(job: JobState) { - listeners.forEach { it.jobSubmitted(job) } - } + private val rootListener = + object : WorkflowSchedulerListener { + /** + * The listeners to delegate to. + */ + val listeners = mutableListOf() + + override fun jobSubmitted(job: JobState) { + listeners.forEach { it.jobSubmitted(job) } + } - override fun jobStarted(job: JobState) { - listeners.forEach { it.jobStarted(job) } - } + override fun jobStarted(job: JobState) { + listeners.forEach { it.jobStarted(job) } + } - override fun jobFinished(job: JobState) { - listeners.forEach { it.jobFinished(job) } - } + override fun jobFinished(job: JobState) { + listeners.forEach { it.jobFinished(job) } + } - override fun taskReady(task: TaskState) { - listeners.forEach { it.taskReady(task) } - } + override fun taskReady(task: TaskState) { + listeners.forEach { it.taskReady(task) } + } - override fun taskAssigned(task: TaskState) { - listeners.forEach { it.taskAssigned(task) } - } + override fun taskAssigned(task: TaskState) { + listeners.forEach { it.taskAssigned(task) } + } - override fun taskStarted(task: TaskState) { - listeners.forEach { it.taskStarted(task) } - } + override fun taskStarted(task: TaskState) { + listeners.forEach { it.taskStarted(task) } + } - override fun taskFinished(task: TaskState) { - listeners.forEach { it.taskFinished(task) } + override fun taskFinished(task: TaskState) { + listeners.forEach { it.taskFinished(task) } + } } - } - private var _workflowsSubmitted: Int = 0 - private var _workflowsRunning: Int = 0 - private var _workflowsFinished: Int = 0 - private var _tasksSubmitted: Int = 0 - private var _tasksRunning: Int = 0 - private var _tasksFinished: Int = 0 + private var localWorkflowsSubmitted: Int = 0 + private var localWorkflowsRunning: Int = 0 + private var localWorkflowsFinished: Int = 0 + private var localTasksSubmitted: Int = 0 + private var localTasksRunning: Int = 0 + private var localTasksFinished: Int = 0 /** * The [Pacer] to use for scheduling the scheduler cycles. @@ -170,37 +171,46 @@ public class WorkflowServiceImpl( scope.launch { image = computeClient.newImage("workflow-runner") } } - override suspend fun invoke(job: Job): Unit = suspendCancellableCoroutine { cont -> - // J1 Incoming Jobs - val jobInstance = JobState(job, clock.millis(), cont) - val instances = job.tasks.associateWith { - TaskState(jobInstance, it) - } + override suspend fun invoke(job: Job): Unit = + suspendCancellableCoroutine { cont -> + // J1 Incoming Jobs + val jobInstance = JobState(job, clock.millis(), cont) + val instances = + job.tasks.associateWith { + TaskState(jobInstance, it) + } - for ((task, instance) in instances) { - instance.dependencies.addAll(task.dependencies.map { instances[it]!! }) - task.dependencies.forEach { - instances[it]!!.dependents.add(instance) - } + for ((task, instance) in instances) { + instance.dependencies.addAll(task.dependencies.map { instances[it]!! }) + task.dependencies.forEach { + instances[it]!!.dependents.add(instance) + } - // If the task has no dependency, it is a root task and can immediately be evaluated - if (instance.isRoot) { - instance.state = TaskStatus.READY - } + // If the task has no dependency, it is a root task and can immediately be evaluated + if (instance.isRoot) { + instance.state = TaskStatus.READY + } - _tasksSubmitted++ - } + localTasksSubmitted++ + } - instances.values.toCollection(jobInstance.tasks) - incomingJobs += jobInstance - rootListener.jobSubmitted(jobInstance) - _workflowsSubmitted++ + instances.values.toCollection(jobInstance.tasks) + incomingJobs += jobInstance + rootListener.jobSubmitted(jobInstance) + localWorkflowsSubmitted++ - pacer.enqueue() - } + pacer.enqueue() + } override fun getSchedulerStats(): SchedulerStats { - return SchedulerStats(_workflowsSubmitted, _workflowsRunning, _workflowsFinished, _tasksSubmitted, _tasksRunning, _tasksFinished) + return SchedulerStats( + localWorkflowsSubmitted, + localWorkflowsRunning, + localWorkflowsFinished, + localTasksSubmitted, + localTasksRunning, + localTasksFinished, + ) } override fun close() { @@ -240,7 +250,7 @@ public class WorkflowServiceImpl( jobQueue.add(jobInstance) activeJobs += jobInstance - _workflowsRunning++ + localWorkflowsRunning++ rootListener.jobStarted(jobInstance) } @@ -286,18 +296,20 @@ public class WorkflowServiceImpl( val cores = instance.task.metadata[WORKFLOW_TASK_CORES] as? Int ?: 1 val image = image scope.launch { - val flavor = computeClient.newFlavor( - instance.task.name, - cores, - 1000 - ) // TODO How to determine memory usage for workflow task - val server = computeClient.newServer( - instance.task.name, - image, - flavor, - start = false, - meta = instance.task.metadata - ) + val flavor = + computeClient.newFlavor( + instance.task.name, + cores, + 1000, + ) // TODO How to determine memory usage for workflow task + val server = + computeClient.newServer( + instance.task.name, + image, + flavor, + start = false, + meta = instance.task.metadata, + ) instance.state = TaskStatus.ACTIVE instance.server = server @@ -313,13 +325,16 @@ public class WorkflowServiceImpl( } } - override fun onStateChanged(server: Server, newState: ServerState) { + override fun onStateChanged( + server: Server, + newState: ServerState, + ) { when (newState) { ServerState.PROVISIONING -> {} ServerState.RUNNING -> { val task = taskByServer.getValue(server) task.startedAt = clock.millis() - _tasksRunning++ + localTasksRunning++ rootListener.taskStarted(task) } ServerState.TERMINATED, ServerState.ERROR -> { @@ -336,8 +351,8 @@ public class WorkflowServiceImpl( job.tasks.remove(task) activeTasks -= task - _tasksRunning-- - _tasksFinished++ + localTasksRunning-- + localTasksFinished++ rootListener.taskFinished(task) // Add job roots to the scheduling queue @@ -363,8 +378,8 @@ public class WorkflowServiceImpl( private fun finishJob(job: JobState) { activeJobs -= job - _workflowsRunning-- - _workflowsFinished++ + localWorkflowsRunning-- + localWorkflowsFinished++ rootListener.jobFinished(job) job.cont.resume(Unit) diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/DurationJobOrderPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/DurationJobOrderPolicy.kt index 1b5b91b99..7ae3244e1 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/DurationJobOrderPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/DurationJobOrderPolicy.kt @@ -47,17 +47,21 @@ public data class DurationJobOrderPolicy(val ascending: Boolean = true) : JobOrd get() = results[this]!! override fun jobSubmitted(job: JobState) { - results[job.job] = job.job.toposort().map { task -> - val estimable = task.metadata[WORKFLOW_TASK_DEADLINE] as? Long? - estimable ?: Long.MAX_VALUE - }.sum() + results[job.job] = + job.job.toposort().map { task -> + val estimable = task.metadata[WORKFLOW_TASK_DEADLINE] as? Long? + estimable ?: Long.MAX_VALUE + }.sum() } override fun jobFinished(job: JobState) { results.remove(job.job) } - override fun compare(o1: JobState, o2: JobState): Int { + override fun compare( + o1: JobState, + o2: JobState, + ): Int { return compareValuesBy(o1, o2) { it.job.duration }.let { if (ascending) it else -it } } } diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/JobAdmissionPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/JobAdmissionPolicy.kt index ed3acff7d..475c8f976 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/JobAdmissionPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/JobAdmissionPolicy.kt @@ -65,6 +65,6 @@ public interface JobAdmissionPolicy : StagePolicy { /** * Deny the current job and also stop admitting jobs. */ - STOP(false, true) + STOP(false, true), } } diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/LimitJobAdmissionPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/LimitJobAdmissionPolicy.kt index bd4165465..f1b812592 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/LimitJobAdmissionPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/LimitJobAdmissionPolicy.kt @@ -31,16 +31,15 @@ import org.opendc.workflow.service.internal.WorkflowServiceImpl * @property limit The maximum number of concurrent jobs in the system. */ public data class LimitJobAdmissionPolicy(public val limit: Int) : JobAdmissionPolicy { - override fun invoke(scheduler: WorkflowServiceImpl): JobAdmissionPolicy.Logic = object : JobAdmissionPolicy.Logic { - override fun invoke( - job: JobState - ): JobAdmissionPolicy.Advice = - if (scheduler.activeJobs.size < limit) { - JobAdmissionPolicy.Advice.ADMIT - } else { - JobAdmissionPolicy.Advice.STOP - } - } + override fun invoke(scheduler: WorkflowServiceImpl): JobAdmissionPolicy.Logic = + object : JobAdmissionPolicy.Logic { + override fun invoke(job: JobState): JobAdmissionPolicy.Advice = + if (scheduler.activeJobs.size < limit) { + JobAdmissionPolicy.Advice.ADMIT + } else { + JobAdmissionPolicy.Advice.STOP + } + } override fun toString(): String = "Limit-Active($limit)" } diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/NullJobAdmissionPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/NullJobAdmissionPolicy.kt index 31f8f8db8..731a00473 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/NullJobAdmissionPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/NullJobAdmissionPolicy.kt @@ -29,9 +29,10 @@ import org.opendc.workflow.service.internal.WorkflowServiceImpl * A [JobAdmissionPolicy] that admits all jobs. */ public object NullJobAdmissionPolicy : JobAdmissionPolicy { - override fun invoke(scheduler: WorkflowServiceImpl): JobAdmissionPolicy.Logic = object : JobAdmissionPolicy.Logic { - override fun invoke(job: JobState): JobAdmissionPolicy.Advice = JobAdmissionPolicy.Advice.ADMIT - } + override fun invoke(scheduler: WorkflowServiceImpl): JobAdmissionPolicy.Logic = + object : JobAdmissionPolicy.Logic { + override fun invoke(job: JobState): JobAdmissionPolicy.Advice = JobAdmissionPolicy.Advice.ADMIT + } override fun toString(): String = "Always" } diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/RandomJobOrderPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/RandomJobOrderPolicy.kt index 4f8dc05bc..ea005e975 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/RandomJobOrderPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/job/RandomJobOrderPolicy.kt @@ -52,7 +52,10 @@ public object RandomJobOrderPolicy : JobOrderPolicy { ids.remove(job.job) } - override fun compare(o1: JobState, o2: JobState): Int { + override fun compare( + o1: JobState, + o2: JobState, + ): Int { return compareValuesBy(o1, o2) { ids.getValue(it.job) } } } diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/ActiveTaskOrderPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/ActiveTaskOrderPolicy.kt index 821d49643..48fdafe9c 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/ActiveTaskOrderPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/ActiveTaskOrderPolicy.kt @@ -55,7 +55,10 @@ public data class ActiveTaskOrderPolicy(public val ascending: Boolean = true) : active.merge(task.job, -1, Int::plus) } - override fun compare(o1: TaskState, o2: TaskState): Int { + override fun compare( + o1: TaskState, + o2: TaskState, + ): Int { return compareValuesBy(o1, o2) { active.getValue(it.job) }.let { if (ascending) it else -it } diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/CompletionTaskOrderPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/CompletionTaskOrderPolicy.kt index dae7ad999..104de1057 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/CompletionTaskOrderPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/CompletionTaskOrderPolicy.kt @@ -51,7 +51,10 @@ public data class CompletionTaskOrderPolicy(public val ascending: Boolean = true finished.merge(task.job, 1, Int::plus) } - override fun compare(o1: TaskState, o2: TaskState): Int { + override fun compare( + o1: TaskState, + o2: TaskState, + ): Int { return compareValuesBy(o1, o2) { finished.getValue(it.job) / it.job.tasks.size.toDouble() }.let { if (ascending) it else -it } diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DependenciesTaskOrderPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DependenciesTaskOrderPolicy.kt index 7786f6ecb..df9ebfada 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DependenciesTaskOrderPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DependenciesTaskOrderPolicy.kt @@ -29,9 +29,10 @@ import org.opendc.workflow.service.internal.WorkflowServiceImpl * A [TaskOrderPolicy] that orders tasks based on the number of dependency tasks it has. */ public data class DependenciesTaskOrderPolicy(public val ascending: Boolean = true) : TaskOrderPolicy { - override fun invoke(scheduler: WorkflowServiceImpl): Comparator = compareBy { - it.task.dependencies.size.let { if (ascending) it else -it } - } + override fun invoke(scheduler: WorkflowServiceImpl): Comparator = + compareBy { + it.task.dependencies.size.let { if (ascending) it else -it } + } override fun toString(): String { return "Task-Dependencies(${if (ascending) "asc" else "desc"})" diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DependentsTaskOrderPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DependentsTaskOrderPolicy.kt index 4fb835d7f..bbc203481 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DependentsTaskOrderPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DependentsTaskOrderPolicy.kt @@ -29,9 +29,10 @@ import org.opendc.workflow.service.internal.WorkflowServiceImpl * A [TaskOrderPolicy] that orders tasks based on the number of dependent tasks it has. */ public data class DependentsTaskOrderPolicy(public val ascending: Boolean = true) : TaskOrderPolicy { - override fun invoke(scheduler: WorkflowServiceImpl): Comparator = compareBy { - it.dependents.size.let { if (ascending) it else -it } - } + override fun invoke(scheduler: WorkflowServiceImpl): Comparator = + compareBy { + it.dependents.size.let { if (ascending) it else -it } + } override fun toString(): String { return "Task-Dependents(${if (ascending) "asc" else "desc"})" diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DurationHistoryTaskOrderPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DurationHistoryTaskOrderPolicy.kt index 3a634de70..e21acb412 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DurationHistoryTaskOrderPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DurationHistoryTaskOrderPolicy.kt @@ -51,7 +51,10 @@ public data class DurationHistoryTaskOrderPolicy(public val ascending: Boolean = results.getValue(task.job) += task.finishedAt - task.startedAt } - override fun compare(o1: TaskState, o2: TaskState): Int { + override fun compare( + o1: TaskState, + o2: TaskState, + ): Int { return compareValuesBy(o1, o2) { key -> val history = results.getValue(key.job) if (history.isEmpty()) { diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DurationTaskOrderPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DurationTaskOrderPolicy.kt index a2ca20869..170f33940 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DurationTaskOrderPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/DurationTaskOrderPolicy.kt @@ -35,7 +35,6 @@ import kotlin.collections.set * A [TaskOrderPolicy] orders tasks based on the pre-specified (approximate) duration of the task. */ public data class DurationTaskOrderPolicy(public val ascending: Boolean = true) : TaskOrderPolicy { - override fun invoke(scheduler: WorkflowServiceImpl): Comparator = object : Comparator, WorkflowSchedulerListener { private val results = HashMap() @@ -56,7 +55,10 @@ public data class DurationTaskOrderPolicy(public val ascending: Boolean = true) private val TaskState.duration: Long get() = results.getValue(task.uid) - override fun compare(o1: TaskState, o2: TaskState): Int { + override fun compare( + o1: TaskState, + o2: TaskState, + ): Int { return compareValuesBy(o1, o2) { state -> state.duration }.let { if (ascending) it else -it } diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/LimitTaskEligibilityPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/LimitTaskEligibilityPolicy.kt index d2edc2562..0e9c93da8 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/LimitTaskEligibilityPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/LimitTaskEligibilityPolicy.kt @@ -29,16 +29,15 @@ import org.opendc.workflow.service.internal.WorkflowServiceImpl * A [TaskEligibilityPolicy] that limits the total number of active tasks in the system. */ public data class LimitTaskEligibilityPolicy(val limit: Int) : TaskEligibilityPolicy { - override fun invoke(scheduler: WorkflowServiceImpl): TaskEligibilityPolicy.Logic = object : TaskEligibilityPolicy.Logic { - override fun invoke( - task: TaskState - ): TaskEligibilityPolicy.Advice = - if (scheduler.activeTasks.size < limit) { - TaskEligibilityPolicy.Advice.ADMIT - } else { - TaskEligibilityPolicy.Advice.STOP - } - } + override fun invoke(scheduler: WorkflowServiceImpl): TaskEligibilityPolicy.Logic = + object : TaskEligibilityPolicy.Logic { + override fun invoke(task: TaskState): TaskEligibilityPolicy.Advice = + if (scheduler.activeTasks.size < limit) { + TaskEligibilityPolicy.Advice.ADMIT + } else { + TaskEligibilityPolicy.Advice.STOP + } + } override fun toString(): String = "Limit-Active($limit)" } diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/NullTaskEligibilityPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/NullTaskEligibilityPolicy.kt index cfe2aeedf..50a117844 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/NullTaskEligibilityPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/NullTaskEligibilityPolicy.kt @@ -32,9 +32,7 @@ public object NullTaskEligibilityPolicy : TaskEligibilityPolicy { override fun invoke(scheduler: WorkflowServiceImpl): TaskEligibilityPolicy.Logic = Logic private object Logic : TaskEligibilityPolicy.Logic { - override fun invoke( - task: TaskState - ): TaskEligibilityPolicy.Advice = TaskEligibilityPolicy.Advice.ADMIT + override fun invoke(task: TaskState): TaskEligibilityPolicy.Advice = TaskEligibilityPolicy.Advice.ADMIT } override fun toString(): String = "Always" diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/RandomTaskEligibilityPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/RandomTaskEligibilityPolicy.kt index 036f3574b..a883ac993 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/RandomTaskEligibilityPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/RandomTaskEligibilityPolicy.kt @@ -30,16 +30,17 @@ import java.util.Random * A [TaskEligibilityPolicy] that randomly accepts tasks in the system with some [probability]. */ public data class RandomTaskEligibilityPolicy(val probability: Double = 0.5) : TaskEligibilityPolicy { - override fun invoke(scheduler: WorkflowServiceImpl): TaskEligibilityPolicy.Logic = object : TaskEligibilityPolicy.Logic { - val random = Random(123) + override fun invoke(scheduler: WorkflowServiceImpl): TaskEligibilityPolicy.Logic = + object : TaskEligibilityPolicy.Logic { + val random = Random(123) - override fun invoke(task: TaskState): TaskEligibilityPolicy.Advice = - if (random.nextDouble() <= probability || scheduler.activeTasks.isEmpty()) { - TaskEligibilityPolicy.Advice.ADMIT - } else { - TaskEligibilityPolicy.Advice.DENY - } - } + override fun invoke(task: TaskState): TaskEligibilityPolicy.Advice = + if (random.nextDouble() <= probability || scheduler.activeTasks.isEmpty()) { + TaskEligibilityPolicy.Advice.ADMIT + } else { + TaskEligibilityPolicy.Advice.DENY + } + } override fun toString(): String = "Random($probability)" } diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/RandomTaskOrderPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/RandomTaskOrderPolicy.kt index c12d6a660..134d22e03 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/RandomTaskOrderPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/RandomTaskOrderPolicy.kt @@ -49,7 +49,10 @@ public object RandomTaskOrderPolicy : TaskOrderPolicy { ids.remove(task.task) } - override fun compare(o1: TaskState, o2: TaskState): Int { + override fun compare( + o1: TaskState, + o2: TaskState, + ): Int { return compareValuesBy(o1, o2) { ids.getValue(it.task) } } } diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/SubmissionTimeTaskOrderPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/SubmissionTimeTaskOrderPolicy.kt index e9bbf8152..3b4bca8ff 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/SubmissionTimeTaskOrderPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/SubmissionTimeTaskOrderPolicy.kt @@ -29,9 +29,10 @@ import org.opendc.workflow.service.internal.WorkflowServiceImpl * A [TaskOrderPolicy] that orders tasks based on the order of arrival in the queue. */ public data class SubmissionTimeTaskOrderPolicy(public val ascending: Boolean = true) : TaskOrderPolicy { - override fun invoke(scheduler: WorkflowServiceImpl): Comparator = compareBy { - it.job.submittedAt.let { if (ascending) it else -it } - } + override fun invoke(scheduler: WorkflowServiceImpl): Comparator = + compareBy { + it.job.submittedAt.let { if (ascending) it else -it } + } override fun toString(): String { return "Submission-Time(${if (ascending) "asc" else "desc"})" diff --git a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/TaskEligibilityPolicy.kt b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/TaskEligibilityPolicy.kt index ee31aee28..89ec38473 100644 --- a/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/TaskEligibilityPolicy.kt +++ b/opendc-workflow/opendc-workflow-service/src/main/kotlin/org/opendc/workflow/service/scheduler/task/TaskEligibilityPolicy.kt @@ -65,6 +65,6 @@ public interface TaskEligibilityPolicy : StagePolicy - val scheduler: (ProvisioningContext) -> ComputeScheduler = { - FilterScheduler( - filters = listOf(ComputeFilter(), VCpuFilter(1.0), RamFilter(1.0)), - weighers = listOf(VCpuWeigher(1.0, multiplier = 1.0)) - ) - } - - provisioner.runSteps( - // Configure the ComputeService that is responsible for mapping virtual machines onto physical hosts - setupComputeService(computeService, scheduler, schedulingQuantum = Duration.ofSeconds(1)), - setupHosts(computeService, List(4) { createHostSpec(it) }), - - // Configure the WorkflowService that is responsible for scheduling the workflow tasks onto machines - setupWorkflowService( - workflowService, - computeService, - WorkflowSchedulerSpec( - schedulingQuantum = Duration.ofMillis(100), - jobAdmissionPolicy = NullJobAdmissionPolicy, - jobOrderPolicy = SubmissionTimeJobOrderPolicy(), - taskEligibilityPolicy = NullTaskEligibilityPolicy, - taskOrderPolicy = SubmissionTimeTaskOrderPolicy() + Provisioner(dispatcher, seed = 0L).use { provisioner -> + val scheduler: (ProvisioningContext) -> ComputeScheduler = { + FilterScheduler( + filters = listOf(ComputeFilter(), VCpuFilter(1.0), RamFilter(1.0)), + weighers = listOf(VCpuWeigher(1.0, multiplier = 1.0)), ) + } + + provisioner.runSteps( + // Configure the ComputeService that is responsible for mapping virtual machines onto physical hosts + setupComputeService(computeService, scheduler, schedulingQuantum = Duration.ofSeconds(1)), + setupHosts(computeService, List(4) { createHostSpec(it) }), + // Configure the WorkflowService that is responsible for scheduling the workflow tasks onto machines + setupWorkflowService( + workflowService, + computeService, + WorkflowSchedulerSpec( + schedulingQuantum = Duration.ofMillis(100), + jobAdmissionPolicy = NullJobAdmissionPolicy, + jobOrderPolicy = SubmissionTimeJobOrderPolicy(), + taskEligibilityPolicy = NullTaskEligibilityPolicy, + taskOrderPolicy = SubmissionTimeTaskOrderPolicy(), + ), + ), ) - ) - val service = provisioner.registry.resolve(workflowService, WorkflowService::class.java)!! + val service = provisioner.registry.resolve(workflowService, WorkflowService::class.java)!! - val trace = Trace.open( - Paths.get(checkNotNull(WorkflowServiceTest::class.java.getResource("/trace.gwf")).toURI()), - format = "gwf" - ) - service.replay(timeSource, trace.toJobs()) + val trace = + Trace.open( + Paths.get(checkNotNull(WorkflowServiceTest::class.java.getResource("/trace.gwf")).toURI()), + format = "gwf", + ) + service.replay(timeSource, trace.toJobs()) - val metrics = service.getSchedulerStats() + val metrics = service.getSchedulerStats() - assertAll( - { assertEquals(758, metrics.workflowsSubmitted, "No jobs submitted") }, - { assertEquals(0, metrics.workflowsRunning, "Not all submitted jobs started") }, - { - assertEquals( - metrics.workflowsSubmitted, - metrics.workflowsFinished, - "Not all started jobs finished" - ) - }, - { assertEquals(0, metrics.tasksRunning, "Not all started tasks finished") }, - { assertEquals(metrics.tasksSubmitted, metrics.tasksFinished, "Not all started tasks finished") }, - { assertEquals(45975707L, timeSource.millis()) { "Total duration incorrect" } } - ) + assertAll( + { assertEquals(758, metrics.workflowsSubmitted, "No jobs submitted") }, + { assertEquals(0, metrics.workflowsRunning, "Not all submitted jobs started") }, + { + assertEquals( + metrics.workflowsSubmitted, + metrics.workflowsFinished, + "Not all started jobs finished", + ) + }, + { assertEquals(0, metrics.tasksRunning, "Not all started tasks finished") }, + { assertEquals(metrics.tasksSubmitted, metrics.tasksFinished, "Not all started tasks finished") }, + { assertEquals(45975707L, timeSource.millis()) { "Total duration incorrect" } }, + ) + } } - } /** * Construct a [HostSpec] for a simulated host. @@ -141,7 +142,7 @@ internal class WorkflowServiceTest { emptyMap(), machineModel, SimPsuFactories.noop(), - FlowMultiplexerFactory.forwardingMultiplexer() + FlowMultiplexerFactory.forwardingMultiplexer(), ) } }