summaryrefslogtreecommitdiff
path: root/src/ri-planner
diff options
context:
space:
mode:
Diffstat (limited to 'src/ri-planner')
-rw-r--r--src/ri-planner/classpath.txt90
-rw-r--r--src/ri-planner/pom.xml38
-rw-r--r--src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudBalance.java48
-rw-r--r--src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudBalancingEasyScoreCalculator.java45
-rw-r--r--src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudComputer.java16
-rw-r--r--src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudPlaningMain.java52
-rw-r--r--src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudPlanner.java22
-rw-r--r--src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudProcess.java48
-rw-r--r--src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudProcessDifficultyComparator.java15
-rw-r--r--src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/ScalewayInstance.java25
-rw-r--r--src/ri-planner/src/main/resources/io/trygvis/acme/planning/machine/solver-config.xml18
11 files changed, 417 insertions, 0 deletions
diff --git a/src/ri-planner/classpath.txt b/src/ri-planner/classpath.txt
new file mode 100644
index 0000000..411ee43
--- /dev/null
+++ b/src/ri-planner/classpath.txt
@@ -0,0 +1,90 @@
+io.trygvis.rules-sandbox:acme-planner:1.0-SNAPSHOT:jar
+ch.obermuhlner:big-math:2.0.0:jar
+ch.qos.logback:logback-classic:1.2.3:jar
+ch.qos.logback:logback-core:1.2.3:jar
+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
+com.google.guava:guava:25.0-jre:jar
+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.activation:jakarta.activation:1.2.2:jar
+com.sun.istack:istack-commons-runtime:3.0.8:jar
+com.thoughtworks.xstream:xstream:1.4.14:jar
+com.zaxxer:SparseBitSet:1.2:jar
+commons-codec:commons-codec:1.14:jar
+commons-io:commons-io:2.8.0:jar
+commons-net:commons-net:3.3:jar
+info.picocli:picocli:4.6.1:jar
+io.trygvis.rules-sandbox:ri-engine:1.0-SNAPSHOT:jar
+io.trygvis.rules-sandbox.module:ri-module-api:1.0-SNAPSHOT:jar
+jakarta.activation:jakarta.activation-api:1.2.2:jar
+jakarta.xml.bind:jakarta.xml.bind-api:2.3.3: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
+org.apache.commons:commons-lang3:3.10:jar
+org.apache.commons:commons-math3:3.4.1:jar
+org.apache.poi:poi:4.1.2:jar
+org.apache.poi:poi-ooxml:4.1.2:jar
+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.48.0.Final:jar
+org.drools:drools-compiler:7.48.0.Final:jar
+org.drools:drools-core:7.48.0.Final:jar
+org.drools:drools-core-dynamic:7.48.0.Final:jar
+org.drools:drools-core-reflective:7.48.0.Final:jar
+org.drools:drools-decisiontables:7.48.0.Final:jar
+org.drools:drools-ecj:7.48.0.Final:jar
+org.drools:drools-model-compiler:7.48.0.Final:jar
+org.drools:drools-mvel:7.48.0.Final:jar
+org.drools:drools-mvel-compiler:7.48.0.Final:jar
+org.drools:drools-mvel-parser:7.48.0.Final:jar
+org.drools:drools-templates:7.48.0.Final:jar
+org.glassfish.jaxb:jaxb-runtime:2.3.3:jar
+org.glassfish.jaxb:txw2:2.3.3:jar
+org.javassist:javassist:3.24.1-GA:jar
+org.jsoup:jsoup:1.10.3:jar
+org.kie:kie-api:7.48.0.Final:jar
+org.kie:kie-internal:7.48.0.Final:jar
+org.kie:kie-memory-compiler:7.48.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.48.0.Final:jar
+org.kie.soup:kie-soup-maven-support:7.48.0.Final:jar
+org.kie.soup:kie-soup-project-datamodel-api:7.48.0.Final:jar
+org.kie.soup:kie-soup-project-datamodel-commons:7.48.0.Final:jar
+org.kie.soup:kie-soup-xstream:7.48.0.Final:jar
+org.mvel:mvel2:2.4.11.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.30:jar
+org.springframework:spring-aop:5.2.7.RELEASE:jar
+org.springframework:spring-beans:5.2.7.RELEASE:jar
+org.springframework:spring-context:5.2.7.RELEASE:jar
+org.springframework:spring-core:5.2.7.RELEASE:jar
+org.springframework:spring-expression:5.2.7.RELEASE:jar
+org.springframework:spring-jcl:5.2.7.RELEASE:jar
+org.springframework.boot:spring-boot:2.3.1.RELEASE:jar
+org.springframework.boot:spring-boot-autoconfigure:2.3.1.RELEASE:jar
+org.yaml:snakeyaml:1.26:jar
+xmlpull:xmlpull:1.1.3.1:jar
+xpp3:xpp3_min:1.1.4c:jar
diff --git a/src/ri-planner/pom.xml b/src/ri-planner/pom.xml
new file mode 100644
index 0000000..ed8699d
--- /dev/null
+++ b/src/ri-planner/pom.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>io.trygvis.rules-sandbox</groupId>
+ <artifactId>rules-sandbox</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>acme-planner</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.optaplanner</groupId>
+ <artifactId>optaplanner-spring-boot-starter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ri-engine</artifactId>
+ <version>${project.version}</version>
+ </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/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudBalance.java b/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudBalance.java
new file mode 100644
index 0000000..7311918
--- /dev/null
+++ b/src/ri-planner/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/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudBalancingEasyScoreCalculator.java b/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudBalancingEasyScoreCalculator.java
new file mode 100644
index 0000000..6a14372
--- /dev/null
+++ b/src/ri-planner/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/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudComputer.java b/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudComputer.java
new file mode 100644
index 0000000..aa2087d
--- /dev/null
+++ b/src/ri-planner/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/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudPlaningMain.java b/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudPlaningMain.java
new file mode 100644
index 0000000..60b3fd3
--- /dev/null
+++ b/src/ri-planner/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/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudPlanner.java b/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudPlanner.java
new file mode 100644
index 0000000..c629656
--- /dev/null
+++ b/src/ri-planner/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/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudProcess.java b/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudProcess.java
new file mode 100644
index 0000000..2002903
--- /dev/null
+++ b/src/ri-planner/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/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudProcessDifficultyComparator.java b/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudProcessDifficultyComparator.java
new file mode 100644
index 0000000..658e826
--- /dev/null
+++ b/src/ri-planner/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/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/ScalewayInstance.java b/src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/ScalewayInstance.java
new file mode 100644
index 0000000..dfdc0e0
--- /dev/null
+++ b/src/ri-planner/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/src/ri-planner/src/main/resources/io/trygvis/acme/planning/machine/solver-config.xml b/src/ri-planner/src/main/resources/io/trygvis/acme/planning/machine/solver-config.xml
new file mode 100644
index 0000000..81ae8ed
--- /dev/null
+++ b/src/ri-planner/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>