blob: a74699c15d71d2d3c7f90174d1aa93957658cb35 [file] [log] [blame]
alshabibf0e7e702015-05-30 18:22:36 -07001/*
Brian O'Connord6a135a2017-08-03 22:46:05 -07002 * Copyright 2016-present Open Networking Foundation
alshabibf0e7e702015-05-30 18:22:36 -07003 *
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 */
alshabib36a4d732016-06-01 16:03:59 -070016package org.opencord.olt.impl;
alshabibf0e7e702015-05-30 18:22:36 -070017
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010018import com.google.common.collect.ImmutableMap;
19import com.google.common.collect.Sets;
Tunahan Sezena07fe962021-02-24 08:24:24 +000020import org.onlab.packet.MacAddress;
alshabibf0e7e702015-05-30 18:22:36 -070021import org.onlab.packet.VlanId;
Jonathan Hart4f178fa2020-02-03 10:46:01 -080022import org.onlab.util.KryoNamespace;
Gamze Abaka1f62dd92020-05-07 08:58:13 +000023import org.onosproject.cfg.ComponentConfigService;
Jonathan Hart4f178fa2020-02-03 10:46:01 -080024import org.onosproject.cluster.ClusterEvent;
25import org.onosproject.cluster.ClusterEventListener;
26import org.onosproject.cluster.ClusterService;
27import org.onosproject.cluster.ControllerNode;
Andrea Campanellaaf39b4c2020-05-13 14:07:44 +020028import org.onosproject.cluster.LeadershipService;
Jonathan Hart4f178fa2020-02-03 10:46:01 -080029import org.onosproject.cluster.NodeId;
alshabibf0e7e702015-05-30 18:22:36 -070030import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
alshabib8e4fd2f2016-01-12 15:55:53 -080032import org.onosproject.event.AbstractListenerManager;
Andrea Campanellaaf39b4c2020-05-13 14:07:44 +020033import org.onosproject.mastership.MastershipService;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010034import org.onosproject.net.AnnotationKeys;
Jonathan Harte533a422015-10-20 17:31:24 -070035import org.onosproject.net.ConnectPoint;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010036import org.onosproject.net.Device;
alshabibf0e7e702015-05-30 18:22:36 -070037import org.onosproject.net.DeviceId;
Tunahan Sezena07fe962021-02-24 08:24:24 +000038import org.onosproject.net.Host;
alshabibdec2e252016-01-15 12:20:25 -080039import org.onosproject.net.Port;
alshabibf0e7e702015-05-30 18:22:36 -070040import org.onosproject.net.PortNumber;
41import org.onosproject.net.device.DeviceEvent;
42import org.onosproject.net.device.DeviceListener;
43import org.onosproject.net.device.DeviceService;
Andrea Campanella438e1ad2021-03-26 11:41:16 +010044import org.onosproject.net.driver.DriverService;
Hardik Windlassa58fbee2020-03-12 18:33:55 +053045import org.onosproject.net.flow.FlowRuleService;
alshabibf0e7e702015-05-30 18:22:36 -070046import org.onosproject.net.flowobjective.FlowObjectiveService;
47import org.onosproject.net.flowobjective.ForwardingObjective;
alshabib3ea82642016-01-12 18:06:53 -080048import org.onosproject.net.flowobjective.Objective;
49import org.onosproject.net.flowobjective.ObjectiveContext;
50import org.onosproject.net.flowobjective.ObjectiveError;
Tunahan Sezena07fe962021-02-24 08:24:24 +000051import org.onosproject.net.host.HostEvent;
52import org.onosproject.net.host.HostListener;
53import org.onosproject.net.host.HostService;
Saurav Daseae48de2019-06-19 13:26:15 -070054import org.onosproject.net.meter.MeterId;
Jonathan Hart4f178fa2020-02-03 10:46:01 -080055import org.onosproject.store.serializers.KryoNamespaces;
56import org.onosproject.store.service.ConsistentMultimap;
57import org.onosproject.store.service.Serializer;
58import org.onosproject.store.service.StorageService;
alshabib36a4d732016-06-01 16:03:59 -070059import org.opencord.olt.AccessDeviceEvent;
60import org.opencord.olt.AccessDeviceListener;
61import org.opencord.olt.AccessDeviceService;
Amit Ghosh31939522018-08-16 13:28:21 +010062import org.opencord.olt.AccessSubscriberId;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000063import org.opencord.olt.internalapi.AccessDeviceFlowService;
64import org.opencord.olt.internalapi.AccessDeviceMeterService;
Gamze Abaka641fc072018-09-04 09:16:27 +000065import org.opencord.sadis.BandwidthProfileInformation;
66import org.opencord.sadis.BaseInformationService;
67import org.opencord.sadis.SadisService;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +010068import org.opencord.sadis.SubscriberAndDeviceInformation;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000069import org.opencord.sadis.UniTagInformation;
alshabibe0559672016-02-21 14:49:51 -080070import org.osgi.service.component.ComponentContext;
Carmelo Casconeca931162019-07-15 18:22:24 -070071import org.osgi.service.component.annotations.Activate;
72import org.osgi.service.component.annotations.Component;
73import org.osgi.service.component.annotations.Deactivate;
74import org.osgi.service.component.annotations.Modified;
75import org.osgi.service.component.annotations.Reference;
76import org.osgi.service.component.annotations.ReferenceCardinality;
Ilayda Ozdemir90a93622021-02-25 09:40:58 +000077import org.osgi.service.component.annotations.ReferencePolicy;
alshabibf0e7e702015-05-30 18:22:36 -070078import org.slf4j.Logger;
79
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010080import java.util.ArrayList;
81import java.util.Collection;
82import java.util.Dictionary;
83import java.util.List;
84import java.util.Map;
85import java.util.Optional;
86import java.util.Properties;
87import java.util.Set;
88import java.util.concurrent.BlockingQueue;
89import java.util.concurrent.CompletableFuture;
Andrea Campanella7a1d7e72020-11-05 10:40:10 +010090import java.util.concurrent.ExecutorService;
91import java.util.concurrent.Executors;
92import java.util.concurrent.LinkedBlockingQueue;
93import java.util.concurrent.ScheduledExecutorService;
94import java.util.concurrent.TimeUnit;
95import java.util.concurrent.atomic.AtomicBoolean;
96
97import static com.google.common.base.Preconditions.checkNotNull;
98import static com.google.common.base.Strings.isNullOrEmpty;
99import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
100import static java.util.stream.Collectors.*;
101import static org.onlab.util.Tools.get;
102import static org.onlab.util.Tools.groupedThreads;
103import static org.opencord.olt.impl.OsgiPropertyConstants.*;
104import static org.slf4j.LoggerFactory.getLogger;
alshabibf0e7e702015-05-30 18:22:36 -0700105
106/**
Jonathan Harte533a422015-10-20 17:31:24 -0700107 * Provisions rules on access devices.
alshabibf0e7e702015-05-30 18:22:36 -0700108 */
Carmelo Casconeca931162019-07-15 18:22:24 -0700109@Component(immediate = true,
110 property = {
Carmelo Casconeca931162019-07-15 18:22:24 -0700111 DEFAULT_BP_ID + ":String=" + DEFAULT_BP_ID_DEFAULT,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000112 DEFAULT_MCAST_SERVICE_NAME + ":String=" + DEFAULT_MCAST_SERVICE_NAME_DEFAULT,
Saurav Das2d3777a2020-08-07 18:48:51 -0700113 EAPOL_DELETE_RETRY_MAX_ATTEMPS + ":Integer=" +
114 EAPOL_DELETE_RETRY_MAX_ATTEMPS_DEFAULT,
Matteo Scandoloa4aaa972020-10-23 15:24:38 -0700115 PROVISION_DELAY + ":Integer=" + PROVISION_DELAY_DEFAULT,
Carmelo Casconeca931162019-07-15 18:22:24 -0700116 })
alshabib8e4fd2f2016-01-12 15:55:53 -0800117public class Olt
118 extends AbstractListenerManager<AccessDeviceEvent, AccessDeviceListener>
119 implements AccessDeviceService {
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000120 private static final String SADIS_NOT_RUNNING = "Sadis is not running.";
Charles Chan54f110f2017-01-20 11:22:42 -0800121 private static final String APP_NAME = "org.opencord.olt";
alshabibe0559672016-02-21 14:49:51 -0800122
Gamze Abakada282b42019-03-11 13:16:48 +0000123 private static final short EAPOL_DEFAULT_VLAN = 4091;
Gamze Abaka838d8142019-02-21 07:06:55 +0000124 private static final String NO_UPLINK_PORT = "No uplink port found for OLT device {}";
alshabibe0559672016-02-21 14:49:51 -0800125
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800126 public static final int HASH_WEIGHT = 10;
127
alshabibf0e7e702015-05-30 18:22:36 -0700128 private final Logger log = getLogger(getClass());
129
Thomas Lee Sd7735f92020-02-20 19:21:47 +0530130 private static final String NNI = "nni-";
131
Carmelo Casconeca931162019-07-15 18:22:24 -0700132 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabibf0e7e702015-05-30 18:22:36 -0700133 protected FlowObjectiveService flowObjectiveService;
134
Carmelo Casconeca931162019-07-15 18:22:24 -0700135 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabibf0e7e702015-05-30 18:22:36 -0700136 protected DeviceService deviceService;
137
Andrea Campanella438e1ad2021-03-26 11:41:16 +0100138
Carmelo Casconeca931162019-07-15 18:22:24 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabibf0e7e702015-05-30 18:22:36 -0700140 protected CoreService coreService;
141
Andrea Campanella438e1ad2021-03-26 11:41:16 +0100142 //Dependency on driver service is to ensure correct startup order
143 @Reference(cardinality = ReferenceCardinality.MANDATORY)
144 protected DriverService driverService;
145
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000146 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
147 bind = "bindSadisService",
148 unbind = "unbindSadisService",
149 policy = ReferencePolicy.DYNAMIC)
150 protected volatile SadisService sadisService;
Gamze Abaka641fc072018-09-04 09:16:27 +0000151
Carmelo Casconeca931162019-07-15 18:22:24 -0700152 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000153 protected AccessDeviceFlowService oltFlowService;
alshabibe0559672016-02-21 14:49:51 -0800154
Carmelo Casconeca931162019-07-15 18:22:24 -0700155 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000156 protected AccessDeviceMeterService oltMeterService;
Gamze Abakaad329652018-12-20 10:12:21 +0000157
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800158 @Reference(cardinality = ReferenceCardinality.MANDATORY)
159 protected StorageService storageService;
160
161 @Reference(cardinality = ReferenceCardinality.MANDATORY)
162 protected ClusterService clusterService;
163
Hardik Windlassa58fbee2020-03-12 18:33:55 +0530164 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellaaf39b4c2020-05-13 14:07:44 +0200165 protected MastershipService mastershipService;
166
167 @Reference(cardinality = ReferenceCardinality.MANDATORY)
168 protected LeadershipService leadershipService;
169
170 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hardik Windlassa58fbee2020-03-12 18:33:55 +0530171 protected FlowRuleService flowRuleService;
172
Gamze Abaka1f62dd92020-05-07 08:58:13 +0000173 @Reference(cardinality = ReferenceCardinality.MANDATORY)
174 protected ComponentConfigService componentConfigService;
175
Tunahan Sezena07fe962021-02-24 08:24:24 +0000176 @Reference(cardinality = ReferenceCardinality.MANDATORY)
177 protected HostService hostService;
178
Carmelo Casconeca931162019-07-15 18:22:24 -0700179 /**
Carmelo Cascone95ff5122019-11-14 14:19:13 -0800180 * Default bandwidth profile id that is used for authentication trap flows.
Carmelo Casconeca931162019-07-15 18:22:24 -0700181 **/
182 protected String defaultBpId = DEFAULT_BP_ID_DEFAULT;
Gamze Abakaad329652018-12-20 10:12:21 +0000183
Carmelo Casconeca931162019-07-15 18:22:24 -0700184 /**
Gamze Abaka51a34e82020-05-08 13:03:14 +0000185 * Default multicast service name.
Carmelo Casconeca931162019-07-15 18:22:24 -0700186 **/
Gamze Abaka51a34e82020-05-08 13:03:14 +0000187 protected String multicastServiceName = DEFAULT_MCAST_SERVICE_NAME_DEFAULT;
Gamze Abaka33feef52019-02-27 08:16:47 +0000188
Saurav Das2d3777a2020-08-07 18:48:51 -0700189 /**
190 * Default amounts of eapol retry.
191 **/
192 protected int eapolDeleteRetryMaxAttempts = EAPOL_DELETE_RETRY_MAX_ATTEMPS_DEFAULT;
193
Matteo Scandoloa4aaa972020-10-23 15:24:38 -0700194 /**
195 * Delay between EAPOL removal and data plane flows provisioning.
196 */
197 protected int provisionDelay = PROVISION_DELAY_DEFAULT;
198
alshabibf0e7e702015-05-30 18:22:36 -0700199 private final DeviceListener deviceListener = new InternalDeviceListener();
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800200 private final ClusterEventListener clusterListener = new InternalClusterListener();
Tunahan Sezena07fe962021-02-24 08:24:24 +0000201 private final HostListener hostListener = new InternalHostListener();
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800202
203 private ConsistentHasher hasher;
alshabibf0e7e702015-05-30 18:22:36 -0700204
Gamze Abaka641fc072018-09-04 09:16:27 +0000205 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
206 private BaseInformationService<BandwidthProfileInformation> bpService;
alshabibf0e7e702015-05-30 18:22:36 -0700207
Gamze Abaka641fc072018-09-04 09:16:27 +0000208 private ExecutorService oltInstallers = Executors.newFixedThreadPool(4,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000209 groupedThreads("onos/olt-service",
210 "olt-installer-%d"));
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100211
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700212 protected ExecutorService eventExecutor;
Tunahan Sezena07fe962021-02-24 08:24:24 +0000213 protected ExecutorService hostEventExecutor;
Saurav Das2d3777a2020-08-07 18:48:51 -0700214 protected ExecutorService retryExecutor;
Matteo Scandoloa4aaa972020-10-23 15:24:38 -0700215 protected ScheduledExecutorService provisionExecutor;
Matteo Scandolo632f0fc2018-09-07 12:21:45 -0700216
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800217 private ConsistentMultimap<ConnectPoint, UniTagInformation> programmedSubs;
Saurav Das2d3777a2020-08-07 18:48:51 -0700218 private ConsistentMultimap<ConnectPoint, UniTagInformation> failedSubs;
Saurav Dasa9d5f442019-03-06 19:32:48 -0800219
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000220 protected Map<DeviceId, BlockingQueue<SubscriberFlowInfo>> pendingSubscribersForDevice;
Tunahan Sezena07fe962021-02-24 08:24:24 +0000221 private ConsistentMultimap<ConnectPoint, SubscriberFlowInfo> waitingMacSubscribers;
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700222
alshabibf0e7e702015-05-30 18:22:36 -0700223 @Activate
alshabibe0559672016-02-21 14:49:51 -0800224 public void activate(ComponentContext context) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000225 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/olt",
226 "events-%d", log));
Tunahan Sezena07fe962021-02-24 08:24:24 +0000227 hostEventExecutor = Executors.newFixedThreadPool(8, groupedThreads("onos/olt", "mac-events-%d", log));
Saurav Das2d3777a2020-08-07 18:48:51 -0700228 retryExecutor = Executors.newCachedThreadPool();
Matteo Scandoloa4aaa972020-10-23 15:24:38 -0700229 provisionExecutor = Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/olt",
230 "provision-%d", log));
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700231
alshabibe0559672016-02-21 14:49:51 -0800232 modified(context);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000233 ApplicationId appId = coreService.registerApplication(APP_NAME);
Gamze Abaka1f62dd92020-05-07 08:58:13 +0000234 componentConfigService.registerProperties(getClass());
Saurav Das62ad75e2019-03-05 12:22:22 -0800235
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800236 KryoNamespace serializer = KryoNamespace.newBuilder()
237 .register(KryoNamespaces.API)
238 .register(UniTagInformation.class)
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000239 .register(SubscriberFlowInfo.class)
240 .register(LinkedBlockingQueue.class)
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800241 .build();
242
243 programmedSubs = storageService.<ConnectPoint, UniTagInformation>consistentMultimapBuilder()
244 .withName("volt-programmed-subs")
245 .withSerializer(Serializer.using(serializer))
246 .withApplicationId(appId)
247 .build();
alshabibc4dfe852015-06-05 13:35:13 -0700248
Saurav Das2d3777a2020-08-07 18:48:51 -0700249 failedSubs = storageService.<ConnectPoint, UniTagInformation>consistentMultimapBuilder()
250 .withName("volt-failed-subs")
251 .withSerializer(Serializer.using(serializer))
252 .withApplicationId(appId)
253 .build();
254
Tunahan Sezena07fe962021-02-24 08:24:24 +0000255 KryoNamespace macSerializer = KryoNamespace.newBuilder()
256 .register(KryoNamespaces.API)
257 .register(SubscriberFlowInfo.class)
258 .register(UniTagInformation.class)
259 .build();
260
261 waitingMacSubscribers = storageService.<ConnectPoint, SubscriberFlowInfo>consistentMultimapBuilder()
262 .withName("volt-waiting-mac-subs")
263 .withSerializer(Serializer.using(macSerializer))
264 .withApplicationId(appId)
265 .build();
266
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000267 pendingSubscribersForDevice = storageService.<DeviceId, BlockingQueue<SubscriberFlowInfo>>consistentMapBuilder()
268 .withName("volt-pending-subs")
269 .withSerializer(Serializer.using(serializer))
270 .withApplicationId(appId)
271 .build().asJavaMap();
alshabib8e4fd2f2016-01-12 15:55:53 -0800272 eventDispatcher.addSink(AccessDeviceEvent.class, listenerRegistry);
273
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000274 if (sadisService != null) {
275 subsService = sadisService.getSubscriberInfoService();
276 bpService = sadisService.getBandwidthProfileService();
277 } else {
278 log.warn(SADIS_NOT_RUNNING);
279 }
Gamze Abaka641fc072018-09-04 09:16:27 +0000280
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800281 List<NodeId> readyNodes = clusterService.getNodes().stream()
282 .filter(c -> clusterService.getState(c.id()) == ControllerNode.State.READY)
283 .map(ControllerNode::id)
284 .collect(toList());
285 hasher = new ConsistentHasher(readyNodes, HASH_WEIGHT);
286 clusterService.addListener(clusterListener);
287
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100288 // look for all provisioned devices in Sadis and create EAPOL flows for the
289 // UNI ports
290 Iterable<Device> devices = deviceService.getDevices();
291 for (Device d : devices) {
Andrea Campanellaaf39b4c2020-05-13 14:07:44 +0200292 if (isLocalLeader(d.id())) {
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800293 checkAndCreateDeviceFlows(d);
294 }
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100295 }
alshabib4ceaed32016-03-03 18:00:58 -0800296
alshabibba357492016-01-27 13:49:46 -0800297 deviceService.addListener(deviceListener);
Tunahan Sezena07fe962021-02-24 08:24:24 +0000298 hostService.addListener(hostListener);
alshabibf0e7e702015-05-30 18:22:36 -0700299 log.info("Started with Application ID {}", appId.id());
300 }
301
302 @Deactivate
303 public void deactivate() {
Gamze Abaka1f62dd92020-05-07 08:58:13 +0000304 componentConfigService.unregisterProperties(getClass(), false);
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800305 clusterService.removeListener(clusterListener);
alshabib62e9ce72016-02-11 17:31:36 -0800306 deviceService.removeListener(deviceListener);
Tunahan Sezena07fe962021-02-24 08:24:24 +0000307 hostService.removeListener(hostListener);
Jonathan Hart5f1c8142018-07-24 17:31:59 -0700308 eventDispatcher.removeSink(AccessDeviceEvent.class);
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700309 eventExecutor.shutdown();
Tunahan Sezena07fe962021-02-24 08:24:24 +0000310 hostEventExecutor.shutdown();
Saurav Das2d3777a2020-08-07 18:48:51 -0700311 retryExecutor.shutdown();
Matteo Scandoloa4aaa972020-10-23 15:24:38 -0700312 provisionExecutor.shutdown();
alshabibf0e7e702015-05-30 18:22:36 -0700313 log.info("Stopped");
314 }
315
alshabibe0559672016-02-21 14:49:51 -0800316 @Modified
317 public void modified(ComponentContext context) {
318 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
319
320 try {
Andrea Campanella971d5b92020-05-07 11:20:43 +0200321 String bpId = get(properties, DEFAULT_BP_ID);
322 defaultBpId = isNullOrEmpty(bpId) ? defaultBpId : bpId;
Gamze Abakaad329652018-12-20 10:12:21 +0000323
Andrea Campanella971d5b92020-05-07 11:20:43 +0200324 String mcastSN = get(properties, DEFAULT_MCAST_SERVICE_NAME);
325 multicastServiceName = isNullOrEmpty(mcastSN) ? multicastServiceName : mcastSN;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000326
Saurav Das2d3777a2020-08-07 18:48:51 -0700327 String eapolDeleteRetryNew = get(properties, EAPOL_DELETE_RETRY_MAX_ATTEMPS);
328 eapolDeleteRetryMaxAttempts = isNullOrEmpty(eapolDeleteRetryNew) ? EAPOL_DELETE_RETRY_MAX_ATTEMPS_DEFAULT :
329 Integer.parseInt(eapolDeleteRetryNew.trim());
330
331 log.debug("OLT properties: DefaultBpId: {}, MulticastServiceName: {}, EapolDeleteRetryMaxAttempts: {}",
332 defaultBpId, multicastServiceName, eapolDeleteRetryMaxAttempts);
Gamze Abaka33feef52019-02-27 08:16:47 +0000333
alshabibe0559672016-02-21 14:49:51 -0800334 } catch (Exception e) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000335 log.error("Error while modifying the properties", e);
Andrea Campanella971d5b92020-05-07 11:20:43 +0200336 defaultBpId = DEFAULT_BP_ID_DEFAULT;
337 multicastServiceName = DEFAULT_MCAST_SERVICE_NAME_DEFAULT;
alshabibe0559672016-02-21 14:49:51 -0800338 }
339 }
340
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000341 protected void bindSadisService(SadisService service) {
342 sadisService = service;
343 bpService = sadisService.getBandwidthProfileService();
344 subsService = sadisService.getSubscriberInfoService();
345 log.info("Sadis-service binds to onos.");
346 }
347
348 protected void unbindSadisService(SadisService service) {
349 sadisService = null;
350 bpService = null;
351 subsService = null;
352 log.info("Sadis-service unbinds from onos.");
353 }
354
alshabib32232c82016-02-25 17:57:24 -0500355 @Override
Gamze Abaka838d8142019-02-21 07:06:55 +0000356 public boolean provisionSubscriber(ConnectPoint connectPoint) {
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200357 log.info("Call to provision subscriber at {}", connectPoint);
Gamze Abaka838d8142019-02-21 07:06:55 +0000358 DeviceId deviceId = connectPoint.deviceId();
359 PortNumber subscriberPortNo = connectPoint.port();
Gamze Abaka838d8142019-02-21 07:06:55 +0000360 checkNotNull(deviceService.getPort(deviceId, subscriberPortNo),
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000361 "Invalid connect point:" + connectPoint);
Hardik Windlass395ff372019-06-13 05:16:00 +0000362
Saurav Das026650f2020-09-21 18:56:35 -0700363 if (isSubscriberInstalled(connectPoint)) {
364 log.warn("Subscriber at {} already provisioned or in the process .."
365 + " not taking any more action", connectPoint);
366 return true;
367 }
368
369 // Find the subscriber config at this connect point
Gamze Abaka838d8142019-02-21 07:06:55 +0000370 SubscriberAndDeviceInformation sub = getSubscriber(connectPoint);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100371 if (sub == null) {
Gamze Abaka838d8142019-02-21 07:06:55 +0000372 log.warn("No subscriber found for {}", connectPoint);
Amit Ghosh31939522018-08-16 13:28:21 +0100373 return false;
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100374 }
Jonathan Harte533a422015-10-20 17:31:24 -0700375
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100376 // Get the uplink port
Gamze Abaka838d8142019-02-21 07:06:55 +0000377 Port uplinkPort = getUplinkPort(deviceService.getDevice(deviceId));
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100378 if (uplinkPort == null) {
Gamze Abaka838d8142019-02-21 07:06:55 +0000379 log.warn(NO_UPLINK_PORT, deviceId);
Amit Ghosh31939522018-08-16 13:28:21 +0100380 return false;
Jonathan Harte533a422015-10-20 17:31:24 -0700381 }
382
Saurav Das2d3777a2020-08-07 18:48:51 -0700383 // delete Eapol authentication flow with default bandwidth
384 // wait until Eapol rule with defaultBpId is removed to install subscriber-based rules
385 // retry deletion if it fails/times-out
386 retryExecutor.execute(new DeleteEapolInstallSub(connectPoint,
387 uplinkPort, sub, 1));
Amit Ghosh31939522018-08-16 13:28:21 +0100388 return true;
alshabibb7a9e172016-01-13 11:23:53 -0800389 }
390
Saurav Das026650f2020-09-21 18:56:35 -0700391 // returns true if subscriber is programmed or in the process of being programmed
392 private boolean isSubscriberInstalled(ConnectPoint connectPoint) {
393 Collection<? extends UniTagInformation> uniTagInformationSet =
394 programmedSubs.get(connectPoint).value();
395 if (!uniTagInformationSet.isEmpty()) {
396 return true;
397 }
Andrea Campanella7a1d7e72020-11-05 10:40:10 +0100398 //Check if the subscriber is already getting provisioned
399 // so we do not provision twice
400 AtomicBoolean isPending = new AtomicBoolean(false);
401 pendingSubscribersForDevice.computeIfPresent(connectPoint.deviceId(), (id, infos) -> {
402 for (SubscriberFlowInfo fi : infos) {
403 if (fi.getUniPort().equals(connectPoint.port())) {
404 isPending.set(true);
405 break;
406 }
Saurav Das026650f2020-09-21 18:56:35 -0700407 }
Andrea Campanella7a1d7e72020-11-05 10:40:10 +0100408 return infos;
409 });
Saurav Das026650f2020-09-21 18:56:35 -0700410
Andrea Campanella7a1d7e72020-11-05 10:40:10 +0100411 return isPending.get();
Saurav Das026650f2020-09-21 18:56:35 -0700412 }
413
Saurav Das2d3777a2020-08-07 18:48:51 -0700414 private class DeleteEapolInstallSub implements Runnable {
415 ConnectPoint cp;
416 Port uplinkPort;
417 SubscriberAndDeviceInformation sub;
418 private int attemptNumber;
419
420 DeleteEapolInstallSub(ConnectPoint cp, Port uplinkPort,
421 SubscriberAndDeviceInformation sub,
422 int attemptNumber) {
423 this.cp = cp;
424 this.uplinkPort = uplinkPort;
425 this.sub = sub;
426 this.attemptNumber = attemptNumber;
427 }
428
429 @Override
430 public void run() {
Tunahan Sezena07fe962021-02-24 08:24:24 +0000431 CompletableFuture<ObjectiveError> filterFuture = new CompletableFuture<>();
Saurav Das2d3777a2020-08-07 18:48:51 -0700432 oltFlowService.processEapolFilteringObjectives(cp.deviceId(), cp.port(),
433 defaultBpId, filterFuture,
434 VlanId.vlanId(EAPOL_DEFAULT_VLAN),
435 false);
436 filterFuture.thenAcceptAsync(filterStatus -> {
437 if (filterStatus == null) {
438 log.info("Default eapol flow deleted in attempt {} of {}"
439 + "... provisioning subscriber flows {}",
440 attemptNumber, eapolDeleteRetryMaxAttempts, cp);
Matteo Scandoloa4aaa972020-10-23 15:24:38 -0700441
442 // FIXME this is needed to prevent that default EAPOL flow removal and
443 // data plane flows install are received by the device at the same time
444 provisionExecutor.schedule(
445 () -> provisionUniTagList(cp, uplinkPort.number(), sub),
446 provisionDelay, TimeUnit.MILLISECONDS);
Saurav Das2d3777a2020-08-07 18:48:51 -0700447 } else {
448 if (attemptNumber <= eapolDeleteRetryMaxAttempts) {
449 log.warn("The filtering future failed {} for subscriber {}"
450 + "... retrying {} of {} attempts",
451 filterStatus, cp, attemptNumber, eapolDeleteRetryMaxAttempts);
452 retryExecutor.execute(
453 new DeleteEapolInstallSub(cp, uplinkPort, sub,
454 attemptNumber + 1));
455 } else {
456 log.error("The filtering future failed {} for subscriber {}"
457 + "after {} attempts. Subscriber provisioning failed",
458 filterStatus, cp, eapolDeleteRetryMaxAttempts);
459 sub.uniTagList().forEach(ut -> failedSubs.put(cp, ut));
460 }
461 }
462 });
463 }
464
465 }
466
alshabibb7a9e172016-01-13 11:23:53 -0800467 @Override
Gamze Abaka838d8142019-02-21 07:06:55 +0000468 public boolean removeSubscriber(ConnectPoint connectPoint) {
Saurav Daseae48de2019-06-19 13:26:15 -0700469 log.info("Call to un-provision subscriber at {}", connectPoint);
Gamze Abaka838d8142019-02-21 07:06:55 +0000470
Saurav Daseae48de2019-06-19 13:26:15 -0700471 // Get the subscriber connected to this port from the local cache
472 // If we don't know about the subscriber there's no need to remove it
Gamze Abaka838d8142019-02-21 07:06:55 +0000473 DeviceId deviceId = connectPoint.deviceId();
474 PortNumber subscriberPortNo = connectPoint.port();
475
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800476 Collection<? extends UniTagInformation> uniTagInformationSet = programmedSubs.get(connectPoint).value();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000477 if (uniTagInformationSet == null || uniTagInformationSet.isEmpty()) {
Gamze Abaka838d8142019-02-21 07:06:55 +0000478 log.warn("Subscriber on connectionPoint {} was not previously programmed, " +
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000479 "no need to remove it", connectPoint);
Matteo Scandolo962a6ad2018-12-11 15:39:42 -0800480 return true;
alshabibbf23a1f2016-01-14 17:27:11 -0800481 }
482
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100483 // Get the uplink port
Gamze Abaka838d8142019-02-21 07:06:55 +0000484 Port uplinkPort = getUplinkPort(deviceService.getDevice(deviceId));
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100485 if (uplinkPort == null) {
Gamze Abaka838d8142019-02-21 07:06:55 +0000486 log.warn(NO_UPLINK_PORT, deviceId);
Amit Ghosh31939522018-08-16 13:28:21 +0100487 return false;
alshabib4ceaed32016-03-03 18:00:58 -0800488 }
489
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000490 for (UniTagInformation uniTag : uniTagInformationSet) {
Amit Ghosh95e2f652017-08-23 12:49:46 +0100491
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000492 if (multicastServiceName.equals(uniTag.getServiceName())) {
493 continue;
494 }
Gamze Abaka838d8142019-02-21 07:06:55 +0000495
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000496 unprovisionVlans(deviceId, uplinkPort.number(), subscriberPortNo, uniTag);
alshabibbf23a1f2016-01-14 17:27:11 -0800497
Saurav Das9da7d522020-03-23 19:14:35 -0700498 // remove eapol with subscriber bandwidth profile
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000499 oltFlowService.processEapolFilteringObjectives(deviceId, subscriberPortNo,
500 uniTag.getUpstreamBandwidthProfile(),
501 null, uniTag.getPonCTag(), false);
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100502
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000503 Port port = deviceService.getPort(deviceId, subscriberPortNo);
504 if (port != null && port.isEnabled()) {
Saurav Das9da7d522020-03-23 19:14:35 -0700505 // reinstall eapol with default bandwidth profile
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000506 oltFlowService.processEapolFilteringObjectives(deviceId, subscriberPortNo, defaultBpId,
507 null, VlanId.vlanId(EAPOL_DEFAULT_VLAN), true);
508 } else {
509 log.debug("Port {} is no longer enabled or it's unavailable. Not "
510 + "reprogramming default eapol flow", connectPoint);
511 }
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100512 }
Amit Ghosh31939522018-08-16 13:28:21 +0100513 return true;
alshabibbf23a1f2016-01-14 17:27:11 -0800514 }
515
Gamze Abakaf59c0912019-04-19 08:24:28 +0000516
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000517 @Override
518 public boolean provisionSubscriber(AccessSubscriberId subscriberId, Optional<VlanId> sTag,
519 Optional<VlanId> cTag, Optional<Integer> tpId) {
520
521 log.info("Provisioning subscriber using subscriberId {}, sTag {}, cTag {}, tpId {}" +
522 "", subscriberId, sTag, cTag, tpId);
Gamze Abakaf59c0912019-04-19 08:24:28 +0000523
Amit Ghosh31939522018-08-16 13:28:21 +0100524 // Check if we can find the connect point to which this subscriber is connected
525 ConnectPoint subsPort = findSubscriberConnectPoint(subscriberId.toString());
526 if (subsPort == null) {
527 log.warn("ConnectPoint for {} not found", subscriberId);
528 return false;
529 }
530
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100531 if (!sTag.isPresent() && !cTag.isPresent()) {
532 return provisionSubscriber(subsPort);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000533 } else if (sTag.isPresent() && cTag.isPresent() && tpId.isPresent()) {
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100534 Port uplinkPort = getUplinkPort(deviceService.getDevice(subsPort.deviceId()));
535 if (uplinkPort == null) {
Gamze Abaka838d8142019-02-21 07:06:55 +0000536 log.warn(NO_UPLINK_PORT, subsPort.deviceId());
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100537 return false;
538 }
539
Gamze Abakaf59c0912019-04-19 08:24:28 +0000540 //delete Eapol authentication flow with default bandwidth
541 //wait until Eapol rule with defaultBpId is removed to install subscriber-based rules
Gamze Abakaf59c0912019-04-19 08:24:28 +0000542 //install subscriber flows
Tunahan Sezena07fe962021-02-24 08:24:24 +0000543 CompletableFuture<ObjectiveError> filterFuture = new CompletableFuture<>();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000544 oltFlowService.processEapolFilteringObjectives(subsPort.deviceId(), subsPort.port(), defaultBpId,
545 filterFuture, VlanId.vlanId(EAPOL_DEFAULT_VLAN), false);
Gamze Abakaf59c0912019-04-19 08:24:28 +0000546 filterFuture.thenAcceptAsync(filterStatus -> {
547 if (filterStatus == null) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000548 provisionUniTagInformation(subsPort.deviceId(), uplinkPort.number(), subsPort.port(),
549 cTag.get(), sTag.get(), tpId.get());
Gamze Abakaf59c0912019-04-19 08:24:28 +0000550 }
551 });
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100552 return true;
553 } else {
554 log.warn("Provisioning failed for subscriber: {}", subscriberId);
555 return false;
556 }
Amit Ghosh31939522018-08-16 13:28:21 +0100557 }
Amit Ghosh95e2f652017-08-23 12:49:46 +0100558
alshabibe0559672016-02-21 14:49:51 -0800559 @Override
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000560 public boolean removeSubscriber(AccessSubscriberId subscriberId, Optional<VlanId> sTag,
561 Optional<VlanId> cTag, Optional<Integer> tpId) {
Amit Ghosh31939522018-08-16 13:28:21 +0100562 // Check if we can find the connect point to which this subscriber is connected
563 ConnectPoint subsPort = findSubscriberConnectPoint(subscriberId.toString());
564 if (subsPort == null) {
565 log.warn("ConnectPoint for {} not found", subscriberId);
566 return false;
567 }
568
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100569 if (!sTag.isPresent() && !cTag.isPresent()) {
570 return removeSubscriber(subsPort);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000571 } else if (sTag.isPresent() && cTag.isPresent() && tpId.isPresent()) {
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100572 // Get the uplink port
573 Port uplinkPort = getUplinkPort(deviceService.getDevice(subsPort.deviceId()));
574 if (uplinkPort == null) {
Gamze Abaka838d8142019-02-21 07:06:55 +0000575 log.warn(NO_UPLINK_PORT, subsPort.deviceId());
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100576 return false;
577 }
578
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000579 Optional<UniTagInformation> tagInfo = getUniTagInformation(subsPort, cTag.get(), sTag.get(), tpId.get());
580 if (!tagInfo.isPresent()) {
581 log.warn("UniTagInformation does not exist for Device/Port {}, cTag {}, sTag {}, tpId {}",
582 subsPort, cTag, sTag, tpId);
583 return false;
584 }
Gamze Abakaf59c0912019-04-19 08:24:28 +0000585
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000586 unprovisionVlans(subsPort.deviceId(), uplinkPort.number(), subsPort.port(), tagInfo.get());
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100587 return true;
588 } else {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000589 log.warn("Removing subscriber is not possible - please check the provided information" +
590 "for the subscriber: {}", subscriberId);
Amit Ghoshe1d3f092018-10-09 19:44:33 +0100591 return false;
592 }
Amit Ghosh31939522018-08-16 13:28:21 +0100593 }
594
595 @Override
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000596 public ImmutableMap<ConnectPoint, Set<UniTagInformation>> getProgSubs() {
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800597 return programmedSubs.stream()
598 .collect(collectingAndThen(
599 groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, toSet())),
600 ImmutableMap::copyOf));
Saurav Das82b8e6d2018-10-04 15:25:12 -0700601 }
602
603 @Override
Saurav Das2d3777a2020-08-07 18:48:51 -0700604 public ImmutableMap<ConnectPoint, Set<UniTagInformation>> getFailedSubs() {
605 return failedSubs.stream()
606 .collect(collectingAndThen(
607 groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, toSet())),
608 ImmutableMap::copyOf));
609 }
610
611 @Override
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100612 public List<DeviceId> fetchOlts() {
613 // look through all the devices and find the ones that are OLTs as per Sadis
614 List<DeviceId> olts = new ArrayList<>();
615 Iterable<Device> devices = deviceService.getDevices();
616 for (Device d : devices) {
Saurav Das82b8e6d2018-10-04 15:25:12 -0700617 if (getOltInfo(d) != null) {
618 // So this is indeed an OLT device
Amit Ghosh1ed9aef2018-07-17 17:08:16 +0100619 olts.add(d.id());
620 }
621 }
622 return olts;
alshabibe0559672016-02-21 14:49:51 -0800623 }
624
Amit Ghosh31939522018-08-16 13:28:21 +0100625 /**
626 * Finds the connect point to which a subscriber is connected.
627 *
628 * @param id The id of the subscriber, this is the same ID as in Sadis
629 * @return Subscribers ConnectPoint if found else null
630 */
631 private ConnectPoint findSubscriberConnectPoint(String id) {
632
633 Iterable<Device> devices = deviceService.getDevices();
634 for (Device d : devices) {
635 for (Port p : deviceService.getPorts(d.id())) {
636 log.trace("Comparing {} with {}", p.annotations().value(AnnotationKeys.PORT_NAME), id);
637 if (p.annotations().value(AnnotationKeys.PORT_NAME).equals(id)) {
638 log.debug("Found on device {} port {}", d.id(), p.number());
639 return new ConnectPoint(d.id(), p.number());
640 }
641 }
642 }
643 return null;
644 }
645
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000646 /**
647 * Gets the context of the bandwidth profile information for the given parameter.
648 *
649 * @param bandwidthProfile the bandwidth profile id
650 * @return the context of the bandwidth profile information
651 */
Gamze Abaka641fc072018-09-04 09:16:27 +0000652 private BandwidthProfileInformation getBandwidthProfileInformation(String bandwidthProfile) {
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000653 if (bpService == null) {
654 log.warn(SADIS_NOT_RUNNING);
655 return null;
656 }
Gamze Abaka641fc072018-09-04 09:16:27 +0000657 if (bandwidthProfile == null) {
658 return null;
659 }
660 return bpService.get(bandwidthProfile);
661 }
662
Gamze Abaka838d8142019-02-21 07:06:55 +0000663 /**
Gamze Abaka33feef52019-02-27 08:16:47 +0000664 * Removes subscriber vlan flows.
Gamze Abaka838d8142019-02-21 07:06:55 +0000665 *
666 * @param deviceId the device identifier
667 * @param uplink uplink port of the OLT
668 * @param subscriberPort uni port
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000669 * @param uniTag uni tag information
Gamze Abaka838d8142019-02-21 07:06:55 +0000670 */
Gamze Abaka33feef52019-02-27 08:16:47 +0000671 private void unprovisionVlans(DeviceId deviceId, PortNumber uplink,
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000672 PortNumber subscriberPort, UniTagInformation uniTag) {
673
674 log.info("Unprovisioning vlans for {} at {}/{}", uniTag, deviceId, subscriberPort);
alshabibbf23a1f2016-01-14 17:27:11 -0800675
Tunahan Sezena07fe962021-02-24 08:24:24 +0000676 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture<>();
677 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture<>();
alshabibbf23a1f2016-01-14 17:27:11 -0800678
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000679 VlanId deviceVlan = uniTag.getPonSTag();
680 VlanId subscriberVlan = uniTag.getPonCTag();
Gamze Abaka641fc072018-09-04 09:16:27 +0000681
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000682 MeterId upstreamMeterId = oltMeterService
683 .getMeterIdFromBpMapping(deviceId, uniTag.getUpstreamBandwidthProfile());
684 MeterId downstreamMeterId = oltMeterService
685 .getMeterIdFromBpMapping(deviceId, uniTag.getDownstreamBandwidthProfile());
Gamze Abaka641fc072018-09-04 09:16:27 +0000686
Tunahan Sezena07fe962021-02-24 08:24:24 +0000687 Optional<SubscriberFlowInfo> waitingMacSubFlowInfo =
688 getAndRemoveWaitingMacSubFlowInfoForCTag(new ConnectPoint(deviceId, subscriberPort), subscriberVlan);
689 if (waitingMacSubFlowInfo.isPresent()) {
690 // only dhcp objectives applied previously, so only dhcp uninstallation objective will be processed
691 log.debug("Waiting MAC service removed and dhcp uninstallation objective will be processed. " +
692 "waitingMacSubFlowInfo:{}", waitingMacSubFlowInfo.get());
693 CompletableFuture<ObjectiveError> dhcpFuture = new CompletableFuture<>();
694 oltFlowService.processDhcpFilteringObjectives(deviceId, subscriberPort,
695 upstreamMeterId, uniTag, false, true, Optional.of(dhcpFuture));
696 dhcpFuture.thenAcceptAsync(dhcpStatus -> {
697 AccessDeviceEvent.Type type;
698 if (dhcpStatus == null) {
699 type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_UNREGISTERED;
700 log.debug("Dhcp uninstallation objective was processed successfully for cTag {}, sTag {}, " +
701 "tpId {} and Device/Port:{}", uniTag.getPonCTag(), uniTag.getPonSTag(),
702 uniTag.getTechnologyProfileId(), subscriberPort);
703 updateProgrammedSubscriber(new ConnectPoint(deviceId, subscriberPort), uniTag, false);
704 } else {
705 type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_UNREGISTRATION_FAILED;
706 log.error("Dhcp uninstallation objective was failed for cTag {}, sTag {}, " +
707 "tpId {} and Device/Port:{} :{}", uniTag.getPonCTag(), uniTag.getPonSTag(),
708 uniTag.getTechnologyProfileId(), subscriberPort, dhcpStatus);
709 }
710 post(new AccessDeviceEvent(type, deviceId, deviceService.getPort(deviceId, subscriberPort),
711 deviceVlan, subscriberVlan, uniTag.getTechnologyProfileId()));
712 });
713 return;
714 } else {
715 log.debug("There is no waiting MAC service for dev/port: {}/{} and subscriberVlan: {}",
716 deviceId, subscriberPort, subscriberVlan);
717 }
718
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000719 ForwardingObjective.Builder upFwd =
720 oltFlowService.createUpBuilder(uplink, subscriberPort, upstreamMeterId, uniTag);
Tunahan Sezena07fe962021-02-24 08:24:24 +0000721
722 Optional<MacAddress> macAddress = getMacAddress(deviceId, subscriberPort, uniTag);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000723 ForwardingObjective.Builder downFwd =
Tunahan Sezena07fe962021-02-24 08:24:24 +0000724 oltFlowService.createDownBuilder(uplink, subscriberPort, downstreamMeterId, uniTag, macAddress);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000725
Andrea Campanella7c49b792020-05-11 11:36:53 +0200726 oltFlowService.processIgmpFilteringObjectives(deviceId, subscriberPort,
727 upstreamMeterId, uniTag, false, true);
728 oltFlowService.processDhcpFilteringObjectives(deviceId, subscriberPort,
Tunahan Sezena07fe962021-02-24 08:24:24 +0000729 upstreamMeterId, uniTag, false, true, Optional.empty());
Gustavo Silva5c492dd2021-02-12 10:21:11 -0300730 oltFlowService.processPPPoEDFilteringObjectives(deviceId, subscriberPort,
731 upstreamMeterId, uniTag, false, true);
alshabibbf23a1f2016-01-14 17:27:11 -0800732
alshabib4ceaed32016-03-03 18:00:58 -0800733 flowObjectiveService.forward(deviceId, upFwd.remove(new ObjectiveContext() {
734 @Override
735 public void onSuccess(Objective objective) {
736 upFuture.complete(null);
737 }
alshabibbf23a1f2016-01-14 17:27:11 -0800738
alshabib4ceaed32016-03-03 18:00:58 -0800739 @Override
740 public void onError(Objective objective, ObjectiveError error) {
741 upFuture.complete(error);
742 }
743 }));
744
745 flowObjectiveService.forward(deviceId, downFwd.remove(new ObjectiveContext() {
746 @Override
747 public void onSuccess(Objective objective) {
748 downFuture.complete(null);
749 }
750
751 @Override
752 public void onError(Objective objective, ObjectiveError error) {
753 downFuture.complete(error);
754 }
755 }));
alshabibbf23a1f2016-01-14 17:27:11 -0800756
757 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000758 AccessDeviceEvent.Type type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_UNREGISTERED;
alshabibbf23a1f2016-01-14 17:27:11 -0800759 if (upStatus == null && downStatus == null) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000760 log.debug("Uni tag information is unregistered successfully for cTag {}, sTag {}, tpId {}, and" +
761 "Device/Port{}", uniTag.getPonCTag(), uniTag.getPonSTag(),
762 uniTag.getTechnologyProfileId(), subscriberPort);
763 updateProgrammedSubscriber(new ConnectPoint(deviceId, subscriberPort), uniTag, false);
alshabibbf23a1f2016-01-14 17:27:11 -0800764 } else if (downStatus != null) {
765 log.error("Subscriber with vlan {} on device {} " +
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000766 "on port {} failed downstream uninstallation: {}",
767 subscriberVlan, deviceId, subscriberPort, downStatus);
768 type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_UNREGISTRATION_FAILED;
alshabibbf23a1f2016-01-14 17:27:11 -0800769 } else if (upStatus != null) {
770 log.error("Subscriber with vlan {} on device {} " +
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000771 "on port {} failed upstream uninstallation: {}",
772 subscriberVlan, deviceId, subscriberPort, upStatus);
773 type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_UNREGISTRATION_FAILED;
alshabibbf23a1f2016-01-14 17:27:11 -0800774 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000775 Port port = deviceService.getPort(deviceId, subscriberPort);
776 post(new AccessDeviceEvent(type, deviceId, port, deviceVlan, subscriberVlan,
777 uniTag.getTechnologyProfileId()));
alshabibbf23a1f2016-01-14 17:27:11 -0800778 }, oltInstallers);
Jonathan Harte533a422015-10-20 17:31:24 -0700779 }
780
Tunahan Sezena07fe962021-02-24 08:24:24 +0000781 private Optional<SubscriberFlowInfo> getAndRemoveWaitingMacSubFlowInfoForCTag(ConnectPoint cp, VlanId cTag) {
782 SubscriberFlowInfo returnSubFlowInfo = null;
783 Collection<? extends SubscriberFlowInfo> subFlowInfoSet = waitingMacSubscribers.get(cp).value();
784 for (SubscriberFlowInfo subFlowInfo : subFlowInfoSet) {
785 if (subFlowInfo.getTagInfo().getPonCTag().equals(cTag)) {
786 returnSubFlowInfo = subFlowInfo;
787 break;
788 }
789 }
790 if (returnSubFlowInfo != null) {
791 waitingMacSubscribers.remove(cp, returnSubFlowInfo);
792 return Optional.of(returnSubFlowInfo);
793 }
794 return Optional.empty();
795 }
796
Gamze Abaka838d8142019-02-21 07:06:55 +0000797 /**
Gamze Abaka33feef52019-02-27 08:16:47 +0000798 * Adds subscriber vlan flows, dhcp, eapol and igmp trap flows for the related uni port.
Gamze Abaka838d8142019-02-21 07:06:55 +0000799 *
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000800 * @param connectPoint the connection point of the subscriber
801 * @param uplinkPort uplink port of the OLT (the nni port)
802 * @param sub subscriber information that includes s, c tags, tech profile and bandwidth profile references
Gamze Abaka838d8142019-02-21 07:06:55 +0000803 */
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000804 private void provisionUniTagList(ConnectPoint connectPoint, PortNumber uplinkPort,
805 SubscriberAndDeviceInformation sub) {
Gamze Abaka641fc072018-09-04 09:16:27 +0000806
Matteo Scandolo19b56f62020-10-29 13:29:21 -0700807 log.debug("Provisioning vlans for subscriber on dev/port: {}", connectPoint.toString());
808 if (log.isTraceEnabled()) {
809 log.trace("Subscriber informations {}", sub);
810 }
Gamze Abaka641fc072018-09-04 09:16:27 +0000811
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000812 if (sub.uniTagList() == null || sub.uniTagList().isEmpty()) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -0700813 log.warn("Unitaglist doesn't exist for the subscriber {} on dev/port {}",
814 sub.id(), connectPoint.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000815 return;
816 }
Gamze Abaka641fc072018-09-04 09:16:27 +0000817
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000818 DeviceId deviceId = connectPoint.deviceId();
819 PortNumber subscriberPort = connectPoint.port();
Gamze Abaka641fc072018-09-04 09:16:27 +0000820
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000821 for (UniTagInformation uniTag : sub.uniTagList()) {
822 handleSubscriberFlows(deviceId, uplinkPort, subscriberPort, uniTag);
823 }
824 }
alshabib3ea82642016-01-12 18:06:53 -0800825
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000826 /**
827 * Finds the uni tag information and provisions the found information.
828 * If the uni tag information is not found, returns
829 *
830 * @param deviceId the access device id
831 * @param uplinkPort the nni port
832 * @param subscriberPort the uni port
833 * @param innerVlan the pon c tag
834 * @param outerVlan the pon s tag
835 * @param tpId the technology profile id
836 */
837 private void provisionUniTagInformation(DeviceId deviceId, PortNumber uplinkPort,
838 PortNumber subscriberPort,
839 VlanId innerVlan,
840 VlanId outerVlan,
841 Integer tpId) {
Jonathan Harte533a422015-10-20 17:31:24 -0700842
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000843 ConnectPoint cp = new ConnectPoint(deviceId, subscriberPort);
844 Optional<UniTagInformation> gotTagInformation = getUniTagInformation(cp, innerVlan, outerVlan, tpId);
845 if (!gotTagInformation.isPresent()) {
846 return;
847 }
848 UniTagInformation tagInformation = gotTagInformation.get();
849 handleSubscriberFlows(deviceId, uplinkPort, subscriberPort, tagInformation);
850 }
alshabib3ea82642016-01-12 18:06:53 -0800851
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000852 private void updateProgrammedSubscriber(ConnectPoint connectPoint, UniTagInformation tagInformation, Boolean add) {
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800853 if (add) {
854 programmedSubs.put(connectPoint, tagInformation);
855 } else {
856 programmedSubs.remove(connectPoint, tagInformation);
857 }
Jonathan Harte533a422015-10-20 17:31:24 -0700858 }
859
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000860 /**
861 * Installs a uni tag information flow.
862 *
863 * @param deviceId the access device id
864 * @param uplinkPort the nni port
865 * @param subscriberPort the uni port
866 * @param tagInfo the uni tag information
867 */
868 private void handleSubscriberFlows(DeviceId deviceId, PortNumber uplinkPort, PortNumber subscriberPort,
869 UniTagInformation tagInfo) {
870
Matteo Scandolo19b56f62020-10-29 13:29:21 -0700871 log.debug("Provisioning vlan-based flows for the uniTagInformation {} on dev/port {}/{}",
872 tagInfo, deviceId, subscriberPort);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000873
874 Port port = deviceService.getPort(deviceId, subscriberPort);
875
876 if (multicastServiceName.equals(tagInfo.getServiceName())) {
877 // IGMP flows are taken care of along with VOD service
878 // Please note that for each service, Subscriber Registered event will be sent
879 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_REGISTERED,
880 deviceId, port, tagInfo.getPonSTag(), tagInfo.getPonCTag(),
881 tagInfo.getTechnologyProfileId()));
882 return;
Gamze Abaka641fc072018-09-04 09:16:27 +0000883 }
884
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000885 BandwidthProfileInformation upstreamBpInfo =
886 getBandwidthProfileInformation(tagInfo.getUpstreamBandwidthProfile());
887 BandwidthProfileInformation downstreamBpInfo =
888 getBandwidthProfileInformation(tagInfo.getDownstreamBandwidthProfile());
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700889 if (upstreamBpInfo == null) {
890 log.warn("No meter installed since no Upstream BW Profile definition found for "
Matteo Scandolo19b56f62020-10-29 13:29:21 -0700891 + "ctag {} stag {} tpId {} and dev/port: {}/{}",
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700892 tagInfo.getPonCTag(), tagInfo.getPonSTag(),
893 tagInfo.getTechnologyProfileId(), deviceId,
894 subscriberPort);
895 return;
896 }
897 if (downstreamBpInfo == null) {
898 log.warn("No meter installed since no Downstream BW Profile definition found for "
Matteo Scandolo19b56f62020-10-29 13:29:21 -0700899 + "ctag {} stag {} tpId {} and dev/port: {}/{}",
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700900 tagInfo.getPonCTag(), tagInfo.getPonSTag(),
901 tagInfo.getTechnologyProfileId(), deviceId,
902 subscriberPort);
903 return;
904 }
Gamze Abakaf59c0912019-04-19 08:24:28 +0000905
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700906 // check for meterIds for the upstream and downstream bandwidth profiles
907 MeterId upMeterId = oltMeterService
908 .getMeterIdFromBpMapping(deviceId, upstreamBpInfo.id());
909 MeterId downMeterId = oltMeterService
910 .getMeterIdFromBpMapping(deviceId, downstreamBpInfo.id());
911 SubscriberFlowInfo fi = new SubscriberFlowInfo(deviceId, uplinkPort, subscriberPort,
912 tagInfo, downMeterId, upMeterId,
913 downstreamBpInfo.id(), upstreamBpInfo.id());
914
915 if (upMeterId != null && downMeterId != null) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -0700916 log.debug("Meters are existing for upstream {} and downstream {} on dev/port {}/{}",
917 upstreamBpInfo.id(), downstreamBpInfo.id(), deviceId, subscriberPort);
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700918 handleSubFlowsWithMeters(fi);
919 } else {
Matteo Scandolo19b56f62020-10-29 13:29:21 -0700920 log.debug("Adding {} on {}/{} to pending subs", fi, deviceId, subscriberPort);
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700921 // one or both meters are not ready. It's possible they are in the process of being
922 // created for other subscribers that share the same bandwidth profile.
Andrea Campanella7a1d7e72020-11-05 10:40:10 +0100923 pendingSubscribersForDevice.compute(deviceId, (id, queue) -> {
924 if (queue == null) {
925 queue = new LinkedBlockingQueue<>();
926 }
927 queue.add(fi);
928 log.info("Added {} to pending subscribers on {}/{}", fi, deviceId, subscriberPort);
929 return queue;
930 });
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700931
932 // queue up the meters to be created
933 if (upMeterId == null) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -0700934 log.debug("Missing meter for upstream {} on {}/{}", upstreamBpInfo.id(), deviceId, subscriberPort);
Andrea Campanella600d2e22020-06-22 11:00:31 +0200935 checkAndCreateDevMeter(deviceId, upstreamBpInfo);
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700936 }
937 if (downMeterId == null) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -0700938 log.debug("Missing meter for downstream {} on {}/{}", downstreamBpInfo.id(), deviceId, subscriberPort);
Andrea Campanella600d2e22020-06-22 11:00:31 +0200939 checkAndCreateDevMeter(deviceId, downstreamBpInfo);
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700940 }
941 }
942 }
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000943
Andrea Campanella600d2e22020-06-22 11:00:31 +0200944 private void checkAndCreateDevMeter(DeviceId deviceId, BandwidthProfileInformation bwpInfo) {
Andrea Campanellad1e26642020-10-23 12:08:32 +0200945 //If false the meter is already being installed, skipping installation
946 if (!oltMeterService.checkAndAddPendingMeter(deviceId, bwpInfo)) {
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700947 return;
948 }
Andrea Campanella600d2e22020-06-22 11:00:31 +0200949 createMeter(deviceId, bwpInfo);
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700950 }
951
Andrea Campanella600d2e22020-06-22 11:00:31 +0200952 private void createMeter(DeviceId deviceId, BandwidthProfileInformation bwpInfo) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -0700953 log.debug("Creating Meter with {} on {}", bwpInfo, deviceId);
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700954 CompletableFuture<Object> meterFuture = new CompletableFuture<>();
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200955
Andrea Campanella600d2e22020-06-22 11:00:31 +0200956 MeterId meterId = oltMeterService.createMeter(deviceId, bwpInfo,
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700957 meterFuture);
958
959 meterFuture.thenAcceptAsync(result -> {
Andrea Campanella7a1d7e72020-11-05 10:40:10 +0100960 BlockingQueue<SubscriberFlowInfo> queue = pendingSubscribersForDevice.get(deviceId);
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700961 // iterate through the subscribers on hold
Andrea Campanella7a1d7e72020-11-05 10:40:10 +0100962 if (queue != null) {
963 while (true) {
964 //TODO this might return the reference and not the actual object so
965 // it can be actually swapped underneath us.
966 SubscriberFlowInfo fi = queue.peek();
967 if (fi == null) {
968 log.debug("No more subscribers pending on {}", deviceId);
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000969 pendingSubscribersForDevice.replace(deviceId, queue);
Andrea Campanella7a1d7e72020-11-05 10:40:10 +0100970 break;
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700971 }
Andrea Campanella7a1d7e72020-11-05 10:40:10 +0100972 if (result == null) {
973 // meter install sent to device
974 log.debug("Meter {} installed for bw {} on {}", meterId, bwpInfo, deviceId);
975
976 MeterId upMeterId = oltMeterService
977 .getMeterIdFromBpMapping(deviceId, fi.getUpBpInfo());
978 MeterId downMeterId = oltMeterService
979 .getMeterIdFromBpMapping(deviceId, fi.getDownBpInfo());
980 if (upMeterId != null && downMeterId != null) {
981 log.debug("Provisioning subscriber after meter {} " +
982 "installation and both meters are present " +
983 "upstream {} and downstream {} on {}/{}",
984 meterId, upMeterId, downMeterId, deviceId, fi.getUniPort());
985 // put in the meterIds because when fi was first
986 // created there may or may not have been a meterId
987 // depending on whether the meter was created or
988 // not at that time.
989 fi.setUpMeterId(upMeterId);
990 fi.setDownMeterId(downMeterId);
991 handleSubFlowsWithMeters(fi);
992 queue.remove(fi);
993 }
994 oltMeterService.removeFromPendingMeters(deviceId, bwpInfo);
995 } else {
996 // meter install failed
997 log.error("Addition of subscriber {} on {}/{} failed due to meter " +
998 "{} with result {}", fi, deviceId, fi.getUniPort(),
999 meterId, result);
1000 queue.remove(fi);
1001 oltMeterService.removeFromPendingMeters(deviceId, bwpInfo);
1002 }
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001003 }
Andrea Campanella7a1d7e72020-11-05 10:40:10 +01001004 } else {
1005 log.info("No pending subscribers on {}", deviceId);
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001006 }
1007 });
Andrea Campanella7a1d7e72020-11-05 10:40:10 +01001008
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001009 }
Ilayda Ozdemir90a93622021-02-25 09:40:58 +00001010
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001011 /**
1012 * Add subscriber flows given meter information for both upstream and
1013 * downstream directions.
1014 *
1015 * @param subscriberFlowInfo relevant information for subscriber
1016 */
1017 private void handleSubFlowsWithMeters(SubscriberFlowInfo subscriberFlowInfo) {
Tunahan Sezena07fe962021-02-24 08:24:24 +00001018 log.debug("Provisioning subscriber flows based on {}", subscriberFlowInfo);
1019 UniTagInformation tagInfo = subscriberFlowInfo.getTagInfo();
1020 if (tagInfo.getIsDhcpRequired()) {
1021 Optional<MacAddress> macAddress =
1022 getMacAddress(subscriberFlowInfo.getDevId(), subscriberFlowInfo.getUniPort(), tagInfo);
1023 if (subscriberFlowInfo.getTagInfo().getEnableMacLearning()) {
1024 ConnectPoint cp = new ConnectPoint(subscriberFlowInfo.getDevId(), subscriberFlowInfo.getUniPort());
1025 if (macAddress.isPresent()) {
1026 log.debug("MAC Address {} obtained for {}", macAddress.get(), subscriberFlowInfo);
1027 } else {
1028 waitingMacSubscribers.put(cp, subscriberFlowInfo);
1029 log.debug("Adding sub to waiting mac map: {}", subscriberFlowInfo);
1030 }
1031
1032 CompletableFuture<ObjectiveError> dhcpFuture = new CompletableFuture<>();
1033 oltFlowService.processDhcpFilteringObjectives(subscriberFlowInfo.getDevId(),
1034 subscriberFlowInfo.getUniPort(), subscriberFlowInfo.getUpId(),
1035 tagInfo, true, true, Optional.of(dhcpFuture));
1036 dhcpFuture.thenAcceptAsync(dhcpStatus -> {
1037 if (dhcpStatus != null) {
1038 log.error("Dhcp Objective failed for {}: {}", subscriberFlowInfo, dhcpStatus);
1039 if (macAddress.isEmpty()) {
1040 waitingMacSubscribers.remove(cp, subscriberFlowInfo);
1041 }
1042 post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_REGISTRATION_FAILED,
1043 subscriberFlowInfo.getDevId(),
1044 deviceService.getPort(subscriberFlowInfo.getDevId(), subscriberFlowInfo.getUniPort()),
1045 tagInfo.getPonSTag(), tagInfo.getPonCTag(), tagInfo.getTechnologyProfileId()));
1046 } else {
1047 log.debug("Dhcp Objective success for: {}", subscriberFlowInfo);
1048 if (macAddress.isPresent()) {
1049 continueProvisioningSubs(subscriberFlowInfo, macAddress);
1050 }
1051 }
1052 });
1053 } else {
1054 log.debug("Dynamic MAC Learning disabled, so will not learn for: {}", subscriberFlowInfo);
1055 // dhcp flows will handle after data plane flows
1056 continueProvisioningSubs(subscriberFlowInfo, macAddress);
1057 }
1058 } else {
1059 // dhcp not required for this service
1060 continueProvisioningSubs(subscriberFlowInfo, Optional.empty());
1061 }
1062 }
1063
1064 private void continueProvisioningSubs(SubscriberFlowInfo subscriberFlowInfo, Optional<MacAddress> macAddress) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -07001065 log.debug("Provisioning subscriber flows on {}/{} based on {}",
1066 subscriberFlowInfo.getDevId(), subscriberFlowInfo.getUniPort(), subscriberFlowInfo);
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001067 UniTagInformation tagInfo = subscriberFlowInfo.getTagInfo();
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001068 CompletableFuture<ObjectiveError> upFuture = new CompletableFuture<>();
1069 CompletableFuture<ObjectiveError> downFuture = new CompletableFuture<>();
Gamze Abakaf59c0912019-04-19 08:24:28 +00001070
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001071 ForwardingObjective.Builder upFwd =
1072 oltFlowService.createUpBuilder(subscriberFlowInfo.getNniPort(), subscriberFlowInfo.getUniPort(),
1073 subscriberFlowInfo.getUpId(), subscriberFlowInfo.getTagInfo());
1074 flowObjectiveService.forward(subscriberFlowInfo.getDevId(), upFwd.add(new ObjectiveContext() {
1075 @Override
1076 public void onSuccess(Objective objective) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -07001077 log.debug("Upstream HSIA flow {} installed successfully on {}/{}",
1078 subscriberFlowInfo, subscriberFlowInfo.getDevId(), subscriberFlowInfo.getUniPort());
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001079 upFuture.complete(null);
Gamze Abakaf59c0912019-04-19 08:24:28 +00001080 }
Gamze Abakaf59c0912019-04-19 08:24:28 +00001081
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001082 @Override
1083 public void onError(Objective objective, ObjectiveError error) {
1084 upFuture.complete(error);
Gamze Abakaf59c0912019-04-19 08:24:28 +00001085 }
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001086 }));
1087
1088 ForwardingObjective.Builder downFwd =
1089 oltFlowService.createDownBuilder(subscriberFlowInfo.getNniPort(), subscriberFlowInfo.getUniPort(),
Tunahan Sezena07fe962021-02-24 08:24:24 +00001090 subscriberFlowInfo.getDownId(), subscriberFlowInfo.getTagInfo(), macAddress);
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001091 flowObjectiveService.forward(subscriberFlowInfo.getDevId(), downFwd.add(new ObjectiveContext() {
1092 @Override
1093 public void onSuccess(Objective objective) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -07001094 log.debug("Downstream HSIA flow {} installed successfully on {}/{}",
1095 subscriberFlowInfo, subscriberFlowInfo.getDevId(), subscriberFlowInfo.getUniPort());
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001096 downFuture.complete(null);
1097 }
1098
1099 @Override
1100 public void onError(Objective objective, ObjectiveError error) {
1101 downFuture.complete(error);
1102 }
1103 }));
Gamze Abakaf59c0912019-04-19 08:24:28 +00001104
Amit Ghoshe1d3f092018-10-09 19:44:33 +01001105 upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001106 AccessDeviceEvent.Type type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_REGISTERED;
Amit Ghoshe1d3f092018-10-09 19:44:33 +01001107 if (downStatus != null) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -07001108 log.error("Flow with innervlan {} and outerVlan {} on {}/{} failed downstream installation: {}",
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001109 tagInfo.getPonCTag(), tagInfo.getPonSTag(), subscriberFlowInfo.getDevId(),
1110 subscriberFlowInfo.getUniPort(), downStatus);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001111 type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_REGISTRATION_FAILED;
Amit Ghoshe1d3f092018-10-09 19:44:33 +01001112 } else if (upStatus != null) {
Tunahan Sezena07fe962021-02-24 08:24:24 +00001113 log.error("Flow with innervlan {} and outerVlan {} on {}/{} failed upstream installation: {}",
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001114 tagInfo.getPonCTag(), tagInfo.getPonSTag(), subscriberFlowInfo.getDevId(),
1115 subscriberFlowInfo.getUniPort(), upStatus);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001116 type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_REGISTRATION_FAILED;
Gamze Abakaf59c0912019-04-19 08:24:28 +00001117 } else {
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001118 log.debug("Upstream and downstream data plane flows are installed successfully " +
Matteo Scandolo19b56f62020-10-29 13:29:21 -07001119 "for {}/{}", subscriberFlowInfo.getDevId(), subscriberFlowInfo.getUniPort());
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001120 oltFlowService.processEapolFilteringObjectives(subscriberFlowInfo.getDevId(),
1121 subscriberFlowInfo.getUniPort(),
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001122 tagInfo.getUpstreamBandwidthProfile(),
1123 null, tagInfo.getPonCTag(), true);
Tunahan Sezena07fe962021-02-24 08:24:24 +00001124
1125
1126 if (!tagInfo.getEnableMacLearning()) {
1127 oltFlowService.processDhcpFilteringObjectives(subscriberFlowInfo.getDevId(),
1128 subscriberFlowInfo.getUniPort(),
1129 subscriberFlowInfo.getUpId(),
1130 tagInfo, true, true, Optional.empty());
1131 }
Gamze Abakaf59c0912019-04-19 08:24:28 +00001132
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001133 oltFlowService.processIgmpFilteringObjectives(subscriberFlowInfo.getDevId(),
1134 subscriberFlowInfo.getUniPort(),
1135 subscriberFlowInfo.getUpId(),
1136 tagInfo, true, true);
Gustavo Silva5c492dd2021-02-12 10:21:11 -03001137
1138 oltFlowService.processPPPoEDFilteringObjectives(subscriberFlowInfo.getDevId(),
1139 subscriberFlowInfo.getUniPort(),
1140 subscriberFlowInfo.getUpId(),
1141 tagInfo, true, true);
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001142 updateProgrammedSubscriber(new ConnectPoint(subscriberFlowInfo.getDevId(),
1143 subscriberFlowInfo.getUniPort()),
1144 tagInfo, true);
Amit Ghoshe1d3f092018-10-09 19:44:33 +01001145 }
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001146 post(new AccessDeviceEvent(type, subscriberFlowInfo.getDevId(),
1147 deviceService.getPort(subscriberFlowInfo.getDevId(),
1148 subscriberFlowInfo.getUniPort()),
1149 tagInfo.getPonSTag(), tagInfo.getPonCTag(),
1150 tagInfo.getTechnologyProfileId()));
Amit Ghoshe1d3f092018-10-09 19:44:33 +01001151 }, oltInstallers);
Amit Ghoshe1d3f092018-10-09 19:44:33 +01001152 }
1153
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001154 /**
Tunahan Sezena07fe962021-02-24 08:24:24 +00001155 * Gets mac address from tag info if present, else checks the host service.
1156 *
1157 * @param deviceId device ID
1158 * @param portNumber uni port
1159 * @param tagInformation tag info
1160 * @return MAC Address of subscriber
1161 */
1162 private Optional<MacAddress> getMacAddress(DeviceId deviceId, PortNumber portNumber,
1163 UniTagInformation tagInformation) {
1164 if (isMacAddressValid(tagInformation)) {
1165 log.debug("Got MAC Address {} from the uniTagInformation for dev/port {}/{} and cTag {}",
1166 tagInformation.getConfiguredMacAddress(), deviceId, portNumber, tagInformation.getPonCTag());
1167 return Optional.of(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()));
1168 } else if (tagInformation.getEnableMacLearning()) {
1169 Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, portNumber))
1170 .stream().filter(host -> host.vlan().equals(tagInformation.getPonCTag())).findFirst();
1171 if (optHost.isPresent()) {
1172 log.debug("Got MAC Address {} from the hostService for dev/port {}/{} and cTag {}",
1173 optHost.get().mac(), deviceId, portNumber, tagInformation.getPonCTag());
1174 return Optional.of(optHost.get().mac());
1175 }
1176 }
1177 log.debug("Could not obtain MAC Address for dev/port {}/{} and cTag {}", deviceId, portNumber,
1178 tagInformation.getPonCTag());
1179 return Optional.empty();
1180 }
1181
1182 private boolean isMacAddressValid(UniTagInformation tagInformation) {
1183 return tagInformation.getConfiguredMacAddress() != null &&
1184 !tagInformation.getConfiguredMacAddress().trim().equals("") &&
1185 !MacAddress.NONE.equals(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()));
1186 }
1187
1188 /**
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001189 * Checks the subscriber uni tag list and find the uni tag information.
1190 * using the pon c tag, pon s tag and the technology profile id
1191 * May return Optional<null>
1192 *
1193 * @param cp the connection point of the subscriber
1194 * @param innerVlan pon c tag
1195 * @param outerVlan pon s tag
1196 * @param tpId the technology profile id
1197 * @return the found uni tag information
1198 */
1199 private Optional<UniTagInformation> getUniTagInformation(ConnectPoint cp, VlanId innerVlan, VlanId outerVlan,
1200 int tpId) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -07001201 log.debug("Getting uni tag information for cp: {}, innerVlan: {}, outerVlan: {}, tpId: {}",
1202 cp.toString(), innerVlan, outerVlan, tpId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001203 SubscriberAndDeviceInformation subInfo = getSubscriber(cp);
Gamze Abakaf59c0912019-04-19 08:24:28 +00001204 if (subInfo == null) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -07001205 log.warn("Subscriber information doesn't exist for the connect point {}", cp.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001206 return Optional.empty();
Gamze Abakaf59c0912019-04-19 08:24:28 +00001207 }
1208
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001209 List<UniTagInformation> uniTagList = subInfo.uniTagList();
1210 if (uniTagList == null) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -07001211 log.warn("Uni tag list is not found for the subscriber {} on {}", subInfo.id(), cp.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001212 return Optional.empty();
1213 }
Amit Ghoshe1d3f092018-10-09 19:44:33 +01001214
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001215 UniTagInformation service = null;
1216 for (UniTagInformation tagInfo : subInfo.uniTagList()) {
1217 if (innerVlan.equals(tagInfo.getPonCTag()) && outerVlan.equals(tagInfo.getPonSTag())
1218 && tpId == tagInfo.getTechnologyProfileId()) {
1219 service = tagInfo;
1220 break;
Andy Bavier160e8682019-05-07 18:32:22 -07001221 }
Gamze Abaka1efc80c2019-02-15 12:10:54 +00001222 }
Gamze Abaka1efc80c2019-02-15 12:10:54 +00001223
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001224 if (service == null) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -07001225 log.warn("SADIS doesn't include the service with ponCtag {} ponStag {} and tpId {} on {}",
1226 innerVlan, outerVlan, tpId, cp.toString());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001227 return Optional.empty();
Gamze Abaka33feef52019-02-27 08:16:47 +00001228 }
1229
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001230 return Optional.of(service);
Amit Ghosh95e2f652017-08-23 12:49:46 +01001231 }
1232
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001233 /**
Jonathan Hart403372d2018-08-22 11:44:13 -07001234 * Creates trap flows for device, including DHCP and LLDP trap on NNI and
1235 * EAPOL trap on the UNIs, if device is present in Sadis config.
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001236 *
1237 * @param dev Device to look for
1238 */
Jonathan Hart403372d2018-08-22 11:44:13 -07001239 private void checkAndCreateDeviceFlows(Device dev) {
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001240 // check if this device is provisioned in Sadis
Gamze Abaka641fc072018-09-04 09:16:27 +00001241 SubscriberAndDeviceInformation deviceInfo = getOltInfo(dev);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001242 log.info("checkAndCreateDeviceFlows: deviceInfo {}", deviceInfo);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001243
1244 if (deviceInfo != null) {
Andrea Campanella438e1ad2021-03-26 11:41:16 +01001245 log.debug("Driver for device {} is {}", dev.id(),
1246 driverService.getDriver(dev.id()));
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001247 for (Port p : deviceService.getPorts(dev.id())) {
Jonathan Hart4f178fa2020-02-03 10:46:01 -08001248 if (PortNumber.LOCAL.equals(p.number()) || !p.isEnabled()) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001249 continue;
1250 }
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001251 if (isUniPort(dev, p)) {
Andrea Campanellaa2491782020-03-13 18:09:31 +01001252 if (!programmedSubs.containsKey(new ConnectPoint(dev.id(), p.number()))) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -07001253 log.info("Creating Eapol on {}/{}", dev.id(), p.number());
Andrea Campanellaa2491782020-03-13 18:09:31 +01001254 oltFlowService.processEapolFilteringObjectives(dev.id(), p.number(), defaultBpId, null,
1255 VlanId.vlanId(EAPOL_DEFAULT_VLAN), true);
1256 } else {
Matteo Scandolo19b56f62020-10-29 13:29:21 -07001257 log.debug("Subscriber Eapol on {}/{} is already provisioned, not installing default",
1258 dev.id(), p.number());
Andrea Campanellaa2491782020-03-13 18:09:31 +01001259 }
Jonathan Hart403372d2018-08-22 11:44:13 -07001260 } else {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001261 oltFlowService.processNniFilteringObjectives(dev.id(), p.number(), true);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001262 }
1263 }
1264 }
1265 }
1266
Jonathan Hart403372d2018-08-22 11:44:13 -07001267
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001268 /**
1269 * Get the uplink for of the OLT device.
Gamze Abakaad329652018-12-20 10:12:21 +00001270 * <p>
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001271 * This assumes that the OLT has a single uplink port. When more uplink ports need to be supported
1272 * this logic needs to be changed
1273 *
1274 * @param dev Device to look for
1275 * @return The uplink Port of the OLT
1276 */
1277 private Port getUplinkPort(Device dev) {
1278 // check if this device is provisioned in Sadis
Gamze Abaka641fc072018-09-04 09:16:27 +00001279 SubscriberAndDeviceInformation deviceInfo = getOltInfo(dev);
Saurav Daseae48de2019-06-19 13:26:15 -07001280 log.trace("getUplinkPort: deviceInfo {}", deviceInfo);
Saurav Das82b8e6d2018-10-04 15:25:12 -07001281 if (deviceInfo == null) {
1282 log.warn("Device {} is not configured in SADIS .. cannot fetch device"
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001283 + " info", dev.id());
Saurav Das82b8e6d2018-10-04 15:25:12 -07001284 return null;
1285 }
1286 // Return the port that has been configured as the uplink port of this OLT in Sadis
kdarapuaa5da252020-04-10 15:58:05 +05301287 Optional<Port> optionalPort = deviceService.getPorts(dev.id()).stream()
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001288 .filter(port -> isNniPort(port) ||
1289 (port.number().toLong() == deviceInfo.uplinkPort()))
1290 .findFirst();
kdarapuaa5da252020-04-10 15:58:05 +05301291 if (optionalPort.isPresent()) {
1292 log.trace("getUplinkPort: Found port {}", optionalPort.get());
1293 return optionalPort.get();
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001294 }
1295
Saurav Daseae48de2019-06-19 13:26:15 -07001296 log.warn("getUplinkPort: " + NO_UPLINK_PORT, dev.id());
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001297 return null;
1298 }
1299
1300 /**
1301 * Return the subscriber on a port.
1302 *
Matteo Scandolo962a6ad2018-12-11 15:39:42 -08001303 * @param cp ConnectPoint on which to find the subscriber
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001304 * @return subscriber if found else null
1305 */
Ilayda Ozdemir90a93622021-02-25 09:40:58 +00001306 protected SubscriberAndDeviceInformation getSubscriber(ConnectPoint cp) {
1307 if (subsService == null) {
1308 log.warn(SADIS_NOT_RUNNING);
1309 return null;
1310 }
Matteo Scandolo962a6ad2018-12-11 15:39:42 -08001311 Port port = deviceService.getPort(cp);
1312 checkNotNull(port, "Invalid connect point");
1313 String portName = port.annotations().value(AnnotationKeys.PORT_NAME);
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001314 return subsService.get(portName);
1315 }
1316
Gamze Abakaad329652018-12-20 10:12:21 +00001317 /**
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001318 * Checks whether the given port of the device is a uni port or not.
1319 *
1320 * @param d the access device
1321 * @param p the port of the device
1322 * @return true if the given port is a uni port
Gamze Abakaad329652018-12-20 10:12:21 +00001323 */
Amit Ghosh1ed9aef2018-07-17 17:08:16 +01001324 private boolean isUniPort(Device d, Port p) {
1325 Port ulPort = getUplinkPort(d);
1326 if (ulPort != null) {
1327 return (ulPort.number().toLong() != p.number().toLong());
1328 }
Thomas Lee Sd7735f92020-02-20 19:21:47 +05301329 //handles a special case where NNI port is misconfigured in SADIS and getUplinkPort(d) returns null
1330 //checks whether the port name starts with nni- which is the signature of an NNI Port
1331 if (p.annotations().value(AnnotationKeys.PORT_NAME) != null &&
1332 p.annotations().value(AnnotationKeys.PORT_NAME).startsWith(NNI)) {
1333 log.error("NNI port number {} is not matching with configured value", p.number().toLong());
1334 return false;
1335 }
1336 return true;
Jonathan Hart1d34c8b2018-05-05 15:37:28 -07001337 }
1338
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001339 /**
1340 * Gets the given device details from SADIS.
1341 * If the device is not found, returns null
1342 *
1343 * @param dev the access device
1344 * @return the olt information
1345 */
Jonathan Hart4c538002018-08-23 10:11:54 -07001346 private SubscriberAndDeviceInformation getOltInfo(Device dev) {
Ilayda Ozdemir90a93622021-02-25 09:40:58 +00001347 if (subsService == null) {
1348 log.warn(SADIS_NOT_RUNNING);
1349 return null;
1350 }
Jonathan Hart4c538002018-08-23 10:11:54 -07001351 String devSerialNo = dev.serialNumber();
Gamze Abaka641fc072018-09-04 09:16:27 +00001352 return subsService.get(devSerialNo);
Jonathan Hart4c538002018-08-23 10:11:54 -07001353 }
1354
Andrea Campanella1edf8832021-05-06 12:51:33 +02001355 /**
1356 * Checks for mastership or falls back to leadership on deviceId.
1357 * If the device is available use mastership,
1358 * otherwise fallback on leadership.
1359 * Leadership on the device topic is needed because the master can be NONE
1360 * in case the device went away, we still need to handle events
1361 * consistently
1362 */
Andrea Campanellaaf39b4c2020-05-13 14:07:44 +02001363 private boolean isLocalLeader(DeviceId deviceId) {
Andrea Campanella1edf8832021-05-06 12:51:33 +02001364 if (deviceService.isAvailable(deviceId)) {
1365 return mastershipService.isLocalMaster(deviceId);
1366 } else {
Andrea Campanellaaf39b4c2020-05-13 14:07:44 +02001367 // Fallback with Leadership service - device id is used as topic
1368 NodeId leader = leadershipService.runForLeadership(
1369 deviceId.toString()).leaderNodeId();
1370 // Verify if this node is the leader
1371 return clusterService.getLocalNode().id().equals(leader);
Jonathan Hart4f178fa2020-02-03 10:46:01 -08001372 }
Jonathan Hart4f178fa2020-02-03 10:46:01 -08001373 }
1374
kdarapuaa5da252020-04-10 15:58:05 +05301375 private boolean isNniPort(Port port) {
1376 if (port.annotations().keys().contains(AnnotationKeys.PORT_NAME)) {
1377 return port.annotations().value(AnnotationKeys.PORT_NAME).contains(NNI);
1378 }
1379 return false;
1380 }
1381
Tunahan Sezena07fe962021-02-24 08:24:24 +00001382 private class InternalHostListener implements HostListener {
1383 @Override
1384 public void event(HostEvent event) {
1385 hostEventExecutor.execute(() -> {
1386 Host host = event.subject();
1387 switch (event.type()) {
1388 case HOST_ADDED:
1389 ConnectPoint cp = new ConnectPoint(host.location().deviceId(), host.location().port());
1390 Optional<SubscriberFlowInfo> optSubFlowInfo =
1391 getAndRemoveWaitingMacSubFlowInfoForCTag(cp, host.vlan());
1392 if (optSubFlowInfo.isPresent()) {
1393 log.debug("Continuing provisioning for waiting mac service. event: {}", event);
1394 continueProvisioningSubs(optSubFlowInfo.get(), Optional.of(host.mac()));
1395 } else {
1396 log.debug("There is no waiting mac sub. event: {}", event);
1397 }
1398 break;
1399 case HOST_UPDATED:
1400 if (event.prevSubject() != null && !event.prevSubject().mac().equals(event.subject().mac())) {
1401 log.debug("Subscriber's MAC address changed. devId/port: {}/{} vlan: {}",
1402 host.location().deviceId(), host.location().port(), host.vlan());
1403 // TODO handle subscriber MAC Address changed
1404 } else {
1405 log.debug("Unhandled HOST_UPDATED event: {}", event);
1406 }
1407 break;
1408 default:
1409 log.debug("Unhandled host event received. event: {}", event);
1410 }
1411 });
1412 }
1413
1414 @Override
1415 public boolean isRelevant(HostEvent event) {
1416 return isLocalLeader(event.subject().location().deviceId());
1417 }
1418 }
1419
alshabibf0e7e702015-05-30 18:22:36 -07001420 private class InternalDeviceListener implements DeviceListener {
Saurav Dasa9d5f442019-03-06 19:32:48 -08001421 private Set<DeviceId> programmedDevices = Sets.newConcurrentHashSet();
1422
alshabibf0e7e702015-05-30 18:22:36 -07001423 @Override
1424 public void event(DeviceEvent event) {
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001425 eventExecutor.execute(() -> {
1426 DeviceId devId = event.subject().id();
1427 Device dev = event.subject();
Gamze Abaka838d8142019-02-21 07:06:55 +00001428 Port port = event.port();
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001429 DeviceEvent.Type eventType = event.type();
Jonathan Hart4c538002018-08-23 10:11:54 -07001430
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001431 if (DeviceEvent.Type.PORT_STATS_UPDATED.equals(eventType) ||
1432 DeviceEvent.Type.DEVICE_SUSPENDED.equals(eventType) ||
1433 DeviceEvent.Type.DEVICE_UPDATED.equals(eventType)) {
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001434 return;
1435 }
Jonathan Hart4c538002018-08-23 10:11:54 -07001436
Andrea Campanellaaf39b4c2020-05-13 14:07:44 +02001437 boolean isLocalLeader = isLocalLeader(devId);
Jonathan Hart4f178fa2020-02-03 10:46:01 -08001438 // Only handle the event if the device belongs to us
Andrea Campanellaaf39b4c2020-05-13 14:07:44 +02001439 if (!isLocalLeader && event.type().equals(DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED)
1440 && !deviceService.isAvailable(devId) && deviceService.getPorts(devId).isEmpty()) {
1441 log.info("Cleaning local state for non master instance upon " +
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001442 "device disconnection {}", devId);
Andrea Campanellaaf39b4c2020-05-13 14:07:44 +02001443 // Since no mastership of the device is present upon disconnection
1444 // the method in the FlowRuleManager only empties the local copy
1445 // of the DeviceFlowTable thus this method needs to get called
1446 // on every instance, see how it's done in the InternalDeviceListener
1447 // in FlowRuleManager: no mastership check for purgeOnDisconnection
Andrea Campanella3f34c992020-07-15 10:54:10 +02001448 handleDeviceDisconnection(dev, false, false);
Andrea Campanellaaf39b4c2020-05-13 14:07:44 +02001449 return;
1450 } else if (!isLocalLeader) {
Andrea Campanella506df202020-05-21 10:26:12 +02001451 log.debug("Not handling event because instance is not leader for {}", devId);
Jonathan Hart4f178fa2020-02-03 10:46:01 -08001452 return;
1453 }
1454
Matteo Scandolo19b56f62020-10-29 13:29:21 -07001455 log.debug("OLT got {} event for {}/{}", eventType, event.subject(), event.port());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001456
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001457 if (getOltInfo(dev) == null) {
Saurav Dasa9d5f442019-03-06 19:32:48 -08001458 // it's possible that we got an event for a previously
1459 // programmed OLT that is no longer available in SADIS
1460 // we let such events go through
1461 if (!programmedDevices.contains(devId)) {
1462 log.warn("No device info found for {}, this is either "
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001463 + "not an OLT or not known to sadis", dev);
Saurav Dasa9d5f442019-03-06 19:32:48 -08001464 return;
1465 }
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001466 }
Jonathan Hart4c538002018-08-23 10:11:54 -07001467
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001468 switch (event.type()) {
1469 //TODO: Port handling and bookkeeping should be improved once
1470 // olt firmware handles correct behaviour.
1471 case PORT_ADDED:
Andrea Campanella3f34c992020-07-15 10:54:10 +02001472 if (!deviceService.isAvailable(devId)) {
1473 log.warn("Received {} for disconnected device {}, ignoring", event, devId);
1474 return;
1475 }
Gamze Abaka838d8142019-02-21 07:06:55 +00001476 if (isUniPort(dev, port)) {
1477 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, port));
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001478
1479 if (port.isEnabled() && !port.number().equals(PortNumber.LOCAL)) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -07001480 log.info("eapol will be sent for port added {}/{}", devId, port);
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001481 oltFlowService.processEapolFilteringObjectives(devId, port.number(), defaultBpId,
1482 null,
Andrea Campanella3f34c992020-07-15 10:54:10 +02001483 VlanId.vlanId(EAPOL_DEFAULT_VLAN),
1484 true);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001485 }
1486 } else {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001487 SubscriberAndDeviceInformation deviceInfo = getOltInfo(dev);
1488 if (deviceInfo != null) {
1489 oltFlowService.processNniFilteringObjectives(dev.id(), port.number(), true);
1490 }
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001491 }
1492 break;
1493 case PORT_REMOVED:
Gamze Abaka838d8142019-02-21 07:06:55 +00001494 if (isUniPort(dev, port)) {
Andrea Campanellacf0e3052020-08-27 11:05:39 +02001495 // if no subscriber is provisioned we need to remove the default EAPOL
1496 // if a subscriber was provisioned the default EAPOL will not be there and we can skip.
1497 // The EAPOL with subscriber tag will be removed by removeSubscriber call.
1498 Collection<? extends UniTagInformation> uniTagInformationSet =
1499 programmedSubs.get(new ConnectPoint(port.element().id(), port.number())).value();
1500 if (uniTagInformationSet == null || uniTagInformationSet.isEmpty()) {
Matteo Scandolo19b56f62020-10-29 13:29:21 -07001501 log.info("No subscriber provisioned on port {}/{} in PORT_REMOVED event, " +
1502 "removing default EAPOL flow", devId, port);
Andrea Campanellacf0e3052020-08-27 11:05:39 +02001503 oltFlowService.processEapolFilteringObjectives(devId, port.number(), defaultBpId,
1504 null,
1505 VlanId.vlanId(EAPOL_DEFAULT_VLAN),
1506 false);
1507 } else {
1508 removeSubscriber(new ConnectPoint(devId, port.number()));
1509 }
Andy Bavier160e8682019-05-07 18:32:22 -07001510
Gamze Abaka838d8142019-02-21 07:06:55 +00001511 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, port));
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001512 }
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001513 break;
1514 case PORT_UPDATED:
Andrea Campanella3f34c992020-07-15 10:54:10 +02001515 if (!deviceService.isAvailable(devId)) {
1516 log.warn("Received {} for disconnected device {}, ignoring", event, devId);
1517 return;
1518 }
Gamze Abaka838d8142019-02-21 07:06:55 +00001519 if (!isUniPort(dev, port)) {
Saurav Das9da7d522020-03-23 19:14:35 -07001520 SubscriberAndDeviceInformation deviceInfo = getOltInfo(dev);
1521 if (deviceInfo != null && port.isEnabled()) {
1522 log.debug("NNI dev/port {}/{} enabled", dev.id(),
1523 port.number());
1524 oltFlowService.processNniFilteringObjectives(dev.id(),
1525 port.number(), true);
1526 }
1527 return;
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001528 }
Jonathan Hart4f178fa2020-02-03 10:46:01 -08001529 ConnectPoint cp = new ConnectPoint(devId, port.number());
1530 Collection<? extends UniTagInformation> uniTagInformationSet = programmedSubs.get(cp).value();
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001531 if (uniTagInformationSet == null || uniTagInformationSet.isEmpty()) {
Saurav Dasb776aef2020-03-09 14:29:46 -07001532 if (!port.number().equals(PortNumber.LOCAL)) {
Matteo Scandolo3a037a32020-04-01 12:17:50 -07001533 log.info("eapol will be {} for dev/port updated {}/{} with default vlan {}",
Saurav Dasb776aef2020-03-09 14:29:46 -07001534 (port.isEnabled()) ? "added" : "removed",
Matteo Scandolo3a037a32020-04-01 12:17:50 -07001535 devId, port.number(), EAPOL_DEFAULT_VLAN);
Matteo Scandolo27c471c2020-02-11 16:41:53 -08001536 oltFlowService.processEapolFilteringObjectives(devId, port.number(), defaultBpId,
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001537 null,
1538 VlanId.vlanId(EAPOL_DEFAULT_VLAN),
1539 port.isEnabled());
1540 }
1541 } else {
Saurav Dasb776aef2020-03-09 14:29:46 -07001542 log.info("eapol will be {} for dev/port updated {}/{}",
1543 (port.isEnabled()) ? "added" : "removed",
1544 devId, port.number());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001545 uniTagInformationSet.forEach(uniTag ->
1546 oltFlowService.processEapolFilteringObjectives(devId, port.number(),
1547 uniTag.getUpstreamBandwidthProfile(), null,
1548 uniTag.getPonCTag(), port.isEnabled()));
1549 }
Gamze Abaka838d8142019-02-21 07:06:55 +00001550 if (port.isEnabled()) {
Gamze Abaka838d8142019-02-21 07:06:55 +00001551 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, port));
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001552 } else {
Gamze Abaka838d8142019-02-21 07:06:55 +00001553 post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, port));
Jonathan Hart1d34c8b2018-05-05 15:37:28 -07001554 }
alshabibbb83aa22016-02-10 15:08:23 -08001555 break;
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001556 case DEVICE_ADDED:
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001557 handleDeviceConnection(dev, true);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001558 break;
1559 case DEVICE_REMOVED:
Andrea Campanella3f34c992020-07-15 10:54:10 +02001560 handleDeviceDisconnection(dev, true, true);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001561 break;
1562 case DEVICE_AVAILABILITY_CHANGED:
1563 if (deviceService.isAvailable(devId)) {
Saurav Dasbd3b6712020-03-31 23:28:35 -07001564 log.info("Handling available device: {}", dev.id());
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001565 handleDeviceConnection(dev, false);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001566 } else {
Hardik Windlassa58fbee2020-03-12 18:33:55 +05301567 if (deviceService.getPorts(devId).isEmpty()) {
Saurav Dasbd3b6712020-03-31 23:28:35 -07001568 log.info("Handling controlled device disconnection .. "
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001569 + "flushing all state for dev:{}", devId);
Andrea Campanella3f34c992020-07-15 10:54:10 +02001570 handleDeviceDisconnection(dev, true, false);
Saurav Dasbd3b6712020-03-31 23:28:35 -07001571 } else {
1572 log.info("Disconnected device has available ports .. "
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001573 + "assuming temporary disconnection, "
1574 + "retaining state for device {}", devId);
Hardik Windlassa58fbee2020-03-12 18:33:55 +05301575 }
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001576 }
1577 break;
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001578 default:
Andrea Campanella3f34c992020-07-15 10:54:10 +02001579 log.debug("Not handling event {}", event);
Matteo Scandolo632f0fc2018-09-07 12:21:45 -07001580 return;
1581 }
1582 });
alshabibf0e7e702015-05-30 18:22:36 -07001583 }
Gamze Abaka838d8142019-02-21 07:06:55 +00001584
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001585 private void sendUniEvent(Device device, AccessDeviceEvent.Type eventType) {
1586 deviceService.getPorts(device.id()).stream()
1587 .filter(p -> !PortNumber.LOCAL.equals(p.number()))
1588 .filter(p -> isUniPort(device, p))
1589 .forEach(p -> post(new AccessDeviceEvent(eventType, device.id(), p)));
1590 }
1591
Andrea Campanella3f34c992020-07-15 10:54:10 +02001592 private void handleDeviceDisconnection(Device device, boolean sendDisconnectedEvent, boolean sendUniEvent) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001593 programmedDevices.remove(device.id());
1594 removeAllSubscribers(device.id());
Tunahan Sezena07fe962021-02-24 08:24:24 +00001595 removeWaitingMacSubs(device.id());
Andrea Campanella600d2e22020-06-22 11:00:31 +02001596 //Handle case where OLT disconnects during subscriber provisioning
Andrea Campanella7a1d7e72020-11-05 10:40:10 +01001597 pendingSubscribersForDevice.remove(device.id());
Andrea Campanella600d2e22020-06-22 11:00:31 +02001598 oltFlowService.clearDeviceState(device.id());
1599
1600 //Complete meter and flow purge
Hardik Windlassa58fbee2020-03-12 18:33:55 +05301601 flowRuleService.purgeFlowRules(device.id());
Jonathan Hart4f178fa2020-02-03 10:46:01 -08001602 oltMeterService.clearMeters(device.id());
Andrea Campanella3f34c992020-07-15 10:54:10 +02001603 if (sendDisconnectedEvent) {
1604 post(new AccessDeviceEvent(
1605 AccessDeviceEvent.Type.DEVICE_DISCONNECTED, device.id(),
1606 null, null, null));
1607 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001608 if (sendUniEvent) {
1609 sendUniEvent(device, AccessDeviceEvent.Type.UNI_REMOVED);
Gamze Abaka838d8142019-02-21 07:06:55 +00001610 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001611 }
1612
1613 private void handleDeviceConnection(Device dev, boolean sendUniEvent) {
1614 post(new AccessDeviceEvent(
1615 AccessDeviceEvent.Type.DEVICE_CONNECTED, dev.id(),
1616 null, null, null));
1617 programmedDevices.add(dev.id());
1618 checkAndCreateDeviceFlows(dev);
1619 if (sendUniEvent) {
1620 sendUniEvent(dev, AccessDeviceEvent.Type.UNI_ADDED);
1621 }
Gamze Abaka838d8142019-02-21 07:06:55 +00001622 }
Gamze Abakada282b42019-03-11 13:16:48 +00001623
1624 private void removeAllSubscribers(DeviceId deviceId) {
Jonathan Hart4f178fa2020-02-03 10:46:01 -08001625 List<Map.Entry<ConnectPoint, UniTagInformation>> subs = programmedSubs.stream()
1626 .filter(e -> e.getKey().deviceId().equals(deviceId))
1627 .collect(toList());
Gamze Abakada282b42019-03-11 13:16:48 +00001628
Jonathan Hart4f178fa2020-02-03 10:46:01 -08001629 subs.forEach(e -> programmedSubs.remove(e.getKey(), e.getValue()));
Gamze Abakada282b42019-03-11 13:16:48 +00001630 }
Gamze Abaka641fc072018-09-04 09:16:27 +00001631
Tunahan Sezena07fe962021-02-24 08:24:24 +00001632 private void removeWaitingMacSubs(DeviceId deviceId) {
1633 List<ConnectPoint> waitingMacKeys = waitingMacSubscribers.stream()
1634 .filter(cp -> cp.getKey().deviceId().equals(deviceId))
1635 .map(Map.Entry::getKey)
1636 .collect(toList());
1637 waitingMacKeys.forEach(cp -> waitingMacSubscribers.removeAll(cp));
1638 }
1639
Gamze Abaka641fc072018-09-04 09:16:27 +00001640 }
Jonathan Hart4f178fa2020-02-03 10:46:01 -08001641
1642 private class InternalClusterListener implements ClusterEventListener {
1643
1644 @Override
1645 public void event(ClusterEvent event) {
1646 if (event.type() == ClusterEvent.Type.INSTANCE_READY) {
1647 hasher.addServer(event.subject().id());
1648 }
1649 if (event.type() == ClusterEvent.Type.INSTANCE_DEACTIVATED) {
1650 hasher.removeServer(event.subject().id());
1651 }
1652 }
1653 }
Andrea Campanella0c3309d2020-05-29 01:51:18 -07001654
Hardik Windlass395ff372019-06-13 05:16:00 +00001655}