blob: 855084e8d19469c3b139e812a20081b9d4238832 [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);
99
100 unprovision();
101 }
102
103 /**
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800104 * Retrieves the router configuration information.
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700105 *
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800106 * @return router configuration information
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700107 */
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800108 public RouterInfo info() {
109 return info;
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700110 }
111
112 /**
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800113 * Changes the router configuration.
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700114 *
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800115 * @param newConfig new configuration
Charles Chanc6d227e2017-02-28 15:15:17 -0800116 * @param forceUnprovision true if we want to force unprovision the device when it goes offline
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700117 */
Charles Chanc6d227e2017-02-28 15:15:17 -0800118 public void changeConfiguration(RouterInfo newConfig, boolean forceUnprovision) {
119 if (forceUnprovision) {
120 asyncDeviceFetcher.registerCallback(info.deviceId(), this::provision, this::forceUnprovision);
121 } else {
122 asyncDeviceFetcher.registerCallback(info.deviceId(), this::provision, null);
123 }
124
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800125 Set<String> oldConfiguredInterfaces = info.interfaces();
126 info = newConfig;
127 Set<String> newConfiguredInterfaces = info.interfaces();
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700128
129 if (newConfiguredInterfaces.isEmpty() && !oldConfiguredInterfaces.isEmpty()) {
130 // Reverted to using all interfaces. Provision interfaces that
131 // weren't previously in the configured list
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800132 getInterfacesForDevice(info.deviceId())
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700133 .filter(intf -> !oldConfiguredInterfaces.contains(intf.name()))
134 .forEach(this::provision);
135 } else if (!newConfiguredInterfaces.isEmpty() && oldConfiguredInterfaces.isEmpty()) {
136 // Began using an interface list. Unprovision interfaces that
137 // are not in the new interface list.
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800138 getInterfacesForDevice(info.deviceId())
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700139 .filter(intf -> !newConfiguredInterfaces.contains(intf.name()))
140 .forEach(this::unprovision);
141 } else {
142 // The existing interface list was changed.
143 Set<String> toUnprovision = Sets.difference(oldConfiguredInterfaces, newConfiguredInterfaces);
144 Set<String> toProvision = Sets.difference(newConfiguredInterfaces, oldConfiguredInterfaces);
145
146 toUnprovision.forEach(name ->
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800147 getInterfacesForDevice(info.deviceId())
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700148 .filter(intf -> intf.name().equals(name))
149 .findFirst()
150 .ifPresent(this::unprovision)
151 );
152
153 toProvision.forEach(name ->
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800154 getInterfacesForDevice(info.deviceId())
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700155 .filter(intf -> intf.name().equals(name))
156 .findFirst()
157 .ifPresent(this::provision)
158 );
159 }
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700160 }
161
162 private void provision() {
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800163 getInterfacesForDevice(info.deviceId())
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700164 .forEach(this::provision);
165 }
166
167 private void unprovision() {
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800168 getInterfacesForDevice(info.deviceId())
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700169 .forEach(this::unprovision);
170 }
171
Charles Chanc6d227e2017-02-28 15:15:17 -0800172 private void forceUnprovision() {
173 getInterfacesForDevice(info.deviceId())
174 .forEach(this::forceUnprovision);
175 }
176
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700177 private void provision(Interface intf) {
Charles Chanc6d227e2017-02-28 15:15:17 -0800178 if (!provisioned.contains(intf) && deviceAvailable(intf) && shouldProvision(intf)) {
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700179 log.info("Provisioning interface {}", intf);
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800180 provisioner.accept(InterfaceProvisionRequest.of(info, intf));
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700181 provisioned.add(intf);
182 }
183 }
184
185 private void unprovision(Interface intf) {
Charles Chanc6d227e2017-02-28 15:15:17 -0800186 if (provisioned.contains(intf) && deviceAvailable(intf) && shouldProvision(intf)) {
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700187 log.info("Unprovisioning interface {}", intf);
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800188 unprovisioner.accept(InterfaceProvisionRequest.of(info, intf));
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700189 provisioned.remove(intf);
190 }
191 }
192
Charles Chanc6d227e2017-02-28 15:15:17 -0800193 private void forceUnprovision(Interface intf) {
194 // Skip availability check when force unprovisioning an interface
195 if (provisioned.contains(intf) && shouldProvision(intf)) {
196 log.info("Unprovisioning interface {}", intf);
197 unprovisioner.accept(InterfaceProvisionRequest.of(info, intf));
198 provisioned.remove(intf);
199 }
200 }
201
202 private boolean deviceAvailable(Interface intf) {
203 return deviceService.isAvailable(intf.connectPoint().deviceId());
204 }
205
Jonathan Hart249b4cf2017-02-03 18:02:58 -0800206 private boolean shouldProvision(Interface intf) {
Charles Chanc6d227e2017-02-28 15:15:17 -0800207 return info.interfaces().isEmpty() || info.interfaces().contains(intf.name());
Jonathan Hartf8cd0522016-10-25 07:09:55 -0700208 }
209
210 private Stream<Interface> getInterfacesForDevice(DeviceId deviceId) {
211 return interfaceService.getInterfaces().stream()
212 .filter(intf -> intf.connectPoint().deviceId().equals(deviceId));
213 }
214
215 private class InternalInterfaceListener implements InterfaceListener {
216 @Override
217 public void event(InterfaceEvent event) {
218 Interface intf = event.subject();
219 switch (event.type()) {
220 case INTERFACE_ADDED:
221 provision(intf);
222 break;
223 case INTERFACE_UPDATED:
224 // TODO
225 break;
226 case INTERFACE_REMOVED:
227 unprovision(intf);
228 break;
229 default:
230 break;
231 }
232 }
233 }
234}