From 0037e24177fe9fc28809b8afd67a881af94037c0 Mon Sep 17 00:00:00 2001
From: Trygve Laugstøl <trygvis@inamo.no>
Date: Sun, 24 Jan 2021 21:43:30 +0100
Subject: WIP: Optaplanner.

---
 module/acme/classpath.txt                          | 33 ++++++++++++++
 module/acme/pom.xml                                | 14 ++++++
 .../acme/planning/machine/CloudBalance.java        | 48 ++++++++++++++++++++
 .../machine/CloudBalancingEasyScoreCalculator.java | 45 +++++++++++++++++++
 .../acme/planning/machine/CloudComputer.java       | 16 +++++++
 .../acme/planning/machine/CloudPlaningMain.java    | 52 ++++++++++++++++++++++
 .../acme/planning/machine/CloudPlanner.java        | 22 +++++++++
 .../acme/planning/machine/CloudProcess.java        | 48 ++++++++++++++++++++
 .../machine/CloudProcessDifficultyComparator.java  | 15 +++++++
 .../acme/planning/machine/ScalewayInstance.java    | 25 +++++++++++
 .../main/resources/io/trygvis/acme/apps/apps.drl   | 22 +++++----
 .../acme/planning/machine/solver-config.xml        | 18 ++++++++
 module/ri-engine/pom.xml                           |  2 +
 .../main/java/io/trygvis/rules/dba/Container.java  |  6 ++-
 .../rules/machine/MachineSpecification.java        | 19 ++++++++
 15 files changed, 375 insertions(+), 10 deletions(-)
 create mode 100644 module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudBalance.java
 create mode 100644 module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudBalancingEasyScoreCalculator.java
 create mode 100644 module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudComputer.java
 create mode 100644 module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudPlaningMain.java
 create mode 100644 module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudPlanner.java
 create mode 100644 module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudProcess.java
 create mode 100644 module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudProcessDifficultyComparator.java
 create mode 100644 module/acme/src/main/java/io/trygvis/acme/planning/machine/ScalewayInstance.java
 create mode 100644 module/acme/src/main/resources/io/trygvis/acme/planning/machine/solver-config.xml
 create mode 100644 module/ri-engine/src/main/java/io/trygvis/rules/machine/MachineSpecification.java

(limited to 'module')

diff --git a/module/acme/classpath.txt b/module/acme/classpath.txt
index ed8e5f6..8777318 100644
--- a/module/acme/classpath.txt
+++ b/module/acme/classpath.txt
@@ -6,6 +6,7 @@ com.fasterxml.jackson.core:jackson-annotations:2.12.0:jar
 com.fasterxml.jackson.core:jackson-core:2.12.0:jar
 com.fasterxml.jackson.core:jackson-databind:2.12.0:jar
 com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.12.0:jar
+com.github.javaparser:javaparser-core:3.13.10:jar
 com.github.virtuald:curvesapi:1.06:jar
 com.google.code.findbugs:annotations:3.0.1:jar
 com.google.errorprone:error_prone_annotations:2.1.3:jar
@@ -14,6 +15,8 @@ com.google.j2objc:j2objc-annotations:1.1:jar
 com.google.re2j:re2j:1.2:jar
 com.googlecode.java-ipv6:java-ipv6:0.17:jar
 com.hubspot.jinjava:jinjava:2.5.6:jar
+com.sun.istack:istack-commons-runtime:3.0.8:jar
+com.sun.xml.fastinfoset:FastInfoset:1.2.16:jar
 com.thoughtworks.xstream:xstream:1.4.14:jar
 com.zaxxer:SparseBitSet:1.2:jar
 commons-codec:commons-codec:1.11:jar
@@ -21,6 +24,8 @@ commons-io:commons-io:2.8.0:jar
 commons-net:commons-net:2.2:jar
 io.trygvis.rules-sandbox:ri-engine:1.0-SNAPSHOT:jar
 io.trygvis.rules-sandbox:ri-wireguard:1.0-SNAPSHOT:jar
+jakarta.activation:jakarta.activation-api:1.2.1:jar
+jakarta.xml.bind:jakarta.xml.bind-api:2.3.2:jar
 org.antlr:antlr-runtime:3.5.2:jar
 org.apache.commons:commons-collections4:4.4:jar
 org.apache.commons:commons-compress:1.19:jar
@@ -32,26 +37,54 @@ org.apache.poi:poi-ooxml-schemas:4.1.2:jar
 org.apache.xmlbeans:xmlbeans:3.1.0:jar
 org.checkerframework:checker-compat-qual:2.0.0:jar
 org.codehaus.mojo:animal-sniffer-annotations:1.14:jar
