ONOS-6730: Topo View i18n:
- Deprecate non-localized PropertyPanel.addProp() methods.
- Add modify*LinkDetails() methods to UiTopoOverlay class.
- Augment TVMH.RequestDetails to handle link details requests.
- Refactor deviceDetails() to allow piecemeal construction of the Properties Panel.
This allows us to include (or not) the location properties (geo/grid).
- Refactor hostDetails() for piecemeal construction of Properties Panel.
- Add edgeLinkDetails() and infraLinkDetails() methods.
- No lat/long suppression now done server-side. Check for trailing separator.
- Augment requestDetails() to format link details requests.
- Added lion.getSafe(Enum<?>) method.
- Added DeviceEnums and LinkEnums resource bundles.
Change-Id: Ibbd113a7d5ef73765cd10aed0fb7ea8efbaa16c5
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
index 3cbce06..a359964 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
@@ -80,6 +80,7 @@
import static java.util.concurrent.Executors.newSingleThreadExecutor;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED;
+import static org.onosproject.net.ConnectPoint.deviceConnectPoint;
import static org.onosproject.net.DeviceId.deviceId;
import static org.onosproject.net.HostId.hostId;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
@@ -137,10 +138,16 @@
private static final String EXTRA = "extra";
private static final String ID = "id";
private static final String KEY = "key";
+ private static final String IS_EDGE_LINK = "isEdgeLink";
+ private static final String SOURCE_ID = "sourceId";
+ private static final String SOURCE_PORT = "sourcePort";
+ private static final String TARGET_ID = "targetId";
+ private static final String TARGET_PORT = "targetPort";
private static final String APP_ID = "appId";
private static final String APP_NAME = "appName";
private static final String DEVICE = "device";
private static final String HOST = "host";
+ private static final String LINK = "link";
private static final String CLASS = "class";
private static final String UNKNOWN = "unknown";
private static final String ONE = "one";
@@ -162,6 +169,8 @@
private static final String MY_APP_ID = "org.onosproject.gui";
+ private static final String SLASH = "/";
+
private static final long TRAFFIC_PERIOD = 5000;
private static final long SUMMARY_PERIOD = 30000;
@@ -362,20 +371,48 @@
@Override
public void process(ObjectNode payload) {
String type = string(payload, CLASS, UNKNOWN);
- String id = string(payload, ID);
+ String id = string(payload, ID, "");
PropertyPanel pp = null;
if (type.equals(DEVICE)) {
DeviceId did = deviceId(id);
pp = deviceDetails(did);
overlayCache.currentOverlay().modifyDeviceDetails(pp, did);
+
} else if (type.equals(HOST)) {
HostId hid = hostId(id);
pp = hostDetails(hid);
overlayCache.currentOverlay().modifyHostDetails(pp, hid);
+
+ } else if (type.equals(LINK)) {
+ String srcId = string(payload, SOURCE_ID);
+ String tgtId = string(payload, TARGET_ID);
+ boolean isEdgeLink = bool(payload, IS_EDGE_LINK);
+
+ if (isEdgeLink) {
+ HostId hid = hostId(srcId);
+ String cpstr = tgtId + SLASH + string(payload, TARGET_PORT);
+ ConnectPoint cp = deviceConnectPoint(cpstr);
+
+ pp = edgeLinkDetails(hid, cp);
+ overlayCache.currentOverlay().modifyEdgeLinkDetails(pp, hid, cp);
+
+ } else {
+ String cpAstr = srcId + SLASH + string(payload, SOURCE_PORT);
+ String cpBstr = tgtId + SLASH + string(payload, TARGET_PORT);
+ ConnectPoint cpA = deviceConnectPoint(cpAstr);
+ ConnectPoint cpB = deviceConnectPoint(cpBstr);
+
+ pp = infraLinkDetails(cpA, cpB);
+ overlayCache.currentOverlay().modifyInfraLinkDetails(pp, cpA, cpB);
+ }
}
- sendMessage(envelope(SHOW_DETAILS, json(pp)));
+ if (pp != null) {
+ sendMessage(envelope(SHOW_DETAILS, json(pp)));
+ } else {
+ log.warn("Unable to process details request: {}", payload);
+ }
}
}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
index c2e290d..c3eae02 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
@@ -21,6 +21,7 @@
import com.google.common.collect.ImmutableSet;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.IpAddress;
+import org.onlab.packet.VlanId;
import org.onlab.util.DefaultHashMap;
import org.onosproject.cluster.ClusterEvent;
import org.onosproject.cluster.ControllerNode;
@@ -67,15 +68,29 @@
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.onosproject.net.PortNumber.portNumber;
import static org.onosproject.ui.topo.TopoConstants.CoreButtons;
-import static org.onosproject.ui.topo.TopoConstants.Properties;
import static org.onosproject.ui.topo.TopoConstants.Properties.DEVICES;
import static org.onosproject.ui.topo.TopoConstants.Properties.FLOWS;
+import static org.onosproject.ui.topo.TopoConstants.Properties.GRID_X;
+import static org.onosproject.ui.topo.TopoConstants.Properties.GRID_Y;
import static org.onosproject.ui.topo.TopoConstants.Properties.HOSTS;
+import static org.onosproject.ui.topo.TopoConstants.Properties.HW_VERSION;
import static org.onosproject.ui.topo.TopoConstants.Properties.INTENTS;
+import static org.onosproject.ui.topo.TopoConstants.Properties.IP;
+import static org.onosproject.ui.topo.TopoConstants.Properties.LATITUDE;
import static org.onosproject.ui.topo.TopoConstants.Properties.LINKS;
+import static org.onosproject.ui.topo.TopoConstants.Properties.LONGITUDE;
+import static org.onosproject.ui.topo.TopoConstants.Properties.MAC;
+import static org.onosproject.ui.topo.TopoConstants.Properties.PORTS;
+import static org.onosproject.ui.topo.TopoConstants.Properties.PROTOCOL;
+import static org.onosproject.ui.topo.TopoConstants.Properties.SERIAL_NUMBER;
+import static org.onosproject.ui.topo.TopoConstants.Properties.SW_VERSION;
import static org.onosproject.ui.topo.TopoConstants.Properties.TOPOLOGY_SSCS;
import static org.onosproject.ui.topo.TopoConstants.Properties.TUNNELS;
+import static org.onosproject.ui.topo.TopoConstants.Properties.URI;
+import static org.onosproject.ui.topo.TopoConstants.Properties.VENDOR;
import static org.onosproject.ui.topo.TopoConstants.Properties.VERSION;
+import static org.onosproject.ui.topo.TopoConstants.Properties.VLAN;
+import static org.onosproject.ui.topo.TopoConstants.Properties.VLAN_NONE;
import static org.onosproject.ui.topo.TopoUtils.compactLinkString;
/**
@@ -85,11 +100,32 @@
private static final String NO_GEO_VALUE = "0.0";
private static final String DASH = "-";
+ private static final String SLASH = " / ";
// nav paths are the view names for hot-link navigation from topo view...
private static final String DEVICE_NAV_PATH = "device";
private static final String HOST_NAV_PATH = "host";
+ // link panel label keys
+ private static final String LPL_FRIENDLY = "lp_label_friendly";
+ private static final String LPL_A_TYPE = "lp_label_a_type";
+ private static final String LPL_A_ID = "lp_label_a_id";
+ private static final String LPL_A_FRIENDLY = "lp_label_a_friendly";
+ private static final String LPL_A_PORT = "lp_label_a_port";
+ private static final String LPL_B_TYPE = "lp_label_b_type";
+ private static final String LPL_B_ID = "lp_label_b_id";
+ private static final String LPL_B_FRIENDLY = "lp_label_b_friendly";
+ private static final String LPL_B_PORT = "lp_label_b_port";
+ private static final String LPL_A2B = "lp_label_a2b";
+ private static final String LPL_B2A = "lp_label_b2a";
+ private static final String LPV_NO_LINK = "lp_value_no_link";
+
+ // other Lion keys
+ private static final String HOST = "host";
+ private static final String DEVICE = "device";
+ private static final String EXPECTED = "expected";
+ private static final String NOT_EXPECTED = "not_expected";
+
// default to an "add" event...
private static final DefaultHashMap<ClusterEvent.Type, String> CLUSTER_EVENT =
new DefaultHashMap<>("addInstance");
@@ -117,6 +153,32 @@
HOST_EVENT.put(HostEvent.Type.HOST_MOVED, "moveHost");
}
+ private static final DefaultHashMap<Device.Type, String> DEVICE_GLYPHS =
+ new DefaultHashMap<>("m_unknown");
+
+ static {
+ DEVICE_GLYPHS.put(Device.Type.SWITCH, "m_switch");
+ DEVICE_GLYPHS.put(Device.Type.ROUTER, "m_router");
+ DEVICE_GLYPHS.put(Device.Type.ROADM, "m_roadm");
+ DEVICE_GLYPHS.put(Device.Type.OTN, "m_otn");
+ DEVICE_GLYPHS.put(Device.Type.ROADM_OTN, "m_roadm_otn");
+ DEVICE_GLYPHS.put(Device.Type.BALANCER, "m_balancer");
+ DEVICE_GLYPHS.put(Device.Type.IPS, "m_ips");
+ DEVICE_GLYPHS.put(Device.Type.IDS, "m_ids");
+ DEVICE_GLYPHS.put(Device.Type.CONTROLLER, "m_controller");
+ DEVICE_GLYPHS.put(Device.Type.VIRTUAL, "m_virtual");
+ DEVICE_GLYPHS.put(Device.Type.FIBER_SWITCH, "m_fiberSwitch");
+ DEVICE_GLYPHS.put(Device.Type.MICROWAVE, "m_microwave");
+ DEVICE_GLYPHS.put(Device.Type.OLT, "m_olt");
+ DEVICE_GLYPHS.put(Device.Type.ONU, "m_onu");
+ DEVICE_GLYPHS.put(Device.Type.OPTICAL_AMPLIFIER, "unknown"); // TODO glyph needed
+ DEVICE_GLYPHS.put(Device.Type.OTHER, "m_other");
+ }
+
+ private static final String DEFAULT_HOST_GLYPH = "m_endstation";
+ private static final String LINK_GLYPH = "m_ports";
+
+
protected static final Logger log =
LoggerFactory.getLogger(TopologyViewMessageHandlerBase.class);
@@ -368,13 +430,19 @@
// -----------------------------------------------------------------------
// Create models of the data to return, that overlays can adjust / augment
+ private String lookupGlyph(Device device) {
+ return DEVICE_GLYPHS.get(device.type());
+ }
+
+
// Returns property panel model for summary response.
protected PropertyPanel summmaryMessage() {
+ // chose NOT to add debug messages, since this is called every few seconds
Topology topology = services.topology().currentTopology();
LionBundle lion = getLionBundle(LION_TOPO);
String panelTitle = lion.getSafe("title_panel_summary");
- return new PropertyPanel(panelTitle, "node")
+ return new PropertyPanel(panelTitle, "bird")
.addProp(VERSION, lion.getSafe(VERSION), version)
.addSeparator()
.addProp(DEVICES, lion.getSafe(DEVICES), services.device().getDeviceCount())
@@ -387,39 +455,79 @@
.addProp(FLOWS, lion.getSafe(FLOWS), services.flow().getFlowRuleCount());
}
- // Returns property panel model for device details response.
- protected PropertyPanel deviceDetails(DeviceId deviceId) {
+
+ private String friendlyDevice(DeviceId deviceId) {
Device device = services.device().getDevice(deviceId);
Annotations annot = device.annotations();
String name = annot.value(AnnotationKeys.NAME);
+ return isNullOrEmpty(name) ? deviceId.toString() : name;
+ }
+
+ // Generates a property panel model for device details response
+ protected PropertyPanel deviceDetails(DeviceId deviceId) {
+ log.debug("generate prop panel data for device {}", deviceId);
+ Device device = services.device().getDevice(deviceId);
+ Annotations annot = device.annotations();
+ String proto = annot.value(AnnotationKeys.PROTOCOL);
+ String title = friendlyDevice(deviceId);
+ LionBundle lion = getLionBundle(LION_TOPO);
+
+ PropertyPanel pp = new PropertyPanel(title, lookupGlyph(device))
+ .navPath(DEVICE_NAV_PATH)
+ .id(deviceId.toString());
+ addDeviceBasicProps(pp, deviceId, device, proto, lion);
+ addLocationProps(pp, annot, lion);
+ addDeviceCountStats(pp, deviceId, lion);
+ addDeviceCoreButtons(pp);
+ return pp;
+ }
+
+ private void addDeviceBasicProps(PropertyPanel pp, DeviceId deviceId,
+ Device device, String proto, LionBundle lion) {
+ pp.addProp(URI, lion.getSafe(URI), deviceId.toString())
+ .addProp(VENDOR, lion.getSafe(VENDOR), device.manufacturer())
+ .addProp(HW_VERSION, lion.getSafe(HW_VERSION), device.hwVersion())
+ .addProp(SW_VERSION, lion.getSafe(SW_VERSION), device.swVersion())
+ .addProp(SERIAL_NUMBER, lion.getSafe(SERIAL_NUMBER), device.serialNumber())
+ .addProp(PROTOCOL, lion.getSafe(PROTOCOL), proto)
+ .addSeparator();
+ }
+
+ // only add location properties if we have them
+ private void addLocationProps(PropertyPanel pp, Annotations annot,
+ LionBundle lion) {
+ String slat = annot.value(AnnotationKeys.LATITUDE);
+ String slng = annot.value(AnnotationKeys.LONGITUDE);
+ String sgrY = annot.value(AnnotationKeys.GRID_Y);
+ String sgrX = annot.value(AnnotationKeys.GRID_X);
+
+ boolean validLat = slat != null && !slat.equals(NO_GEO_VALUE);
+ boolean validLng = slng != null && !slng.equals(NO_GEO_VALUE);
+ if (validLat && validLng) {
+ pp.addProp(LATITUDE, lion.getSafe(LATITUDE), slat)
+ .addProp(LONGITUDE, lion.getSafe(LONGITUDE), slng)
+ .addSeparator();
+
+ } else if (sgrY != null && sgrX != null) {
+ pp.addProp(GRID_Y, lion.getSafe(GRID_Y), sgrY)
+ .addProp(GRID_X, lion.getSafe(GRID_X), sgrX)
+ .addSeparator();
+ }
+ // else, no location
+ }
+
+ private void addDeviceCountStats(PropertyPanel pp, DeviceId deviceId, LionBundle lion) {
int portCount = services.device().getPorts(deviceId).size();
int flowCount = getFlowCount(deviceId);
int tunnelCount = getTunnelCount(deviceId);
- String title = isNullOrEmpty(name) ? deviceId.toString() : name;
- String typeId = device.type().toString().toLowerCase();
+ pp.addProp(PORTS, lion.getSafe(PORTS), portCount)
+ .addProp(FLOWS, lion.getSafe(FLOWS), flowCount)
+ .addProp(TUNNELS, lion.getSafe(TUNNELS), tunnelCount);
+ }
- return new PropertyPanel(title, typeId)
- .navPath(DEVICE_NAV_PATH)
- .id(deviceId.toString())
-
- .addProp(Properties.URI, deviceId.toString())
- .addProp(Properties.VENDOR, device.manufacturer())
- .addProp(Properties.HW_VERSION, device.hwVersion())
- .addProp(Properties.SW_VERSION, device.swVersion())
- .addProp(Properties.SERIAL_NUMBER, device.serialNumber())
- .addProp(Properties.PROTOCOL, annot.value(AnnotationKeys.PROTOCOL))
- .addSeparator()
-
- .addProp(Properties.LATITUDE, annot.value(AnnotationKeys.LATITUDE))
- .addProp(Properties.LONGITUDE, annot.value(AnnotationKeys.LONGITUDE))
- .addSeparator()
-
- .addProp(Properties.PORTS, portCount)
- .addProp(FLOWS, flowCount)
- .addProp(TUNNELS, tunnelCount)
-
- .addButton(CoreButtons.SHOW_DEVICE_VIEW)
+ private void addDeviceCoreButtons(PropertyPanel pp) {
+ pp.addButton(CoreButtons.SHOW_DEVICE_VIEW)
.addButton(CoreButtons.SHOW_FLOW_VIEW)
.addButton(CoreButtons.SHOW_PORT_VIEW)
.addButton(CoreButtons.SHOW_GROUP_VIEW)
@@ -467,23 +575,108 @@
return useDefaultName(name) ? ip(host.ipAddresses()) : name;
}
- // Returns host details response.
- protected PropertyPanel hostDetails(HostId hostId) {
- Host host = services.host().getHost(hostId);
- Annotations annot = host.annotations();
- String type = annot.value(AnnotationKeys.TYPE);
- String vlan = host.vlan().toString();
- String typeId = isNullOrEmpty(type) ? "endstation" : type;
-
- return new PropertyPanel(nameForHost(host), typeId)
- .navPath(HOST_NAV_PATH)
- .id(hostId.toString())
- .addProp(Properties.MAC, host.mac())
- .addProp(Properties.IP, host.ipAddresses(), "[\\[\\]]")
- .addProp(Properties.VLAN, "-1".equals(vlan) ? "none" : vlan)
- .addSeparator()
- .addProp(Properties.LATITUDE, annot.value(AnnotationKeys.LATITUDE))
- .addProp(Properties.LONGITUDE, annot.value(AnnotationKeys.LONGITUDE));
+ private String glyphForHost(Annotations annot) {
+ String uiType = annot.value(AnnotationKeys.UI_TYPE);
+ return isNullOrEmpty(uiType) ? DEFAULT_HOST_GLYPH : uiType;
}
+ // Generates a property panel model for a host details response
+ protected PropertyPanel hostDetails(HostId hostId) {
+ log.debug("generate prop panel data for host {}", hostId);
+ Host host = services.host().getHost(hostId);
+ Annotations annot = host.annotations();
+ String glyphId = glyphForHost(annot);
+ LionBundle lion = getLionBundle(LION_TOPO);
+
+ PropertyPanel pp = new PropertyPanel(nameForHost(host), glyphId)
+ .navPath(HOST_NAV_PATH)
+ .id(hostId.toString());
+ addHostBasicProps(pp, host, lion);
+ addLocationProps(pp, annot, lion);
+ return pp;
+ }
+
+ private void addHostBasicProps(PropertyPanel pp, Host host, LionBundle lion) {
+ pp.addProp(LPL_FRIENDLY, lion.getSafe(LPL_FRIENDLY), nameForHost(host))
+ .addProp(MAC, lion.getSafe(MAC), host.mac())
+ .addProp(IP, lion.getSafe(IP), host.ipAddresses(), "[\\[\\]]")
+ .addProp(VLAN, lion.getSafe(VLAN), displayVlan(host.vlan(), lion))
+ .addSeparator();
+ }
+
+ private String displayVlan(VlanId vlan, LionBundle lion) {
+ return VlanId.NONE.equals(vlan) ? lion.getSafe(VLAN_NONE) : vlan.toString();
+ }
+
+ // Generates a property panel model for a link details response (edge-link)
+ protected PropertyPanel edgeLinkDetails(HostId hid, ConnectPoint cp) {
+ log.debug("generate prop panel data for edgelink {} {}", hid, cp);
+ LionBundle lion = getLionBundle(LION_TOPO);
+ String title = lion.getSafe("title_edge_link");
+
+ PropertyPanel pp = new PropertyPanel(title, LINK_GLYPH);
+ addLinkHostProps(pp, hid, lion);
+ addLinkCpBProps(pp, cp, lion);
+ return pp;
+ }
+
+ // Generates a property panel model for a link details response (infra-link)
+ protected PropertyPanel infraLinkDetails(ConnectPoint cpA, ConnectPoint cpB) {
+ log.debug("generate prop panel data for infralink {} {}", cpA, cpB);
+ LionBundle lion = getLionBundle(LION_TOPO);
+ String title = lion.getSafe("title_infra_link");
+
+ PropertyPanel pp = new PropertyPanel(title, LINK_GLYPH);
+ addLinkCpAProps(pp, cpA, lion);
+ addLinkCpBProps(pp, cpB, lion);
+ addLinkBackingProps(pp, cpA, cpB, lion);
+ return pp;
+ }
+
+ private void addLinkHostProps(PropertyPanel pp, HostId hostId, LionBundle lion) {
+ Host host = services.host().getHost(hostId);
+
+ pp.addProp(LPL_A_TYPE, lion.getSafe(LPL_A_TYPE), lion.getSafe(HOST))
+ .addProp(LPL_A_ID, lion.getSafe(LPL_A_ID), hostId.toString())
+ .addProp(LPL_A_FRIENDLY, lion.getSafe(LPL_A_FRIENDLY), nameForHost(host))
+ .addSeparator();
+ }
+
+ private void addLinkCpAProps(PropertyPanel pp, ConnectPoint cp, LionBundle lion) {
+ DeviceId did = cp.deviceId();
+
+ pp.addProp(LPL_A_TYPE, lion.getSafe(LPL_A_TYPE), lion.getSafe(DEVICE))
+ .addProp(LPL_A_ID, lion.getSafe(LPL_A_ID), did.toString())
+ .addProp(LPL_A_FRIENDLY, lion.getSafe(LPL_A_FRIENDLY), friendlyDevice(did))
+ .addProp(LPL_A_PORT, lion.getSafe(LPL_A_PORT), cp.port().toLong())
+ .addSeparator();
+ }
+
+ private void addLinkCpBProps(PropertyPanel pp, ConnectPoint cp, LionBundle lion) {
+ DeviceId did = cp.deviceId();
+
+ pp.addProp(LPL_B_TYPE, lion.getSafe(LPL_B_TYPE), lion.getSafe(DEVICE))
+ .addProp(LPL_B_ID, lion.getSafe(LPL_B_ID), did.toString())
+ .addProp(LPL_B_FRIENDLY, lion.getSafe(LPL_B_FRIENDLY), friendlyDevice(did))
+ .addProp(LPL_B_PORT, lion.getSafe(LPL_B_PORT), cp.port().toLong())
+ .addSeparator();
+ }
+
+ private void addLinkBackingProps(PropertyPanel pp, ConnectPoint cpA,
+ ConnectPoint cpB, LionBundle lion) {
+ Link a2b = services.link().getLink(cpA, cpB);
+ Link b2a = services.link().getLink(cpB, cpA);
+
+ pp.addProp(LPL_A2B, lion.getSafe(LPL_A2B), linkPropString(a2b, lion))
+ .addProp(LPL_B2A, lion.getSafe(LPL_B2A), linkPropString(b2a, lion));
+ }
+
+ private String linkPropString(Link link, LionBundle lion) {
+ if (link == null) {
+ return lion.getSafe(LPV_NO_LINK);
+ }
+ return lion.getSafe(link.type()) + SLASH +
+ lion.getSafe(link.state()) + SLASH +
+ lion.getSafe(link.isExpected() ? EXPECTED : NOT_EXPECTED);
+ }
}