1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-30 22:50:10 +03:00

F #1550: sanitize weights and refactor one_drs defaults (#3481)

This commit is contained in:
MarioRobres 2025-03-18 17:30:52 +01:00 committed by GitHub
parent bfbc4c16c6
commit bd0d225b7f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 76 additions and 60 deletions

View File

@ -41,8 +41,30 @@ from lib.models.scheduler_driver_action import SchedulerDriverAction
class OptimizerParser:
CONFIG_FILE_PATH = "/etc/one/schedulers/one_drs.conf"
DEFAULT_CBC_PATH = "/usr/lib/one/python/pulp/solverdir/cbc/linux/64/cbc"
SOLVERS = {"GLPK": GLPK_CMD, "CBC": COIN_CMD, "COINMP": COINMP_DLL}
DEFAULT_CONFIG = {
"DEFAULT_SCHED": {
"SOLVER": "CBC",
"SOLVER_PATH": "/usr/lib/one/python/pulp/solverdir/cbc/linux/64/cbc",
},
"PLACE": {
"POLICY": "PACK",
},
"OPTIMIZE": {
"POLICY": "BALANCE",
"MIGRATION_THRESHOLD": 10,
"WEIGHTS": {
"CPU_USAGE": 0.2,
"CPU": 0.2,
"MEMORY": 0.4,
"DISK": 0.1,
"NET": 0.1,
},
},
"PREDICTIVE": 0.3,
"MEMORY_SYSTEM_DS_SCALE": 0,
"DIFFERENT_VNETS": True,
}
__slots__ = (
"parser",
@ -89,79 +111,64 @@ class OptimizerParser:
sys.exit(1)
# Select Policy based on mode
if mode == "optimize":
mode_config = config_data.get("OPTIMIZE", {})
if mode_config.get("POLICY", "").upper() != "BALANCE":
cls.log_general(
"WARNING",
f"Unknown or missing {mode} configuration. Using default options.",
)
mode_config = {}
mode_config = config_data.get(mode.upper(), {})
default_mode_config = cls.DEFAULT_CONFIG.get(mode.upper(), {})
mode_config.setdefault(
"WEIGHTS",
{
"CPU_USAGE": 0.2,
"CPU": 0.2,
"MEMORY": 0.4,
"DISK": 0.1,
"NET": 0.1,
},
if not mode_config or mode_config.get(
"POLICY", ""
).upper() != default_mode_config.get("POLICY", ""):
cls.log_general(
"WARNING",
f"Unknown or missing {mode} configuration. Using default options.",
)
mode_config.setdefault("MIGRATION_THRESHOLD", 10)
mode_config.setdefault("POLICY", "balance")
elif mode in ("deploy", "place"):
mode_config = config_data.get("PLACE", {})
if mode_config.get("POLICY", "").upper() != "PACK":
cls.log_general(
"WARNING",
f"Unknown or missing {mode} configuration. Using default options.",
)
mode_config = {"POLICY": "pack"}
mode_config = default_mode_config.copy()
else:
mode_config = {}
for key, value in default_mode_config.items():
if key == "WEIGHTS":
if sum(mode_config[key].values()) > 1:
mode_config[key] = default_mode_config[key]
continue
mode_config.setdefault(key, value)
# Optimizer solver
default_solver = cls.DEFAULT_CONFIG["DEFAULT_SCHED"]
default_sched = config_data.get("DEFAULT_SCHED", {})
solver_name = default_sched.get("SOLVER", "CBC").upper()
solver_path = default_sched.get("SOLVER_PATH", cls.DEFAULT_CBC_PATH)
solver_name = default_sched.get("SOLVER", "").upper()
solver_path = default_sched.get("SOLVER_PATH", None)
if solver_name not in cls.SOLVERS or not solver_path:
cls.log_general(
"WARNING",
f"Invalid or missing solver '{solver_name}' with path '{solver_path}'. Using default CBC.",
f"Invalid or missing solver '{solver_name}' at '{solver_path}'. Using default.",
)
solver_name, solver_path = (
default_solver["SOLVER"],
default_solver["SOLVER_PATH"],
)
solver_name, solver_path = "CBC", cls.DEFAULT_CBC_PATH
solver = cls.SOLVERS[solver_name](msg=False, timeLimit=60, path=solver_path)
if not solver.available():
cls.log_general("ERROR", f"Solver {solver_name} is not available.")
sys.exit(1)
# Schedule configuration
config_defaults = {
"MEMORY_SYSTEM_DS_SCALE": 0,
"DIFFERENT_VNETS": True,
"PREDICTIVE": 0,
}
config_values = {
key: config_data.get(key, default)
for key, default in config_defaults.items()
}
for key, default in config_defaults.items():
if key not in config_data:
cls.log_general(
"WARNING", f"Missing {key}. Using default value of {default}."
)
sched_config = {}
sched_config["MEMORY_SYSTEM_DS_SCALE"] = config_data.get(
"MEMORY_SYSTEM_DS_SCALE", cls.DEFAULT_CONFIG["MEMORY_SYSTEM_DS_SCALE"]
)
sched_config["DIFFERENT_VNETS"] = config_data.get(
"DIFFERENT_VNETS", cls.DEFAULT_CONFIG["DIFFERENT_VNETS"]
)
sched_config["PREDICTIVE"] = config_data.get(
"PREDICTIVE", cls.DEFAULT_CONFIG["PREDICTIVE"]
)
return {
"MODE": mode_config,
"SOLVER": solver,
**config_values,
**sched_config,
}
def build_optimizer(self) -> ILPOptimizer:
if self.mode == "deploy":
if self.mode == "place":
criteria = self.config["MODE"]["POLICY"].lower()
allowed_migrations = None
else:
@ -495,7 +502,10 @@ class OptimizerParser:
None,
)
if one_drs is None:
return None
return {
**self.config["MODE"].copy(),
"PREDICTIVE": self.config["PREDICTIVE"],
}
migration_threshold = next(
(
@ -523,7 +533,7 @@ class OptimizerParser:
)
weights = self._get_weights(one_drs)
# TODO: Consider AUTOMATION and OPTIMIZATION_TARGET attributes
# TODO: Consider AUTOMATION attribute
return {
"POLICY": policy,
"MIGRATION_THRESHOLD": migration_threshold,
@ -581,9 +591,7 @@ class OptimizerParser:
}
return {
weight_map[child.qname.upper()]: OptimizerParser._sanity_check(
float(child.text)
)
weight_map[child.qname.upper()]: float(child.text)
for child in one_drs.children
if child.qname.upper() in weight_map
}
@ -608,7 +616,16 @@ class OptimizerParser:
else:
clone_attr = disk_attrs.get("CLONE")
if clone_attr is None:
break
host_ds, share_ds, allow = self._build_dstores(vm_req, host_ds)
ds_req[req_id] = DStoreRequirement(
id=req_id,
vm_id=int(vm.id),
size=0,
allow_host_dstores=allow,
host_dstore_ids=host_ds,
shared_dstore_ids=share_ds,
)
continue
st = (
disk_attrs.get("CLONE_TARGET")
if clone_attr.upper() == "YES"
@ -630,7 +647,6 @@ class OptimizerParser:
if disk_attrs.get("DATASTORE_ID")
else None
)
# image DS req
if image_ds_usage > 0:
ds_req[req_id] = DStoreRequirement(
@ -752,7 +768,7 @@ class OptimizerParser:
keys = ["CPU_USAGE", "CPU", "MEMORY", "DISK", "NET"]
provided = {k: cluster_config[k] for k in keys if k in cluster_config}
if sum(provided.values()) > 1:
return self.config["MODE"]["WEIGHTS"].copy()
provided = self.config["MODE"]["WEIGHTS"].copy()
result = {}
for key, weight in provided.items():
lower_key = key.lower()

View File

@ -46,7 +46,7 @@ def main():
write_debug_file(DEBUG_INPUT_PATH, decoded_data, mode="wb")
# Parse XML input and build Optimizer
parser = OptimizerParser(stdin_data=decoded_data, mode="deploy")
parser = OptimizerParser(stdin_data=decoded_data, mode="place")
optimizer = parser.build_optimizer()
# Execute Optimizer