+org.drools:drools-canonical-model:7.47.0.Final:jar
 org.drools:drools-compiler:7.47.0.Final:jar
 org.drools:drools-core:7.47.0.Final:jar
 org.drools:drools-core-dynamic:7.47.0.Final:jar
 org.drools:drools-core-reflective:7.47.0.Final:jar
 org.drools:drools-decisiontables:7.47.0.Final:jar
 org.drools:drools-ecj:7.47.0.Final:jar
+org.drools:drools-model-compiler:7.47.0.Final:jar
 org.drools:drools-mvel:7.47.0.Final:jar
+org.drools:drools-mvel-compiler:7.47.0.Final:jar
+org.drools:drools-mvel-parser:7.47.0.Final:jar
 org.drools:drools-templates:7.47.0.Final:jar
+org.glassfish.jaxb:jaxb-runtime:2.3.2:jar
+org.glassfish.jaxb:txw2:2.3.2:jar
 org.javassist:javassist:3.26.0-GA:jar
 org.jsoup:jsoup:1.8.3:jar
+org.jvnet.staxex:stax-ex:1.8.1:jar
 org.kie:kie-api:7.47.0.Final:jar
 org.kie:kie-internal:7.47.0.Final:jar
 org.kie:kie-memory-compiler:7.47.0.Final:jar
+org.kie.kogito:drools-compiler:1.1.0.Final:jar
+org.kie.kogito:drools-core:1.1.0.Final:jar
+org.kie.kogito:drools-core-dynamic:1.1.0.Final:jar
+org.kie.kogito:drools-core-static:1.1.0.Final:jar
+org.kie.kogito:kogito-api:1.1.0.Final:jar
+org.kie.kogito:kogito-drools-model:1.1.0.Final:jar
+org.kie.kogito:kogito-internal:1.1.0.Final:jar
+org.kie.kogito:kogito-services:1.1.0.Final:jar
 org.kie.soup:kie-soup-commons:7.47.0.Final:jar
 org.kie.soup:kie-soup-maven-support:7.47.0.Final:jar
 org.kie.soup:kie-soup-project-datamodel-api:7.47.0.Final:jar
 org.kie.soup:kie-soup-project-datamodel-commons:7.47.0.Final:jar
 org.kie.soup:kie-soup-xstream:7.47.0.Final:jar
 org.mvel:mvel2:2.4.10.Final:jar
+org.optaplanner:optaplanner-core:8.1.0.Final:jar
+org.optaplanner:optaplanner-persistence-common:8.1.0.Final:jar
+org.optaplanner:optaplanner-persistence-jackson:8.1.0.Final:jar
+org.optaplanner:optaplanner-spring-boot-autoconfigure:8.1.0.Final:jar
+org.optaplanner:optaplanner-spring-boot-starter:8.1.0.Final:jar
 org.slf4j:slf4j-api:1.7.26:jar
+org.springframework:spring-aop:5.2.5.RELEASE:jar
+org.springframework:spring-beans:5.2.5.RELEASE:jar
+org.springframework:spring-context:5.2.5.RELEASE:jar
+org.springframework:spring-core:5.2.5.RELEASE:jar
+org.springframework:spring-expression:5.2.5.RELEASE:jar
+org.springframework:spring-jcl:5.2.5.RELEASE:jar
+org.springframework.boot:spring-boot:2.2.6.RELEASE:jar
+org.springframework.boot:spring-boot-autoconfigure:2.2.6.RELEASE:jar
 org.yaml:snakeyaml:1.26:jar
 xmlpull:xmlpull:1.2.0:jar
 xpp3:xpp3_min:1.2.0:jar
diff --git a/module/acme/pom.xml b/module/acme/pom.xml
index b3fc43c..ceb01b9 100644
--- a/module/acme/pom.xml
+++ b/module/acme/pom.xml
@@ -14,6 +14,10 @@
     <artifactId>acme</artifactId>
 
     <dependencies>
+        <dependency>
+            <groupId>org.optaplanner</groupId>
+            <artifactId>optaplanner-spring-boot-starter</artifactId>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>ri-engine</artifactId>
@@ -21,4 +25,14 @@
         </dependency>
     </dependencies>
 
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.optaplanner</groupId>
+                <artifactId>optaplanner-spring-boot-starter</artifactId>
+                <version>8.1.0.Final</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
 </project>
