From 128c52cf62e8cbdd30773daa377e415499ad25fa Mon Sep 17 00:00:00 2001 From: daniel park Date: Mon, 4 Sep 2017 13:15:51 +0900 Subject: [PATCH] [ONOS-6982] Implement OpenStackNetworking UI Service - This implements the UI service for OpenStack Netwrorking App - When mouse is over host or device, based on VNI,the UI highlights related hosts, links and devices - The UI also supports flow trace functionality Change-Id: I1944f3237cc112ed5c5e0d19351759cc66145881 --- apps/openstacknetworkingui/BUCK | 34 ++ apps/openstacknetworkingui/pom.xml | 158 ++++++ .../openstacknetworkingui/OpenstackLink.java | 102 ++++ .../OpenstackLinkMap.java | 30 ++ .../OpenstackNetworkingUiManager.java | 187 ++++++++ .../OpenstackNetworkingUiMessageHandler.java | 452 ++++++++++++++++++ .../OpenstackNetworkingUiOverlay.java | 79 +++ .../OpenstackNetworkingUiService.java | 62 +++ .../cli/GetRestServerAuthInfoCommand.java | 34 ++ .../cli/GetRestServerCommand.java | 35 ++ .../cli/SetRestServerAuthInfoCommand.java | 45 ++ .../cli/SetRestServerCommand.java | 40 ++ .../cli/package-info.java | 20 + .../openstacknetworkingui/package-info.java | 20 + .../web/FlowTraceWebResource.java | 75 +++ .../web/package-info.java | 20 + .../OSGI-INF/blueprint/shell-config.xml | 31 ++ .../app/view/sonaTopov/sonaTopov.css | 126 +++++ .../app/view/sonaTopov/sonaTopov.html | 4 + .../app/view/sonaTopov/sonaTopovOverlay.js | 220 +++++++++ .../app/view/sonaTopov/sonaTopovService.js | 198 ++++++++ .../src/main/resources/sonaTopov/css.html | 1 + .../src/main/resources/sonaTopov/js.html | 2 + .../src/main/webapp/WEB-INF/web.xml | 39 ++ apps/pom.xml | 1 + modules.defs | 1 + 26 files changed, 2016 insertions(+) create mode 100644 apps/openstacknetworkingui/BUCK create mode 100644 apps/openstacknetworkingui/pom.xml create mode 100644 apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackLink.java create mode 100644 apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackLinkMap.java create mode 100644 apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiManager.java create mode 100644 apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiMessageHandler.java create mode 100644 apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiOverlay.java create mode 100644 apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiService.java create mode 100644 apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/GetRestServerAuthInfoCommand.java create mode 100644 apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/GetRestServerCommand.java create mode 100644 apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/SetRestServerAuthInfoCommand.java create mode 100644 apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/SetRestServerCommand.java create mode 100644 apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/package-info.java create mode 100644 apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/package-info.java create mode 100644 apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/web/FlowTraceWebResource.java create mode 100644 apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/web/package-info.java create mode 100644 apps/openstacknetworkingui/src/main/resources/OSGI-INF/blueprint/shell-config.xml create mode 100644 apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopov.css create mode 100644 apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopov.html create mode 100644 apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovOverlay.js create mode 100644 apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovService.js create mode 100644 apps/openstacknetworkingui/src/main/resources/sonaTopov/css.html create mode 100644 apps/openstacknetworkingui/src/main/resources/sonaTopov/js.html create mode 100644 apps/openstacknetworkingui/src/main/webapp/WEB-INF/web.xml diff --git a/apps/openstacknetworkingui/BUCK b/apps/openstacknetworkingui/BUCK new file mode 100644 index 0000000000..d07732838c --- /dev/null +++ b/apps/openstacknetworkingui/BUCK @@ -0,0 +1,34 @@ +COMPILE_DEPS = [ + '//lib:CORE_DEPS', + '//lib:JACKSON', + '//core/store/serializers:onos-core-serializers', + '//lib:javax.ws.rs-api', + '//utils/rest:onlab-rest', + '//lib:jersey-client', + '//cli:onos-cli', + '//lib:org.apache.karaf.shell.console', +] + +TEST_DEPS = [ + '//lib:TEST_ADAPTERS', + '//core/api:onos-api-tests', + '//core/common:onos-core-common-tests', +] + +osgi_jar_with_tests ( + deps = COMPILE_DEPS, + test_deps = TEST_DEPS, + web_context = '/onos/openstacknetworkingui', + api_title = 'OpenStack Networking UI REST API', + api_version = '0.9', + api_description = 'OpenStack Networking UI REST API', + api_package = 'org.onosproject.openstacknetworkingui.web', +) + +onos_app ( + app_name = 'org.onosproject.openstacknetworkingui', + title = 'Openstack Networking UI', + category = 'Utility', + url = 'http://onosproject.org', + description = 'Openstack Networking UI Service', +) diff --git a/apps/openstacknetworkingui/pom.xml b/apps/openstacknetworkingui/pom.xml new file mode 100644 index 0000000000..de9d24bcde --- /dev/null +++ b/apps/openstacknetworkingui/pom.xml @@ -0,0 +1,158 @@ + + + + + 4.0.0 + + + org.onosproject + onos-apps + 1.12.0-SNAPSHOT + + + onos-apps-openstacknetworkingui + bundle + + OpenStack Networking UI Service + http://onosproject.org + + + UTF-8 + org.onosproject.openstacknetworkingui + OpenStack Networking UI Service + UI + http://onosproject.org + OpenStack Networking UI Application + /onos/openstacknetworkingui + 1.0.0 + OpenStack Networking UI REST API + + APIs for interacting with OpenStack Networking Monitoring server. + + org.onosproject.openstacknetworkingui.web + + + + + org.onosproject + onos-api + ${project.version} + + + + org.onosproject + onlab-osgi + ${project.version} + + + + javax.ws.rs + javax.ws.rs-api + 2.0.1 + + + + org.onosproject + onos-cli + ${project.version} + + + + org.apache.karaf.shell + org.apache.karaf.shell.console + + + + org.apache.felix + org.apache.felix.scr.annotations + 1.9.12 + provided + + + + org.glassfish.jersey.containers + jersey-container-servlet + + + + org.glassfish.jersey.core + jersey-client + + + + org.glassfish.jersey.core + jersey-common + 2.25 + + + + com.fasterxml.jackson.core + jackson-databind + + + + com.fasterxml.jackson.core + jackson-annotations + + + + commons-io + commons-io + 2.4 + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + <_wab>src/main/webapp/ + + WEB-INF/classes/apidoc/swagger.json=target/swagger.json, + {maven-resources} + + + ${project.groupId}.${project.artifactId} + + + *,org.glassfish.jersey.servlet + + ${web.context} + + + + + + org.apache.felix + maven-scr-plugin + + + + org.onosproject + onos-maven-plugin + + + + + + + diff --git a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackLink.java b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackLink.java new file mode 100644 index 0000000000..7da4402ea8 --- /dev/null +++ b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackLink.java @@ -0,0 +1,102 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.openstacknetworkingui; + +import org.onosproject.net.Link; +import org.onosproject.net.LinkKey; + +import org.onosproject.ui.topo.BiLink; +import org.onosproject.ui.topo.LinkHighlight; +import org.onosproject.ui.topo.Mod; + +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static org.onosproject.ui.topo.LinkHighlight.Flavor.SECONDARY_HIGHLIGHT; + +/** + * Link for OpenStack Networking UI service. + */ +public class OpenstackLink extends BiLink { + + private static final Mod PORT_TRAFFIC_GREEN = new Mod("port-traffic-green"); + private static final Mod PORT_TRAFFIC_ORANGE = new Mod("port-traffic-orange"); + + public OpenstackLink(LinkKey key, Link link) { + super(key, link); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof OpenstackLink) { + OpenstackLink that = (OpenstackLink) obj; + if (Objects.equals(linkId(), that.linkId())) { + return true; + } + } + + return false; + } + + @Override + public int hashCode() { + return Objects.hash(linkId()); + } + + @Override + public String toString() { + return toStringHelper(this) + .add("linkId", linkId()) + .add("link src", one().src().deviceId()) + .add("link dst", one().dst().deviceId()) + .toString(); + } + + @Override + public LinkHighlight highlight(Enum type) { + RequestType requestType = (RequestType) type; + + Mod m = null; + + switch (requestType) { + case HOST_SELECTED: + m = PORT_TRAFFIC_GREEN; + break; + case DEVICE_SELECTED: + m = PORT_TRAFFIC_ORANGE; + break; + default: + break; + } + LinkHighlight hlite = new LinkHighlight(linkId(), SECONDARY_HIGHLIGHT); + if (m != null) { + hlite.addMod(m); + } + + return hlite; + } + + /** + * Designates requested type. + */ + public enum RequestType { + HOST_SELECTED, + DEVICE_SELECTED, + } +} diff --git a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackLinkMap.java b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackLinkMap.java new file mode 100644 index 0000000000..7086b4cfd8 --- /dev/null +++ b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackLinkMap.java @@ -0,0 +1,30 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.openstacknetworkingui; + +import org.onosproject.net.Link; +import org.onosproject.net.LinkKey; +import org.onosproject.ui.topo.BiLinkMap; + +/** + * Link for OpenStack Networking UI service. + */ +public class OpenstackLinkMap extends BiLinkMap { + @Override + protected OpenstackLink create(LinkKey linkKey, Link link) { + return new OpenstackLink(linkKey, link); + } +} diff --git a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiManager.java b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiManager.java new file mode 100644 index 0000000000..daeebec008 --- /dev/null +++ b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiManager.java @@ -0,0 +1,187 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.openstacknetworkingui; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Streams; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Port; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.driver.DriverService; +import org.onosproject.net.link.DefaultLinkDescription; +import org.onosproject.net.link.LinkDescription; +import org.onosproject.net.link.LinkStore; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.ui.UiExtension; +import org.onosproject.ui.UiExtensionService; +import org.onosproject.ui.UiMessageHandlerFactory; +import org.onosproject.ui.UiTopoOverlayFactory; +import org.onosproject.ui.UiView; +import org.onosproject.ui.UiViewHidden; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.onosproject.net.Link.Type; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Implementation of OpenStack Networking UI service. + */ +@Service +@Component(immediate = true) +public class OpenstackNetworkingUiManager implements OpenstackNetworkingUiService { + + private static final ClassLoader CL = OpenstackNetworkingUiManager.class.getClassLoader(); + private static final String VIEW_ID = "sonaTopov"; + private static final String PORT_NAME = "portName"; + private static final String VXLAN = "vxlan"; + private static final String OVS = "ovs"; + private static final String APP_ID = "org.onosproject.openstacknetworkingui"; + private static final String SONA_GUI = "sonagui"; + + private final Logger log = LoggerFactory.getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected UiExtensionService uiExtensionService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DeviceService deviceService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DriverService driverService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected LinkStore linkStore; + Set vDevices; + + private OpenstackNetworkingUiMessageHandler messageHandler = new OpenstackNetworkingUiMessageHandler(); + + private final List uiViews = ImmutableList.of( + new UiViewHidden(VIEW_ID) + ); + + + private final UiMessageHandlerFactory messageHandlerFactory = + () -> ImmutableList.of(messageHandler); + + private final UiTopoOverlayFactory topoOverlayFactory = + () -> ImmutableList.of(new OpenstackNetworkingUiOverlay()); + + protected UiExtension extension = + new UiExtension.Builder(CL, uiViews) + .resourcePath(VIEW_ID) + .messageHandlerFactory(messageHandlerFactory) + .topoOverlayFactory(topoOverlayFactory) + .build(); + + @Activate + protected void activate() { + uiExtensionService.register(extension); + + vDevices = Streams.stream(deviceService.getAvailableDevices()) + .filter(this::isVirtualDevice) + .collect(Collectors.toSet()); + + vDevices.forEach(this::createLinksConnectedToTargetvDevice); + + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + uiExtensionService.unregister(extension); + log.info("Stopped"); + } + + @Override + public void sendMessage(String type, ObjectNode payload) { + messageHandler.sendMessagetoUi(type, payload); + } + + @Override + public void setRestServerIp(String ipAddress) { + messageHandler.setRestUrl(ipAddress); + } + + @Override + public String restServerUrl() { + return messageHandler.restUrl(); + } + + @Override + public void setRestServerAuthInfo(String id, String password) { + messageHandler.setRestAuthInfo(id, password); + } + + @Override + public String restServerAuthInfo() { + return messageHandler.restAuthInfo(); + } + + + private Optional vxlanPort(DeviceId deviceId) { + return deviceService.getPorts(deviceId) + .stream() + .filter(port -> port.annotations().value(PORT_NAME).equals(VXLAN)) + .findAny(); + } + private boolean isVirtualDevice(Device device) { + return driverService.getDriver(device.id()).name().equals(OVS); + } + + private void createLinksConnectedToTargetvDevice(Device targetvDevice) { + vDevices.stream().filter(d -> !d.equals(targetvDevice)) + .forEach(device -> { + if (vxlanPort(targetvDevice.id()).isPresent() && vxlanPort(device.id()).isPresent()) { + ConnectPoint srcConnectPoint = createConnectPoint(targetvDevice.id()); + + ConnectPoint dstConnectPoint = createConnectPoint(device.id()); + + LinkDescription linkDescription = createLinkDescription(srcConnectPoint, dstConnectPoint); + + linkStore.createOrUpdateLink(new ProviderId(SONA_GUI, APP_ID), + linkDescription); + } + }); + } + + private ConnectPoint createConnectPoint(DeviceId deviceId) { + try { + return new ConnectPoint(deviceId, vxlanPort(deviceId).get().number()); + } catch (NoSuchElementException exception) { + log.warn("Exception occured because of {}", exception.toString()); + return null; + } + } + + private LinkDescription createLinkDescription(ConnectPoint srcConnectPoint, ConnectPoint dstConnectPoint) { + return new DefaultLinkDescription(srcConnectPoint, dstConnectPoint, Type.DIRECT, true); + } + +} diff --git a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiMessageHandler.java b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiMessageHandler.java new file mode 100644 index 0000000000..0e8b7810a1 --- /dev/null +++ b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiMessageHandler.java @@ -0,0 +1,452 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.openstacknetworkingui; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.common.collect.Streams; +import org.onlab.osgi.ServiceDirectory; +import org.onosproject.cluster.ClusterService; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Element; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.Path; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.host.HostService; +import org.onosproject.net.topology.PathService; +import org.onosproject.ui.JsonUtils; +import org.onosproject.ui.RequestHandler; +import org.onosproject.ui.UiConnection; +import org.onosproject.ui.UiMessageHandler; +import org.apache.commons.io.IOUtils; + +import org.onosproject.ui.topo.Highlights; +import org.onosproject.ui.topo.HostHighlight; +import org.onosproject.ui.topo.NodeBadge; +import org.onosproject.ui.topo.NodeBadge.Status; +import org.onosproject.ui.topo.TopoJson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.Invocation; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Collection; +import java.util.List; +import java.util.Set; + + +import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; + +/** + * OpenStack Networking UI message handler. + */ +public class OpenstackNetworkingUiMessageHandler extends UiMessageHandler { + + private static final String OPENSTACK_NETWORKING_UI_START = "openstackNetworkingUiStart"; + private static final String OPENSTACK_NETWORKING_UI_UPDATE = "openstackNetworkingUiUpdate"; + private static final String OPENSTACK_NETWORKING_UI_STOP = "openstackNetworkingUiStop"; + private static final String ANNOTATION_NETWORK_ID = "networkId"; + private static final String FLOW_TRACE_REQUEST = "flowTraceRequest"; + private static final String SRC_IP = "srcIp"; + private static final String DST_IP = "dstIp"; + private static final String ANNOTATION_SEGMENT_ID = "segId"; + private static final String AUTHORIZATION = "Authorization"; + private static final String COMMAND = "command"; + private static final String FLOW_TRACE = "flowtrace"; + private static final String REVERSE = "reverse"; + private static final String TRANSACTION_ID = "transaction_id"; + private static final String TRANSACTION_VALUE = "sona"; + private static final String APP_REST_URL = "app_rest_url"; + private static final String MATCHING_FIELDS = "matchingfields"; + private static final String SOURCE_IP = "source_ip"; + private static final String DESTINATION_IP = "destination_ip"; + private static final String TO_GATEWAY = "to_gateway"; + private static final String IP_PROTOCOL = "ip_protocol"; + private static final String HTTP = "http://"; + private static final String OPENSTACK_NETWORKING_UI_RESULT = ":8181/onos/openstacknetworkingui/result"; + + private static final String ID = "id"; + private static final String MODE = "mode"; + private static final String MOUSE = "mouse"; + + private enum Mode { IDLE, MOUSE } + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private DeviceService deviceService; + private HostService hostService; + private PathService pathService; + private ClusterService clusterService; + private String restUrl; + private String restAuthInfo; + private Mode currentMode = Mode.IDLE; + private Element elementOfNote; + private final Client client = ClientBuilder.newClient(); + + + // ===============-=-=-=-=-=-======================-=-=-=-=-=-=-================================ + + + @Override + public void init(UiConnection connection, ServiceDirectory directory) { + super.init(connection, directory); + deviceService = directory.get(DeviceService.class); + hostService = directory.get(HostService.class); + pathService = directory.get(PathService.class); + clusterService = directory.get(ClusterService.class); + } + + @Override + protected Collection createRequestHandlers() { + return ImmutableSet.of( + new DisplayStartHandler(), + new DisplayUpdateHandler(), + new DisplayStopHandler(), + new FlowTraceRequestHandler() + ); + } + + public void setRestUrl(String ipAddress) { + restUrl = "http://" + ipAddress + ":8000/trace_request"; + } + + public String restUrl() { + return restUrl; + } + + public void setRestAuthInfo(String id, String password) { + restAuthInfo = Base64.getEncoder().encodeToString(id.concat(":").concat(password).getBytes()); + } + + public String restAuthInfo() { + return restAuthInfo; + } + + private final class DisplayStartHandler extends RequestHandler { + + public DisplayStartHandler() { + super(OPENSTACK_NETWORKING_UI_START); + } + + @Override + public void process(ObjectNode payload) { + String mode = string(payload, MODE); + + log.debug("Start Display: mode [{}]", mode); + clearState(); + clearForMode(); + + switch (mode) { + case MOUSE: + currentMode = Mode.MOUSE; + sendMouseData(); + break; + + default: + currentMode = Mode.IDLE; + break; + } + } + } + + private final class FlowTraceRequestHandler extends RequestHandler { + public FlowTraceRequestHandler() { + super(FLOW_TRACE_REQUEST); + } + + @Override + public void process(ObjectNode payload) { + String srcIp = string(payload, SRC_IP); + String dstIp = string(payload, DST_IP); + log.debug("SendEvent called with src IP: {}, dst IP: {}", srcIp, dstIp); + + ObjectNode objectNode = getFlowTraceRequestAsJson(srcIp, dstIp); + InputStream byteArrayInputStream + = new ByteArrayInputStream(objectNode.toString().getBytes()); + + Invocation.Builder builder = getClientBuilder(restUrl); + + if (builder == null) { + log.error("Fail to get the client builder for the trace from {} to {}", srcIp, dstIp); + return; + } + + try { + Response response = builder.header(AUTHORIZATION, restAuthInfo.toString()) + .post(Entity.entity(IOUtils.toString(byteArrayInputStream, StandardCharsets.UTF_8), + MediaType.APPLICATION_JSON_TYPE)); + + log.debug("Response from server: {}", response); + + if (response.getStatus() != 200) { + log.error("FlowTraceRequest failed because of {}", response); + } + + } catch (IOException e) { + log.error("Exception occured because of {}", e.toString()); + } + + } + } + + private ObjectNode getFlowTraceRequestAsJson(String srcIp, String dstIp) { + ObjectMapper mapper = new ObjectMapper(); + String controllerUrl = HTTP + clusterService.getLocalNode().ip() + + OPENSTACK_NETWORKING_UI_RESULT; + + ObjectNode objectNode = mapper.createObjectNode(); + + objectNode.put(COMMAND, FLOW_TRACE) + .put(REVERSE, false) + .put(TRANSACTION_ID, TRANSACTION_VALUE) + .put(APP_REST_URL, controllerUrl); + + if (srcIp.equals(dstIp)) { + objectNode.putObject(MATCHING_FIELDS) + .put(SOURCE_IP, srcIp) + .put(DESTINATION_IP, dstIp) + .put(TO_GATEWAY, true) + .put(IP_PROTOCOL, 1); + + } else { + objectNode.putObject(MATCHING_FIELDS) + .put(SOURCE_IP, srcIp) + .put(DESTINATION_IP, dstIp); + } + return objectNode; + } + + private Invocation.Builder getClientBuilder(String url) { + if (Strings.isNullOrEmpty(url)) { + log.warn("URL in not set"); + return null; + } + + WebTarget wt = client.target(url); + + return wt.request(MediaType.APPLICATION_JSON_TYPE); + } + + private final class DisplayUpdateHandler extends RequestHandler { + public DisplayUpdateHandler() { + super(OPENSTACK_NETWORKING_UI_UPDATE); + } + + @Override + public void process(ObjectNode payload) { + String id = string(payload, ID); + log.debug("Update Display: id [{}]", id); + if (!Strings.isNullOrEmpty(id)) { + updateForMode(id); + } else { + clearForMode(); + } + } + } + + private final class DisplayStopHandler extends RequestHandler { + public DisplayStopHandler() { + super(OPENSTACK_NETWORKING_UI_STOP); + } + + @Override + public void process(ObjectNode payload) { + log.debug("Stop Display"); + clearState(); + clearForMode(); + } + } + + // === ------------ + + private void clearState() { + currentMode = Mode.IDLE; + elementOfNote = null; + } + + private void updateForMode(String id) { + + try { + HostId hid = HostId.hostId(id); + elementOfNote = hostService.getHost(hid); + + } catch (Exception e) { + try { + DeviceId did = DeviceId.deviceId(id); + elementOfNote = deviceService.getDevice(did); + + } catch (Exception e2) { + log.debug("Unable to process ID [{}]", id); + elementOfNote = null; + } + } + + switch (currentMode) { + case MOUSE: + sendMouseData(); + break; + + default: + break; + } + + } + + private void clearForMode() { + sendHighlights(new Highlights()); + } + + private void sendHighlights(Highlights highlights) { + sendMessage(TopoJson.highlightsMessage(highlights)); + } + + public void sendMessagetoUi(String type, ObjectNode payload) { + sendMessage(JsonUtils.envelope(type, payload)); + } + + private int getVni(Host host) { + String vni = host.annotations().value(ANNOTATION_SEGMENT_ID); + + return vni == null ? 0 : Integer.valueOf(vni).intValue(); + } + + private void sendMouseData() { + Highlights highlights = new Highlights(); + + if (elementOfNote != null && elementOfNote instanceof Device) { + DeviceId deviceId = (DeviceId) elementOfNote.id(); + + List edgeLinks = edgeLinks(deviceId); + + edgeLinks.forEach(edgeLink -> { + highlights.add(edgeLink.highlight(OpenstackLink.RequestType.DEVICE_SELECTED)); + }); + + hostService.getConnectedHosts(deviceId).forEach(host -> { + HostHighlight hostHighlight = new HostHighlight(host.id().toString()); + hostHighlight.setBadge(createBadge(getVni(host))); + highlights.add(hostHighlight); + }); + + sendHighlights(highlights); + + } else if (elementOfNote != null && elementOfNote instanceof Host) { + + HostId hostId = HostId.hostId(elementOfNote.id().toString()); + if (!hostMadeFromOpenstack(hostId)) { + return; + } + + List openstackLinks = linksInSameNetwork(hostId); + + openstackLinks.forEach(openstackLink -> { + highlights.add(openstackLink.highlight(OpenstackLink.RequestType.HOST_SELECTED)); + }); + + hostHighlightsInSameNetwork(hostId).forEach(highlights::add); + + sendHighlights(highlights); + + } + } + + private boolean hostMadeFromOpenstack(HostId hostId) { + return hostService.getHost(hostId).annotations() + .value(ANNOTATION_NETWORK_ID) == null ? false : true; + } + + private String networkId(HostId hostId) { + return hostService.getHost(hostId).annotations().value(ANNOTATION_NETWORK_ID); + } + + private Set hostHighlightsInSameNetwork(HostId hostId) { + + Set hostHighlights = Sets.newHashSet(); + Streams.stream(hostService.getHosts()) + .filter(host -> isHostInSameNetwork(host, networkId(hostId))) + .forEach(host -> { + HostHighlight hostHighlight = new HostHighlight(host.id().toString()); + hostHighlight.setBadge(createBadge(getVni(host))); + hostHighlights.add(hostHighlight); + }); + + return hostHighlights; + } + + private List edgeLinks(DeviceId deviceId) { + OpenstackLinkMap openstackLinkMap = new OpenstackLinkMap(); + + hostService.getConnectedHosts(deviceId).forEach(host -> { + openstackLinkMap.add(createEdgeLink(host, true)); + openstackLinkMap.add(createEdgeLink(host, false)); + }); + + List edgeLinks = Lists.newArrayList(); + + openstackLinkMap.biLinks().forEach(edgeLinks::add); + + return edgeLinks; + } + + private List linksInSameNetwork(HostId hostId) { + OpenstackLinkMap linkMap = new OpenstackLinkMap(); + + Streams.stream(hostService.getHosts()) + .filter(host -> isHostInSameNetwork(host, networkId(hostId))) + .forEach(host -> { + linkMap.add(createEdgeLink(host, true)); + linkMap.add(createEdgeLink(host, false)); + + Set paths = pathService.getPaths(hostId, + host.id()); + + if (!paths.isEmpty()) { + paths.forEach(path -> path.links().forEach(linkMap::add)); + } + }); + + List openstackLinks = Lists.newArrayList(); + + linkMap.biLinks().forEach(openstackLinks::add); + + return openstackLinks; + } + + private boolean isHostInSameNetwork(Host host, String networkId) { + return hostService.getHost(host.id()).annotations() + .value(ANNOTATION_NETWORK_ID).equals(networkId); + } + + private NodeBadge createBadge(int n) { + return NodeBadge.number(Status.INFO, n, "Openstack Node"); + } +} diff --git a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiOverlay.java b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiOverlay.java new file mode 100644 index 0000000000..8ad477ce1d --- /dev/null +++ b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiOverlay.java @@ -0,0 +1,79 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.openstacknetworkingui; + +import org.onlab.osgi.DefaultServiceDirectory; +import org.onosproject.net.HostId; +import org.onosproject.net.host.HostService; +import org.onosproject.ui.UiTopoOverlay; +import org.onosproject.ui.topo.ButtonId; +import org.onosproject.ui.topo.PropertyPanel; + +import static org.onosproject.ui.topo.TopoConstants.Properties.INTENTS; +import static org.onosproject.ui.topo.TopoConstants.Properties.TOPOLOGY_SSCS; +import static org.onosproject.ui.topo.TopoConstants.Properties.TUNNELS; +import static org.onosproject.ui.topo.TopoConstants.Properties.VERSION; +import static org.onosproject.ui.topo.TopoConstants.Properties.VLAN; + +/** + * Topology overlay for OpenStack Networking UI. + */ +public class OpenstackNetworkingUiOverlay extends UiTopoOverlay { + private static final String OVERLAY_ID = "sona-overlay"; + private static final String SONA = "SONA"; + private static final String SUMMARY_TITLE = "OpenStack Networking UI"; + private static final String SUMMARY_VERSION = "0.9"; + private static final String VNI = "VNI"; + private static final String ANNOTATION_SEGMENT_ID = "segId"; + + private static final String NOT_AVAILABLE = "N/A"; + + private static final ButtonId FLOW_TRACE_BUTTON = new ButtonId("flowtrace"); + private static final ButtonId RESET_BUTTON = new ButtonId("reset"); + private static final ButtonId TO_GATEWAY_BUTTON = new ButtonId("toGateway"); + private static final ButtonId TO_EXTERNAL_BUTTON = new ButtonId("toExternal"); + + private final HostService hostService = DefaultServiceDirectory.getService(HostService.class); + + public OpenstackNetworkingUiOverlay() { + super(OVERLAY_ID); + } + + + @Override + public void modifySummary(PropertyPanel pp) { + pp.title(SUMMARY_TITLE) + .removeProps( + TOPOLOGY_SSCS, + INTENTS, + TUNNELS, + VERSION + ) + .addProp(SONA, VERSION, SUMMARY_VERSION); + } + + @Override + public void modifyHostDetails(PropertyPanel pp, HostId hostId) { + String vni = hostService.getHost(hostId).annotations().value(ANNOTATION_SEGMENT_ID); + + pp.removeProps(VLAN); + pp.addProp(SONA, VNI, vni == null ? NOT_AVAILABLE : vni) + .addButton(FLOW_TRACE_BUTTON) + .addButton(RESET_BUTTON) + .addButton(TO_GATEWAY_BUTTON) + .addButton(TO_EXTERNAL_BUTTON); + } +} diff --git a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiService.java b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiService.java new file mode 100644 index 0000000000..aea97639a7 --- /dev/null +++ b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiService.java @@ -0,0 +1,62 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.openstacknetworkingui; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Service for OpenStack Networking UI. + */ +public interface OpenstackNetworkingUiService { + + /** + * Sends message to OpenStack Networking UI. + * + * @param type event type + * @param payload payload + */ + void sendMessage(String type, ObjectNode payload); + + /** + * Sets the REST server ip address. + * + * @param ipAddress rest server ip address + */ + void setRestServerIp(String ipAddress); + + /** + * Gets the REST server url. + * + * @return REST server url + */ + String restServerUrl(); + + /** + * Sets the REST server authorization information. + * + * @param id id + * @param password password + */ + void setRestServerAuthInfo(String id, String password); + + /** + * Gets the REST server authorization information. + * + * @return REST server authorization information as String + */ + String restServerAuthInfo(); + +} diff --git a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/GetRestServerAuthInfoCommand.java b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/GetRestServerAuthInfoCommand.java new file mode 100644 index 0000000000..fdaf23dc5e --- /dev/null +++ b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/GetRestServerAuthInfoCommand.java @@ -0,0 +1,34 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.openstacknetworkingui.cli; + +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.openstacknetworkingui.OpenstackNetworkingUiService; + +/** + * Gets the REST server authorization information. + */ +@Command(scope = "onos", name = "openstacknetworking-ui-get-restserver-auth", + description = "Gets the REST server authorization information") +public class GetRestServerAuthInfoCommand extends AbstractShellCommand { + + @Override + protected void execute() { + OpenstackNetworkingUiService service = AbstractShellCommand.get(OpenstackNetworkingUiService.class); + print("Encoded information for the REST server authorization: %s", service.restServerAuthInfo()); + } +} diff --git a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/GetRestServerCommand.java b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/GetRestServerCommand.java new file mode 100644 index 0000000000..6cccfa1d0a --- /dev/null +++ b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/GetRestServerCommand.java @@ -0,0 +1,35 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.openstacknetworkingui.cli; + +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.openstacknetworkingui.OpenstackNetworkingUiService; + +/** + * Gets the REST server url. + */ + +@Command(scope = "onos", name = "openstacknetworking-ui-get-restserver-url", + description = "Gets the REST server url") +public class GetRestServerCommand extends AbstractShellCommand { + + @Override + protected void execute() { + OpenstackNetworkingUiService service = AbstractShellCommand.get(OpenstackNetworkingUiService.class); + print("REST server url : %s", service.restServerUrl()); + } +} diff --git a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/SetRestServerAuthInfoCommand.java b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/SetRestServerAuthInfoCommand.java new file mode 100644 index 0000000000..148818cd75 --- /dev/null +++ b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/SetRestServerAuthInfoCommand.java @@ -0,0 +1,45 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.openstacknetworkingui.cli; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.openstacknetworkingui.OpenstackNetworkingUiService; + +/** + * Sets the REST server authorization information. + */ +@Command(scope = "onos", name = "openstacknetworking-ui-set-restserver-auth", + description = "Sets the REST server authorization information") +public class SetRestServerAuthInfoCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "restServerAuthId", description = "REST server authorization id", + required = true, multiValued = false) + private String restServerAuthId = null; + + @Argument(index = 1, name = "restServerAuthPass", description = "REST server authorization password", + required = true, multiValued = false) + private String restServerAuthPass = null; + + @Override + protected void execute() { + OpenstackNetworkingUiService service = AbstractShellCommand.get(OpenstackNetworkingUiService.class); + service.setRestServerAuthInfo(restServerAuthId, restServerAuthPass); + print("Id and password for the REST server authorization are %s and %s.", restServerAuthId, restServerAuthPass); + print("Encoded result as based 64 format: %s", service.restServerAuthInfo()); + } +} diff --git a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/SetRestServerCommand.java b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/SetRestServerCommand.java new file mode 100644 index 0000000000..f9cef4d38c --- /dev/null +++ b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/SetRestServerCommand.java @@ -0,0 +1,40 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.openstacknetworkingui.cli; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.openstacknetworkingui.OpenstackNetworkingUiService; + +/** + * Sets the REST server ip address. + */ +@Command(scope = "onos", name = "openstacknetworking-ui-set-restserver-ip", + description = "Sets the REST server ip address") +public class SetRestServerCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "restServerIp", description = "REST server ip address", + required = true, multiValued = false) + private String restServerIp = null; + + @Override + protected void execute() { + OpenstackNetworkingUiService service = AbstractShellCommand.get(OpenstackNetworkingUiService.class); + service.setRestServerIp(restServerIp); + print("The REST server url is set to %s", service.restServerUrl()); + } +} diff --git a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/package-info.java b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/package-info.java new file mode 100644 index 0000000000..2a69dda791 --- /dev/null +++ b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/cli/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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. + */ + +/** + * CLI handlers of OpenStack Networking UI service. + */ +package org.onosproject.openstacknetworkingui.cli; \ No newline at end of file diff --git a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/package-info.java b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/package-info.java new file mode 100644 index 0000000000..3e08d0b22d --- /dev/null +++ b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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. + */ + +/** + * OpenStack Networking UI package. + */ +package org.onosproject.openstacknetworkingui; \ No newline at end of file diff --git a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/web/FlowTraceWebResource.java b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/web/FlowTraceWebResource.java new file mode 100644 index 0000000000..d4fbf28a2b --- /dev/null +++ b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/web/FlowTraceWebResource.java @@ -0,0 +1,75 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.openstacknetworkingui.web; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.onlab.osgi.DefaultServiceDirectory; +import org.onosproject.openstacknetworkingui.OpenstackNetworkingUiService; +import org.onosproject.rest.AbstractWebResource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import java.io.IOException; +import java.io.InputStream; + +import static javax.ws.rs.core.Response.status; + +/** + * Handles REST API from monitoring server. + */ + +@Path("result") +public class FlowTraceWebResource extends AbstractWebResource { + protected final Logger log = LoggerFactory.getLogger(getClass()); + private final OpenstackNetworkingUiService uiService = + DefaultServiceDirectory.getService(OpenstackNetworkingUiService.class); + + private static final String FLOW_TRACE_RESULT = "flowTraceResult"; + + @Context + private UriInfo uriInfo; + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response flowTraceResponse(InputStream inputStream) throws IOException { + try { + JsonNode jsonNode = mapper().enable(SerializationFeature.INDENT_OUTPUT).readTree(inputStream); + ObjectNode objectNode = jsonNode.deepCopy(); + + log.debug("FlowTraceResponse: {}", jsonNode.toString()); + + uiService.sendMessage(FLOW_TRACE_RESULT, objectNode); + + } catch (IOException e) { + log.error("Exception occured because of {}", e.toString()); + } + + return status(Response.Status.OK).build(); + } + +} diff --git a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/web/package-info.java b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/web/package-info.java new file mode 100644 index 0000000000..570f9cf632 --- /dev/null +++ b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/web/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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. + */ + +/** + * Web implementation of OpenStack Networking UI service. + */ +package org.onosproject.openstacknetworkingui.web; \ No newline at end of file diff --git a/apps/openstacknetworkingui/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/openstacknetworkingui/src/main/resources/OSGI-INF/blueprint/shell-config.xml new file mode 100644 index 0000000000..9e78f63fcb --- /dev/null +++ b/apps/openstacknetworkingui/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + diff --git a/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopov.css b/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopov.css new file mode 100644 index 0000000000..11523e1693 --- /dev/null +++ b/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopov.css @@ -0,0 +1,126 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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. + */ + +/* css for OpenStack Networking UI. */ + +#traceInfoDialogId h2 { + text-align: center; + width: 200px; + margin: 0; + font-weight: lighter; + word-wrap: break-word; + display: inline-block; + vertical-align: middle; + font-size: 14pt; +} + +div.traceInfo table { + border-collapse: collapse; + table-layout: fixed; + empty-cells: show; + margin: 0; + width: 200px; +} + +div.traceInfo td { + cursor: pointer; + font-variant: small-caps; + + font-size: 10pt; + padding-top: 14px; + padding-bottom: 14px; + + letter-spacing: 0.02em; + cursor: pointer; + + color: #3c3a3a; + width: 100px; +} + +div.traceInfo td.label { + font-weight: bold; +} + +#flowTraceResultDialogId h2 { + text-align: center; + width: 600px; + + padding: 0 0 0 10px; + margin: 0; + font-weight: lighter; + word-wrap: break-word; + display: inline-block; + vertical-align: middle; +} + +div.flowTraceResult table { + border-collapse: collapse; + table-layout: fixed; + empty-cells: show; + margin: 0; + width: 600px; +} + +div.flowTraceResult td { + padding: 4px; + text-align: center; + word-wrap: break-word; + font-size: 12pt; +} + +div.flowTraceResult .table-header td { + font-weight: bold; + font-variant: small-caps; + text-transform: uppercase; + font-size: 11pt; + padding-top: 14px; + padding-bottom: 14px; + + letter-spacing: 0.02em; + cursor: pointer; + + background-color: #e5e5e6; + color: #3c3a3a; + width: 120px; + +} + +div.flowTraceResult .table-body tr:nth-child(even) { + background-color: #f4f4f4; +} + +div.flowTraceResult .table-body tr:nth-child(odd) { + background-color: #fbfbfb; +} + +div.flowTraceResult .table-body td { + cursor: pointer; + font-variant: small-caps; + + font-size: 10pt; + padding-top: 14px; + padding-bottom: 14px; + + letter-spacing: 0.02em; + cursor: pointer; + + color: #3c3a3a; + width: 120px; +} + +div.flowTraceResult .table-body tr.drop { + background-color: #e28d8d; +} \ No newline at end of file diff --git a/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopov.html b/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopov.html new file mode 100644 index 0000000000..8d31977b6e --- /dev/null +++ b/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopov.html @@ -0,0 +1,4 @@ + +
+

