Extend ControlPlaneRedirect (vRouter) to allow multiple redirects
CORD-787
Change-Id: I9e5a6f74b7239eb0dcb42b52e562698ce415debc
diff --git a/apps/routing-api/src/main/java/org/onosproject/routing/AsyncDeviceFetcher.java b/apps/routing-api/src/main/java/org/onosproject/routing/AsyncDeviceFetcher.java
index fd5dfc3..65ffec0 100644
--- a/apps/routing-api/src/main/java/org/onosproject/routing/AsyncDeviceFetcher.java
+++ b/apps/routing-api/src/main/java/org/onosproject/routing/AsyncDeviceFetcher.java
@@ -25,6 +25,8 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
+import static com.google.common.base.Preconditions.checkNotNull;
+
/**
* Provides a means of asynchronously waiting on devices.
*/
@@ -37,7 +39,7 @@
private Map<DeviceId, CompletableFuture<DeviceId>> devices = new ConcurrentHashMap();
private AsyncDeviceFetcher(DeviceService deviceService) {
- this.deviceService = deviceService;
+ this.deviceService = checkNotNull(deviceService);
deviceService.addListener(listener);
}
diff --git a/apps/routing-api/src/main/java/org/onosproject/routing/InterfaceProvisionRequest.java b/apps/routing-api/src/main/java/org/onosproject/routing/InterfaceProvisionRequest.java
new file mode 100644
index 0000000..4815236
--- /dev/null
+++ b/apps/routing-api/src/main/java/org/onosproject/routing/InterfaceProvisionRequest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package org.onosproject.routing;
+
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.ConnectPoint;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Encapsulates information needed to provision a router interface.
+ */
+public final class InterfaceProvisionRequest {
+
+ private final RouterInfo info;
+ private final Interface intf;
+
+ private InterfaceProvisionRequest(RouterInfo info, Interface intf) {
+ this.info = checkNotNull(info);
+ this.intf = checkNotNull(intf);
+ }
+
+ /**
+ * Retrieves the router's control plane connect point.
+ *
+ * @return connect point
+ */
+ public ConnectPoint controlPlaneConnectPoint() {
+ return info.controlPlaneConnectPoint();
+ }
+
+ /**
+ * Retrieves the router configuration info.
+ *
+ * @return router configuration info
+ */
+ public RouterInfo info() {
+ return info;
+ }
+
+ /**
+ * Retrieves the interface to be (un)provisioned.
+ *
+ * @return interface
+ */
+ public Interface intf() {
+ return intf;
+ }
+
+ /**
+ * Creates a new provision request from a router configuration and an
+ * interface.
+ *
+ * @param info router configuration info
+ * @param intf interface
+ * @return provision request
+ */
+ public static InterfaceProvisionRequest of(RouterInfo info, Interface intf) {
+ return new InterfaceProvisionRequest(info, intf);
+ }
+}
diff --git a/apps/routing-api/src/main/java/org/onosproject/routing/RouterInterfaceManager.java b/apps/routing-api/src/main/java/org/onosproject/routing/Router.java
similarity index 65%
rename from apps/routing-api/src/main/java/org/onosproject/routing/RouterInterfaceManager.java
rename to apps/routing-api/src/main/java/org/onosproject/routing/Router.java
index 8b1deff..97fee88 100644
--- a/apps/routing-api/src/main/java/org/onosproject/routing/RouterInterfaceManager.java
+++ b/apps/routing-api/src/main/java/org/onosproject/routing/Router.java
@@ -16,17 +16,16 @@
package org.onosproject.routing;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import org.onosproject.incubator.net.intf.Interface;
import org.onosproject.incubator.net.intf.InterfaceEvent;
import org.onosproject.incubator.net.intf.InterfaceListener;
import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
@@ -35,46 +34,58 @@
import static com.google.common.base.Preconditions.checkNotNull;
/**
- * Manages which interfaces are part of the router when the configuration is
- * updated, and handles the provisioning/unprovisioning of interfaces when they
+ * Manages the configuration and provisioning of a single-device router.
+ * It maintains which interfaces are part of the router when the configuration
+ * changes, and handles the provisioning/unprovisioning of interfaces when they
* are added/removed.
*/
-public class RouterInterfaceManager {
+public class Router {
private final Logger log = LoggerFactory.getLogger(getClass());
- private final Consumer<Interface> provisioner;
- private final Consumer<Interface> unprovisioner;
+ private final Consumer<InterfaceProvisionRequest> provisioner;
+ private final Consumer<InterfaceProvisionRequest> unprovisioner;
- private Set<String> configuredInterfaces = Collections.emptySet();
+ private RouterInfo info;
+
private Set<Interface> provisioned = new HashSet<>();
private InterfaceService interfaceService;
private InterfaceListener listener = new InternalInterfaceListener();
- private final DeviceId routerDeviceId;
+ private AsyncDeviceFetcher asyncDeviceFetcher;
+
+ private volatile boolean deviceAvailable = false;
/**
* Creates a new router interface manager.
*
- * @param deviceId router device ID
- * @param configuredInterfaces names of interfaces configured for this router
+ * @param info router configuration information
* @param interfaceService interface service
+ * @param deviceService device service
* @param provisioner consumer that will provision new interfaces
* @param unprovisioner consumer that will unprovision old interfaces
*/
- public RouterInterfaceManager(DeviceId deviceId,
- Set<String> configuredInterfaces,
- InterfaceService interfaceService,
- Consumer<Interface> provisioner,
- Consumer<Interface> unprovisioner) {
- this.routerDeviceId = checkNotNull(deviceId);
+ public Router(RouterInfo info,
+ InterfaceService interfaceService,
+ DeviceService deviceService,
+ Consumer<InterfaceProvisionRequest> provisioner,
+ Consumer<InterfaceProvisionRequest> unprovisioner) {
+ this.info = checkNotNull(info);
this.provisioner = checkNotNull(provisioner);
this.unprovisioner = checkNotNull(unprovisioner);
this.interfaceService = checkNotNull(interfaceService);
- this.configuredInterfaces = checkNotNull(configuredInterfaces);
- provision();
+ this.asyncDeviceFetcher = AsyncDeviceFetcher.create(deviceService);
+ asyncDeviceFetcher.getDevice(info.deviceId())
+ .thenAccept(deviceId1 -> {
+ deviceAvailable = true;
+ provision();
+ }).whenComplete((v, t) -> {
+ if (t != null) {
+ log.error("Error provisioning: ", t);
+ }
+ });
interfaceService.addListener(listener);
}
@@ -89,33 +100,34 @@
}
/**
- * Retrieves the set of configured interface names.
+ * Retrieves the router configuration information.
*
- * @return interface names
+ * @return router configuration information
*/
- public Set<String> configuredInterfaces() {
- return configuredInterfaces;
+ public RouterInfo info() {
+ return info;
}
/**
- * Changes the set of interfaces configured on the router.
+ * Changes the router configuration.
*
- * @param newConfiguredInterfaces new set of router interfaces
+ * @param newConfig new configuration
*/
- public void changeConfiguredInterfaces(Set<String> newConfiguredInterfaces) {
- Set<String> oldConfiguredInterfaces = configuredInterfaces;
- configuredInterfaces = ImmutableSet.copyOf(newConfiguredInterfaces);
+ public void changeConfiguration(RouterInfo newConfig) {
+ Set<String> oldConfiguredInterfaces = info.interfaces();
+ info = newConfig;
+ Set<String> newConfiguredInterfaces = info.interfaces();
if (newConfiguredInterfaces.isEmpty() && !oldConfiguredInterfaces.isEmpty()) {
// Reverted to using all interfaces. Provision interfaces that
// weren't previously in the configured list
- getInterfacesForDevice(routerDeviceId)
+ getInterfacesForDevice(info.deviceId())
.filter(intf -> !oldConfiguredInterfaces.contains(intf.name()))
.forEach(this::provision);
} else if (!newConfiguredInterfaces.isEmpty() && oldConfiguredInterfaces.isEmpty()) {
// Began using an interface list. Unprovision interfaces that
// are not in the new interface list.
- getInterfacesForDevice(routerDeviceId)
+ getInterfacesForDevice(info.deviceId())
.filter(intf -> !newConfiguredInterfaces.contains(intf.name()))
.forEach(this::unprovision);
} else {
@@ -124,39 +136,37 @@
Set<String> toProvision = Sets.difference(newConfiguredInterfaces, oldConfiguredInterfaces);
toUnprovision.forEach(name ->
- getInterfacesForDevice(routerDeviceId)
+ getInterfacesForDevice(info.deviceId())
.filter(intf -> intf.name().equals(name))
.findFirst()
.ifPresent(this::unprovision)
);
toProvision.forEach(name ->
- getInterfacesForDevice(routerDeviceId)
+ getInterfacesForDevice(info.deviceId())
.filter(intf -> intf.name().equals(name))
.findFirst()
.ifPresent(this::provision)
);
}
-
- configuredInterfaces = newConfiguredInterfaces;
}
private void provision() {
- getInterfacesForDevice(routerDeviceId)
- .filter(this::shouldUse)
+ getInterfacesForDevice(info.deviceId())
+ .filter(this::shouldProvision)
.forEach(this::provision);
}
private void unprovision() {
- getInterfacesForDevice(routerDeviceId)
- .filter(this::shouldUse)
+ getInterfacesForDevice(info.deviceId())
+ .filter(this::shouldProvision)
.forEach(this::unprovision);
}
private void provision(Interface intf) {
- if (!provisioned.contains(intf) && shouldUse(intf)) {
+ if (!provisioned.contains(intf) && shouldProvision(intf)) {
log.info("Provisioning interface {}", intf);
- provisioner.accept(intf);
+ provisioner.accept(InterfaceProvisionRequest.of(info, intf));
provisioned.add(intf);
}
}
@@ -164,13 +174,14 @@
private void unprovision(Interface intf) {
if (provisioned.contains(intf)) {
log.info("Unprovisioning interface {}", intf);
- unprovisioner.accept(intf);
+ unprovisioner.accept(InterfaceProvisionRequest.of(info, intf));
provisioned.remove(intf);
}
}
- private boolean shouldUse(Interface intf) {
- return configuredInterfaces.isEmpty() || configuredInterfaces.contains(intf.name());
+ private boolean shouldProvision(Interface intf) {
+ return deviceAvailable &&
+ (info.interfaces().isEmpty() || info.interfaces().contains(intf.name()));
}
private Stream<Interface> getInterfacesForDevice(DeviceId deviceId) {
diff --git a/apps/routing-api/src/main/java/org/onosproject/routing/RouterInfo.java b/apps/routing-api/src/main/java/org/onosproject/routing/RouterInfo.java
new file mode 100644
index 0000000..df44a37
--- /dev/null
+++ b/apps/routing-api/src/main/java/org/onosproject/routing/RouterInfo.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package org.onosproject.routing;
+
+import com.google.common.collect.ImmutableSet;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.routing.config.RoutersConfig;
+
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Stores configuration information about a router.
+ */
+public class RouterInfo {
+
+ private final ConnectPoint controlPlaneConnectPoint;
+ private final boolean ospfEnabled;
+ private final Set<String> interfaces;
+
+ /**
+ * Creates a new router info.
+ *
+ * @param controlPlaneConnectPoint control plane connect point
+ * @param ospfEnabled whether OSPF is enabled
+ * @param interfaces set of interface names
+ */
+ public RouterInfo(ConnectPoint controlPlaneConnectPoint, boolean ospfEnabled, Set<String> interfaces) {
+ this.controlPlaneConnectPoint = checkNotNull(controlPlaneConnectPoint);
+ this.ospfEnabled = checkNotNull(ospfEnabled);
+ this.interfaces = ImmutableSet.copyOf(checkNotNull(interfaces));
+ }
+
+ /**
+ * Returns the control plane connect point.
+ *
+ * @return connect point
+ */
+ public ConnectPoint controlPlaneConnectPoint() {
+ return controlPlaneConnectPoint;
+ }
+
+ /**
+ * Returns the router device ID.
+ *
+ * @return device ID
+ */
+ public DeviceId deviceId() {
+ return controlPlaneConnectPoint.deviceId();
+ }
+
+ /**
+ * Returns whether OSPF is enabled on the router.
+ *
+ * @return OSPF enabled
+ */
+ public boolean ospfEnabled() {
+ return ospfEnabled;
+ }
+
+ /**
+ * Returns the set of interfaces belonging to the router.
+ *
+ * @return set of interface names
+ */
+ public Set<String> interfaces() {
+ return interfaces;
+ }
+
+ /**
+ * Creates a router info from a router config.
+ *
+ * @param config router config
+ * @return new router info object
+ */
+ public static RouterInfo from(RoutersConfig.Router config) {
+ return new RouterInfo(config.controlPlaneConnectPoint(), config.isOspfEnabled(), config.interfaces());
+ }
+}
diff --git a/apps/routing-api/src/main/java/org/onosproject/routing/config/RouterConfigHelper.java b/apps/routing-api/src/main/java/org/onosproject/routing/config/RouterConfigHelper.java
new file mode 100644
index 0000000..ddfd1db
--- /dev/null
+++ b/apps/routing-api/src/main/java/org/onosproject/routing/config/RouterConfigHelper.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package org.onosproject.routing.config;
+
+import com.google.common.collect.Sets;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.routing.RoutingService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Helper class to manage retrieving config from multiple Config sections.
+ * Should be unnecessary once old config is removed.
+ */
+public final class RouterConfigHelper {
+
+ private static final String WARNING =
+ "Config apps/org.onosproject.routing/router is deprecated "
+ + "and will be removed in a future release.";
+ private static final String WARNING2 =
+ "Use apps/org.onosproject.routing/routers instead";
+
+ private static final Logger log = LoggerFactory.getLogger(RouterConfigHelper.class);
+
+ private RouterConfigHelper() {
+ // make checkstyle happy
+ }
+
+ /**
+ * Retrieves the router configurations.
+ *
+ * @param configService network config service
+ * @param routingAppId routing app ID
+ * @return set of router configurations
+ */
+ public static Set<RoutersConfig.Router> getRouterConfigurations(
+ NetworkConfigService configService, ApplicationId routingAppId) {
+
+ RouterConfig config = configService.getConfig(
+ routingAppId, RoutingService.ROUTER_CONFIG_CLASS);
+ RoutersConfig multiConfig = configService.getConfig(routingAppId, RoutersConfig.class);
+
+ if (config != null) {
+ log.warn(WARNING);
+ log.warn(WARNING2);
+
+ return Collections.singleton(
+ new RoutersConfig.Router(config.getControlPlaneConnectPoint(),
+ config.getOspfEnabled(),
+ Sets.newHashSet(config.getInterfaces())));
+ } else if (multiConfig != null) {
+ return multiConfig.getRouters();
+ } else {
+ return Collections.emptySet();
+ }
+ }
+}
diff --git a/apps/routing-api/src/main/java/org/onosproject/routing/config/RoutersConfig.java b/apps/routing-api/src/main/java/org/onosproject/routing/config/RoutersConfig.java
index 940a727..0a60023 100644
--- a/apps/routing-api/src/main/java/org/onosproject/routing/config/RoutersConfig.java
+++ b/apps/routing-api/src/main/java/org/onosproject/routing/config/RoutersConfig.java
@@ -19,6 +19,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableSet;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.config.Config;
@@ -50,19 +51,18 @@
JsonNode intfNode = routerNode.path(INTERFACES);
- if (intfNode.isMissingNode() || !intfNode.isArray()) {
- continue;
- }
- ArrayNode array = (ArrayNode) intfNode;
Set<String> interfaces = new HashSet<>(array.size());
- for (JsonNode intf : array) {
- interfaces.add(intf.asText());
+ if (!intfNode.isMissingNode()) {
+ ArrayNode array = (ArrayNode) intfNode;
+ for (JsonNode intf : array) {
+ interfaces.add(intf.asText());
+ }
}
routers.add(new Router(connectPoint, ospfEnabled, interfaces));
}
- return routers;
+ return ImmutableSet.copyOf(routers);
}
@Override