diff --git a/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudBalance.java b/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudBalance.java
new file mode 100644
index 0000000..7311918
--- /dev/null
+++ b/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudBalance.java
@@ -0,0 +1,48 @@
+package io.trygvis.acme.planning.machine;
+
+import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
+import org.optaplanner.core.api.domain.solution.PlanningScore;
+import org.optaplanner.core.api.domain.solution.PlanningSolution;
+import org.optaplanner.core.api.domain.solution.ProblemFactCollectionProperty;
+import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;
+import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
+
+import java.util.List;
+
+@PlanningSolution
+public class CloudBalance {
+
+    private List<CloudComputer> computerList;
+
+    private List<CloudProcess> processList;
+
+    private HardSoftScore score;
+
+    public CloudBalance(List<CloudComputer> computerList, List<CloudProcess> processList) {
+        this.computerList = computerList;
+        this.processList = processList;
+    }
+
+    public CloudBalance() {
+    }
+
+    @ValueRangeProvider(id = "computerRange")
+    @ProblemFactCollectionProperty
+    public List<CloudComputer> getComputerList() {
+        return computerList;
+    }
+
+    @PlanningEntityCollectionProperty
+    public List<CloudProcess> getProcessList() {
+        return processList;
+    }
+
+    @PlanningScore
+    public HardSoftScore getScore() {
+        return score;
+    }
+
+    public void setScore(HardSoftScore score) {
+        this.score = score;
+    }
+}
diff --git a/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudBalancingEasyScoreCalculator.java b/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudBalancingEasyScoreCalculator.java
new file mode 100644
index 0000000..6a14372
--- /dev/null
+++ b/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudBalancingEasyScoreCalculator.java
@@ -0,0 +1,45 @@
+package io.trygvis.acme.planning.machine;
+
+import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
+import org.optaplanner.core.api.score.calculator.EasyScoreCalculator;
+
+public class CloudBalancingEasyScoreCalculator implements EasyScoreCalculator<CloudBalance, HardSoftScore> {
+
+    @Override
+    public HardSoftScore calculateScore(CloudBalance cloudBalance) {
+        int hardScore = 0;
+        int softScore = 0;
+        for (CloudComputer computer : cloudBalance.getComputerList()) {
+            int cpu = 0;
+            int memory = 0;
+            boolean used = false;
+
+            // Calculate usage
+            for (CloudProcess process : cloudBalance.getProcessList()) {
+                if (computer.equals(process.computer)) {
+                    cpu += process.requiredCpu;
+                    memory += process.requiredMemory;
+                    used = true;
+                }
+            }
+
+            var instance = computer.instance;
+
+            // Hard constraints
+            int cpuPowerAvailable = instance.cpu - cpu;
+            if (cpuPowerAvailable < 0) {
+                hardScore += cpuPowerAvailable;
+            }
+            int memoryAvailable = instance.memory - memory;
+            if (memoryAvailable < 0) {
+                hardScore += memoryAvailable;
+            }
+
+            // Soft constraints
+            if (used) {
+                softScore -= instance.cost;
+            }
+        }
+        return HardSoftScore.of(hardScore, softScore);
+    }
+}
diff --git a/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudComputer.java b/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudComputer.java
new file mode 100644
index 0000000..aa2087d
--- /dev/null
+++ b/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudComputer.java
@@ -0,0 +1,16 @@
+package io.trygvis.acme.planning.machine;
+
+public class CloudComputer {
+    public final String host;
+    public final ScalewayInstance instance;
+
+    public CloudComputer(String host, ScalewayInstance instance) {
+        this.host = host;
+        this.instance = instance;
+    }
+
+    @Override
+    public String toString() {
+        return host;
+    }
+}
diff --git a/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudPlaningMain.java b/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudPlaningMain.java
new file mode 100644
index 0000000..60b3fd3
--- /dev/null
+++ b/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudPlaningMain.java
@@ -0,0 +1,52 @@
+package io.trygvis.acme.planning.machine;
+
+import org.optaplanner.core.api.solver.SolverFactory;
+
+import java.util.ArrayList;
+
+public class CloudPlaningMain {
+    public static final ScalewayInstance Stardust1_s = new ScalewayInstance("Stardust1-s", 0.0025, 1000, 1000);
+    public static final ScalewayInstance DEV1_S = new ScalewayInstance("DEV1-S", 0.01, 2000, 2000);
+    public static final ScalewayInstance DEV1_M = new ScalewayInstance("DEV1-M", 0.02, 3000, 4000);
+    public static final ScalewayInstance DEV1_L = new ScalewayInstance("DEV1-L", 0.04, 4000, 8000);
+    public static final ScalewayInstance DEV1_XL = new ScalewayInstance("DEV1-XL", 0.06, 4000, 12000);
+
+    public static void main(String[] args) {
+        var solverFactory = SolverFactory.<CloudBalance>createFromXmlResource("io/trygvis/acme/planning/machine/solver-config.xml");
+        var solver = solverFactory.buildSolver();
+
+        var computers = new ArrayList<CloudComputer>();
+
+        computers.add(new CloudComputer("acme-1", DEV1_S));
+        computers.add(new CloudComputer("acme-2", DEV1_M));
+        computers.add(new CloudComputer("acme-3", DEV1_S));
+
+        var processes = new ArrayList<CloudProcess>();
+
+        processes.add(new CloudProcess("statera", 200, 1000));
+        processes.add(new CloudProcess("statera-console", 100, 50));
+        processes.add(new CloudProcess("4tune-web", 100, 50));
+        processes.add(new CloudProcess("4tune-api", 200, 200));
+        processes.add(new CloudProcess("pdb", 500, 500));
+        processes.add(new CloudProcess("mdb", 500, 200));
+
+        var unsolvedCloudBalance = new CloudBalance(computers, processes);
+
+        var solvedCloudBalance = solver.solve(unsolvedCloudBalance);
+
+        System.out.println("solvedCloudBalance.getScore() = " + solvedCloudBalance.getScore());
+        for (CloudProcess process : solvedCloudBalance.getProcessList()) {
+            System.out.println("process.id = " + process.id);
+            if (process.computer == null) {
+                System.out.println("COMPUTER IS NULL");
+            } else {
+                System.out.println("process.computer.host = " + process.computer.host);
+            }
+        }
+
+        System.out.println("------------------------------------------------------------------------");
+
+//        System.out.println("\nSolved cloudBalance with 400 computers and 1200 processes:\n"
+//                + toDisplayString(solvedCloudBalance));
+    }
+}
diff --git a/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudPlanner.java b/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudPlanner.java
new file mode 100644
index 0000000..c629656
--- /dev/null
+++ b/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudPlanner.java
@@ -0,0 +1,22 @@
+package io.trygvis.acme.planning.machine;
+
+import org.optaplanner.core.api.domain.entity.PlanningEntity;
+import org.optaplanner.core.api.domain.variable.PlanningVariable;
+
+@PlanningEntity()
+public class CloudPlanner {
+    private int requiredCpuPower;
+    private int requiredMemory;
+    private int requiredNetworkBandwidth;
+
+    private CloudComputer computer;
+
+    @PlanningVariable(valueRangeProviderRefs = {"computerRange"})
+    public CloudComputer getComputer() {
+        return computer;
+    }
+
+    public void setComputer(CloudComputer computer) {
+        this.computer = computer;
+    }
+}
diff --git a/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudProcess.java b/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudProcess.java
new file mode 100644
index 0000000..2002903
--- /dev/null
+++ b/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudProcess.java
@@ -0,0 +1,48 @@
+package io.trygvis.acme.planning.machine;
+
+import org.apache.commons.lang3.builder.CompareToBuilder;
+import org.optaplanner.core.api.domain.entity.PlanningEntity;
+import org.optaplanner.core.api.domain.variable.PlanningVariable;
+
+import java.util.Comparator;
+
+@PlanningEntity(difficultyComparatorClass = CloudProcessDifficultyComparator.class)
+public class CloudProcess {
+    public String id;
+    public int requiredCpu;
+    public int requiredMemory;
+    public int requiredMultiplicand;
+
+    @PlanningVariable(
+            valueRangeProviderRefs = "computerRange",
+            strengthComparatorClass = CloudComputerStrengthComparator.class)
+    public CloudComputer computer;
+
+    public CloudProcess(String id, int requiredCpu, int requiredMemory) {
+        this.id = id;
+        this.requiredCpu = requiredCpu;
+        this.requiredMemory = requiredMemory;
+
+        this.requiredMultiplicand = requiredCpu * requiredMemory;
+    }
+
+    @SuppressWarnings("unused")
+    public CloudProcess() {
+    }
+
+    public String toString() {
+        return id;
+    }
+
+    public static class CloudComputerStrengthComparator implements Comparator<CloudComputer> {
+        public int compare(CloudComputer a, CloudComputer b) {
+            var x = a.instance;
+            var y = b.instance;
+            return new CompareToBuilder()
+                    .append(x.multiplicand, y.multiplicand)
+                    .append(y.cost, x.cost) // Descending (but this is debatable)
+                    .append(x.kind, y.kind)
+                    .toComparison();
+        }
+    }
+}
diff --git a/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudProcessDifficultyComparator.java b/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudProcessDifficultyComparator.java
new file mode 100644
index 0000000..658e826
--- /dev/null
+++ b/module/acme/src/main/java/io/trygvis/acme/planning/machine/CloudProcessDifficultyComparator.java
@@ -0,0 +1,15 @@
+package io.trygvis.acme.planning.machine;
+
+import org.apache.commons.lang3.builder.CompareToBuilder;
+
+import java.util.Comparator;
+
+public class CloudProcessDifficultyComparator implements Comparator<CloudProcess> {
+
+    public int compare(CloudProcess a, CloudProcess b) {
+        return new CompareToBuilder()
+                .append(a.requiredMultiplicand, b.requiredMultiplicand)
+                .append(a.id, b.id)
+                .toComparison();
+    }
+}
diff --git a/module/acme/src/main/java/io/trygvis/acme/planning/machine/ScalewayInstance.java b/module/acme/src/main/java/io/trygvis/acme/planning/machine/ScalewayInstance.java
new file mode 100644
index 0000000..dfdc0e0
--- /dev/null
+++ b/module/acme/src/main/java/io/trygvis/acme/planning/machine/ScalewayInstance.java
@@ -0,0 +1,25 @@
+package io.trygvis.acme.planning.machine;
+
+public class ScalewayInstance {
+    public final String kind;
+    // Euros / hour
+    public final double cost;
+    public final int cpu;
+    public final int memory;
+
+    public int multiplicand;
+
+    public ScalewayInstance(String kind, double cost, int cpu, int memory) {
+        this.kind = kind;
+        this.cost = cost;
+        this.cpu = cpu;
+        this.memory = memory;
+
+        this.multiplicand = cpu * memory;
+    }
+
+    @Override
+    public String toString() {
+        return kind;
+    }
+}
diff --git a/module/acme/src/main/resources/io/trygvis/acme/apps/apps.drl b/module/acme/src/main/resources/io/trygvis/acme/apps/apps.drl
index e7bdfe3..670079f 100644
--- a/module/acme/src/main/resources/io/trygvis/acme/apps/apps.drl
+++ b/module/acme/src/main/resources/io/trygvis/acme/apps/apps.drl
@@ -1,9 +1,13 @@
-package io.trygvis.acme.apps;
+package io.trygvis.acme.apps
 
 import io.trygvis.rules.machine.Machine;
 import io.trygvis.rules.dba.Cluster;
 import io.trygvis.rules.dba.Container
 import io.trygvis.rules.dns.DnsZone;
