(rest) - fixed editing link api (gui) - added link update in link details panel

This commit is contained in:
Blake J. Harnden 2018-09-24 09:50:26 -07:00
parent d2b459e503
commit a852a60fd4
12 changed files with 161 additions and 43 deletions

View file

@ -63,7 +63,7 @@ public class Controller implements Initializable {
private NetworkGraph networkGraph = new NetworkGraph(this); private NetworkGraph networkGraph = new NetworkGraph(this);
private AnnotationToolbar annotationToolbar = new AnnotationToolbar(networkGraph); private AnnotationToolbar annotationToolbar = new AnnotationToolbar(networkGraph);
private NodeDetails nodeDetails = new NodeDetails(this); private NodeDetails nodeDetails = new NodeDetails(this);
private LinkDetails linkDetails = new LinkDetails(networkGraph); private LinkDetails linkDetails = new LinkDetails(this);
private GraphToolbar graphToolbar = new GraphToolbar(this); private GraphToolbar graphToolbar = new GraphToolbar(this);
private MobilityPlayer mobilityPlayer = new MobilityPlayer(this); private MobilityPlayer mobilityPlayer = new MobilityPlayer(this);

View file

@ -64,6 +64,8 @@ public interface ICoreClient {
boolean createLink(CoreLink link) throws IOException; boolean createLink(CoreLink link) throws IOException;
boolean editLink(CoreLink link) throws IOException;
boolean createHook(Hook hook) throws IOException; boolean createHook(Hook hook) throws IOException;
GetHooks getHooks() throws IOException; GetHooks getHooks() throws IOException;

View file

@ -345,6 +345,12 @@ public class CoreRestClient implements ICoreClient {
return WebUtils.postJson(url, link); return WebUtils.postJson(url, link);
} }
@Override
public boolean editLink(CoreLink link) throws IOException {
String url = getUrl(String.format("sessions/%s/links", sessionId));
return WebUtils.putJson(url, link);
}
@Override @Override
public boolean createHook(Hook hook) throws IOException { public boolean createHook(Hook hook) throws IOException {
String url = getUrl(String.format("sessions/%s/hooks", sessionId)); String url = getUrl(String.format("sessions/%s/hooks", sessionId));

View file

@ -12,6 +12,7 @@ import lombok.NoArgsConstructor;
public class CoreLink { public class CoreLink {
@EqualsAndHashCode.Include @EqualsAndHashCode.Include
private Integer id; private Integer id;
@JsonIgnore
private Float weight = 1.0f; private Float weight = 1.0f;
@JsonIgnore @JsonIgnore

View file

@ -1,34 +1,49 @@
package com.core.ui; package com.core.ui;
import com.core.Controller;
import com.core.client.ICoreClient;
import com.core.data.CoreInterface; import com.core.data.CoreInterface;
import com.core.data.CoreLink; import com.core.data.CoreLink;
import com.core.data.CoreLinkOptions;
import com.core.data.CoreNode; import com.core.data.CoreNode;
import com.core.graph.NetworkGraph; import com.core.graph.NetworkGraph;
import com.core.ui.textfields.DoubleFilter;
import com.core.utils.FxmlUtils; import com.core.utils.FxmlUtils;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTextField; import com.jfoenix.controls.JFXTextField;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.geometry.Orientation; import javafx.geometry.Orientation;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane; import javafx.scene.control.ScrollPane;
import javafx.scene.control.Separator; import javafx.scene.control.Separator;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.util.converter.DoubleStringConverter;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.io.IOException;
public class LinkDetails extends ScrollPane { public class LinkDetails extends ScrollPane {
private static final Logger logger = LogManager.getLogger(); private static final Logger logger = LogManager.getLogger();
private static final int START_INDEX = 1; private static final int START_INDEX = 1;
private NetworkGraph graph; private final Controller controller;
private int index = START_INDEX; private int index = START_INDEX;
@FXML private GridPane gridPane; @FXML private GridPane gridPane;
public LinkDetails(NetworkGraph graph) { public LinkDetails(Controller controller) {
this.graph = graph; this.controller = controller;
FxmlUtils.loadRootController(this, "/fxml/link_details.fxml"); FxmlUtils.loadRootController(this, "/fxml/link_details.fxml");
setPrefWidth(400);
} }
public void setLink(CoreLink link) { public void setLink(CoreLink link) {
NetworkGraph graph = controller.getNetworkGraph();
ICoreClient coreClient = controller.getCoreClient();
clear(); clear();
addSeparator(); addSeparator();
@ -38,19 +53,68 @@ public class LinkDetails extends ScrollPane {
if (interfaceOne != null) { if (interfaceOne != null) {
addInterface(interfaceOne); addInterface(interfaceOne);
} }
addSeparator();
addSeparator();
CoreNode nodeTwo = graph.getVertex(link.getNodeTwo()); CoreNode nodeTwo = graph.getVertex(link.getNodeTwo());
CoreInterface interfaceTwo = link.getInterfaceTwo(); CoreInterface interfaceTwo = link.getInterfaceTwo();
addLabel(nodeTwo.getName()); addLabel(nodeTwo.getName());
if (interfaceTwo != null) { if (interfaceTwo != null) {
addInterface(interfaceTwo); addInterface(interfaceTwo);
} }
addSeparator();
addLabel("Properties");
JFXTextField bandwidthField = addRow("Bandwidth (bps)", link.getOptions().getBandwidth());
JFXTextField delayField = addRow("Delay (us)", link.getOptions().getDelay());
JFXTextField jitterField = addRow("Jitter (us)", link.getOptions().getJitter());
JFXTextField lossField = addRow("Loss (%)", link.getOptions().getPer());
JFXTextField dupsField = addRow("Duplicate (%)", link.getOptions().getDup());
addButton("Update", event -> {
CoreLinkOptions options = link.getOptions();
options.setBandwidth(getDouble(bandwidthField));
options.setDelay(getDouble(delayField));
options.setJitter(getDouble(jitterField));
options.setPer(getDouble(lossField));
options.setDup(getDouble(dupsField));
if (coreClient.isRunning()) {
try {
coreClient.editLink(link);
Toast.info("Link updated!");
} catch (IOException ex) {
Toast.error("Failure to update link", ex);
}
}
});
}
private Double getDouble(JFXTextField textField) {
if (textField.getText() == null) {
return null;
}
Double value = null;
try {
logger.info("double field text: {}", textField.getText());
value = Double.parseDouble(textField.getText());
} catch (NumberFormatException ex) {
logger.error("error getting double value", ex);
}
return value;
}
private void addButton(String text, EventHandler<ActionEvent> handler) {
JFXButton button = new JFXButton(text);
button.getStyleClass().add("core-button");
button.setMaxWidth(Double.MAX_VALUE);
button.setOnAction(handler);
gridPane.add(button, 0, index++, 2, 1);
GridPane.setMargin(button, new Insets(10, 0, 0, 0));
} }
private void addLabel(String text) { private void addLabel(String text) {
Label label = new Label(text); Label label = new Label(text);
label.getStyleClass().add("details-title"); label.getStyleClass().add("details-label");
gridPane.add(label, 0, index++, 2, 1); gridPane.add(label, 0, index++, 2, 1);
} }
@ -61,32 +125,48 @@ public class LinkDetails extends ScrollPane {
} }
private void addInterface(CoreInterface coreInterface) { private void addInterface(CoreInterface coreInterface) {
addRow("Interface", coreInterface.getName()); addRow("Interface", coreInterface.getName(), true);
if (coreInterface.getMac() != null) { if (coreInterface.getMac() != null) {
addRow("MAC", coreInterface.getMac()); addRow("MAC", coreInterface.getMac(), true);
} }
addIp4Address(coreInterface.getIp4(), coreInterface.getIp4Mask()); addIp4Address(coreInterface.getIp4(), coreInterface.getIp4Mask());
addIp6Address(coreInterface.getIp6(), coreInterface.getIp6Mask()); addIp6Address(coreInterface.getIp6(), coreInterface.getIp6Mask());
} }
private void addRow(String labelText, String value) { private void addRow(String labelText, String value, boolean disabled) {
Label label = new Label(labelText); Label label = new Label(labelText);
JFXTextField textField = new JFXTextField(value); JFXTextField textField = new JFXTextField(value);
textField.setDisable(disabled);
gridPane.addRow(index++, label, textField); gridPane.addRow(index++, label, textField);
} }
private JFXTextField addRow(String labelText, Double value) {
Label label = new Label(labelText);
String doubleString = null;
if (value != null) {
doubleString = value.toString();
}
JFXTextField textField = new JFXTextField();
TextFormatter<Double> formatter = new TextFormatter<>(
new DoubleStringConverter(), null, new DoubleFilter());
textField.setTextFormatter(formatter);
textField.setText(doubleString);
gridPane.addRow(index++, label, textField);
return textField;
}
private void addIp4Address(String ip, Integer mask) { private void addIp4Address(String ip, Integer mask) {
if (ip == null) { if (ip == null) {
return; return;
} }
addRow("IP4", String.format("%s/%s", ip, mask)); addRow("IP4", String.format("%s/%s", ip, mask), true);
} }
private void addIp6Address(String ip, String mask) { private void addIp6Address(String ip, String mask) {
if (ip == null) { if (ip == null) {
return; return;
} }
addRow("IP6", String.format("%s/%s", ip, mask)); addRow("IP6", String.format("%s/%s", ip, mask), true);
} }
private void clear() { private void clear() {

View file

@ -26,6 +26,7 @@ public class NodeDetails extends ScrollPane {
private static final Logger logger = LogManager.getLogger(); private static final Logger logger = LogManager.getLogger();
private static final int START_INDEX = 1; private static final int START_INDEX = 1;
private final Controller controller; private final Controller controller;
@FXML private Label title;
@FXML private ScrollPane scrollPane; @FXML private ScrollPane scrollPane;
@FXML private GridPane gridPane; @FXML private GridPane gridPane;
private int index = START_INDEX; private int index = START_INDEX;
@ -33,32 +34,36 @@ public class NodeDetails extends ScrollPane {
public NodeDetails(Controller controller) { public NodeDetails(Controller controller) {
this.controller = controller; this.controller = controller;
FxmlUtils.loadRootController(this, "/fxml/node_details.fxml"); FxmlUtils.loadRootController(this, "/fxml/node_details.fxml");
setPrefWidth(500); setPrefWidth(400);
} }
public void setNode(CoreNode node) { public void setNode(CoreNode node) {
clear(); clear();
addSeparator(); title.setText(node.getName());
addRow("Name", node.getName()); addSeparator();
addLabel("Properties");
if (node.getType() == NodeType.DEFAULT) { if (node.getType() == NodeType.DEFAULT) {
addRow("Model", node.getModel()); addRow("Model", node.getModel(), true);
} else { } else {
addRow("Type", node.getNodeType().getDisplay()); addRow("Type", node.getNodeType().getDisplay(), true);
} }
if (node.getEmane() != null) { if (node.getEmane() != null) {
addRow("EMANE", node.getEmane()); addRow("EMANE", node.getEmane(), true);
} }
addSeparator(); addSeparator();
addLabel("Position");
if (node.getPosition().getX() != null) { if (node.getPosition().getX() != null) {
addRow("X", node.getPosition().getX().toString()); addRow("X", node.getPosition().getX().toString(), true);
} }
if (node.getPosition().getY() != null) { if (node.getPosition().getY() != null) {
addRow("Y", node.getPosition().getY().toString()); addRow("Y", node.getPosition().getY().toString(), true);
} }
addSeparator();
addLabel("Interfaces");
for (CoreLink link : controller.getNetworkGraph().getGraph().getIncidentEdges(node)) { for (CoreLink link : controller.getNetworkGraph().getGraph().getIncidentEdges(node)) {
CoreNode linkedNode; CoreNode linkedNode;
CoreInterface coreInterface; CoreInterface coreInterface;
@ -121,7 +126,7 @@ public class NodeDetails extends ScrollPane {
private void addLabel(String text) { private void addLabel(String text) {
Label label = new Label(text); Label label = new Label(text);
label.getStyleClass().add("details-title"); label.getStyleClass().add("details-label");
gridPane.add(label, 0, index++, 2, 1); gridPane.add(label, 0, index++, 2, 1);
} }
@ -132,18 +137,19 @@ public class NodeDetails extends ScrollPane {
} }
private void addInterface(CoreInterface coreInterface, CoreNode linkedNode) { private void addInterface(CoreInterface coreInterface, CoreNode linkedNode) {
addRow("Linked To", linkedNode.getName()); addRow("Linked To", linkedNode.getName(), true);
addRow("Interface", coreInterface.getName()); addRow("Interface", coreInterface.getName(), true);
if (coreInterface.getMac() != null) { if (coreInterface.getMac() != null) {
addRow("MAC", coreInterface.getMac()); addRow("MAC", coreInterface.getMac(), true);
} }
addIp4Address(coreInterface.getIp4(), coreInterface.getIp4Mask()); addIp4Address(coreInterface.getIp4(), coreInterface.getIp4Mask());
addIp6Address(coreInterface.getIp6(), coreInterface.getIp6Mask()); addIp6Address(coreInterface.getIp6(), coreInterface.getIp6Mask());
} }
private void addRow(String labelText, String value) { private void addRow(String labelText, String value, boolean disabled) {
Label label = new Label(labelText); Label label = new Label(labelText);
JFXTextField textField = new JFXTextField(value); JFXTextField textField = new JFXTextField(value);
textField.setDisable(disabled);
gridPane.addRow(index++, label, textField); gridPane.addRow(index++, label, textField);
} }
@ -151,14 +157,14 @@ public class NodeDetails extends ScrollPane {
if (ip == null) { if (ip == null) {
return; return;
} }
addRow("IP4", String.format("%s/%s", ip, mask)); addRow("IP4", String.format("%s/%s", ip, mask), true);
} }
private void addIp6Address(String ip, String mask) { private void addIp6Address(String ip, String mask) {
if (ip == null) { if (ip == null) {
return; return;
} }
addRow("IP6", String.format("%s/%s", ip, mask)); addRow("IP6", String.format("%s/%s", ip, mask), true);
} }
private void clear() { private void clear() {

View file

@ -0,0 +1,15 @@
package com.core.ui.textfields;
import javafx.scene.control.TextFormatter;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
public class DoubleFilter implements UnaryOperator<TextFormatter.Change> {
private static final Pattern DIGIT_PATTERN = Pattern.compile("\\d*\\.?\\d*");
@Override
public TextFormatter.Change apply(TextFormatter.Change change) {
return DIGIT_PATTERN.matcher(change.getText()).matches() ? change : null;
}
}

View file

@ -12,6 +12,15 @@
} }
.details-label { .details-label {
-fx-background-color: slategrey;
-fx-pref-width: Infinity;
-fx-text-fill: white;
-fx-alignment: center;
-fx-padding: 5;
-fx-background-radius: 5;
}
.details-title {
-fx-background-color: olivedrab; -fx-background-color: olivedrab;
-fx-pref-width: Infinity; -fx-pref-width: Infinity;
-fx-text-fill: white; -fx-text-fill: white;
@ -20,13 +29,6 @@
-fx-background-radius: 5; -fx-background-radius: 5;
} }
.details-title {
-fx-pref-width: Infinity;
-fx-alignment: center;
-fx-padding: 10;
-fx-font-size: 17;
}
.code { .code {
-fx-text-fill: lime; -fx-text-fill: lime;
-fx-font-family: monospaced; -fx-font-family: monospaced;

View file

@ -23,7 +23,7 @@
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding> </padding>
<children> <children>
<Label styleClass="details-label" text="Link Details" GridPane.columnSpan="2147483647"> <Label styleClass="details-title" text="Link Details" GridPane.columnSpan="2147483647">
<font> <font>
<Font name="System Bold" size="17.0" /> <Font name="System Bold" size="17.0" />
</font> </font>

View file

@ -22,7 +22,7 @@
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding> </padding>
<children> <children>
<Label styleClass="details-label" text="Node Details" GridPane.columnSpan="2147483647"> <Label fx:id="title" styleClass="details-title" text="Node Details" GridPane.columnSpan="2147483647">
<font> <font>
<Font name="System Bold" size="17.0" /> <Font name="System Bold" size="17.0" />
</font> </font>

View file

@ -403,6 +403,8 @@ class EmuSession(Session):
else: else:
if not node_one and not node_two: if not node_one and not node_two:
if net_one and net_two: if net_one and net_two:
logger.debug("updating link between two networks")
# modify link between nets # modify link between nets
interface = net_one.getlinknetif(net_two) interface = net_one.getlinknetif(net_two)
upstream = False upstream = False
@ -431,20 +433,24 @@ class EmuSession(Session):
else: else:
raise ValueError("modify link for unknown nodes") raise ValueError("modify link for unknown nodes")
elif not node_one: elif not node_one:
logger.debug("updating link, node one is a network")
# node1 = layer 2node, node2 = layer3 node # node1 = layer 2node, node2 = layer3 node
interface = node_two.netif(interface_two_id, net_one) interface = node_two.netif(interface_two_id, net_one)
link_config(net_one, interface, link_options) link_config(net_one, interface, link_options)
elif not node_two: elif not node_two:
logger.debug("updating link, node two is a network")
# node2 = layer 2node, node1 = layer3 node # node2 = layer 2node, node1 = layer3 node
interface = node_one.netif(interface_one_id, net_one) interface = node_one.netif(interface_one_id, net_one)
link_config(net_one, interface, link_options) link_config(net_one, interface, link_options)
else: else:
logger.debug("updating link between non-network nodes")
common_networks = node_one.commonnets(node_two) common_networks = node_one.commonnets(node_two)
if not common_networks: if not common_networks:
raise ValueError("no common network found") raise ValueError("no common network found")
for net_one, interface_one, interface_two in common_networks: for net_one, interface_one, interface_two in common_networks:
if interface_one_id is not None and interface_one_id != node_one.getifindex(interface_one): if interface_one_id is not None and interface_one_id != node_one.getifindex(interface_one):
logger.warn("skipping link for interface one(%s) - %s", interface_one_id, interface_one)
continue continue
link_config(net_one, interface_one, link_options, interface_two=interface_two) link_config(net_one, interface_one, link_options, interface_two=interface_two)

View file

@ -81,12 +81,12 @@ def edit_link(session_id):
data = request.get_json() data = request.get_json()
node_one = data.get("node_one") node_one = data["node_one"]
node_two = data.get("node_two") node_two = data["node_two"]
interface_one = data.get("interface_one") interface_one_id = data["interface_one"]["id"]
interface_two = data.get("interface_two") interface_two_id = data["interface_two"]["id"]
options_data = data.get("options") options_data = data["options"]
link_options = LinkOptions() link_options = LinkOptions()
if options_data: if options_data:
link_options.delay = options_data.get("delay") link_options.delay = options_data.get("delay")
@ -102,7 +102,7 @@ def edit_link(session_id):
link_options.key = options_data.get("key") link_options.key = options_data.get("key")
link_options.opaque = options_data.get("opaque") link_options.opaque = options_data.get("opaque")
session.update_link(node_one, node_two, link_options, interface_one, interface_two) session.update_link(node_one, node_two, interface_one_id, interface_two_id, link_options)
return jsonify(), 201 return jsonify(), 201