diff options
author | olaaun <ola.aunroe@gmail.com> | 2017-07-25 20:37:02 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-25 20:37:02 +0800 |
commit | a44477bcd943021fb80dda761e485e81b7c929ac (patch) | |
tree | b1a7a40b4cbb31612c3f65261860a84b4a0cc61d /node-maintainer/src/main/java/com | |
parent | 674749e424fe50980479e462b4c1a3fb8b77147a (diff) |
Node verification (#2999)
* Initial node verification commit
Diffstat (limited to 'node-maintainer/src/main/java/com')
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); + } + } + +} |