+import io.trygvis.rules.machine.Machine
+import io.trygvis.rules.machine.MachineSpecification
+import io.trygvis.rules.dba.Cluster
+import io.trygvis.rules.dba.Container
 
 dialect "mvel"
 
@@ -13,8 +17,8 @@ when
 then
     var cluster = new Cluster("acme-ops");
     insert(cluster);
-    insert(new Container(cluster, "pdb", "ops", "postgresql", "11"));
-    insert(new Container(cluster, "n8n", "ops", "n8n", "0.84.1"));
+    insert(new Container(cluster, "pdb", "ops", "postgresql", "11", null));
+    insert(new Container(cluster, "n8n", "ops", "n8n", "0.84.1", null));
 end
 
 rule "MyApp"
@@ -31,10 +35,10 @@ then
     var db = $app.environment + "-db";
 
     var tag = $app.dockerTag;
-    insert(new Container(cluster, "statera", app, "statera", tag));
-    insert(new Container(cluster, "statera-console", app, "statera-console", tag));
-    insert(new Container(cluster, "4tune-web", app, "4tune-web", tag));
-    insert(new Container(cluster, "4tune-api", app, "4tune-api", tag));
-    insert(new Container(cluster, "pdb", db, "postgresql", "13"));
-    insert(new Container(cluster, "mdb", db, "mongodb", "3.2"));
+    insert(new Container(cluster, "statera", app, "statera", tag, new MachineSpecification(200, 1000)));
+    insert(new Container(cluster, "statera-console", app, "statera-console", tag, new MachineSpecification(100, 50)));
+    insert(new Container(cluster, "4tune-web", app, "4tune-web", tag, new MachineSpecification(100, 50)));
+    insert(new Container(cluster, "4tune-api", app, "4tune-api", tag, new MachineSpecification(200, 200)));
+    insert(new Container(cluster, "pdb", db, "postgresql", "13", new MachineSpecification(500, 500)));
+    insert(new Container(cluster, "mdb", db, "mongodb", "3.2", new MachineSpecification(500, 200)));
 end