This is a hidden view .. just a placeholder to house the javascript

+
diff --git a/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovOverlay.js b/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovOverlay.js new file mode 100644 index 0000000000..39517b6848 --- /dev/null +++ b/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovOverlay.js @@ -0,0 +1,220 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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. + */ + +/* OpenStack Networking UI Overlay description */ +(function () { + 'use strict'; + + // injected refs + var $log, tov, sts, flash, ds, wss; + var traceSrc = null; + var traceDst = null; + + var traceInfoDialogId = 'traceInfoDialogId', + traceInfoDialogOpt = { + width: 200, + edge: 'left', + margin: 20, + hideMargin: -20 + } + + var overlay = { + // NOTE: this must match the ID defined in AppUiTopovOverlay + overlayId: 'sona-overlay', + glyphId: '*star4', + tooltip: 'OpenstackNetworking UI', + + glyphs: { + star4: { + vb: '0 0 8 8', + d: 'M1,4l2,-1l1,-2l1,2l2,1l-2,1l-1,2l-1,-2z' + }, + banner: { + vb: '0 0 6 6', + d: 'M1,1v4l2,-2l2,2v-4z' + } + }, + + activate: function () { + $log.debug("OpenstackNetworking UI ACTIVATED"); + }, + deactivate: function () { + sts.stopDisplay(); + $log.debug("OpenstackNetworking UI DEACTIVATED"); + }, + + // detail panel button definitions + buttons: { + flowtrace: { + gid: 'checkMark', + tt: 'Flow Trace', + cb: function (data) { + + if (traceSrc == null && data.navPath == 'host') { + traceSrc = data.title; + + flash.flash('Src ' + traceSrc + ' selected. Please select the dst'); + } else if (traceDst == null && data.title != traceSrc && data.navPath == 'host') { + traceDst = data.title; + openTraceInfoDialog(); + flash.flash('Dst ' + traceDst + ' selected. Press Request button'); + } + + $log.debug('Perform flow trace test between VMs:', data); + } + }, + + reset: { + gid: 'xMark', + tt: 'Reset', + cb: function (data) { + flash.flash('Reset flow trace'); + traceSrc = null; + traceDst = null; + ds.closeDialog(); + $log.debug('BAR action invoked with data:', data); + } + }, + toGateway: { + gid: 'm_switch', + tt: 'Trace to Gateway', + cb: function (data) { + if (traceSrc != null && data.title == traceSrc && data.navPath == 'host') { + //Set traceSrc to traceDst in case trace to gateway + traceDst = traceSrc; + openTraceInfoDialog(); + flash.flash('Trace to Gateway'); + } + } + }, + toExternal: { + gid: 'm_cloud', + tt: 'Trace to External', + cb: function (data) { + if (traceSrc != null && data.title == traceSrc && data.navPath == 'host') { + //Set traceDst to 8.8.8.8 to check external connection + traceDst = '8.8.8.8'; + openTraceInfoDialog(); + flash.flash('Trace to External') + } + } + } + }, + + keyBindings: { + 0: { + cb: function () { sts.stopDisplay(); }, + tt: 'Cancel OpenstackNetworking UI Overlay Mode', + gid: 'xMark' + }, + V: { + cb: function () { + wss.bindHandlers({ + flowTraceResult: sts, + }); + sts.startDisplay('mouse'); + }, + tt: 'Start OpenstackNetworking UI Overlay Mode', + gid: 'crown' + }, + + _keyOrder: [ + '0', 'V' + ] + }, + + hooks: { + // hook for handling escape key + // Must return true to consume ESC, false otherwise. + escape: function () { + // Must return true to consume ESC, false otherwise. + return sts.stopDisplay(); + }, + mouseover: function (m) { + // m has id, class, and type properties + $log.debug('mouseover:', m); + sts.updateDisplay(m); + }, + mouseout: function () { + $log.debug('mouseout'); + sts.updateDisplay(); + } + } + }; + + function openTraceInfoDialog() { + ds.openDialog(traceInfoDialogId, traceInfoDialogOpt) + .setTitle('Flow Trace Information') + .addContent(createTraceInfoDiv(traceSrc, traceDst)) + .addOk(flowTraceResultBtn, 'Request') + .bindKeys(); + } + + function createTraceInfoDiv(src, dst) { + var texts = ds.createDiv('traceInfo'); + texts.append('hr'); + texts.append('table').append('tbody').append('tr'); + + var tBodySelection = texts.select('table').select('tbody').select('tr'); + + tBodySelection.append('td').text('Source IP:').attr("class", "label"); + tBodySelection.append('td').text(src).attr("class", "value"); + + texts.select('table').select('tbody').append('tr'); + + tBodySelection = texts.select('table').select('tbody').select('tr:nth-child(2)'); + + tBodySelection.append('td').text('Destination IP:').attr("class", "label"); + if (dst == src) { + tBodySelection.append('td').text('toGateway').attr("class", "value"); + } else { + tBodySelection.append('td').text(dst).attr("class", "value"); + } + + texts.append('hr'); + + return texts; + + } + + function flowTraceResultBtn() { + sts.sendFlowTraceRequest(traceSrc, traceDst); + ds.closeDialog(); + traceSrc = null; + traceDst = null; + flash.flash('Send Flow Trace Request'); + } + + function buttonCallback(x) { + $log.debug('Toolbar-button callback', x); + } + + // invoke code to register with the overlay service + angular.module('ovSonaTopov') + .run(['$log', 'TopoOverlayService', 'SonaTopovService', + 'FlashService', 'DialogService', 'WebSocketService', + + function (_$log_, _tov_, _sts_, _flash_, _ds_, _wss_) { + $log = _$log_; + tov = _tov_; + sts = _sts_; + flash = _flash_; + ds = _ds_; + wss = _wss_; + tov.register(overlay); + }]); + +}()); diff --git a/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovService.js b/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovService.js new file mode 100644 index 0000000000..a9bbf69dca --- /dev/null +++ b/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovService.js @@ -0,0 +1,198 @@ +/* + * Copyright 2017-present 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. + */ + +/* + OpenStack Networking UI Service + + Provides a mechanism to highlight hosts, devices and links according to + a virtual network. Also provides trace functionality to prove that + flow rules for the specific vm are installed appropriately. + */ + +(function () { + 'use strict'; + + // injected refs + var $log, fs, flash, wss, ds; + + // constants + var displayStart = 'openstackNetworkingUiStart', + displayUpdate = 'openstackNetworkingUiUpdate', + displayStop = 'openstackNetworkingUiStop', + flowTraceRequest = 'flowTraceRequest'; + + // internal state + var currentMode = null; + + // === --------------------------- + // === Helper functions + + function sendDisplayStart(mode) { + wss.sendEvent(displayStart, { + mode: mode + }); + } + + function sendDisplayUpdate(what) { + wss.sendEvent(displayUpdate, { + id: what ? what.id : '' + }); + } + + function sendDisplayStop() { + wss.sendEvent(displayStop); + } + + function sendFlowTraceRequest(src, dst) { + wss.sendEvent(flowTraceRequest, { + srcIp: src, + dstIp: dst + }); + flash.flash('sendFlowTraceRequest called'); + } + + // === --------------------------- + // === Main API functions + + function startDisplay(mode) { + if (currentMode === mode) { + $log.debug('(in mode', mode, 'already)'); + } else { + currentMode = mode; + sendDisplayStart(mode); + + flash.flash('Starting Openstack Networking UI mode'); + } + } + + function updateDisplay(m) { + if (currentMode) { + sendDisplayUpdate(m); + } + } + + function stopDisplay() { + if (currentMode) { + currentMode = null; + sendDisplayStop(); + flash.flash('Canceling Openstack Networking UI Overlay mode'); + return true; + } + return false; + } + + + function dOk() { + ds.closeDialog(); + } + function openFlowTraceResultDialog(data) { + var flowTraceResultDialogId = 'flowTraceResultDialogId', + flowTraceResultDialogOpt = { + width: 650, + edge: 'left', + margin: 20, + hideMargin: -20 + } + var traceSuccess = data.trace_success == true ? "SUCCESS" : "FALSE"; + ds.openDialog(flowTraceResultDialogId, flowTraceResultDialogOpt) + .setTitle('Flow Trace Result: ' + traceSuccess) + .addContent(createTraceResultInfoDiv(data)) + .addOk(dOk, 'Close') + .bindKeys(); + } + + function createTraceResultInfoDiv(data) { + var texts = ds.createDiv('flowTraceResult'); + + texts.append('div').attr("class", "table-header"); + texts.append('div').attr("class", "table-body"); + + texts.select('.table-header').append('table').append('tbody').append('tr'); + texts.select('.table-body').append('table').append('tbody'); + + + var theaderSelection = texts.select('.table-header') + .select('table').select('tbody').select('tr'); + + theaderSelection.append('td').text('Node'); + theaderSelection.append('td').text('Table Id'); + theaderSelection.append('td').text('Priority'); + theaderSelection.append('td').text('Selector'); + theaderSelection.append('td').text('Action'); + + var tbodySelection = texts.select('.table-body').select('table').select('tbody'); + var rowNum = 1; + + data.trace_result.forEach(function(result) { + result.flow_rules.forEach(function(flowRule) { + tbodySelection.append('tr'); + var tbodyTrSelection = tbodySelection.select('tr:nth-child(' + rowNum + ')'); + tbodyTrSelection.append('td').text(result.trace_node_name); + tbodyTrSelection.append('td').text(flowRule.table); + tbodyTrSelection.append('td').text(flowRule.priority); + tbodyTrSelection.append('td').text(jsonToSring(flowRule.selector)); + tbodyTrSelection.append('td').text(jsonToSring(flowRule.actions)); + if (jsonToSring(flowRule.actions).includes("drop")) { + tbodyTrSelection.attr("class", "drop"); + } + rowNum++; + }); + + }); + + return texts; + } + + function jsonToSring(jsonData) { + var result = []; + for (var key in jsonData) { + result.push(key + ':' + jsonData[key]); + } + + return result.join('/'); + + } + + function flowTraceResult(data) { + flash.flash('flowTraceResult called'); + $log.debug(data); + + openFlowTraceResultDialog(data) + } + + // === --------------------------- + // === Module Factory Definition + + angular.module('ovSonaTopov', []) + .factory('SonaTopovService', + ['$log', 'FnService', 'FlashService', 'WebSocketService', 'DialogService', + + function (_$log_, _fs_, _flash_, _wss_, _ds_) { + $log = _$log_; + fs = _fs_; + flash = _flash_; + wss = _wss_; + ds = _ds_; + + return { + startDisplay: startDisplay, + updateDisplay: updateDisplay, + stopDisplay: stopDisplay, + flowTraceResult: flowTraceResult, + sendFlowTraceRequest: sendFlowTraceRequest, + }; + }]); +}()); diff --git a/apps/openstacknetworkingui/src/main/resources/sonaTopov/css.html b/apps/openstacknetworkingui/src/main/resources/sonaTopov/css.html new file mode 100644 index 0000000000..a2e04f44f7 --- /dev/null +++ b/apps/openstacknetworkingui/src/main/resources/sonaTopov/css.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/openstacknetworkingui/src/main/resources/sonaTopov/js.html b/apps/openstacknetworkingui/src/main/resources/sonaTopov/js.html new file mode 100644 index 0000000000..72026ffa3c --- /dev/null +++ b/apps/openstacknetworkingui/src/main/resources/sonaTopov/js.html @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/apps/openstacknetworkingui/src/main/webapp/WEB-INF/web.xml b/apps/openstacknetworkingui/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..616ad86566 --- /dev/null +++ b/apps/openstacknetworkingui/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,39 @@ + + + + SONA GUI REST API v1.0 + + + JAX-RS Service + org.glassfish.jersey.servlet.ServletContainer + + jersey.config.server.provider.classnames + + org.onosproject.openstacknetworkingui.web.FlowTraceWebResource + + + 1 + + + + JAX-RS Service + /* + + diff --git a/apps/pom.xml b/apps/pom.xml index b43c0db663..26deeee453 100644 --- a/apps/pom.xml +++ b/apps/pom.xml @@ -96,6 +96,7 @@ route-service evpn-route-service l3vpn + openstacknetworkingui diff --git a/modules.defs b/modules.defs index 572881fd97..4f67048591 100644 --- a/modules.defs +++ b/modules.defs @@ -218,6 +218,7 @@ ONOS_APPS = [ '//apps/route-service:onos-apps-route-service-oar', '//apps/evpn-route-service:onos-apps-evpn-route-service-oar', '//incubator/protobuf/registry:onos-incubator-protobuf-registry-oar', + '//apps/openstacknetworkingui:onos-apps-openstacknetworkingui-oar', ] PROTOCOL_APPS = [