vm-placement / tests /test_constraints.py
blackopsrepl's picture
.
e2dcb4d verified
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)
)