From 6a07d2d622eaa06953f3353e39c080984076e8de Mon Sep 17 00:00:00 2001 From: Ashlee Young Date: Fri, 9 Oct 2015 18:32:44 -0700 Subject: Updated master to commit id 6ee8aa3e67ce89908a8c93aa9445c6f71a18f986 Change-Id: I94b055ee2f298daf71e2ec794fd0f2495bd8081f --- .../rest/resources/FlowsWebResource.java | 24 ++-- .../rest/resources/NetworkConfigWebResource.java | 158 +++++++++++---------- .../rest/resources/PathsWebResource.java | 64 +++++---- .../rest/resources/StatisticsWebResource.java | 61 ++++++++ .../ui/impl/DeviceViewMessageHandler.java | 77 ++++++++-- .../ui/impl/ProcessorViewMessageHandler.java | 108 ++++++++++++++ .../onosproject/ui/impl/UiExtensionManager.java | 2 + .../web/gui/src/main/webapp/app/fw/svg/glyph.js | 30 ++++ .../web/gui/src/main/webapp/app/fw/svg/icon.js | 6 +- .../web/gui/src/main/webapp/app/fw/util/keys.js | 30 +++- .../gui/src/main/webapp/app/view/device/device.css | 9 ++ .../src/main/webapp/app/view/device/device.html | 6 +- .../gui/src/main/webapp/app/view/device/device.js | 116 ++++++++++++--- .../main/webapp/app/view/processor/processor.css | 49 +++++++ .../main/webapp/app/view/processor/processor.html | 63 ++++++++ .../main/webapp/app/view/processor/processor.js | 58 ++++++++ .../main/webapp/app/view/settings/settings.html | 2 +- .../web/gui/src/main/webapp/app/view/topo/topo.js | 3 + .../src/onos/web/gui/src/main/webapp/index.html | 2 + .../_karma/ev/devices/ev_10_addDevice_s9_ids.json | 18 +++ .../ev/devices/ev_11_addDevice_s10_controller.json | 18 +++ .../ev/devices/ev_12_addDevice_s11_virtual.json | 18 +++ .../devices/ev_13_addDevice_s12_fiber_switch.json | 18 +++ .../ev/devices/ev_14_addDevice_s13_microwave.json | 18 +++ .../ev/devices/ev_15_addDevice_s14_other.json | 18 +++ .../ev/devices/ev_16_addDevice_s15_unmatched.json | 18 +++ .../test/_karma/ev/devices/ev_1_addInstance.json | 14 ++ .../ev/devices/ev_2_addDevice_s1_switch.json | 18 +++ .../ev/devices/ev_3_addDevice_s2_router.json | 18 +++ .../_karma/ev/devices/ev_4_addDevice_s3_roadm.json | 18 +++ .../_karma/ev/devices/ev_5_addDevice_s4_otn.json | 18 +++ .../ev/devices/ev_6_addDevice_s5_roadm_otn.json | 18 +++ .../ev/devices/ev_7_addDevice_s6_firewall.json | 18 +++ .../ev/devices/ev_8_addDevice_s7_balancer.json | 18 +++ .../_karma/ev/devices/ev_9_addDevice_s8_ips.json | 18 +++ .../gui/src/test/_karma/ev/devices/scenario.json | 12 ++ 36 files changed, 1014 insertions(+), 150 deletions(-) create mode 100644 framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/ProcessorViewMessageHandler.java create mode 100644 framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.css create mode 100644 framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.html create mode 100644 framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.js create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_10_addDevice_s9_ids.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_11_addDevice_s10_controller.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_12_addDevice_s11_virtual.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_13_addDevice_s12_fiber_switch.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_14_addDevice_s13_microwave.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_15_addDevice_s14_other.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_16_addDevice_s15_unmatched.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_1_addInstance.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_2_addDevice_s1_switch.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_3_addDevice_s2_router.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_4_addDevice_s3_roadm.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_5_addDevice_s4_otn.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_6_addDevice_s5_roadm_otn.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_7_addDevice_s6_firewall.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_8_addDevice_s7_balancer.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_9_addDevice_s8_ips.json create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/devices/scenario.json (limited to 'framework/src/onos/web') 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 devices = get(DeviceService.class).getDevices(); for (final Device device : devices) { - final Iterable deviceEntries = service.getFlowEntries(device.id()); - if (deviceEntries != null) { - for (final FlowEntry entry : deviceEntries) { + final Iterable 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 deviceEntries = + final Iterable 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 deviceEntries = + final Iterable 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 deviceEntries = + final Iterable 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. @@ -49,6 +46,17 @@ public class PathsWebResource extends AbstractWebResource { return id.matches("..:..:..:..:..:../.*") ? HostId.hostId(id) : null; } + /** + * 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 paths = pathService.getPaths(srcElement, dstElement); - ObjectNode root = encodeArray(org.onosproject.net.Path.class, "paths", paths); - return ok(root).build(); + Set 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 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 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 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 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 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 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 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 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 @@ + Friendly Name Device ID Master Instance - Ports + Ports Vendor H/W Version S/W Version - Protocol + Protocol @@ -64,6 +65,7 @@
+ {{dev.name}} {{dev.id}} {{dev.masterid}} {{dev.num_ports}} 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 @@ + +
+
+

+ Packet Processors ({{tableData.length}} Processors total) +

+
+
+ +
+
+ +
+
+ +
+ + + + + + + + +
Priority Type Class Packets Average (ms)
+
+ +
+ + + + + + + + + + + + +
+ No Processors found +
{{processor.priority}}{{processor.type}}{{processor.processor}}{{processor.packets}}{{processor.avgMillis}}
+
+ +
+ +
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 @@
- + 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 @@ + @@ -139,6 +140,7 @@ + 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." + ] +} -- cgit 1.2.3-korg
Component Component Property Type Value