diff options
12 files changed, 115 insertions, 56 deletions
diff --git a/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ComputeService.java b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ComputeService.java index 557fb760..3f6ef73b 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ComputeService.java +++ b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ComputeService.java @@ -193,6 +193,10 @@ public final class ComputeService implements AutoCloseable, CarbonReceiver { host.delete(task); + if (host.isEmpty()) { + setHostEmpty(host); + } + if (newState == TaskState.COMPLETED) { tasksCompleted++; addCompletedTask(task); @@ -294,6 +298,12 @@ public final class ComputeService implements AutoCloseable, CarbonReceiver { host.addListener(hostListener); } + public void setHostEmpty(SimHost host) { + HostView hv = hostToView.get(host); + + this.scheduler.setHostEmpty(hv); + } + public void addPowerSource(SimPowerSource simPowerSource) { // Check if host is already known if (powerSources.contains(simPowerSource)) { @@ -437,11 +447,6 @@ public final class ComputeService implements AutoCloseable, CarbonReceiver { void addCompletedTask(ServiceTask completedTask) { int parentId = completedTask.getId(); - // int taskId = task.getId(); - - // if (!this.completedTasks.contains(taskId)) { - // this.completedTasks.add(taskId); - // } for (int taskId : completedTask.getFlavor().getChildren()) { SchedulingRequest request = blockedTasks.get(taskId); @@ -457,24 +462,6 @@ public final class ComputeService implements AutoCloseable, CarbonReceiver { } } } - - // List<SchedulingRequest> requestsToRemove = new ArrayList<>(); - // - // for (SchedulingRequest request : blockedTasks) { - // request.getTask().getFlavor().updatePendingDependencies(taskId); - // - // Set<Integer> pendingDependencies = request.getTask().getFlavor().getDependencies(); - // - // if (pendingDependencies.isEmpty()) { - // requestsToRemove.add(request); - // taskQueue.add(request); - // tasksPending++; - // } - // } - // - // for (SchedulingRequest request : requestsToRemove) { - // blockedTasks.remove(request); - // } } void addTerminatedTask(ServiceTask task) { @@ -493,25 +480,6 @@ public final class ComputeService implements AutoCloseable, CarbonReceiver { blockedTasks.remove(childTask.getId()); } } - - // int taskId = task.getId(); - // - // List<SchedulingRequest> requestsToRemove = new ArrayList<>(); - // - // if (!this.terminatedTasks.contains(taskId)) { - // this.terminatedTasks.add(taskId); - // } - // - // for (SchedulingRequest request : blockedTasks) { - // if (request.getTask().getFlavor().isInDependencies(taskId)) { - // requestsToRemove.add(request); - // request.getTask().setState(TaskState.TERMINATED); - // } - // } - // - // for (SchedulingRequest request : requestsToRemove) { - // blockedTasks.remove(request); - // } } void delete(ServiceFlavor flavor) { diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/host/SimHost.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/host/SimHost.kt index 1a0cc316..856cfd3b 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/host/SimHost.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/host/SimHost.kt @@ -56,6 +56,7 @@ import java.time.InstantSource */ public class SimHost( private val name: String, + private val type: String, private val clusterName: String, private val clock: InstantSource, private val engine: FlowEngine, @@ -201,6 +202,10 @@ public class SimHost( return name } + public fun getType(): String { + return type + } + public fun getClusterName(): String { return clusterName } @@ -217,6 +222,10 @@ public class SimHost( return taskToGuestMap.keys } + public fun isEmpty(): Boolean { + return guests.isEmpty() + } + public fun getGuests(): List<Guest> { return this.guests.toList() } 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 7a0d5d65..8eaf95d1 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 @@ -132,6 +132,7 @@ public class HostsProvisioningStep internal constructor( val simHost = SimHost( hostSpec.name, + hostSpec.type, cluster.name, ctx.dispatcher.timeSource, engine, diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/ComputeScheduler.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/ComputeScheduler.kt index f702ace9..65037817 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/ComputeScheduler.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/ComputeScheduler.kt @@ -39,6 +39,8 @@ public interface ComputeScheduler { */ public fun removeHost(host: HostView) + public fun setHostEmpty(hostView: HostView) + /** * Select a host for the specified [iter]. * We implicity assume that the task has been scheduled onto the host. diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/FilterScheduler.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/FilterScheduler.kt index 386e0d63..9295acb2 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/FilterScheduler.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/FilterScheduler.kt @@ -28,7 +28,6 @@ import org.opendc.compute.simulator.service.HostView import org.opendc.compute.simulator.service.ServiceTask import java.util.SplittableRandom import java.util.random.RandomGenerator -import kotlin.math.min /** * A [ComputeScheduler] implementation that uses filtering and weighing passes to select @@ -51,18 +50,39 @@ public class FilterScheduler( /** * The pool of hosts available to the scheduler. */ - private val hosts = mutableListOf<HostView>() + + private val emptyHostMap = mutableMapOf<String, MutableList<HostView>>() + private val usedHosts = mutableListOf<HostView>() init { require(subsetSize >= 1) { "Subset size must be one or greater" } } - override fun addHost(host: HostView) { - hosts.add(host) + override fun addHost(hostView: HostView) { + val hostType = hostView.host.getType() + + if (emptyHostMap.containsKey(hostType)) { + emptyHostMap[hostType]?.add(hostView) + } else { + emptyHostMap[hostType] = mutableListOf(hostView) + } + } + + override fun removeHost(hostView: HostView) { + val hostType = hostView.host.getType() + + emptyHostMap[hostType]?.remove(hostView) } - override fun removeHost(host: HostView) { - hosts.remove(host) + override fun setHostEmpty(hostView: HostView) { + val hostType = hostView.host.getType() + + usedHosts.remove(hostView) + if (emptyHostMap.containsKey(hostType)) { + emptyHostMap[hostType]?.add(hostView) + } else { + emptyHostMap[hostType] = mutableListOf(hostView) + } } override fun select(iter: MutableIterator<SchedulingRequest>): SchedulingResult { @@ -78,8 +98,15 @@ public class FilterScheduler( } } + val availableHosts = usedHosts.toMutableList() + for (emptyHosts in emptyHostMap.values) { + if (!emptyHosts.isEmpty()) { + availableHosts += emptyHosts.first() + } + } + val task = req.task - val filteredHosts = hosts.filter { host -> filters.all { filter -> filter.test(host, task) } } + val filteredHosts = availableHosts.filter { host -> filters.all { filter -> filter.test(host, task) } } val subset = if (weighers.isNotEmpty()) { @@ -113,14 +140,19 @@ public class FilterScheduler( filteredHosts } - // fixme: currently finding no matching hosts can result in an error - val maxSize = min(subsetSize, subset.size) - if (maxSize == 0) { + if (subset.isEmpty()) { return SchedulingResult(SchedulingResultType.FAILURE, null, req) - } else { - iter.remove() - return SchedulingResult(SchedulingResultType.SUCCESS, subset[random.nextInt(maxSize)], req) } + + iter.remove() + + val host = subset.first() + val hostType = host.host.getType() + + emptyHostMap[hostType]?.remove(host) + usedHosts.add(host) + + return SchedulingResult(SchedulingResultType.SUCCESS, host, req) } override fun removeTask( diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/MemorizingScheduler.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/MemorizingScheduler.kt index 72d9199f..9f21c71f 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/MemorizingScheduler.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/MemorizingScheduler.kt @@ -73,6 +73,10 @@ public class MemorizingScheduler( numHosts-- } + override fun setHostEmpty(hostView: HostView) { + // No-op + } + override fun select(iter: MutableIterator<SchedulingRequest>): SchedulingResult { if (numHosts == 0) { return SchedulingResult(SchedulingResultType.FAILURE) diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/timeshift/MemorizingTimeshift.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/timeshift/MemorizingTimeshift.kt index 443f1f0e..6361f7d1 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/timeshift/MemorizingTimeshift.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/timeshift/MemorizingTimeshift.kt @@ -88,6 +88,10 @@ public class MemorizingTimeshift( numHosts-- } + override fun setHostEmpty(hostView: HostView) { + // No-op + } + override fun select(iter: MutableIterator<SchedulingRequest>): SchedulingResult { if (numHosts == 0) { return SchedulingResult(SchedulingResultType.FAILURE) diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/timeshift/TimeshiftScheduler.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/timeshift/TimeshiftScheduler.kt index 970b7761..ff8be34b 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/timeshift/TimeshiftScheduler.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/scheduler/timeshift/TimeshiftScheduler.kt @@ -73,6 +73,10 @@ public class TimeshiftScheduler( hosts.remove(host) } + override fun setHostEmpty(hostView: HostView) { + // No-op + } + override fun select(iter: MutableIterator<SchedulingRequest>): SchedulingResult { var result: SchedulingResult? = null for (req in iter) { diff --git a/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/scheduler/FilterSchedulerTest.kt b/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/scheduler/FilterSchedulerTest.kt index 65fbfb38..4f00100f 100644 --- a/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/scheduler/FilterSchedulerTest.kt +++ b/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/scheduler/FilterSchedulerTest.kt @@ -97,9 +97,11 @@ internal class FilterSchedulerTest { val hostA = mockk<HostView>() every { hostA.host.getState() } returns HostState.DOWN + every { hostA.host.getType() } returns "A" val hostB = mockk<HostView>() every { hostB.host.getState() } returns HostState.UP + every { hostB.host.getType() } returns "B" scheduler.addHost(hostA) scheduler.addHost(hostB) @@ -128,9 +130,11 @@ internal class FilterSchedulerTest { val hostA = mockk<HostView>() every { hostA.host.getState() } returns HostState.DOWN + every { hostA.host.getType() } returns "A" val hostB = mockk<HostView>() every { hostB.host.getState() } returns HostState.UP + every { hostB.host.getType() } returns "B" scheduler.addHost(hostA) scheduler.addHost(hostB) @@ -142,7 +146,7 @@ internal class FilterSchedulerTest { // Make sure we get the first host both times assertAll( - { assertEquals(hostB, scheduler.select(mutableListOf(req).iterator()).host) }, + { assertEquals(hostA, scheduler.select(mutableListOf(req).iterator()).host) }, { assertEquals(hostA, scheduler.select(mutableListOf(req).iterator()).host) }, ) } @@ -157,6 +161,7 @@ internal class FilterSchedulerTest { val host = mockk<HostView>() every { host.host.getState() } returns HostState.DOWN + every { host.host.getType() } returns "A" scheduler.addHost(host) @@ -178,6 +183,7 @@ internal class FilterSchedulerTest { val host = mockk<HostView>() every { host.host.getState() } returns HostState.UP + every { host.host.getType() } returns "A" scheduler.addHost(host) @@ -201,11 +207,13 @@ internal class FilterSchedulerTest { every { hostA.host.getState() } returns HostState.UP every { hostA.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostA.availableMemory } returns 512 + every { hostA.host.getType() } returns "A" val hostB = mockk<HostView>() every { hostB.host.getState() } returns HostState.UP every { hostB.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostB.availableMemory } returns 2048 + every { hostB.host.getType() } returns "B" scheduler.addHost(hostA) scheduler.addHost(hostB) @@ -230,6 +238,7 @@ internal class FilterSchedulerTest { every { host.host.getState() } returns HostState.UP every { host.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { host.availableMemory } returns 2048 + every { host.host.getType() } returns "A" scheduler.addHost(host) @@ -253,11 +262,13 @@ internal class FilterSchedulerTest { every { hostA.host.getState() } returns HostState.UP every { hostA.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostA.provisionedCpuCores } returns 3 + every { hostA.host.getType() } returns "A" val hostB = mockk<HostView>() every { hostB.host.getState() } returns HostState.UP every { hostB.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostB.provisionedCpuCores } returns 0 + every { hostB.host.getType() } returns "B" scheduler.addHost(hostA) scheduler.addHost(hostB) @@ -282,6 +293,7 @@ internal class FilterSchedulerTest { every { host.host.getState() } returns HostState.UP every { host.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { host.provisionedCpuCores } returns 0 + every { host.host.getType() } returns "A" scheduler.addHost(host) @@ -305,12 +317,14 @@ internal class FilterSchedulerTest { every { hostA.host.getState() } returns HostState.UP every { hostA.host.getModel() } returns HostModel(8 * 2600.0, 8, 2048) every { hostA.availableMemory } returns 512 + every { hostA.host.getType() } returns "A" scheduler.addHost(hostA) val hostB = mockk<HostView>() every { hostB.host.getState() } returns HostState.UP every { hostB.host.getModel() } returns HostModel(4 * 3200.0, 4, 2048) every { hostB.availableMemory } returns 512 + every { hostB.host.getType() } returns "B" scheduler.addHost(hostB) val req = mockk<SchedulingRequest>() @@ -334,11 +348,13 @@ internal class FilterSchedulerTest { every { hostA.host.getState() } returns HostState.UP every { hostA.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostA.instanceCount } returns 2 + every { hostA.host.getType() } returns "A" val hostB = mockk<HostView>() every { hostB.host.getState() } returns HostState.UP every { hostB.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostB.instanceCount } returns 0 + every { hostB.host.getType() } returns "B" scheduler.addHost(hostA) scheduler.addHost(hostB) @@ -372,12 +388,14 @@ internal class FilterSchedulerTest { every { hostA.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostA.host.getInstances() } returns emptySet() every { hostA.provisionedCpuCores } returns 3 + every { hostA.host.getType() } returns "A" val hostB = mockk<HostView>() every { hostB.host.getState() } returns HostState.UP every { hostB.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostB.host.getInstances() } returns setOf(reqA.task) every { hostB.provisionedCpuCores } returns 0 + every { hostB.host.getType() } returns "B" scheduler.addHost(hostA) scheduler.addHost(hostB) @@ -416,12 +434,14 @@ internal class FilterSchedulerTest { every { hostA.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostA.host.getInstances() } returns setOf(reqA.task) every { hostA.provisionedCpuCores } returns 3 + every { hostA.host.getType() } returns "A" val hostB = mockk<HostView>() every { hostB.host.getState() } returns HostState.UP every { hostB.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostB.host.getInstances() } returns emptySet() every { hostB.provisionedCpuCores } returns 0 + every { hostB.host.getType() } returns "B" scheduler.addHost(hostA) scheduler.addHost(hostB) @@ -459,6 +479,7 @@ internal class FilterSchedulerTest { ), ) every { hostA.provisionedGpuCores } returns 0 + every { hostA.host.getType() } returns "A" scheduler.addHost(hostA) val hostB = mockk<HostView>() @@ -474,6 +495,7 @@ internal class FilterSchedulerTest { ), ) every { hostB.provisionedGpuCores } returns 0 + every { hostB.host.getType() } returns "B" scheduler.addHost(hostB) val req = mockk<SchedulingRequest>() @@ -505,6 +527,7 @@ internal class FilterSchedulerTest { ), ) every { hostA.availableMemory } returns 512 + every { hostA.host.getType() } returns "A" scheduler.addHost(hostA) val hostB = mockk<HostView>() @@ -520,6 +543,7 @@ internal class FilterSchedulerTest { ), ) every { hostB.availableMemory } returns 512 + every { hostB.host.getType() } returns "B" scheduler.addHost(hostB) val req = mockk<SchedulingRequest>() @@ -543,11 +567,13 @@ internal class FilterSchedulerTest { every { hostA.host.getState() } returns HostState.UP every { hostA.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostA.availableMemory } returns 1024 + every { hostA.host.getType() } returns "A" val hostB = mockk<HostView>() every { hostB.host.getState() } returns HostState.UP every { hostB.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostB.availableMemory } returns 512 + every { hostB.host.getType() } returns "B" scheduler.addHost(hostA) scheduler.addHost(hostB) @@ -572,11 +598,13 @@ internal class FilterSchedulerTest { every { hostA.host.getState() } returns HostState.UP every { hostA.host.getModel() } returns HostModel(12 * 2600.0, 12, 2048) every { hostA.availableMemory } returns 1024 + every { hostA.host.getType() } returns "A" val hostB = mockk<HostView>() every { hostB.host.getState() } returns HostState.UP every { hostB.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostB.availableMemory } returns 512 + every { hostB.host.getType() } returns "B" scheduler.addHost(hostA) scheduler.addHost(hostB) @@ -601,11 +629,13 @@ internal class FilterSchedulerTest { every { hostA.host.getState() } returns HostState.UP every { hostA.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostA.provisionedCpuCores } returns 2 + every { hostA.host.getType() } returns "A" val hostB = mockk<HostView>() every { hostB.host.getState() } returns HostState.UP every { hostB.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostB.provisionedCpuCores } returns 0 + every { hostB.host.getType() } returns "B" scheduler.addHost(hostA) scheduler.addHost(hostB) @@ -630,11 +660,13 @@ internal class FilterSchedulerTest { every { hostA.host.getState() } returns HostState.UP every { hostA.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostA.instanceCount } returns 2 + every { hostA.host.getType() } returns "A" val hostB = mockk<HostView>() every { hostB.host.getState() } returns HostState.UP every { hostB.host.getModel() } returns HostModel(4 * 2600.0, 4, 2048) every { hostB.instanceCount } returns 0 + every { hostB.host.getType() } returns "B" scheduler.addHost(hostA) scheduler.addHost(hostB) 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 fd66ca11..0a44d318 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 @@ -224,6 +224,7 @@ private fun HostJSONSpec.toHostSpec(clusterName: String): HostSpec { val hostSpec = HostSpec( createUniqueName(this.name, hostNames), + this.name, clusterName, machineModel, cpuPowerModel, diff --git a/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/specs/HostSpec.kt b/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/specs/HostSpec.kt index 8bfef464..42be2daf 100644 --- a/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/specs/HostSpec.kt +++ b/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/specs/HostSpec.kt @@ -35,6 +35,7 @@ import org.opendc.simulator.engine.graph.distributionPolicies.FlowDistributorFac */ public data class HostSpec( val name: String, + val type: String, val clusterName: String, val model: MachineModel, val cpuPowerModel: PowerModel, 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 309763f1..b4c76fdb 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 @@ -358,6 +358,7 @@ public class OpenDCRunner( val spec = HostSpec( "node-$clusterId-$position", + "node-$clusterId", clusterId, MachineModel(processors, memoryUnits[0]), cpuPowerModel, |