diff --git a/module/acme/src/main/resources/io/trygvis/acme/planning/machine/solver-config.xml b/module/acme/src/main/resources/io/trygvis/acme/planning/machine/solver-config.xml
new file mode 100644
index 0000000..81ae8ed
--- /dev/null
+++ b/module/acme/src/main/resources/io/trygvis/acme/planning/machine/solver-config.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<solver xmlns="https://www.optaplanner.org/xsd/solver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="https://www.optaplanner.org/xsd/solver https://www.optaplanner.org/xsd/solver/solver.xsd">
+  <!-- Domain model configuration -->
+  <solutionClass>io.trygvis.acme.planning.machine.CloudBalance</solutionClass>
+  <entityClass>io.trygvis.acme.planning.machine.CloudProcess</entityClass>
+
+  <!-- Score configuration -->
+  <scoreDirectorFactory>
+    <easyScoreCalculatorClass>io.trygvis.acme.planning.machine.CloudBalancingEasyScoreCalculator</easyScoreCalculatorClass>
+    <!--<scoreDrl>org/optaplanner/examples/cloudbalancing/solver/cloudBalancingConstraints.drl</scoreDrl>-->
+  </scoreDirectorFactory>
+
+  <!-- Optimization algorithms configuration -->
+  <termination>
+    <secondsSpentLimit>3</secondsSpentLimit>
+  </termination>
+</solver>
diff --git a/module/ri-engine/pom.xml b/module/ri-engine/pom.xml
index fd9ba7c..80ef901 100644
--- a/module/ri-engine/pom.xml
+++ b/module/ri-engine/pom.xml
@@ -41,10 +41,12 @@
             <groupId>org.drools</groupId>
             <artifactId>drools-templates</artifactId>
         </dependency>
