Spaces:
Sleeping
Sleeping
| from solverforge_legacy.solver.test import ConstraintVerifier | |
| from vm_placement.domain import Server, VM, VMPlacementPlan | |
| from vm_placement.constraints import ( | |
| define_constraints, | |
| cpu_capacity, | |
| memory_capacity, | |
| storage_capacity, | |
| anti_affinity, | |
| affinity, | |
| minimize_servers_used, | |
| balance_utilization, | |
| prioritize_placement, | |
| ) | |
| # VM is the only planning entity (Server is a problem fact) | |
| constraint_verifier = ConstraintVerifier.build( | |
| define_constraints, VMPlacementPlan, VM | |
| ) | |
| def assign(server: Server, *vms: VM): | |
| """Helper to assign VMs to a server.""" | |
| for vm in vms: | |
| vm.server = server | |
| ############################################## | |
| # CPU Capacity Tests | |
| ############################################## | |
| def test_cpu_capacity_not_exceeded(): | |
| server = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM(id="vm1", name="VM1", cpu_cores=4, memory_gb=8, storage_gb=50) | |
| vm2 = VM(id="vm2", name="VM2", cpu_cores=8, memory_gb=16, storage_gb=100) | |
| assign(server, vm1, vm2) | |
| ( | |
| constraint_verifier.verify_that(cpu_capacity) | |
| .given(server, vm1, vm2) | |
| .penalizes_by(0) | |
| ) | |
| def test_cpu_capacity_exceeded(): | |
| server = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM(id="vm1", name="VM1", cpu_cores=12, memory_gb=8, storage_gb=50) | |
| vm2 = VM(id="vm2", name="VM2", cpu_cores=8, memory_gb=16, storage_gb=100) | |
| assign(server, vm1, vm2) | |
| # 12 + 8 = 20 cores, capacity = 16, excess = 4 | |
| ( | |
| constraint_verifier.verify_that(cpu_capacity) | |
| .given(server, vm1, vm2) | |
| .penalizes_by(4) | |
| ) | |
| ############################################## | |
| # Memory Capacity Tests | |
| ############################################## | |
| def test_memory_capacity_not_exceeded(): | |
| server = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM(id="vm1", name="VM1", cpu_cores=4, memory_gb=32, storage_gb=50) | |
| vm2 = VM(id="vm2", name="VM2", cpu_cores=4, memory_gb=16, storage_gb=100) | |
| assign(server, vm1, vm2) | |
| ( | |
| constraint_verifier.verify_that(memory_capacity) | |
| .given(server, vm1, vm2) | |
| .penalizes_by(0) | |
| ) | |
| def test_memory_capacity_exceeded(): | |
| server = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM(id="vm1", name="VM1", cpu_cores=4, memory_gb=48, storage_gb=50) | |
| vm2 = VM(id="vm2", name="VM2", cpu_cores=4, memory_gb=32, storage_gb=100) | |
| assign(server, vm1, vm2) | |
| # 48 + 32 = 80 GB, capacity = 64, excess = 16 | |
| ( | |
| constraint_verifier.verify_that(memory_capacity) | |
| .given(server, vm1, vm2) | |
| .penalizes_by(16) | |
| ) | |
| ############################################## | |
| # Storage Capacity Tests | |
| ############################################## | |
| def test_storage_capacity_not_exceeded(): | |
| server = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM(id="vm1", name="VM1", cpu_cores=4, memory_gb=8, storage_gb=200) | |
| vm2 = VM(id="vm2", name="VM2", cpu_cores=4, memory_gb=16, storage_gb=250) | |
| assign(server, vm1, vm2) | |
| ( | |
| constraint_verifier.verify_that(storage_capacity) | |
| .given(server, vm1, vm2) | |
| .penalizes_by(0) | |
| ) | |
| def test_storage_capacity_exceeded(): | |
| server = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM(id="vm1", name="VM1", cpu_cores=4, memory_gb=8, storage_gb=300) | |
| vm2 = VM(id="vm2", name="VM2", cpu_cores=4, memory_gb=16, storage_gb=300) | |
| assign(server, vm1, vm2) | |
| # 300 + 300 = 600 GB, capacity = 500, excess = 100 | |
| ( | |
| constraint_verifier.verify_that(storage_capacity) | |
| .given(server, vm1, vm2) | |
| .penalizes_by(100) | |
| ) | |
| ############################################## | |
| # Anti-Affinity Tests | |
| ############################################## | |
| def test_anti_affinity_satisfied(): | |
| server1 = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| server2 = Server(id="s2", name="Server2", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM( | |
| id="vm1", name="DB-Primary", cpu_cores=4, memory_gb=8, storage_gb=50, | |
| anti_affinity_group="db-replicas" | |
| ) | |
| vm2 = VM( | |
| id="vm2", name="DB-Replica", cpu_cores=4, memory_gb=8, storage_gb=50, | |
| anti_affinity_group="db-replicas" | |
| ) | |
| assign(server1, vm1) | |
| assign(server2, vm2) | |
| ( | |
| constraint_verifier.verify_that(anti_affinity) | |
| .given(server1, server2, vm1, vm2) | |
| .penalizes_by(0) | |
| ) | |
| def test_anti_affinity_violated(): | |
| server = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM( | |
| id="vm1", name="DB-Primary", cpu_cores=4, memory_gb=8, storage_gb=50, | |
| anti_affinity_group="db-replicas" | |
| ) | |
| vm2 = VM( | |
| id="vm2", name="DB-Replica", cpu_cores=4, memory_gb=8, storage_gb=50, | |
| anti_affinity_group="db-replicas" | |
| ) | |
| assign(server, vm1, vm2) | |
| # Both VMs on same server with same anti-affinity group = 1 violation | |
| ( | |
| constraint_verifier.verify_that(anti_affinity) | |
| .given(server, vm1, vm2) | |
| .penalizes_by(1) | |
| ) | |
| def test_anti_affinity_no_group(): | |
| server = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM(id="vm1", name="VM1", cpu_cores=4, memory_gb=8, storage_gb=50) | |
| vm2 = VM(id="vm2", name="VM2", cpu_cores=4, memory_gb=8, storage_gb=50) | |
| assign(server, vm1, vm2) | |
| # No anti-affinity group, so no penalty | |
| ( | |
| constraint_verifier.verify_that(anti_affinity) | |
| .given(server, vm1, vm2) | |
| .penalizes_by(0) | |
| ) | |
| ############################################## | |
| # Affinity Tests | |
| ############################################## | |
| def test_affinity_satisfied(): | |
| server = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM( | |
| id="vm1", name="WebApp1", cpu_cores=2, memory_gb=4, storage_gb=20, | |
| affinity_group="web-tier" | |
| ) | |
| vm2 = VM( | |
| id="vm2", name="WebApp2", cpu_cores=2, memory_gb=4, storage_gb=20, | |
| affinity_group="web-tier" | |
| ) | |
| assign(server, vm1, vm2) | |
| ( | |
| constraint_verifier.verify_that(affinity) | |
| .given(server, vm1, vm2) | |
| .penalizes_by(0) | |
| ) | |
| def test_affinity_violated(): | |
| server1 = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| server2 = Server(id="s2", name="Server2", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM( | |
| id="vm1", name="WebApp1", cpu_cores=2, memory_gb=4, storage_gb=20, | |
| affinity_group="web-tier" | |
| ) | |
| vm2 = VM( | |
| id="vm2", name="WebApp2", cpu_cores=2, memory_gb=4, storage_gb=20, | |
| affinity_group="web-tier" | |
| ) | |
| assign(server1, vm1) | |
| assign(server2, vm2) | |
| # VMs on different servers with same affinity group = penalty of 100 | |
| ( | |
| constraint_verifier.verify_that(affinity) | |
| .given(server1, server2, vm1, vm2) | |
| .penalizes_by(100) | |
| ) | |
| def test_affinity_no_group(): | |
| server1 = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| server2 = Server(id="s2", name="Server2", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM(id="vm1", name="VM1", cpu_cores=2, memory_gb=4, storage_gb=20) | |
| vm2 = VM(id="vm2", name="VM2", cpu_cores=2, memory_gb=4, storage_gb=20) | |
| assign(server1, vm1) | |
| assign(server2, vm2) | |
| # No affinity group, so no penalty | |
| ( | |
| constraint_verifier.verify_that(affinity) | |
| .given(server1, server2, vm1, vm2) | |
| .penalizes_by(0) | |
| ) | |
| ############################################## | |
| # Minimize Servers Used Tests | |
| ############################################## | |
| def test_minimize_servers_no_active(): | |
| server1 = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| server2 = Server(id="s2", name="Server2", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| ( | |
| constraint_verifier.verify_that(minimize_servers_used) | |
| .given(server1, server2) | |
| .penalizes_by(0) | |
| ) | |
| def test_minimize_servers_one_active(): | |
| server1 = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| server2 = Server(id="s2", name="Server2", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM(id="vm1", name="VM1", cpu_cores=2, memory_gb=4, storage_gb=20) | |
| assign(server1, vm1) | |
| # 1 active server = 100 penalty | |
| ( | |
| constraint_verifier.verify_that(minimize_servers_used) | |
| .given(server1, server2, vm1) | |
| .penalizes_by(100) | |
| ) | |
| def test_minimize_servers_two_active(): | |
| server1 = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| server2 = Server(id="s2", name="Server2", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM(id="vm1", name="VM1", cpu_cores=2, memory_gb=4, storage_gb=20) | |
| vm2 = VM(id="vm2", name="VM2", cpu_cores=2, memory_gb=4, storage_gb=20) | |
| assign(server1, vm1) | |
| assign(server2, vm2) | |
| # 2 active servers = 200 penalty | |
| ( | |
| constraint_verifier.verify_that(minimize_servers_used) | |
| .given(server1, server2, vm1, vm2) | |
| .penalizes_by(200) | |
| ) | |
| ############################################## | |
| # Balance Utilization Tests | |
| ############################################## | |
| def test_balance_utilization_empty(): | |
| server = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| ( | |
| constraint_verifier.verify_that(balance_utilization) | |
| .given(server) | |
| .penalizes_by(0) | |
| ) | |
| def test_balance_utilization_50_percent(): | |
| server = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM(id="vm1", name="VM1", cpu_cores=8, memory_gb=32, storage_gb=250) | |
| assign(server, vm1) | |
| # 50% utilization = 0.5, squared = 0.25, * 10 = 2.5, int = 2 | |
| ( | |
| constraint_verifier.verify_that(balance_utilization) | |
| .given(server, vm1) | |
| .penalizes_by(2) | |
| ) | |
| def test_balance_utilization_100_percent(): | |
| server = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM(id="vm1", name="VM1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| assign(server, vm1) | |
| # 100% utilization = 1.0, squared = 1.0, * 10 = 10 | |
| ( | |
| constraint_verifier.verify_that(balance_utilization) | |
| .given(server, vm1) | |
| .penalizes_by(10) | |
| ) | |
| ############################################## | |
| # Prioritize Placement Tests | |
| ############################################## | |
| def test_prioritize_placement_assigned(): | |
| server = Server(id="s1", name="Server1", cpu_cores=16, memory_gb=64, storage_gb=500) | |
| vm1 = VM(id="vm1", name="VM1", cpu_cores=4, memory_gb=8, storage_gb=50, priority=5) | |
| assign(server, vm1) | |
| ( | |
| constraint_verifier.verify_that(prioritize_placement) | |
| .given(server, vm1) | |
| .penalizes_by(0) | |
| ) | |