mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
Improve deviation model
This commit is contained in:
parent
0c4eb14077
commit
f1fdf3c0a5
@ -26,6 +26,7 @@ import bisq.core.dao.monitoring.model.ProposalStateBlock;
|
||||
import bisq.core.dao.state.DaoStateService;
|
||||
import bisq.core.network.p2p.inventory.messages.GetInventoryRequest;
|
||||
import bisq.core.network.p2p.inventory.messages.GetInventoryResponse;
|
||||
import bisq.core.network.p2p.inventory.model.InventoryItem;
|
||||
|
||||
import bisq.network.p2p.network.Connection;
|
||||
import bisq.network.p2p.network.MessageListener;
|
||||
@ -48,11 +49,9 @@ import com.google.common.base.Enums;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
@ -68,7 +67,6 @@ public class GetInventoryRequestHandler implements MessageListener {
|
||||
private final ProposalStateMonitoringService proposalStateMonitoringService;
|
||||
private final BlindVoteStateMonitoringService blindVoteStateMonitoringService;
|
||||
private final int maxConnections;
|
||||
private final Set<String> permittedRequestersPubKey = new HashSet<>();
|
||||
|
||||
@Inject
|
||||
public GetInventoryRequestHandler(NetworkNode networkNode,
|
||||
@ -94,10 +92,6 @@ public class GetInventoryRequestHandler implements MessageListener {
|
||||
@Override
|
||||
public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) {
|
||||
if (networkEnvelope instanceof GetInventoryRequest) {
|
||||
if (permittedRequestersPubKey.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Data
|
||||
GetInventoryRequest getInventoryRequest = (GetInventoryRequest) networkEnvelope;
|
||||
Map<InventoryItem, Integer> dataObjects = new HashMap<>();
|
||||
@ -179,8 +173,4 @@ public class GetInventoryRequestHandler implements MessageListener {
|
||||
public void shutDown() {
|
||||
networkNode.removeMessageListener(this);
|
||||
}
|
||||
|
||||
public void addPermittedRequestersPubKey(String pubKey) {
|
||||
permittedRequestersPubKey.add(pubKey);
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
|
||||
package bisq.core.network.p2p.inventory;
|
||||
|
||||
import bisq.core.network.p2p.inventory.model.InventoryItem;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
import bisq.network.p2p.network.NetworkNode;
|
||||
|
||||
|
@ -19,6 +19,7 @@ package bisq.core.network.p2p.inventory;
|
||||
|
||||
import bisq.core.network.p2p.inventory.messages.GetInventoryRequest;
|
||||
import bisq.core.network.p2p.inventory.messages.GetInventoryResponse;
|
||||
import bisq.core.network.p2p.inventory.model.InventoryItem;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
import bisq.network.p2p.network.CloseConnectionReason;
|
||||
|
@ -1,97 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.network.p2p.inventory;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public enum InventoryItem {
|
||||
OfferPayload("OfferPayload", Integer.class),
|
||||
MailboxStoragePayload("MailboxStoragePayload", Integer.class, 0.9, 1.1, 0.95, 1.05),
|
||||
TradeStatistics3("TradeStatistics3", Integer.class, 0.9, 1.1, 0.95, 1.05),
|
||||
Alert("Alert", Integer.class),
|
||||
Filter("Filter", Integer.class),
|
||||
Mediator("Mediator", Integer.class),
|
||||
RefundAgent("RefundAgent", Integer.class),
|
||||
AccountAgeWitness("AccountAgeWitness", Integer.class, 0.9, 1.1, 0.95, 1.05),
|
||||
SignedWitness("SignedWitness", Integer.class, 0.9, 1.1, 0.95, 1.05),
|
||||
|
||||
TempProposalPayload("TempProposalPayload", Integer.class),
|
||||
ProposalPayload("ProposalPayload", Integer.class),
|
||||
BlindVotePayload("BlindVotePayload", Integer.class),
|
||||
|
||||
daoStateChainHeight("daoStateChainHeight", Integer.class),
|
||||
numBsqBlocks("numBsqBlocks", Integer.class),
|
||||
daoStateHash("daoStateHash", String.class),
|
||||
proposalHash("proposalHash", String.class),
|
||||
blindVoteHash("blindVoteHash", String.class),
|
||||
|
||||
maxConnections("maxConnections", Integer.class, 0.33, 3, 0.4, 2.5),
|
||||
numConnections("numConnections", Integer.class, 0.33, 3, 0.4, 2.5),
|
||||
peakNumConnections("peakNumConnections", Integer.class, 0.33, 3, 0.4, 2.5),
|
||||
numAllConnectionsLostEvents("numAllConnectionsLostEvents", Integer.class, 0.9, 1.1, 0.95, 1.05),
|
||||
sentBytes("sentBytes", Long.class, 0, 5, 0, 4),
|
||||
sentBytesPerSec("sentBytesPerSec", Double.class, 0, 3, 0, 2),
|
||||
receivedBytes("receivedBytes", Long.class, 0, 5, 0, 4),
|
||||
receivedBytesPerSec("receivedBytesPerSec", Double.class, 0, 3, 0, 2),
|
||||
receivedMessagesPerSec("receivedMessagesPerSec", Double.class, 0, 3, 0, 2),
|
||||
sentMessagesPerSec("sentMessagesPerSec", Double.class, 0, 3, 0, 2),
|
||||
|
||||
version("version", String.class),
|
||||
usedMemory("usedMemory", Long.class, 0, 3, 0, 2),
|
||||
jvmStartTime("jvmStartTime", Long.class);
|
||||
|
||||
@Getter
|
||||
private final String key;
|
||||
@Getter
|
||||
private final Class type;
|
||||
private double lowerAlertTrigger = 0.7;
|
||||
private double upperAlertTrigger = 1.5;
|
||||
private double lowerWarnTrigger = 0.8;
|
||||
private double upperWarnTrigger = 1.3;
|
||||
|
||||
InventoryItem(String key, Class type) {
|
||||
this.key = key;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
InventoryItem(String key,
|
||||
Class type,
|
||||
double lowerAlertTrigger,
|
||||
double upperAlertTrigger,
|
||||
double lowerWarnTrigger,
|
||||
double upperWarnTrigger) {
|
||||
this.key = key;
|
||||
this.type = type;
|
||||
this.lowerAlertTrigger = lowerAlertTrigger;
|
||||
this.upperAlertTrigger = upperAlertTrigger;
|
||||
this.lowerWarnTrigger = lowerWarnTrigger;
|
||||
this.upperWarnTrigger = upperWarnTrigger;
|
||||
}
|
||||
|
||||
public DeviationSeverity getDeviationSeverity(double deviation) {
|
||||
if (deviation <= lowerAlertTrigger || deviation >= upperAlertTrigger) {
|
||||
return DeviationSeverity.ALERT;
|
||||
}
|
||||
|
||||
if (deviation <= lowerWarnTrigger || deviation >= upperWarnTrigger) {
|
||||
return DeviationSeverity.WARN;
|
||||
}
|
||||
|
||||
return DeviationSeverity.OK;
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
|
||||
package bisq.core.network.p2p.inventory.messages;
|
||||
|
||||
import bisq.core.network.p2p.inventory.InventoryItem;
|
||||
import bisq.core.network.p2p.inventory.model.InventoryItem;
|
||||
|
||||
import bisq.common.app.Version;
|
||||
import bisq.common.proto.network.NetworkEnvelope;
|
||||
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.network.p2p.inventory.model;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class Average {
|
||||
public static Map<InventoryItem, Double> of(Set<RequestInfo> requestInfoSet) {
|
||||
Map<InventoryItem, Double> averageValuesPerItem = new HashMap<>();
|
||||
Arrays.asList(InventoryItem.values()).forEach(inventoryItem -> {
|
||||
if (inventoryItem.isNumberValue()) {
|
||||
averageValuesPerItem.put(inventoryItem, getAverage(requestInfoSet, inventoryItem));
|
||||
}
|
||||
});
|
||||
return averageValuesPerItem;
|
||||
}
|
||||
|
||||
public static double getAverage(Set<RequestInfo> requestInfoSet, InventoryItem inventoryItem) {
|
||||
return requestInfoSet.stream()
|
||||
.map(RequestInfo::getInventory)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(inventory -> inventory.containsKey(inventoryItem))
|
||||
.mapToDouble(inventory -> Double.parseDouble((inventory.get(inventoryItem))))
|
||||
.average()
|
||||
.orElse(0d);
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.network.p2p.inventory.model;
|
||||
|
||||
import bisq.common.util.Tuple2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class DeviationByIntegerDiff implements DeviationType {
|
||||
private final int warnTrigger;
|
||||
private final int alertTrigger;
|
||||
|
||||
public DeviationByIntegerDiff(int warnTrigger, int alertTrigger) {
|
||||
this.warnTrigger = warnTrigger;
|
||||
this.alertTrigger = alertTrigger;
|
||||
}
|
||||
|
||||
public DeviationSeverity getDeviationSeverity(Collection<List<RequestInfo>> collection,
|
||||
@Nullable String value,
|
||||
InventoryItem inventoryItem) {
|
||||
DeviationSeverity deviationSeverity = DeviationSeverity.OK;
|
||||
if (value == null) {
|
||||
return deviationSeverity;
|
||||
}
|
||||
|
||||
Map<String, Integer> sameItemsByValue = new HashMap<>();
|
||||
collection.stream()
|
||||
.filter(list -> !list.isEmpty())
|
||||
.map(list -> list.get(list.size() - 1)) // We use last item only
|
||||
.map(RequestInfo::getInventory)
|
||||
.filter(Objects::nonNull)
|
||||
.map(e -> e.get(inventoryItem))
|
||||
.forEach(e -> {
|
||||
sameItemsByValue.putIfAbsent(e, 0);
|
||||
int prev = sameItemsByValue.get(e);
|
||||
sameItemsByValue.put(e, prev + 1);
|
||||
});
|
||||
if (sameItemsByValue.size() > 1) {
|
||||
List<Tuple2<String, Integer>> sameItems = new ArrayList<>();
|
||||
sameItemsByValue.forEach((k, v) -> sameItems.add(new Tuple2<>(k, v)));
|
||||
sameItems.sort(Comparator.comparing(o -> o.second));
|
||||
Collections.reverse(sameItems);
|
||||
String majority = sameItems.get(0).first;
|
||||
if (!majority.equals(value)) {
|
||||
int majorityAsInt = Integer.parseInt(majority);
|
||||
int valueAsInt = Integer.parseInt(value);
|
||||
int diff = Math.abs(majorityAsInt - valueAsInt);
|
||||
if (diff >= alertTrigger) {
|
||||
deviationSeverity = DeviationSeverity.ALERT;
|
||||
} else if (diff >= warnTrigger) {
|
||||
deviationSeverity = DeviationSeverity.WARN;
|
||||
} else {
|
||||
deviationSeverity = DeviationSeverity.OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return deviationSeverity;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.network.p2p.inventory.model;
|
||||
|
||||
public class DeviationByPercentage implements DeviationType {
|
||||
private final double lowerAlertTrigger;
|
||||
private final double upperAlertTrigger;
|
||||
private final double lowerWarnTrigger;
|
||||
private final double upperWarnTrigger;
|
||||
|
||||
public DeviationByPercentage(double lowerAlertTrigger,
|
||||
double upperAlertTrigger,
|
||||
double lowerWarnTrigger,
|
||||
double upperWarnTrigger) {
|
||||
this.lowerAlertTrigger = lowerAlertTrigger;
|
||||
this.upperAlertTrigger = upperAlertTrigger;
|
||||
this.lowerWarnTrigger = lowerWarnTrigger;
|
||||
this.upperWarnTrigger = upperWarnTrigger;
|
||||
}
|
||||
|
||||
public DeviationSeverity getDeviationSeverity(double deviation) {
|
||||
if (deviation <= lowerAlertTrigger || deviation >= upperAlertTrigger) {
|
||||
return DeviationSeverity.ALERT;
|
||||
}
|
||||
|
||||
if (deviation <= lowerWarnTrigger || deviation >= upperWarnTrigger) {
|
||||
return DeviationSeverity.WARN;
|
||||
}
|
||||
|
||||
return DeviationSeverity.OK;
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.network.p2p.inventory.model;
|
||||
|
||||
import bisq.common.util.Tuple2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class DeviationOfHashes implements DeviationType {
|
||||
public DeviationSeverity getDeviationSeverity(Collection<List<RequestInfo>> collection,
|
||||
@Nullable String value,
|
||||
InventoryItem inventoryItem,
|
||||
String currentBlockHeight) {
|
||||
DeviationSeverity deviationSeverity = DeviationSeverity.OK;
|
||||
if (value == null) {
|
||||
return deviationSeverity;
|
||||
}
|
||||
|
||||
Map<String, Integer> sameHashesPerHashListByHash = new HashMap<>();
|
||||
collection.stream()
|
||||
.filter(list -> !list.isEmpty())
|
||||
.map(list -> list.get(list.size() - 1)) // We use last item only
|
||||
.map(RequestInfo::getInventory)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(inventory -> inventory.get(InventoryItem.daoStateChainHeight).equals(currentBlockHeight))
|
||||
.map(inventory -> inventory.get(inventoryItem))
|
||||
.forEach(v -> {
|
||||
sameHashesPerHashListByHash.putIfAbsent(v, 0);
|
||||
int prev = sameHashesPerHashListByHash.get(v);
|
||||
sameHashesPerHashListByHash.put(v, prev + 1);
|
||||
});
|
||||
if (sameHashesPerHashListByHash.size() > 1) {
|
||||
List<Tuple2<String, Integer>> sameHashesPerHashList = new ArrayList<>();
|
||||
sameHashesPerHashListByHash.forEach((k, v) -> sameHashesPerHashList.add(new Tuple2<>(k, v)));
|
||||
sameHashesPerHashList.sort(Comparator.comparing(o -> o.second));
|
||||
Collections.reverse(sameHashesPerHashList);
|
||||
|
||||
// It could be that first and any following list entry has same number of hashes, but we ignore that as
|
||||
// it is reason enough to alert the operators in case not all hashes are the same.
|
||||
if (sameHashesPerHashList.get(0).first.equals(value)) {
|
||||
// We are in the majority group.
|
||||
// We also set a warning to make sure the operators act quickly and to check if there are
|
||||
// more severe issues.
|
||||
deviationSeverity = DeviationSeverity.WARN;
|
||||
} else {
|
||||
deviationSeverity = DeviationSeverity.ALERT;
|
||||
}
|
||||
}
|
||||
return deviationSeverity;
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.network.p2p.inventory;
|
||||
package bisq.core.network.p2p.inventory.model;
|
||||
|
||||
public enum DeviationSeverity {
|
||||
OK,
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.network.p2p.inventory.model;
|
||||
|
||||
public interface DeviationType {
|
||||
}
|
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.network.p2p.inventory.model;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public enum InventoryItem {
|
||||
// Percentage deviation
|
||||
OfferPayload("OfferPayload",
|
||||
true,
|
||||
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05)),
|
||||
MailboxStoragePayload("MailboxStoragePayload",
|
||||
true,
|
||||
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05)),
|
||||
TradeStatistics3("MailboxStoragePayload",
|
||||
true,
|
||||
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05)),
|
||||
AccountAgeWitness("MailboxStoragePayload",
|
||||
true,
|
||||
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05)),
|
||||
SignedWitness("MailboxStoragePayload",
|
||||
true,
|
||||
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05)),
|
||||
|
||||
// Should be same value
|
||||
Alert("Alert",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 1)),
|
||||
Filter("Filter",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 1)),
|
||||
Mediator("Mediator",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 1)),
|
||||
RefundAgent("RefundAgent",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 1)),
|
||||
|
||||
// Should be very close values
|
||||
TempProposalPayload("TempProposalPayload",
|
||||
true,
|
||||
new DeviationByIntegerDiff(3, 5)),
|
||||
ProposalPayload("ProposalPayload",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 2)),
|
||||
BlindVotePayload("BlindVotePayload",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 2)),
|
||||
|
||||
// Should be very close values
|
||||
daoStateChainHeight("daoStateChainHeight",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 3)),
|
||||
numBsqBlocks("numBsqBlocks",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 3)),
|
||||
|
||||
// Has to be same values at same block
|
||||
daoStateHash("daoStateHash",
|
||||
false,
|
||||
new DeviationOfHashes()),
|
||||
proposalHash("proposalHash",
|
||||
false,
|
||||
new DeviationOfHashes()),
|
||||
blindVoteHash("blindVoteHash",
|
||||
false,
|
||||
new DeviationOfHashes()),
|
||||
|
||||
// Percentage deviation
|
||||
maxConnections("maxConnections",
|
||||
true,
|
||||
new DeviationByPercentage(0.33, 3, 0.4, 2.5)),
|
||||
numConnections("numConnections",
|
||||
true,
|
||||
new DeviationByPercentage(0.33, 3, 0.4, 2.5)),
|
||||
peakNumConnections("peakNumConnections",
|
||||
true,
|
||||
new DeviationByPercentage(0.33, 3, 0.4, 2.5)),
|
||||
numAllConnectionsLostEvents("numAllConnectionsLostEvents",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 2)),
|
||||
sentBytesPerSec("sentBytesPerSec",
|
||||
true,
|
||||
new DeviationByPercentage(0.33, 3, 0.4, 2.5)),
|
||||
receivedBytesPerSec("receivedBytesPerSec",
|
||||
true,
|
||||
new DeviationByPercentage(0.33, 3, 0.4, 2.5)),
|
||||
receivedMessagesPerSec("receivedMessagesPerSec",
|
||||
true,
|
||||
new DeviationByPercentage(0.33, 3, 0.4, 2.5)),
|
||||
sentMessagesPerSec("sentMessagesPerSec",
|
||||
true,
|
||||
new DeviationByPercentage(0.33, 3, 0.4, 2.5)),
|
||||
|
||||
// No deviation check
|
||||
sentBytes("sentBytes", true),
|
||||
receivedBytes("receivedBytes", true),
|
||||
|
||||
// No deviation check
|
||||
version("version", false),
|
||||
usedMemory("usedMemory", true),
|
||||
jvmStartTime("jvmStartTime", true);
|
||||
|
||||
@Getter
|
||||
private final String key;
|
||||
@Getter
|
||||
private final boolean isNumberValue;
|
||||
@Getter
|
||||
@Nullable
|
||||
private DeviationType deviationType;
|
||||
|
||||
InventoryItem(String key, boolean isNumberValue) {
|
||||
this.key = key;
|
||||
this.isNumberValue = isNumberValue;
|
||||
}
|
||||
|
||||
InventoryItem(String key, boolean isNumberValue, DeviationType deviationType) {
|
||||
this(key, isNumberValue);
|
||||
this.deviationType = deviationType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double getDeviation(Map<InventoryItem, Double> averageValues, @Nullable String value) {
|
||||
if (averageValues.containsKey(this) && value != null) {
|
||||
double averageValue = averageValues.get(this);
|
||||
return getDeviation(value, averageValue);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double getDeviation(@Nullable String value, double average) {
|
||||
if (deviationType != null && value != null && average != 0 && isNumberValue) {
|
||||
return Double.parseDouble(value) / average;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public DeviationSeverity getDeviationSeverity(Double deviation,
|
||||
Collection<List<RequestInfo>> collection,
|
||||
@Nullable String value,
|
||||
String currentBlockHeight) {
|
||||
if (deviationType == null || deviation == null || value == null) {
|
||||
return DeviationSeverity.OK;
|
||||
}
|
||||
|
||||
if (deviationType instanceof DeviationByPercentage) {
|
||||
return ((DeviationByPercentage) deviationType).getDeviationSeverity(deviation);
|
||||
} else if (deviationType instanceof DeviationByIntegerDiff) {
|
||||
return ((DeviationByIntegerDiff) deviationType).getDeviationSeverity(collection, value, this);
|
||||
} else if (deviationType instanceof DeviationOfHashes) {
|
||||
return ((DeviationOfHashes) deviationType).getDeviationSeverity(collection, value, this, currentBlockHeight);
|
||||
} else {
|
||||
return DeviationSeverity.OK;
|
||||
}
|
||||
}
|
||||
}
|
@ -15,9 +15,7 @@
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.inventory;
|
||||
|
||||
import bisq.core.network.p2p.inventory.InventoryItem;
|
||||
package bisq.core.network.p2p.inventory.model;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@ -41,4 +39,16 @@ public class RequestInfo {
|
||||
public RequestInfo(long requestStartTime) {
|
||||
this.requestStartTime = requestStartTime;
|
||||
}
|
||||
|
||||
public String getDisplayValue(InventoryItem inventoryItem) {
|
||||
String value = getValue(inventoryItem);
|
||||
return value != null ? value : "n/a";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getValue(InventoryItem inventoryItem) {
|
||||
return inventory != null && inventory.containsKey(inventoryItem) ?
|
||||
inventory.get(inventoryItem) :
|
||||
null;
|
||||
}
|
||||
}
|
@ -19,7 +19,9 @@ package bisq.inventory;
|
||||
|
||||
|
||||
import bisq.core.network.p2p.inventory.GetInventoryRequestManager;
|
||||
import bisq.core.network.p2p.inventory.InventoryItem;
|
||||
import bisq.core.network.p2p.inventory.model.Average;
|
||||
import bisq.core.network.p2p.inventory.model.InventoryItem;
|
||||
import bisq.core.network.p2p.inventory.model.RequestInfo;
|
||||
import bisq.core.network.p2p.seed.DefaultSeedNodeRepository;
|
||||
import bisq.core.proto.network.CoreNetworkProtoResolver;
|
||||
|
||||
@ -50,7 +52,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@Slf4j
|
||||
public class InventoryMonitor implements SetupListener {
|
||||
|
||||
private final Map<NodeAddress, JsonFileManager> jsonFileManagerByNodeAddress = new HashMap<>();
|
||||
private final Map<NodeAddress, List<RequestInfo>> requestInfoListByNode = new HashMap<>();
|
||||
private final File appDir;
|
||||
@ -58,10 +59,16 @@ public class InventoryMonitor implements SetupListener {
|
||||
private final int intervalSec;
|
||||
private final NetworkNode networkNode;
|
||||
private final GetInventoryRequestManager getInventoryRequestManager;
|
||||
|
||||
private ArrayList<NodeAddress> seedNodes;
|
||||
private InventoryWebServer inventoryWebServer;
|
||||
private int requestCounter = 0;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public InventoryMonitor(File appDir,
|
||||
boolean useLocalhostForP2P,
|
||||
BaseCurrencyNetwork network,
|
||||
@ -87,10 +94,26 @@ public class InventoryMonitor implements SetupListener {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void shutDown(Runnable shutDownCompleteHandler) {
|
||||
networkNode.shutDown(shutDownCompleteHandler);
|
||||
jsonFileManagerByNodeAddress.values().forEach(JsonFileManager::shutDown);
|
||||
inventoryWebServer.shutDown();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SetupListener
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void onTorNodeReady() {
|
||||
UserThread.runPeriodically(this::requestAllSeeds, intervalSec);
|
||||
requestAllSeeds();
|
||||
UserThread.runPeriodically(this::requestFromAllSeeds, intervalSec);
|
||||
requestFromAllSeeds();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -105,13 +128,12 @@ public class InventoryMonitor implements SetupListener {
|
||||
public void onRequestCustomBridges() {
|
||||
}
|
||||
|
||||
public void shutDown(Runnable shutDownCompleteHandler) {
|
||||
networkNode.shutDown(shutDownCompleteHandler);
|
||||
jsonFileManagerByNodeAddress.values().forEach(JsonFileManager::shutDown);
|
||||
inventoryWebServer.shutDown();
|
||||
}
|
||||
|
||||
private void requestAllSeeds() {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void requestFromAllSeeds() {
|
||||
requestCounter++;
|
||||
seedNodes.forEach(nodeAddress -> {
|
||||
RequestInfo requestInfo = new RequestInfo(System.currentTimeMillis());
|
||||
@ -121,7 +143,6 @@ public class InventoryMonitor implements SetupListener {
|
||||
result -> processResponse(nodeAddress, requestInfo, result, null),
|
||||
errorMessage -> processResponse(nodeAddress, requestInfo, null, errorMessage));
|
||||
}).start();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -147,11 +168,11 @@ public class InventoryMonitor implements SetupListener {
|
||||
|
||||
// We create average of all nodes latest results. It might be that the nodes last result is
|
||||
// from a previous request as the response has not arrived yet.
|
||||
Set<RequestInfo> requestInfoSetOfOtherNodes = requestInfoListByNode.values().stream()
|
||||
Set<RequestInfo> requestInfoSet = requestInfoListByNode.values().stream()
|
||||
.filter(list -> !list.isEmpty())
|
||||
.map(list -> list.get(list.size() - 1))
|
||||
.collect(Collectors.toSet());
|
||||
Map<InventoryItem, Double> averageValues = InventoryUtil.getAverageValues(requestInfoSetOfOtherNodes);
|
||||
Map<InventoryItem, Double> averageValues = Average.of(requestInfoSet);
|
||||
|
||||
inventoryWebServer.onNewRequestInfo(requestInfoListByNode, averageValues, requestCounter);
|
||||
|
||||
|
@ -1,177 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.inventory;
|
||||
|
||||
import bisq.core.network.p2p.inventory.DeviationSeverity;
|
||||
import bisq.core.network.p2p.inventory.InventoryItem;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
|
||||
import bisq.common.util.Tuple2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
public class InventoryUtil {
|
||||
|
||||
public static Map<InventoryItem, Double> getAverageValues(Set<RequestInfo> requestInfoSetOfOtherNodes) {
|
||||
Map<InventoryItem, Double> averageValuesPerItem = new HashMap<>();
|
||||
Arrays.asList(InventoryItem.values()).forEach(inventoryItem -> {
|
||||
if (inventoryItem.getType().equals(Integer.class)) {
|
||||
averageValuesPerItem.put(inventoryItem, getAverageFromIntegerValues(requestInfoSetOfOtherNodes, inventoryItem));
|
||||
} else if (inventoryItem.getType().equals(Long.class)) {
|
||||
averageValuesPerItem.put(inventoryItem, getAverageFromLongValues(requestInfoSetOfOtherNodes, inventoryItem));
|
||||
} else if (inventoryItem.getType().equals(Double.class)) {
|
||||
averageValuesPerItem.put(inventoryItem, getAverageFromDoubleValues(requestInfoSetOfOtherNodes, inventoryItem));
|
||||
}
|
||||
// If type of value is String we ignore it
|
||||
});
|
||||
return averageValuesPerItem;
|
||||
}
|
||||
|
||||
public static double getAverageFromIntegerValues(Set<RequestInfo> requestInfoSetOfOtherNodes,
|
||||
InventoryItem inventoryItem) {
|
||||
checkArgument(inventoryItem.getType().equals(Integer.class));
|
||||
return requestInfoSetOfOtherNodes.stream()
|
||||
.map(RequestInfo::getInventory)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(inventory -> inventory.containsKey(inventoryItem))
|
||||
.mapToInt(inventory -> Integer.parseInt(inventory.get(inventoryItem)))
|
||||
.average()
|
||||
.orElse(0d);
|
||||
}
|
||||
|
||||
public static double getAverageFromLongValues(Set<RequestInfo> requestInfoSetOfOtherNodes,
|
||||
InventoryItem inventoryItem) {
|
||||
checkArgument(inventoryItem.getType().equals(Long.class));
|
||||
return requestInfoSetOfOtherNodes.stream()
|
||||
.map(RequestInfo::getInventory)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(inventory -> inventory.containsKey(inventoryItem))
|
||||
.mapToLong(inventory -> Long.parseLong(inventory.get(inventoryItem)))
|
||||
.average()
|
||||
.orElse(0d);
|
||||
}
|
||||
|
||||
public static double getAverageFromDoubleValues(Set<RequestInfo> requestInfoSetOfOtherNodes,
|
||||
InventoryItem inventoryItem) {
|
||||
checkArgument(inventoryItem.getType().equals(Double.class));
|
||||
return requestInfoSetOfOtherNodes.stream()
|
||||
.map(RequestInfo::getInventory)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(inventory -> inventory.containsKey(inventoryItem))
|
||||
.mapToDouble(inventory -> Double.parseDouble((inventory.get(inventoryItem))))
|
||||
.average()
|
||||
.orElse(0d);
|
||||
}
|
||||
|
||||
|
||||
public static DeviationSeverity getDeviationSeverityByIntegerDistance(Map<NodeAddress, List<RequestInfo>> map,
|
||||
RequestInfo requestInfo,
|
||||
InventoryItem inventoryItem,
|
||||
int warnTrigger,
|
||||
int alertTrigger) {
|
||||
DeviationSeverity deviationSeverity = DeviationSeverity.OK;
|
||||
Map<String, Integer> sameItemsByValue = new HashMap<>();
|
||||
map.values().stream()
|
||||
.filter(list -> !list.isEmpty())
|
||||
.map(list -> list.get(list.size() - 1)) // We use last item only
|
||||
.map(RequestInfo::getInventory)
|
||||
.filter(Objects::nonNull)
|
||||
.map(e -> e.get(inventoryItem))
|
||||
.forEach(e -> {
|
||||
sameItemsByValue.putIfAbsent(e, 0);
|
||||
int prev = sameItemsByValue.get(e);
|
||||
sameItemsByValue.put(e, prev + 1);
|
||||
});
|
||||
if (sameItemsByValue.size() > 1) {
|
||||
List<Tuple2<String, Integer>> sameItems = new ArrayList<>();
|
||||
sameItemsByValue.forEach((key, value) -> sameItems.add(new Tuple2<>(key, value)));
|
||||
sameItems.sort(Comparator.comparing(o -> o.second));
|
||||
Collections.reverse(sameItems);
|
||||
String majority = sameItems.get(0).first;
|
||||
Map<InventoryItem, String> inventory = requestInfo.getInventory();
|
||||
if (inventory != null) {
|
||||
String candidate = inventory.get(inventoryItem);
|
||||
if (!majority.equals(candidate)) {
|
||||
int majorityAsInt = Integer.parseInt(majority);
|
||||
int candidateAsInt = Integer.parseInt(candidate);
|
||||
int diff = Math.abs(majorityAsInt - candidateAsInt);
|
||||
if (diff >= alertTrigger) {
|
||||
deviationSeverity = DeviationSeverity.ALERT;
|
||||
} else if (diff >= warnTrigger) {
|
||||
deviationSeverity = DeviationSeverity.WARN;
|
||||
} else {
|
||||
deviationSeverity = DeviationSeverity.OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return deviationSeverity;
|
||||
}
|
||||
|
||||
public static DeviationSeverity getDeviationSeverityForHash(Map<NodeAddress, List<RequestInfo>> map,
|
||||
String daoStateChainHeightAsString,
|
||||
RequestInfo requestInfo,
|
||||
InventoryItem inventoryItem) {
|
||||
DeviationSeverity deviationSeverity = DeviationSeverity.OK;
|
||||
Map<String, Integer> sameHashesPerHashListByHash = new HashMap<>();
|
||||
map.values().stream()
|
||||
.filter(list -> !list.isEmpty())
|
||||
.map(list -> list.get(list.size() - 1)) // We use last item only
|
||||
.map(RequestInfo::getInventory)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(inventory -> inventory.get(InventoryItem.daoStateChainHeight).equals(daoStateChainHeightAsString))
|
||||
.map(inventory -> inventory.get(inventoryItem))
|
||||
.forEach(value -> {
|
||||
sameHashesPerHashListByHash.putIfAbsent(value, 0);
|
||||
int prev = sameHashesPerHashListByHash.get(value);
|
||||
sameHashesPerHashListByHash.put(value, prev + 1);
|
||||
});
|
||||
if (sameHashesPerHashListByHash.size() > 1) {
|
||||
List<Tuple2<String, Integer>> sameHashesPerHashList = new ArrayList<>();
|
||||
sameHashesPerHashListByHash.forEach((key, value) -> sameHashesPerHashList.add(new Tuple2<>(key, value)));
|
||||
sameHashesPerHashList.sort(Comparator.comparing(o -> o.second));
|
||||
Collections.reverse(sameHashesPerHashList);
|
||||
|
||||
// It could be that first and any following list entry has same number of hashes, but we ignore that as
|
||||
// it is reason enough to alert the operators in case not all hashes are the same.
|
||||
Map<InventoryItem, String> inventory = requestInfo.getInventory();
|
||||
if (inventory != null) {
|
||||
if (sameHashesPerHashList.get(0).first.equals(inventory.get(inventoryItem))) {
|
||||
// We are in the majority group.
|
||||
// We also set a warning to make sure the operators act quickly and to check if there are
|
||||
// more severe issues.
|
||||
deviationSeverity = DeviationSeverity.WARN;
|
||||
} else {
|
||||
deviationSeverity = DeviationSeverity.ALERT;
|
||||
}
|
||||
}
|
||||
}
|
||||
return deviationSeverity;
|
||||
}
|
||||
}
|
@ -17,8 +17,9 @@
|
||||
|
||||
package bisq.inventory;
|
||||
|
||||
import bisq.core.network.p2p.inventory.DeviationSeverity;
|
||||
import bisq.core.network.p2p.inventory.InventoryItem;
|
||||
import bisq.core.network.p2p.inventory.model.DeviationSeverity;
|
||||
import bisq.core.network.p2p.inventory.model.InventoryItem;
|
||||
import bisq.core.network.p2p.inventory.model.RequestInfo;
|
||||
import bisq.core.util.FormattingUtils;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
@ -28,6 +29,7 @@ import bisq.common.util.Utilities;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -48,9 +50,15 @@ public class InventoryWebServer {
|
||||
|
||||
private final List<NodeAddress> seedNodes;
|
||||
private final Map<String, String> operatorByNodeAddress = new HashMap<>();
|
||||
|
||||
private String html;
|
||||
private int requestCounter;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public InventoryWebServer(int port,
|
||||
List<NodeAddress> seedNodes,
|
||||
BufferedReader seedNodeFile) {
|
||||
@ -64,6 +72,11 @@ public class InventoryWebServer {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onNewRequestInfo(Map<NodeAddress, List<RequestInfo>> requestInfoListByNode,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
int requestCounter) {
|
||||
@ -75,6 +88,11 @@ public class InventoryWebServer {
|
||||
Spark.stop();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// HTML
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private String generateHtml(Map<NodeAddress, List<RequestInfo>> map,
|
||||
Map<InventoryItem, Double> averageValues) {
|
||||
StringBuilder html = new StringBuilder();
|
||||
@ -97,13 +115,13 @@ public class InventoryWebServer {
|
||||
List<RequestInfo> list = map.get(seedNode);
|
||||
int numResponses = list.size();
|
||||
RequestInfo requestInfo = list.get(numResponses - 1);
|
||||
html.append("<td>").append(getSeedNodeInfo(seedNode, requestInfo, averageValues)).append("</td>")
|
||||
html.append("<td>").append(getSeedNodeInfo(seedNode, requestInfo)).append("</td>")
|
||||
.append("<td>").append(getRequestInfo(requestInfo, numResponses)).append("</td>")
|
||||
.append("<td>").append(getDataInfo(requestInfo, averageValues, map)).append("</td>")
|
||||
.append("<td>").append(getDaoInfo(requestInfo, averageValues, map)).append("</td>")
|
||||
.append("<td>").append(getNetworkInfo(requestInfo, averageValues)).append("</td>");
|
||||
.append("<td>").append(getNetworkInfo(requestInfo, averageValues, map)).append("</td>");
|
||||
} else {
|
||||
html.append("<td>").append(getSeedNodeInfo(seedNode, null, averageValues)).append("</td>")
|
||||
html.append("<td>").append(getSeedNodeInfo(seedNode, null)).append("</td>")
|
||||
.append("<td>").append("n/a").append("</td>")
|
||||
.append("<td>").append("n/a").append("</td>")
|
||||
.append("<td>").append("n/a").append("</td>")
|
||||
@ -116,9 +134,13 @@ public class InventoryWebServer {
|
||||
return html.toString();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Sub sections
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private String getSeedNodeInfo(NodeAddress nodeAddress,
|
||||
@Nullable RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues) {
|
||||
@Nullable RequestInfo requestInfo) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
String operator = operatorByNodeAddress.get(nodeAddress.getFullAddress());
|
||||
@ -128,19 +150,21 @@ public class InventoryWebServer {
|
||||
sb.append("Node address: ").append(address).append("<br/>");
|
||||
|
||||
if (requestInfo != null) {
|
||||
addInventoryItem("Version: ", requestInfo, sb, InventoryItem.version);
|
||||
addInventoryItem("Memory used: ", requestInfo, averageValues, sb, InventoryItem.usedMemory,
|
||||
value -> Utilities.readableFileSize(Long.parseLong(value)));
|
||||
String jvmStartTimeString = addInventoryItem("Node started at: ",
|
||||
requestInfo,
|
||||
null,
|
||||
sb,
|
||||
InventoryItem.jvmStartTime,
|
||||
value -> new Date(Long.parseLong(value)).toString());
|
||||
sb.append("Version: ").append(requestInfo.getDisplayValue(InventoryItem.version)).append("<br/>");
|
||||
String memory = requestInfo.getValue(InventoryItem.usedMemory);
|
||||
String memoryString = memory != null ? Utilities.readableFileSize(Long.parseLong(memory)) : "n/a";
|
||||
sb.append("Memory used: ")
|
||||
.append(memoryString)
|
||||
.append("<br/>");
|
||||
|
||||
String duration = jvmStartTimeString != null ?
|
||||
FormattingUtils.formatDurationAsWords(
|
||||
System.currentTimeMillis() - Long.parseLong(jvmStartTimeString),
|
||||
String jvmStartTimeString = requestInfo.getValue(InventoryItem.jvmStartTime);
|
||||
long jvmStartTime = jvmStartTimeString != null ? Long.parseLong(jvmStartTimeString) : 0;
|
||||
sb.append("Node started at: ")
|
||||
.append(new Date(jvmStartTime).toString())
|
||||
.append("<br/>");
|
||||
|
||||
String duration = jvmStartTime > 0 ?
|
||||
FormattingUtils.formatDurationAsWords(System.currentTimeMillis() - jvmStartTime,
|
||||
true, true) :
|
||||
"n/a";
|
||||
sb.append("Run duration: ").append(duration).append("<br/>");
|
||||
@ -160,20 +184,20 @@ public class InventoryWebServer {
|
||||
sb.append("Number of responses: ").append(getColorTagByDeviationSeverity(deviationSeverity))
|
||||
.append(numResponses).append(CLOSE_TAG);
|
||||
|
||||
DeviationSeverity rrtDeviationSeverity = DeviationSeverity.OK;
|
||||
String rrtString = "n/a";
|
||||
if (requestInfo.getResponseTime() > 0) {
|
||||
long rrt = requestInfo.getResponseTime() - requestInfo.getRequestStartTime();
|
||||
DeviationSeverity rrtDeviationSeverity = DeviationSeverity.OK;
|
||||
if (rrt > 20_000) {
|
||||
rrtDeviationSeverity = DeviationSeverity.ALERT;
|
||||
} else if (rrt > 10_000) {
|
||||
rrtDeviationSeverity = DeviationSeverity.WARN;
|
||||
}
|
||||
String rrtString = MathUtils.roundDouble(rrt / 1000d, 3) + " sec";
|
||||
sb.append("Round trip time: ").append(getColorTagByDeviationSeverity(rrtDeviationSeverity))
|
||||
.append(rrtString).append(CLOSE_TAG);
|
||||
} else {
|
||||
sb.append("Round trip time: ").append("n/a").append(CLOSE_TAG);
|
||||
rrtString = MathUtils.roundDouble(rrt / 1000d, 3) + " sec";
|
||||
|
||||
}
|
||||
sb.append("Round trip time: ").append(getColorTagByDeviationSeverity(rrtDeviationSeverity))
|
||||
.append(rrtString).append(CLOSE_TAG);
|
||||
|
||||
Date requestStartTime = new Date(requestInfo.getRequestStartTime());
|
||||
sb.append("Requested at: ").append(requestStartTime).append("<br/>");
|
||||
@ -185,7 +209,7 @@ public class InventoryWebServer {
|
||||
|
||||
String errorMessage = requestInfo.getErrorMessage();
|
||||
if (errorMessage != null && !errorMessage.isEmpty()) {
|
||||
sb.append("Error message: ").append(getColorTagByDeviationSeverity(DeviationSeverity.WARN))
|
||||
sb.append("Error message: ").append(getColorTagByDeviationSeverity(DeviationSeverity.ALERT))
|
||||
.append(errorMessage).append(CLOSE_TAG);
|
||||
}
|
||||
return sb.toString();
|
||||
@ -195,33 +219,18 @@ public class InventoryWebServer {
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
Map<NodeAddress, List<RequestInfo>> map) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.OfferPayload);
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.MailboxStoragePayload);
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.TradeStatistics3);
|
||||
|
||||
DeviationSeverity deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map,
|
||||
requestInfo, InventoryItem.Alert, 1, 1);
|
||||
addInventoryItem(getTitle(InventoryItem.Alert), requestInfo, averageValues, sb, InventoryItem.Alert,
|
||||
null, deviationSeverity);
|
||||
sb.append(getLine(InventoryItem.OfferPayload, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.MailboxStoragePayload, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.TradeStatistics3, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.AccountAgeWitness, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.SignedWitness, requestInfo, averageValues, map.values()));
|
||||
|
||||
deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map,
|
||||
requestInfo, InventoryItem.Filter, 1, 1);
|
||||
addInventoryItem(getTitle(InventoryItem.Filter), requestInfo, averageValues, sb, InventoryItem.Filter,
|
||||
null, deviationSeverity);
|
||||
sb.append(getLine(InventoryItem.Alert, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.Filter, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.Mediator, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.RefundAgent, requestInfo, averageValues, map.values()));
|
||||
|
||||
|
||||
deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map,
|
||||
requestInfo, InventoryItem.Mediator, 1, 1);
|
||||
addInventoryItem(getTitle(InventoryItem.Mediator), requestInfo, averageValues, sb, InventoryItem.Mediator,
|
||||
null, deviationSeverity);
|
||||
|
||||
deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map,
|
||||
requestInfo, InventoryItem.RefundAgent, 1, 1);
|
||||
addInventoryItem(getTitle(InventoryItem.RefundAgent), requestInfo, averageValues, sb, InventoryItem.RefundAgent,
|
||||
null, deviationSeverity);
|
||||
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.AccountAgeWitness);
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.SignedWitness);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@ -229,183 +238,133 @@ public class InventoryWebServer {
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
Map<NodeAddress, List<RequestInfo>> map) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
DeviationSeverity deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map,
|
||||
requestInfo, InventoryItem.numBsqBlocks, 1, 3);
|
||||
addInventoryItem("Number of BSQ blocks: ", requestInfo, averageValues, sb, InventoryItem.numBsqBlocks,
|
||||
null, deviationSeverity);
|
||||
|
||||
deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map,
|
||||
requestInfo, InventoryItem.TempProposalPayload, 3, 5);
|
||||
addInventoryItem(getTitle(InventoryItem.TempProposalPayload), requestInfo, averageValues, sb, InventoryItem.TempProposalPayload,
|
||||
null, deviationSeverity);
|
||||
sb.append(getLine("Number of BSQ blocks: ", InventoryItem.numBsqBlocks, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.TempProposalPayload, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.ProposalPayload, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.BlindVotePayload, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine("DAO state block height: ", InventoryItem.daoStateChainHeight, requestInfo, averageValues, map.values()));
|
||||
|
||||
deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map,
|
||||
requestInfo, InventoryItem.ProposalPayload, 1, 2);
|
||||
addInventoryItem(getTitle(InventoryItem.ProposalPayload), requestInfo, averageValues, sb, InventoryItem.ProposalPayload,
|
||||
null, deviationSeverity);
|
||||
String daoStateChainHeight = null;
|
||||
if (requestInfo.getInventory() != null && requestInfo.getInventory().containsKey(InventoryItem.daoStateChainHeight)) {
|
||||
daoStateChainHeight = requestInfo.getInventory().get(InventoryItem.daoStateChainHeight);
|
||||
}
|
||||
|
||||
deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map,
|
||||
requestInfo, InventoryItem.BlindVotePayload, 1, 2);
|
||||
addInventoryItem(getTitle(InventoryItem.BlindVotePayload), requestInfo, averageValues, sb, InventoryItem.BlindVotePayload,
|
||||
null, deviationSeverity);
|
||||
|
||||
deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map,
|
||||
requestInfo, InventoryItem.daoStateChainHeight, 1, 3);
|
||||
String daoStateChainHeightAsString = addInventoryItem("DAO state block height: ", requestInfo,
|
||||
averageValues, sb, InventoryItem.daoStateChainHeight, null, deviationSeverity);
|
||||
|
||||
DeviationSeverity daoStateHashDeviationSeverity = InventoryUtil.getDeviationSeverityForHash(map,
|
||||
daoStateChainHeightAsString,
|
||||
requestInfo,
|
||||
InventoryItem.daoStateHash);
|
||||
addInventoryItem("DAO state hash: ", requestInfo, null, sb,
|
||||
InventoryItem.daoStateHash, null, daoStateHashDeviationSeverity);
|
||||
sb.append(getLine("DAO state hash: ", InventoryItem.daoStateHash, requestInfo, averageValues, map.values(), daoStateChainHeight));
|
||||
|
||||
// The hash for proposal changes only at first block of blind vote phase but as we do not want to initialize the
|
||||
// dao domain we cannot check that. But we also don't need that as we can just compare that all hashes at all
|
||||
// blocks from all seeds are the same. Same for blindVoteHash.
|
||||
|
||||
DeviationSeverity proposalHashDeviationSeverity = InventoryUtil.getDeviationSeverityForHash(map,
|
||||
daoStateChainHeightAsString,
|
||||
requestInfo,
|
||||
InventoryItem.proposalHash);
|
||||
addInventoryItem("Proposal state hash: ", requestInfo, null, sb,
|
||||
InventoryItem.proposalHash, null, proposalHashDeviationSeverity);
|
||||
|
||||
DeviationSeverity blindVoteHashDeviationSeverity = InventoryUtil.getDeviationSeverityForHash(map,
|
||||
daoStateChainHeightAsString,
|
||||
requestInfo,
|
||||
InventoryItem.blindVoteHash);
|
||||
addInventoryItem("Blind vote state hash: ", requestInfo, null, sb,
|
||||
InventoryItem.blindVoteHash, null, blindVoteHashDeviationSeverity);
|
||||
sb.append(getLine("Proposal state hash: ", InventoryItem.proposalHash, requestInfo, averageValues, map.values(), daoStateChainHeight));
|
||||
sb.append(getLine("Blind vote state hash: ", InventoryItem.blindVoteHash, requestInfo, averageValues, map.values(), daoStateChainHeight));
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String getNetworkInfo(RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues) {
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
Map<NodeAddress, List<RequestInfo>> map) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
addInventoryItem("Max. connections: ", requestInfo, averageValues, sb, InventoryItem.maxConnections);
|
||||
addInventoryItem("Number of connections: ", requestInfo, averageValues, sb, InventoryItem.numConnections);
|
||||
addInventoryItem("Peak number of connections: ", requestInfo, averageValues, sb, InventoryItem.peakNumConnections);
|
||||
addInventoryItem("Number of 'All connections lost' events: ", requestInfo, averageValues, sb, InventoryItem.numAllConnectionsLostEvents);
|
||||
addInventoryItem("Sent messages/sec: ", requestInfo, averageValues, sb, InventoryItem.sentMessagesPerSec,
|
||||
value -> String.valueOf(MathUtils.roundDouble(Double.parseDouble(value), 2)));
|
||||
addInventoryItem("Received messages/sec: ", requestInfo, averageValues, sb, InventoryItem.receivedMessagesPerSec,
|
||||
value -> String.valueOf(MathUtils.roundDouble(Double.parseDouble(value), 2)));
|
||||
addInventoryItem("Sent kB/sec: ", requestInfo, averageValues, sb, InventoryItem.sentBytesPerSec,
|
||||
value -> String.valueOf(MathUtils.roundDouble(Double.parseDouble(value) / 1024, 2)));
|
||||
addInventoryItem("Received kB/sec: ", requestInfo, averageValues, sb, InventoryItem.receivedBytesPerSec,
|
||||
value -> String.valueOf(MathUtils.roundDouble(Double.parseDouble(value) / 1024, 2)));
|
||||
addInventoryItem("Sent data: ", requestInfo, averageValues, sb, InventoryItem.sentBytes,
|
||||
value -> Utilities.readableFileSize(Long.parseLong(value)));
|
||||
addInventoryItem("Received data: ", requestInfo, averageValues, sb, InventoryItem.receivedBytes,
|
||||
value -> Utilities.readableFileSize(Long.parseLong(value)));
|
||||
|
||||
sb.append(getLine("Max. connections: ", InventoryItem.maxConnections, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine("Number of connections: ", InventoryItem.numConnections, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine("Peak number of connections: ", InventoryItem.peakNumConnections, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine("Number of 'All connections lost' events: ", InventoryItem.numAllConnectionsLostEvents, requestInfo, averageValues, map.values()));
|
||||
|
||||
sb.append(getLine("Sent messages/sec: ", InventoryItem.sentMessagesPerSec, requestInfo,
|
||||
averageValues, map.values(), null, this::getRounded));
|
||||
sb.append(getLine("Received messages/sec: ", InventoryItem.receivedMessagesPerSec, requestInfo,
|
||||
averageValues, map.values(), null, this::getRounded));
|
||||
sb.append(getLine("Sent kB/sec: ", InventoryItem.sentBytesPerSec, requestInfo,
|
||||
averageValues, map.values(), null, this::getRounded));
|
||||
sb.append(getLine("Received kB/sec: ", InventoryItem.receivedBytesPerSec, requestInfo,
|
||||
averageValues, map.values(), null, this::getRounded));
|
||||
sb.append(getLine("Sent data: ", InventoryItem.sentBytes, requestInfo,
|
||||
averageValues, map.values(), null, value -> Utilities.readableFileSize(Long.parseLong(value))));
|
||||
sb.append(getLine("Received data: ", InventoryItem.receivedBytes, requestInfo,
|
||||
averageValues, map.values(), null, value -> Utilities.readableFileSize(Long.parseLong(value))));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String addInventoryItem(RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
StringBuilder sb,
|
||||
InventoryItem inventoryItem) {
|
||||
return addInventoryItem(getTitle(inventoryItem),
|
||||
requestInfo,
|
||||
averageValues,
|
||||
sb,
|
||||
inventoryItem);
|
||||
}
|
||||
|
||||
private String addInventoryItem(String title,
|
||||
RequestInfo requestInfo,
|
||||
StringBuilder sb,
|
||||
InventoryItem inventoryItem) {
|
||||
return addInventoryItem(title,
|
||||
requestInfo,
|
||||
null,
|
||||
sb,
|
||||
inventoryItem);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private String addInventoryItem(String title,
|
||||
RequestInfo requestInfo,
|
||||
@Nullable Map<InventoryItem, Double> averageValues,
|
||||
StringBuilder sb,
|
||||
InventoryItem inventoryItem) {
|
||||
return addInventoryItem(title,
|
||||
requestInfo,
|
||||
averageValues,
|
||||
sb,
|
||||
private String getLine(InventoryItem inventoryItem,
|
||||
RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
Collection<List<RequestInfo>> collection) {
|
||||
return getLine(getTitle(inventoryItem),
|
||||
inventoryItem,
|
||||
requestInfo,
|
||||
averageValues,
|
||||
collection);
|
||||
}
|
||||
|
||||
private String getLine(String title,
|
||||
InventoryItem inventoryItem,
|
||||
RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
Collection<List<RequestInfo>> collection) {
|
||||
return getLine(title,
|
||||
inventoryItem,
|
||||
requestInfo,
|
||||
averageValues,
|
||||
collection,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
private String addInventoryItem(String title,
|
||||
RequestInfo requestInfo,
|
||||
@Nullable Map<InventoryItem, Double> averageValues,
|
||||
StringBuilder sb,
|
||||
InventoryItem inventoryItem,
|
||||
@Nullable Function<String, String> formatter) {
|
||||
return addInventoryItem(title,
|
||||
private String getLine(String title,
|
||||
InventoryItem inventoryItem,
|
||||
RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
Collection<List<RequestInfo>> collection,
|
||||
@Nullable String daoStateChainHeight) {
|
||||
return getLine(title,
|
||||
inventoryItem,
|
||||
requestInfo,
|
||||
averageValues,
|
||||
sb,
|
||||
inventoryItem,
|
||||
formatter,
|
||||
collection,
|
||||
daoStateChainHeight,
|
||||
null);
|
||||
}
|
||||
|
||||
private String addInventoryItem(String title,
|
||||
RequestInfo requestInfo,
|
||||
@Nullable Map<InventoryItem, Double> averageValues,
|
||||
StringBuilder sb,
|
||||
InventoryItem inventoryItem,
|
||||
@Nullable Function<String, String> formatter,
|
||||
@Nullable DeviationSeverity deviationSeverity) {
|
||||
String valueAsString = null;
|
||||
String displayString = "n/a";
|
||||
String deviationAsString = "";
|
||||
String colorTag = getColorTagByDeviationSeverity(DeviationSeverity.OK);
|
||||
Map<InventoryItem, String> inventory = requestInfo.getInventory();
|
||||
if (inventory != null && inventory.containsKey(inventoryItem)) {
|
||||
valueAsString = inventory.get(inventoryItem);
|
||||
if (averageValues != null && averageValues.containsKey(inventoryItem)) {
|
||||
double average = averageValues.get(inventoryItem);
|
||||
double deviation = 0;
|
||||
if (inventoryItem.getType().equals(Integer.class)) {
|
||||
int value = Integer.parseInt(valueAsString);
|
||||
deviation = value / average;
|
||||
} else if (inventoryItem.getType().equals(Long.class)) {
|
||||
long value = Long.parseLong(valueAsString);
|
||||
deviation = value / average;
|
||||
} else if (inventoryItem.getType().equals(Double.class)) {
|
||||
double value = Double.parseDouble(valueAsString);
|
||||
deviation = value / average;
|
||||
}
|
||||
private String getLine(String title,
|
||||
InventoryItem inventoryItem,
|
||||
RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
Collection<List<RequestInfo>> collection,
|
||||
@Nullable String daoStateChainHeight,
|
||||
@Nullable Function<String, String> formatter) {
|
||||
String displayValue = requestInfo.getDisplayValue(inventoryItem);
|
||||
String value = requestInfo.getValue(inventoryItem);
|
||||
if (formatter != null && value != null) {
|
||||
displayValue = formatter.apply(value);
|
||||
}
|
||||
Double deviation = inventoryItem.getDeviation(averageValues, value);
|
||||
DeviationSeverity deviationSeverity = inventoryItem.getDeviationSeverity(deviation, collection, value, daoStateChainHeight);
|
||||
return title +
|
||||
getColorTagByDeviationSeverity(deviationSeverity) +
|
||||
displayValue +
|
||||
getDeviationAsPercentString(deviation) +
|
||||
CLOSE_TAG;
|
||||
}
|
||||
|
||||
if (!inventoryItem.getType().equals(String.class)) {
|
||||
colorTag = getColorTagByDeviationSeverity(inventoryItem.getDeviationSeverity(deviation));
|
||||
deviationAsString = " (" + MathUtils.roundDouble(100 * deviation, 2) + " %)";
|
||||
}
|
||||
}
|
||||
|
||||
if (deviationSeverity != null) {
|
||||
colorTag = getColorTagByDeviationSeverity(deviationSeverity);
|
||||
}
|
||||
|
||||
// We only do formatting if we have any value
|
||||
if (formatter != null) {
|
||||
displayString = formatter.apply(valueAsString);
|
||||
} else {
|
||||
displayString = valueAsString;
|
||||
}
|
||||
private String getDeviationAsPercentString(@Nullable Double deviation) {
|
||||
if (deviation == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
sb.append(title).append(colorTag).append(displayString).append(deviationAsString).append(CLOSE_TAG);
|
||||
return valueAsString;
|
||||
return " (" + MathUtils.roundDouble(100 * deviation, 2) + " %)";
|
||||
}
|
||||
|
||||
private String getColorTagByDeviationSeverity(DeviationSeverity deviationSeverity) {
|
||||
private String getColorTagByDeviationSeverity(@Nullable DeviationSeverity deviationSeverity) {
|
||||
if (deviationSeverity == null) {
|
||||
return "<font color=\"black\">";
|
||||
}
|
||||
|
||||
switch (deviationSeverity) {
|
||||
case WARN:
|
||||
return "<font color=\"blue\">";
|
||||
@ -417,6 +376,14 @@ public class InventoryWebServer {
|
||||
}
|
||||
}
|
||||
|
||||
private String getTitle(InventoryItem inventoryItem) {
|
||||
return "Number of " + inventoryItem.getKey() + ": ";
|
||||
}
|
||||
|
||||
private String getRounded(String value) {
|
||||
return String.valueOf(MathUtils.roundDouble(Double.parseDouble(value), 2));
|
||||
}
|
||||
|
||||
private void setupOperatorMap(BufferedReader seedNodeFile) {
|
||||
seedNodeFile.lines().forEach(line -> {
|
||||
if (!line.startsWith("#")) {
|
||||
@ -427,8 +394,4 @@ public class InventoryWebServer {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String getTitle(InventoryItem inventoryItem) {
|
||||
return "Number of " + inventoryItem.getKey() + ": ";
|
||||
}
|
||||
}
|
||||
|
@ -41,9 +41,6 @@ public class SeedNode {
|
||||
appSetup.start();
|
||||
|
||||
getInventoryRequestHandler = injector.getInstance(GetInventoryRequestHandler.class);
|
||||
|
||||
// TODO dev test key
|
||||
getInventoryRequestHandler.addPermittedRequestersPubKey("035243719433b154a86329fd23a8389bee6369698be809f7e27f003f5b5aa9eb42");
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
|
Loading…
Reference in New Issue
Block a user