summaryrefslogtreecommitdiffstats
path: root/node-maintainer/src/main/java/com
diff options
context:
space:
mode:
authorolaaun <ola.aunroe@gmail.com>2017-07-25 20:37:02 +0800
committerGitHub <noreply@github.com>2017-07-25 20:37:02 +0800
commita44477bcd943021fb80dda761e485e81b7c929ac (patch)
treeb1a7a40b4cbb31612c3f65261860a84b4a0cc61d /node-maintainer/src/main/java/com
parent674749e424fe50980479e462b4c1a3fb8b77147a (diff)
Node verification (#2999)
* Initial node verification commit
Diffstat (limited to 'node-maintainer/src/main/java/com')
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/CommandExecutor.java44
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/OutputParser.java83
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/ParseInstructions.java56
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/ParseResult.java48
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/HardwareVerifier.java47
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/README.md2
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/Benchmark.java10
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/CPUBenchmark.java113
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/DiskBenchmark.java96
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/HardwareResults.java56
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/MemoryBenchmark.java99
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/NetBenchmark.java60
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/net/Client.java46
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/net/Server.java41
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/yamasreport/HardwareReportDimensions.java13
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/yamasreport/HardwareReportMetrics.java41
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/yamasreport/YamasHardwareReport.java80
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/HardwareNodeComparator.java128
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/HostURLGenerator.java23
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/README.md2
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/SpecVerifier.java71
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/IPAddressVerifier.java101
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/NodeGenerator.java23
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/NodeInfoRetriever.java31
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/NodeJsonModel.java51
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/CPURetriever.java58
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/DiskRetriever.java96
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/HardwareInfo.java75
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/HardwareInfoRetriever.java28
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/HardwareRetriever.java10
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/MemoryRetriever.java63
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/NetRetriever.java112
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/yamasreport/SpecReportDimensions.java82
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/yamasreport/SpecReportMetrics.java89
-rw-r--r--node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/yamasreport/YamasSpecReport.java77
35 files changed, 2055 insertions, 0 deletions
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/CommandExecutor.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/CommandExecutor.java
new file mode 100644
index 00000000000..c03e90d298a
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/CommandExecutor.java
@@ -0,0 +1,44 @@
+package com.yahoo.vespa.hosted.node.verification.commons;
+
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.exec.DefaultExecutor;
+import org.apache.commons.exec.PumpStreamHandler;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+
+/**
+ * Created by olaa on 03/07/2017.
+ * Wrapper for executing terminal commands
+ */
+public class CommandExecutor {
+
+ public ArrayList<String> executeCommand(String command) throws IOException {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ ArrayList<String> results = new ArrayList<>();
+ writeToOutputStream(outputStream, command);
+ writeOutputStreamToResults(outputStream, results);
+ return results;
+ }
+
+ private void writeToOutputStream(ByteArrayOutputStream outputStream, String command) throws IOException {
+ CommandLine cmdLine = CommandLine.parse(command);
+ DefaultExecutor executor = new DefaultExecutor();
+ PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream);
+ executor.setStreamHandler(streamHandler);
+ executor.execute(cmdLine);
+ }
+
+ private void writeOutputStreamToResults(ByteArrayOutputStream outputStream, ArrayList<String> results) throws IOException {
+ String out = outputStream.toString();
+ BufferedReader br = new BufferedReader(new StringReader(out));
+ String line;
+ while ((line = br.readLine()) != null) {
+ results.add(line);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/OutputParser.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/OutputParser.java
new file mode 100644
index 00000000000..09060bdebdf
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/OutputParser.java
@@ -0,0 +1,83 @@
+package com.yahoo.vespa.hosted.node.verification.commons;
+
+import java.util.ArrayList;
+import java.util.regex.Pattern;
+
+/**
+ * Created by sgrostad on 17/07/2017.
+ * Parses terminal command output, and returns results based on ParseInstructions
+ */
+public class OutputParser {
+
+ public static ArrayList<ParseResult> parseOutput(ParseInstructions parseInstructions, ArrayList<String> commandOutput) {
+ ArrayList<ParseResult> results = new ArrayList<>();
+ int searchElementIndex = parseInstructions.getSearchElementIndex();
+ int valueElementIndex = parseInstructions.getValueElementIndex();
+ ArrayList<String> searchWords = parseInstructions.getSearchWords();
+ for (String line : commandOutput) {
+ String[] lineSplit = line.trim().split(parseInstructions.getSplitRegex());
+ if (lineSplit.length <= Math.max(searchElementIndex, valueElementIndex)) {
+ continue;
+ }
+ String searchWordCandidate = lineSplit[searchElementIndex].trim();
+ boolean searchWordCandidateMatch = matchingSearchWord(searchWords, searchWordCandidate);
+ if (searchWordCandidateMatch) {
+ String value = lineSplit[valueElementIndex];
+ results.add(new ParseResult(searchWordCandidate, value.trim()));
+ }
+ }
+ return results;
+ }
+
+ public static ParseResult parseSingleOutput(ParseInstructions parseInstructions, ArrayList<String> commandOutput) {
+ ArrayList<ParseResult> parseResults = parseOutput(parseInstructions, commandOutput);
+ if (parseResults.size() == 0) {
+ return new ParseResult("invalid", "invalid");
+ }
+ return parseResults.get(0);
+ }
+
+ public static ArrayList<ParseResult> parseOutPutWithSkips(ParseInstructions parseInstructions, ArrayList<String> commandOutput) {
+ ArrayList<ParseResult> results = new ArrayList<>();
+ int searchElementIndex = parseInstructions.getSearchElementIndex();
+ int valueElementIndex = parseInstructions.getValueElementIndex();
+ String skipWord = parseInstructions.getSkipWord();
+ ArrayList<String> searchWords = parseInstructions.getSearchWords();
+ for (int i = 0; i < commandOutput.size(); i++) {
+ String line = commandOutput.get(i);
+ String[] lineSplit = line.trim().split(parseInstructions.getSplitRegex());
+ if (lineSplit.length <= Math.max(searchElementIndex, valueElementIndex)) {
+ continue;
+ }
+ if (lineSplit[searchElementIndex].equals(skipWord)) {
+ i = skipToIndex(i, parseInstructions, commandOutput);
+ continue;
+ }
+ String searchWordCandidate = lineSplit[searchElementIndex];
+ boolean searchWordCandidateMatch = matchingSearchWord(searchWords, searchWordCandidate);
+ if (searchWordCandidateMatch) {
+ String value = lineSplit[valueElementIndex];
+ results.add(new ParseResult(searchWordCandidate, value.trim()));
+ }
+ }
+ return results;
+ }
+
+ protected static int skipToIndex(int index, ParseInstructions parseInstructions, ArrayList<String> commandOutput) {
+ String skipUntilKeyword = parseInstructions.getSkipUntilKeyword();
+ int returnIndex = commandOutput.size();
+ for (int i = index; i < commandOutput.size(); i++) {
+ String line = commandOutput.get(i);
+ if (line.equals(skipUntilKeyword)) {
+ returnIndex = i;
+ break;
+ }
+ }
+ return returnIndex - 1;
+ }
+
+ private static boolean matchingSearchWord(ArrayList<String> searchWords, String searchWordCandidate) {
+ return searchWords.stream().anyMatch(w -> Pattern.compile(w).matcher(searchWordCandidate).matches());
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/ParseInstructions.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/ParseInstructions.java
new file mode 100644
index 00000000000..1e8531512f2
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/ParseInstructions.java
@@ -0,0 +1,56 @@
+package com.yahoo.vespa.hosted.node.verification.commons;
+
+import java.util.ArrayList;
+
+/**
+ * Created by sgrostad on 17/07/2017.
+ */
+public class ParseInstructions {
+
+ private final int searchElementIndex;
+ private final int valueElementIndex;
+ private final String splitRegex;
+ private final ArrayList<String> searchWords;
+ private String skipWord;
+ private String skipUntilKeyword;
+
+ public ParseInstructions(int searchElementIndex, int returnElementNum, String splitRegex, ArrayList<String> searchWords) {
+ this.searchElementIndex = searchElementIndex;
+ this.valueElementIndex = returnElementNum;
+ this.splitRegex = splitRegex;
+ this.searchWords = searchWords;
+ }
+
+ public int getSearchElementIndex() {
+ return searchElementIndex;
+ }
+
+ public int getValueElementIndex() {
+ return valueElementIndex;
+ }
+
+ public String getSplitRegex() {
+ return splitRegex;
+ }
+
+ public ArrayList<String> getSearchWords() {
+ return searchWords;
+ }
+
+ public void setSkipWord(String skipWord) {
+ this.skipWord = skipWord;
+ }
+
+ public void setSkipUntilKeyword(String skipUntilWord) {
+ this.skipUntilKeyword = skipUntilWord;
+ }
+
+ public String getSkipWord() {
+ return this.skipWord;
+ }
+
+ public String getSkipUntilKeyword() {
+ return this.skipUntilKeyword;
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/ParseResult.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/ParseResult.java
new file mode 100644
index 00000000000..57b42aa5dc9
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/ParseResult.java
@@ -0,0 +1,48 @@
+package com.yahoo.vespa.hosted.node.verification.commons;
+
+import java.util.Objects;
+
+/**
+ * Created by sgrostad on 17/07/2017.
+ */
+public class ParseResult {
+
+ private final String searchWord;
+ private final String value;
+
+ public ParseResult(String searchWord, String value) {
+ this.searchWord = searchWord;
+ this.value = value;
+ }
+
+ public String getSearchWord() {
+ return searchWord;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj instanceof ParseResult) {
+ ParseResult parseResult = (ParseResult) obj;
+ if (this.searchWord.equals(parseResult.getSearchWord()) && this.value.equals(parseResult.getValue())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(searchWord, value);
+ }
+
+ @Override
+ public String toString() {
+ return "Search word: " + searchWord + ", Value: " + value;
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/HardwareVerifier.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/HardwareVerifier.java
new file mode 100644
index 00000000000..61d26b322f4
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/HardwareVerifier.java
@@ -0,0 +1,47 @@
+package com.yahoo.vespa.hosted.node.verification.hardware;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yahoo.vespa.hosted.node.verification.commons.CommandExecutor;
+import com.yahoo.vespa.hosted.node.verification.hardware.benchmarks.Benchmark;
+import com.yahoo.vespa.hosted.node.verification.hardware.benchmarks.CPUBenchmark;
+import com.yahoo.vespa.hosted.node.verification.hardware.benchmarks.DiskBenchmark;
+import com.yahoo.vespa.hosted.node.verification.hardware.benchmarks.HardwareResults;
+import com.yahoo.vespa.hosted.node.verification.hardware.benchmarks.MemoryBenchmark;
+import com.yahoo.vespa.hosted.node.verification.hardware.benchmarks.NetBenchmark;
+import com.yahoo.vespa.hosted.node.verification.hardware.yamasreport.YamasHardwareReport;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Benchmarks different hardware components and creates report
+ */
+public class HardwareVerifier {
+
+ public static void verifyHardware() {
+ HardwareResults hardwareResults = new HardwareResults();
+ CommandExecutor commandExecutor = new CommandExecutor();
+ ArrayList<Benchmark> benchmarks = new ArrayList<>(Arrays.asList(
+ new DiskBenchmark(hardwareResults, commandExecutor),
+ new CPUBenchmark(hardwareResults, commandExecutor),
+ new MemoryBenchmark(hardwareResults, commandExecutor),
+ new NetBenchmark(hardwareResults, commandExecutor)));
+
+ for (Benchmark benchmark : benchmarks) {
+ benchmark.doBenchmark();
+ }
+ YamasHardwareReport yamasHardwareReport = new YamasHardwareReport();
+ yamasHardwareReport.createFromHardwareResults(hardwareResults);
+ ObjectMapper om = new ObjectMapper();
+ try {
+ System.out.println(om.writeValueAsString(yamasHardwareReport));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void main(String[] args) {
+ HardwareVerifier.verifyHardware();
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/README.md b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/README.md
new file mode 100644
index 00000000000..2797a6b5a9c
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/README.md
@@ -0,0 +1,2 @@
+# Hardware Verification
+Verification of behaviour and performance of hardware \ No newline at end of file
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/Benchmark.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/Benchmark.java
new file mode 100644
index 00000000000..a76555d79d3
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/Benchmark.java
@@ -0,0 +1,10 @@
+package com.yahoo.vespa.hosted.node.verification.hardware.benchmarks;
+
+/**
+ * Created by sgrostad on 11/07/2017.
+ */
+public interface Benchmark {
+
+ void doBenchmark();
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/CPUBenchmark.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/CPUBenchmark.java
new file mode 100644
index 00000000000..836b8213ffb
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/CPUBenchmark.java
@@ -0,0 +1,113 @@
+package com.yahoo.vespa.hosted.node.verification.hardware.benchmarks;
+
+import com.yahoo.vespa.hosted.node.verification.commons.CommandExecutor;
+import com.yahoo.vespa.hosted.node.verification.commons.OutputParser;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseInstructions;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseResult;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Created by sgrostad on 11/07/2017.
+ */
+public class CPUBenchmark implements Benchmark {
+
+ private static final String CPU_BENCHMARK_COMMAND = "perf stat -e cycles dd if=/dev/zero of=/dev/null count=100000 2>&1 | grep 'cycles\\|seconds'";
+ private static final String CYCLES_SEARCH_WORD = "cycles";
+ private static final String SECONDS_SEARCH_WORD = "seconds";
+ private static final String SPLIT_REGEX_STRING = "\\s+";
+ private static final int SEARCH_ELEMENT_INDEX = 1;
+ private static final int RETURN_ELEMENT_INDEX = 0;
+ private static final Logger logger = Logger.getLogger(CPUBenchmark.class.getName());
+ private final HardwareResults hardwareResults;
+
+ private final CommandExecutor commandExecutor;
+
+ public CPUBenchmark(HardwareResults hardwareResults, CommandExecutor commandExecutor) {
+ this.hardwareResults = hardwareResults;
+ this.commandExecutor = commandExecutor;
+ }
+
+ public void doBenchmark() {
+ try {
+ ArrayList<String> commandOutput = commandExecutor.executeCommand(CPU_BENCHMARK_COMMAND);
+ ArrayList<ParseResult> parseResults = parseCpuCyclesPerSec(commandOutput);
+ setCpuCyclesPerSec(parseResults);
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Failed to perform CPU benchmark", e);
+ }
+ }
+
+ protected ArrayList<ParseResult> parseCpuCyclesPerSec(ArrayList<String> commandOutput) {
+ ArrayList<String> searchWords = new ArrayList<>(Arrays.asList(CYCLES_SEARCH_WORD, SECONDS_SEARCH_WORD));
+ ParseInstructions parseInstructions = new ParseInstructions(SEARCH_ELEMENT_INDEX, RETURN_ELEMENT_INDEX, SPLIT_REGEX_STRING, searchWords);
+ return OutputParser.parseOutput(parseInstructions, commandOutput);
+ }
+
+
+ protected void setCpuCyclesPerSec(ArrayList<ParseResult> parseResults) {
+ double cpuCyclesPerSec = getCyclesPerSecond(parseResults);
+ if (cpuCyclesPerSec > 0) {
+ hardwareResults.setCpuCyclesPerSec(cpuCyclesPerSec);
+ }
+ }
+
+ protected double getCyclesPerSecond(ArrayList<ParseResult> parseResults) {
+ double cycles = -1;
+ double seconds = -1;
+ for (ParseResult parseResult : parseResults) {
+ switch (parseResult.getSearchWord()) {
+ case CYCLES_SEARCH_WORD:
+ cycles = makeCyclesDouble(parseResult.getValue());
+ break;
+ case SECONDS_SEARCH_WORD:
+ seconds = makeSecondsDouble(parseResult.getValue());
+ break;
+ default:
+ throw new RuntimeException("Invalid ParseResult searchWord");
+ }
+ }
+ if (cycles > 0 && seconds > 0) {
+ return convertToGHz(cycles, seconds);
+ }
+ return -1;
+ }
+
+ protected double makeCyclesDouble(String cycles) {
+ cycles = cycles.replaceAll("[^\\d]", "");
+ if (checkIfNumber(cycles)) {
+ return Double.parseDouble(cycles);
+ }
+ return -1;
+ }
+
+ protected double makeSecondsDouble(String seconds) {
+ seconds = seconds.replaceAll(",", ".");
+ if (checkIfNumber(seconds)) {
+ return Double.parseDouble(seconds);
+ }
+ return -1;
+ }
+
+ protected boolean checkIfNumber(String numberCandidate) {
+ if (numberCandidate == null || numberCandidate.equals("")) {
+ return false;
+ }
+ try {
+ Double.parseDouble(numberCandidate);
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ return true;
+ }
+
+ protected double convertToGHz(double cycles, double seconds) {
+ double giga = 1000000000.0;
+ return (cycles / seconds) / giga;
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/DiskBenchmark.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/DiskBenchmark.java
new file mode 100644
index 00000000000..8b5502cd415
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/DiskBenchmark.java
@@ -0,0 +1,96 @@
+package com.yahoo.vespa.hosted.node.verification.hardware.benchmarks;
+
+import com.yahoo.vespa.hosted.node.verification.commons.CommandExecutor;
+import com.yahoo.vespa.hosted.node.verification.commons.OutputParser;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseInstructions;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseResult;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Created by olaa on 10/07/2017.
+ */
+public class DiskBenchmark implements Benchmark {
+
+ private static final String DISK_BENCHMARK_COMMAND = "time (dd if=/dev/zero of=/tmp/tempfile bs=16k count=16k > /dev/null; sync; rm /tmp/tempfile) 2>&1 | grep bytes | awk '{ print $8 \" \" $9 }'";
+ private static final String KILO_BYTE_SEARCH_WORD = "kB/s";
+ private static final String MEGA_BYTE_SEARCH_WORD = "MB/s";
+ private static final String GIGA_BYTE_SEARCH_WORD = "GB/s";
+ private static final String SPLIT_REGEX_STRING = " ";
+ private static final int SEARCH_ELEMENT_INDEX = 1;
+ private static final int RETURN_ELEMENT_INDEX = 0;
+ private static final Logger logger = Logger.getLogger(DiskBenchmark.class.getName());
+ private final HardwareResults hardwareResults;
+ private final CommandExecutor commandExecutor;
+
+ public DiskBenchmark(HardwareResults hardwareResults, CommandExecutor commandExecutor) {
+ this.hardwareResults = hardwareResults;
+ this.commandExecutor = commandExecutor;
+ }
+
+ public void doBenchmark() {
+ try {
+ ArrayList<String> commandOutput = commandExecutor.executeCommand(DISK_BENCHMARK_COMMAND);
+ ParseResult parseResult = parseDiskSpeed(commandOutput);
+ setDiskSpeed(parseResult);
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Failed to perform disk benchmark", e);
+ }
+ }
+
+ protected ParseResult parseDiskSpeed(ArrayList<String> commandOutput) {
+ ArrayList<String> searchWords = new ArrayList<>(Arrays.asList(KILO_BYTE_SEARCH_WORD, MEGA_BYTE_SEARCH_WORD, GIGA_BYTE_SEARCH_WORD));
+ ParseInstructions parseInstructions = new ParseInstructions(SEARCH_ELEMENT_INDEX, RETURN_ELEMENT_INDEX, SPLIT_REGEX_STRING, searchWords);
+ return OutputParser.parseSingleOutput(parseInstructions, commandOutput);
+ }
+
+ protected void setDiskSpeed(ParseResult parseResult) {
+ double diskSpeedMBs = getDiskSpeedInMBs(parseResult);
+ hardwareResults.setDiskSpeedMbs(diskSpeedMBs);
+ }
+
+ protected double getDiskSpeedInMBs(ParseResult parseResult) {
+ double diskSpeedMBs = 0;
+ double convertKBsToMBs = 1 / 1000.0;
+ double convertGBsToMBs = 1000.0;
+ double convertMbsToMBs = 1.0;
+ String diskSpeed = parseResult.getValue();
+ if (checkSpeedValidity(diskSpeed)) {
+ switch (parseResult.getSearchWord()) {
+ case KILO_BYTE_SEARCH_WORD:
+ diskSpeedMBs = convertToMBs(diskSpeed, convertKBsToMBs);
+ break;
+ case MEGA_BYTE_SEARCH_WORD:
+ diskSpeedMBs = convertToMBs(diskSpeed, convertMbsToMBs);
+ break;
+ case GIGA_BYTE_SEARCH_WORD:
+ diskSpeedMBs = convertToMBs(diskSpeed, convertGBsToMBs);
+ break;
+ default:
+ throw new RuntimeException("Invalid ParseResult searchWord");
+ }
+ }
+
+ return diskSpeedMBs;
+ }
+
+ protected boolean checkSpeedValidity(String speed) {
+ try {
+ Double.parseDouble(speed);
+ } catch (NullPointerException | NumberFormatException e) {
+ return false;
+ }
+ return true;
+ }
+
+ protected Double convertToMBs(String speed, double numberToConvert) {
+ double speedMbs = Double.parseDouble(speed);
+ return speedMbs * numberToConvert;
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/HardwareResults.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/HardwareResults.java
new file mode 100644
index 00000000000..6d1cb01fdbb
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/HardwareResults.java
@@ -0,0 +1,56 @@
+package com.yahoo.vespa.hosted.node.verification.hardware.benchmarks;
+
+/**
+ * Created by sgrostad on 11/07/2017.
+ * Stores results from benchmarks
+ */
+public class HardwareResults {
+
+ private double cpuCyclesPerSec;
+ private double diskSpeedMbs;
+ private boolean ipv6Connectivity;
+ private Double memoryWriteSpeedGBs;
+ private Double memoryReadSpeedGBs;
+
+
+ public Double getMemoryWriteSpeedGBs() {
+ return memoryWriteSpeedGBs;
+ }
+
+ public void setMemoryWriteSpeedGBs(Double memoryWriteSpeedGBs) {
+ this.memoryWriteSpeedGBs = memoryWriteSpeedGBs;
+ }
+
+ public Double getMemoryReadSpeedGBs() {
+ return memoryReadSpeedGBs;
+ }
+
+ public void setMemoryReadSpeedGBs(Double memoryReadSpeedGBs) {
+ this.memoryReadSpeedGBs = memoryReadSpeedGBs;
+ }
+
+ public double getCpuCyclesPerSec() {
+ return cpuCyclesPerSec;
+ }
+
+ public void setCpuCyclesPerSec(double cpuCycles) {
+ this.cpuCyclesPerSec = cpuCycles;
+ }
+
+ public double getDiskSpeedMbs() {
+ return diskSpeedMbs;
+ }
+
+ public void setDiskSpeedMbs(double diskSpeedMbs) {
+ this.diskSpeedMbs = diskSpeedMbs;
+ }
+
+ public boolean isIpv6Connectivity() {
+ return ipv6Connectivity;
+ }
+
+ public void setIpv6Connectivity(boolean ipv6Connectivity) {
+ this.ipv6Connectivity = ipv6Connectivity;
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/MemoryBenchmark.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/MemoryBenchmark.java
new file mode 100644
index 00000000000..898c2071b92
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/MemoryBenchmark.java
@@ -0,0 +1,99 @@
+package com.yahoo.vespa.hosted.node.verification.hardware.benchmarks;
+
+import com.yahoo.vespa.hosted.node.verification.commons.CommandExecutor;
+import com.yahoo.vespa.hosted.node.verification.commons.OutputParser;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseInstructions;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseResult;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Created by sgrostad on 11/07/2017.
+ */
+public class MemoryBenchmark implements Benchmark {
+
+ private static final String MEM_BENCHMARK_CREATE_FOLDER = "mkdir -p RAM_test";
+ private static final String MEM_BENCHMARK_MOUNT_TMPFS = "sudo mount tmpfs -t tmpfs RAM_test/";
+ private static final String MEM_BENCHMARK_UNMOUNT_TMPFS = "umount RAM_test";
+ private static final String MEM_BENCHMARK_DELETE_FOLDER = "rm -rf RAM_test";
+ private static final String MEM_BENCHMARK_WRITE_SPEED = "dd if=/dev/zero of=RAM_test/data_tmp bs=1M count=512";
+ private static final String MEM_BENCHMARK_READ_SPEED = "dd if=RAM_test/data_tmp of=/dev/null bs=1M count=512";
+ private static final String READ_AND_WRITE_SEARCH_WORD = "GB/s";
+ private static final String SPLIT_REGEX_STRING = " ";
+ private static final int SEARCH_ELEMENT_INDEX = 8;
+ private static final int RETURN_ELEMENT_INDEX = 7;
+ private static final Logger logger = Logger.getLogger(MemoryBenchmark.class.getName());
+ private final HardwareResults hardwareResults;
+ private final CommandExecutor commandExecutor;
+
+ public MemoryBenchmark(HardwareResults hardwareResults, CommandExecutor commandExecutor) {
+ this.hardwareResults = hardwareResults;
+ this.commandExecutor = commandExecutor;
+ }
+
+ public void doBenchmark() {
+ try {
+ setupMountPoint();
+ ArrayList<String> commandOutput = commandExecutor.executeCommand(MEM_BENCHMARK_WRITE_SPEED);
+ ParseResult parseResult = parseMemorySpeed(commandOutput);
+ updateMemoryWriteSpeed(parseResult.getValue());
+ commandOutput = commandExecutor.executeCommand(MEM_BENCHMARK_READ_SPEED);
+ parseResult = parseMemorySpeed(commandOutput);
+ updateMemoryReadSpeed(parseResult.getValue());
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Failed to perform memory benchmark", e);
+ } finally {
+ breakDownMountPoint();
+ }
+ }
+
+ private void setupMountPoint() throws IOException {
+ commandExecutor.executeCommand(MEM_BENCHMARK_CREATE_FOLDER);
+ commandExecutor.executeCommand(MEM_BENCHMARK_MOUNT_TMPFS);
+ }
+
+ private void breakDownMountPoint() {
+ try {
+ commandExecutor.executeCommand(MEM_BENCHMARK_UNMOUNT_TMPFS);
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Failed to unmount tmpfs folder", e);
+ }
+ try {
+ commandExecutor.executeCommand(MEM_BENCHMARK_DELETE_FOLDER);
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Failed to delete memory benchmark folder", e);
+ }
+ }
+
+ protected ParseResult parseMemorySpeed(ArrayList<String> commandOutput) {
+ ArrayList<String> searchWords = new ArrayList<>(Arrays.asList(READ_AND_WRITE_SEARCH_WORD));
+ ParseInstructions parseInstructions = new ParseInstructions(SEARCH_ELEMENT_INDEX, RETURN_ELEMENT_INDEX, SPLIT_REGEX_STRING, searchWords);
+ return OutputParser.parseSingleOutput(parseInstructions, commandOutput);
+ }
+
+ protected void updateMemoryWriteSpeed(String memorySpeed) {
+ if (!isValidMemory(memorySpeed)) return;
+ double memoryWriteSpeedGbs = Double.parseDouble(memorySpeed);
+ hardwareResults.setMemoryWriteSpeedGBs(memoryWriteSpeedGbs);
+ }
+
+ protected void updateMemoryReadSpeed(String memorySpeed) {
+ if (!isValidMemory(memorySpeed)) return;
+ double memoryReadSpeedGbs = Double.parseDouble(memorySpeed);
+ hardwareResults.setMemoryReadSpeedGBs(memoryReadSpeedGbs);
+ }
+
+ protected boolean isValidMemory(String benchmarkOutput) {
+ try {
+ Double.parseDouble(benchmarkOutput);
+ } catch (NumberFormatException | NullPointerException e) {
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/NetBenchmark.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/NetBenchmark.java
new file mode 100644
index 00000000000..9133cb47690
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/benchmarks/NetBenchmark.java
@@ -0,0 +1,60 @@
+package com.yahoo.vespa.hosted.node.verification.hardware.benchmarks;
+
+import com.yahoo.vespa.hosted.node.verification.commons.CommandExecutor;
+import com.yahoo.vespa.hosted.node.verification.commons.OutputParser;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseInstructions;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseResult;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Created by sgrostad on 11/07/2017.
+ */
+public class NetBenchmark implements Benchmark {
+
+ private static final String NET_BENCHMARK_COMMAND = "ping6 -c 10 www.yahoo.com | grep transmitted";
+ private static final String PING_SEARCH_WORD = "loss,";
+ private static final String SPLIT_REGEX_STRING = "\\s+";
+ private static final int SEARCH_ELEMENT_INDEX = 7;
+ private static final int RETURN_ELEMENT_INDEX = 5;
+ private static final Logger logger = Logger.getLogger(NetBenchmark.class.getName());
+ private final HardwareResults hardwareResults;
+ private final CommandExecutor commandExecutor;
+
+ public NetBenchmark(HardwareResults hardwareResults, CommandExecutor commandExecutor) {
+ this.hardwareResults = hardwareResults;
+ this.commandExecutor = commandExecutor;
+ }
+
+ public void doBenchmark() {
+ try {
+ ArrayList<String> commandOutput = commandExecutor.executeCommand(NET_BENCHMARK_COMMAND);
+ ParseResult parseResult = parsePingResponse(commandOutput);
+ setIpv6Connectivity(parseResult);
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Failed to perform net benchmark", e);
+ }
+ }
+
+ protected ParseResult parsePingResponse(ArrayList<String> commandOutput) {
+ ArrayList<String> searchWords = new ArrayList<>(Arrays.asList(PING_SEARCH_WORD));
+ ParseInstructions parseInstructions = new ParseInstructions(SEARCH_ELEMENT_INDEX, RETURN_ELEMENT_INDEX, SPLIT_REGEX_STRING, searchWords);
+ return OutputParser.parseSingleOutput(parseInstructions, commandOutput);
+
+ }
+
+ protected void setIpv6Connectivity(ParseResult parseResult) {
+ if (parseResult.getSearchWord().equals(PING_SEARCH_WORD)) {
+ String pingResponse = parseResult.getValue();
+ String packetLoss = pingResponse.replaceAll("[^\\d.]", "");
+ if (packetLoss.equals("")) return;
+ if (Double.parseDouble(packetLoss) > 99) return;
+ hardwareResults.setIpv6Connectivity(true);
+ }
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/net/Client.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/net/Client.java
new file mode 100644
index 00000000000..26c97e0707b
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/net/Client.java
@@ -0,0 +1,46 @@
+package com.yahoo.vespa.hosted.node.verification.hardware.net;
+
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.Socket;
+
+/**
+ * Created by olaa on 19/07/2017.
+ * Sends file to server, for checking connection speed.
+ * Not used, can be deleted
+ */
+public class Client {
+
+ public void sendFile(Socket socket, File file) throws IOException {
+ try (FileInputStream inputStream = new FileInputStream(file);
+ DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream())) {
+ int fileSize = (int) file.length();
+ byte[] buffer = new byte[fileSize];
+ outputStream.writeUTF(file.getName());
+ int receivedBytesCount;
+ while ((receivedBytesCount = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, receivedBytesCount);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ socket.close();
+ }
+ }
+
+ public static void main(String[] args) {
+ Client client = new Client();
+ File file = new File("src/test/resources/testReadFile.txt");
+ double start = System.currentTimeMillis() / 1000.0;
+ try {
+ client.sendFile(new Socket("localhost", 10000), file);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ double finish = System.currentTimeMillis() / 1000.0;
+ System.out.println(((double) file.length() / (finish - start)) + " B/s");
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/net/Server.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/net/Server.java
new file mode 100644
index 00000000000..96d363f8da2
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/net/Server.java
@@ -0,0 +1,41 @@
+package com.yahoo.vespa.hosted.node.verification.hardware.net;
+
+import java.io.DataInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+/**
+ * Created by olaa on 19/07/2017.
+ * Receives file from client.
+ * Not used, can be deleted
+ */
+public class Server {
+
+ public void serve(int portNumber) throws IOException {
+ ServerSocket server;
+ Socket client;
+ server = new ServerSocket(portNumber);
+ client = server.accept();
+ DataInputStream dataInputStream = new DataInputStream(client.getInputStream());
+ DataInputStream inputStream = new DataInputStream(client.getInputStream());
+ String fileName = dataInputStream.readUTF();
+ FileOutputStream fileOutputStream = new FileOutputStream("./" + fileName);
+ byte[] buffer = new byte[65535];
+ int currentLength;
+ while ((currentLength = inputStream.read(buffer)) != -1) {
+ fileOutputStream.write(buffer, 0, currentLength);
+ }
+ }
+
+ public static void main(String[] args) {
+ Server server = new Server();
+ try {
+ server.serve(10000);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/yamasreport/HardwareReportDimensions.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/yamasreport/HardwareReportDimensions.java
new file mode 100644
index 00000000000..44c7ff44c67
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/yamasreport/HardwareReportDimensions.java
@@ -0,0 +1,13 @@
+package com.yahoo.vespa.hosted.node.verification.hardware.yamasreport;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Created by sgrostad on 12/07/2017.
+ */
+public class HardwareReportDimensions {
+
+ @JsonProperty
+ private String hostname = "hostname.something";
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/yamasreport/HardwareReportMetrics.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/yamasreport/HardwareReportMetrics.java
new file mode 100644
index 00000000000..6e4d868b710
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/yamasreport/HardwareReportMetrics.java
@@ -0,0 +1,41 @@
+package com.yahoo.vespa.hosted.node.verification.hardware.yamasreport;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Created by sgrostad on 12/07/2017.
+ */
+public class HardwareReportMetrics {
+
+ @JsonProperty
+ private Double cpuCyclesPerSec;
+ @JsonProperty
+ private Double diskSpeedMbs;
+ @JsonProperty
+ private Boolean ipv6Connectivity;
+ @JsonProperty
+ private Double memoryWriteSpeedGBs;
+ @JsonProperty
+ private Double memoryReadSpeedGBs;
+
+ public void setCpuCyclesPerSec(Double cpuCyclesPerSec) {
+ this.cpuCyclesPerSec = cpuCyclesPerSec;
+ }
+
+ public void setDiskSpeedMbs(Double diskSpeedMbs) {
+ this.diskSpeedMbs = diskSpeedMbs != null ? diskSpeedMbs : -1;
+ }
+
+ public void setIpv6Connectivity(Boolean ipv6Connectivity) {
+ this.ipv6Connectivity = ipv6Connectivity;
+ }
+
+ public void setMemoryWriteSpeedGBs(Double memoryWriteSpeedGBs) {
+ this.memoryWriteSpeedGBs = memoryWriteSpeedGBs != null ? memoryWriteSpeedGBs : -1;
+ }
+
+ public void setMemoryReadSpeedGBs(Double memoryReadSpeedGBs) {
+ this.memoryReadSpeedGBs = memoryReadSpeedGBs != null ? memoryReadSpeedGBs : -1;
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/yamasreport/YamasHardwareReport.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/yamasreport/YamasHardwareReport.java
new file mode 100644
index 00000000000..7efb1d8c31a
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/hardware/yamasreport/YamasHardwareReport.java
@@ -0,0 +1,80 @@
+package com.yahoo.vespa.hosted.node.verification.hardware.yamasreport;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.yahoo.vespa.hosted.node.verification.hardware.benchmarks.HardwareResults;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by sgrostad on 12/07/2017.
+ * JSON-mapped class for reporting to YAMAS
+ */
+public class YamasHardwareReport {
+
+ @JsonProperty
+ private long timestamp;
+ @JsonProperty
+ private HardwareReportDimensions dimensions;
+ @JsonProperty
+ private HardwareReportMetrics metrics;
+ @JsonProperty
+ JsonObjectWrapper routing;
+
+ public YamasHardwareReport() {
+ this.timestamp = System.currentTimeMillis() / 1000L;
+ setRouting();
+ }
+
+ public HardwareReportDimensions getDimensions() {
+ return dimensions;
+ }
+
+ public void setDimensions(HardwareReportDimensions dimensions) {
+ this.dimensions = dimensions;
+ }
+
+ public HardwareReportMetrics getMetrics() {
+ return metrics;
+ }
+
+ public void setMetrics(HardwareReportMetrics metrics) {
+ this.metrics = metrics;
+ }
+
+ private void setRouting() {
+ JsonObjectWrapper wrap = new JsonObjectWrapper("namespace", new String[]{"Vespa"});
+ routing = new JsonObjectWrapper("yamas", wrap);
+ }
+
+ public void createFromHardwareResults(HardwareResults hardwareResults) {
+ metrics = new HardwareReportMetrics();
+ dimensions = new HardwareReportDimensions();
+ metrics.setCpuCyclesPerSec(hardwareResults.getCpuCyclesPerSec());
+ metrics.setDiskSpeedMbs(hardwareResults.getDiskSpeedMbs());
+ metrics.setIpv6Connectivity(hardwareResults.isIpv6Connectivity());
+ metrics.setMemoryWriteSpeedGBs(hardwareResults.getMemoryWriteSpeedGBs());
+ metrics.setMemoryReadSpeedGBs(hardwareResults.getMemoryReadSpeedGBs());
+ }
+
+ class JsonObjectWrapper<T> {
+ private Map<String, T> wrappedObjects = new HashMap<String, T>();
+
+ public JsonObjectWrapper(String name, T wrappedObject) {
+ this.wrappedObjects.put(name, wrappedObject);
+ }
+
+ @JsonAnyGetter
+ public Map<String, T> any() {
+ return wrappedObjects;
+ }
+
+ @JsonAnySetter
+ public void set(String name, T value) {
+ wrappedObjects.put(name, value);
+ }
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/HardwareNodeComparator.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/HardwareNodeComparator.java
new file mode 100644
index 00000000000..adec38c2ea4
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/HardwareNodeComparator.java
@@ -0,0 +1,128 @@
+package com.yahoo.vespa.hosted.node.verification.spec;
+
+import com.yahoo.vespa.hosted.node.verification.spec.retrievers.HardwareInfo;
+import com.yahoo.vespa.hosted.node.verification.spec.yamasreport.SpecReportDimensions;
+import com.yahoo.vespa.hosted.node.verification.spec.yamasreport.SpecReportMetrics;
+import com.yahoo.vespa.hosted.node.verification.spec.yamasreport.YamasSpecReport;
+
+/**
+ * Created by olaa on 04/07/2017.
+ * Compares two HardwareInfo objects
+ */
+public class HardwareNodeComparator {
+
+ public static YamasSpecReport compare(HardwareInfo node, HardwareInfo actualHardware) {
+ Boolean equalHardware = true;
+ YamasSpecReport yamasSpecReport = new YamasSpecReport();
+ SpecReportDimensions specReportDimensions = new SpecReportDimensions();
+ SpecReportMetrics specReportMetrics = new SpecReportMetrics();
+
+ if (node == null || actualHardware == null) {
+ return yamasSpecReport;
+ }
+
+ setReportMetrics(node, actualHardware, specReportMetrics);
+
+ equalHardware &= compareMemory(node, actualHardware, specReportDimensions);
+ equalHardware &= compareCPU(node, actualHardware, specReportDimensions);
+ equalHardware &= compareNetInterface(node, actualHardware, specReportDimensions);
+ equalHardware &= compareDisk(node, actualHardware, specReportDimensions);
+
+ specReportMetrics.setMatch(equalHardware);
+ yamasSpecReport.setDimensions(specReportDimensions);
+ yamasSpecReport.setMetrics(specReportMetrics);
+
+ return yamasSpecReport;
+ }
+
+ private static void setReportMetrics(HardwareInfo node, HardwareInfo actualHardware, SpecReportMetrics specReportMetrics) {
+ setMemoryMetrics(node, actualHardware, specReportMetrics);
+ setCpuMetrics(node, actualHardware, specReportMetrics);
+ setDiskTypeMetrics(node, actualHardware, specReportMetrics);
+ setDiskSpaceMetrics(node, actualHardware, specReportMetrics);
+ setNetMetrics(node, actualHardware, specReportMetrics);
+ }
+
+ private static void setMemoryMetrics(HardwareInfo node, HardwareInfo actualHardware, SpecReportMetrics specReportMetrics) {
+ double expectedMemory = node.getMinMainMemoryAvailableGb();
+ double actualMemory = actualHardware.getMinMainMemoryAvailableGb();
+ if (!insideThreshold(expectedMemory, actualMemory)) {
+ specReportMetrics.setExpectedMemoryAvailable(expectedMemory);
+ specReportMetrics.setActualMemoryAvailable(actualMemory);
+ }
+ }
+
+ private static void setCpuMetrics(HardwareInfo node, HardwareInfo actualHardware, SpecReportMetrics specReportMetrics) {
+ int expectedCpuCores = node.getMinCpuCores();
+ int actualCpuCores = actualHardware.getMinCpuCores();
+ if (expectedCpuCores != actualCpuCores) {
+ specReportMetrics.setExpectedcpuCores(expectedCpuCores);
+ specReportMetrics.setActualcpuCores(actualCpuCores);
+ }
+ }
+
+ private static void setDiskTypeMetrics(HardwareInfo node, HardwareInfo actualHardware, SpecReportMetrics specReportMetrics) {
+ Boolean expectedFastDisk = node.getFastDisk();
+ Boolean actualFastDisk = actualHardware.getFastDisk();
+ if (expectedFastDisk != null && actualFastDisk != null && expectedFastDisk != actualFastDisk) {
+ specReportMetrics.setExpectedDiskType(expectedFastDisk);
+ specReportMetrics.setActualDiskType(actualFastDisk);
+ }
+ }
+
+ private static void setDiskSpaceMetrics(HardwareInfo node, HardwareInfo actualHardware, SpecReportMetrics specReportMetrics) {
+ double expectedDiskSpace = node.getMinDiskAvailableGb();
+ double actualDiskSpace = actualHardware.getMinDiskAvailableGb();
+ if (!insideThreshold(expectedDiskSpace, actualDiskSpace)) {
+ specReportMetrics.setExpectedDiskSpaceAvailable(expectedDiskSpace);
+ specReportMetrics.setActualDiskSpaceAvailable(actualDiskSpace);
+ }
+ }
+
+ private static void setNetMetrics(HardwareInfo node, HardwareInfo actualHardware, SpecReportMetrics specReportMetrics) {
+ double expectedInterfaceSpeed = node.getInterfaceSpeedMbs();
+ double actualInterfaceSpeed = actualHardware.getInterfaceSpeedMbs();
+ if (!insideThreshold(expectedInterfaceSpeed, actualInterfaceSpeed)) {
+ specReportMetrics.setExpectedInterfaceSpeed(expectedInterfaceSpeed);
+ specReportMetrics.setActualInterfaceSpeed(actualInterfaceSpeed);
+ }
+ }
+
+ private static boolean compareCPU(HardwareInfo node, HardwareInfo actualHardware, SpecReportDimensions specReportDimensions) {
+ boolean equalCPU = node.getMinCpuCores() == actualHardware.getMinCpuCores();
+ specReportDimensions.setCpuCoresMatch(equalCPU);
+ return equalCPU;
+ }
+
+ private static boolean compareMemory(HardwareInfo node, HardwareInfo actualHardware, SpecReportDimensions specReportDimensions) {
+ boolean equalMemory = insideThreshold(node.getMinMainMemoryAvailableGb(), actualHardware.getMinMainMemoryAvailableGb());
+ specReportDimensions.setMemoryMatch(equalMemory);
+ return equalMemory;
+ }
+
+ private static boolean compareNetInterface(HardwareInfo node, HardwareInfo actualHardware, SpecReportDimensions specReportDimensions) {
+ boolean equalNetInterfaceSpeed = insideThreshold(node.getInterfaceSpeedMbs(), actualHardware.getInterfaceSpeedMbs());
+ boolean equalIpv6 = node.getIpv6Connectivity() == actualHardware.getIpv6Connectivity();
+ boolean equalIpv4 = node.getIpv4Connectivity() == actualHardware.getIpv4Connectivity();
+ specReportDimensions.setNetInterfaceSpeedMatch(equalNetInterfaceSpeed);
+ specReportDimensions.setIpv6Match(equalIpv6);
+ specReportDimensions.setIpv4Match(equalIpv4);
+ return equalNetInterfaceSpeed && equalIpv6 && equalIpv4;
+
+ }
+
+ private static boolean compareDisk(HardwareInfo node, HardwareInfo actualHardware, SpecReportDimensions specReportDimensions) {
+ boolean equalDiskType = node.getFastDisk() == actualHardware.getFastDisk();
+ boolean equalDiskSize = insideThreshold(node.getMinDiskAvailableGb(), actualHardware.getMinDiskAvailableGb());
+ specReportDimensions.setFastDiskMatch(equalDiskType);
+ specReportDimensions.setDiskAvailableMatch(equalDiskSize);
+ return equalDiskType && equalDiskSize;
+ }
+
+ private static boolean insideThreshold(double value1, double value2) {
+ double lowerThresholdPercentage = 0.8;
+ double upperThresholdPercentage = 1.2;
+ return value1 > lowerThresholdPercentage * value2 && value1 < upperThresholdPercentage * value2;
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/HostURLGenerator.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/HostURLGenerator.java
new file mode 100644
index 00000000000..6f619141d2c
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/HostURLGenerator.java
@@ -0,0 +1,23 @@
+package com.yahoo.vespa.hosted.node.verification.spec;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Created by olaa on 14/07/2017.
+ * Makes the URL used to retrieve the JSON from the node repository with information about the node's spec.
+ */
+public class HostURLGenerator {
+
+ private static final String NODE_HOSTNAME_PREFIX = "/nodes/v2/node/";
+
+ protected URL generateNodeInfoUrl(String configServerHostName) throws MalformedURLException {
+ String nodeHostName = getEnvironmentVariable("HOSTNAME");
+ return new URL(configServerHostName + NODE_HOSTNAME_PREFIX + nodeHostName);
+ }
+
+ protected String getEnvironmentVariable(String variableName) {
+ return System.getenv(variableName);
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/README.md b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/README.md
new file mode 100644
index 00000000000..b250223841f
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/README.md
@@ -0,0 +1,2 @@
+# Spec Verification
+Verification of node repo information
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/SpecVerifier.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/SpecVerifier.java
new file mode 100644
index 00000000000..5f5690a16f6
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/SpecVerifier.java
@@ -0,0 +1,71 @@
+package com.yahoo.vespa.hosted.node.verification.spec;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yahoo.vespa.hosted.node.verification.spec.noderepo.IPAddressVerifier;
+import com.yahoo.vespa.hosted.node.verification.spec.noderepo.NodeGenerator;
+import com.yahoo.vespa.hosted.node.verification.spec.noderepo.NodeInfoRetriever;
+import com.yahoo.vespa.hosted.node.verification.spec.noderepo.NodeJsonModel;
+import com.yahoo.vespa.hosted.node.verification.spec.retrievers.HardwareInfo;
+import com.yahoo.vespa.hosted.node.verification.spec.retrievers.HardwareInfoRetriever;
+import com.yahoo.vespa.hosted.node.verification.spec.yamasreport.YamasSpecReport;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Created by olaa on 14/07/2017.
+ * Creates two HardwareInfo objects, one with spec from node repository and one from spec retrieved at the node.
+ * Compares the objects and returns the result.
+ */
+public class SpecVerifier {
+
+ private static final Logger logger = Logger.getLogger(SpecVerifier.class.getName());
+
+ public void verifySpec(String zoneHostName) {
+ URL nodeRepoUrl;
+ try {
+ HostURLGenerator hostURLGenerator = new HostURLGenerator();
+ nodeRepoUrl = hostURLGenerator.generateNodeInfoUrl(zoneHostName);
+ } catch (MalformedURLException e) {
+ logger.log(Level.WARNING, "Failed to generate config server url", e);
+ return;
+ }
+ NodeJsonModel nodeJsonModel = NodeInfoRetriever.retrieve(nodeRepoUrl);
+ HardwareInfo node = NodeGenerator.convertJsonModel(nodeJsonModel);
+ HardwareInfo actualHardware = HardwareInfoRetriever.retrieve();
+ YamasSpecReport yamasSpecReport = HardwareNodeComparator.compare(node, actualHardware);
+ IPAddressVerifier ipAddressVerifier = new IPAddressVerifier();
+ ipAddressVerifier.reportFaultyIpAddresses(nodeJsonModel, yamasSpecReport);
+
+ printResults(yamasSpecReport);
+ }
+
+ private void printResults(YamasSpecReport yamasSpecReport) {
+ //TODO: Instead of println, report JSON to YAMAS
+ ObjectMapper om = new ObjectMapper();
+ try {
+ System.out.println(om.writeValueAsString(yamasSpecReport));
+ } catch (JsonProcessingException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ public static void main(String[] args) {
+ /**
+ * When testing in docker container
+ * docker run --hostname 13305821.ostk.bm2.prod.gq1.yahoo.com --name 13305821.ostk.bm2.prod.gq1.yahoo.com [image]
+ */
+ if (args.length != 1) {
+ throw new RuntimeException("Expected only 1 argument - config server zone url");
+ }
+
+ String zoneHostName = args[0];
+ SpecVerifier specVerifier = new SpecVerifier();
+ specVerifier.verifySpec(zoneHostName);
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/IPAddressVerifier.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/IPAddressVerifier.java
new file mode 100644
index 00000000000..b3b6950a54a
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/IPAddressVerifier.java
@@ -0,0 +1,101 @@
+package com.yahoo.vespa.hosted.node.verification.spec.noderepo;
+
+import com.yahoo.vespa.hosted.node.verification.spec.yamasreport.YamasSpecReport;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Created by olaa on 14/07/2017.
+ * Checks if all additional Ipv6 addresses has the same hostname as the main Ipv6 address.
+ */
+
+public class IPAddressVerifier {
+
+ private static final Logger logger = Logger.getLogger(IPAddressVerifier.class.getName());
+
+ public void reportFaultyIpAddresses(NodeJsonModel nodeJsonModel, YamasSpecReport yamasSpecReport) {
+ String[] faultyIpAddresses = getFaultyIpAddresses(nodeJsonModel.getIpv6Address(), nodeJsonModel.getAdditionalIpAddresses());
+ if (faultyIpAddresses.length > 0) {
+ yamasSpecReport.setFaultyIpAddresses(faultyIpAddresses);
+ }
+ }
+
+ protected String reverseLookUp(String ipAddress) throws NamingException {
+ Hashtable env = new Hashtable();
+ env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
+ String ipAddressInLookupFormat = convertToLookupFormat(ipAddress);
+ String attributeName = ipAddress;
+ DirContext ctx = new InitialDirContext(env);
+ //98.138.253.109
+ //Attributes attrs = ctx.getAttributes("1.0.6.f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.9.7.7.0.c.0.4.9.9.8.0.2.0.0.1.ip6.arpa",new String[] {"PTR"});
+ Attributes attrs = ctx.getAttributes(attributeName, new String[]{"PTR"});
+ for (NamingEnumeration ae = attrs.getAll(); ae.hasMoreElements(); ) {
+ Attribute attr = (Attribute) ae.next();
+ Enumeration vals = attr.getAll();
+ if (vals.hasMoreElements()) {
+ return vals.nextElement().toString();
+ }
+ }
+ ctx.close();
+ return "";
+ }
+
+ protected String convertToLookupFormat(String ipAddress) {
+ StringBuilder newIpAddress = new StringBuilder();
+ String doubleColonReplacement = "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.";
+ String domain = "ip6.arpa";
+ String[] hextets = ipAddress.split(":");
+ for (int i = hextets.length - 1; i >= 0; i--) {
+ String reversedHextet = new StringBuilder(hextets[i]).reverse().toString();
+ if (reversedHextet.equals("")) {
+ newIpAddress.append(doubleColonReplacement);
+ continue;
+ }
+ String trailingZeroes = "0000";
+ String paddedHextet = (reversedHextet + trailingZeroes).substring(0, trailingZeroes.length());
+ String punctuatedHextet = paddedHextet.replaceAll(".(?=)", "$0.");
+ newIpAddress.append(punctuatedHextet);
+ }
+ newIpAddress.append(domain);
+ return newIpAddress.toString();
+ }
+
+ public String[] getFaultyIpAddresses(String ipAddress, String[] additionalIpAddresses) {
+ if (ipAddress == null || additionalIpAddresses == null || additionalIpAddresses.length == 0)
+ return new String[0];
+ String realHostname;
+ ArrayList<String> faultyIpAddresses = new ArrayList<>();
+ try {
+ realHostname = reverseLookUp(ipAddress);
+ } catch (NamingException e) {
+ logger.log(Level.WARNING, "Unable to look up host name of address " + ipAddress, e);
+ return new String[0];
+ }
+ for (String additionalIpAddress : additionalIpAddresses) {
+ addIfFaultyIpAddress(realHostname, additionalIpAddress, faultyIpAddresses);
+ }
+ return faultyIpAddresses.stream().toArray(String[]::new);
+ }
+
+ private void addIfFaultyIpAddress(String realHostname, String additionalIpAddress, ArrayList<String> faultyIpAddresses) {
+ try {
+ String additionalHostName = reverseLookUp(additionalIpAddress);
+ if (!realHostname.equals(additionalHostName)) {
+ faultyIpAddresses.add(additionalIpAddress);
+ }
+ } catch (NamingException e) {
+ logger.log(Level.WARNING, "Unable to retrieve hostname of additional address: " + additionalIpAddress, e);
+ }
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/NodeGenerator.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/NodeGenerator.java
new file mode 100644
index 00000000000..680cbbb132e
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/NodeGenerator.java
@@ -0,0 +1,23 @@
+package com.yahoo.vespa.hosted.node.verification.spec.noderepo;
+
+import com.yahoo.vespa.hosted.node.verification.spec.retrievers.HardwareInfo;
+
+/**
+ * Created by olaa on 07/07/2017.
+ * Converts a NodeJsonModel object to a HardwareInfo object.
+ */
+public class NodeGenerator {
+
+ private static void addStandardSpecifications(HardwareInfo node) {
+ node.setIpv4Connectivity(true);
+ node.setIpv6Connectivity(true);
+ node.setInterfaceSpeedMbs(1000);
+ }
+
+ public static HardwareInfo convertJsonModel(NodeJsonModel nodeJsonModel) {
+ HardwareInfo node = nodeJsonModel.copyToHardwareInfo();
+ addStandardSpecifications(node);
+ return node;
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/NodeInfoRetriever.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/NodeInfoRetriever.java
new file mode 100644
index 00000000000..9297ad873be
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/NodeInfoRetriever.java
@@ -0,0 +1,31 @@
+package com.yahoo.vespa.hosted.node.verification.spec.noderepo;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Created by olaa on 04/07/2017.
+ * Parse JSON from node repository and stores information as a NodeJsonModel object.
+ */
+public class NodeInfoRetriever {
+
+ private static final Logger logger = Logger.getLogger(NodeInfoRetriever.class.getName());
+
+ public static NodeJsonModel retrieve(URL url) {
+ NodeJsonModel nodeJsonModel;
+ ObjectMapper objectMapper = new ObjectMapper();
+ try {
+ nodeJsonModel = objectMapper.readValue(url, NodeJsonModel.class);
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Failed to parse JSON", e);
+ return null;
+ }
+ return nodeJsonModel;
+ }
+
+}
+
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/NodeJsonModel.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/NodeJsonModel.java
new file mode 100644
index 00000000000..f5bbf39e876
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/noderepo/NodeJsonModel.java
@@ -0,0 +1,51 @@
+package com.yahoo.vespa.hosted.node.verification.spec.noderepo;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.yahoo.vespa.hosted.node.verification.spec.retrievers.HardwareInfo;
+
+/**
+ * Created by olaa on 05/07/2017.
+ * Object with the information node repositories has about the node.
+ */
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class NodeJsonModel {
+
+ @JsonProperty("minDiskAvailableGb")
+ private double minDiskAvailableGb;
+ @JsonProperty("minMainMemoryAvailableGb")
+ private double minMainMemoryAvailableGb;
+ @JsonProperty("minCpuCores")
+ private double minCpuCores;
+ @JsonProperty("fastDisk")
+ private boolean fastDisk;
+ @JsonProperty("ipAddresses")
+ private String[] ipAddresses;
+ @JsonProperty("additionalIpAddresses")
+ private String[] additionalIpAddresses;
+
+ public String[] getAdditionalIpAddresses() {
+ return additionalIpAddresses;
+ }
+
+ public HardwareInfo copyToHardwareInfo() {
+ HardwareInfo hardwareInfo = new HardwareInfo();
+ hardwareInfo.setMinMainMemoryAvailableGb(this.minMainMemoryAvailableGb);
+ hardwareInfo.setMinDiskAvailableGb(this.minDiskAvailableGb);
+ hardwareInfo.setMinCpuCores((int) Math.round(this.minCpuCores));
+ hardwareInfo.setFastDisk(this.fastDisk);
+ return hardwareInfo;
+ }
+
+ public String getIpv6Address() {
+ String ipv6Regex = "^((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7}$";
+ for (String ipAddress : ipAddresses) {
+ if (ipAddress.matches(ipv6Regex)) {
+ return ipAddress;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/CPURetriever.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/CPURetriever.java
new file mode 100644
index 00000000000..e903bc4183f
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/CPURetriever.java
@@ -0,0 +1,58 @@
+package com.yahoo.vespa.hosted.node.verification.spec.retrievers;
+
+import com.yahoo.vespa.hosted.node.verification.commons.CommandExecutor;
+import com.yahoo.vespa.hosted.node.verification.commons.OutputParser;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseInstructions;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseResult;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Created by olaa on 30/06/2017.
+ */
+public class CPURetriever implements HardwareRetriever {
+
+ private static final String CPU_INFO_COMMAND = "cat /proc/cpuinfo";
+ private static final String SEARCH_WORD = "cpu MHz";
+ private static final String REGEX_SPLIT = "\\s+:\\s";
+ private static final int SEARCH_ELEMENT_INDEX = 0;
+ private static final int RETURN_ELEMENT_INDEX = 1;
+ private static final Logger logger = Logger.getLogger(CPURetriever.class.getName());
+ private final HardwareInfo hardwareInfo;
+ private final CommandExecutor commandExecutor;
+
+ public CPURetriever(HardwareInfo hardwareInfo, CommandExecutor commandExecutor) {
+ this.hardwareInfo = hardwareInfo;
+ this.commandExecutor = commandExecutor;
+ }
+
+ public void updateInfo() {
+ try {
+ ArrayList<String> commandOutput = commandExecutor.executeCommand(CPU_INFO_COMMAND);
+ ArrayList<ParseResult> parseResults = parseCPUInfoFile(commandOutput);
+ setCpuCores(parseResults);
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Failed to retrieve CPU info", e);
+ }
+ }
+
+ protected ArrayList<ParseResult> parseCPUInfoFile(ArrayList<String> commandOutput) {
+ ArrayList<String> searchWords = new ArrayList<>(Arrays.asList(SEARCH_WORD));
+ ParseInstructions parseInstructions = new ParseInstructions(SEARCH_ELEMENT_INDEX, RETURN_ELEMENT_INDEX, REGEX_SPLIT, searchWords);
+ ArrayList<ParseResult> parseResults = OutputParser.parseOutput(parseInstructions, commandOutput);
+ return parseResults;
+ }
+
+ protected void setCpuCores(ArrayList<ParseResult> parseResults) {
+ hardwareInfo.setMinCpuCores(countCpuCores(parseResults));
+ }
+
+ protected int countCpuCores(ArrayList<ParseResult> parseResults) {
+ return parseResults.size();
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/DiskRetriever.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/DiskRetriever.java
new file mode 100644
index 00000000000..f8cb169ef5e
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/DiskRetriever.java
@@ -0,0 +1,96 @@
+package com.yahoo.vespa.hosted.node.verification.spec.retrievers;
+
+import com.yahoo.vespa.hosted.node.verification.commons.CommandExecutor;
+import com.yahoo.vespa.hosted.node.verification.commons.OutputParser;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseInstructions;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseResult;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Created by olaa on 30/06/2017.
+ */
+public class DiskRetriever implements HardwareRetriever {
+ private static final String DISK_CHECK_TYPE = "lsblk -d -o name,rota";
+ private static final String DISK_CHECK_SIZE = "df -BG /";
+ private static final String DISK_NAME = "sda";
+ private static final String DISK_TYPE_REGEX_SPLIT = "\\s+";
+ private static final int DISK_TYPE_SEARCH_ELEMENT_INDEX = 0;
+ private static final int DISK_TYPE_RETURN_ELEMENT_INDEX = 1;
+ private static final String DISK_SIZE_SEARCH_WORD = ".*\\d+.*";
+ private static final String DISK_SIZE_REGEX_SPLIT = "\\s+";
+ private static final int DISK_SIZE_SEARCH_ELEMENT_INDEX = 3;
+ private static final int DISK_SIZE_RETURN_ELEMENT_INDEX = 1;
+ private static final Logger logger = Logger.getLogger(DiskRetriever.class.getName());
+ private final HardwareInfo hardwareInfo;
+ private final CommandExecutor commandExecutor;
+
+
+ public DiskRetriever(HardwareInfo hardwareInfo, CommandExecutor commandExecutor) {
+ this.hardwareInfo = hardwareInfo;
+ this.commandExecutor = commandExecutor;
+ }
+
+ public void updateInfo() {
+ try {
+ updateDiskType();
+ updateDiskSize();
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Failed to retrieve disk info", e);
+ }
+ }
+
+ protected void updateDiskType() throws IOException {
+ ArrayList<String> commandOutput = commandExecutor.executeCommand(DISK_CHECK_TYPE);
+ ParseResult parseResult = parseDiskType(commandOutput);
+ setDiskType(parseResult);
+ }
+
+ protected void updateDiskSize() throws IOException {
+ ArrayList<String> commandOutput = commandExecutor.executeCommand(DISK_CHECK_SIZE);
+ ParseResult parseResult = parseDiskSize(commandOutput);
+ setDiskSize(parseResult);
+ }
+
+ protected ParseResult parseDiskType(ArrayList<String> commandOutput) {
+ ArrayList<String> searchWords = new ArrayList<>(Arrays.asList(DISK_NAME));
+ ParseInstructions parseInstructions = new ParseInstructions(DISK_TYPE_SEARCH_ELEMENT_INDEX, DISK_TYPE_RETURN_ELEMENT_INDEX, DISK_TYPE_REGEX_SPLIT, searchWords);
+ return OutputParser.parseSingleOutput(parseInstructions, commandOutput);
+ }
+
+ protected void setDiskType(ParseResult parseResult) {
+ if (!parseResult.getSearchWord().equals(DISK_NAME)) {
+ return;
+ }
+ String fastDiskEnum = "0";
+ String nonFastDiskEnum = "1";
+ Boolean fastdisk = null;
+ if (parseResult.getValue().equals(fastDiskEnum)) {
+ fastdisk = true;
+ } else if (parseResult.getValue().equals(nonFastDiskEnum)) {
+ fastdisk = false;
+ }
+ hardwareInfo.setFastDisk(fastdisk);
+ }
+
+ protected void setDiskSize(ParseResult parseResult) {
+ try {
+ String sizeValue = parseResult.getValue().replaceAll("[^\\d.]", "");
+ double diskSize = Double.parseDouble(sizeValue);
+ hardwareInfo.setMinDiskAvailableGb(diskSize);
+ } catch (NumberFormatException | NullPointerException e) {
+ return;
+ }
+ }
+
+ protected ParseResult parseDiskSize(ArrayList<String> commandOutput) {
+ ArrayList<String> searchWords = new ArrayList<>(Arrays.asList(DISK_SIZE_SEARCH_WORD));
+ ParseInstructions parseInstructions = new ParseInstructions(DISK_SIZE_SEARCH_ELEMENT_INDEX, DISK_SIZE_RETURN_ELEMENT_INDEX, DISK_SIZE_REGEX_SPLIT, searchWords);
+ return OutputParser.parseSingleOutput(parseInstructions, commandOutput);
+ }
+
+} \ No newline at end of file
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/HardwareInfo.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/HardwareInfo.java
new file mode 100644
index 00000000000..3252c6ae8dc
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/HardwareInfo.java
@@ -0,0 +1,75 @@
+package com.yahoo.vespa.hosted.node.verification.spec.retrievers;
+
+/**
+ * Created by olaa on 04/07/2017.
+ * All information the different retrievers retrieve is stored as a HardwareInfo object.
+ */
+
+public class HardwareInfo {
+
+ private double minDiskAvailableGb;
+ private double minMainMemoryAvailableGb;
+ private int minCpuCores;
+ private Boolean fastDisk;
+ private Boolean ipv4Connectivity;
+ private Boolean ipv6Connectivity;
+ private double interfaceSpeedMbs;
+
+
+ public double getInterfaceSpeedMbs() {
+ return interfaceSpeedMbs;
+ }
+
+ public void setInterfaceSpeedMbs(double interfaceSpeedMbs) {
+ this.interfaceSpeedMbs = interfaceSpeedMbs;
+ }
+
+ public double getMinDiskAvailableGb() {
+ return minDiskAvailableGb;
+ }
+
+ public void setMinDiskAvailableGb(double minDiskAvailableGb) {
+ this.minDiskAvailableGb = minDiskAvailableGb;
+ }
+
+ public Boolean getIpv6Connectivity() {
+ return ipv6Connectivity;
+ }
+
+ public void setIpv6Connectivity(Boolean ipv6Connectivity) {
+ this.ipv6Connectivity = ipv6Connectivity;
+ }
+
+ public Boolean getIpv4Connectivity() {
+ return ipv4Connectivity;
+ }
+
+ public void setIpv4Connectivity(Boolean ipv4Connectivity) {
+ this.ipv4Connectivity = ipv4Connectivity;
+ }
+
+ public double getMinMainMemoryAvailableGb() {
+ return minMainMemoryAvailableGb;
+ }
+
+ public void setMinMainMemoryAvailableGb(double minMainMemoryAvailableGb) {
+ this.minMainMemoryAvailableGb = minMainMemoryAvailableGb;
+ }
+
+ public void setFastDisk(Boolean fastDisk) {
+ this.fastDisk = fastDisk;
+ }
+
+ public Boolean getFastDisk() {
+ return fastDisk;
+ }
+
+ public int getMinCpuCores() {
+ return minCpuCores;
+ }
+
+ public void setMinCpuCores(int minCpuCores) {
+ this.minCpuCores = minCpuCores;
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/HardwareInfoRetriever.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/HardwareInfoRetriever.java
new file mode 100644
index 00000000000..0ea0d91ac19
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/HardwareInfoRetriever.java
@@ -0,0 +1,28 @@
+package com.yahoo.vespa.hosted.node.verification.spec.retrievers;
+
+import com.yahoo.vespa.hosted.node.verification.commons.CommandExecutor;
+
+import java.util.ArrayList;
+
+/**
+ * Created by olaa on 30/06/2017.
+ * Makes a HardwareInfo object and calls all the retrievers for this object.
+ */
+public class HardwareInfoRetriever {
+
+ public static HardwareInfo retrieve() {
+ HardwareInfo hardwareInfo = new HardwareInfo();
+ CommandExecutor commandExecutor = new CommandExecutor();
+ ArrayList<HardwareRetriever> infoList = new ArrayList<>();
+ infoList.add(new CPURetriever(hardwareInfo, commandExecutor));
+ infoList.add(new MemoryRetriever(hardwareInfo, commandExecutor));
+ infoList.add(new DiskRetriever(hardwareInfo, commandExecutor));
+ infoList.add(new NetRetriever(hardwareInfo, commandExecutor));
+
+ for (HardwareRetriever hardwareInfoType : infoList) {
+ hardwareInfoType.updateInfo();
+ }
+ return hardwareInfo;
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/HardwareRetriever.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/HardwareRetriever.java
new file mode 100644
index 00000000000..2e3852a1d0f
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/HardwareRetriever.java
@@ -0,0 +1,10 @@
+package com.yahoo.vespa.hosted.node.verification.spec.retrievers;
+
+/**
+ * Created by olaa on 30/06/2017.
+ */
+public interface HardwareRetriever {
+
+ void updateInfo();
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/MemoryRetriever.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/MemoryRetriever.java
new file mode 100644
index 00000000000..3cb2b1059ce
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/MemoryRetriever.java
@@ -0,0 +1,63 @@
+package com.yahoo.vespa.hosted.node.verification.spec.retrievers;
+
+import com.yahoo.vespa.hosted.node.verification.commons.CommandExecutor;
+import com.yahoo.vespa.hosted.node.verification.commons.OutputParser;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseInstructions;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseResult;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Created by olaa on 30/06/2017.
+ */
+public class MemoryRetriever implements HardwareRetriever {
+
+ private static final String MEMORY_INFO_COMMAND = "cat /proc/meminfo";
+ private static final String searchWord = "MemTotal";
+ private static final String regexSplit = ":\\s";
+ private static final int searchElementIndex = 0;
+ private static final int returnElementIndex = 1;
+ private static final Logger logger = Logger.getLogger(MemoryRetriever.class.getName());
+ private final HardwareInfo hardwareInfo;
+ private final CommandExecutor commandExecutor;
+
+ public MemoryRetriever(HardwareInfo hardwareInfo, CommandExecutor commandExecutor) {
+ this.hardwareInfo = hardwareInfo;
+ this.commandExecutor = commandExecutor;
+ }
+
+
+ public void updateInfo() {
+ try {
+ ArrayList<String> commandOutput = commandExecutor.executeCommand(MEMORY_INFO_COMMAND);
+ ParseResult parseResult = parseMemInfoFile(commandOutput);
+ updateMemoryInfo(parseResult);
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Failed to retrieve memory info", e);
+ }
+ }
+
+ protected ParseResult parseMemInfoFile(ArrayList<String> commandOutput) {
+ ArrayList<String> searchWords = new ArrayList<>(Arrays.asList(searchWord));
+ ParseInstructions parseInstructions = new ParseInstructions(searchElementIndex, returnElementIndex, regexSplit, searchWords);
+ ParseResult parseResult = OutputParser.parseSingleOutput(parseInstructions, commandOutput);
+ return parseResult;
+ }
+
+ protected void updateMemoryInfo(ParseResult parseResult) {
+ double memory = convertKBToGB(parseResult.getValue());
+ hardwareInfo.setMinMainMemoryAvailableGb(memory);
+ }
+
+ protected double convertKBToGB(String totMem) {
+ String[] split = totMem.split(" ");
+ double value = Double.parseDouble(split[0]);
+ double kiloToGiga = 1000000.0;
+ return value / kiloToGiga;
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/NetRetriever.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/NetRetriever.java
new file mode 100644
index 00000000000..0d793bf416e
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/retrievers/NetRetriever.java
@@ -0,0 +1,112 @@
+package com.yahoo.vespa.hosted.node.verification.spec.retrievers;
+
+import com.yahoo.vespa.hosted.node.verification.commons.CommandExecutor;
+import com.yahoo.vespa.hosted.node.verification.commons.OutputParser;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseInstructions;
+import com.yahoo.vespa.hosted.node.verification.commons.ParseResult;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Created by olaa on 30/06/2017.
+ */
+public class NetRetriever implements HardwareRetriever {
+ private static final String NET_FIND_INTERFACE = "/sbin/ifconfig";
+ private static final String NET_CHECK_INTERFACE_SPEED = "/sbin/ethtool";
+ private static final String SEARCH_WORD_INTERFACE_IP4 = "inet";
+ private static final String SEARCH_WORD_INTERFACE_IPV6 = "inet6";
+ private static final String SEARCH_WORD_INTERFACE_NAME = "eth.";
+ private static final String SEARCH_WORD_INTERFACE_SPEED = "Speed";
+ private static final String INTERFACE_NAME_REGEX_SPLIT = "\\s+";
+ private static final String INTERFACE_NAME_SKIP_WORD = "lo";
+ private static final String INTERFACE_NAME_SKIP_UNTIL_WORD = "";
+ private static final int INTERFACE_NAME_SEARCH_ELEMENT_INDEX = 0;
+ private static final int INTERFACE_NAME_RETURN_ELEMENT_INDEX = 0;
+ private static final String INTERFACE_SPEED_REGEX_SPLIT = ":";
+ private static final int INTERFACE_SPEED_SEARCH_ELEMENT_INDEX = 0;
+ private static final int INTERFACE_SPEED_RETURN_ELEMENT_INDEX = 1;
+ private static final Logger logger = Logger.getLogger(NetRetriever.class.getName());
+ private final HardwareInfo hardwareInfo;
+ private final CommandExecutor commandExecutor;
+
+
+ public NetRetriever(HardwareInfo hardwareInfo, CommandExecutor commandExecutor) {
+ this.hardwareInfo = hardwareInfo;
+ this.commandExecutor = commandExecutor;
+ }
+
+ public void updateInfo() {
+ try {
+ ArrayList<ParseResult> parseResults = findInterface();
+ findInterfaceSpeed(parseResults);
+ updateHardwareInfoWithNet(parseResults);
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Failed to retrieve net info", e);
+ }
+ }
+
+ protected ArrayList<ParseResult> findInterface() throws IOException {
+ ArrayList<String> commandOutput = commandExecutor.executeCommand(NET_FIND_INTERFACE);
+ ArrayList<ParseResult> parseResults = parseNetInterface(commandOutput);
+ return parseResults;
+ }
+
+ protected void findInterfaceSpeed(ArrayList<ParseResult> parseResults) throws IOException {
+ String interfaceName = findInterfaceName(parseResults);
+ String command = NET_CHECK_INTERFACE_SPEED + " " + interfaceName;
+ ArrayList<String> commandOutput = commandExecutor.executeCommand(command);
+ parseResults.add(parseInterfaceSpeed(commandOutput));
+ }
+
+ protected ArrayList<ParseResult> parseNetInterface(ArrayList<String> commandOutput) {
+ ArrayList<String> searchWords = new ArrayList<>(Arrays.asList(SEARCH_WORD_INTERFACE_IP4, SEARCH_WORD_INTERFACE_IPV6, SEARCH_WORD_INTERFACE_NAME));
+ ParseInstructions parseInstructions = new ParseInstructions(INTERFACE_NAME_SEARCH_ELEMENT_INDEX, INTERFACE_NAME_RETURN_ELEMENT_INDEX, INTERFACE_NAME_REGEX_SPLIT, searchWords);
+ parseInstructions.setSkipWord(INTERFACE_NAME_SKIP_WORD);
+ parseInstructions.setSkipUntilKeyword(INTERFACE_NAME_SKIP_UNTIL_WORD);
+ ArrayList<ParseResult> parseResults = OutputParser.parseOutPutWithSkips(parseInstructions, commandOutput);
+ return parseResults;
+ }
+
+ protected ParseResult parseInterfaceSpeed(ArrayList<String> commandOutput) {
+ ArrayList<String> searchWords = new ArrayList<>(Arrays.asList(SEARCH_WORD_INTERFACE_SPEED));
+ ParseInstructions parseInstructions = new ParseInstructions(INTERFACE_SPEED_SEARCH_ELEMENT_INDEX, INTERFACE_SPEED_RETURN_ELEMENT_INDEX, INTERFACE_SPEED_REGEX_SPLIT, searchWords);
+ ParseResult parseResult = OutputParser.parseSingleOutput(parseInstructions, commandOutput);
+ return parseResult;
+ }
+
+ protected String findInterfaceName(ArrayList<ParseResult> parseResults) {
+ for (ParseResult parseResult : parseResults) {
+ if (!parseResult.getSearchWord().matches(SEARCH_WORD_INTERFACE_NAME)) continue;
+ return parseResult.getValue();
+ }
+ return "";
+ }
+
+ protected void updateHardwareInfoWithNet(ArrayList<ParseResult> parseResults) {
+ hardwareInfo.setIpv6Connectivity(false);
+ hardwareInfo.setIpv4Connectivity(false);
+ for (ParseResult parseResult : parseResults) {
+ switch (parseResult.getSearchWord()) {
+ case SEARCH_WORD_INTERFACE_IP4:
+ hardwareInfo.setIpv4Connectivity(true);
+ break;
+ case SEARCH_WORD_INTERFACE_IPV6:
+ hardwareInfo.setIpv6Connectivity(true);
+ break;
+ case SEARCH_WORD_INTERFACE_SPEED:
+ String speedValue = parseResult.getValue().replaceAll("[^\\d.]", "");
+ double speed = Double.parseDouble(speedValue);
+ hardwareInfo.setInterfaceSpeedMbs(speed);
+ break;
+ default:
+ if (parseResult.getSearchWord().matches(SEARCH_WORD_INTERFACE_NAME)) break;
+ throw new RuntimeException("Invalid ParseResult search word");
+ }
+ }
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/yamasreport/SpecReportDimensions.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/yamasreport/SpecReportDimensions.java
new file mode 100644
index 00000000000..abf2f93023f
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/yamasreport/SpecReportDimensions.java
@@ -0,0 +1,82 @@
+package com.yahoo.vespa.hosted.node.verification.spec.yamasreport;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Created by olaa on 12/07/2017.
+ */
+public class SpecReportDimensions {
+
+
+ @JsonProperty
+ private boolean memoryMatch;
+ @JsonProperty
+ private boolean cpuCoresMatch;
+ @JsonProperty
+ private boolean fastDiskMatch;
+ @JsonProperty
+ private boolean netInterfaceSpeedMatch;
+ @JsonProperty
+ private boolean diskAvailableMatch;
+ @JsonProperty
+ private boolean ipv4Match;
+ @JsonProperty
+ private boolean ipv6Match;
+
+ public boolean isNetInterfaceSpeedMatch() {
+ return netInterfaceSpeedMatch;
+ }
+
+ public void setNetInterfaceSpeedMatch(boolean netInterfaceSpeedMatch) {
+ this.netInterfaceSpeedMatch = netInterfaceSpeedMatch;
+ }
+
+ public boolean isMemoryMatch() {
+ return memoryMatch;
+ }
+
+ public void setMemoryMatch(boolean memoryMatch) {
+ this.memoryMatch = memoryMatch;
+ }
+
+ public boolean isCpuCoresMatch() {
+ return cpuCoresMatch;
+ }
+
+ public void setCpuCoresMatch(boolean cpuCoresMatch) {
+ this.cpuCoresMatch = cpuCoresMatch;
+ }
+
+ public boolean isFastDiskMatch() {
+ return fastDiskMatch;
+ }
+
+ public void setFastDiskMatch(boolean fastDiskMatch) {
+ this.fastDiskMatch = fastDiskMatch;
+ }
+
+ public boolean isDiskAvailableMatch() {
+ return diskAvailableMatch;
+ }
+
+ public void setDiskAvailableMatch(boolean diskAvailableMatch) {
+ this.diskAvailableMatch = diskAvailableMatch;
+ }
+
+ public boolean isIpv4Match() {
+ return ipv4Match;
+ }
+
+ public void setIpv4Match(boolean ipv4Match) {
+ this.ipv4Match = ipv4Match;
+ }
+
+ public boolean isIpv6Match() {
+ return ipv6Match;
+ }
+
+ public void setIpv6Match(boolean ipv6Match) {
+ this.ipv6Match = ipv6Match;
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/yamasreport/SpecReportMetrics.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/yamasreport/SpecReportMetrics.java
new file mode 100644
index 00000000000..44332dd6aad
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/yamasreport/SpecReportMetrics.java
@@ -0,0 +1,89 @@
+package com.yahoo.vespa.hosted.node.verification.spec.yamasreport;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Created by olaa on 12/07/2017.
+ */
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class SpecReportMetrics {
+
+ @JsonProperty
+ private boolean match;
+ @JsonProperty
+ private Double expectedMemoryAvailable;
+ @JsonProperty
+ private Double actualMemoryAvailable;
+ @JsonProperty
+ private Boolean expectedFastDisk;
+ @JsonProperty
+ private Boolean actualFastDisk;
+ @JsonProperty
+ private Double expectedDiskSpaceAvailable;
+ @JsonProperty
+ private Double actualDiskSpaceAvailable;
+ @JsonProperty
+ private Double expectedInterfaceSpeed;
+ @JsonProperty
+ private Double actualInterfaceSpeed;
+ @JsonProperty
+ private Integer expectedcpuCores;
+ @JsonProperty
+ private Integer actualcpuCores;
+ @JsonProperty
+ private String[] faultyIpAddresses;
+
+ public void setMatch(boolean match) {
+ this.match = match;
+ }
+
+ public boolean isMatch() {
+ return this.match;
+ }
+
+ public void setExpectedMemoryAvailable(Double expectedMemoryAvailable) {
+ this.expectedMemoryAvailable = expectedMemoryAvailable;
+ }
+
+ public void setActualMemoryAvailable(Double actualMemoryAvailable) {
+ this.actualMemoryAvailable = actualMemoryAvailable;
+ }
+
+ public void setExpectedDiskType(Boolean expectedFastDisk) {
+ this.expectedFastDisk = expectedFastDisk;
+ }
+
+ public void setActualDiskType(Boolean actualFastDisk) {
+ this.actualFastDisk = actualFastDisk;
+ }
+
+ public void setExpectedDiskSpaceAvailable(Double expectedDiskSpaceAvailable) {
+ this.expectedDiskSpaceAvailable = expectedDiskSpaceAvailable;
+ }
+
+ public void setActualDiskSpaceAvailable(Double actualDiskSpaceAvailable) {
+ this.actualDiskSpaceAvailable = actualDiskSpaceAvailable;
+ }
+
+ public void setExpectedcpuCores(int expectedcpuCores) {
+ this.expectedcpuCores = expectedcpuCores;
+ }
+
+ public void setActualcpuCores(int actualcpuCores) {
+ this.actualcpuCores = actualcpuCores;
+ }
+
+ public void setExpectedInterfaceSpeed(Double expectedInterfaceSpeed) {
+ this.expectedInterfaceSpeed = expectedInterfaceSpeed;
+ }
+
+ public void setActualInterfaceSpeed(Double actualInterfaceSpeed) {
+ this.actualInterfaceSpeed = actualInterfaceSpeed;
+ }
+
+ public void setFaultyIpAddresses(String[] faultyIpAddresses) {
+ this.faultyIpAddresses = faultyIpAddresses;
+ }
+
+}
diff --git a/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/yamasreport/YamasSpecReport.java b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/yamasreport/YamasSpecReport.java
new file mode 100644
index 00000000000..c9c2857bbea
--- /dev/null
+++ b/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/spec/yamasreport/YamasSpecReport.java
@@ -0,0 +1,77 @@
+package com.yahoo.vespa.hosted.node.verification.spec.yamasreport;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by olaa on 12/07/2017.
+ */
+public class YamasSpecReport {
+
+ @JsonProperty
+ private long timeStamp;
+ @JsonProperty
+ private SpecReportDimensions dimensions;
+ @JsonProperty
+ private SpecReportMetrics metrics;
+ @JsonProperty
+ private JsonObjectWrapper routing;
+
+ public YamasSpecReport() {
+ this.timeStamp = System.currentTimeMillis() / 1000L;
+ setRouting();
+ }
+
+ public void setDimensions(SpecReportDimensions dimensions) {
+ this.dimensions = dimensions;
+ }
+
+ public SpecReportDimensions getDimensions() {
+ return this.dimensions;
+ }
+
+ public void setMetrics(SpecReportMetrics metrics) {
+ this.metrics = metrics;
+ }
+
+ public void setFaultyIpAddresses(String[] faultyIpAddresses) {
+ this.metrics.setFaultyIpAddresses(faultyIpAddresses);
+ }
+
+ public SpecReportMetrics getMetrics() {
+ return this.metrics;
+ }
+
+ public long getTimeStamp() {
+ return this.timeStamp;
+ }
+
+ private void setRouting() {
+ JsonObjectWrapper wrap = new JsonObjectWrapper("namespace", new String[]{"Vespa"});
+ routing = new JsonObjectWrapper("yamas", wrap);
+ }
+
+ class JsonObjectWrapper<T> {
+
+ private Map<String, T> wrappedObjects = new HashMap<String, T>();
+
+ public JsonObjectWrapper(String name, T wrappedObject) {
+ this.wrappedObjects.put(name, wrappedObject);
+ }
+
+ @JsonAnyGetter
+ public Map<String, T> any() {
+ return wrappedObjects;
+ }
+
+ @JsonAnySetter
+ public void set(String name, T value) {
+ wrappedObjects.put(name, value);
+ }
+ }
+
+}