blob: cd8ab9672030c262e0e9976fdec95c7ac3fec73a [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2014-present Open Networking Foundation
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.net.device.impl;
tomd3097b02014-08-26 10:40:29 -070017
Simon Huntffbad3b2017-05-16 15:37:51 -070018import com.google.common.collect.ImmutableList;
Carmelo Cascone1da7a4d2018-06-27 18:03:11 +020019import com.google.common.collect.Lists;
Simon Huntffbad3b2017-05-16 15:37:51 -070020import com.google.common.collect.Maps;
21import com.google.common.collect.Multimap;
pier388ec252020-04-15 20:53:14 +020022import com.google.common.util.concurrent.Futures;
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -070023import org.onlab.util.KryoNamespace;
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -070024import org.onlab.util.Tools;
Brian O'Connorabafb502014-12-02 22:26:20 -080025import org.onosproject.cluster.ClusterService;
pierventreb2f636b2022-01-03 17:19:24 +010026import org.onosproject.cluster.ControllerNode;
Brian O'Connorabafb502014-12-02 22:26:20 -080027import org.onosproject.cluster.NodeId;
pierventreb2f636b2022-01-03 17:19:24 +010028import org.onosproject.mastership.MastershipAdminService;
Brian O'Connorabafb502014-12-02 22:26:20 -080029import org.onosproject.mastership.MastershipEvent;
30import org.onosproject.mastership.MastershipListener;
31import org.onosproject.mastership.MastershipService;
32import org.onosproject.mastership.MastershipTerm;
33import org.onosproject.mastership.MastershipTermService;
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -070034import org.onosproject.net.ConnectPoint;
Brian O'Connorabafb502014-12-02 22:26:20 -080035import org.onosproject.net.Device;
samuel738dfaf2015-07-11 11:08:57 +080036import org.onosproject.net.Device.Type;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.net.DeviceId;
38import org.onosproject.net.MastershipRole;
39import org.onosproject.net.Port;
40import org.onosproject.net.PortNumber;
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -070041import org.onosproject.net.config.Config;
Yafit Hadara9a73de2015-09-06 13:52:52 +030042import org.onosproject.net.config.NetworkConfigEvent;
43import org.onosproject.net.config.NetworkConfigListener;
44import org.onosproject.net.config.NetworkConfigService;
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -070045import org.onosproject.net.config.PortConfigOperator;
46import org.onosproject.net.config.PortConfigOperatorRegistry;
Yafit Hadara9a73de2015-09-06 13:52:52 +030047import org.onosproject.net.config.basics.BasicDeviceConfig;
Palash Kalaa06a6162017-11-15 20:42:40 +090048import org.onosproject.net.config.basics.DeviceAnnotationConfig;
Yuta HIGUCHI7438f5a2017-02-15 22:09:46 -080049import org.onosproject.net.config.basics.PortAnnotationConfig;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080050import org.onosproject.net.config.basics.PortDescriptionsConfig;
Brian O'Connorabafb502014-12-02 22:26:20 -080051import org.onosproject.net.device.DefaultPortDescription;
52import org.onosproject.net.device.DeviceAdminService;
Brian O'Connorabafb502014-12-02 22:26:20 -080053import org.onosproject.net.device.DeviceDescription;
54import org.onosproject.net.device.DeviceEvent;
55import org.onosproject.net.device.DeviceListener;
56import org.onosproject.net.device.DeviceProvider;
57import org.onosproject.net.device.DeviceProviderRegistry;
58import org.onosproject.net.device.DeviceProviderService;
59import org.onosproject.net.device.DeviceService;
60import org.onosproject.net.device.DeviceStore;
61import org.onosproject.net.device.DeviceStoreDelegate;
62import org.onosproject.net.device.PortDescription;
sangho538108b2015-04-08 14:29:20 -070063import org.onosproject.net.device.PortStatistics;
Yafit Hadara9a73de2015-09-06 13:52:52 +030064import org.onosproject.net.provider.AbstractListenerProviderRegistry;
Brian O'Connorabafb502014-12-02 22:26:20 -080065import org.onosproject.net.provider.AbstractProviderService;
Yuta HIGUCHI3a2a9872016-11-29 20:24:23 -080066import org.onosproject.net.provider.Provider;
67import org.onosproject.net.provider.ProviderId;
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -070068import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
69import org.onosproject.store.cluster.messaging.MessageSubject;
70import org.onosproject.store.serializers.KryoNamespaces;
71import org.onosproject.store.service.Serializer;
Jordan Halterman9416aea2017-11-17 12:40:21 -080072import org.onosproject.upgrade.UpgradeService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070073import org.osgi.service.component.annotations.Activate;
74import org.osgi.service.component.annotations.Component;
75import org.osgi.service.component.annotations.Deactivate;
76import org.osgi.service.component.annotations.Reference;
77import org.osgi.service.component.annotations.ReferenceCardinality;
tomd3097b02014-08-26 10:40:29 -070078import org.slf4j.Logger;
tomd3097b02014-08-26 10:40:29 -070079
Yuta HIGUCHI0c47d532017-08-18 23:16:35 -070080import java.time.Instant;
Simon Huntffbad3b2017-05-16 15:37:51 -070081import java.util.Collection;
82import java.util.HashSet;
83import java.util.List;
84import java.util.Map;
85import java.util.Objects;
86import java.util.Optional;
87import java.util.Set;
88import java.util.concurrent.CompletableFuture;
89import java.util.concurrent.ConcurrentHashMap;
90import java.util.concurrent.CopyOnWriteArrayList;
91import java.util.concurrent.ExecutionException;
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -070092import java.util.concurrent.ExecutorService;
Simon Huntffbad3b2017-05-16 15:37:51 -070093import java.util.concurrent.ScheduledExecutorService;
94import java.util.concurrent.TimeUnit;
pierventre00ac2502021-12-02 09:42:28 +010095import java.util.concurrent.atomic.AtomicBoolean;
Simon Huntffbad3b2017-05-16 15:37:51 -070096import java.util.stream.Collectors;
Jonathan Hart2f669362015-02-11 16:19:20 -080097
Ray Milkey9ef22232016-07-14 12:42:37 -070098import static com.google.common.base.Preconditions.checkNotNull;
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -070099import static com.google.common.base.Preconditions.checkState;
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -0700100import static com.google.common.collect.Multimaps.newListMultimap;
101import static com.google.common.collect.Multimaps.synchronizedListMultimap;
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700102import static java.util.concurrent.Executors.newSingleThreadExecutor;
Ray Milkey9ef22232016-07-14 12:42:37 -0700103import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
pierventre00ac2502021-12-02 09:42:28 +0100104import static java.lang.System.currentTimeMillis;
Ray Milkey9ef22232016-07-14 12:42:37 -0700105import static org.onlab.util.Tools.groupedThreads;
106import static org.onosproject.net.MastershipRole.MASTER;
107import static org.onosproject.net.MastershipRole.NONE;
108import static org.onosproject.net.MastershipRole.STANDBY;
Ray Milkey9ef22232016-07-14 12:42:37 -0700109import static org.onosproject.security.AppGuard.checkPermission;
110import static org.onosproject.security.AppPermission.Type.DEVICE_READ;
111import static org.slf4j.LoggerFactory.getLogger;
112
tomd3097b02014-08-26 10:40:29 -0700113/**
tome4729872014-09-23 00:37:37 -0700114 * Provides implementation of the device SB & NB APIs.
tomd3097b02014-08-26 10:40:29 -0700115 */
Ray Milkey86ad7bb2018-09-27 12:32:28 -0700116@Component(immediate = true,
117 service = {DeviceService.class, DeviceAdminService.class,
118 DeviceProviderRegistry.class, PortConfigOperatorRegistry.class })
tom41a2c5f2014-09-19 09:20:35 -0700119public class DeviceManager
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700120 extends AbstractListenerProviderRegistry<DeviceEvent, DeviceListener, DeviceProvider, DeviceProviderService>
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -0700121 implements DeviceService, DeviceAdminService, DeviceProviderRegistry, PortConfigOperatorRegistry {
tom32f66842014-08-27 19:27:47 -0700122
tome5ec3fd2014-09-04 15:18:06 -0700123 private static final String DEVICE_ID_NULL = "Device ID cannot be null";
124 private static final String PORT_NUMBER_NULL = "Port number cannot be null";
125 private static final String DEVICE_DESCRIPTION_NULL = "Device description cannot be null";
126 private static final String PORT_DESCRIPTION_NULL = "Port description cannot be null";
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -0700127 private static final String PORT_DESC_LIST_NULL = "Port description list cannot be null";
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700128 private static final String EVENT_NON_MASTER = "Non-master node cannot handle this event";
tomd3097b02014-08-26 10:40:29 -0700129
tom5f38b3a2014-08-27 23:50:54 -0700130 private final Logger log = getLogger(getClass());
tomd3097b02014-08-26 10:40:29 -0700131
alshabib339a3d92014-09-26 17:54:32 -0700132 private final DeviceStoreDelegate delegate = new InternalStoreDelegate();
tomf80c9722014-09-24 14:49:18 -0700133
tomc78acee2014-09-24 15:16:55 -0700134 private final MastershipListener mastershipListener = new InternalMastershipListener();
Madan Jampanide003d92015-05-11 17:14:20 -0700135 private NodeId localNodeId;
tomb41d1ac2014-09-24 01:51:24 -0700136
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800137 private ScheduledExecutorService backgroundService;
138
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700139 private final NetworkConfigListener networkConfigListener = new InternalNetworkConfigListener();
140
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
tom41a2c5f2014-09-19 09:20:35 -0700142 protected DeviceStore store;
tomd3097b02014-08-26 10:40:29 -0700143
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700144 @Reference(cardinality = ReferenceCardinality.MANDATORY)
tomb41d1ac2014-09-24 01:51:24 -0700145 protected ClusterService clusterService;
146
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700147 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700148 protected MastershipService mastershipService;
149
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700150 @Reference(cardinality = ReferenceCardinality.MANDATORY)
pierventreb2f636b2022-01-03 17:19:24 +0100151 protected MastershipAdminService mastershipAdminService;
152
153 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700154 protected MastershipTermService termService;
155
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700156 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jordan Halterman9416aea2017-11-17 12:40:21 -0800157 protected UpgradeService upgradeService;
158
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700159 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700160 protected NetworkConfigService networkConfigService;
161
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700162 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700163 protected ClusterCommunicationService communicationService;
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -0700164
pierventreb2f636b2022-01-03 17:19:24 +0100165 private ExecutorService clusterRequestExecutor;
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -0700166 /**
167 * List of all registered PortConfigOperator.
168 */
169 private final List<PortConfigOperator> portOps = new CopyOnWriteArrayList<>();
170
171 /**
172 * Index to look up PortConfigOperator from Config each PortConfigOperator uses.
173 */
174 private final Multimap<Class<? extends Config<ConnectPoint>>, PortConfigOperator> portOpsIndex
Simon Huntffbad3b2017-05-16 15:37:51 -0700175 = synchronizedListMultimap(
176 newListMultimap(new ConcurrentHashMap<>(), CopyOnWriteArrayList::new));
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -0700177
Yuta HIGUCHI7438f5a2017-02-15 22:09:46 -0800178 // not part of portOps. must be executed at the end
179 private PortAnnotationOperator portAnnotationOp;
Palash Kalaa06a6162017-11-15 20:42:40 +0900180 private DeviceAnnotationOperator deviceAnnotationOp;
Yuta HIGUCHI7438f5a2017-02-15 22:09:46 -0800181
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700182 private static final MessageSubject PORT_UPDOWN_SUBJECT =
183 new MessageSubject("port-updown-req");
184
pierventreb2f636b2022-01-03 17:19:24 +0100185 private static final MessageSubject PROBE_SUBJECT =
186 new MessageSubject("probe-req");
187 private static final long PROBE_TIMEOUT_MILLIS = 5000;
188 private static final int PROBE_ATTEMPTS = 3;
189
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700190 private static final Serializer SERIALIZER = Serializer.using(
191 KryoNamespace.newBuilder()
192 .register(KryoNamespaces.API)
193 .register(InternalPortUpDownEvent.class)
194 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
195 .build("DeviceManager"));
196
Saurav Dasd5ec9e92017-01-17 10:40:18 -0800197 /**
198 * Local storage for connectivity status of devices.
199 */
200 private class LocalStatus {
201 boolean connected;
Yuta HIGUCHI0c47d532017-08-18 23:16:35 -0700202 Instant dateTime;
Saurav Dasd5ec9e92017-01-17 10:40:18 -0800203
Yuta HIGUCHI0c47d532017-08-18 23:16:35 -0700204 public LocalStatus(boolean b, Instant now) {
Saurav Dasd5ec9e92017-01-17 10:40:18 -0800205 connected = b;
206 dateTime = now;
207 }
208 }
Simon Huntffbad3b2017-05-16 15:37:51 -0700209
Saurav Dasd5ec9e92017-01-17 10:40:18 -0800210 private final Map<DeviceId, LocalStatus> deviceLocalStatus =
211 Maps.newConcurrentMap();
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -0700212
pierventre00ac2502021-12-02 09:42:28 +0100213 // To remember whether or not the role was acknowledged by the device
214 private final Map<DeviceId, Long> roleToAcknowledge =
215 Maps.newConcurrentMap();
216 private ScheduledExecutorService backgroundRoleChecker;
217 private static final int ROLE_TIMEOUT_SECONDS = 10;
218
tomd3097b02014-08-26 10:40:29 -0700219 @Activate
220 public void activate() {
Yuta HIGUCHI7438f5a2017-02-15 22:09:46 -0800221 portAnnotationOp = new PortAnnotationOperator(networkConfigService);
Palash Kalaa06a6162017-11-15 20:42:40 +0900222 deviceAnnotationOp = new DeviceAnnotationOperator(networkConfigService);
Yuta HIGUCHI7438f5a2017-02-15 22:09:46 -0800223 portOpsIndex.put(PortAnnotationConfig.class, portAnnotationOp);
224
HIGUCHI Yuta060da9a2016-03-11 19:16:35 -0800225 backgroundService = newSingleThreadScheduledExecutor(
Simon Huntffbad3b2017-05-16 15:37:51 -0700226 groupedThreads("onos/device", "manager-background", log));
Madan Jampanide003d92015-05-11 17:14:20 -0700227 localNodeId = clusterService.getLocalNode().id();
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800228
tomf80c9722014-09-24 14:49:18 -0700229 store.setDelegate(delegate);
tom96dfcab2014-08-28 09:26:03 -0700230 eventDispatcher.addSink(DeviceEvent.class, listenerRegistry);
tomb41d1ac2014-09-24 01:51:24 -0700231 mastershipService.addListener(mastershipListener);
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700232 networkConfigService.addListener(networkConfigListener);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800233
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700234 backgroundService.scheduleWithFixedDelay(() -> {
235 try {
236 mastershipCheck();
237 } catch (Exception e) {
pierventre00ac2502021-12-02 09:42:28 +0100238 log.error("Exception thrown during mastership integrity check", e);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800239 }
240 }, 1, 1, TimeUnit.MINUTES);
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700241
pierventreb2f636b2022-01-03 17:19:24 +0100242 clusterRequestExecutor = newSingleThreadExecutor();
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700243
pierventreb2f636b2022-01-03 17:19:24 +0100244 communicationService.addSubscriber(
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700245 PORT_UPDOWN_SUBJECT,
246 SERIALIZER::decode,
247 this::handlePortRequest,
pierventreb2f636b2022-01-03 17:19:24 +0100248 clusterRequestExecutor);
249
250 communicationService.addSubscriber(
251 PROBE_SUBJECT,
252 SERIALIZER::decode,
253 this::handleProbeRequest,
254 SERIALIZER::encode,
255 clusterRequestExecutor);
pierventre00ac2502021-12-02 09:42:28 +0100256
257 backgroundRoleChecker = newSingleThreadScheduledExecutor(
258 groupedThreads("onos/device", "manager-role", log));
259 backgroundRoleChecker.scheduleAtFixedRate(() -> {
260 try {
261 roleCheck();
262 } catch (Exception e) {
263 log.error("Exception thrown while verifying role acknowledgement from all devices", e);
264 }
265 }, 0, 10, TimeUnit.SECONDS);
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700266
tomd3097b02014-08-26 10:40:29 -0700267 log.info("Started");
268 }
269
270 @Deactivate
271 public void deactivate() {
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800272 backgroundService.shutdown();
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700273 networkConfigService.removeListener(networkConfigListener);
tomf80c9722014-09-24 14:49:18 -0700274 store.unsetDelegate(delegate);
tomb41d1ac2014-09-24 01:51:24 -0700275 mastershipService.removeListener(mastershipListener);
tom5f38b3a2014-08-27 23:50:54 -0700276 eventDispatcher.removeSink(DeviceEvent.class);
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700277 communicationService.removeSubscriber(PORT_UPDOWN_SUBJECT);
pierventreb2f636b2022-01-03 17:19:24 +0100278 clusterRequestExecutor.shutdown();
pierventre00ac2502021-12-02 09:42:28 +0100279 backgroundRoleChecker.shutdown();
tomd3097b02014-08-26 10:40:29 -0700280 log.info("Stopped");
281 }
282
283 @Override
tomad2d2092014-09-06 23:24:20 -0700284 public int getDeviceCount() {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900285 checkPermission(DEVICE_READ);
tomad2d2092014-09-06 23:24:20 -0700286 return store.getDeviceCount();
tomd3097b02014-08-26 10:40:29 -0700287 }
288
289 @Override
mskala32000d32017-07-14 16:27:06 +0200290 public int getAvailableDeviceCount() {
291 checkPermission(DEVICE_READ);
292 return store.getAvailableDeviceCount();
293 }
294
295 @Override
tom32f66842014-08-27 19:27:47 -0700296 public Iterable<Device> getDevices() {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900297 checkPermission(DEVICE_READ);
tome5ec3fd2014-09-04 15:18:06 -0700298 return store.getDevices();
tomd3097b02014-08-26 10:40:29 -0700299 }
300
tom32f66842014-08-27 19:27:47 -0700301 @Override
Yuta HIGUCHIf1f2ac02014-11-26 14:02:22 -0800302 public Iterable<Device> getAvailableDevices() {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900303 checkPermission(DEVICE_READ);
Yuta HIGUCHIf1f2ac02014-11-26 14:02:22 -0800304 return store.getAvailableDevices();
305 }
306
307 @Override
tom32f66842014-08-27 19:27:47 -0700308 public Device getDevice(DeviceId deviceId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900309 checkPermission(DEVICE_READ);
tom32f66842014-08-27 19:27:47 -0700310 checkNotNull(deviceId, DEVICE_ID_NULL);
tom132b58a2014-08-28 16:11:28 -0700311 return store.getDevice(deviceId);
tom32f66842014-08-27 19:27:47 -0700312 }
313
314 @Override
tomad2d2092014-09-06 23:24:20 -0700315 public MastershipRole getRole(DeviceId deviceId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900316 checkPermission(DEVICE_READ);
tomad2d2092014-09-06 23:24:20 -0700317 checkNotNull(deviceId, DEVICE_ID_NULL);
tomb41d1ac2014-09-24 01:51:24 -0700318 return mastershipService.getLocalRole(deviceId);
tomad2d2092014-09-06 23:24:20 -0700319 }
320
321 @Override
tom32f66842014-08-27 19:27:47 -0700322 public List<Port> getPorts(DeviceId deviceId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900323 checkPermission(DEVICE_READ);
tom32f66842014-08-27 19:27:47 -0700324 checkNotNull(deviceId, DEVICE_ID_NULL);
tom132b58a2014-08-28 16:11:28 -0700325 return store.getPorts(deviceId);
tom32f66842014-08-27 19:27:47 -0700326 }
327
328 @Override
sangho538108b2015-04-08 14:29:20 -0700329 public List<PortStatistics> getPortStatistics(DeviceId deviceId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900330 checkPermission(DEVICE_READ);
sangho538108b2015-04-08 14:29:20 -0700331 checkNotNull(deviceId, DEVICE_ID_NULL);
332 return store.getPortStatistics(deviceId);
333 }
334
335 @Override
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200336 public List<PortStatistics> getPortDeltaStatistics(DeviceId deviceId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900337 checkPermission(DEVICE_READ);
Dusan Pajin11ff4a82015-08-20 18:03:05 +0200338 checkNotNull(deviceId, DEVICE_ID_NULL);
339 return store.getPortDeltaStatistics(deviceId);
340 }
341
342 @Override
Viswanath KSP22774cd2016-08-20 20:06:30 +0530343 public PortStatistics getStatisticsForPort(DeviceId deviceId, PortNumber portNumber) {
344 checkPermission(DEVICE_READ);
345 checkNotNull(deviceId, DEVICE_ID_NULL);
346 checkNotNull(portNumber, PORT_NUMBER_NULL);
347 return store.getStatisticsForPort(deviceId, portNumber);
348 }
349
350 @Override
351 public PortStatistics getDeltaStatisticsForPort(DeviceId deviceId, PortNumber portNumber) {
352 checkPermission(DEVICE_READ);
353 checkNotNull(deviceId, DEVICE_ID_NULL);
354 checkNotNull(portNumber, PORT_NUMBER_NULL);
355 return store.getDeltaStatisticsForPort(deviceId, portNumber);
356 }
357
358 @Override
tom32f66842014-08-27 19:27:47 -0700359 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900360 checkPermission(DEVICE_READ);
tom32f66842014-08-27 19:27:47 -0700361 checkNotNull(deviceId, DEVICE_ID_NULL);
362 checkNotNull(portNumber, PORT_NUMBER_NULL);
tom132b58a2014-08-28 16:11:28 -0700363 return store.getPort(deviceId, portNumber);
tom32f66842014-08-27 19:27:47 -0700364 }
365
366 @Override
tomff7eb7c2014-09-08 12:49:03 -0700367 public boolean isAvailable(DeviceId deviceId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900368 checkPermission(DEVICE_READ);
Changhoon Yoon541ef712015-05-23 17:18:34 +0900369
tomff7eb7c2014-09-08 12:49:03 -0700370 checkNotNull(deviceId, DEVICE_ID_NULL);
371 return store.isAvailable(deviceId);
372 }
373
Saurav Dasd5ec9e92017-01-17 10:40:18 -0800374 @Override
375 public String localStatus(DeviceId deviceId) {
376 LocalStatus ls = deviceLocalStatus.get(deviceId);
377 if (ls == null) {
378 return "No Record";
379 }
Yuta HIGUCHI0c47d532017-08-18 23:16:35 -0700380 String timeAgo = Tools.timeAgo(ls.dateTime.toEpochMilli());
Saurav Dasd5ec9e92017-01-17 10:40:18 -0800381 return (ls.connected) ? "connected " + timeAgo : "disconnected " + timeAgo;
382 }
383
Palash Kala0d817b02018-03-23 18:09:45 +0900384 private boolean isLocallyConnected(DeviceId deviceId) {
385 LocalStatus ls = deviceLocalStatus.get(deviceId);
386 if (ls == null) {
387 return false;
388 }
389 return ls.connected;
390 }
391
Ray Milkey054e23d2018-03-22 13:37:11 -0700392 @Override
393 public long getLastUpdatedInstant(DeviceId deviceId) {
394 LocalStatus ls = deviceLocalStatus.get(deviceId);
395 if (ls == null) {
396 return 0;
397 }
398 return ls.dateTime.toEpochMilli();
399 }
400
Palash Kala0d817b02018-03-23 18:09:45 +0900401 // Check a device for control channel connectivity
402 // and changes local-status appropriately.
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700403 private boolean isReachable(DeviceId deviceId) {
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -0800404 if (deviceId == null) {
405 return false;
406 }
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700407 DeviceProvider provider = getProvider(deviceId);
408 if (provider != null) {
pierventreb2f636b2022-01-03 17:19:24 +0100409 boolean reachable = probeReachability(deviceId);
Palash Kala0d817b02018-03-23 18:09:45 +0900410 if (reachable && !isLocallyConnected(deviceId)) {
411 deviceLocalStatus.put(deviceId, new LocalStatus(true, Instant.now()));
412 } else if (!reachable && isLocallyConnected(deviceId)) {
413 deviceLocalStatus.put(deviceId, new LocalStatus(false, Instant.now()));
414 }
415 return reachable;
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700416 } else {
Yuta HIGUCHI72669c42014-11-13 14:48:17 -0800417 log.debug("Provider not found for {}", deviceId);
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700418 return false;
419 }
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700420 }
421
tome5ec3fd2014-09-04 15:18:06 -0700422 @Override
423 public void removeDevice(DeviceId deviceId) {
424 checkNotNull(deviceId, DEVICE_ID_NULL);
425 DeviceEvent event = store.removeDevice(deviceId);
tom0efbb1d2014-09-09 11:54:28 -0700426 if (event != null) {
427 log.info("Device {} administratively removed", deviceId);
tom0efbb1d2014-09-09 11:54:28 -0700428 }
tome5ec3fd2014-09-04 15:18:06 -0700429 }
430
Thomas Vachuska811ea2b2020-02-11 10:20:10 -0800431 @Override
432 public void removeDevicePorts(DeviceId deviceId) {
433 checkNotNull(deviceId, DEVICE_ID_NULL);
434 if (isAvailable(deviceId)) {
435 log.debug("Cannot remove ports of device {} while it is available.", deviceId);
436 return;
437 }
438
439 List<PortDescription> portDescriptions = ImmutableList.of();
440 List<DeviceEvent> events = store.updatePorts(getProvider(deviceId).id(),
441 deviceId, portDescriptions);
442 if (events != null) {
443 for (DeviceEvent event : events) {
444 post(event);
445 }
446 }
447 }
448
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700449 private void handlePortRequest(InternalPortUpDownEvent event) {
450 DeviceId deviceId = event.deviceId();
Saurav Dasa2d37502016-03-25 17:50:40 -0700451 checkNotNull(deviceId, DEVICE_ID_NULL);
Sean Condon0bd777c2021-01-01 14:23:29 +0000452 checkNotNull(event.portNumber(), PORT_NUMBER_NULL);
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700453 checkState(mastershipService.isLocalMaster(deviceId), EVENT_NON_MASTER);
454 changePortStateAtMaster(event.deviceId(), event.portNumber(), event.isEnable());
455 }
456
457 private void changePortStateAtMaster(DeviceId deviceId, PortNumber portNumber,
458 boolean enable) {
Saurav Dasa2d37502016-03-25 17:50:40 -0700459 DeviceProvider provider = getProvider(deviceId);
460 if (provider != null) {
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700461 log.info("Port {} on device {} being administratively brought {}",
Saurav Dasa2d37502016-03-25 17:50:40 -0700462 portNumber, deviceId,
463 (enable) ? "UP" : "DOWN");
464 provider.changePortState(deviceId, portNumber, enable);
465 } else {
466 log.warn("Provider not found for {}", deviceId);
467 }
468 }
469
470 @Override
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700471 public void changePortState(DeviceId deviceId, PortNumber portNumber,
472 boolean enable) {
473 checkNotNull(deviceId, DEVICE_ID_NULL);
Sean Condon0bd777c2021-01-01 14:23:29 +0000474 checkNotNull(portNumber, PORT_NUMBER_NULL);
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700475 NodeId masterId = mastershipService.getMasterFor(deviceId);
476
Ray Milkey4ef245e2018-05-10 15:41:16 -0700477 if (masterId == null) {
478 // No master found; device is offline
479 log.info("No master found for port state change for {}", deviceId);
480 return;
481 }
482
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -0700483 if (!masterId.equals(localNodeId)) {
484 //Send the request to the master node for the device
485 log.info("Device {} is managed by {}, forwarding the request to the MASTER",
486 deviceId, masterId);
487 communicationService.unicast(
488 new InternalPortUpDownEvent(deviceId, portNumber, enable),
489 PORT_UPDOWN_SUBJECT,
490 SERIALIZER::encode,
491 masterId).whenComplete((r, error) -> {
492 if (error != null) {
493 log.warn("Failed to send packet-updown-req to {}", masterId, error);
494 }
495 });
496 } else {
497 changePortStateAtMaster(deviceId, portNumber, enable);
498 }
499 }
500
501 @Override
samuele1fa7322015-07-14 16:35:16 +0800502 protected DeviceProviderService createProviderService(
503 DeviceProvider provider) {
tom7869ad92014-09-09 14:32:08 -0700504 return new InternalDeviceProviderService(provider);
505 }
506
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800507 /**
508 * Checks if all the reachable devices have a valid mastership role.
509 */
510 private void mastershipCheck() {
511 log.debug("Checking mastership");
512 for (Device device : getDevices()) {
513 final DeviceId deviceId = device.id();
Ray Milkeyc7104672016-08-31 12:04:34 -0700514 MastershipRole myRole = mastershipService.getLocalRole(deviceId);
515 log.trace("Checking device {}. Current role is {}", deviceId, myRole);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800516 if (!isReachable(deviceId)) {
Ray Milkeyc7104672016-08-31 12:04:34 -0700517 if (myRole != NONE) {
pierventreb2f636b2022-01-03 17:19:24 +0100518 // Verify if the device is fully disconnected from the cluster
519 if (updateMastershipFor(deviceId) == null
520 && myRole == MASTER && isAvailable(deviceId)) {
521 log.info("Local Role {}, Marking unreachable device {} offline", MASTER, deviceId);
522 post(store.markOffline(deviceId));
helenyrwufd296b62016-06-22 17:43:02 -0700523 }
Bharath Thiruveedula651a7da2016-12-13 02:52:50 +0530524 } else {
pierventreb2f636b2022-01-03 17:19:24 +0100525 /* Firstly get a role and then check if the device is available in the store.
526 if it is, if this node is the master and the device is fully disconnected
527 from the cluster mark the device offline. In principle, this condition should
528 never be hit unless in a device removed phase for NONE mastership roles. */
529 try {
530 mastershipService.requestRoleFor(deviceId).get();
531 } catch (InterruptedException e) {
532 Thread.currentThread().interrupt();
533 log.error("Interrupted waiting for Mastership", e);
534 } catch (ExecutionException e) {
535 log.error("Encountered an error waiting for Mastership", e);
536 }
537
538 MastershipTerm term = termService.getMastershipTerm(deviceId);
539 if (updateMastershipFor(deviceId) == null &&
540 term != null && localNodeId.equals(term.master()) &&
541 isAvailable(deviceId)) {
542 log.info("Marking unreachable device {} offline", deviceId);
543 post(store.markOffline(deviceId));
Jordan Haltermanbc0308f2017-11-29 15:37:34 -0800544 }
helenyrwufd296b62016-06-22 17:43:02 -0700545 }
pierventreb2f636b2022-01-03 17:19:24 +0100546 roleToAcknowledge.remove(deviceId);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800547 continue;
548 }
549
Jordan Halterman368bd3d2019-01-04 12:45:57 -0800550 // If this node is the master, ensure the device is marked online.
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800551 if (myRole == MASTER && canMarkOnline(device)) {
Jordan Halterman368bd3d2019-01-04 12:45:57 -0800552 post(store.markOnline(deviceId));
553 }
554
pierventreb2f636b2022-01-03 17:19:24 +0100555 log.info("{} is reachable - reasserting the role", deviceId);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800556
pierventreb2f636b2022-01-03 17:19:24 +0100557 /* Device is still reachable. It is useful for some protocols
558 to reassert the role. Note: NONE triggers request to MastershipService */
559 reassertRole(deviceId, myRole);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800560 }
561 }
562
pierventre00ac2502021-12-02 09:42:28 +0100563 /**
564 * Checks if all the devices have acknowledged the mastership role.
565 */
566 private void roleCheck() {
567 log.debug("Checking role");
568 for (Device device : getDevices()) {
569 final DeviceId deviceId = device.id();
570 MastershipRole myRole = mastershipService.getLocalRole(deviceId);
571 log.trace("Checking device {}. Current role is {}", deviceId, myRole);
572 final AtomicBoolean exists = new AtomicBoolean(false);
573 final Long ts = roleToAcknowledge.compute(deviceId, (key, value) -> {
574 if (value == null) {
575 return null;
576 }
577 exists.set(true);
578 if (currentTimeMillis() - value < (ROLE_TIMEOUT_SECONDS * 1000)) {
579 return value;
580 }
581 return null;
582 });
583 // Nobody applied the role recently
584 if (!exists.get()) {
585 log.trace("Role was not applied or it has been acknowledged for device {}", deviceId);
586 continue;
587 }
588 // Timeout still on
589 if (ts != null) {
590 log.debug("Timeout expires in {} ms", ((ROLE_TIMEOUT_SECONDS * 1000) - currentTimeMillis() + ts));
591 continue;
592 }
593 if (myRole != MASTER) {
594 log.debug("Timeout is expired but current role is not MASTER ({}), nothing to do", myRole);
595 continue;
596 }
597 /* Switch failed to acknowledge master role we asked for.
598 Yield mastership to other instance*/
599 log.warn("Failed to assert role onto device {}. requested={}, no response",
600 deviceId, myRole);
pierventreb2f636b2022-01-03 17:19:24 +0100601 updateMastershipFor(deviceId);
pierventre00ac2502021-12-02 09:42:28 +0100602 }
603 }
604
tomd3097b02014-08-26 10:40:29 -0700605 // Personalized device provider service issued to the supplied provider.
tomdc361b62014-09-09 20:36:52 -0700606 private class InternalDeviceProviderService
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700607 extends AbstractProviderService<DeviceProvider>
608 implements DeviceProviderService {
tomd3097b02014-08-26 10:40:29 -0700609
tomcfde0622014-09-09 11:02:42 -0700610 InternalDeviceProviderService(DeviceProvider provider) {
tomd3097b02014-08-26 10:40:29 -0700611 super(provider);
612 }
613
Ray Milkey33306ba2018-09-20 13:27:25 -0700614 /**
615 * Apply role in reaction to provider event.
616 *
617 * @param deviceId device identifier
618 * @param newRole new role to apply to the device
619 * @return true if the request was sent to provider
620 */
621 private boolean applyRole(DeviceId deviceId, MastershipRole newRole) {
622
623 if (newRole.equals(MastershipRole.NONE)) {
624 //no-op
625 return true;
626 }
627
628 DeviceProvider provider = provider();
629 if (provider == null) {
630 log.warn("Provider for {} was not found. Cannot apply role {}",
631 deviceId, newRole);
632 return false;
633 }
pierventre00ac2502021-12-02 09:42:28 +0100634 // Start the timer
635 roleToAcknowledge.put(deviceId, currentTimeMillis());
Ray Milkey33306ba2018-09-20 13:27:25 -0700636 provider.roleChanged(deviceId, newRole);
637 // not triggering probe when triggered by provider service event
638 return true;
639 }
640
tomd3097b02014-08-26 10:40:29 -0700641 @Override
alshabibb7b40632014-09-28 21:30:00 -0700642 public void deviceConnected(DeviceId deviceId,
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700643 DeviceDescription deviceDescription) {
tom32f66842014-08-27 19:27:47 -0700644 checkNotNull(deviceId, DEVICE_ID_NULL);
645 checkNotNull(deviceDescription, DEVICE_DESCRIPTION_NULL);
tomeadbb462014-09-07 16:10:19 -0700646 checkValidity();
Yuta HIGUCHI24b2e2a2014-10-07 15:53:57 -0700647
Yuta HIGUCHI0c47d532017-08-18 23:16:35 -0700648 deviceLocalStatus.put(deviceId, new LocalStatus(true, Instant.now()));
Saurav Dasd5ec9e92017-01-17 10:40:18 -0800649
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -0700650 BasicDeviceConfig cfg = networkConfigService.getConfig(deviceId, BasicDeviceConfig.class);
651 if (!isAllowed(cfg)) {
652 log.warn("Device {} is not allowed", deviceId);
653 return;
654 }
Andrea Campanella75ef9f52017-07-27 20:14:32 +0200655 PortDescriptionsConfig portConfig = networkConfigService.getConfig(deviceId, PortDescriptionsConfig.class);
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -0700656 // Generate updated description and establish my Role
657 deviceDescription = BasicDeviceOperator.combine(cfg, deviceDescription);
jaegonkim1c1f0c22018-01-28 22:20:42 +0900658 DeviceAnnotationConfig annoConfig = networkConfigService.getConfig(deviceId, DeviceAnnotationConfig.class);
659 if (annoConfig != null) {
660 deviceDescription = deviceAnnotationOp.combine(deviceId, deviceDescription, Optional.of(annoConfig));
661 }
662
pier388ec252020-04-15 20:53:14 +0200663 // Wait for the end of the election. sync call of requestRoleFor
664 // wait only 3s and it is not entirely safe since the leadership
665 // election timer can be higher.
666 MastershipRole role = Futures.getUnchecked(mastershipService.requestRoleFor(deviceId));
jaegonkim8eabcec2018-07-27 23:40:43 +0900667 log.info("Local role is {} for {}", role, deviceId);
pier388ec252020-04-15 20:53:14 +0200668 store.createOrUpdateDevice(provider().id(), deviceId, deviceDescription);
Ray Milkey33306ba2018-09-20 13:27:25 -0700669 applyRole(deviceId, role);
HIGUCHI Yuta11530fb2015-05-27 13:10:20 -0700670
Andrea Campanella75ef9f52017-07-27 20:14:32 +0200671 if (portConfig != null) {
672 //updating the ports if configration exists
673 List<PortDescription> complete = store.getPortDescriptions(provider().id(), deviceId)
674 .collect(Collectors.toList());
675 complete.addAll(portConfig.portDescriptions());
676 List<PortDescription> portDescriptions = complete.stream()
677 .map(e -> applyAllPortOps(deviceId, e))
678 .collect(Collectors.toList());
679 store.updatePorts(provider().id(), deviceId, portDescriptions);
680 }
681
helenyrwufd296b62016-06-22 17:43:02 -0700682 if (deviceDescription.isDefaultAvailable()) {
683 log.info("Device {} connected", deviceId);
684 } else {
685 log.info("Device {} registered", deviceId);
686 }
tomd3097b02014-08-26 10:40:29 -0700687 }
688
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -0700689 private PortDescription ensurePortEnabledState(PortDescription desc, boolean enabled) {
690 if (desc.isEnabled() != enabled) {
Yuta HIGUCHI2b1935d2018-03-01 21:41:06 -0800691 return DefaultPortDescription.builder(desc)
692 .isEnabled(enabled)
693 .build();
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -0700694 }
695 return desc;
696 }
697
tomd3097b02014-08-26 10:40:29 -0700698 @Override
699 public void deviceDisconnected(DeviceId deviceId) {
tom32f66842014-08-27 19:27:47 -0700700 checkNotNull(deviceId, DEVICE_ID_NULL);
tomeadbb462014-09-07 16:10:19 -0700701 checkValidity();
pierventreb2f636b2022-01-03 17:19:24 +0100702 // Update the local status
Yuta HIGUCHI0c47d532017-08-18 23:16:35 -0700703 deviceLocalStatus.put(deviceId, new LocalStatus(false, Instant.now()));
Andrea Campanellab0b93ac2021-09-13 12:37:36 +0200704 log.info("Device {} disconnected from this node: {}", deviceId,
705 clusterService.getLocalNode().id());
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700706
pierventreb2f636b2022-01-03 17:19:24 +0100707 /* If none can reach the device, we will continue with the disconnection logic.
708 If there is one instance that reported device is still reachable, we hand over
709 the mastership to it if we are the current master, otherwise if we are a backup
710 we demote ourselves to the bottom of the backups list */
711 if (updateMastershipFor(deviceId) == null) {
712 log.info("Device {} is fully disconnected from the cluster", deviceId);
713 List<PortDescription> descs = store.getPortDescriptions(provider().id(), deviceId)
714 .map(desc -> ensurePortEnabledState(desc, false))
715 .collect(Collectors.toList());
716 store.updatePorts(this.provider().id(), deviceId, descs);
Yafit Hadara9a73de2015-09-06 13:52:52 +0300717
Madan Jampanic6e574f2015-05-29 13:41:52 -0700718 try {
pierventreb2f636b2022-01-03 17:19:24 +0100719 if (mastershipService.isLocalMaster(deviceId)) {
720 post(store.markOffline(deviceId));
721 }
722 } catch (IllegalStateException e) {
723 log.warn("Failed to mark {} offline", deviceId);
724 // only the MASTER should be marking off-line in normal cases,
725 // but if I was the last STANDBY connection, etc. and no one else
726 // was there to mark the device offline, this instance may need to
727 // temporarily request for Master Role and mark offline.
728
729 //there are times when this node will correctly have mastership, BUT
730 //that isn't reflected in the ClockManager before the device disconnects.
731 //we want to let go of the device anyways, so make sure this happens.
732
733 // FIXME: Store semantics leaking out as IllegalStateException.
734 // Consider revising store API to handle this scenario.
735 CompletableFuture<MastershipRole> roleFuture = mastershipService.requestRoleFor(deviceId);
736 roleFuture.whenComplete((role, error) -> {
737 MastershipTerm term = termService.getMastershipTerm(deviceId);
738 // TODO: Move this type of check inside device clock manager, etc.
739 if (term != null && localNodeId.equals(term.master())) {
740 log.info("Retry marking {} offline", deviceId);
741 post(store.markOffline(deviceId));
742 } else {
743 log.info("Failed again marking {} offline. {}", deviceId, role);
744 }
745 });
746 } finally {
pierventre00ac2502021-12-02 09:42:28 +0100747 roleToAcknowledge.remove(deviceId);
Madan Jampanic6e574f2015-05-29 13:41:52 -0700748 }
tom0efbb1d2014-09-09 11:54:28 -0700749 }
pierventreb2f636b2022-01-03 17:19:24 +0100750
tomd3097b02014-08-26 10:40:29 -0700751 }
752
753 @Override
alshabibb7b40632014-09-28 21:30:00 -0700754 public void updatePorts(DeviceId deviceId,
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700755 List<PortDescription> portDescriptions) {
tom32f66842014-08-27 19:27:47 -0700756 checkNotNull(deviceId, DEVICE_ID_NULL);
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -0700757 checkNotNull(portDescriptions, PORT_DESC_LIST_NULL);
tomeadbb462014-09-07 16:10:19 -0700758 checkValidity();
Madan Jampani565a66a2015-07-25 17:01:13 -0700759 if (!mastershipService.isLocalMaster(deviceId)) {
Yuta HIGUCHI13c0b872014-10-30 18:09:22 -0700760 // Never been a master for this device
761 // any update will be ignored.
samuele1fa7322015-07-14 16:35:16 +0800762 log.trace("Ignoring {} port updates on standby node. {}", deviceId, portDescriptions);
Yuta HIGUCHI13c0b872014-10-30 18:09:22 -0700763 return;
764 }
Andrea Campanella75ef9f52017-07-27 20:14:32 +0200765 PortDescriptionsConfig portConfig = networkConfigService.getConfig(deviceId, PortDescriptionsConfig.class);
766 if (portConfig != null) {
Carmelo Cascone1da7a4d2018-06-27 18:03:11 +0200767 // Updating the ports if configuration exists (on new lists as
768 // the passed one might be immutable)
769 portDescriptions = Lists.newArrayList(portDescriptions);
Andrea Campanella75ef9f52017-07-27 20:14:32 +0200770 portDescriptions.addAll(portConfig.portDescriptions());
771 }
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -0700772 portDescriptions = portDescriptions.stream()
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -0700773 .map(e -> applyAllPortOps(deviceId, e))
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -0700774 .collect(Collectors.toList());
Yuta HIGUCHI5f6739c2014-10-01 14:04:01 -0700775 List<DeviceEvent> events = store.updatePorts(this.provider().id(),
samuele1fa7322015-07-14 16:35:16 +0800776 deviceId, portDescriptions);
Thomas Vachuska5923b9a2016-01-26 10:52:57 -0800777 if (events != null) {
778 for (DeviceEvent event : events) {
779 post(event);
780 }
tom32f66842014-08-27 19:27:47 -0700781 }
tomd3097b02014-08-26 10:40:29 -0700782 }
783
784 @Override
alshabibb7b40632014-09-28 21:30:00 -0700785 public void portStatusChanged(DeviceId deviceId,
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700786 PortDescription portDescription) {
tom32f66842014-08-27 19:27:47 -0700787 checkNotNull(deviceId, DEVICE_ID_NULL);
788 checkNotNull(portDescription, PORT_DESCRIPTION_NULL);
tomeadbb462014-09-07 16:10:19 -0700789 checkValidity();
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700790
Madan Jampani565a66a2015-07-25 17:01:13 -0700791 if (!mastershipService.isLocalMaster(deviceId)) {
Yuta HIGUCHI13c0b872014-10-30 18:09:22 -0700792 // Never been a master for this device
793 // any update will be ignored.
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700794 log.trace("Ignoring {} port update on standby node. {}", deviceId,
795 portDescription);
Yuta HIGUCHI13c0b872014-10-30 18:09:22 -0700796 return;
797 }
Ray Milkey9ef22232016-07-14 12:42:37 -0700798 Device device = getDevice(deviceId);
799 if (device == null) {
800 log.trace("Device not found: {}", deviceId);
nitinanandddfa8c92017-03-24 16:14:23 +0530801 return;
Ray Milkey9ef22232016-07-14 12:42:37 -0700802 }
Andrea Campanella1c24fb92018-12-20 16:43:59 +0100803 if ((Type.ROADM.equals(device.type())) || (Type.OTN.equals(device.type())) ||
804 (Type.OLS.equals(device.type())) || (Type.TERMINAL_DEVICE.equals(device.type()))) {
HIGUCHI Yuta6972ae62016-05-12 19:57:46 -0700805 // FIXME This is ignoring all other info in portDescription given as input??
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -0700806 PortDescription storedPortDesc = store.getPortDescription(provider().id(),
Simon Huntffbad3b2017-05-16 15:37:51 -0700807 deviceId,
808 portDescription.portNumber());
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -0700809 portDescription = ensurePortEnabledState(storedPortDesc,
810 portDescription.isEnabled());
Yafit Hadara9a73de2015-09-06 13:52:52 +0300811 }
812
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -0700813 portDescription = applyAllPortOps(deviceId, portDescription);
samuele1fa7322015-07-14 16:35:16 +0800814 final DeviceEvent event = store.updatePortStatus(this.provider().id(),
HIGUCHI Yuta6972ae62016-05-12 19:57:46 -0700815 deviceId,
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -0700816 portDescription);
tomff7eb7c2014-09-08 12:49:03 -0700817 if (event != null) {
Carmelo Casconeab5d41e2019-03-06 18:02:34 -0800818 log.info("Device {} port {} status changed (enabled={})",
819 deviceId, event.port().number(), portDescription.isEnabled());
tom0efbb1d2014-09-09 11:54:28 -0700820 post(event);
tomff7eb7c2014-09-08 12:49:03 -0700821 }
tomd3097b02014-08-26 10:40:29 -0700822 }
tom3f2bbd72014-09-24 12:07:58 -0700823
824 @Override
Michal Machce774332017-01-25 11:02:55 +0100825 public void deletePort(DeviceId deviceId, PortDescription basePortDescription) {
826
827 checkNotNull(deviceId, DEVICE_ID_NULL);
828 checkNotNull(basePortDescription, PORT_DESCRIPTION_NULL);
829 checkValidity();
830
831 if (!mastershipService.isLocalMaster(deviceId)) {
832 // Never been a master for this device
833 // any update will be ignored.
834 log.trace("Ignoring {} port update on standby node. {}", deviceId,
835 basePortDescription);
836 return;
837 }
838
839 Device device = getDevice(deviceId);
840 if (device == null) {
841 log.trace("Device not found: {}", deviceId);
842 }
843
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800844 PortDescription newPortDescription = DefaultPortDescription.builder(basePortDescription)
845 .isRemoved(true)
846 .build();
847
Michal Machce774332017-01-25 11:02:55 +0100848 final DeviceEvent event = store.updatePortStatus(this.provider().id(),
849 deviceId,
850 newPortDescription);
851 if (event != null) {
852 log.info("Device {} port {} status changed", deviceId, event.port().number());
853 post(event);
854 }
855 }
856
857 @Override
samuele1fa7322015-07-14 16:35:16 +0800858 public void receivedRoleReply(DeviceId deviceId, MastershipRole requested,
Thomas Vachuskab17c41f2015-05-19 11:16:05 -0700859 MastershipRole response) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700860 // Several things can happen here:
861 // 1. request and response match
862 // 2. request and response don't match
863 // 3. MastershipRole and requested match (and 1 or 2 are true)
864 // 4. MastershipRole and requested don't match (and 1 or 2 are true)
865 //
866 // 2, 4, and 3 with case 2 are failure modes.
867
tom3f2bbd72014-09-24 12:07:58 -0700868 // FIXME: implement response to this notification
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700869
Madan Jampanif2af7712015-05-29 18:43:52 -0700870 log.debug("got reply to a role request for {}: asked for {}, and got {}",
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700871 deviceId, requested, response);
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700872
873 if (requested == null && response == null) {
samuele1fa7322015-07-14 16:35:16 +0800874 // something was off with DeviceProvider, maybe check channel too?
Shashikanth VH387a1ca2016-02-09 20:35:21 +0530875 log.warn("Failed to assert role onto Device {}", deviceId);
pierventre00ac2502021-12-02 09:42:28 +0100876 roleToAcknowledge.remove(deviceId);
pierventreb2f636b2022-01-03 17:19:24 +0100877 updateMastershipFor(deviceId);
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700878 return;
Yuta HIGUCHIcf603902014-10-07 23:04:32 -0700879 }
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700880
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800881 final MastershipRole expected = mastershipService.getLocalRole(deviceId);
882
883 if (requested == null) {
884 // Provider is not able to reconcile role responses with
885 // requests. We assume what was requested is what we expect.
886 // This will work only if mastership doesn't change too often,
887 // and devices are left enough time to provide responses before
888 // a different role is requested.
889 requested = expected;
890 }
891
Thomas Vachuskab17c41f2015-05-19 11:16:05 -0700892 if (Objects.equals(requested, response)) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800893 if (Objects.equals(requested, expected)) {
pierventre00ac2502021-12-02 09:42:28 +0100894 // Stop the timer
895 log.info("Role has been acknowledged for device {}", deviceId);
896 roleToAcknowledge.remove(deviceId);
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700897 } else {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800898 log.warn("Role mismatch on {}. Set to {}, but store demands {}",
899 deviceId, response, expected);
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800900 // roleManager got the device to comply, but doesn't agree with
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700901 // the store; use the store's view, then try to reassert.
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800902 backgroundService.execute(() -> reassertRole(deviceId, expected));
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700903 }
904 } else {
905 // we didn't get back what we asked for. Reelect someone else.
Carmelo Casconee5b28722018-06-22 17:28:28 +0200906 log.warn("Failed to assert role onto device {}. requested={}, response={}",
907 deviceId, requested, response);
Carmelo Casconeb37bdde2018-06-22 17:25:46 +0200908 if (requested == MastershipRole.MASTER) {
pierventre00ac2502021-12-02 09:42:28 +0100909 // Stop the timer
910 roleToAcknowledge.remove(deviceId);
pierventreb2f636b2022-01-03 17:19:24 +0100911 updateMastershipFor(deviceId);
Charles Chan3855dc12021-01-06 01:33:03 +0000912 } else if (requested == MastershipRole.STANDBY) {
913 // For P4RT devices, the response role will be NONE when this node is expected to be STANDBY
914 // but the stream channel is not opened correctly.
915 // Calling reassertRole will trigger the mechanism in GeneralDeviceProvider that
916 // attempts to re-establish the stream channel
917 backgroundService.execute(() -> reassertRole(deviceId, expected));
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700918 }
919 }
tom3f2bbd72014-09-24 12:07:58 -0700920 }
sangho538108b2015-04-08 14:29:20 -0700921
922 @Override
samuele1fa7322015-07-14 16:35:16 +0800923 public void updatePortStatistics(DeviceId deviceId, Collection<PortStatistics> portStatistics) {
sangho538108b2015-04-08 14:29:20 -0700924 checkNotNull(deviceId, DEVICE_ID_NULL);
Thomas Vachuskab17c41f2015-05-19 11:16:05 -0700925 checkNotNull(portStatistics, "Port statistics list cannot be null");
sangho538108b2015-04-08 14:29:20 -0700926 checkValidity();
927
samuele1fa7322015-07-14 16:35:16 +0800928 DeviceEvent event = store.updatePortStatistics(this.provider().id(),
929 deviceId, portStatistics);
sangho538108b2015-04-08 14:29:20 -0700930 post(event);
931 }
pierventree1f80102021-10-01 22:01:22 +0200932
933 @Override
934 public DeviceDescription getDeviceDescription(DeviceId deviceId) {
935 checkNotNull(deviceId, DEVICE_ID_NULL);
936 checkValidity();
937
938 return store.getDeviceDescription(provider().id(), deviceId);
939 }
tomd3097b02014-08-26 10:40:29 -0700940 }
tom32f66842014-08-27 19:27:47 -0700941
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -0700942 // by default allowed, otherwise check flag
943 private boolean isAllowed(BasicDeviceConfig cfg) {
944 return (cfg == null || cfg.isAllowed());
945 }
946
Carmelo Cascone0761cd32018-08-29 19:22:50 -0700947 private boolean canMarkOnline(Device device) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800948 DeviceProvider provider = getProvider(device.id());
949 if (provider == null) {
950 log.warn("Provider for {} was not found. Cannot evaluate availability", device.id());
951 return false;
952 }
953 return provider.isAvailable(device.id());
Carmelo Cascone0761cd32018-08-29 19:22:50 -0700954 }
955
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800956 // Applies the specified role to the device; ignores NONE
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700957
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800958 /**
959 * Apply role to device and send probe if MASTER.
960 *
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700961 * @param deviceId device identifier
962 * @param newRole new role to apply to the device
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800963 * @return true if the request was sent to provider
964 */
965 private boolean applyRoleAndProbe(DeviceId deviceId, MastershipRole newRole) {
966 if (newRole.equals(MastershipRole.NONE)) {
samuele1fa7322015-07-14 16:35:16 +0800967 //no-op
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700968 return true;
969 }
Ayaka Koshibe317245a2014-10-29 00:34:43 -0700970
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -0800971 DeviceProvider provider = getProvider(deviceId);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800972 if (provider == null) {
samuele1fa7322015-07-14 16:35:16 +0800973 log.warn("Provider for {} was not found. Cannot apply role {}", deviceId, newRole);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800974 return false;
975 }
pierventre00ac2502021-12-02 09:42:28 +0100976 // Start the timer
977 roleToAcknowledge.put(deviceId, currentTimeMillis());
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800978 provider.roleChanged(deviceId, newRole);
979
980 if (newRole.equals(MastershipRole.MASTER)) {
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800981 log.debug("sent TriggerProbe({})", deviceId);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800982 // only trigger event when request was sent to provider
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -0800983 provider.triggerProbe(deviceId);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800984 }
985 return true;
986 }
987
pierventreb2f636b2022-01-03 17:19:24 +0100988 private boolean probeReachability(DeviceId deviceId) {
989 DeviceProvider provider = getProvider(deviceId);
990 if (provider == null) {
991 log.warn("Provider for {} was not found. Cannot probe reachability", deviceId);
992 return false;
993 }
994 return provider.isReachable(deviceId) && Tools.futureGetOrElse(provider.probeReachability(deviceId),
995 PROBE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS, Boolean.FALSE);
996 }
997
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800998 /**
Harold Huangff6e1e62017-11-09 16:25:36 +0800999 * Reassert role for specified device connected to this node.
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -08001000 *
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001001 * @param did device identifier
1002 * @param nextRole role to apply. If NONE is specified,
1003 * it will ask mastership service for a role and apply it.
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -08001004 */
samuele1fa7322015-07-14 16:35:16 +08001005 private void reassertRole(final DeviceId did,
1006 final MastershipRole nextRole) {
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -08001007
Jordan Halterman9416aea2017-11-17 12:40:21 -08001008 MastershipRole myNextRole = nextRole;
1009 if (myNextRole == NONE && upgradeService.isLocalActive()) {
1010 try {
1011 mastershipService.requestRoleFor(did).get();
1012 MastershipTerm term = termService.getMastershipTerm(did);
1013 if (term != null && localNodeId.equals(term.master())) {
1014 myNextRole = MASTER;
1015 } else {
1016 myNextRole = STANDBY;
1017 }
1018 } catch (InterruptedException e) {
1019 Thread.currentThread().interrupt();
1020 log.error("Interrupted waiting for Mastership", e);
1021 } catch (ExecutionException e) {
1022 log.error("Encountered an error waiting for Mastership", e);
1023 }
1024 }
1025
1026 switch (myNextRole) {
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001027 case MASTER:
1028 final Device device = getDevice(did);
Carmelo Cascone0761cd32018-08-29 19:22:50 -07001029 if (device != null && !isAvailable(did) && canMarkOnline(device)) {
Palash Kala6c526062018-04-03 18:25:11 +09001030 post(store.markOnline(did));
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001031 }
1032 // TODO: should apply role only if there is mismatch
Jordan Halterman9416aea2017-11-17 12:40:21 -08001033 log.debug("Applying role {} to {}", myNextRole, did);
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001034 if (!applyRoleAndProbe(did, MASTER)) {
Jordan Halterman9416aea2017-11-17 12:40:21 -08001035 log.warn("Unsuccessful applying role {} to {}", myNextRole, did);
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001036 // immediately failed to apply role
pierventreb2f636b2022-01-03 17:19:24 +01001037 updateMastershipFor(did);
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001038 // FIXME disconnect?
1039 }
1040 break;
1041 case STANDBY:
Jordan Halterman9416aea2017-11-17 12:40:21 -08001042 log.debug("Applying role {} to {}", myNextRole, did);
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001043 if (!applyRoleAndProbe(did, STANDBY)) {
Jordan Halterman9416aea2017-11-17 12:40:21 -08001044 log.warn("Unsuccessful applying role {} to {}", myNextRole, did);
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001045 // immediately failed to apply role
pierventreb2f636b2022-01-03 17:19:24 +01001046 updateMastershipFor(did);
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001047 // FIXME disconnect?
1048 }
1049 break;
1050 case NONE:
Jordan Halterman980a8c12017-09-22 18:01:19 -07001051 break;
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001052 default:
1053 // should never reach here
1054 log.error("You didn't see anything. I did not exist.");
1055 break;
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -08001056 }
1057 }
1058
Madan Jampani328371d2015-05-29 14:06:27 -07001059 private void handleMastershipEvent(MastershipEvent event) {
Madan Jampani328371d2015-05-29 14:06:27 -07001060 final DeviceId did = event.subject();
1061
pierventreb2f636b2022-01-03 17:19:24 +01001062 // myNextRole suggested by MastershipService event
Madan Jampani328371d2015-05-29 14:06:27 -07001063 MastershipRole myNextRole;
Jon Hall7a8bfc62016-05-26 17:59:04 -07001064 if (event.type() == MastershipEvent.Type.SUSPENDED) {
1065 myNextRole = NONE; // FIXME STANDBY OR NONE?
1066 } else if (localNodeId.equals(event.roleInfo().master())) {
Madan Jampani328371d2015-05-29 14:06:27 -07001067 // confirm latest info
1068 MastershipTerm term = termService.getMastershipTerm(did);
samuele1fa7322015-07-14 16:35:16 +08001069 final boolean iHaveControl = term != null && localNodeId.equals(term.master());
Madan Jampani328371d2015-05-29 14:06:27 -07001070 if (iHaveControl) {
Madan Jampani328371d2015-05-29 14:06:27 -07001071 myNextRole = MASTER;
1072 } else {
1073 myNextRole = STANDBY;
1074 }
1075 } else if (event.roleInfo().backups().contains(localNodeId)) {
1076 myNextRole = STANDBY;
1077 } else {
1078 myNextRole = NONE;
1079 }
1080
Madan Jampani328371d2015-05-29 14:06:27 -07001081 final boolean isReachable = isReachable(did);
1082 if (!isReachable) {
pierventreb2f636b2022-01-03 17:19:24 +01001083 // device is not connected to this node, nevertheless we should get a role
Jon Halla90c44c2017-01-24 16:02:07 -08001084 if (mastershipService.getLocalRole(did) == NONE) {
1085 log.debug("Node was instructed to be {} role for {}, "
Simon Huntffbad3b2017-05-16 15:37:51 -07001086 + "but this node cannot reach the device "
pierventreb2f636b2022-01-03 17:19:24 +01001087 + "and role is already None. Asking a new role "
1088 + "and then apply the disconnection protocol.",
Simon Huntffbad3b2017-05-16 15:37:51 -07001089 myNextRole, did);
pierventreb2f636b2022-01-03 17:19:24 +01001090 try {
1091 mastershipService.requestRoleFor(did).get();
1092 } catch (InterruptedException e) {
1093 Thread.currentThread().interrupt();
1094 log.error("Interrupted waiting for Mastership", e);
1095 } catch (ExecutionException e) {
1096 log.error("Encountered an error waiting for Mastership", e);
1097 }
Jon Halla90c44c2017-01-24 16:02:07 -08001098 } else if (myNextRole != NONE) {
Madan Jampani328371d2015-05-29 14:06:27 -07001099 log.warn("Node was instructed to be {} role for {}, "
pierventreb2f636b2022-01-03 17:19:24 +01001100 + "but this node cannot reach the device. "
1101 + "Apply the disconnection protocol.",
samuele1fa7322015-07-14 16:35:16 +08001102 myNextRole, did);
Madan Jampani328371d2015-05-29 14:06:27 -07001103 }
pierventreb2f636b2022-01-03 17:19:24 +01001104 // Let's put some order in the candidates list
1105 roleToAcknowledge.remove(did);
1106 updateMastershipFor(did);
Madan Jampani328371d2015-05-29 14:06:27 -07001107 return;
1108 }
1109
pierventreb2f636b2022-01-03 17:19:24 +01001110 /* Device is connected to this node - always reassert the role.
1111 Ideally, protocols like OpenFlow would not need to reassert the
1112 role because the instances are only identified by the role. However,
1113 other protocols like P4RT require to provide also an election id
1114 which maybe different over time, by reasserting the role will guarantee
1115 that updated election ids are communicated to the devices. It should not
1116 cost us a lot as it is equivalent to a probe.*/
Madan Jampani328371d2015-05-29 14:06:27 -07001117 if (store.getDevice(did) != null) {
1118 reassertRole(did, myNextRole);
1119 } else {
1120 log.debug("Device is not yet/no longer in the store: {}", did);
1121 }
1122 }
1123
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -08001124 // Intercepts mastership events
1125 private class InternalMastershipListener implements MastershipListener {
1126
tomb41d1ac2014-09-24 01:51:24 -07001127 @Override
1128 public void event(MastershipEvent event) {
HIGUCHI Yuta060da9a2016-03-11 19:16:35 -08001129 backgroundService.execute(() -> {
Madan Jampani328371d2015-05-29 14:06:27 -07001130 try {
1131 handleMastershipEvent(event);
1132 } catch (Exception e) {
1133 log.warn("Failed to handle {}", event, e);
Yuta HIGUCHId26354d2014-10-31 14:14:38 -07001134 }
Madan Jampani328371d2015-05-29 14:06:27 -07001135 });
Ayaka Koshibe317245a2014-10-29 00:34:43 -07001136 }
tomb41d1ac2014-09-24 01:51:24 -07001137 }
tomf80c9722014-09-24 14:49:18 -07001138
1139 // Store delegate to re-post events emitted from the store.
Thomas Vachuskab17c41f2015-05-19 11:16:05 -07001140 private class InternalStoreDelegate implements DeviceStoreDelegate {
tomf80c9722014-09-24 14:49:18 -07001141 @Override
1142 public void notify(DeviceEvent event) {
1143 post(event);
Rafał Szalecki9fb87f62017-12-06 15:06:09 +01001144 if (event.type().equals(DeviceEvent.Type.DEVICE_REMOVED)) {
Thomas Vachuska5b38dc02018-05-10 15:24:40 -07001145 // When device is administratively removed, force disconnect.
1146 DeviceId deviceId = event.subject().id();
1147 deviceLocalStatus.remove(deviceId);
1148
1149 DeviceProvider provider = getProvider(deviceId);
1150 if (provider != null) {
1151 log.info("Triggering disconnect for device {}", deviceId);
1152 try {
1153 provider.triggerDisconnect(deviceId);
1154 } catch (UnsupportedOperationException e) {
1155 log.warn("Unable to trigger disconnect due to {}", e.getMessage());
1156 }
1157 }
Rafał Szalecki9fb87f62017-12-06 15:06:09 +01001158 }
tomf80c9722014-09-24 14:49:18 -07001159 }
1160 }
samuel738dfaf2015-07-11 11:08:57 +08001161
1162 @Override
1163 public Iterable<Device> getDevices(Type type) {
Changhoon Yoonb856b812015-08-10 03:47:19 +09001164 checkPermission(DEVICE_READ);
samuel738dfaf2015-07-11 11:08:57 +08001165 Set<Device> results = new HashSet<>();
1166 Iterable<Device> devices = store.getDevices();
1167 if (devices != null) {
1168 devices.forEach(d -> {
1169 if (type.equals(d.type())) {
1170 results.add(d);
1171 }
1172 });
1173 }
1174 return results;
1175 }
1176
1177 @Override
1178 public Iterable<Device> getAvailableDevices(Type type) {
Changhoon Yoonb856b812015-08-10 03:47:19 +09001179 checkPermission(DEVICE_READ);
samuel738dfaf2015-07-11 11:08:57 +08001180 Set<Device> results = new HashSet<>();
1181 Iterable<Device> availableDevices = store.getAvailableDevices();
1182 if (availableDevices != null) {
1183 availableDevices.forEach(d -> {
1184 if (type.equals(d.type())) {
1185 results.add(d);
1186 }
1187 });
1188 }
1189 return results;
1190 }
Sahil Lele3a0cdd52015-07-21 14:16:31 -07001191
1192 private class InternalNetworkConfigListener implements NetworkConfigListener {
Ray Milkey69ca82a2019-03-28 09:24:40 -07001193 private DeviceId extractDeviceId(NetworkConfigEvent event) {
1194 DeviceId deviceId = null;
debmaiti9553ed72019-03-18 14:27:42 +05301195 if (event.configClass().equals(PortAnnotationConfig.class)) {
Ray Milkey397caca2019-04-01 16:27:50 -07001196 if (event.subject().getClass() == ConnectPoint.class) {
1197 deviceId = ((ConnectPoint) event.subject()).deviceId();
1198 }
Ray Milkey69ca82a2019-03-28 09:24:40 -07001199 } else if (event.subject().getClass() == DeviceId.class) {
debmaiti9553ed72019-03-18 14:27:42 +05301200 deviceId = (DeviceId) event.subject();
1201 }
Ray Milkey69ca82a2019-03-28 09:24:40 -07001202 return deviceId;
1203 }
1204
1205 @Override
1206 public boolean isRelevant(NetworkConfigEvent event) {
1207 DeviceId deviceId = extractDeviceId(event);
1208
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001209 return (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED
debmaiti9553ed72019-03-18 14:27:42 +05301210 || event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED
1211 || event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED)
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001212 && (event.configClass().equals(BasicDeviceConfig.class)
Andrea Campanella75ef9f52017-07-27 20:14:32 +02001213 || portOpsIndex.containsKey(event.configClass())
Palash Kalaa06a6162017-11-15 20:42:40 +09001214 || event.configClass().equals(PortDescriptionsConfig.class)
Thomas Vachuskaf131e592018-05-07 11:52:14 -07001215 || event.configClass().equals(DeviceAnnotationConfig.class))
Ray Milkey69ca82a2019-03-28 09:24:40 -07001216 && deviceId != null && mastershipService.isLocalMaster(deviceId);
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001217 }
1218
1219 @Override
Sahil Lele3a0cdd52015-07-21 14:16:31 -07001220 public void event(NetworkConfigEvent event) {
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001221 DeviceEvent de = null;
1222 if (event.configClass().equals(BasicDeviceConfig.class)) {
Thomas Vachuska138de8b2016-01-11 21:31:38 -08001223 log.debug("Detected device network config event {}", event.type());
Yuta HIGUCHI1d4b3aa2017-08-09 15:10:18 -07001224 DeviceId did = (DeviceId) event.subject();
1225 DeviceProvider dp = getProvider(did);
Simon Huntffbad3b2017-05-16 15:37:51 -07001226 BasicDeviceConfig cfg =
1227 networkConfigService.getConfig(did, BasicDeviceConfig.class);
Sahil Lele3a0cdd52015-07-21 14:16:31 -07001228
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001229 if (!isAllowed(cfg)) {
1230 kickOutBadDevice(did);
1231 } else {
1232 Device dev = getDevice(did);
Simon Huntffbad3b2017-05-16 15:37:51 -07001233 DeviceDescription desc =
1234 (dev == null) ? null : BasicDeviceOperator.descriptionOf(dev);
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001235 desc = BasicDeviceOperator.combine(cfg, desc);
Simon Huntffbad3b2017-05-16 15:37:51 -07001236 if (desc != null && dp != null) {
pier388ec252020-04-15 20:53:14 +02001237 store.createOrUpdateDevice(dp.id(), did, desc);
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001238 }
1239 }
Yuta HIGUCHI1d4b3aa2017-08-09 15:10:18 -07001240 } else if (event.configClass().equals(PortDescriptionsConfig.class)) {
1241 DeviceId did = (DeviceId) event.subject();
1242 DeviceProvider dp = getProvider(did);
1243 if (!event.config().isPresent() ||
1244 getDevice(did) == null || dp == null) {
1245 // sanity check failed, ignore
1246 return;
1247 }
Andrea Campanella75ef9f52017-07-27 20:14:32 +02001248 PortDescriptionsConfig portConfig = (PortDescriptionsConfig) event.config().get();
Yuta HIGUCHI1d4b3aa2017-08-09 15:10:18 -07001249 //updating the ports if configuration exists
Andrea Campanella75ef9f52017-07-27 20:14:32 +02001250 List<PortDescription> complete = store.getPortDescriptions(dp.id(), did)
1251 .collect(Collectors.toList());
1252 complete.addAll(portConfig.portDescriptions());
1253 store.updatePorts(dp.id(), did, complete);
Palash Kalaa06a6162017-11-15 20:42:40 +09001254 } else if (event.configClass().equals(DeviceAnnotationConfig.class)) {
1255 DeviceId did = (DeviceId) event.subject();
1256 DeviceProvider dp = getProvider(did);
1257 Device dev = getDevice(did);
1258 DeviceDescription desc =
1259 (dev == null) ? null : BasicDeviceOperator.descriptionOf(dev);
1260 Optional<Config> prevConfig = event.prevConfig();
Anurag Chadha61946392020-08-28 15:18:03 +05301261 if (desc != null) { // Fix for NPE due to desc being null
1262 desc = deviceAnnotationOp.combine(did, desc, prevConfig);
1263 }
Palash Kalaa06a6162017-11-15 20:42:40 +09001264 if (desc != null && dp != null) {
pier388ec252020-04-15 20:53:14 +02001265 store.createOrUpdateDevice(dp.id(), did, desc);
Palash Kalaa06a6162017-11-15 20:42:40 +09001266 }
Yuta HIGUCHI1d4b3aa2017-08-09 15:10:18 -07001267 } else if (portOpsIndex.containsKey(event.configClass())) {
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001268 ConnectPoint cpt = (ConnectPoint) event.subject();
Yuta HIGUCHI1d4b3aa2017-08-09 15:10:18 -07001269 DeviceId did = cpt.deviceId();
1270 DeviceProvider dp = getProvider(did);
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001271
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001272 // Note: assuming PortOperator can modify existing port,
1273 // but cannot add new port purely from Config.
Simon Huntffbad3b2017-05-16 15:37:51 -07001274 de = Optional.ofNullable(dp)
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001275 .map(provider -> store.getPortDescription(provider.id(), did, cpt.port()))
debmaiti9553ed72019-03-18 14:27:42 +05301276 .map(desc -> applyAllPortOps(cpt, desc, event.prevConfig()))
Simon Huntffbad3b2017-05-16 15:37:51 -07001277 .map(desc -> store.updatePortStatus(dp.id(), did, desc))
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001278 .orElse(null);
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001279 }
1280
1281 if (de != null) {
1282 post(de);
1283 }
1284 }
1285
Simon Hunt8f60ff82017-04-24 17:19:30 -07001286 // removes the specified device if it exists
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001287 private void kickOutBadDevice(DeviceId deviceId) {
Sahil Lele3a0cdd52015-07-21 14:16:31 -07001288 Device badDevice = getDevice(deviceId);
1289 if (badDevice != null) {
1290 removeDevice(deviceId);
Sahil Lele3a0cdd52015-07-21 14:16:31 -07001291 }
1292 }
1293 }
Saurav Dasa2d37502016-03-25 17:50:40 -07001294
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001295 @Override
1296 @SafeVarargs
1297 public final void registerPortConfigOperator(PortConfigOperator portOp,
Simon Huntffbad3b2017-05-16 15:37:51 -07001298 Class<? extends Config<ConnectPoint>>... configs) {
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001299 checkNotNull(portOp);
1300
1301 portOp.bindService(networkConfigService);
1302
1303 // update both portOpsIndex and portOps
1304 synchronized (portOpsIndex) {
1305 for (Class<? extends Config<ConnectPoint>> config : configs) {
1306 portOpsIndex.put(config, portOp);
1307 }
1308
1309 portOps.add(portOp);
1310 }
1311
1312 // TODO: Should we be applying to all existing Ports?
1313 Tools.stream(store.getAvailableDevices())
Simon Huntffbad3b2017-05-16 15:37:51 -07001314 .map(Device::id)
1315 .filter(mastershipService::isLocalMaster)
1316 // for each locally managed Device, update all port descriptions
1317 .map(did -> {
1318 ProviderId pid = Optional.ofNullable(getProvider(did))
1319 .map(Provider::id)
1320 .orElse(null);
1321 if (pid == null) {
1322 log.warn("Provider not found for {}", did);
1323 return ImmutableList.<DeviceEvent>of();
1324 }
1325 List<PortDescription> pds
1326 = store.getPortDescriptions(pid, did)
1327 .map(pdesc -> applyAllPortOps(did, pdesc))
1328 .collect(Collectors.toList());
1329 return store.updatePorts(pid, did, pds);
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001330 })
Simon Huntffbad3b2017-05-16 15:37:51 -07001331 // ..and port port update event if necessary
1332 .forEach(evts -> evts.forEach(this::post));
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001333 }
1334
1335 @Override
1336 public void unregisterPortConfigOperator(PortConfigOperator portOp) {
1337 checkNotNull(portOp);
1338
1339
1340 // remove all portOp
1341 synchronized (portOpsIndex) {
1342 portOps.remove(portOp);
1343
1344 // had to do this since COWArrayList iterator doesn't support remove
1345 portOpsIndex.keySet().forEach(key -> portOpsIndex.remove(key, portOp));
1346 }
1347
1348 }
1349
1350 /**
1351 * Merges the appropriate PortConfig with the description.
1352 *
1353 * @param did ID of the Device where the port is attached
1354 * @param desc {@link PortDescription}
1355 * @return merged {@link PortDescription}
1356 */
1357 private PortDescription applyAllPortOps(DeviceId did, PortDescription desc) {
1358 return applyAllPortOps(new ConnectPoint(did, desc.portNumber()), desc);
1359 }
1360
1361 /**
1362 * Merges the appropriate PortConfig with the description.
1363 *
Simon Huntffbad3b2017-05-16 15:37:51 -07001364 * @param cpt ConnectPoint where the port is attached
1365 * @param desc {@link PortDescription}
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001366 * @return merged {@link PortDescription}
1367 */
1368 private PortDescription applyAllPortOps(ConnectPoint cpt, PortDescription desc) {
1369 PortDescription work = desc;
1370 for (PortConfigOperator portOp : portOps) {
1371 work = portOp.combine(cpt, work);
1372 }
Yuta HIGUCHI7438f5a2017-02-15 22:09:46 -08001373 return portAnnotationOp.combine(cpt, work);
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001374 }
1375
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -07001376 /**
debmaiti9553ed72019-03-18 14:27:42 +05301377 * Merges the appropriate PortConfig with the description.
1378 *
1379 * @param cpt ConnectPoint where the port is attached
1380 * @param desc {@link PortDescription}
1381 * @param prevConfig previous configuration
1382 * @return merged {@link PortDescription}
1383 */
1384 private PortDescription applyAllPortOps(ConnectPoint cpt, PortDescription desc,
1385 Optional<Config> prevConfig) {
1386 PortDescription work = desc;
1387 for (PortConfigOperator portOp : portOps) {
1388 work = portOp.combine(cpt, work, prevConfig);
1389 }
1390 return portAnnotationOp.combine(cpt, work, prevConfig);
1391 }
1392
1393 /**
pierventreb2f636b2022-01-03 17:19:24 +01001394 * Handler for remote probe requests.
1395 *
1396 * @param deviceId the device to check
1397 * @return whether or not the device is reachable
1398 */
1399 private boolean handleProbeRequest(DeviceId deviceId) {
1400 int attempt = 0;
1401 // Let's do a number of attempts
1402 while (attempt < PROBE_ATTEMPTS) {
1403 if (!probeReachability(deviceId)) {
1404 return false;
1405 }
1406 attempt++;
1407 }
1408 return true;
1409 }
1410
1411 /**
1412 * Update the mastership for this device. If there is a node able
1413 * to reach the device and this node is the master move the
1414 * mastership to the next node still connected to this device.
1415 * If the current node is a backup, it demotes itself to the bottom
1416 * of the candidates list
1417 *
1418 * @param deviceId the device for which we have to update the mastership
1419 * @return the NodeId of any node that can reach the device, or null if
1420 * none of the ONOS instances can reach the device
1421 */
1422 private NodeId updateMastershipFor(DeviceId deviceId) {
1423 Map<NodeId, CompletableFuture<Boolean>> probes = Maps.newHashMap();
1424 // Request a probe only if the node is ready
1425 for (ControllerNode onosNode : clusterService.getNodes()) {
1426 if (!clusterService.getState(onosNode.id()).isReady() || localNodeId.equals(onosNode.id())) {
1427 continue;
1428 }
1429 probes.put(onosNode.id(), communicationService.sendAndReceive(deviceId, PROBE_SUBJECT, SERIALIZER::encode,
1430 SERIALIZER::decode, onosNode.id()));
1431 }
1432
1433 // Returns the first node able to reach the device
1434 // FIXME [SDFAB-935] optimize by looking at the MastershipInfo
1435 boolean isReachable;
1436 NodeId nextMaster = null;
1437 // FIXME Should we expose timeout? Understand if there is need to signal to the caller
1438 for (Map.Entry<NodeId, CompletableFuture<Boolean>> probe : probes.entrySet()) {
1439 isReachable = Tools.futureGetOrElse(probe.getValue(), PROBE_TIMEOUT_MILLIS,
1440 TimeUnit.MILLISECONDS, Boolean.FALSE);
1441 if (isReachable) {
1442 nextMaster = probe.getKey();
1443 }
1444 }
1445
1446 // FIXME [SDFAB-935] optimize demote by looking at the MastershipInfo;
1447 if (nextMaster != null) {
1448 log.info("Device {} is still connected to {}", deviceId, nextMaster);
1449 MastershipRole myRole = mastershipService.getLocalRole(deviceId);
1450 if (myRole == MASTER) {
1451 log.info("Handing over the mastership of {} to next master {}", deviceId, nextMaster);
1452 mastershipAdminService.setRole(nextMaster, deviceId, MASTER);
1453 /* Do not demote here because setRole can return before the mastership has been passed.
1454 Current implementation promotes first the nextMaster as top of candidate list and then
1455 transfer the leadership. We can use the BACKUP events to do demote or leverage periodic
1456 checks.*/
1457 } else if (myRole == STANDBY) {
1458 log.info("Demote current instance to the bottom of the candidates list for {}", deviceId);
1459 mastershipAdminService.demote(localNodeId, deviceId);
1460 } else {
1461 log.debug("No valid role for {}", deviceId);
1462 }
1463 }
1464
1465 return nextMaster;
1466 }
1467
1468
1469
1470 /**
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -07001471 * Port Enable/Disable message sent to the device's MASTER node.
1472 */
1473 private class InternalPortUpDownEvent {
1474 private final DeviceId deviceId;
1475 private final PortNumber portNumber;
1476 private final boolean enable;
1477
1478 protected InternalPortUpDownEvent(
1479 DeviceId deviceId, PortNumber portNumber, boolean enable) {
1480 this.deviceId = deviceId;
1481 this.portNumber = portNumber;
1482 this.enable = enable;
1483 }
1484
1485 public DeviceId deviceId() {
1486 return deviceId;
1487 }
1488 public PortNumber portNumber() {
1489 return portNumber;
1490 }
1491 public boolean isEnable() {
1492 return enable;
1493 }
1494
1495 protected InternalPortUpDownEvent() {
1496 this.deviceId = null;
1497 this.portNumber = null;
1498 this.enable = false;
1499 }
1500 }
tomd3097b02014-08-26 10:40:29 -07001501}