+        <!--
         <dependency>
             <groupId>org.kie</groupId>
             <artifactId>kie-internal</artifactId>
         </dependency>
+        -->
 
         <dependency>
             <groupId>org.mvel</groupId>
diff --git a/module/ri-engine/src/main/java/io/trygvis/rules/dba/Container.java b/module/ri-engine/src/main/java/io/trygvis/rules/dba/Container.java
index f6d2ba4..3888661 100644
--- a/module/ri-engine/src/main/java/io/trygvis/rules/dba/Container.java
+++ b/module/ri-engine/src/main/java/io/trygvis/rules/dba/Container.java
@@ -2,6 +2,7 @@ package io.trygvis.rules.dba;
 
 import com.fasterxml.jackson.annotation.JsonIdentityReference;
 import io.trygvis.rules.machine.Machine;
+import io.trygvis.rules.machine.MachineSpecification;
 
 //@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
 public class Container {
@@ -15,14 +16,17 @@ public class Container {
     public final String tag;
 
     private Machine machine;
+    public final MachineSpecification machineSpecification;
 
-    public Container(Cluster cluster, String name, String machineRole, String image, String tag) {
+    public Container(Cluster cluster, String name, String machineRole, String image, String tag,
+                     MachineSpecification machineSpecification) {
         this.id = cluster.name + "-" + name;
         this.cluster = cluster;
         this.name = name;
         this.machineRole = machineRole;
         this.image = image;
         this.tag = tag;
+        this.machineSpecification = machineSpecification;
     }
 
     public Cluster getCluster() {
diff --git a/module/ri-engine/src/main/java/io/trygvis/rules/machine/MachineSpecification.java b/module/ri-engine/src/main/java/io/trygvis/rules/machine/MachineSpecification.java
new file mode 100644
index 0000000..2e17ae5
--- /dev/null
+++ b/module/ri-engine/src/main/java/io/trygvis/rules/machine/MachineSpecification.java
@@ -0,0 +1,19 @@
+package io.trygvis.rules.machine;
+
+public class MachineSpecification {
+    public final int cpu;
+    public final int memory;
+
+    public MachineSpecification(int cpu, int memory) {
+        this.cpu = cpu;
+        this.memory = memory;
+    }
+
+    public int getCpu() {
+        return cpu;
+    }
+
+    public int getMemory() {
+        return memory;
+    }
+}
-- 
cgit v1.2.3