blob: c6ee281dc2bd285a495c163163c08e1075a4e1ad [file] [log] [blame]
Jonathan Hartf8cd0522016-10-25 07:09:55 -07001/*
2 * Copyright 2017-present Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jonathan Hartf4bd0482017-01-27 15:11:18 -080017package org.onosproject.routing;
Jonathan Hartf8cd0522016-10-25 07:09:55 -070018
Jonathan Hartf8cd0522016-10-25 07:09:55 -070019import com.google.common.collect.Sets;
20import org.onosproject.incubator.net.intf.Interface;
21import org.onosproject.incubator.net.intf.InterfaceEvent;
22import org.onosproject.incubator.net.intf.InterfaceListener;
23import org.onosproject.incubator.net.intf.InterfaceService;
24import org.onosproject.net.DeviceId;
Jonathan Hart249b4cf2017-02-03 18:02:58 -080025import org.onosproject.net.device.DeviceService;
Jonathan Hartf8cd0522016-10-25 07:09:55 -070026import org.slf4j.Logger;
27import org.slf4j.LoggerFactory;
28
Jonathan Hartf8cd0522016-10-25 07:09:55 -070029import java.util.HashSet;
30import java.util.Set;
31import java.util.function.Consumer;
32import java.util.stream.Stream;
33
34import static com.google.common.base.Preconditions.checkNotNull;
35
36/**
Jonathan Hart249b4cf2017-02-03 18:02:58 -080037 * Manages the configuration and provisioning of a single-device router.
38 * It maintains which interfaces are part of the router when the configuration
39 * changes, and handles the provisioning/unprovisioning of interfaces when they
Jonathan Hartf8cd0522016-10-25 07:09:55 -070040 * are added/removed.
41 */
Jonathan Hart249b4cf2017-02-03 18:02:58 -080042public class Router {
Jonathan Hartf8cd0522016-10-25 07:09:55 -070043
44 private final Logger log = LoggerFactory.getLogger(getClass());
45
Jonathan Hart249b4cf2017-02-03 18:02:58 -080046 private final Consumer<InterfaceProvisionRequest> provisioner;
47 private final Consumer<InterfaceProvisionRequest> unprovisioner;
Jonathan Hartf8cd0522016-10-25 07:09:55 -070048
Jonathan Hart249b4cf2017-02-03 18:02:58 -080049 private RouterInfo info;
50
Jonathan Hartf8cd0522016-10-25 07:09:55 -070051 private Set<Interface> provisioned = new HashSet<>();
52
53 private InterfaceService interfaceService;
54 private InterfaceListener listener = new InternalInterfaceListener();
55
Charles Chanc6d227e2017-02-28 15:15:17 -080056 private DeviceService deviceService;
Jonathan Hart249b4cf2017-02-03 18:02:58 -080057
Charles Chanc6d227e2017-02-28 15:15:17 -080058 private AsyncDeviceFetcher asyncDeviceFetcher;
Jonathan Hartf8cd0522016-10-25 07:09:55 -070059
60 /**
61 * Creates a new router interface manager.
62 *
Jonathan Hart249b4cf2017-02-03 18:02:58 -080063 * @param info router configuration information
Jonathan Hartf8cd0522016-10-25 07:09:55 -070064 * @param interfaceService interface service
Jonathan Hart249b4cf2017-02-03 18:02:58 -080065 * @param deviceService device service
Jonathan Hartf8cd0522016-10-25 07:09:55 -070066 * @param provisioner consumer that will provision new interfaces
67 * @param unprovisioner consumer that will unprovision old interfaces
Charles Chanc6d227e2017-02-28 15:15:17 -080068 * @param forceUnprovision force unprovision when the device goes offline
Jonathan Hartf8cd0522016-10-25 07:09:55 -070069 */
Jonathan Hart249b4cf2017-02-03 18:02:58 -080070 public Router(RouterInfo info,
71 InterfaceService interfaceService,
72 DeviceService deviceService,
73 Consumer<InterfaceProvisionRequest> provisioner,
Charles Chanc6d227e2017-02-28 15:15:17 -080074 Consumer<InterfaceProvisionRequest> unprovisioner,
75 boolean forceUnprovision) {
Jonathan Hart249b4cf2017-02-03 18:02:58 -080076 this.info = checkNotNull(info);
Jonathan Hartf8cd0522016-10-25 07:09:55 -070077 this.provisioner = checkNotNull(provisioner);
78 this.unprovisioner = checkNotNull(unprovisioner);
79 this.interfaceService = checkNotNull(interfaceService);
Charles Chanc6d227e2017-02-28 15:15:17 -080080 this.deviceService = checkNotNull(deviceService);
Jonathan Hartf8cd0522016-10-25 07:09:55 -070081
Jonathan Hart249b4cf2017-02-03 18:02:58 -080082 this.asyncDeviceFetcher = AsyncDeviceFetcher.create(deviceService);
Charles Chanc6d227e2017-02-28 15:15:17 -080083 if (forceUnprovision) {
84 asyncDeviceFetcher.registerCallback(info.deviceId(), this::provision, this::forceUnprovision);
85 } else {
86 asyncDeviceFetcher.registerCallback(info.deviceId(), this::provision, null);
87 }
Jonathan Hartf8cd0522016-10-25 07:09:55 -070088
89 interfaceService.addListener(listener);
90 }
91
92 /**
93 * Cleans up the router and unprovisions all interfaces.
94 */
95 public void cleanup() {
Charles Chanc6d227e2017-02-28 15:15:17 -080096 asyncDeviceFetcher.shutdown();
97
Jonathan Hartf8cd0522016-10-25 07:09:55 -070098 interfaceService.removeListener(listener);
Jonathan Hart60e7f512017-02-10 10:24:24 -080099 asyncDeviceFetcher.shutdown();
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700100
101 unprovision();
102 }
103
104 /**
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800105 * Retrieves the router configuration information.
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700106 *
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800107 * @return router configuration information
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700108 */
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800109 public RouterInfo info() {
110 return info;
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700111 }
112
113 /**
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800114 * Changes the router configuration.
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700115 *
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800116 * @param newConfig new configuration
Charles Chanc6d227e2017-02-28 15:15:17 -0800117 * @param forceUnprovision true if we want to force unprovision the device when it goes offline
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700118 */
Charles Chanc6d227e2017-02-28 15:15:17 -0800119 public void changeConfiguration(RouterInfo newConfig, boolean forceUnprovision) {
120 if (forceUnprovision) {
121 asyncDeviceFetcher.registerCallback(info.deviceId(), this::provision, this::forceUnprovision);
122 } else {
123 asyncDeviceFetcher.registerCallback(info.deviceId(), this::provision, null);
124 }
125
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800126 Set<String> oldConfiguredInterfaces = info.interfaces();
127 info = newConfig;
128 Set<String> newConfiguredInterfaces = info.interfaces();
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700129
130 if (newConfiguredInterfaces.isEmpty() && !oldConfiguredInterfaces.isEmpty()) {
131 // Reverted to using all interfaces. Provision interfaces that
132 // weren't previously in the configured list
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800133 getInterfacesForDevice(info.deviceId())
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700134 .filter(intf -> !oldConfiguredInterfaces.contains(intf.name()))
135 .forEach(this::provision);
136 } else if (!newConfiguredInterfaces.isEmpty() && oldConfiguredInterfaces.isEmpty()) {
137 // Began using an interface list. Unprovision interfaces that
138 // are not in the new interface list.
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800139 getInterfacesForDevice(info.deviceId())
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700140 .filter(intf -> !newConfiguredInterfaces.contains(intf.name()))
141 .forEach(this::unprovision);
142 } else {
143 // The existing interface list was changed.
144 Set<String> toUnprovision = Sets.difference(oldConfiguredInterfaces, newConfiguredInterfaces);
145 Set<String> toProvision = Sets.difference(newConfiguredInterfaces, oldConfiguredInterfaces);
146
147 toUnprovision.forEach(name ->
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800148 getInterfacesForDevice(info.deviceId())
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700149 .filter(intf -> intf.name().equals(name))
150 .findFirst()
151 .ifPresent(this::unprovision)
152 );
153
154 toProvision.forEach(name ->
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800155 getInterfacesForDevice(info.deviceId())
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700156 .filter(intf -> intf.name().equals(name))
157 .findFirst()
158 .ifPresent(this::provision)
159 );
160 }
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700161 }
162
163 private void provision() {
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800164 getInterfacesForDevice(info.deviceId())
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700165 .forEach(this::provision);
166 }
167
168 private void unprovision() {
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800169 getInterfacesForDevice(info.deviceId())
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700170 .forEach(this::unprovision);
171 }
172
Charles Chanc6d227e2017-02-28 15:15:17 -0800173 private void forceUnprovision() {
174 getInterfacesForDevice(info.deviceId())
175 .forEach(this::forceUnprovision);
176 }
177
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700178 private void provision(Interface intf) {
Charles Chanc6d227e2017-02-28 15:15:17 -0800179 if (!provisioned.contains(intf) && deviceAvailable(intf) && shouldProvision(intf)) {
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700180 log.info("Provisioning interface {}", intf);
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800181 provisioner.accept(InterfaceProvisionRequest.of(info, intf));
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700182 provisioned.add(intf);
183 }
184 }
185
186 private void unprovision(Interface intf) {
Charles Chanc6d227e2017-02-28 15:15:17 -0800187 if (provisioned.contains(intf) && deviceAvailable(intf) && shouldProvision(intf)) {
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700188 log.info("Unprovisioning interface {}", intf);
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800189 unprovisioner.accept(InterfaceProvisionRequest.of(info, intf));
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700190 provisioned.remove(intf);
191 }
192 }
193
Charles Chanc6d227e2017-02-28 15:15:17 -0800194 private void forceUnprovision(Interface intf) {
195 // Skip availability check when force unprovisioning an interface
196 if (provisioned.contains(intf) && shouldProvision(intf)) {
197 log.info("Unprovisioning interface {}", intf);
198 unprovisioner.accept(InterfaceProvisionRequest.of(info, intf));
199 provisioned.remove(intf);
200 }
201 }
202
203 private boolean deviceAvailable(Interface intf) {
204 return deviceService.isAvailable(intf.connectPoint().deviceId());
205 }
206
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800207 private boolean shouldProvision(Interface intf) {
Charles Chanc6d227e2017-02-28 15:15:17 -0800208 return info.interfaces().isEmpty() || info.interfaces().contains(intf.name());
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700209 }
210
211 private Stream<Interface> getInterfacesForDevice(DeviceId deviceId) {
212 return interfaceService.getInterfaces().stream()
213 .filter(intf -> intf.connectPoint().deviceId().equals(deviceId));
214 }
215
216 private class InternalInterfaceListener implements InterfaceListener {
217 @Override
218 public void event(InterfaceEvent event) {
219 Interface intf = event.subject();
220 switch (event.type()) {
221 case INTERFACE_ADDED:
222 provision(intf);
223 break;
224 case INTERFACE_UPDATED:
225 // TODO
226 break;
227 case INTERFACE_REMOVED:
228 unprovision(intf);
229 break;
230 default:
231 break;
232 }
233 }
234 }
235}