blob: d3120ddb5452d0e997d9d888d0e75d1f37587083 [file] [log] [blame]
alshabib0ccde6d2015-05-30 18:22:36 -07001/*
2 * Copyright 2014 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 */
16package org.onosproject.olt;
17
alshabib02cbe6a2016-01-14 17:27:11 -080018import com.google.common.collect.Maps;
19import com.google.common.collect.Sets;
alshabib0ccde6d2015-05-30 18:22:36 -070020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
alshabib0ccde6d2015-05-30 18:22:36 -070023import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Hart3e594642015-10-20 17:31:24 -070025import org.apache.felix.scr.annotations.Service;
alshabib0ccde6d2015-05-30 18:22:36 -070026import org.onlab.packet.VlanId;
alshabib0ccde6d2015-05-30 18:22:36 -070027import org.onosproject.core.ApplicationId;
28import org.onosproject.core.CoreService;
alshabib6b139b42016-01-12 15:55:53 -080029import org.onosproject.event.AbstractListenerManager;
Jonathan Hart3e594642015-10-20 17:31:24 -070030import org.onosproject.net.ConnectPoint;
alshabib0ccde6d2015-05-30 18:22:36 -070031import org.onosproject.net.DeviceId;
32import org.onosproject.net.PortNumber;
Jonathan Hart3e594642015-10-20 17:31:24 -070033import org.onosproject.net.config.ConfigFactory;
34import org.onosproject.net.config.NetworkConfigEvent;
35import org.onosproject.net.config.NetworkConfigListener;
36import org.onosproject.net.config.NetworkConfigRegistry;
37import org.onosproject.net.config.basics.SubjectFactories;
alshabib0ccde6d2015-05-30 18:22:36 -070038import org.onosproject.net.device.DeviceEvent;
39import org.onosproject.net.device.DeviceListener;
40import org.onosproject.net.device.DeviceService;
41import org.onosproject.net.flow.DefaultTrafficSelector;
42import org.onosproject.net.flow.DefaultTrafficTreatment;
43import org.onosproject.net.flow.TrafficSelector;
44import org.onosproject.net.flow.TrafficTreatment;
45import org.onosproject.net.flowobjective.DefaultForwardingObjective;
46import org.onosproject.net.flowobjective.FlowObjectiveService;
47import org.onosproject.net.flowobjective.ForwardingObjective;
alshabib49cadbd2016-01-12 18:06:53 -080048import org.onosproject.net.flowobjective.Objective;
49import org.onosproject.net.flowobjective.ObjectiveContext;
50import org.onosproject.net.flowobjective.ObjectiveError;
alshabib6b139b42016-01-12 15:55:53 -080051import org.onosproject.olt.api.AccessDeviceEvent;
52import org.onosproject.olt.api.AccessDeviceListener;
alshabib0ccde6d2015-05-30 18:22:36 -070053import org.slf4j.Logger;
54
Jonathan Hart3e594642015-10-20 17:31:24 -070055import java.util.Map;
Jonathan Hart48327842015-11-10 16:09:22 -080056import java.util.Optional;
alshabib02cbe6a2016-01-14 17:27:11 -080057import java.util.Set;
alshabib49cadbd2016-01-12 18:06:53 -080058import java.util.concurrent.CompletableFuture;
Jonathan Hart3e594642015-10-20 17:31:24 -070059import java.util.concurrent.ConcurrentHashMap;
alshabib49cadbd2016-01-12 18:06:53 -080060import java.util.concurrent.ExecutorService;
61import java.util.concurrent.Executors;
alshabib0ccde6d2015-05-30 18:22:36 -070062
alshabib49cadbd2016-01-12 18:06:53 -080063import static org.onlab.util.Tools.groupedThreads;
alshabib0ccde6d2015-05-30 18:22:36 -070064import static org.slf4j.LoggerFactory.getLogger;
65
66/**
Jonathan Hart3e594642015-10-20 17:31:24 -070067 * Provisions rules on access devices.
alshabib0ccde6d2015-05-30 18:22:36 -070068 */
Jonathan Hart3e594642015-10-20 17:31:24 -070069@Service
alshabib0ccde6d2015-05-30 18:22:36 -070070@Component(immediate = true)
alshabib6b139b42016-01-12 15:55:53 -080071public class Olt
72 extends AbstractListenerManager<AccessDeviceEvent, AccessDeviceListener>
73 implements AccessDeviceService {
alshabib0ccde6d2015-05-30 18:22:36 -070074 private final Logger log = getLogger(getClass());
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected FlowObjectiveService flowObjectiveService;
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected DeviceService deviceService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected CoreService coreService;
84
Jonathan Hart3e594642015-10-20 17:31:24 -070085 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected NetworkConfigRegistry networkConfig;
87
alshabib0ccde6d2015-05-30 18:22:36 -070088 private final DeviceListener deviceListener = new InternalDeviceListener();
89
90 private ApplicationId appId;
91
Jonathan Hart3e594642015-10-20 17:31:24 -070092 private static final VlanId DEFAULT_VLAN = VlanId.vlanId((short) 0);
alshabib17ff25f2015-06-01 10:57:31 -070093
alshabib49cadbd2016-01-12 18:06:53 -080094 private ExecutorService oltInstallers = Executors.newFixedThreadPool(4,
alshabib02cbe6a2016-01-14 17:27:11 -080095 groupedThreads("onos/olt-service",
96 "olt-installer-%d"));
alshabib1af3b712015-06-05 14:55:24 -070097
Jonathan Hart3e594642015-10-20 17:31:24 -070098 private Map<DeviceId, AccessDeviceData> oltData = new ConcurrentHashMap<>();
99
alshabib02cbe6a2016-01-14 17:27:11 -0800100 private Map<ConnectPoint, Set<ForwardingObjective.Builder>> objectives =
101 Maps.newConcurrentMap();
102
103 private Map<ConnectPoint, VlanId> subscribers = Maps.newConcurrentMap();
104
Jonathan Hart3e594642015-10-20 17:31:24 -0700105 private InternalNetworkConfigListener configListener =
106 new InternalNetworkConfigListener();
107 private static final Class<AccessDeviceConfig> CONFIG_CLASS =
108 AccessDeviceConfig.class;
109
110 private ConfigFactory<DeviceId, AccessDeviceConfig> configFactory =
111 new ConfigFactory<DeviceId, AccessDeviceConfig>(
112 SubjectFactories.DEVICE_SUBJECT_FACTORY, CONFIG_CLASS, "accessDevice") {
alshabib02cbe6a2016-01-14 17:27:11 -0800113 @Override
114 public AccessDeviceConfig createConfig() {
115 return new AccessDeviceConfig();
116 }
117 };
118
alshabib0ccde6d2015-05-30 18:22:36 -0700119
120 @Activate
121 public void activate() {
alshabib1af3b712015-06-05 14:55:24 -0700122 appId = coreService.registerApplication("org.onosproject.olt");
alshabibbeb2dc92015-06-05 13:35:13 -0700123
alshabib6b139b42016-01-12 15:55:53 -0800124 eventDispatcher.addSink(AccessDeviceEvent.class, listenerRegistry);
125
Jonathan Hart3e594642015-10-20 17:31:24 -0700126 networkConfig.registerConfigFactory(configFactory);
127 networkConfig.addListener(configListener);
128
129 networkConfig.getSubjects(DeviceId.class, AccessDeviceConfig.class).forEach(
130 subject -> {
131 AccessDeviceConfig config = networkConfig.getConfig(subject, AccessDeviceConfig.class);
132 if (config != null) {
133 AccessDeviceData data = config.getOlt();
134 oltData.put(data.deviceId(), data);
135 }
136 }
137 );
138
alshabib0ccde6d2015-05-30 18:22:36 -0700139 log.info("Started with Application ID {}", appId.id());
140 }
141
142 @Deactivate
143 public void deactivate() {
Jonathan Hart3e594642015-10-20 17:31:24 -0700144 networkConfig.removeListener(configListener);
145 networkConfig.unregisterConfigFactory(configFactory);
alshabib0ccde6d2015-05-30 18:22:36 -0700146 log.info("Stopped");
147 }
148
Jonathan Hart3e594642015-10-20 17:31:24 -0700149 @Override
150 public void provisionSubscriber(ConnectPoint port, VlanId vlan) {
151 AccessDeviceData olt = oltData.get(port.deviceId());
152
153 if (olt == null) {
154 log.warn("No data found for OLT device {}", port.deviceId());
155 return;
156 }
157
Jonathan Hart48327842015-11-10 16:09:22 -0800158 provisionVlans(olt.deviceId(), olt.uplink(), port.port(), vlan, olt.vlan(),
alshabibfa0dc662016-01-13 11:23:53 -0800159 olt.defaultVlan());
160 }
161
162 @Override
163 public void removeSubscriber(ConnectPoint port) {
alshabib02cbe6a2016-01-14 17:27:11 -0800164 AccessDeviceData olt = oltData.get(port.deviceId());
165
166 if (olt == null) {
167 log.warn("No data found for OLT device {}", port.deviceId());
168 return;
169 }
170
171 unprovisionSubscriber(olt.deviceId(), olt.uplink(), port.port(), olt.vlan());
172
173 }
174
175 private void unprovisionSubscriber(DeviceId deviceId, PortNumber uplink,
176 PortNumber subscriberPort, VlanId deviceVlan) {
177
178 //FIXME: This method is slightly ugly but it'll do until we have a better
179 // way to remove flows from the flow store.
180
181 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
182 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
183
184 ConnectPoint cp = new ConnectPoint(deviceId, subscriberPort);
185
186 VlanId subscriberVlan = subscribers.remove(cp);
187
188 Set<ForwardingObjective.Builder> fwds = objectives.remove(cp);
189
190 if (fwds == null || fwds.size() != 2) {
191 log.warn("Unknown or incomplete subscriber at {}", cp);
192 return;
193 }
194
195
196 fwds.stream().forEach(
197 fwd -> flowObjectiveService.forward(deviceId,
198 fwd.remove(new ObjectiveContext() {
199 @Override
200 public void onSuccess(Objective objective) {
201 upFuture.complete(null);
202 }
203
204 @Override
205 public void onError(Objective objective, ObjectiveError error) {
206 upFuture.complete(error);
207 }
208 })));
209
210 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
211 if (upStatus == null && downStatus == null) {
212 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_UNREGISTERED,
213 deviceId,
214 deviceVlan,
215 subscriberVlan));
216 } else if (downStatus != null) {
217 log.error("Subscriber with vlan {} on device {} " +
218 "on port {} failed downstream uninstallation: {}",
219 subscriberVlan, deviceId, subscriberPort, downStatus);
220 } else if (upStatus != null) {
221 log.error("Subscriber with vlan {} on device {} " +
222 "on port {} failed upstream uninstallation: {}",
223 subscriberVlan, deviceId, subscriberPort, upStatus);
224 }
225 }, oltInstallers);
alshabibfa0dc662016-01-13 11:23:53 -0800226
Jonathan Hart3e594642015-10-20 17:31:24 -0700227 }
228
229 private void provisionVlans(DeviceId deviceId, PortNumber uplinkPort,
230 PortNumber subscriberPort,
Jonathan Hart48327842015-11-10 16:09:22 -0800231 VlanId subscriberVlan, VlanId deviceVlan,
232 Optional<VlanId> defaultVlan) {
Jonathan Hart3e594642015-10-20 17:31:24 -0700233
alshabib49cadbd2016-01-12 18:06:53 -0800234 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture();
235 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture();
236
Jonathan Hart3e594642015-10-20 17:31:24 -0700237 TrafficSelector upstream = DefaultTrafficSelector.builder()
Jonathan Hart48327842015-11-10 16:09:22 -0800238 .matchVlanId((defaultVlan.isPresent()) ? defaultVlan.get() : DEFAULT_VLAN)
Jonathan Hart3e594642015-10-20 17:31:24 -0700239 .matchInPort(subscriberPort)
240 .build();
241
242 TrafficSelector downstream = DefaultTrafficSelector.builder()
243 .matchVlanId(deviceVlan)
244 .matchInPort(uplinkPort)
alshabibfa0dc662016-01-13 11:23:53 -0800245 .matchInnerVlanId(subscriberVlan)
Jonathan Hart3e594642015-10-20 17:31:24 -0700246 .build();
247
248 TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
alshabib49cadbd2016-01-12 18:06:53 -0800249 .pushVlan()
Jonathan Hart3e594642015-10-20 17:31:24 -0700250 .setVlanId(subscriberVlan)
251 .pushVlan()
252 .setVlanId(deviceVlan)
253 .setOutput(uplinkPort)
254 .build();
255
256 TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
257 .popVlan()
Jonathan Hart48327842015-11-10 16:09:22 -0800258 .setVlanId((defaultVlan.isPresent()) ? defaultVlan.get() : DEFAULT_VLAN)
Jonathan Hart3e594642015-10-20 17:31:24 -0700259 .setOutput(subscriberPort)
260 .build();
261
262
alshabib02cbe6a2016-01-14 17:27:11 -0800263 ForwardingObjective.Builder upFwd = DefaultForwardingObjective.builder()
Jonathan Hart3e594642015-10-20 17:31:24 -0700264 .withFlag(ForwardingObjective.Flag.VERSATILE)
265 .withPriority(1000)
266 .makePermanent()
267 .withSelector(upstream)
268 .fromApp(appId)
alshabib02cbe6a2016-01-14 17:27:11 -0800269 .withTreatment(upstreamTreatment);
alshabib49cadbd2016-01-12 18:06:53 -0800270
Jonathan Hart3e594642015-10-20 17:31:24 -0700271
alshabib02cbe6a2016-01-14 17:27:11 -0800272 ForwardingObjective.Builder downFwd = DefaultForwardingObjective.builder()
Jonathan Hart3e594642015-10-20 17:31:24 -0700273 .withFlag(ForwardingObjective.Flag.VERSATILE)
274 .withPriority(1000)
275 .makePermanent()
276 .withSelector(downstream)
277 .fromApp(appId)
alshabib02cbe6a2016-01-14 17:27:11 -0800278 .withTreatment(downstreamTreatment);
alshabib49cadbd2016-01-12 18:06:53 -0800279
alshabib02cbe6a2016-01-14 17:27:11 -0800280 ConnectPoint cp = new ConnectPoint(deviceId, subscriberPort);
Jonathan Hart3e594642015-10-20 17:31:24 -0700281
alshabib02cbe6a2016-01-14 17:27:11 -0800282 subscribers.put(cp, subscriberVlan);
283 objectives.put(cp, Sets.newHashSet(upFwd, downFwd));
284
285
286 flowObjectiveService.forward(deviceId, upFwd.add(new ObjectiveContext() {
287 @Override
288 public void onSuccess(Objective objective) {
289 upFuture.complete(null);
290 }
291
292 @Override
293 public void onError(Objective objective, ObjectiveError error) {
294 upFuture.complete(error);
295 }
296 }));
297
298
299 flowObjectiveService.forward(deviceId, downFwd.add(new ObjectiveContext() {
300 @Override
301 public void onSuccess(Objective objective) {
302 downFuture.complete(null);
303 }
304
305 @Override
306 public void onError(Objective objective, ObjectiveError error) {
307 downFuture.complete(error);
308 }
309 }));
alshabib49cadbd2016-01-12 18:06:53 -0800310
311 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
312 if (upStatus == null && downStatus == null) {
313 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_REGISTERED,
314 deviceId,
315 deviceVlan,
316 subscriberVlan));
317 } else if (downStatus != null) {
318 log.error("Subscriber with vlan {} on device {} " +
319 "on port {} failed downstream installation: {}",
320 subscriberVlan, deviceId, subscriberPort, downStatus);
321 } else if (upStatus != null) {
322 log.error("Subscriber with vlan {} on device {} " +
323 "on port {} failed upstream installation: {}",
324 subscriberVlan, deviceId, subscriberPort, upStatus);
325 }
326 }, oltInstallers);
327
Jonathan Hart3e594642015-10-20 17:31:24 -0700328 }
329
alshabib0ccde6d2015-05-30 18:22:36 -0700330 private class InternalDeviceListener implements DeviceListener {
331 @Override
332 public void event(DeviceEvent event) {
alshabib49cadbd2016-01-12 18:06:53 -0800333 DeviceId devId = event.subject().id();
334 if (!oltData.containsKey(devId)) {
335 log.debug("Device {} is not an OLT", devId);
alshabib6b139b42016-01-12 15:55:53 -0800336 return;
337 }
alshabib0ccde6d2015-05-30 18:22:36 -0700338 switch (event.type()) {
339 case PORT_ADDED:
340 case PORT_UPDATED:
alshabib0ccde6d2015-05-30 18:22:36 -0700341 break;
342 case DEVICE_ADDED:
alshabib6b139b42016-01-12 15:55:53 -0800343 post(new AccessDeviceEvent(
344 AccessDeviceEvent.Type.DEVICE_CONNECTED, devId,
345 null, null));
346 break;
alshabib0ccde6d2015-05-30 18:22:36 -0700347 case DEVICE_REMOVED:
alshabib6b139b42016-01-12 15:55:53 -0800348 post(new AccessDeviceEvent(
349 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, devId,
350 null, null));
351 break;
352 case DEVICE_UPDATED:
alshabib0ccde6d2015-05-30 18:22:36 -0700353 case DEVICE_SUSPENDED:
354 case DEVICE_AVAILABILITY_CHANGED:
355 case PORT_REMOVED:
356 case PORT_STATS_UPDATED:
357 default:
358 return;
359 }
360 }
361 }
362
Jonathan Hart3e594642015-10-20 17:31:24 -0700363 private class InternalNetworkConfigListener implements NetworkConfigListener {
364 @Override
365 public void event(NetworkConfigEvent event) {
366 switch (event.type()) {
367
alshabib02cbe6a2016-01-14 17:27:11 -0800368 case CONFIG_ADDED:
369 case CONFIG_UPDATED:
370 if (event.configClass().equals(CONFIG_CLASS)) {
371 AccessDeviceConfig config =
372 networkConfig.getConfig((DeviceId) event.subject(), CONFIG_CLASS);
373 if (config != null) {
374 oltData.put(config.getOlt().deviceId(), config.getOlt());
375 }
Jonathan Hart3e594642015-10-20 17:31:24 -0700376 }
alshabib02cbe6a2016-01-14 17:27:11 -0800377 break;
378 case CONFIG_UNREGISTERED:
379 case CONFIG_REMOVED:
380 default:
381 break;
Jonathan Hart3e594642015-10-20 17:31:24 -0700382 }
383 }
384 }
alshabib0ccde6d2015-05-30 18:22:36 -0700385
386}