Implement host probing retry with major refactoring
- Implement probe retry
- Switch to typical core/provider design pattern for HostProbingService
and as a result decoupling the dependency between SR and HostLocationProvider
Change-Id: I33a15af580677ea376b421ac3e26f9821dcca844
diff --git a/providers/hostprobing/BUCK b/providers/hostprobing/BUCK
new file mode 100644
index 0000000..21bf493
--- /dev/null
+++ b/providers/hostprobing/BUCK
@@ -0,0 +1,22 @@
+COMPILE_DEPS = [
+ '//lib:CORE_DEPS',
+ '//incubator/api:onos-incubator-api',
+]
+
+TEST_DEPS = [
+ '//lib:TEST_ADAPTERS',
+ '//utils/osgi:onlab-osgi-tests',
+]
+
+osgi_jar_with_tests (
+ deps = COMPILE_DEPS,
+ test_deps = TEST_DEPS,
+)
+
+onos_app (
+ app_name = 'org.onosproject.hostprobingprovider',
+ title = 'Host Probing Provider',
+ category = 'Provider',
+ url = 'http://onosproject.org',
+ description = 'Provides host probing mechanism that discovers or verifies the existence of a host at specific location',
+)
diff --git a/providers/hostprobing/pom.xml b/providers/hostprobing/pom.xml
new file mode 100644
index 0000000..3f3c001
--- /dev/null
+++ b/providers/hostprobing/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2014-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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-providers</artifactId>
+ <version>1.12.1-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-host-provider</artifactId>
+ <packaging>bundle</packaging>
+
+ <properties>
+ <onos.app.name>org.onosproject.hostprobingprovider</onos.app.name>
+ <onos.app.title>Host Probing Provider</onos.app.title>
+ <onos.app.origin>ONF</onos.app.origin>
+ <onos.app.category>Provider</onos.app.category>
+ <onos.app.url>http://onosproject.org</onos.app.url>
+ <onos.app.readme>Provides host probing mechanism that discovers or verifies the existence of a host at specific location</onos.app.readme>
+ </properties>
+
+ <description>ONOS host probing provider</description>
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-osgi</artifactId>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-incubator-api</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/providers/hostprobing/src/main/java/org/onosproject/provider/hostprobing/impl/DefaultHostProbingProvider.java b/providers/hostprobing/src/main/java/org/onosproject/provider/hostprobing/impl/DefaultHostProbingProvider.java
new file mode 100644
index 0000000..dfcbf0b
--- /dev/null
+++ b/providers/hostprobing/src/main/java/org/onosproject/provider/hostprobing/impl/DefaultHostProbingProvider.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2018-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.provider.hostprobing.impl;
+
+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.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv6;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.packet.ndp.NeighborSolicitation;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.host.HostProbe;
+import org.onosproject.net.host.HostProbingEvent;
+import org.onosproject.net.host.HostProbingProvider;
+import org.onosproject.net.host.HostProbingProviderRegistry;
+import org.onosproject.net.host.HostProbingProviderService;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.HostProviderRegistry;
+import org.onosproject.net.host.HostProviderService;
+import org.onosproject.net.host.ProbeMode;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Optional;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import static java.util.concurrent.Executors.newScheduledThreadPool;
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider which sends host location probes to discover or verify a host at specific location.
+ */
+@Component(immediate = true)
+@Service
+public class DefaultHostProbingProvider extends AbstractProvider implements HostProvider, HostProbingProvider {
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private HostProviderRegistry providerRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private HostProbingProviderRegistry hostProbingProviderRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private MastershipService mastershipService;
+
+ private HostProviderService providerService;
+ private HostProbingProviderService hostProbingProviderService;
+ private ExecutorService packetHandler;
+ private ExecutorService probeEventHandler;
+ private ScheduledExecutorService hostProber;
+
+ private final PacketProcessor packetProcessor = context ->
+ packetHandler.execute(() -> {
+ Ethernet eth = context.inPacket().parsed();
+ if (eth == null) {
+ return;
+ }
+ MacAddress srcMac = eth.getSourceMAC();
+ MacAddress destMac = eth.getDestinationMAC();
+ VlanId vlan = VlanId.vlanId(eth.getVlanID());
+ ConnectPoint heardOn = context.inPacket().receivedFrom();
+
+ // Receives a location probe. Invalid entry from the cache
+ if (destMac.isOnos() && !MacAddress.NONE.equals(destMac)) {
+ log.debug("Receives probe for {}/{} on {}", srcMac, vlan, heardOn);
+ hostProbingProviderService.removeProbingHost(destMac);
+ }
+ });
+
+ // TODO Make this configurable
+ private static final int PROBE_INIT_DELAY_MS = 1000;
+ private static final int DEFAULT_RETRY = 5;
+
+ /**
+ * Creates an OpenFlow host provider.
+ */
+ public DefaultHostProbingProvider() {
+ super(new ProviderId("hostprobing", "org.onosproject.provider.hostprobing"));
+ }
+
+ @Activate
+ public void activate(ComponentContext context) {
+ providerService = providerRegistry.register(this);
+ hostProbingProviderService = hostProbingProviderRegistry.register(this);
+
+ packetHandler = newSingleThreadScheduledExecutor(groupedThreads("onos/host-loc-provider",
+ "packet-handler", log));
+ probeEventHandler = newSingleThreadScheduledExecutor(groupedThreads("onos/host-loc-provider",
+ "probe-handler", log));
+ hostProber = newScheduledThreadPool(32, groupedThreads("onos/host-loc-probe", "%d", log));
+
+ packetService.addProcessor(packetProcessor, PacketProcessor.advisor(1));
+ }
+
+ @Deactivate
+ public void deactivate() {
+ providerRegistry.unregister(this);
+ hostProbingProviderRegistry.unregister(this);
+ providerService = null;
+
+ packetService.removeProcessor(packetProcessor);
+
+ packetHandler.shutdown();
+ probeEventHandler.shutdown();
+ hostProber.shutdown();
+ }
+
+ @Override
+ public void triggerProbe(Host host) {
+ // Not doing anything at this moment...
+ }
+
+ @Override
+ public void processEvent(HostProbingEvent event) {
+ probeEventHandler.execute(() -> {
+ log.debug("Receiving HostProbingEvent {}", event);
+ HostProbe hostProbe = event.subject();
+
+ switch (event.type()) {
+ case PROBE_REQUESTED:
+ // Do nothing
+ break;
+ case PROBE_TIMEOUT:
+ // Retry probe until PROBE_FAIL
+ // TODO Only retry DISCOVER probes
+ probeHostInternal(hostProbe, hostProbe.connectPoint(),
+ hostProbe.mode(), hostProbe.probeMac(), hostProbe.retry());
+ break;
+ case PROBE_FAIL:
+ // Remove this location if this is a verify probe.
+ if (hostProbe.mode() == ProbeMode.VERIFY) {
+ providerService.removeLocationFromHost(hostProbe.id(),
+ (HostLocation) hostProbe.connectPoint());
+ }
+ break;
+ case PROBE_COMPLETED:
+ // Add this location if this is a discover probe.
+ if (hostProbe.mode() == ProbeMode.DISCOVER) {
+ HostLocation newLocation = new HostLocation(hostProbe.connectPoint(),
+ System.currentTimeMillis());
+ providerService.addLocationToHost(hostProbe.id(), newLocation);
+ }
+ break;
+ default:
+ log.warn("Unknown HostProbingEvent type: {}", event.type());
+ }
+ });
+ }
+
+ @Override
+ public void probeHost(Host host, ConnectPoint connectPoint, ProbeMode probeMode) {
+ probeHostInternal(host, connectPoint, probeMode, null, DEFAULT_RETRY);
+ }
+
+ // probeMac can be null if this is the very first probe and the mac is to-be-generated.
+ private void probeHostInternal(Host host, ConnectPoint connectPoint, ProbeMode probeMode,
+ MacAddress probeMac, int retry) {
+ if (!mastershipService.isLocalMaster(connectPoint.deviceId())) {
+ log.debug("Current node is not master of {}, abort probing {}", connectPoint.deviceId(), host);
+ return;
+ }
+
+ log.debug("probeHostInternal host={}, cp={}, mode={}, probeMac={}, retry={}", host, connectPoint,
+ probeMode, probeMac, retry);
+ Optional<IpAddress> ipOptional = host.ipAddresses().stream().findFirst();
+
+ if (ipOptional.isPresent()) {
+ probeMac = hostProbingProviderService.addProbingHost(host, connectPoint, probeMode, probeMac, retry);
+
+ IpAddress ip = ipOptional.get();
+ log.debug("Constructing {} probe for host {} with {}", probeMode, host.id(), ip);
+ Ethernet probe;
+ if (ip.isIp4()) {
+ probe = ARP.buildArpRequest(probeMac.toBytes(), Ip4Address.ZERO.toOctets(),
+ host.id().mac().toBytes(), ip.toOctets(),
+ host.id().mac().toBytes(), host.id().vlanId().toShort());
+ } else {
+ probe = NeighborSolicitation.buildNdpSolicit(
+ ip.getIp6Address(),
+ Ip6Address.valueOf(IPv6.getLinkLocalAddress(probeMac.toBytes())),
+ ip.getIp6Address(),
+ probeMac,
+ host.id().mac(),
+ host.id().vlanId());
+ }
+
+ // NOTE: delay the probe a little bit to wait for the store synchronization is done
+ hostProber.schedule(() ->
+ sendLocationProbe(probe, connectPoint), PROBE_INIT_DELAY_MS, TimeUnit.MILLISECONDS);
+ } else {
+ log.debug("Host {} has no IP address yet. Skip probing.", host);
+ }
+ }
+
+ /**
+ * Send the probe packet on given port.
+ *
+ * @param probe the probe packet
+ * @param connectPoint the port we want to probe
+ */
+ private void sendLocationProbe(Ethernet probe, ConnectPoint connectPoint) {
+ log.debug("Sending probe for host {} on location {} with probeMac {}",
+ probe.getDestinationMAC(), connectPoint, probe.getSourceMAC());
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
+ OutboundPacket outboundPacket = new DefaultOutboundPacket(connectPoint.deviceId(),
+ treatment, ByteBuffer.wrap(probe.serialize()));
+ packetService.emit(outboundPacket);
+ }
+}
\ No newline at end of file
diff --git a/providers/hostprobing/src/main/java/org/onosproject/provider/hostprobing/impl/package-info.java b/providers/hostprobing/src/main/java/org/onosproject/provider/hostprobing/impl/package-info.java
new file mode 100644
index 0000000..60ef748
--- /dev/null
+++ b/providers/hostprobing/src/main/java/org/onosproject/provider/hostprobing/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2018-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.
+ */
+
+/**
+ * Provider that uses packet service as a means of host discovery and tracking.
+ */
+package org.onosproject.provider.hostprobing.impl;