diff options
| author | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2021-01-11 16:33:15 +0100 |
|---|---|---|
| committer | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2021-01-11 16:33:15 +0100 |
| commit | 9dbb7bbcc2202955c715aaa3b28c70641a2fbd5b (patch) | |
| tree | bea8e47037660c88df42e04105e7a0b7f709173a | |
| parent | f2028b23e25c8520f25a53771a1b261c4e991bb8 (diff) | |
Add support for hypervisor selection
This change allows users to select the hypervisor scheduler to use when
deploying hypervisors onto bare-metal machines.
13 files changed, 141 insertions, 29 deletions
diff --git a/simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts b/simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts index d7570e54..dc93e956 100644 --- a/simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts +++ b/simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts @@ -29,10 +29,10 @@ plugins { dependencies { api(project(":opendc-compute:opendc-compute-core")) + api(project(":opendc-simulator:opendc-simulator-compute")) + api(project(":opendc-simulator:opendc-simulator-failures")) implementation(project(":opendc-utils")) implementation("io.github.microutils:kotlin-logging:1.7.9") - implementation(project(":opendc-simulator:opendc-simulator-compute")) - api(project(":opendc-simulator:opendc-simulator-failures")) testImplementation(project(":opendc-simulator:opendc-simulator-core")) testRuntimeOnly("org.slf4j:slf4j-simple:${Library.SLF4J}") diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtDriver.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtDriver.kt index 508720e2..d7a8a8b2 100644 --- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtDriver.kt +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtDriver.kt @@ -44,7 +44,7 @@ import java.util.* /** * A [VirtDriver] that is simulates virtual machines on a physical machine using [SimHypervisor]. */ -public class SimVirtDriver(private val coroutineScope: CoroutineScope) : VirtDriver, SimWorkload { +public class SimVirtDriver(private val coroutineScope: CoroutineScope, hypervisor: SimHypervisorProvider) : VirtDriver, SimWorkload { /** * The execution context in which the [VirtDriver] runs. */ @@ -71,7 +71,7 @@ public class SimVirtDriver(private val coroutineScope: CoroutineScope) : VirtDri /** * The hypervisor to run multiple workloads. */ - private val hypervisor = SimFairShareHypervisor( + private val hypervisor = hypervisor.create( object : SimHypervisor.Listener { override fun onSliceFinish( hypervisor: SimHypervisor, @@ -132,7 +132,6 @@ public class SimVirtDriver(private val coroutineScope: CoroutineScope) : VirtDri ) availableMemory -= requiredMemory - val vm = VirtualMachine(server, events, hypervisor.createMachine(flavor.toMachineModel())) vms.add(vm) vmStarted(vm) diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtProvisioningService.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtProvisioningService.kt index 6b0021e1..defea888 100644 --- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtProvisioningService.kt +++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtProvisioningService.kt @@ -40,7 +40,9 @@ import org.opendc.compute.core.virt.service.VirtProvisioningEvent import org.opendc.compute.core.virt.service.VirtProvisioningService import org.opendc.compute.core.virt.service.events.* import org.opendc.compute.simulator.allocation.AllocationPolicy +import org.opendc.simulator.compute.SimHypervisorProvider import org.opendc.trace.core.EventTracer +import org.opendc.utils.TimerScheduler import org.opendc.utils.flow.EventFlow import java.time.Clock import java.util.* @@ -55,7 +57,8 @@ public class SimVirtProvisioningService( private val provisioningService: ProvisioningService, public val allocationPolicy: AllocationPolicy, private val tracer: EventTracer, - private val schedulingQuantum: Long = 300000 // 5 minutes in milliseconds + private val hypervisor: SimHypervisorProvider, + private val schedulingQuantum: Long = 300000, // 5 minutes in milliseconds ) : VirtProvisioningService { /** * The logger instance to use. @@ -75,7 +78,7 @@ public class SimVirtProvisioningService( /** * The incoming images to be processed by the provisioner. */ - private val incomingImages: MutableSet<ImageView> = mutableSetOf() + private val incomingImages: Deque<ImageView> = ArrayDeque() /** * The active images in the system. @@ -103,11 +106,16 @@ public class SimVirtProvisioningService( override val events: Flow<VirtProvisioningEvent> = eventFlow + /** + * The [TimerScheduler] to use for scheduling the scheduler cycles. + */ + private var scheduler: TimerScheduler<Unit> = TimerScheduler(coroutineScope, clock) + init { coroutineScope.launch { val provisionedNodes = provisioningService.nodes() provisionedNodes.forEach { node -> - val workload = SimVirtDriver(coroutineScope) + val workload = SimVirtDriver(coroutineScope, hypervisor) val hypervisorImage = SimWorkloadImage(UUID.randomUUID(), "vmm", emptyMap(), workload) launch { var init = false @@ -169,10 +177,9 @@ public class SimVirtProvisioningService( provisionedNodes.forEach { node -> provisioningService.stop(node) } } - private var call: Job? = null - private fun requestCycle() { - if (call != null) { + // Bail out in case we have already requested a new cycle. + if (scheduler.isTimerActive(Unit)) { return } @@ -181,23 +188,19 @@ public class SimVirtProvisioningService( // We calculate here the delay until the next scheduling slot. val delay = schedulingQuantum - (clock.millis() % schedulingQuantum) - val call = coroutineScope.launch { - delay(delay) - this@SimVirtProvisioningService.call = null - schedule() + scheduler.startSingleTimer(Unit, delay) { + coroutineScope.launch { schedule() } } - this.call = call } private suspend fun schedule() { - val imagesToBeScheduled = incomingImages.toSet() - - for (imageInstance in imagesToBeScheduled) { + while (incomingImages.isNotEmpty()) { + val imageInstance = incomingImages.peekFirst() val requiredMemory = imageInstance.flavor.memorySize val selectedHv = allocationLogic.select(availableHypervisors, imageInstance) if (selectedHv == null || !selectedHv.driver.canFit(imageInstance.flavor)) { - logger.debug { "Server ${imageInstance.server} selected for scheduling but no capacity available for it." } + logger.trace { "Image ${imageInstance.image} selected for scheduling but no capacity available for it." } if (requiredMemory > maxMemory || imageInstance.flavor.cpuCount > maxCores) { tracer.commit(VmSubmissionInvalidEvent(imageInstance.name)) @@ -215,7 +218,8 @@ public class SimVirtProvisioningService( ) ) - incomingImages -= imageInstance + // Remove the incoming image + incomingImages.poll() logger.warn("Failed to spawn ${imageInstance.image}: does not fit [${clock.millis()}]") continue @@ -226,7 +230,7 @@ public class SimVirtProvisioningService( try { logger.info { "[${clock.millis()}] Spawning ${imageInstance.image} on ${selectedHv.server.uid} ${selectedHv.server.name} ${selectedHv.server.flavor}" } - incomingImages -= imageInstance + incomingImages.poll() // Speculatively update the hypervisor view information to prevent other images in the queue from // deciding on stale values. diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimVirtDriverTest.kt b/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimVirtDriverTest.kt index 163b326a..1831eae0 100644 --- a/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimVirtDriverTest.kt +++ b/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimVirtDriverTest.kt @@ -34,6 +34,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll import org.opendc.compute.core.Flavor import org.opendc.compute.core.virt.HypervisorEvent +import org.opendc.simulator.compute.SimFairShareHypervisorProvider import org.opendc.simulator.compute.SimMachineModel import org.opendc.simulator.compute.model.MemoryUnit import org.opendc.simulator.compute.model.ProcessingNode @@ -75,7 +76,7 @@ internal class SimVirtDriverTest { var overcommittedWork = 0L scope.launch { - val virtDriver = SimVirtDriver(this) + val virtDriver = SimVirtDriver(this, SimFairShareHypervisorProvider()) val vmm = SimWorkloadImage(UUID.randomUUID(), "vmm", emptyMap(), virtDriver) val duration = 5 * 60L val vmImageA = SimWorkloadImage( diff --git a/simulator/opendc-experiments/opendc-experiments-sc18/src/main/kotlin/org/opendc/experiments/sc18/TestExperiment.kt b/simulator/opendc-experiments/opendc-experiments-sc18/src/main/kotlin/org/opendc/experiments/sc18/TestExperiment.kt index 9ad744f2..202df6df 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc18/src/main/kotlin/org/opendc/experiments/sc18/TestExperiment.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc18/src/main/kotlin/org/opendc/experiments/sc18/TestExperiment.kt @@ -32,6 +32,7 @@ import org.opendc.compute.simulator.SimVirtProvisioningService import org.opendc.compute.simulator.allocation.NumberOfActiveServersAllocationPolicy import org.opendc.format.environment.sc18.Sc18EnvironmentReader import org.opendc.format.trace.gwf.GwfTraceReader +import org.opendc.simulator.compute.SimSpaceSharedHypervisorProvider import org.opendc.simulator.utils.DelayControllerClockAdapter import org.opendc.trace.core.EventTracer import org.opendc.workflows.service.StageWorkflowService @@ -71,7 +72,7 @@ public fun main(args: Array<String>) { // Wait for the bare metal nodes to be spawned delay(10) - val provisioner = SimVirtProvisioningService(testScope, clock, bareMetal, NumberOfActiveServersAllocationPolicy(), tracer, schedulingQuantum = 1000) + val provisioner = SimVirtProvisioningService(testScope, clock, bareMetal, NumberOfActiveServersAllocationPolicy(), tracer, SimSpaceSharedHypervisorProvider(), schedulingQuantum = 1000) // Wait for the hypervisors to be spawned delay(10) diff --git a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt index f939738d..1e01e892 100644 --- a/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt +++ b/simulator/opendc-experiments/opendc-experiments-sc20/src/main/kotlin/org/opendc/experiments/sc20/experiment/ExperimentHelpers.kt @@ -48,6 +48,7 @@ import org.opendc.experiments.sc20.experiment.monitor.ExperimentMonitor import org.opendc.experiments.sc20.trace.Sc20StreamingParquetTraceReader import org.opendc.format.environment.EnvironmentReader import org.opendc.format.trace.TraceReader +import org.opendc.simulator.compute.SimFairShareHypervisorProvider import org.opendc.simulator.compute.interference.PerformanceInterferenceModel import org.opendc.simulator.failures.CorrelatedFaultInjector import org.opendc.simulator.failures.FailureDomain @@ -150,7 +151,7 @@ public suspend fun createProvisioner( // Wait for the bare metal nodes to be spawned delay(10) - val scheduler = SimVirtProvisioningService(coroutineScope, clock, bareMetalProvisioner, allocationPolicy, eventTracer) + val scheduler = SimVirtProvisioningService(coroutineScope, clock, bareMetalProvisioner, allocationPolicy, eventTracer, SimFairShareHypervisorProvider()) // Wait for the hypervisors to be spawned delay(10) diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimFairShareHypervisorProvider.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimFairShareHypervisorProvider.kt new file mode 100644 index 00000000..02eb6ad0 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimFairShareHypervisorProvider.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute + +/** + * A [SimHypervisorProvider] for the [SimFairShareHypervisor] implementation. + */ +public class SimFairShareHypervisorProvider : SimHypervisorProvider { + override val id: String = "fair-share" + + override fun create(listener: SimHypervisor.Listener?): SimHypervisor = SimFairShareHypervisor(listener) +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt index 872b5f72..d8f00bef 100644 --- a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt @@ -26,8 +26,8 @@ import org.opendc.simulator.compute.interference.PerformanceInterferenceModel import org.opendc.simulator.compute.workload.SimWorkload /** - * SimHypervisor distributes the computing requirements of multiple [SimWorkload] on a single [SimBareMetalMachine] - * concurrently. + * A SimHypervisor facilitates the execution of multiple concurrent [SimWorkload]s, while acting as a single workload + * to a [SimBareMetalMachine]. */ public interface SimHypervisor : SimWorkload { /** diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisorProvider.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisorProvider.kt new file mode 100644 index 00000000..a5b4526b --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisorProvider.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute + +/** + * A service provider interface for constructing a [SimHypervisor]. + */ +public interface SimHypervisorProvider { + /** + * A unique identifier for this hypervisor implementation. + * + * Each hypervisor must provide a unique ID, so that they can be selected by the user. + * When in doubt, you may use the fully qualified name of your custom [SimHypervisor] implementation class. + */ + public val id: String + + /** + * Create a [SimHypervisor] instance with the specified [listener]. + */ + public fun create(listener: SimHypervisor.Listener? = null): SimHypervisor +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorProvider.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorProvider.kt new file mode 100644 index 00000000..3d49e544 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorProvider.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute + +/** + * A [SimHypervisorProvider] for the [SimSpaceSharedHypervisor] implementation. + */ +public class SimSpaceSharedHypervisorProvider : SimHypervisorProvider { + override val id: String = "space-shared" + + override fun create(listener: SimHypervisor.Listener?): SimHypervisor = SimSpaceSharedHypervisor(listener) +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorTest.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorTest.kt index 7356a1b9..1a9faf11 100644 --- a/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorTest.kt @@ -34,7 +34,6 @@ import org.junit.jupiter.api.assertThrows import org.opendc.simulator.compute.model.MemoryUnit import org.opendc.simulator.compute.model.ProcessingNode import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.compute.workload.SimFlopsWorkload import org.opendc.simulator.compute.workload.SimRuntimeWorkload import org.opendc.simulator.compute.workload.SimTraceWorkload import org.opendc.simulator.utils.DelayControllerClockAdapter diff --git a/simulator/opendc-workflows/build.gradle.kts b/simulator/opendc-workflows/build.gradle.kts index 4346efcc..e9c85de5 100644 --- a/simulator/opendc-workflows/build.gradle.kts +++ b/simulator/opendc-workflows/build.gradle.kts @@ -41,6 +41,7 @@ dependencies { exclude("org.jetbrains.kotlin", module = "kotlin-reflect") } testImplementation(kotlin("reflect")) + testRuntimeOnly("org.slf4j:slf4j-simple:${Library.SLF4J}") testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Library.JUNIT_JUPITER}") testImplementation("org.junit.platform:junit-platform-launcher:${Library.JUNIT_PLATFORM}") diff --git a/simulator/opendc-workflows/src/test/kotlin/org/opendc/workflows/service/StageWorkflowSchedulerIntegrationTest.kt b/simulator/opendc-workflows/src/test/kotlin/org/opendc/workflows/service/StageWorkflowSchedulerIntegrationTest.kt index 8727ea2e..2bfcba35 100644 --- a/simulator/opendc-workflows/src/test/kotlin/org/opendc/workflows/service/StageWorkflowSchedulerIntegrationTest.kt +++ b/simulator/opendc-workflows/src/test/kotlin/org/opendc/workflows/service/StageWorkflowSchedulerIntegrationTest.kt @@ -41,6 +41,7 @@ import org.opendc.compute.simulator.SimVirtProvisioningService import org.opendc.compute.simulator.allocation.NumberOfActiveServersAllocationPolicy import org.opendc.format.environment.sc18.Sc18EnvironmentReader import org.opendc.format.trace.gwf.GwfTraceReader +import org.opendc.simulator.compute.SimSpaceSharedHypervisorProvider import org.opendc.simulator.utils.DelayControllerClockAdapter import org.opendc.trace.core.EventTracer import org.opendc.workflows.service.stage.job.NullJobAdmissionPolicy @@ -79,7 +80,7 @@ internal class StageWorkflowSchedulerIntegrationTest { // Wait for the bare metal nodes to be spawned delay(10) - val provisioner = SimVirtProvisioningService(testScope, clock, bareMetal, NumberOfActiveServersAllocationPolicy(), tracer, schedulingQuantum = 1000) + val provisioner = SimVirtProvisioningService(testScope, clock, bareMetal, NumberOfActiveServersAllocationPolicy(), tracer, SimSpaceSharedHypervisorProvider(), schedulingQuantum = 1000) // Wait for the hypervisors to be spawned delay(10) |
