aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/web
diff options
context:
space:
mode:
authorAshlee Young <ashlee@onosfw.com>2015-10-09 18:32:44 -0700
committerAshlee Young <ashlee@onosfw.com>2015-10-09 18:32:44 -0700
commit6a07d2d622eaa06953f3353e39c080984076e8de (patch)
treebfb50a2090fce186c2cc545a400c969bf2ea702b /framework/src/onos/web
parente6d71622143ff9b2421a1abbe8434b954b5b1099 (diff)
Updated master to commit id 6ee8aa3e67ce89908a8c93aa9445c6f71a18f986
Change-Id: I94b055ee2f298daf71e2ec794fd0f2495bd8081f
Diffstat (limited to 'framework/src/onos/web')
-rw-r--r--framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java24
-rw-r--r--framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java158
-rw-r--r--framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/PathsWebResource.java64
-rw-r--r--framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java61
-rw-r--r--framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/DeviceViewMessageHandler.java77
-rw-r--r--framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/ProcessorViewMessageHandler.java108
-rw-r--r--framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java2
-rw-r--r--framework/src/onos/web/gui/src/main/webapp/app/fw/svg/glyph.js30
-rw-r--r--framework/src/onos/web/gui/src/main/webapp/app/fw/svg/icon.js6
-rw-r--r--framework/src/onos/web/gui/src/main/webapp/app/fw/util/keys.js30
-rw-r--r--framework/src/onos/web/gui/src/main/webapp/app/view/device/device.css9
-rw-r--r--framework/src/onos/web/gui/src/main/webapp/app/view/device/device.html6
-rw-r--r--framework/src/onos/web/gui/src/main/webapp/app/view/device/device.js116
-rw-r--r--framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.css49
-rw-r--r--framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.html63
-rw-r--r--framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.js58
-rw-r--r--framework/src/onos/web/gui/src/main/webapp/app/view/settings/settings.html2
-rw-r--r--framework/src/onos/web/gui/src/main/webapp/app/view/topo/topo.js3
-rw-r--r--framework/src/onos/web/gui/src/main/webapp/index.html2
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_10_addDevice_s9_ids.json18
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_11_addDevice_s10_controller.json18
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_12_addDevice_s11_virtual.json18
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_13_addDevice_s12_fiber_switch.json18
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_14_addDevice_s13_microwave.json18
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_15_addDevice_s14_other.json18
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_16_addDevice_s15_unmatched.json18
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_1_addInstance.json14
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_2_addDevice_s1_switch.json18
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_3_addDevice_s2_router.json18
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_4_addDevice_s3_roadm.json18
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_5_addDevice_s4_otn.json18
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_6_addDevice_s5_roadm_otn.json18
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_7_addDevice_s6_firewall.json18
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_8_addDevice_s7_balancer.json18
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_9_addDevice_s8_ips.json18
-rw-r--r--framework/src/onos/web/gui/src/test/_karma/ev/devices/scenario.json12
36 files changed, 1014 insertions, 150 deletions
diff --git a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
index 325e191b..0e88e34e 100644
--- a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
+++ b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
@@ -66,9 +66,9 @@ public class FlowsWebResource extends AbstractWebResource {
public Response getFlows() {
final Iterable<Device> devices = get(DeviceService.class).getDevices();
for (final Device device : devices) {
- final Iterable<FlowEntry> deviceEntries = service.getFlowEntries(device.id());
- if (deviceEntries != null) {
- for (final FlowEntry entry : deviceEntries) {
+ final Iterable<FlowEntry> flowEntries = service.getFlowEntries(device.id());
+ if (flowEntries != null) {
+ for (final FlowEntry entry : flowEntries) {
flowsNode.add(codec(FlowEntry.class).encode(entry, this));
}
}
@@ -88,13 +88,13 @@ public class FlowsWebResource extends AbstractWebResource {
@Produces(MediaType.APPLICATION_JSON)
@Path("{deviceId}")
public Response getFlowByDeviceId(@PathParam("deviceId") String deviceId) {
- final Iterable<FlowEntry> deviceEntries =
+ final Iterable<FlowEntry> flowEntries =
service.getFlowEntries(DeviceId.deviceId(deviceId));
- if (!deviceEntries.iterator().hasNext()) {
+ if (!flowEntries.iterator().hasNext()) {
throw new ItemNotFoundException(DEVICE_NOT_FOUND);
}
- for (final FlowEntry entry : deviceEntries) {
+ for (final FlowEntry entry : flowEntries) {
flowsNode.add(codec(FlowEntry.class).encode(entry, this));
}
return ok(root).build();
@@ -113,13 +113,13 @@ public class FlowsWebResource extends AbstractWebResource {
@Path("{deviceId}/{flowId}")
public Response getFlowByDeviceIdAndFlowId(@PathParam("deviceId") String deviceId,
@PathParam("flowId") long flowId) {
- final Iterable<FlowEntry> deviceEntries =
+ final Iterable<FlowEntry> flowEntries =
service.getFlowEntries(DeviceId.deviceId(deviceId));
- if (!deviceEntries.iterator().hasNext()) {
+ if (!flowEntries.iterator().hasNext()) {
throw new ItemNotFoundException(DEVICE_NOT_FOUND);
}
- for (final FlowEntry entry : deviceEntries) {
+ for (final FlowEntry entry : flowEntries) {
if (entry.id().value() == flowId) {
flowsNode.add(codec(FlowEntry.class).encode(entry, this));
}
@@ -175,14 +175,14 @@ public class FlowsWebResource extends AbstractWebResource {
@Path("{deviceId}/{flowId}")
public void deleteFlowByDeviceIdAndFlowId(@PathParam("deviceId") String deviceId,
@PathParam("flowId") long flowId) {
- final Iterable<FlowEntry> deviceEntries =
+ final Iterable<FlowEntry> flowEntries =
service.getFlowEntries(DeviceId.deviceId(deviceId));
- if (!deviceEntries.iterator().hasNext()) {
+ if (!flowEntries.iterator().hasNext()) {
throw new ItemNotFoundException(DEVICE_NOT_FOUND);
}
- StreamSupport.stream(deviceEntries.spliterator(), false)
+ StreamSupport.stream(flowEntries.spliterator(), false)
.filter(entry -> entry.id().value() == flowId)
.forEach(service::removeFlowRules);
}
diff --git a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
index 9e2b6273..808fcc16 100644
--- a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
+++ b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
@@ -49,73 +49,77 @@ public class NetworkConfigWebResource extends AbstractWebResource {
public Response download() {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = mapper().createObjectNode();
- service.getSubjectClasses().forEach(sc ->
- produceJson(service, newObject(root, service.getSubjectFactory(sc).subjectKey()), sc));
+ service.getSubjectClasses().forEach(sc -> {
+ SubjectFactory subjectFactory = service.getSubjectFactory(sc);
+ produceJson(service, newObject(root, subjectFactory.subjectClassKey()),
+ subjectFactory, sc);
+ });
return ok(root).build();
}
/**
* Get all network configuration for a subject class.
*
- * @param subjectKey subject class key
+ * @param subjectClassKey subject class key
* @return network configuration JSON
*/
@GET
- @Path("{subjectKey}")
+ @Path("{subjectClassKey}")
@Produces(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
- public Response download(@PathParam("subjectKey") String subjectKey) {
+ public Response download(@PathParam("subjectClassKey") String subjectClassKey) {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = mapper().createObjectNode();
- produceJson(service, root, service.getSubjectFactory(subjectKey).subjectClass());
+ SubjectFactory subjectFactory = service.getSubjectFactory(subjectClassKey);
+ produceJson(service, root, subjectFactory, subjectFactory.subjectClass());
return ok(root).build();
}
/**
- * Get all network configuration for a subject.
+ * Get all network configuration for a subjectKey.
*
- * @param subjectKey subject class key
- * @param subject subject key
+ * @param subjectClassKey subjectKey class key
+ * @param subjectKey subjectKey key
* @return network configuration JSON
*/
@GET
- @Path("{subjectKey}/{subject}")
+ @Path("{subjectClassKey}/{subjectKey}")
@Produces(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
- public Response download(@PathParam("subjectKey") String subjectKey,
- @PathParam("subject") String subject) {
+ public Response download(@PathParam("subjectClassKey") String subjectClassKey,
+ @PathParam("subjectKey") String subjectKey) {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = mapper().createObjectNode();
- produceSubjectJson(service, root,
- service.getSubjectFactory(subjectKey).createSubject(subject));
+ SubjectFactory subjectFactory = service.getSubjectFactory(subjectClassKey);
+ produceSubjectJson(service, root, subjectFactory.createSubject(subjectKey));
return ok(root).build();
}
/**
- * Get specific network configuration for a subject.
+ * Get specific network configuration for a subjectKey.
*
- * @param subjectKey subject class key
- * @param subject subject key
- * @param configKey configuration class key
+ * @param subjectClassKey subjectKey class key
+ * @param subjectKey subjectKey key
+ * @param configKey configuration class key
* @return network configuration JSON
*/
@GET
- @Path("{subjectKey}/{subject}/{configKey}")
+ @Path("{subjectClassKey}/{subjectKey}/{configKey}")
@Produces(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
- public Response download(@PathParam("subjectKey") String subjectKey,
- @PathParam("subject") String subject,
+ public Response download(@PathParam("subjectClassKey") String subjectClassKey,
+ @PathParam("subjectKey") String subjectKey,
@PathParam("configKey") String configKey) {
NetworkConfigService service = get(NetworkConfigService.class);
- return ok(service.getConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
- service.getConfigClass(subjectKey, configKey)).node()).build();
+ return ok(service.getConfig(service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
+ service.getConfigClass(subjectClassKey, configKey)).node()).build();
}
@SuppressWarnings("unchecked")
private void produceJson(NetworkConfigService service, ObjectNode node,
- Class subjectClass) {
+ SubjectFactory subjectFactory, Class subjectClass) {
service.getSubjects(subjectClass).forEach(s ->
- produceSubjectJson(service, newObject(node, s.toString()), s));
+ produceSubjectJson(service, newObject(node, subjectFactory.subjectKey(s)), s));
}
private void produceSubjectJson(NetworkConfigService service, ObjectNode node,
@@ -128,8 +132,8 @@ public class NetworkConfigWebResource extends AbstractWebResource {
* Upload bulk network configuration.
*
* @param request network configuration JSON rooted at the top node
- * @throws IOException if unable to parse the request
* @return empty response
+ * @throws IOException if unable to parse the request
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@@ -146,78 +150,78 @@ public class NetworkConfigWebResource extends AbstractWebResource {
/**
* Upload multiple network configurations for a subject class.
*
- * @param subjectKey subject class key
- * @param request network configuration JSON rooted at the top node
+ * @param subjectClassKey subject class key
+ * @param request network configuration JSON rooted at the top node
* @return empty response
* @throws IOException if unable to parse the request
*/
@POST
- @Path("{subjectKey}")
+ @Path("{subjectClassKey}")
@Consumes(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
- public Response upload(@PathParam("subjectKey") String subjectKey,
+ public Response upload(@PathParam("subjectClassKey") String subjectClassKey,
InputStream request) throws IOException {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = (ObjectNode) mapper().readTree(request);
- consumeJson(service, root, service.getSubjectFactory(subjectKey));
+ consumeJson(service, root, service.getSubjectFactory(subjectClassKey));
return Response.ok().build();
}
/**
- * Upload mutliple network configurations for a subject.
+ * Upload mutliple network configurations for a subjectKey.
*
- * @param subjectKey subject class key
- * @param subject subject key
- * @param request network configuration JSON rooted at the top node
+ * @param subjectClassKey subjectKey class key
+ * @param subjectKey subjectKey key
+ * @param request network configuration JSON rooted at the top node
* @return empty response
* @throws IOException if unable to parse the request
*/
@POST
- @Path("{subjectKey}/{subject}")
+ @Path("{subjectClassKey}/{subjectKey}")
@Consumes(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
- public Response upload(@PathParam("subjectKey") String subjectKey,
- @PathParam("subject") String subject,
+ public Response upload(@PathParam("subjectClassKey") String subjectClassKey,
+ @PathParam("subjectKey") String subjectKey,
InputStream request) throws IOException {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = (ObjectNode) mapper().readTree(request);
consumeSubjectJson(service, root,
- service.getSubjectFactory(subjectKey).createSubject(subject),
- subjectKey);
+ service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
+ subjectClassKey);
return Response.ok().build();
}
/**
- * Upload specific network configuration for a subject.
+ * Upload specific network configuration for a subjectKey.
*
- * @param subjectKey subject class key
- * @param subject subject key
- * @param configKey configuration class key
- * @param request network configuration JSON rooted at the top node
+ * @param subjectClassKey subjectKey class key
+ * @param subjectKey subjectKey key
+ * @param configKey configuration class key
+ * @param request network configuration JSON rooted at the top node
* @return empty response
* @throws IOException if unable to parse the request
*/
@POST
- @Path("{subjectKey}/{subject}/{configKey}")
+ @Path("{subjectClassKey}/{subjectKey}/{configKey}")
@Consumes(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
- public Response upload(@PathParam("subjectKey") String subjectKey,
- @PathParam("subject") String subject,
+ public Response upload(@PathParam("subjectClassKey") String subjectClassKey,
+ @PathParam("subjectKey") String subjectKey,
@PathParam("configKey") String configKey,
InputStream request) throws IOException {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = (ObjectNode) mapper().readTree(request);
- service.applyConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
- service.getConfigClass(subjectKey, configKey), root);
+ service.applyConfig(service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
+ service.getConfigClass(subjectClassKey, configKey), root);
return Response.ok().build();
}
private void consumeJson(NetworkConfigService service, ObjectNode classNode,
SubjectFactory subjectFactory) {
classNode.fieldNames().forEachRemaining(s ->
- consumeSubjectJson(service, (ObjectNode) classNode.path(s),
- subjectFactory.createSubject(s),
- subjectFactory.subjectKey()));
+ consumeSubjectJson(service, (ObjectNode) classNode.path(s),
+ subjectFactory.createSubject(s),
+ subjectFactory.subjectClassKey()));
}
private void consumeSubjectJson(NetworkConfigService service,
@@ -225,7 +229,7 @@ public class NetworkConfigWebResource extends AbstractWebResource {
String subjectKey) {
subjectNode.fieldNames().forEachRemaining(c ->
service.applyConfig(subject, service.getConfigClass(subjectKey, c),
- (ObjectNode) subjectNode.path(c)));
+ subjectNode.path(c)));
}
@@ -241,64 +245,62 @@ public class NetworkConfigWebResource extends AbstractWebResource {
service.getSubjectClasses()
.forEach(subjectClass -> service.getSubjects(subjectClass)
.forEach(subject -> service.getConfigs(subject)
- .forEach(config -> service
- .removeConfig(subject, config.getClass()))));
+ .forEach(config -> service.removeConfig(subject, config.getClass()))));
return Response.ok().build();
}
/**
* Clear all network configurations for a subject class.
*
- * @param subjectKey subject class key
+ * @param subjectClassKey subject class key
* @return empty response
*/
@DELETE
- @Path("{subjectKey}")
+ @Path("{subjectClassKey}")
@SuppressWarnings("unchecked")
- public Response delete(@PathParam("subjectKey") String subjectKey) {
+ public Response delete(@PathParam("subjectClassKey") String subjectClassKey) {
NetworkConfigService service = get(NetworkConfigService.class);
- service.getSubjects(service.getSubjectFactory(subjectKey).getClass())
+ service.getSubjects(service.getSubjectFactory(subjectClassKey).getClass())
.forEach(subject -> service.getConfigs(subject)
- .forEach(config -> service
- .removeConfig(subject, config.getClass())));
+ .forEach(config -> service.removeConfig(subject, config.getClass())));
return Response.ok().build();
}
/**
- * Clear all network configurations for a subject.
+ * Clear all network configurations for a subjectKey.
*
- * @param subjectKey subject class key
- * @param subject subject key
+ * @param subjectClassKey subjectKey class key
+ * @param subjectKey subjectKey key
* @return empty response
*/
@DELETE
- @Path("{subjectKey}/{subject}")
+ @Path("{subjectClassKey}/{subjectKey}")
@SuppressWarnings("unchecked")
- public Response delete(@PathParam("subjectKey") String subjectKey,
- @PathParam("subject") String subject) {
+ public Response delete(@PathParam("subjectClassKey") String subjectClassKey,
+ @PathParam("subjectKey") String subjectKey) {
NetworkConfigService service = get(NetworkConfigService.class);
- Object s = service.getSubjectFactory(subjectKey).createSubject(subject);
+ Object s = service.getSubjectFactory(subjectClassKey).createSubject(subjectKey);
service.getConfigs(s).forEach(c -> service.removeConfig(s, c.getClass()));
return Response.ok().build();
}
/**
- * Clear specific network configuration for a subject.
+ * Clear specific network configuration for a subjectKey.
*
- * @param subjectKey subject class key
- * @param subject subject key
- * @param configKey configuration class key
+ * @param subjectClassKey subjectKey class key
+ * @param subjectKey subjectKey key
+ * @param configKey configuration class key
* @return empty response
*/
@DELETE
- @Path("{subjectKey}/{subject}/{configKey}")
+ @Path("{subjectClassKey}/{subjectKey}/{configKey}")
@SuppressWarnings("unchecked")
- public Response delete(@PathParam("subjectKey") String subjectKey,
- @PathParam("subject") String subject,
+ public Response delete(@PathParam("subjectClassKey") String subjectClassKey,
+ @PathParam("subjectKey") String subjectKey,
@PathParam("configKey") String configKey) {
NetworkConfigService service = get(NetworkConfigService.class);
- service.removeConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
- service.getConfigClass(subjectKey, configKey));
+ service.removeConfig(service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
+ service.getConfigClass(subjectClassKey, configKey));
return Response.ok().build();
}
diff --git a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/PathsWebResource.java b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/PathsWebResource.java
index baa1b1e6..9714690c 100644
--- a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/PathsWebResource.java
+++ b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/PathsWebResource.java
@@ -15,7 +15,11 @@
*/
package org.onosproject.rest.resources;
-import java.util.Set;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.topology.PathService;
+import org.onosproject.rest.AbstractWebResource;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@@ -23,14 +27,7 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.ElementId;
-import org.onosproject.net.HostId;
-import org.onosproject.net.topology.PathService;
-
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.onosproject.rest.AbstractWebResource;
+import java.util.Set;
/**
* Compute paths in the network graph.
@@ -50,6 +47,17 @@ public class PathsWebResource extends AbstractWebResource {
}
/**
+ * Returns either host id or device id, depending on the ID format.
+ *
+ * @param id host or device id string
+ * @return element id
+ */
+ private ElementId elementId(String id) {
+ ElementId elementId = isHostId(id);
+ return elementId != null ? elementId : DeviceId.deviceId(id);
+ }
+
+ /**
* Get all shortest paths between any two hosts or devices.
* Returns array of all shortest paths between any two elements.
*
@@ -63,23 +71,27 @@ public class PathsWebResource extends AbstractWebResource {
public Response getPath(@PathParam("src") String src,
@PathParam("dst") String dst) {
PathService pathService = get(PathService.class);
-
- ElementId srcElement = isHostId(src);
- ElementId dstElement = isHostId(dst);
-
- if (srcElement == null) {
- // Doesn't look like a host, assume it is a device
- srcElement = DeviceId.deviceId(src);
- }
-
- if (dstElement == null) {
- // Doesn't look like a host, assume it is a device
- dstElement = DeviceId.deviceId(dst);
- }
-
- Set<org.onosproject.net.Path> paths = pathService.getPaths(srcElement, dstElement);
- ObjectNode root = encodeArray(org.onosproject.net.Path.class, "paths", paths);
- return ok(root).build();
+ Set<org.onosproject.net.Path> paths =
+ pathService.getPaths(elementId(src), elementId(dst));
+ return ok(encodeArray(org.onosproject.net.Path.class, "paths", paths)).build();
}
+ /**
+ * Get all shortest disjoint paths between any two hosts or devices.
+ * Returns array of all shortest disjoint paths between any two elements.
+ *
+ * @param src source identifier
+ * @param dst destination identifier
+ * @return path data
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("{src}/{dst}/disjoint")
+ public Response getDisjointPath(@PathParam("src") String src,
+ @PathParam("dst") String dst) {
+ PathService pathService = get(PathService.class);
+ Set<org.onosproject.net.DisjointPath> paths =
+ pathService.getDisjointPaths(elementId(src), elementId(dst));
+ return ok(encodeArray(org.onosproject.net.DisjointPath.class, "paths", paths)).build();
+ }
}
diff --git a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java
index 2ffa2295..c91cb6d0 100644
--- a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java
+++ b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java
@@ -21,6 +21,7 @@ import java.util.stream.StreamSupport;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
@@ -31,7 +32,12 @@ import javax.ws.rs.core.UriInfo;
import org.onosproject.codec.JsonCodec;
import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TableStatisticsEntry;
import org.onosproject.net.link.LinkService;
import org.onosproject.net.statistic.Load;
import org.onosproject.net.statistic.StatisticService;
@@ -92,4 +98,59 @@ public class StatisticsWebResource extends AbstractWebResource {
result.set("loads", loads);
return ok(result).build();
}
+
+ /**
+ * Get table statistics for all tables of all devices.
+ *
+ * @return JSON encoded array of table statistics
+ */
+ @GET
+ @Path("flows/tables")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getTableStatistics() {
+ final FlowRuleService service = get(FlowRuleService.class);
+ final Iterable<Device> devices = get(DeviceService.class).getDevices();
+ final ObjectNode root = mapper().createObjectNode();
+ final ArrayNode rootArrayNode = root.putArray("device-table-statistics");
+ for (final Device device : devices) {
+ final ObjectNode deviceStatsNode = mapper().createObjectNode();
+ deviceStatsNode.put("device", device.id().toString());
+ final ArrayNode statisticsNode = deviceStatsNode.putArray("table-statistics");
+ final Iterable<TableStatisticsEntry> tableStatsEntries = service.getFlowTableStatistics(device.id());
+ if (tableStatsEntries != null) {
+ for (final TableStatisticsEntry entry : tableStatsEntries) {
+ statisticsNode.add(codec(TableStatisticsEntry.class).encode(entry, this));
+ }
+ }
+ rootArrayNode.add(deviceStatsNode);
+ }
+
+ return ok(root).build();
+ }
+
+ /**
+ * Get table statistics for all tables of a specified device.
+ *
+ * @param deviceId device ID
+ * @return JSON encoded array of table statistics
+ */
+ @GET
+ @Path("flows/tables/{deviceId}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getTableStatisticsByDeviceId(@PathParam("deviceId") String deviceId) {
+ final FlowRuleService service = get(FlowRuleService.class);
+ final Iterable<TableStatisticsEntry> tableStatisticsEntries =
+ service.getFlowTableStatistics(DeviceId.deviceId(deviceId));
+ final ObjectNode root = mapper().createObjectNode();
+ final ArrayNode rootArrayNode = root.putArray("table-statistics");
+
+ final ObjectNode deviceStatsNode = mapper().createObjectNode();
+ deviceStatsNode.put("device", deviceId);
+ final ArrayNode statisticsNode = deviceStatsNode.putArray("table-statistics");
+ for (final TableStatisticsEntry entry : tableStatisticsEntries) {
+ statisticsNode.add(codec(TableStatisticsEntry.class).encode(entry, this));
+ }
+ rootArrayNode.add(deviceStatsNode);
+ return ok(root).build();
+ }
}
diff --git a/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/DeviceViewMessageHandler.java b/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/DeviceViewMessageHandler.java
index fb83cdd7..53b16a65 100644
--- a/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/DeviceViewMessageHandler.java
+++ b/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/DeviceViewMessageHandler.java
@@ -25,12 +25,16 @@ import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.Port;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.BasicDeviceConfig;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.link.LinkService;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.table.TableModel;
import org.onosproject.ui.table.TableRequestHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
@@ -38,7 +42,10 @@ import java.util.Collections;
import java.util.List;
import java.util.Set;
+import static com.google.common.base.Strings.emptyToNull;
+import static com.google.common.base.Strings.isNullOrEmpty;
import static org.apache.commons.lang.WordUtils.capitalizeFully;
+import static org.onosproject.net.DeviceId.deviceId;
/**
* Message handler for device view related messages.
@@ -53,6 +60,11 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
private static final String DEV_DETAILS_RESP = "deviceDetailsResponse";
private static final String DETAILS = "details";
+ private static final String DEV_NAME_CHANGE_REQ = "deviceNameChangeRequest";
+ private static final String DEV_NAME_CHANGE_RESP = "deviceNameChangeResponse";
+
+ private static final String ZERO_URI = "of:0000000000000000";
+
private static final String ID = "id";
private static final String TYPE = "type";
private static final String AVAILABLE = "available";
@@ -72,25 +84,41 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
private static final String ENABLED = "enabled";
private static final String SPEED = "speed";
private static final String NAME = "name";
+ private static final String WARN = "warn";
private static final String[] COL_IDS = {
- AVAILABLE, AVAILABLE_IID, TYPE_IID, ID,
- NUM_PORTS, MASTER_ID, MFR, HW, SW,
+ AVAILABLE, AVAILABLE_IID, TYPE_IID,
+ NAME, ID, MASTER_ID, NUM_PORTS, MFR, HW, SW,
PROTOCOL, CHASSIS_ID, SERIAL
};
private static final String ICON_ID_ONLINE = "active";
private static final String ICON_ID_OFFLINE = "inactive";
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+
@Override
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(
new DataRequestHandler(),
+ new NameChangeHandler(),
new DetailRequestHandler()
);
}
+ // Get friendly name of the device from the annotations
+ private static String deviceName(Device device) {
+ String name = device.annotations().value(AnnotationKeys.NAME);
+ return isNullOrEmpty(name) ? device.id().toString() : name;
+ }
+
+ private static String deviceProtocol(Device device) {
+ String protocol = device.annotations().value(PROTOCOL);
+ return protocol != null ? protocol : "";
+ }
+
private static String getTypeIconId(Device d) {
return DEV_ICON_PREFIX + d.type().toString();
}
@@ -121,16 +149,15 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
boolean available = ds.isAvailable(id);
String iconId = available ? ICON_ID_ONLINE : ICON_ID_OFFLINE;
- String protocol = dev.annotations().value(PROTOCOL);
-
row.cell(ID, id)
+ .cell(NAME, deviceName(dev))
.cell(AVAILABLE, available)
.cell(AVAILABLE_IID, iconId)
.cell(TYPE_IID, getTypeIconId(dev))
.cell(MFR, dev.manufacturer())
.cell(HW, dev.hwVersion())
.cell(SW, dev.swVersion())
- .cell(PROTOCOL, protocol != null ? protocol : "")
+ .cell(PROTOCOL, deviceProtocol(dev))
.cell(NUM_PORTS, ds.getPorts(id).size())
.cell(MASTER_ID, ms.getMasterFor(id));
}
@@ -144,15 +171,16 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
@Override
public void process(long sid, ObjectNode payload) {
- String id = string(payload, "id", "of:0000000000000000");
+ String id = string(payload, ID, ZERO_URI);
- DeviceId deviceId = DeviceId.deviceId(id);
+ DeviceId deviceId = deviceId(id);
DeviceService service = get(DeviceService.class);
MastershipService ms = get(MastershipService.class);
Device device = service.getDevice(deviceId);
- ObjectNode data = MAPPER.createObjectNode();
+ ObjectNode data = objectNode();
data.put(ID, deviceId.toString());
+ data.put(NAME, deviceName(device));
data.put(TYPE, capitalizeFully(device.type().toString()));
data.put(TYPE_IID, getTypeIconId(device));
data.put(MFR, device.manufacturer());
@@ -161,9 +189,9 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
data.put(SERIAL, device.serialNumber());
data.put(CHASSIS_ID, device.chassisId().toString());
data.put(MASTER_ID, ms.getMasterFor(deviceId).toString());
- data.put(PROTOCOL, device.annotations().value(PROTOCOL));
+ data.put(PROTOCOL, deviceProtocol(device));
- ArrayNode ports = MAPPER.createArrayNode();
+ ArrayNode ports = arrayNode();
List<Port> portList = new ArrayList<>(service.getPorts(deviceId));
Collections.sort(portList, (p1, p2) -> {
@@ -176,13 +204,13 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
}
data.set(PORTS, ports);
- ObjectNode rootNode = MAPPER.createObjectNode();
+ ObjectNode rootNode = objectNode();
rootNode.set(DETAILS, data);
sendMessage(DEV_DETAILS_RESP, 0, rootNode);
}
private ObjectNode portData(Port p, DeviceId id) {
- ObjectNode port = MAPPER.createObjectNode();
+ ObjectNode port = objectNode();
LinkService ls = get(LinkService.class);
String name = p.annotations().value(AnnotationKeys.PORT_NAME);
@@ -206,4 +234,29 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
return port;
}
}
+
+
+ // handler for changing device friendly name
+ private final class NameChangeHandler extends RequestHandler {
+ private NameChangeHandler() {
+ super(DEV_NAME_CHANGE_REQ);
+ }
+
+ @Override
+ public void process(long sid, ObjectNode payload) {
+ DeviceId deviceId = deviceId(string(payload, ID, ZERO_URI));
+ String name = emptyToNull(string(payload, NAME, null));
+ log.debug("Name change request: {} -- '{}'", deviceId, name);
+
+ NetworkConfigService service = get(NetworkConfigService.class);
+ BasicDeviceConfig cfg =
+ service.addConfig(deviceId, BasicDeviceConfig.class);
+
+ // Name attribute missing from the payload (or empty string)
+ // means that the friendly name should be unset.
+ cfg.name(name);
+ cfg.apply();
+ sendMessage(DEV_NAME_CHANGE_RESP, 0, payload);
+ }
+ }
}
diff --git a/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/ProcessorViewMessageHandler.java b/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/ProcessorViewMessageHandler.java
new file mode 100644
index 00000000..5d97504e
--- /dev/null
+++ b/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/ProcessorViewMessageHandler.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.ui.impl;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableSet;
+import org.onosproject.net.packet.PacketProcessorEntry;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.ui.RequestHandler;
+import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.table.TableModel;
+import org.onosproject.ui.table.TableRequestHandler;
+import org.onosproject.ui.table.cell.NumberFormatter;
+
+import java.util.Collection;
+
+import static org.onosproject.net.packet.PacketProcessor.ADVISOR_MAX;
+import static org.onosproject.net.packet.PacketProcessor.DIRECTOR_MAX;
+
+/**
+ * Message handler for packet processor view related messages.
+ */
+public class ProcessorViewMessageHandler extends UiMessageHandler {
+
+ private static final String PROCESSOR_DATA_REQ = "processorDataRequest";
+ private static final String PROCESSOR_DATA_RESP = "processorDataResponse";
+ private static final String PROCESSORS = "processors";
+
+ private static final String OBSERVER = "observer";
+ private static final String DIRECTOR = "director";
+ private static final String ADVISOR = "advisor";
+
+ private static final String ID = "id";
+ private static final String TYPE = "type";
+ private static final String PRIORITY = "priority";
+ private static final String PROCESSOR = "processor";
+ private static final String PACKETS = "packets";
+ private static final String AVG_MS = "avgMillis";
+
+ private static final long NANOS_IN_MS = 1_000_000;
+
+ private static final String[] COL_IDS = {
+ ID, TYPE, PRIORITY, PROCESSOR, PACKETS, AVG_MS
+ };
+
+ @Override
+ protected Collection<RequestHandler> createRequestHandlers() {
+ return ImmutableSet.of(new ProcessorDataRequest());
+ }
+
+ // handler for packet processor table requests
+ private final class ProcessorDataRequest extends TableRequestHandler {
+ private ProcessorDataRequest() {
+ super(PROCESSOR_DATA_REQ, PROCESSOR_DATA_RESP, PROCESSORS);
+ }
+
+ @Override
+ protected String[] getColumnIds() {
+ return COL_IDS;
+ }
+
+ @Override
+ protected TableModel createTableModel() {
+ TableModel tm = super.createTableModel();
+ tm.setFormatter(AVG_MS, new NumberFormatter());
+ return tm;
+ }
+
+ @Override
+ protected void populateTable(TableModel tm, ObjectNode payload) {
+ PacketService ps = get(PacketService.class);
+ ps.getProcessors().forEach(entry -> populateRow(tm.addRow(), entry));
+ }
+
+ private void populateRow(TableModel.Row row, PacketProcessorEntry entry) {
+ row.cell(ID, entry.priority())
+ .cell(TYPE, processorType(entry.priority()))
+ .cell(PRIORITY, processorPriority(entry.priority()))
+ .cell(PROCESSOR, entry.processor().getClass().getName())
+ .cell(PACKETS, entry.invocations())
+ .cell(AVG_MS, (double) entry.averageNanos() / NANOS_IN_MS);
+ }
+
+ private String processorType(int p) {
+ return p > DIRECTOR_MAX ? OBSERVER : p > ADVISOR_MAX ? DIRECTOR : ADVISOR;
+ }
+
+ private int processorPriority(int p) {
+ return p > DIRECTOR_MAX ? (p - DIRECTOR_MAX - 1) :
+ p > ADVISOR_MAX ? (p - ADVISOR_MAX - 1) : (p - 1);
+ }
+
+ }
+}
diff --git a/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java b/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
index 2bd0bb61..86cf038b 100644
--- a/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
+++ b/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
@@ -77,6 +77,7 @@ public class UiExtensionManager implements UiExtensionService, SpriteService {
new UiView(PLATFORM, "app", "Applications", "nav_apps"),
new UiView(PLATFORM, "settings", "Settings", "nav_settings"),
new UiView(PLATFORM, "cluster", "Cluster Nodes", "nav_cluster"),
+ new UiView(PLATFORM, "processor", "Packet Processors", "nav_processors"),
new UiView(NETWORK, "topo", "Topology", "nav_topo"),
new UiView(NETWORK, "device", "Devices", "nav_devs"),
new UiViewHidden("flow"),
@@ -102,6 +103,7 @@ public class UiExtensionManager implements UiExtensionService, SpriteService {
new ApplicationViewMessageHandler(),
new SettingsViewMessageHandler(),
new ClusterViewMessageHandler(),
+ new ProcessorViewMessageHandler(),
new TunnelViewMessageHandler()
);
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/glyph.js b/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/glyph.js
index 838a2ac0..28f262a1 100644
--- a/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/glyph.js
+++ b/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/glyph.js
@@ -55,6 +55,15 @@
unknown: "M35,40a5,5,0,0,1,5-5h30a5,5,0,0,1,5,5v30a5,5,0,0,1-5,5" +
"h-30a5,5,0,0,1-5-5z",
+ query: "M51.4,69.9c0-0.9,0-1.6,0-2.1c0-2.7,0.4-5.1,1.2-7.1" +
+ "c0.6-1.5,1.5-3,2.8-4.5c0.9-1.1,2.6-2.7,5.1-4.8c2.4-2.1,4-3.8," +
+ "4.8-5.1 c0.7-1.3,1.1-2.6,1.1-4.1c0-2.7-1.1-5.1-3.2-7.1c-2.1-2" +
+ "-4.8-3.1-7.9-3.1c-3,0-5.5,0.9-7.5,2.8c-2,1.9-3.3,4.8-4,8.7l-7.2" +
+ "-0.8 c0.7-5.3,2.6-9.3,5.8-12.1c3.2-2.8,7.5-4.2,12.8-4.2c5.6,0," +
+ "10.1,1.5,13.4,4.5c3.3,3,5,6.7,5,10.9c0,2.5-0.6,4.8-1.8,6.8 " +
+ "s-3.5,4.6-6.9,7.6c-2.3,2-3.8,3.5-4.5,4.4c-0.7,1-1.2,2-1.6,3.3" +
+ "c-0.3,1.2-0.5,3.2-0.6,6H51.4z M51,83.8v-7.9h8v7.9H51z",
+
node: "M15,100a5,5,0,0,1-5-5v-65a5,5,0,0,1,5-5h80a5,5,0,0,1,5,5" +
"v65a5,5,0,0,1-5,5zM14,22.5l11-11a10,3,0,0,1,10-2h40a10,3,0,0,1," +
"10,2l11,11zM16,35a5,5,0,0,1,10,0a5,5,0,0,1-10,0z",
@@ -84,6 +93,22 @@
"M50,29l12,0,0-8,18,13-18,13,0-8-12,0zM60,57l-12,0,0-8-18,13," +
"18,13,0-8,12,0z",
+ microwave: "M85,71.2c-8.9,10.5-29.6,8.7-45.3-3.5C23.9,55.4,19.8," +
+ "37,28.6,26.5C29.9,38.6,71.5,69.9,85,71.2z M92.7,76.2M16.2,15 " +
+ "M69.5,100.7v-4c0-1.4-1.2-2.2-2.6-2.2H19.3c-1.4,0-2.8,0.7-2.8,2.2" +
+ "v3.9c0,0.7,0.8,1,1.5,1h50.3C69,101.5,69.5,101.3,69.5,100.7z " +
+ "M77.3,7.5l0,3.7c9,0.1,16.3,7.1,16.2,15.7l3.9,0C97.5,16.3,88.5," +
+ "7.6,77.3,7.5z M77.6,14.7l0,2.5c5.3,0,9.7,4.2,9.6,9.3l2.6,0C89.9" +
+ ",20,84.4,14.7,77.6,14.7z M82.3,22.2c-1.3-1.2-2.9-1.9-4.7-1.9" +
+ "l0,1.2c1.4,0,2.8,0.6,3.8,1.5c1,1,1.6,2.3,1.6,3.7l1.3,0C84.3,25.1," +
+ "83.6,23.4,82.3,22.2z M38.9,69.5l-5.1,23h16.5l-2.5-17.2C44.1,73.3," +
+ "38.9,69.5,38.9,69.5zM58.1,54.1c13.7,10.1,26.5,16.8,29.2,13.7" +
+ "c2.7-3.1-5.6-13-19.3-24.4 M62.9,34.2 M62,37.9C47.7,27.3,33.7,20," +
+ "31,23.1c-2.7,3.2,7,14.2,20.6,26 M73.9,25.7c-2.9,0.1-5.2,2.3-5.1," +
+ "4.8c0,0.7,0.2,1.4,0.6,2l0,0L53.8,49.7l3.3,2.5L72.7,35l-0.4-0.3" +
+ "c0.6,0.2,1.3,0.3,1.9,0.3c2.9-0.1,5.2-2.3,5.1-4.9C79.3,27.6,76.8," +
+ "25.6,73.9,25.7z",
+
chain: "M60.4,77.6c-4.9,5.2-9.6,11.3-15.3,16.3c-8.6,7.5-20.4,6.8" +
"-28-0.8c-7.7-7.7-8.4-19.6-0.8-28.4c6.5-7.4,13.5-14.4,20.9-20.9" +
"c7.5-6.7,19.2-6.7,26.5-0.8c3.5,2.8,4.4,6.1,2.2,8.7c-2.7,3.1" +
@@ -570,6 +595,10 @@
return glyphs.get(id);
}
+ function glyphDefined(id) {
+ return glyphs.has(id);
+ }
+
// Note: defs should be a D3 selection of a single <defs> element
function loadDefs(defs, glyphIds, noClear) {
var list = fs.isA(glyphIds) || ids(),
@@ -633,6 +662,7 @@
registerGlyphSet: registerGlyphSet,
ids: ids,
glyph: glyph,
+ glyphDefined: glyphDefined,
loadDefs: loadDefs,
addGlyph: addGlyph
};
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/icon.js b/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/icon.js
index ba794313..15b44bc9 100644
--- a/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/icon.js
+++ b/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/icon.js
@@ -64,7 +64,8 @@
nav_devs: 'switch',
nav_links: 'ports',
nav_hosts: 'endstation',
- nav_intents: 'relatedIntents'
+ nav_intents: 'relatedIntents',
+ nav_processors: 'allTraffic'
};
function ensureIconLibDefs() {
@@ -162,6 +163,7 @@
// Returns the D3 selection of the icon.
function addDeviceIcon(elem, glyphId) {
var cfg = config.device,
+ gid = gs.glyphDefined(glyphId) ? glyphId : 'query',
g = elem.append('g')
.attr('class', 'svgIcon deviceIcon');
@@ -174,7 +176,7 @@
});
g.append('use').attr({
- 'xlink:href': '#' + glyphId,
+ 'xlink:href': '#' + gid,
width: cfg.dim,
height: cfg.dim
});
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/fw/util/keys.js b/framework/src/onos/web/gui/src/main/webapp/app/fw/util/keys.js
index 2985565c..5ff4f7f4 100644
--- a/framework/src/onos/web/gui/src/main/webapp/app/fw/util/keys.js
+++ b/framework/src/onos/web/gui/src/main/webapp/app/fw/util/keys.js
@@ -25,6 +25,7 @@
// internal state
var enabled = true,
+ globalEnabled = true,
keyHandler = {
globalKeys: {},
maskedKeys: {},
@@ -116,6 +117,9 @@
}
function quickHelp(view, key, code, ev) {
+ if (!globalEnabled) {
+ return false;
+ }
qhs.showQuickHelp(keyHandler);
return true;
}
@@ -126,6 +130,9 @@
}
function toggleTheme(view, key, code, ev) {
+ if (!globalEnabled) {
+ return false;
+ }
ts.toggleTheme();
return true;
}
@@ -173,6 +180,23 @@
keyHandler.viewGestures = [];
}
+ function checkNotGlobal(o) {
+ var oops = [];
+ if (fs.isO(o)) {
+ angular.forEach(o, function (val, key) {
+ if (keyHandler.globalKeys[key]) {
+ oops.push(key);
+ }
+ });
+ if (oops.length) {
+ $log.warn('Ignoring reserved global key(s):', oops.join(','));
+ oops.forEach(function (key) {
+ delete o[key];
+ });
+ }
+ }
+ }
+
angular.module('onosUtil')
.factory('KeyService',
['$log', 'FnService', 'ThemeService', 'NavService',
@@ -208,7 +232,11 @@
},
enableKeys: function (b) {
enabled = b;
- }
+ },
+ enableGlobalKeys: function (b) {
+ globalEnabled = b;
+ },
+ checkNotGlobal: checkNotGlobal
};
}]);
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.css b/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.css
index fc08f68b..e0e9cf57 100644
--- a/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.css
+++ b/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.css
@@ -75,6 +75,15 @@
margin: 8px 0;
}
+#device-details-panel .editable {
+ cursor: pointer;
+ border-bottom: 1px dashed darkgreen;
+}
+
+#device-details-panel h2 input {
+ font-size: 1.0em;
+}
+
#device-details-panel .top div.left {
float: left;
padding: 0 18px 0 0;
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.html b/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.html
index 5d51d1d4..63a04db8 100644
--- a/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.html
+++ b/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.html
@@ -35,13 +35,14 @@
<tr>
<td colId="available" class="table-icon" sortable></td>
<td colId="type" class="table-icon" sortable></td>
+ <td colId="name" sortable>Friendly Name </td>
<td colId="id" sortable>Device ID </td>
<td colId="masterid" sortable>Master Instance </td>
- <td colId="num_ports" sortable>Ports </td>
+ <td colId="num_ports" col-width="60px" sortable>Ports </td>
<td colId="mfr" sortable>Vendor </td>
<td colId="hw" sortable>H/W Version </td>
<td colId="sw" sortable>S/W Version </td>
- <td colId="protocol" sortable>Protocol </td>
+ <td colId="protocol" col-width="80px" sortable>Protocol </td>
</tr>
</table>
</div>
@@ -64,6 +65,7 @@
<td class="table-icon">
<div icon icon-id="{{dev._iconid_type}}"></div>
</td>
+ <td>{{dev.name}}</td>
<td>{{dev.id}}</td>
<td>{{dev.masterid}}</td>
<td>{{dev.num_ports}}</td>
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.js b/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.js
index 7a2dc4f9..5b7120fd 100644
--- a/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.js
+++ b/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.js
@@ -22,13 +22,17 @@
'use strict';
// injected refs
- var $log, $scope, $location, fs, mast, ps, wss, is, ns;
+ var $log, $scope, $loc, fs, mast, ps, wss, is, ns, ks;
// internal state
var detailsPanel,
- pStartY, pHeight,
- top, bottom, iconDiv,
- wSize;
+ pStartY,
+ pHeight,
+ top,
+ bottom,
+ iconDiv,
+ wSize,
+ editingName = false;
// constants
var topPdg = 13,
@@ -39,13 +43,15 @@
pName = 'device-details-panel',
detailsReq = 'deviceDetailsRequest',
detailsResp = 'deviceDetailsResponse',
+ nameChangeReq = 'deviceNameChangeRequest',
+ nameChangeResp = 'deviceNameChangeResponse',
propOrder = [
- 'type', 'masterid', 'chassisid',
+ 'id', 'type', 'masterid', 'chassisid',
'mfr', 'hw', 'sw', 'protocol', 'serial'
],
friendlyProps = [
- 'Type', 'Master ID', 'Chassis ID',
+ 'URI', 'Type', 'Master ID', 'Chassis ID',
'Vendor', 'H/W Version', 'S/W Version', 'Protocol', 'Serial #'
],
portCols = [
@@ -59,7 +65,9 @@
if (detailsPanel.isVisible()) {
$scope.selId = null;
detailsPanel.hide();
+ return true;
}
+ return false;
}
function addCloseBtn(div) {
@@ -68,6 +76,59 @@
div.on('click', closePanel);
}
+ function exitEditMode(nameH2, name) {
+ nameH2.html(name);
+ nameH2.classed('editable', true);
+ editingName = false;
+ ks.enableGlobalKeys(true);
+ }
+
+ function editNameSave() {
+ var nameH2 = top.select('h2'),
+ id = $scope.panelData.id,
+ val,
+ newVal;
+
+ if (editingName) {
+ val = nameH2.select('input').property('value').trim();
+ newVal = val || id;
+
+ exitEditMode(nameH2, newVal);
+ $scope.panelData.name = newVal;
+ wss.sendEvent(nameChangeReq, { id: id, name: val });
+ }
+ }
+
+ function editNameCancel() {
+ if (editingName) {
+ exitEditMode(top.select('h2'), $scope.panelData.name);
+ return true;
+ }
+ return false;
+ }
+
+ function editName() {
+ var nameH2 = top.select('h2'),
+ tf, el;
+
+ if (!editingName) {
+ nameH2.classed('editable', false);
+ nameH2.html('');
+ tf = nameH2.append('input').classed('name-input', true)
+ .attr('type', 'text')
+ .attr('value', $scope.panelData.name);
+ el = tf[0][0];
+ el.focus();
+ el.select();
+ editingName = true;
+ ks.enableGlobalKeys(false);
+ }
+ }
+
+ function handleEscape() {
+ return editNameCancel() || closePanel();
+ }
+
function setUpPanel() {
var container, closeBtn, tblDiv;
detailsPanel.empty();
@@ -78,7 +139,7 @@
closeBtn = top.append('div').classed('close-btn', true);
addCloseBtn(closeBtn);
iconDiv = top.append('div').classed('dev-icon', true);
- top.append('h2');
+ top.append('h2').classed('editable', true).on('click', editName);
tblDiv = top.append('div').classed('top-tables', true);
tblDiv.append('div').classed('left', true).append('table');
@@ -110,11 +171,11 @@
.append('tbody');
is.loadEmbeddedIcon(iconDiv, details._iconid_type, 40);
- top.select('h2').html(details.id);
+ top.select('h2').html(details.name);
propOrder.forEach(function (prop, i) {
// properties are split into two tables
- addProp(i < 3 ? leftTbl : rightTbl, i, details[prop]);
+ addProp(i < 4 ? leftTbl : rightTbl, i, details[prop]);
});
}
@@ -156,14 +217,23 @@
detailsPanel.width(tbWidth + ctnrPdg);
}
+ function populateName(div, name) {
+ var lab = div.select('.label'),
+ val = div.select('.value');
+ lab.html('Friendly Name:');
+ val.html(name);
+ }
+
function populateDetails(details) {
- var topTbs, btmTbl, ports;
+ var nameDiv, topTbs, btmTbl, ports;
setUpPanel();
+ nameDiv = top.select('.name-div');
topTbs = top.select('.top-tables');
btmTbl = bottom.select('table');
ports = details.ports;
+ populateName(nameDiv, details.name);
populateTop(topTbs, details);
populateBottom(btmTbl, ports);
@@ -175,6 +245,13 @@
$scope.$apply();
}
+ function respNameCb(data) {
+ if (data.warn) {
+ $log.warn(data.warn, data.id);
+ top.select('h2').html(data.id);
+ }
+ }
+
function createDetailsPane() {
detailsPanel = ps.createPanel(pName, {
width: wSize.width,
@@ -193,21 +270,26 @@
.controller('OvDeviceCtrl',
['$log', '$scope', '$location', 'TableBuilderService', 'FnService',
'MastService', 'PanelService', 'WebSocketService', 'IconService',
- 'NavService',
+ 'NavService', 'KeyService',
function (_$log_, _$scope_, _$location_,
- tbs, _fs_, _mast_, _ps_, _wss_, _is_, _ns_) {
+ tbs, _fs_, _mast_, _ps_, _wss_, _is_, _ns_, _ks_) {
+ var params,
+ handlers = {};
+
$log = _$log_;
$scope = _$scope_;
- $location = _$location_;
+ $loc = _$location_;
fs = _fs_;
mast = _mast_;
ps = _ps_;
wss = _wss_;
is = _is_;
ns = _ns_;
- var params = $location.search(),
- handlers = {};
+ ks = _ks_;
+
+ params = $loc.search();
+
$scope.panelData = {};
$scope.flowTip = 'Show flow view for selected device';
$scope.portTip = 'Show port view for selected device';
@@ -215,6 +297,7 @@
// details panel handlers
handlers[detailsResp] = respDetailsCb;
+ handlers[nameChangeResp] = respNameCb;
wss.bindHandlers(handlers);
// query for if a certain device needs to be highlighted
@@ -278,7 +361,8 @@
}
// create key bindings to handle panel
ks.keyBindings({
- esc: [closePanel, 'Close the details panel'],
+ enter: editNameSave,
+ esc: [handleEscape, 'Close the details panel'],
_helpFormat: ['esc']
});
ks.gestureNotes([
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.css b/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.css
new file mode 100644
index 00000000..12cf6377
--- /dev/null
+++ b/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.css
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Processor View -- CSS file
+ */
+
+#ov-processor h2 {
+ display: inline-block;
+}
+
+#ov-processor div.ctrl-btns {
+ width: 40px;
+}
+
+.light #ov-processor .current-view use {
+ fill: white;
+}
+.dark #ov-processor .current-view use {
+ fill: #304860;
+}
+
+.light #ov-processor .current-view rect {
+ fill: deepskyblue;
+}
+.dark #ov-processor .current-view rect {
+ fill: #eee;
+}
+
+#ov-processor td.number {
+ text-align: right;
+}
+
+#ov-processor tr.no-data td {
+ text-align: center;
+}
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.html b/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.html
new file mode 100644
index 00000000..1c615041
--- /dev/null
+++ b/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.html
@@ -0,0 +1,63 @@
+<!-- processor partial HTML -->
+<div id="ov-processor">
+ <div class="tabular-header">
+ <h2>
+ Packet Processors ({{tableData.length}} Processors total)
+ </h2>
+ <div class="ctrl-btns">
+ <div class="refresh" ng-class="{active: autoRefresh}"
+ icon icon-size="36" icon-id="refresh"
+ tooltip tt-msg="autoRefreshTip"
+ ng-click="toggleRefresh()"></div>
+ <!--
+ <div class="separator"></div>
+
+ <div class="current-view"
+ icon icon-id="processorTable" icon-size="36"></div>
+
+ <div class="active"
+ icon icon-id="requestTable" icon-size="36"git sta
+ tooltip tt-msg="requestTip"
+ ng-click="nav('request')"></div>
+ -->
+ </div>
+ </div>
+
+ <div class="summary-list" onos-table-resize>
+ <div ng-show="loading" class="loading-wheel"
+ icon icon-id="loading" icon-size="75"></div>
+
+ <div class="table-header" onos-sortable-header>
+ <table>
+ <tr>
+ <td class="number" colId="priority" sortable col-width="80px">Priority </td>
+ <td colId="type" sortable col-width="80px">Type </td>
+ <td colId="processor" sortable col-width="500px">Class </td>
+ <td class="number" colId="packets" sortable col-width="100px">Packets </td>
+ <td class="number" colId="avgMillis" sortable col-width="100px">Average (ms) </td>
+ </tr>
+ </table>
+ </div>
+
+ <div class="table-body">
+ <table onos-flash-changes id-prop="id">
+ <tr ng-if="!tableData.length" class="no-data">
+ <td colspan="5">
+ No Processors found
+ </td>
+ </tr>
+
+ <tr ng-repeat="processor in tableData track by $index"
+ ng-repeat-complete row-id="{{processor.id}}">
+ <td class="number">{{processor.priority}}</td>
+ <td>{{processor.type}}</td>
+ <td>{{processor.processor}}</td>
+ <td class="number">{{processor.packets}}</td>
+ <td class="number">{{processor.avgMillis}}</td>
+ </tr>
+ </table>
+ </div>
+
+ </div>
+
+</div>
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.js b/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.js
new file mode 100644
index 00000000..89d717b6
--- /dev/null
+++ b/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.js
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Packet Processor View Module
+ */
+
+(function () {
+ 'use strict';
+
+ // injected references
+ var $log, $scope, $location, fs, tbs, ns;
+
+ angular.module('ovProcessor', [])
+ .controller('OvProcessorCtrl',
+ ['$log', '$scope', '$location',
+ 'FnService', 'TableBuilderService', 'NavService',
+
+ function (_$log_, _$scope_, _$location_, _fs_, _tbs_, _ns_) {
+ var params;
+ $log = _$log_;
+ $scope = _$scope_;
+ $location = _$location_;
+ fs = _fs_;
+ tbs = _tbs_;
+ ns = _ns_;
+ $scope.requestTip = 'Show packet requests';
+
+ params = $location.search();
+
+ tbs.buildTable({
+ scope: $scope,
+ tag: 'processor',
+ query: params
+ });
+
+ $scope.nav = function (path) {
+ if ($scope.devId) {
+ ns.navTo(path);
+ }
+ };
+
+ $log.log('OvProcessorCtrl has been created');
+ }]);
+}());
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/settings/settings.html b/framework/src/onos/web/gui/src/main/webapp/app/view/settings/settings.html
index ee069d37..61081017 100644
--- a/framework/src/onos/web/gui/src/main/webapp/app/view/settings/settings.html
+++ b/framework/src/onos/web/gui/src/main/webapp/app/view/settings/settings.html
@@ -17,7 +17,7 @@
<div class="table-header" onos-sortable-header>
<table>
<tr>
- <td colId="component" sortable col-width="200px">Component </td>
+ <td colId="component" sortable col-width="300px">Component </td>
<td colId="id" sortable>Property </td>
<td colId="type" sortable col-width="70px">Type </td>
<td colId="value" sortable>Value </td>
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topo.js b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topo.js
index 21894100..42b6f4bd 100644
--- a/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -95,6 +95,9 @@
// and include them in the quick-help panel
function mergeKeys(extra) {
var _hf = actionMap._helpFormat[2];
+
+ ks.checkNotGlobal(extra);
+
extra._keyOrder.forEach(function (k) {
var d = extra[k],
cb = d && d.cb,
diff --git a/framework/src/onos/web/gui/src/main/webapp/index.html b/framework/src/onos/web/gui/src/main/webapp/index.html
index 154f9416..5df3c664 100644
--- a/framework/src/onos/web/gui/src/main/webapp/index.html
+++ b/framework/src/onos/web/gui/src/main/webapp/index.html
@@ -121,6 +121,7 @@
<script src="app/view/app/app.js"></script>
<script src="app/view/settings/settings.js"></script>
<script src="app/view/cluster/cluster.js"></script>
+ <script src="app/view/processor/processor.js"></script>
<script src="app/view/tunnel/tunnel.js"></script>
<!-- This is where contributed javascript will get injected -->
@@ -139,6 +140,7 @@
<link rel="stylesheet" href="app/view/app/app.css">
<link rel="stylesheet" href="app/view/settings/settings.css">
<link rel="stylesheet" href="app/view/cluster/cluster.css">
+ <link rel="stylesheet" href="app/view/processor/processor.css">
<link rel="stylesheet" href="app/view/tunnel/tunnel.css">
<!-- This is where contributed stylesheets will get injected -->
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_10_addDevice_s9_ids.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_10_addDevice_s9_ids.json
new file mode 100644
index 00000000..232d4edb
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_10_addDevice_s9_ids.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:0000000000000009",
+ "type": "ids",
+ "online": true,
+ "master": "ONOS",
+ "labels": [
+ "",
+ "ids",
+ "of:0000000000000009"
+ ],
+ "metaUi": {
+ "x": 200,
+ "y": 400
+ }
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_11_addDevice_s10_controller.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_11_addDevice_s10_controller.json
new file mode 100644
index 00000000..abd24d66
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_11_addDevice_s10_controller.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:00000000000000010",
+ "type": "controller",
+ "online": true,
+ "master": "ONOS",
+ "labels": [
+ "",
+ "controller",
+ "of:0000000000000010"
+ ],
+ "metaUi": {
+ "x": 350,
+ "y": 400
+ }
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_12_addDevice_s11_virtual.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_12_addDevice_s11_virtual.json
new file mode 100644
index 00000000..1fe1837b
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_12_addDevice_s11_virtual.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:00000000000000011",
+ "type": "virtual",
+ "online": true,
+ "master": "ONOS",
+ "labels": [
+ "",
+ "virtual",
+ "of:0000000000000011"
+ ],
+ "metaUi": {
+ "x": 500,
+ "y": 400
+ }
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_13_addDevice_s12_fiber_switch.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_13_addDevice_s12_fiber_switch.json
new file mode 100644
index 00000000..954376cd
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_13_addDevice_s12_fiber_switch.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:00000000000000012",
+ "type": "fiber_switch",
+ "online": true,
+ "master": "ONOS",
+ "labels": [
+ "",
+ "fiber_switch",
+ "of:0000000000000012"
+ ],
+ "metaUi": {
+ "x": 650,
+ "y": 400
+ }
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_14_addDevice_s13_microwave.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_14_addDevice_s13_microwave.json
new file mode 100644
index 00000000..3d40ceca
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_14_addDevice_s13_microwave.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:00000000000000013",
+ "type": "microwave",
+ "online": true,
+ "master": "ONOS",
+ "labels": [
+ "",
+ "microwave",
+ "of:0000000000000013"
+ ],
+ "metaUi": {
+ "x": 300,
+ "y": 500
+ }
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_15_addDevice_s14_other.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_15_addDevice_s14_other.json
new file mode 100644
index 00000000..e33532d7
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_15_addDevice_s14_other.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:00000000000000014",
+ "type": "other",
+ "online": true,
+ "master": "ONOS",
+ "labels": [
+ "",
+ "other",
+ "of:0000000000000014"
+ ],
+ "metaUi": {
+ "x": 450,
+ "y": 500
+ }
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_16_addDevice_s15_unmatched.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_16_addDevice_s15_unmatched.json
new file mode 100644
index 00000000..1773c917
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_16_addDevice_s15_unmatched.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:00000000000000015",
+ "type": "-unmatched-",
+ "online": true,
+ "master": "ONOS-B",
+ "labels": [
+ "",
+ "-unmatched-",
+ "of:0000000000000015"
+ ],
+ "metaUi": {
+ "x": 600,
+ "y": 500
+ }
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_1_addInstance.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_1_addInstance.json
new file mode 100644
index 00000000..20be9e2b
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_1_addInstance.json
@@ -0,0 +1,14 @@
+{
+ "event": "addInstance",
+ "payload": {
+ "id": "ONOS",
+ "ip": "192.168.56.101",
+ "online": true,
+ "uiAttached": true,
+ "switches": 4,
+ "labels": [
+ "ONOS",
+ "192.168.56.101"
+ ]
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_2_addDevice_s1_switch.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_2_addDevice_s1_switch.json
new file mode 100644
index 00000000..5f8ad66e
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_2_addDevice_s1_switch.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:0000000000000001",
+ "type": "switch",
+ "online": true,
+ "master": "ONOS",
+ "labels": [
+ "",
+ "switch",
+ "of:0000000000000001"
+ ],
+ "metaUi": {
+ "x": 200,
+ "y": 200
+ }
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_3_addDevice_s2_router.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_3_addDevice_s2_router.json
new file mode 100644
index 00000000..b6da44ce
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_3_addDevice_s2_router.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:0000000000000002",
+ "type": "router",
+ "online": true,
+ "master": "ONOS",
+ "labels": [
+ "",
+ "router",
+ "of:0000000000000002"
+ ],
+ "metaUi": {
+ "x": 350,
+ "y": 200
+ }
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_4_addDevice_s3_roadm.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_4_addDevice_s3_roadm.json
new file mode 100644
index 00000000..468749ef
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_4_addDevice_s3_roadm.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:0000000000000003",
+ "type": "roadm",
+ "online": true,
+ "master": "ONOS",
+ "labels": [
+ "",
+ "roadm",
+ "of:0000000000000003"
+ ],
+ "metaUi": {
+ "x": 500,
+ "y": 200
+ }
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_5_addDevice_s4_otn.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_5_addDevice_s4_otn.json
new file mode 100644
index 00000000..c8e234be
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_5_addDevice_s4_otn.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:0000000000000004",
+ "type": "otn",
+ "online": true,
+ "master": "ONOS",
+ "labels": [
+ "",
+ "otn",
+ "of:0000000000000004"
+ ],
+ "metaUi": {
+ "x": 650,
+ "y": 200
+ }
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_6_addDevice_s5_roadm_otn.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_6_addDevice_s5_roadm_otn.json
new file mode 100644
index 00000000..c54cc4ae
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_6_addDevice_s5_roadm_otn.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:0000000000000005",
+ "type": "roadm_otn",
+ "online": true,
+ "master": "ONOS",
+ "labels": [
+ "",
+ "roadm_otn",
+ "of:0000000000000005"
+ ],
+ "metaUi": {
+ "x": 300,
+ "y": 300
+ }
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_7_addDevice_s6_firewall.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_7_addDevice_s6_firewall.json
new file mode 100644
index 00000000..19d50d45
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_7_addDevice_s6_firewall.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:0000000000000006",
+ "type": "firewall",
+ "online": true,
+ "master": "ONOS",
+ "labels": [
+ "",
+ "firewall",
+ "of:0000000000000006"
+ ],
+ "metaUi": {
+ "x": 450,
+ "y": 300
+ }
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_8_addDevice_s7_balancer.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_8_addDevice_s7_balancer.json
new file mode 100644
index 00000000..d9a3b795
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_8_addDevice_s7_balancer.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:0000000000000007",
+ "type": "balancer",
+ "online": true,
+ "master": "ONOS",
+ "labels": [
+ "",
+ "balancer",
+ "of:0000000000000007"
+ ],
+ "metaUi": {
+ "x": 600,
+ "y": 300
+ }
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_9_addDevice_s8_ips.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_9_addDevice_s8_ips.json
new file mode 100644
index 00000000..c89f565e
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_9_addDevice_s8_ips.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:0000000000000008",
+ "type": "ips",
+ "online": true,
+ "master": "ONOS",
+ "labels": [
+ "",
+ "ips",
+ "of:0000000000000008"
+ ],
+ "metaUi": {
+ "x": 750,
+ "y": 300
+ }
+ }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/scenario.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/scenario.json
new file mode 100644
index 00000000..ee3c1eaf
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/scenario.json
@@ -0,0 +1,12 @@
+{
+ "comments": [
+ "Showing all device types"
+ ],
+ "title": "Show Device Types",
+ "params": {
+ "lastAuto": 16
+ },
+ "description": [
+ "Show all device types."
+ ]
+}