From bafe762ac01d16904c18404283027e426e19bc73 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Wed, 3 Feb 2021 16:35:48 +0100 Subject: Code reorganization. Moving main code to src, keeping modules in modules/ --- src/ri-planner/classpath.txt | 90 ++++++++++++++++++++++ src/ri-planner/pom.xml | 38 +++++++++ .../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 ++++++ .../acme/planning/machine/solver-config.xml | 18 +++++ 11 files changed, 417 insertions(+) create mode 100644 src/ri-planner/classpath.txt create mode 100644 src/ri-planner/pom.xml create mode 100644 src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudBalance.java create mode 100644 src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudBalancingEasyScoreCalculator.java create mode 100644 src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudComputer.java create mode 100644 src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudPlaningMain.java create mode 100644 src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudPlanner.java create mode 100644 src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudProcess.java create mode 100644 src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/CloudProcessDifficultyComparator.java create mode 100644 src/ri-planner/src/main/java/io/trygvis/acme/planning/machine/ScalewayInstance.java create mode 100644 src/ri-planner/src/main/resources/io/trygvis/acme/planning/machine/solver-config.xml (limited to 'src/ri-planner') 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 @@ + + + 4.0.0 + + + io.trygvis.rules-sandbox + rules-sandbox + 1.0-SNAPSHOT + ../../pom.xml + + + acme-planner + + + + org.optaplanner + optaplanner-spring-boot-starter + + + ${project.groupId} + ri-engine + ${project.version} + + + + + + + org.optaplanner + optaplanner-spring-boot-starter + 8.1.0.Final + + + + + 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 computerList; + + private List processList; + + private HardSoftScore score; + + public CloudBalance(List computerList, List processList) { + this.computerList = computerList; + this.processList = processList; + } + + public CloudBalance() { + } + + @ValueRangeProvider(id = "computerRange") + @ProblemFactCollectionProperty + public List getComputerList() { + return computerList; + } + + @PlanningEntityCollectionProperty + public List 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 { + + @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.createFromXmlResource("io/trygvis/acme/planning/machine/solver-config.xml"); + var solver = solverFactory.buildSolver(); + + var computers = new ArrayList(); + + 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(); + + 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 { + 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 { + + 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 @@ + + + + io.trygvis.acme.planning.machine.CloudBalance + io.trygvis.acme.planning.machine.CloudProcess + + + + io.trygvis.acme.planning.machine.CloudBalancingEasyScoreCalculator + + + + + + 3 + + -- cgit v1.2.3