proxlb-solver 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,234 @@
1
+ Metadata-Version: 2.4
2
+ Name: proxlb-solver
3
+ Version: 0.1.0
4
+ Summary: CP-SAT based VM placement solver for ProxLB
5
+ Author-email: Alexander Wirt <alexander.wirt@credativ.de>
6
+ License: GPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/credativ/proxlb-solver
8
+ Project-URL: Repository, https://github.com/credativ/proxlb-solver
9
+ Project-URL: Bug Tracker, https://github.com/credativ/proxlb-solver/issues
10
+ Keywords: proxmox,vm,scheduling,placement,balancing,cp-sat,ortools,proxlb
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: System :: Systems Administration
22
+ Classifier: Topic :: System :: Clustering
23
+ Requires-Python: >=3.11
24
+ Description-Content-Type: text/markdown
25
+ Requires-Dist: ortools>=9.9
26
+ Requires-Dist: PyYAML>=6.0
27
+ Requires-Dist: rich>=13.0
28
+ Requires-Dist: pydantic>=2.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=8.0; extra == "dev"
31
+ Requires-Dist: pytest-mypy; extra == "dev"
32
+ Requires-Dist: flake8>=7.0; extra == "dev"
33
+ Requires-Dist: types-PyYAML; extra == "dev"
34
+
35
+ # ProxLB CP-SAT Solver
36
+
37
+ ![Tests & Report](https://github.com/credativ/proxlb-solver/actions/workflows/test-and-pages.yml/badge.svg)
38
+
39
+ The ProxLB Solver is a mathematically exact scheduler for Proxmox VE clusters.
40
+ It uses Google's **OR-Tools CP-SAT** to find the provably global optimum for VM and Container placement, moving beyond simple greedy heuristics.
41
+
42
+ ## Algorithmic Overview
43
+
44
+ ```mermaid
45
+ graph TD
46
+ A[Input: Cluster Snapshot] --> B{Validator}
47
+ B -- Conflict detected --> C[Status: RULE_CONFLICT]
48
+ B -- OK --> D[CP-SAT Modeling]
49
+
50
+ subgraph Solver [Optimization Loop]
51
+ D --> E[Solver: Find Optimum]
52
+ E -- Infeasible --> F[Status: INFEASIBLE]
53
+ E -- Optimal State --> G[Planner: Calculate Path]
54
+ G -- Blocked cycle? --> H{Reachable?}
55
+ H -- No --> I[Add No-Good Clause]
56
+ I --> E
57
+ H -- Yes --> J[Solution: Optimal & Executable]
58
+ end
59
+ ```
60
+
61
+ ---
62
+
63
+ ## 1. Mathematical Core
64
+
65
+ The solver treats guest placement as an **Integer Linear Programming (ILP)** problem.
66
+
67
+ ### Decision Variables
68
+ For every guest $i$ (VM or Container) and node $j$, a binary variable $x_{i,j}$ is defined:
69
+ * $x_{i,j} = 1$: Guest $i$ is assigned to node $j$.
70
+ * $x_{i,j} = 0$: Guest $i$ is not assigned to node $j$.
71
+
72
+ Every guest must be assigned to exactly one node: $\sum_{j} x_{i,j} = 1$.
73
+
74
+ ### Integer Arithmetic & Scaling
75
+ CP-SAT is an integer-only solver. To handle fractional values (like 0.5 CPU cores or 12.5% PSI), the solver internally scales all metrics by a factor of **10,000** (`_LOAD_SCALE`).
76
+ * 100% load is represented as 10,000.
77
+ * 0.5% PSI is represented as 50.
78
+ * This scaling explains the large objective values seen in technical solver logs.
79
+
80
+ ### Objective Function
81
+ The solver minimizes a weighted cost function:
82
+ $$\text{Minimize: } (w_\text{balance} \cdot \text{Spread}) + (w_\text{stickiness} \cdot \text{MigrationCost}) + \text{Penalty}_\text{SoftRules}$$
83
+
84
+ * **Spread**: The difference between the most and least utilized node ($\text{Max} - \text{Min}$), scaled by total capacity.
85
+ * **MigrationCost**: A weighted sum over all migrated guests — see §2 below.
86
+ * **Penalty**: A massive malus ($1{,}000{,}000$) for every violated soft constraint.
87
+
88
+ ---
89
+
90
+ ## 2. Migration Cost Model
91
+
92
+ Migrating a guest has a real cost: RAM must be copied live over the network (dirty-page tracking); local disk requires a full sequential copy. The cost model reflects this:
93
+
94
+ $$\text{cost}(\text{Guest}) = \max(1,\ \lfloor \text{RAM} / 256\,\text{MiB} \rfloor) + 4 \times \lfloor \text{LocalDisk} / 256\,\text{MiB} \rfloor$$
95
+
96
+ The 256 MiB base unit gives enough granularity for the solver to distinguish between a 512 MiB guest (cost 2) and a 1 GiB guest (cost 4). The `max(1, …)` floor ensures tiny containers still have a non-zero weight.
97
+
98
+ The **4× local disk factor** reflects that copying a local disk (LVM/ZFS) is significantly slower than a RAM live-migration.
99
+
100
+ ---
101
+
102
+ ## 3. Resource Metrics & Strategy Modes
103
+
104
+ ProxLB supports multiple optimization dimensions via the `method` parameter:
105
+
106
+ | Method | Balance objective | Use Case |
107
+ | :--- | :--- | :--- |
108
+ | `memory` | RAM allocation | Classic memory-based balancing (default). |
109
+ | `cpu` | CPU load (cores) | Throughput optimization. |
110
+ | `disk` | Local storage usage | Disk capacity balancing across nodes. |
111
+ | `cpu_psi` | CPU stall time (PSI) | Latency optimization (PVE 9+). |
112
+ | `cpu_smart` | CPU load + PSI | Balance of throughput and responsiveness. |
113
+ | `global_smart` | RAM + CPU + IO | **Holistic cluster-wide optimization**. |
114
+
115
+ ### RAM: configured allocation vs. actual RSS
116
+ The solver balances RAM by **configured allocation**, not actual RSS. This is correct for capacity planning — a VM configured with 4 GiB must be placed on a node that has 4 GiB reserved, regardless of its current usage.
117
+
118
+ ### CPU: Usage vs. Assigned
119
+ - **Used Mode (Default)**: Balances based on the actual measured CPU load (`cpu_used`).
120
+ - **Assigned Mode**: Balances based on the *configured number of vCPUs*. This ensures that reserved compute capacity is distributed evenly.
121
+
122
+ ### The PSI Footprint Model (CPU, RAM, IO)
123
+ For PSI metrics, the solver uses an **additive footprint model**. It tries to spread these contributions so that the aggregate pressure on each node stays as low and uniform as possible.
124
+
125
+ ---
126
+
127
+ ## 4. Weight Hierarchy
128
+
129
+ Optimization is fine-tuned via three distinct tiers:
130
+
131
+ 1. **Global Level (`w_global_*`)**: Importance of resource pools (e.g., "RAM balance is 10x more important than IO").
132
+ 2. **Resource Level (`w_*_usage` vs `w_*_psi`)**: Weighting raw utilization against dynamic pressure stalls.
133
+ 3. **Guest Level (`priority`)**:
134
+ * **Priority 3 (High)**: Footprint counts 3× towards the spread calculation.
135
+ * **Priority 1 (Low)**: Footprint counts 1×.
136
+ * *Effect*: High-priority guests "force" their way onto nodes with the most free resources by artificially inflating their perceived load during the optimization phase.
137
+
138
+ ---
139
+
140
+ ## 5. Constraints
141
+
142
+ ### Hard Constraints (Strict)
143
+ Violations result in `INFEASIBLE`.
144
+ - **Capacity**: RAM, CPU cores (with overcommit), and named storage pools (ZFS, LVM).
145
+ - **Pinning**: Binding guests to specific hardware. **Pinning is always hard.**
146
+ - **Maintenance**: Nodes in maintenance mode are forbidden targets.
147
+ - **Hard Rules**: Affinity/Anti-Affinity marked as `hard: true`.
148
+
149
+ ### Rule Origins & Specialized Handling
150
+ The solver distinguishes between rules based on their `origin`:
151
+
152
+ | Origin | Type | Handling | Rationale |
153
+ | :--- | :--- | :--- | :--- |
154
+ | `pve` | Native HA | **Atomic / Strict** | Proxmox enforces these rules automatically. |
155
+ | `plb` | Internal Tags | **Granular / Soft** | ProxLB manages these; allows flexible transitions. |
156
+
157
+ 1. **PVE Affinity (Atomic)**: Members of a native Proxmox affinity group are moved in the **same execution step**.
158
+ 2. **PVE Anti-Affinity (Strict)**: The planner ensures partners **never share a node** even for a split second during transitions.
159
+
160
+ ---
161
+
162
+ ## 6. Reachability Guarantee & Migration Planning
163
+
164
+ An optimal state is worthless if it cannot be executed (e.g., no buffer space for a swap). The Solver finds the *target state*, but the **Planner** determines the *safe path* to get there.
165
+
166
+ 1. **Dependency Analysis**: The Planner builds a directed graph where an edge $A \to B$ means "Guest A must move before Guest B can fit on its target node".
167
+ 2. **Step-by-Step Simulation**: Migrations are grouped into execution steps. For every step, the Planner simulates node capacities to ensure that no host is oversubscribed even during the transition.
168
+ 3. **Cycle Breaking (Temp-Moves)**: Circular dependencies (e.g., Guest-A $\leftrightarrow$ Guest-B swap) are detected. If a third "spare" node with sufficient capacity exists, the Planner inserts a **temp-move** (parking) to break the loop.
169
+ 4. **Atomic PVE Affinity**: For native Proxmox affinity groups, the Planner picks one "trigger" guest for the API call. It assumes Proxmox will co-migrate the other group members atomically and accounts for their total resource footprint in that single step.
170
+ 5. **Strict PVE Anti-Affinity**: To satisfy native PVE anti-affinity, the Planner ensures that partners **never** share a node, even temporarily. One must fully vacate the target before the other is allowed to land.
171
+ 6. **No-Good Feedback**: If a dependency cycle is mathematically unbreakable (e.g., cluster is too full for parking), the Planner rejects the solution. The Solver is then re-triggered with a "No-Good" constraint to find the next-best reachable state.
172
+
173
+ ---
174
+
175
+ ## 7. ProxLB Integration (Shadow & Active Mode)
176
+
177
+ The solver integrates with ProxLB via two operating modes:
178
+
179
+ ### Shadow mode (default, read-only)
180
+ The solver runs alongside ProxLB's built-in balancer without changing anything. It produces a structured **JSONL log** and an **HTML report** for validation.
181
+
182
+ ### Active mode
183
+ The solver takes over execution. ProxLB's `Balancing()` class is still used for API calls, but the solver determines the plan. A **feedback loop** handles migration failures by pinning failed guests and re-solving.
184
+
185
+ ---
186
+
187
+ ## Administrator Guide: Configuration & Defaults
188
+
189
+ The ProxLB Solver is tuned for **Stability over Agility** by default.
190
+
191
+ #### 1. Operational Safety
192
+ * **`max_node_inflow` (Default: 1)**: Only one guest at a time can migrate *into* a host. This prevents memory or CPU peaks that could trigger OOM on the target host.
193
+ * **`max_parallel_migrations` (Default: 2)**: Limits simultaneous migrations cluster-wide.
194
+ * **`balanciness` (Default: 3 — Moderate)**:
195
+ * Level 1–2: Only moves guests for maintenance or hard rule violations.
196
+ * Level 3: Rebalances only if the spread exceeds ~15%.
197
+ * Level 5: Chases perfect balance.
198
+
199
+ #### 2. Resource Balancing Strategy
200
+ * **`method` (Default: `memory`)**: RAM is usually the hardest bottleneck. Start with memory balancing before exploring CPU or Smart modes.
201
+ * **`cpu_overcommit` (Default: 2.0)**: Allows assigning more vCPUs than physical cores exist.
202
+
203
+ ---
204
+
205
+ ## Usage for Developers
206
+
207
+ ### Installation
208
+ ```bash
209
+ make install
210
+ ```
211
+
212
+ ### Running Tests
213
+ ```bash
214
+ make test
215
+ ```
216
+
217
+ ### Generating Reports
218
+ Integration results and shadow-mode comparisons can be visualized as interactive reports:
219
+ ```bash
220
+ make report
221
+ ```
222
+ This produces the following artifacts in the project root:
223
+ - `results.html`: Full interactive report with sidebar, Mermaid dependency graphs, and per-run detail pages.
224
+ - `results.md`: A concise Markdown summary of all processed runs.
225
+ - `results.xml`: JUnit-compatible XML for CI/CD integration.
226
+
227
+ Individual run logs are stored as `.jsonl` files in the configured `log_dir` (e.g., `/tmp/proxlb-solver-logs`).
228
+
229
+ ### Internal Architecture
230
+ - **`models.py`**: Strict type definitions for the cluster state.
231
+ - **`adapter.py`**: Bridge between ProxLB's runtime data and the solver models.
232
+ - **`solver.py`**: Mathematical model and CP-SAT integration.
233
+ - **`planner.py`**: Topological sort and dependency resolution for migrations.
234
+ - **`shadow.py`**: Non-intrusive "shadow mode" for live cluster observation.
@@ -0,0 +1,200 @@
1
+ # ProxLB CP-SAT Solver
2
+
3
+ ![Tests & Report](https://github.com/credativ/proxlb-solver/actions/workflows/test-and-pages.yml/badge.svg)
4
+
5
+ The ProxLB Solver is a mathematically exact scheduler for Proxmox VE clusters.
6
+ It uses Google's **OR-Tools CP-SAT** to find the provably global optimum for VM and Container placement, moving beyond simple greedy heuristics.
7
+
8
+ ## Algorithmic Overview
9
+
10
+ ```mermaid
11
+ graph TD
12
+ A[Input: Cluster Snapshot] --> B{Validator}
13
+ B -- Conflict detected --> C[Status: RULE_CONFLICT]
14
+ B -- OK --> D[CP-SAT Modeling]
15
+
16
+ subgraph Solver [Optimization Loop]
17
+ D --> E[Solver: Find Optimum]
18
+ E -- Infeasible --> F[Status: INFEASIBLE]
19
+ E -- Optimal State --> G[Planner: Calculate Path]
20
+ G -- Blocked cycle? --> H{Reachable?}
21
+ H -- No --> I[Add No-Good Clause]
22
+ I --> E
23
+ H -- Yes --> J[Solution: Optimal & Executable]
24
+ end
25
+ ```
26
+
27
+ ---
28
+
29
+ ## 1. Mathematical Core
30
+
31
+ The solver treats guest placement as an **Integer Linear Programming (ILP)** problem.
32
+
33
+ ### Decision Variables
34
+ For every guest $i$ (VM or Container) and node $j$, a binary variable $x_{i,j}$ is defined:
35
+ * $x_{i,j} = 1$: Guest $i$ is assigned to node $j$.
36
+ * $x_{i,j} = 0$: Guest $i$ is not assigned to node $j$.
37
+
38
+ Every guest must be assigned to exactly one node: $\sum_{j} x_{i,j} = 1$.
39
+
40
+ ### Integer Arithmetic & Scaling
41
+ CP-SAT is an integer-only solver. To handle fractional values (like 0.5 CPU cores or 12.5% PSI), the solver internally scales all metrics by a factor of **10,000** (`_LOAD_SCALE`).
42
+ * 100% load is represented as 10,000.
43
+ * 0.5% PSI is represented as 50.
44
+ * This scaling explains the large objective values seen in technical solver logs.
45
+
46
+ ### Objective Function
47
+ The solver minimizes a weighted cost function:
48
+ $$\text{Minimize: } (w_\text{balance} \cdot \text{Spread}) + (w_\text{stickiness} \cdot \text{MigrationCost}) + \text{Penalty}_\text{SoftRules}$$
49
+
50
+ * **Spread**: The difference between the most and least utilized node ($\text{Max} - \text{Min}$), scaled by total capacity.
51
+ * **MigrationCost**: A weighted sum over all migrated guests — see §2 below.
52
+ * **Penalty**: A massive malus ($1{,}000{,}000$) for every violated soft constraint.
53
+
54
+ ---
55
+
56
+ ## 2. Migration Cost Model
57
+
58
+ Migrating a guest has a real cost: RAM must be copied live over the network (dirty-page tracking); local disk requires a full sequential copy. The cost model reflects this:
59
+
60
+ $$\text{cost}(\text{Guest}) = \max(1,\ \lfloor \text{RAM} / 256\,\text{MiB} \rfloor) + 4 \times \lfloor \text{LocalDisk} / 256\,\text{MiB} \rfloor$$
61
+
62
+ The 256 MiB base unit gives enough granularity for the solver to distinguish between a 512 MiB guest (cost 2) and a 1 GiB guest (cost 4). The `max(1, …)` floor ensures tiny containers still have a non-zero weight.
63
+
64
+ The **4× local disk factor** reflects that copying a local disk (LVM/ZFS) is significantly slower than a RAM live-migration.
65
+
66
+ ---
67
+
68
+ ## 3. Resource Metrics & Strategy Modes
69
+
70
+ ProxLB supports multiple optimization dimensions via the `method` parameter:
71
+
72
+ | Method | Balance objective | Use Case |
73
+ | :--- | :--- | :--- |
74
+ | `memory` | RAM allocation | Classic memory-based balancing (default). |
75
+ | `cpu` | CPU load (cores) | Throughput optimization. |
76
+ | `disk` | Local storage usage | Disk capacity balancing across nodes. |
77
+ | `cpu_psi` | CPU stall time (PSI) | Latency optimization (PVE 9+). |
78
+ | `cpu_smart` | CPU load + PSI | Balance of throughput and responsiveness. |
79
+ | `global_smart` | RAM + CPU + IO | **Holistic cluster-wide optimization**. |
80
+
81
+ ### RAM: configured allocation vs. actual RSS
82
+ The solver balances RAM by **configured allocation**, not actual RSS. This is correct for capacity planning — a VM configured with 4 GiB must be placed on a node that has 4 GiB reserved, regardless of its current usage.
83
+
84
+ ### CPU: Usage vs. Assigned
85
+ - **Used Mode (Default)**: Balances based on the actual measured CPU load (`cpu_used`).
86
+ - **Assigned Mode**: Balances based on the *configured number of vCPUs*. This ensures that reserved compute capacity is distributed evenly.
87
+
88
+ ### The PSI Footprint Model (CPU, RAM, IO)
89
+ For PSI metrics, the solver uses an **additive footprint model**. It tries to spread these contributions so that the aggregate pressure on each node stays as low and uniform as possible.
90
+
91
+ ---
92
+
93
+ ## 4. Weight Hierarchy
94
+
95
+ Optimization is fine-tuned via three distinct tiers:
96
+
97
+ 1. **Global Level (`w_global_*`)**: Importance of resource pools (e.g., "RAM balance is 10x more important than IO").
98
+ 2. **Resource Level (`w_*_usage` vs `w_*_psi`)**: Weighting raw utilization against dynamic pressure stalls.
99
+ 3. **Guest Level (`priority`)**:
100
+ * **Priority 3 (High)**: Footprint counts 3× towards the spread calculation.
101
+ * **Priority 1 (Low)**: Footprint counts 1×.
102
+ * *Effect*: High-priority guests "force" their way onto nodes with the most free resources by artificially inflating their perceived load during the optimization phase.
103
+
104
+ ---
105
+
106
+ ## 5. Constraints
107
+
108
+ ### Hard Constraints (Strict)
109
+ Violations result in `INFEASIBLE`.
110
+ - **Capacity**: RAM, CPU cores (with overcommit), and named storage pools (ZFS, LVM).
111
+ - **Pinning**: Binding guests to specific hardware. **Pinning is always hard.**
112
+ - **Maintenance**: Nodes in maintenance mode are forbidden targets.
113
+ - **Hard Rules**: Affinity/Anti-Affinity marked as `hard: true`.
114
+
115
+ ### Rule Origins & Specialized Handling
116
+ The solver distinguishes between rules based on their `origin`:
117
+
118
+ | Origin | Type | Handling | Rationale |
119
+ | :--- | :--- | :--- | :--- |
120
+ | `pve` | Native HA | **Atomic / Strict** | Proxmox enforces these rules automatically. |
121
+ | `plb` | Internal Tags | **Granular / Soft** | ProxLB manages these; allows flexible transitions. |
122
+
123
+ 1. **PVE Affinity (Atomic)**: Members of a native Proxmox affinity group are moved in the **same execution step**.
124
+ 2. **PVE Anti-Affinity (Strict)**: The planner ensures partners **never share a node** even for a split second during transitions.
125
+
126
+ ---
127
+
128
+ ## 6. Reachability Guarantee & Migration Planning
129
+
130
+ An optimal state is worthless if it cannot be executed (e.g., no buffer space for a swap). The Solver finds the *target state*, but the **Planner** determines the *safe path* to get there.
131
+
132
+ 1. **Dependency Analysis**: The Planner builds a directed graph where an edge $A \to B$ means "Guest A must move before Guest B can fit on its target node".
133
+ 2. **Step-by-Step Simulation**: Migrations are grouped into execution steps. For every step, the Planner simulates node capacities to ensure that no host is oversubscribed even during the transition.
134
+ 3. **Cycle Breaking (Temp-Moves)**: Circular dependencies (e.g., Guest-A $\leftrightarrow$ Guest-B swap) are detected. If a third "spare" node with sufficient capacity exists, the Planner inserts a **temp-move** (parking) to break the loop.
135
+ 4. **Atomic PVE Affinity**: For native Proxmox affinity groups, the Planner picks one "trigger" guest for the API call. It assumes Proxmox will co-migrate the other group members atomically and accounts for their total resource footprint in that single step.
136
+ 5. **Strict PVE Anti-Affinity**: To satisfy native PVE anti-affinity, the Planner ensures that partners **never** share a node, even temporarily. One must fully vacate the target before the other is allowed to land.
137
+ 6. **No-Good Feedback**: If a dependency cycle is mathematically unbreakable (e.g., cluster is too full for parking), the Planner rejects the solution. The Solver is then re-triggered with a "No-Good" constraint to find the next-best reachable state.
138
+
139
+ ---
140
+
141
+ ## 7. ProxLB Integration (Shadow & Active Mode)
142
+
143
+ The solver integrates with ProxLB via two operating modes:
144
+
145
+ ### Shadow mode (default, read-only)
146
+ The solver runs alongside ProxLB's built-in balancer without changing anything. It produces a structured **JSONL log** and an **HTML report** for validation.
147
+
148
+ ### Active mode
149
+ The solver takes over execution. ProxLB's `Balancing()` class is still used for API calls, but the solver determines the plan. A **feedback loop** handles migration failures by pinning failed guests and re-solving.
150
+
151
+ ---
152
+
153
+ ## Administrator Guide: Configuration & Defaults
154
+
155
+ The ProxLB Solver is tuned for **Stability over Agility** by default.
156
+
157
+ #### 1. Operational Safety
158
+ * **`max_node_inflow` (Default: 1)**: Only one guest at a time can migrate *into* a host. This prevents memory or CPU peaks that could trigger OOM on the target host.
159
+ * **`max_parallel_migrations` (Default: 2)**: Limits simultaneous migrations cluster-wide.
160
+ * **`balanciness` (Default: 3 — Moderate)**:
161
+ * Level 1–2: Only moves guests for maintenance or hard rule violations.
162
+ * Level 3: Rebalances only if the spread exceeds ~15%.
163
+ * Level 5: Chases perfect balance.
164
+
165
+ #### 2. Resource Balancing Strategy
166
+ * **`method` (Default: `memory`)**: RAM is usually the hardest bottleneck. Start with memory balancing before exploring CPU or Smart modes.
167
+ * **`cpu_overcommit` (Default: 2.0)**: Allows assigning more vCPUs than physical cores exist.
168
+
169
+ ---
170
+
171
+ ## Usage for Developers
172
+
173
+ ### Installation
174
+ ```bash
175
+ make install
176
+ ```
177
+
178
+ ### Running Tests
179
+ ```bash
180
+ make test
181
+ ```
182
+
183
+ ### Generating Reports
184
+ Integration results and shadow-mode comparisons can be visualized as interactive reports:
185
+ ```bash
186
+ make report
187
+ ```
188
+ This produces the following artifacts in the project root:
189
+ - `results.html`: Full interactive report with sidebar, Mermaid dependency graphs, and per-run detail pages.
190
+ - `results.md`: A concise Markdown summary of all processed runs.
191
+ - `results.xml`: JUnit-compatible XML for CI/CD integration.
192
+
193
+ Individual run logs are stored as `.jsonl` files in the configured `log_dir` (e.g., `/tmp/proxlb-solver-logs`).
194
+
195
+ ### Internal Architecture
196
+ - **`models.py`**: Strict type definitions for the cluster state.
197
+ - **`adapter.py`**: Bridge between ProxLB's runtime data and the solver models.
198
+ - **`solver.py`**: Mathematical model and CP-SAT integration.
199
+ - **`planner.py`**: Topological sort and dependency resolution for migrations.
200
+ - **`shadow.py`**: Non-intrusive "shadow mode" for live cluster observation.
@@ -0,0 +1,53 @@
1
+ """ProxLB CP-SAT VM Scheduler.
2
+
3
+ Public API
4
+ ----------
5
+ Core types:
6
+ Cluster, Node, VM, Constraints, Balancing,
7
+ Solution, Migration, MigrationPlan, SolverStats
8
+
9
+ Solver functions:
10
+ solve(cluster, time_limit_s) — single-shot solve
11
+ solve_reachable(cluster, ...) — solve with planner feedback loop
12
+
13
+ ProxLB integration:
14
+ from_proxlb_data(proxlb_data, ...) — convert live ProxLB data to Cluster
15
+ run_shadow(proxlb_data, cfg) — shadow-mode observer (read-only)
16
+ """
17
+
18
+ __version__ = "0.1.0"
19
+
20
+ from .models import (
21
+ Cluster,
22
+ Node,
23
+ VM,
24
+ Constraints,
25
+ Balancing,
26
+ Solution,
27
+ Migration,
28
+ MigrationPlan,
29
+ SolverStats,
30
+ )
31
+ from .solver import solve, solve_reachable
32
+ from .adapter import from_proxlb_data
33
+ from .shadow import run_shadow
34
+
35
+ __all__ = [
36
+ "__version__",
37
+ # Data models
38
+ "Cluster",
39
+ "Node",
40
+ "VM",
41
+ "Constraints",
42
+ "Balancing",
43
+ "Solution",
44
+ "Migration",
45
+ "MigrationPlan",
46
+ "SolverStats",
47
+ # Solver
48
+ "solve",
49
+ "solve_reachable",
50
+ # ProxLB integration
51
+ "from_proxlb_data",
52
+ "run_shadow",
53
+ ]