blob: a85ee8f5b85605c83db03a76106d9addd1a33d59 [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);
pierventred510b7d2022-02-03 21:56:37 +0100516 log.info("Device local status is {}", localStatus(deviceId));
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800517 if (!isReachable(deviceId)) {
Ray Milkeyc7104672016-08-31 12:04:34 -0700518 if (myRole != NONE) {
pierventreb2f636b2022-01-03 17:19:24 +0100519 // Verify if the device is fully disconnected from the cluster
520 if (updateMastershipFor(deviceId) == null
521 && myRole == MASTER && isAvailable(deviceId)) {
522 log.info("Local Role {}, Marking unreachable device {} offline", MASTER, deviceId);
523 post(store.markOffline(deviceId));
helenyrwufd296b62016-06-22 17:43:02 -0700524 }
Bharath Thiruveedula651a7da2016-12-13 02:52:50 +0530525 } else {
pierventreb2f636b2022-01-03 17:19:24 +0100526 /* Firstly get a role and then check if the device is available in the store.
527 if it is, if this node is the master and the device is fully disconnected
528 from the cluster mark the device offline. In principle, this condition should
529 never be hit unless in a device removed phase for NONE mastership roles. */
530 try {
531 mastershipService.requestRoleFor(deviceId).get();
532 } catch (InterruptedException e) {
533 Thread.currentThread().interrupt();
534 log.error("Interrupted waiting for Mastership", e);
535 } catch (ExecutionException e) {
536 log.error("Encountered an error waiting for Mastership", e);
537 }
538
539 MastershipTerm term = termService.getMastershipTerm(deviceId);
540 if (updateMastershipFor(deviceId) == null &&
541 term != null && localNodeId.equals(term.master()) &&
542 isAvailable(deviceId)) {
543 log.info("Marking unreachable device {} offline", deviceId);
544 post(store.markOffline(deviceId));
Jordan Haltermanbc0308f2017-11-29 15:37:34 -0800545 }
helenyrwufd296b62016-06-22 17:43:02 -0700546 }
pierventreb2f636b2022-01-03 17:19:24 +0100547 roleToAcknowledge.remove(deviceId);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800548 continue;
549 }
550
Jordan Halterman368bd3d2019-01-04 12:45:57 -0800551 // If this node is the master, ensure the device is marked online.
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800552 if (myRole == MASTER && canMarkOnline(device)) {
Jordan Halterman368bd3d2019-01-04 12:45:57 -0800553 post(store.markOnline(deviceId));
554 }
555
pierventreb2f636b2022-01-03 17:19:24 +0100556 log.info("{} is reachable - reasserting the role", deviceId);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800557
pierventreb2f636b2022-01-03 17:19:24 +0100558 /* Device is still reachable. It is useful for some protocols
559 to reassert the role. Note: NONE triggers request to MastershipService */
560 reassertRole(deviceId, myRole);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800561 }
562 }
563
pierventre00ac2502021-12-02 09:42:28 +0100564 /**
565 * Checks if all the devices have acknowledged the mastership role.
566 */
567 private void roleCheck() {
568 log.debug("Checking role");
569 for (Device device : getDevices()) {
570 final DeviceId deviceId = device.id();
571 MastershipRole myRole = mastershipService.getLocalRole(deviceId);
572 log.trace("Checking device {}. Current role is {}", deviceId, myRole);
573 final AtomicBoolean exists = new AtomicBoolean(false);
574 final Long ts = roleToAcknowledge.compute(deviceId, (key, value) -> {
575 if (value == null) {
576 return null;
577 }
578 exists.set(true);
579 if (currentTimeMillis() - value < (ROLE_TIMEOUT_SECONDS * 1000)) {
580 return value;
581 }
582 return null;
583 });
584 // Nobody applied the role recently
585 if (!exists.get()) {
586 log.trace("Role was not applied or it has been acknowledged for device {}", deviceId);
587 continue;
588 }
589 // Timeout still on
590 if (ts != null) {
591 log.debug("Timeout expires in {} ms", ((ROLE_TIMEOUT_SECONDS * 1000) - currentTimeMillis() + ts));
592 continue;
593 }
594 if (myRole != MASTER) {
595 log.debug("Timeout is expired but current role is not MASTER ({}), nothing to do", myRole);
596 continue;
597 }
598 /* Switch failed to acknowledge master role we asked for.
599 Yield mastership to other instance*/
600 log.warn("Failed to assert role onto device {}. requested={}, no response",
601 deviceId, myRole);
pierventreb2f636b2022-01-03 17:19:24 +0100602 updateMastershipFor(deviceId);
pierventre00ac2502021-12-02 09:42:28 +0100603 }
604 }
605
tomd3097b02014-08-26 10:40:29 -0700606 // Personalized device provider service issued to the supplied provider.
tomdc361b62014-09-09 20:36:52 -0700607 private class InternalDeviceProviderService
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700608 extends AbstractProviderService<DeviceProvider>
609 implements DeviceProviderService {
tomd3097b02014-08-26 10:40:29 -0700610
tomcfde0622014-09-09 11:02:42 -0700611 InternalDeviceProviderService(DeviceProvider provider) {
tomd3097b02014-08-26 10:40:29 -0700612 super(provider);
613 }
614
Ray Milkey33306ba2018-09-20 13:27:25 -0700615 /**
616 * Apply role in reaction to provider event.
617 *
618 * @param deviceId device identifier
619 * @param newRole new role to apply to the device
620 * @return true if the request was sent to provider
621 */
622 private boolean applyRole(DeviceId deviceId, MastershipRole newRole) {
623
624 if (newRole.equals(MastershipRole.NONE)) {
625 //no-op
626 return true;
627 }
628
629 DeviceProvider provider = provider();
630 if (provider == null) {
631 log.warn("Provider for {} was not found. Cannot apply role {}",
632 deviceId, newRole);
633 return false;
634 }
pierventre00ac2502021-12-02 09:42:28 +0100635 // Start the timer
636 roleToAcknowledge.put(deviceId, currentTimeMillis());
Ray Milkey33306ba2018-09-20 13:27:25 -0700637 provider.roleChanged(deviceId, newRole);
638 // not triggering probe when triggered by provider service event
639 return true;
640 }
641
tomd3097b02014-08-26 10:40:29 -0700642 @Override
alshabibb7b40632014-09-28 21:30:00 -0700643 public void deviceConnected(DeviceId deviceId,
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700644 DeviceDescription deviceDescription) {
tom32f66842014-08-27 19:27:47 -0700645 checkNotNull(deviceId, DEVICE_ID_NULL);
646 checkNotNull(deviceDescription, DEVICE_DESCRIPTION_NULL);
tomeadbb462014-09-07 16:10:19 -0700647 checkValidity();
Yuta HIGUCHI24b2e2a2014-10-07 15:53:57 -0700648
Yuta HIGUCHI0c47d532017-08-18 23:16:35 -0700649 deviceLocalStatus.put(deviceId, new LocalStatus(true, Instant.now()));
Saurav Dasd5ec9e92017-01-17 10:40:18 -0800650
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -0700651 BasicDeviceConfig cfg = networkConfigService.getConfig(deviceId, BasicDeviceConfig.class);
652 if (!isAllowed(cfg)) {
653 log.warn("Device {} is not allowed", deviceId);
654 return;
655 }
Andrea Campanella75ef9f52017-07-27 20:14:32 +0200656 PortDescriptionsConfig portConfig = networkConfigService.getConfig(deviceId, PortDescriptionsConfig.class);
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -0700657 // Generate updated description and establish my Role
658 deviceDescription = BasicDeviceOperator.combine(cfg, deviceDescription);
jaegonkim1c1f0c22018-01-28 22:20:42 +0900659 DeviceAnnotationConfig annoConfig = networkConfigService.getConfig(deviceId, DeviceAnnotationConfig.class);
660 if (annoConfig != null) {
661 deviceDescription = deviceAnnotationOp.combine(deviceId, deviceDescription, Optional.of(annoConfig));
662 }
663
pier388ec252020-04-15 20:53:14 +0200664 // Wait for the end of the election. sync call of requestRoleFor
665 // wait only 3s and it is not entirely safe since the leadership
666 // election timer can be higher.
667 MastershipRole role = Futures.getUnchecked(mastershipService.requestRoleFor(deviceId));
jaegonkim8eabcec2018-07-27 23:40:43 +0900668 log.info("Local role is {} for {}", role, deviceId);
pier388ec252020-04-15 20:53:14 +0200669 store.createOrUpdateDevice(provider().id(), deviceId, deviceDescription);
Ray Milkey33306ba2018-09-20 13:27:25 -0700670 applyRole(deviceId, role);
HIGUCHI Yuta11530fb2015-05-27 13:10:20 -0700671
Andrea Campanella75ef9f52017-07-27 20:14:32 +0200672 if (portConfig != null) {
673 //updating the ports if configration exists
674 List<PortDescription> complete = store.getPortDescriptions(provider().id(), deviceId)
675 .collect(Collectors.toList());
676 complete.addAll(portConfig.portDescriptions());
677 List<PortDescription> portDescriptions = complete.stream()
678 .map(e -> applyAllPortOps(deviceId, e))
679 .collect(Collectors.toList());
680 store.updatePorts(provider().id(), deviceId, portDescriptions);
681 }
682
helenyrwufd296b62016-06-22 17:43:02 -0700683 if (deviceDescription.isDefaultAvailable()) {
684 log.info("Device {} connected", deviceId);
685 } else {
686 log.info("Device {} registered", deviceId);
687 }
tomd3097b02014-08-26 10:40:29 -0700688 }
689
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -0700690 private PortDescription ensurePortEnabledState(PortDescription desc, boolean enabled) {
691 if (desc.isEnabled() != enabled) {
Yuta HIGUCHI2b1935d2018-03-01 21:41:06 -0800692 return DefaultPortDescription.builder(desc)
693 .isEnabled(enabled)
694 .build();
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -0700695 }
696 return desc;
697 }
698
tomd3097b02014-08-26 10:40:29 -0700699 @Override
700 public void deviceDisconnected(DeviceId deviceId) {
tom32f66842014-08-27 19:27:47 -0700701 checkNotNull(deviceId, DEVICE_ID_NULL);
tomeadbb462014-09-07 16:10:19 -0700702 checkValidity();
pierventreb2f636b2022-01-03 17:19:24 +0100703 // Update the local status
Yuta HIGUCHI0c47d532017-08-18 23:16:35 -0700704 deviceLocalStatus.put(deviceId, new LocalStatus(false, Instant.now()));
Andrea Campanellab0b93ac2021-09-13 12:37:36 +0200705 log.info("Device {} disconnected from this node: {}", deviceId,
706 clusterService.getLocalNode().id());
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700707
pierventreb2f636b2022-01-03 17:19:24 +0100708 /* If none can reach the device, we will continue with the disconnection logic.
709 If there is one instance that reported device is still reachable, we hand over
710 the mastership to it if we are the current master, otherwise if we are a backup
711 we demote ourselves to the bottom of the backups list */
712 if (updateMastershipFor(deviceId) == null) {
713 log.info("Device {} is fully disconnected from the cluster", deviceId);
714 List<PortDescription> descs = store.getPortDescriptions(provider().id(), deviceId)
715 .map(desc -> ensurePortEnabledState(desc, false))
716 .collect(Collectors.toList());
717 store.updatePorts(this.provider().id(), deviceId, descs);
Yafit Hadara9a73de2015-09-06 13:52:52 +0300718
Madan Jampanic6e574f2015-05-29 13:41:52 -0700719 try {
pierventreb2f636b2022-01-03 17:19:24 +0100720 if (mastershipService.isLocalMaster(deviceId)) {
721 post(store.markOffline(deviceId));
722 }
723 } catch (IllegalStateException e) {
724 log.warn("Failed to mark {} offline", deviceId);
725 // only the MASTER should be marking off-line in normal cases,
726 // but if I was the last STANDBY connection, etc. and no one else
727 // was there to mark the device offline, this instance may need to
728 // temporarily request for Master Role and mark offline.
729
730 //there are times when this node will correctly have mastership, BUT
731 //that isn't reflected in the ClockManager before the device disconnects.
732 //we want to let go of the device anyways, so make sure this happens.
733
734 // FIXME: Store semantics leaking out as IllegalStateException.
735 // Consider revising store API to handle this scenario.
736 CompletableFuture<MastershipRole> roleFuture = mastershipService.requestRoleFor(deviceId);
737 roleFuture.whenComplete((role, error) -> {
738 MastershipTerm term = termService.getMastershipTerm(deviceId);
739 // TODO: Move this type of check inside device clock manager, etc.
740 if (term != null && localNodeId.equals(term.master())) {
741 log.info("Retry marking {} offline", deviceId);
742 post(store.markOffline(deviceId));
743 } else {
744 log.info("Failed again marking {} offline. {}", deviceId, role);
745 }
746 });
747 } finally {
pierventre00ac2502021-12-02 09:42:28 +0100748 roleToAcknowledge.remove(deviceId);
Madan Jampanic6e574f2015-05-29 13:41:52 -0700749 }
tom0efbb1d2014-09-09 11:54:28 -0700750 }
pierventreb2f636b2022-01-03 17:19:24 +0100751
tomd3097b02014-08-26 10:40:29 -0700752 }
753
754 @Override
alshabibb7b40632014-09-28 21:30:00 -0700755 public void updatePorts(DeviceId deviceId,
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700756 List<PortDescription> portDescriptions) {
tom32f66842014-08-27 19:27:47 -0700757 checkNotNull(deviceId, DEVICE_ID_NULL);
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -0700758 checkNotNull(portDescriptions, PORT_DESC_LIST_NULL);
tomeadbb462014-09-07 16:10:19 -0700759 checkValidity();
Madan Jampani565a66a2015-07-25 17:01:13 -0700760 if (!mastershipService.isLocalMaster(deviceId)) {
Yuta HIGUCHI13c0b872014-10-30 18:09:22 -0700761 // Never been a master for this device
762 // any update will be ignored.
samuele1fa7322015-07-14 16:35:16 +0800763 log.trace("Ignoring {} port updates on standby node. {}", deviceId, portDescriptions);
Yuta HIGUCHI13c0b872014-10-30 18:09:22 -0700764 return;
765 }
Andrea Campanella75ef9f52017-07-27 20:14:32 +0200766 PortDescriptionsConfig portConfig = networkConfigService.getConfig(deviceId, PortDescriptionsConfig.class);
767 if (portConfig != null) {
Carmelo Cascone1da7a4d2018-06-27 18:03:11 +0200768 // Updating the ports if configuration exists (on new lists as
769 // the passed one might be immutable)
770 portDescriptions = Lists.newArrayList(portDescriptions);
Andrea Campanella75ef9f52017-07-27 20:14:32 +0200771 portDescriptions.addAll(portConfig.portDescriptions());
772 }
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -0700773 portDescriptions = portDescriptions.stream()
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -0700774 .map(e -> applyAllPortOps(deviceId, e))
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -0700775 .collect(Collectors.toList());
Yuta HIGUCHI5f6739c2014-10-01 14:04:01 -0700776 List<DeviceEvent> events = store.updatePorts(this.provider().id(),
samuele1fa7322015-07-14 16:35:16 +0800777 deviceId, portDescriptions);
Thomas Vachuska5923b9a2016-01-26 10:52:57 -0800778 if (events != null) {
779 for (DeviceEvent event : events) {
780 post(event);
781 }
tom32f66842014-08-27 19:27:47 -0700782 }
tomd3097b02014-08-26 10:40:29 -0700783 }
784
785 @Override
alshabibb7b40632014-09-28 21:30:00 -0700786 public void portStatusChanged(DeviceId deviceId,
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700787 PortDescription portDescription) {
tom32f66842014-08-27 19:27:47 -0700788 checkNotNull(deviceId, DEVICE_ID_NULL);
789 checkNotNull(portDescription, PORT_DESCRIPTION_NULL);
tomeadbb462014-09-07 16:10:19 -0700790 checkValidity();
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700791
Madan Jampani565a66a2015-07-25 17:01:13 -0700792 if (!mastershipService.isLocalMaster(deviceId)) {
Yuta HIGUCHI13c0b872014-10-30 18:09:22 -0700793 // Never been a master for this device
794 // any update will be ignored.
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700795 log.trace("Ignoring {} port update on standby node. {}", deviceId,
796 portDescription);
Yuta HIGUCHI13c0b872014-10-30 18:09:22 -0700797 return;
798 }
Ray Milkey9ef22232016-07-14 12:42:37 -0700799 Device device = getDevice(deviceId);
800 if (device == null) {
801 log.trace("Device not found: {}", deviceId);
nitinanandddfa8c92017-03-24 16:14:23 +0530802 return;
Ray Milkey9ef22232016-07-14 12:42:37 -0700803 }
Andrea Campanella1c24fb92018-12-20 16:43:59 +0100804 if ((Type.ROADM.equals(device.type())) || (Type.OTN.equals(device.type())) ||
805 (Type.OLS.equals(device.type())) || (Type.TERMINAL_DEVICE.equals(device.type()))) {
HIGUCHI Yuta6972ae62016-05-12 19:57:46 -0700806 // FIXME This is ignoring all other info in portDescription given as input??
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -0700807 PortDescription storedPortDesc = store.getPortDescription(provider().id(),
Simon Huntffbad3b2017-05-16 15:37:51 -0700808 deviceId,
809 portDescription.portNumber());
Yuta HIGUCHI6eb00cc2016-06-10 11:55:12 -0700810 portDescription = ensurePortEnabledState(storedPortDesc,
811 portDescription.isEnabled());
Yafit Hadara9a73de2015-09-06 13:52:52 +0300812 }
813
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -0700814 portDescription = applyAllPortOps(deviceId, portDescription);
samuele1fa7322015-07-14 16:35:16 +0800815 final DeviceEvent event = store.updatePortStatus(this.provider().id(),
HIGUCHI Yuta6972ae62016-05-12 19:57:46 -0700816 deviceId,
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -0700817 portDescription);
tomff7eb7c2014-09-08 12:49:03 -0700818 if (event != null) {
Carmelo Casconeab5d41e2019-03-06 18:02:34 -0800819 log.info("Device {} port {} status changed (enabled={})",
820 deviceId, event.port().number(), portDescription.isEnabled());
tom0efbb1d2014-09-09 11:54:28 -0700821 post(event);
tomff7eb7c2014-09-08 12:49:03 -0700822 }
tomd3097b02014-08-26 10:40:29 -0700823 }
tom3f2bbd72014-09-24 12:07:58 -0700824
825 @Override
Michal Machce774332017-01-25 11:02:55 +0100826 public void deletePort(DeviceId deviceId, PortDescription basePortDescription) {
827
828 checkNotNull(deviceId, DEVICE_ID_NULL);
829 checkNotNull(basePortDescription, PORT_DESCRIPTION_NULL);
830 checkValidity();
831
832 if (!mastershipService.isLocalMaster(deviceId)) {
833 // Never been a master for this device
834 // any update will be ignored.
835 log.trace("Ignoring {} port update on standby node. {}", deviceId,
836 basePortDescription);
837 return;
838 }
839
840 Device device = getDevice(deviceId);
841 if (device == null) {
842 log.trace("Device not found: {}", deviceId);
843 }
844
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800845 PortDescription newPortDescription = DefaultPortDescription.builder(basePortDescription)
846 .isRemoved(true)
847 .build();
848
Michal Machce774332017-01-25 11:02:55 +0100849 final DeviceEvent event = store.updatePortStatus(this.provider().id(),
850 deviceId,
851 newPortDescription);
852 if (event != null) {
853 log.info("Device {} port {} status changed", deviceId, event.port().number());
854 post(event);
855 }
856 }
857
858 @Override
samuele1fa7322015-07-14 16:35:16 +0800859 public void receivedRoleReply(DeviceId deviceId, MastershipRole requested,
Thomas Vachuskab17c41f2015-05-19 11:16:05 -0700860 MastershipRole response) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700861 // Several things can happen here:
862 // 1. request and response match
863 // 2. request and response don't match
864 // 3. MastershipRole and requested match (and 1 or 2 are true)
865 // 4. MastershipRole and requested don't match (and 1 or 2 are true)
866 //
867 // 2, 4, and 3 with case 2 are failure modes.
868
tom3f2bbd72014-09-24 12:07:58 -0700869 // FIXME: implement response to this notification
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700870
Madan Jampanif2af7712015-05-29 18:43:52 -0700871 log.debug("got reply to a role request for {}: asked for {}, and got {}",
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700872 deviceId, requested, response);
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700873
874 if (requested == null && response == null) {
samuele1fa7322015-07-14 16:35:16 +0800875 // something was off with DeviceProvider, maybe check channel too?
Shashikanth VH387a1ca2016-02-09 20:35:21 +0530876 log.warn("Failed to assert role onto Device {}", deviceId);
pierventre00ac2502021-12-02 09:42:28 +0100877 roleToAcknowledge.remove(deviceId);
pierventreb2f636b2022-01-03 17:19:24 +0100878 updateMastershipFor(deviceId);
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700879 return;
Yuta HIGUCHIcf603902014-10-07 23:04:32 -0700880 }
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700881
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800882 final MastershipRole expected = mastershipService.getLocalRole(deviceId);
883
884 if (requested == null) {
885 // Provider is not able to reconcile role responses with
886 // requests. We assume what was requested is what we expect.
887 // This will work only if mastership doesn't change too often,
888 // and devices are left enough time to provide responses before
889 // a different role is requested.
890 requested = expected;
891 }
892
Thomas Vachuskab17c41f2015-05-19 11:16:05 -0700893 if (Objects.equals(requested, response)) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800894 if (Objects.equals(requested, expected)) {
pierventre00ac2502021-12-02 09:42:28 +0100895 // Stop the timer
896 log.info("Role has been acknowledged for device {}", deviceId);
897 roleToAcknowledge.remove(deviceId);
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700898 } else {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800899 log.warn("Role mismatch on {}. Set to {}, but store demands {}",
900 deviceId, response, expected);
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800901 // roleManager got the device to comply, but doesn't agree with
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700902 // the store; use the store's view, then try to reassert.
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800903 backgroundService.execute(() -> reassertRole(deviceId, expected));
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700904 }
905 } else {
906 // we didn't get back what we asked for. Reelect someone else.
Carmelo Casconee5b28722018-06-22 17:28:28 +0200907 log.warn("Failed to assert role onto device {}. requested={}, response={}",
908 deviceId, requested, response);
Carmelo Casconeb37bdde2018-06-22 17:25:46 +0200909 if (requested == MastershipRole.MASTER) {
pierventre00ac2502021-12-02 09:42:28 +0100910 // Stop the timer
911 roleToAcknowledge.remove(deviceId);
pierventreb2f636b2022-01-03 17:19:24 +0100912 updateMastershipFor(deviceId);
Charles Chan3855dc12021-01-06 01:33:03 +0000913 } else if (requested == MastershipRole.STANDBY) {
914 // For P4RT devices, the response role will be NONE when this node is expected to be STANDBY
915 // but the stream channel is not opened correctly.
916 // Calling reassertRole will trigger the mechanism in GeneralDeviceProvider that
917 // attempts to re-establish the stream channel
918 backgroundService.execute(() -> reassertRole(deviceId, expected));
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700919 }
920 }
tom3f2bbd72014-09-24 12:07:58 -0700921 }
sangho538108b2015-04-08 14:29:20 -0700922
923 @Override
samuele1fa7322015-07-14 16:35:16 +0800924 public void updatePortStatistics(DeviceId deviceId, Collection<PortStatistics> portStatistics) {
sangho538108b2015-04-08 14:29:20 -0700925 checkNotNull(deviceId, DEVICE_ID_NULL);
Thomas Vachuskab17c41f2015-05-19 11:16:05 -0700926 checkNotNull(portStatistics, "Port statistics list cannot be null");
sangho538108b2015-04-08 14:29:20 -0700927 checkValidity();
928
samuele1fa7322015-07-14 16:35:16 +0800929 DeviceEvent event = store.updatePortStatistics(this.provider().id(),
930 deviceId, portStatistics);
sangho538108b2015-04-08 14:29:20 -0700931 post(event);
932 }
pierventree1f80102021-10-01 22:01:22 +0200933
934 @Override
935 public DeviceDescription getDeviceDescription(DeviceId deviceId) {
936 checkNotNull(deviceId, DEVICE_ID_NULL);
937 checkValidity();
938
939 return store.getDeviceDescription(provider().id(), deviceId);
940 }
tomd3097b02014-08-26 10:40:29 -0700941 }
tom32f66842014-08-27 19:27:47 -0700942
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -0700943 // by default allowed, otherwise check flag
944 private boolean isAllowed(BasicDeviceConfig cfg) {
945 return (cfg == null || cfg.isAllowed());
946 }
947
Carmelo Cascone0761cd32018-08-29 19:22:50 -0700948 private boolean canMarkOnline(Device device) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800949 DeviceProvider provider = getProvider(device.id());
950 if (provider == null) {
951 log.warn("Provider for {} was not found. Cannot evaluate availability", device.id());
952 return false;
953 }
954 return provider.isAvailable(device.id());
Carmelo Cascone0761cd32018-08-29 19:22:50 -0700955 }
956
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800957 // Applies the specified role to the device; ignores NONE
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700958
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800959 /**
960 * Apply role to device and send probe if MASTER.
961 *
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700962 * @param deviceId device identifier
963 * @param newRole new role to apply to the device
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800964 * @return true if the request was sent to provider
965 */
966 private boolean applyRoleAndProbe(DeviceId deviceId, MastershipRole newRole) {
967 if (newRole.equals(MastershipRole.NONE)) {
samuele1fa7322015-07-14 16:35:16 +0800968 //no-op
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700969 return true;
970 }
Ayaka Koshibe317245a2014-10-29 00:34:43 -0700971
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -0800972 DeviceProvider provider = getProvider(deviceId);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800973 if (provider == null) {
samuele1fa7322015-07-14 16:35:16 +0800974 log.warn("Provider for {} was not found. Cannot apply role {}", deviceId, newRole);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800975 return false;
976 }
pierventre00ac2502021-12-02 09:42:28 +0100977 // Start the timer
978 roleToAcknowledge.put(deviceId, currentTimeMillis());
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800979 provider.roleChanged(deviceId, newRole);
980
981 if (newRole.equals(MastershipRole.MASTER)) {
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800982 log.debug("sent TriggerProbe({})", deviceId);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800983 // only trigger event when request was sent to provider
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -0800984 provider.triggerProbe(deviceId);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800985 }
986 return true;
987 }
988
pierventreb2f636b2022-01-03 17:19:24 +0100989 private boolean probeReachability(DeviceId deviceId) {
990 DeviceProvider provider = getProvider(deviceId);
991 if (provider == null) {
992 log.warn("Provider for {} was not found. Cannot probe reachability", deviceId);
993 return false;
994 }
995 return provider.isReachable(deviceId) && Tools.futureGetOrElse(provider.probeReachability(deviceId),
996 PROBE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS, Boolean.FALSE);
997 }
998
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800999 /**
Harold Huangff6e1e62017-11-09 16:25:36 +08001000 * Reassert role for specified device connected to this node.
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -08001001 *
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001002 * @param did device identifier
1003 * @param nextRole role to apply. If NONE is specified,
1004 * it will ask mastership service for a role and apply it.
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -08001005 */
samuele1fa7322015-07-14 16:35:16 +08001006 private void reassertRole(final DeviceId did,
1007 final MastershipRole nextRole) {
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -08001008
Jordan Halterman9416aea2017-11-17 12:40:21 -08001009 MastershipRole myNextRole = nextRole;
1010 if (myNextRole == NONE && upgradeService.isLocalActive()) {
1011 try {
1012 mastershipService.requestRoleFor(did).get();
1013 MastershipTerm term = termService.getMastershipTerm(did);
1014 if (term != null && localNodeId.equals(term.master())) {
1015 myNextRole = MASTER;
1016 } else {
1017 myNextRole = STANDBY;
1018 }
1019 } catch (InterruptedException e) {
1020 Thread.currentThread().interrupt();
1021 log.error("Interrupted waiting for Mastership", e);
1022 } catch (ExecutionException e) {
1023 log.error("Encountered an error waiting for Mastership", e);
1024 }
1025 }
1026
1027 switch (myNextRole) {
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001028 case MASTER:
1029 final Device device = getDevice(did);
Carmelo Cascone0761cd32018-08-29 19:22:50 -07001030 if (device != null && !isAvailable(did) && canMarkOnline(device)) {
Palash Kala6c526062018-04-03 18:25:11 +09001031 post(store.markOnline(did));
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001032 }
1033 // TODO: should apply role only if there is mismatch
Jordan Halterman9416aea2017-11-17 12:40:21 -08001034 log.debug("Applying role {} to {}", myNextRole, did);
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001035 if (!applyRoleAndProbe(did, MASTER)) {
Jordan Halterman9416aea2017-11-17 12:40:21 -08001036 log.warn("Unsuccessful applying role {} to {}", myNextRole, did);
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001037 // immediately failed to apply role
pierventreb2f636b2022-01-03 17:19:24 +01001038 updateMastershipFor(did);
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001039 // FIXME disconnect?
1040 }
1041 break;
1042 case STANDBY:
Jordan Halterman9416aea2017-11-17 12:40:21 -08001043 log.debug("Applying role {} to {}", myNextRole, did);
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001044 if (!applyRoleAndProbe(did, STANDBY)) {
Jordan Halterman9416aea2017-11-17 12:40:21 -08001045 log.warn("Unsuccessful applying role {} to {}", myNextRole, did);
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001046 // immediately failed to apply role
pierventreb2f636b2022-01-03 17:19:24 +01001047 updateMastershipFor(did);
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001048 // FIXME disconnect?
1049 }
1050 break;
1051 case NONE:
Jordan Halterman980a8c12017-09-22 18:01:19 -07001052 break;
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001053 default:
1054 // should never reach here
1055 log.error("You didn't see anything. I did not exist.");
1056 break;
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -08001057 }
1058 }
1059
Madan Jampani328371d2015-05-29 14:06:27 -07001060 private void handleMastershipEvent(MastershipEvent event) {
Madan Jampani328371d2015-05-29 14:06:27 -07001061 final DeviceId did = event.subject();
1062
pierventreb2f636b2022-01-03 17:19:24 +01001063 // myNextRole suggested by MastershipService event
Madan Jampani328371d2015-05-29 14:06:27 -07001064 MastershipRole myNextRole;
Jon Hall7a8bfc62016-05-26 17:59:04 -07001065 if (event.type() == MastershipEvent.Type.SUSPENDED) {
1066 myNextRole = NONE; // FIXME STANDBY OR NONE?
1067 } else if (localNodeId.equals(event.roleInfo().master())) {
Madan Jampani328371d2015-05-29 14:06:27 -07001068 // confirm latest info
1069 MastershipTerm term = termService.getMastershipTerm(did);
samuele1fa7322015-07-14 16:35:16 +08001070 final boolean iHaveControl = term != null && localNodeId.equals(term.master());
Madan Jampani328371d2015-05-29 14:06:27 -07001071 if (iHaveControl) {
Madan Jampani328371d2015-05-29 14:06:27 -07001072 myNextRole = MASTER;
1073 } else {
1074 myNextRole = STANDBY;
1075 }
1076 } else if (event.roleInfo().backups().contains(localNodeId)) {
1077 myNextRole = STANDBY;
1078 } else {
1079 myNextRole = NONE;
1080 }
1081
Madan Jampani328371d2015-05-29 14:06:27 -07001082 final boolean isReachable = isReachable(did);
pierventred510b7d2022-02-03 21:56:37 +01001083 log.info("Device local status is {}", localStatus(did));
Madan Jampani328371d2015-05-29 14:06:27 -07001084 if (!isReachable) {
pierventreb2f636b2022-01-03 17:19:24 +01001085 // device is not connected to this node, nevertheless we should get a role
Jon Halla90c44c2017-01-24 16:02:07 -08001086 if (mastershipService.getLocalRole(did) == NONE) {
1087 log.debug("Node was instructed to be {} role for {}, "
Simon Huntffbad3b2017-05-16 15:37:51 -07001088 + "but this node cannot reach the device "
pierventreb2f636b2022-01-03 17:19:24 +01001089 + "and role is already None. Asking a new role "
1090 + "and then apply the disconnection protocol.",
Simon Huntffbad3b2017-05-16 15:37:51 -07001091 myNextRole, did);
pierventreb2f636b2022-01-03 17:19:24 +01001092 try {
1093 mastershipService.requestRoleFor(did).get();
1094 } catch (InterruptedException e) {
1095 Thread.currentThread().interrupt();
1096 log.error("Interrupted waiting for Mastership", e);
1097 } catch (ExecutionException e) {
1098 log.error("Encountered an error waiting for Mastership", e);
1099 }
Jon Halla90c44c2017-01-24 16:02:07 -08001100 } else if (myNextRole != NONE) {
Madan Jampani328371d2015-05-29 14:06:27 -07001101 log.warn("Node was instructed to be {} role for {}, "
pierventreb2f636b2022-01-03 17:19:24 +01001102 + "but this node cannot reach the device. "
1103 + "Apply the disconnection protocol.",
samuele1fa7322015-07-14 16:35:16 +08001104 myNextRole, did);
Madan Jampani328371d2015-05-29 14:06:27 -07001105 }
pierventreb2f636b2022-01-03 17:19:24 +01001106 // Let's put some order in the candidates list
1107 roleToAcknowledge.remove(did);
1108 updateMastershipFor(did);
Madan Jampani328371d2015-05-29 14:06:27 -07001109 return;
1110 }
1111
pierventreb2f636b2022-01-03 17:19:24 +01001112 /* Device is connected to this node - always reassert the role.
1113 Ideally, protocols like OpenFlow would not need to reassert the
1114 role because the instances are only identified by the role. However,
1115 other protocols like P4RT require to provide also an election id
1116 which maybe different over time, by reasserting the role will guarantee
1117 that updated election ids are communicated to the devices. It should not
1118 cost us a lot as it is equivalent to a probe.*/
Madan Jampani328371d2015-05-29 14:06:27 -07001119 if (store.getDevice(did) != null) {
1120 reassertRole(did, myNextRole);
1121 } else {
1122 log.debug("Device is not yet/no longer in the store: {}", did);
1123 }
1124 }
1125
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -08001126 // Intercepts mastership events
1127 private class InternalMastershipListener implements MastershipListener {
1128
tomb41d1ac2014-09-24 01:51:24 -07001129 @Override
1130 public void event(MastershipEvent event) {
HIGUCHI Yuta060da9a2016-03-11 19:16:35 -08001131 backgroundService.execute(() -> {
Madan Jampani328371d2015-05-29 14:06:27 -07001132 try {
1133 handleMastershipEvent(event);
1134 } catch (Exception e) {
1135 log.warn("Failed to handle {}", event, e);
Yuta HIGUCHId26354d2014-10-31 14:14:38 -07001136 }
Madan Jampani328371d2015-05-29 14:06:27 -07001137 });
Ayaka Koshibe317245a2014-10-29 00:34:43 -07001138 }
tomb41d1ac2014-09-24 01:51:24 -07001139 }
tomf80c9722014-09-24 14:49:18 -07001140
1141 // Store delegate to re-post events emitted from the store.
Thomas Vachuskab17c41f2015-05-19 11:16:05 -07001142 private class InternalStoreDelegate implements DeviceStoreDelegate {
tomf80c9722014-09-24 14:49:18 -07001143 @Override
1144 public void notify(DeviceEvent event) {
1145 post(event);
Rafał Szalecki9fb87f62017-12-06 15:06:09 +01001146 if (event.type().equals(DeviceEvent.Type.DEVICE_REMOVED)) {
Thomas Vachuska5b38dc02018-05-10 15:24:40 -07001147 // When device is administratively removed, force disconnect.
1148 DeviceId deviceId = event.subject().id();
1149 deviceLocalStatus.remove(deviceId);
1150
1151 DeviceProvider provider = getProvider(deviceId);
1152 if (provider != null) {
1153 log.info("Triggering disconnect for device {}", deviceId);
1154 try {
1155 provider.triggerDisconnect(deviceId);
1156 } catch (UnsupportedOperationException e) {
1157 log.warn("Unable to trigger disconnect due to {}", e.getMessage());
1158 }
1159 }
Rafał Szalecki9fb87f62017-12-06 15:06:09 +01001160 }
tomf80c9722014-09-24 14:49:18 -07001161 }
1162 }
samuel738dfaf2015-07-11 11:08:57 +08001163
1164 @Override
1165 public Iterable<Device> getDevices(Type type) {
Changhoon Yoonb856b812015-08-10 03:47:19 +09001166 checkPermission(DEVICE_READ);
samuel738dfaf2015-07-11 11:08:57 +08001167 Set<Device> results = new HashSet<>();
1168 Iterable<Device> devices = store.getDevices();
1169 if (devices != null) {
1170 devices.forEach(d -> {
1171 if (type.equals(d.type())) {
1172 results.add(d);
1173 }
1174 });
1175 }
1176 return results;
1177 }
1178
1179 @Override
1180 public Iterable<Device> getAvailableDevices(Type type) {
Changhoon Yoonb856b812015-08-10 03:47:19 +09001181 checkPermission(DEVICE_READ);
samuel738dfaf2015-07-11 11:08:57 +08001182 Set<Device> results = new HashSet<>();
1183 Iterable<Device> availableDevices = store.getAvailableDevices();
1184 if (availableDevices != null) {
1185 availableDevices.forEach(d -> {
1186 if (type.equals(d.type())) {
1187 results.add(d);
1188 }
1189 });
1190 }
1191 return results;
1192 }
Sahil Lele3a0cdd52015-07-21 14:16:31 -07001193
1194 private class InternalNetworkConfigListener implements NetworkConfigListener {
Ray Milkey69ca82a2019-03-28 09:24:40 -07001195 private DeviceId extractDeviceId(NetworkConfigEvent event) {
1196 DeviceId deviceId = null;
debmaiti9553ed72019-03-18 14:27:42 +05301197 if (event.configClass().equals(PortAnnotationConfig.class)) {
Ray Milkey397caca2019-04-01 16:27:50 -07001198 if (event.subject().getClass() == ConnectPoint.class) {
1199 deviceId = ((ConnectPoint) event.subject()).deviceId();
1200 }
Ray Milkey69ca82a2019-03-28 09:24:40 -07001201 } else if (event.subject().getClass() == DeviceId.class) {
debmaiti9553ed72019-03-18 14:27:42 +05301202 deviceId = (DeviceId) event.subject();
1203 }
Ray Milkey69ca82a2019-03-28 09:24:40 -07001204 return deviceId;
1205 }
1206
1207 @Override
1208 public boolean isRelevant(NetworkConfigEvent event) {
1209 DeviceId deviceId = extractDeviceId(event);
1210
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001211 return (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED
debmaiti9553ed72019-03-18 14:27:42 +05301212 || event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED
1213 || event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED)
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001214 && (event.configClass().equals(BasicDeviceConfig.class)
Andrea Campanella75ef9f52017-07-27 20:14:32 +02001215 || portOpsIndex.containsKey(event.configClass())
Palash Kalaa06a6162017-11-15 20:42:40 +09001216 || event.configClass().equals(PortDescriptionsConfig.class)
Thomas Vachuskaf131e592018-05-07 11:52:14 -07001217 || event.configClass().equals(DeviceAnnotationConfig.class))
Ray Milkey69ca82a2019-03-28 09:24:40 -07001218 && deviceId != null && mastershipService.isLocalMaster(deviceId);
Thomas Vachuska42e8cce2015-07-29 19:25:18 -07001219 }
1220
1221 @Override
Sahil Lele3a0cdd52015-07-21 14:16:31 -07001222 public void event(NetworkConfigEvent event) {
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001223 DeviceEvent de = null;
1224 if (event.configClass().equals(BasicDeviceConfig.class)) {
Thomas Vachuska138de8b2016-01-11 21:31:38 -08001225 log.debug("Detected device network config event {}", event.type());
Yuta HIGUCHI1d4b3aa2017-08-09 15:10:18 -07001226 DeviceId did = (DeviceId) event.subject();
1227 DeviceProvider dp = getProvider(did);
Simon Huntffbad3b2017-05-16 15:37:51 -07001228 BasicDeviceConfig cfg =
1229 networkConfigService.getConfig(did, BasicDeviceConfig.class);
Sahil Lele3a0cdd52015-07-21 14:16:31 -07001230
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001231 if (!isAllowed(cfg)) {
1232 kickOutBadDevice(did);
1233 } else {
1234 Device dev = getDevice(did);
Simon Huntffbad3b2017-05-16 15:37:51 -07001235 DeviceDescription desc =
1236 (dev == null) ? null : BasicDeviceOperator.descriptionOf(dev);
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001237 desc = BasicDeviceOperator.combine(cfg, desc);
Simon Huntffbad3b2017-05-16 15:37:51 -07001238 if (desc != null && dp != null) {
pier388ec252020-04-15 20:53:14 +02001239 store.createOrUpdateDevice(dp.id(), did, desc);
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001240 }
1241 }
Yuta HIGUCHI1d4b3aa2017-08-09 15:10:18 -07001242 } else if (event.configClass().equals(PortDescriptionsConfig.class)) {
1243 DeviceId did = (DeviceId) event.subject();
1244 DeviceProvider dp = getProvider(did);
1245 if (!event.config().isPresent() ||
1246 getDevice(did) == null || dp == null) {
1247 // sanity check failed, ignore
1248 return;
1249 }
Andrea Campanella75ef9f52017-07-27 20:14:32 +02001250 PortDescriptionsConfig portConfig = (PortDescriptionsConfig) event.config().get();
Yuta HIGUCHI1d4b3aa2017-08-09 15:10:18 -07001251 //updating the ports if configuration exists
Andrea Campanella75ef9f52017-07-27 20:14:32 +02001252 List<PortDescription> complete = store.getPortDescriptions(dp.id(), did)
1253 .collect(Collectors.toList());
1254 complete.addAll(portConfig.portDescriptions());
1255 store.updatePorts(dp.id(), did, complete);
Palash Kalaa06a6162017-11-15 20:42:40 +09001256 } else if (event.configClass().equals(DeviceAnnotationConfig.class)) {
1257 DeviceId did = (DeviceId) event.subject();
1258 DeviceProvider dp = getProvider(did);
1259 Device dev = getDevice(did);
1260 DeviceDescription desc =
1261 (dev == null) ? null : BasicDeviceOperator.descriptionOf(dev);
1262 Optional<Config> prevConfig = event.prevConfig();
Anurag Chadha61946392020-08-28 15:18:03 +05301263 if (desc != null) { // Fix for NPE due to desc being null
1264 desc = deviceAnnotationOp.combine(did, desc, prevConfig);
1265 }
Palash Kalaa06a6162017-11-15 20:42:40 +09001266 if (desc != null && dp != null) {
pier388ec252020-04-15 20:53:14 +02001267 store.createOrUpdateDevice(dp.id(), did, desc);
Palash Kalaa06a6162017-11-15 20:42:40 +09001268 }
Yuta HIGUCHI1d4b3aa2017-08-09 15:10:18 -07001269 } else if (portOpsIndex.containsKey(event.configClass())) {
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001270 ConnectPoint cpt = (ConnectPoint) event.subject();
Yuta HIGUCHI1d4b3aa2017-08-09 15:10:18 -07001271 DeviceId did = cpt.deviceId();
1272 DeviceProvider dp = getProvider(did);
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001273
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001274 // Note: assuming PortOperator can modify existing port,
1275 // but cannot add new port purely from Config.
Simon Huntffbad3b2017-05-16 15:37:51 -07001276 de = Optional.ofNullable(dp)
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001277 .map(provider -> store.getPortDescription(provider.id(), did, cpt.port()))
debmaiti9553ed72019-03-18 14:27:42 +05301278 .map(desc -> applyAllPortOps(cpt, desc, event.prevConfig()))
Simon Huntffbad3b2017-05-16 15:37:51 -07001279 .map(desc -> store.updatePortStatus(dp.id(), did, desc))
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001280 .orElse(null);
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001281 }
1282
1283 if (de != null) {
1284 post(de);
1285 }
1286 }
1287
Simon Hunt8f60ff82017-04-24 17:19:30 -07001288 // removes the specified device if it exists
Ayaka Koshibed0ab3c02015-09-04 15:43:46 -07001289 private void kickOutBadDevice(DeviceId deviceId) {
Sahil Lele3a0cdd52015-07-21 14:16:31 -07001290 Device badDevice = getDevice(deviceId);
1291 if (badDevice != null) {
1292 removeDevice(deviceId);
Sahil Lele3a0cdd52015-07-21 14:16:31 -07001293 }
1294 }
1295 }
Saurav Dasa2d37502016-03-25 17:50:40 -07001296
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001297 @Override
1298 @SafeVarargs
1299 public final void registerPortConfigOperator(PortConfigOperator portOp,
Simon Huntffbad3b2017-05-16 15:37:51 -07001300 Class<? extends Config<ConnectPoint>>... configs) {
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001301 checkNotNull(portOp);
1302
1303 portOp.bindService(networkConfigService);
1304
1305 // update both portOpsIndex and portOps
1306 synchronized (portOpsIndex) {
1307 for (Class<? extends Config<ConnectPoint>> config : configs) {
1308 portOpsIndex.put(config, portOp);
1309 }
1310
1311 portOps.add(portOp);
1312 }
1313
1314 // TODO: Should we be applying to all existing Ports?
1315 Tools.stream(store.getAvailableDevices())
Simon Huntffbad3b2017-05-16 15:37:51 -07001316 .map(Device::id)
1317 .filter(mastershipService::isLocalMaster)
1318 // for each locally managed Device, update all port descriptions
1319 .map(did -> {
1320 ProviderId pid = Optional.ofNullable(getProvider(did))
1321 .map(Provider::id)
1322 .orElse(null);
1323 if (pid == null) {
1324 log.warn("Provider not found for {}", did);
1325 return ImmutableList.<DeviceEvent>of();
1326 }
1327 List<PortDescription> pds
1328 = store.getPortDescriptions(pid, did)
1329 .map(pdesc -> applyAllPortOps(did, pdesc))
1330 .collect(Collectors.toList());
1331 return store.updatePorts(pid, did, pds);
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001332 })
Simon Huntffbad3b2017-05-16 15:37:51 -07001333 // ..and port port update event if necessary
1334 .forEach(evts -> evts.forEach(this::post));
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001335 }
1336
1337 @Override
1338 public void unregisterPortConfigOperator(PortConfigOperator portOp) {
1339 checkNotNull(portOp);
1340
1341
1342 // remove all portOp
1343 synchronized (portOpsIndex) {
1344 portOps.remove(portOp);
1345
1346 // had to do this since COWArrayList iterator doesn't support remove
1347 portOpsIndex.keySet().forEach(key -> portOpsIndex.remove(key, portOp));
1348 }
1349
1350 }
1351
1352 /**
1353 * Merges the appropriate PortConfig with the description.
1354 *
1355 * @param did ID of the Device where the port is attached
1356 * @param desc {@link PortDescription}
1357 * @return merged {@link PortDescription}
1358 */
1359 private PortDescription applyAllPortOps(DeviceId did, PortDescription desc) {
1360 return applyAllPortOps(new ConnectPoint(did, desc.portNumber()), desc);
1361 }
1362
1363 /**
1364 * Merges the appropriate PortConfig with the description.
1365 *
Simon Huntffbad3b2017-05-16 15:37:51 -07001366 * @param cpt ConnectPoint where the port is attached
1367 * @param desc {@link PortDescription}
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001368 * @return merged {@link PortDescription}
1369 */
1370 private PortDescription applyAllPortOps(ConnectPoint cpt, PortDescription desc) {
1371 PortDescription work = desc;
1372 for (PortConfigOperator portOp : portOps) {
1373 work = portOp.combine(cpt, work);
1374 }
Yuta HIGUCHI7438f5a2017-02-15 22:09:46 -08001375 return portAnnotationOp.combine(cpt, work);
Yuta HIGUCHIb9af6b72016-06-10 10:52:58 -07001376 }
1377
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -07001378 /**
debmaiti9553ed72019-03-18 14:27:42 +05301379 * Merges the appropriate PortConfig with the description.
1380 *
1381 * @param cpt ConnectPoint where the port is attached
1382 * @param desc {@link PortDescription}
1383 * @param prevConfig previous configuration
1384 * @return merged {@link PortDescription}
1385 */
1386 private PortDescription applyAllPortOps(ConnectPoint cpt, PortDescription desc,
1387 Optional<Config> prevConfig) {
1388 PortDescription work = desc;
1389 for (PortConfigOperator portOp : portOps) {
1390 work = portOp.combine(cpt, work, prevConfig);
1391 }
1392 return portAnnotationOp.combine(cpt, work, prevConfig);
1393 }
1394
1395 /**
pierventreb2f636b2022-01-03 17:19:24 +01001396 * Handler for remote probe requests.
1397 *
1398 * @param deviceId the device to check
1399 * @return whether or not the device is reachable
1400 */
1401 private boolean handleProbeRequest(DeviceId deviceId) {
1402 int attempt = 0;
1403 // Let's do a number of attempts
1404 while (attempt < PROBE_ATTEMPTS) {
1405 if (!probeReachability(deviceId)) {
1406 return false;
1407 }
1408 attempt++;
1409 }
1410 return true;
1411 }
1412
1413 /**
1414 * Update the mastership for this device. If there is a node able
1415 * to reach the device and this node is the master move the
1416 * mastership to the next node still connected to this device.
1417 * If the current node is a backup, it demotes itself to the bottom
1418 * of the candidates list
1419 *
1420 * @param deviceId the device for which we have to update the mastership
1421 * @return the NodeId of any node that can reach the device, or null if
1422 * none of the ONOS instances can reach the device
1423 */
1424 private NodeId updateMastershipFor(DeviceId deviceId) {
1425 Map<NodeId, CompletableFuture<Boolean>> probes = Maps.newHashMap();
1426 // Request a probe only if the node is ready
1427 for (ControllerNode onosNode : clusterService.getNodes()) {
1428 if (!clusterService.getState(onosNode.id()).isReady() || localNodeId.equals(onosNode.id())) {
1429 continue;
1430 }
1431 probes.put(onosNode.id(), communicationService.sendAndReceive(deviceId, PROBE_SUBJECT, SERIALIZER::encode,
1432 SERIALIZER::decode, onosNode.id()));
1433 }
1434
1435 // Returns the first node able to reach the device
1436 // FIXME [SDFAB-935] optimize by looking at the MastershipInfo
1437 boolean isReachable;
1438 NodeId nextMaster = null;
1439 // FIXME Should we expose timeout? Understand if there is need to signal to the caller
1440 for (Map.Entry<NodeId, CompletableFuture<Boolean>> probe : probes.entrySet()) {
1441 isReachable = Tools.futureGetOrElse(probe.getValue(), PROBE_TIMEOUT_MILLIS,
1442 TimeUnit.MILLISECONDS, Boolean.FALSE);
1443 if (isReachable) {
1444 nextMaster = probe.getKey();
1445 }
1446 }
1447
1448 // FIXME [SDFAB-935] optimize demote by looking at the MastershipInfo;
1449 if (nextMaster != null) {
1450 log.info("Device {} is still connected to {}", deviceId, nextMaster);
1451 MastershipRole myRole = mastershipService.getLocalRole(deviceId);
1452 if (myRole == MASTER) {
1453 log.info("Handing over the mastership of {} to next master {}", deviceId, nextMaster);
1454 mastershipAdminService.setRole(nextMaster, deviceId, MASTER);
1455 /* Do not demote here because setRole can return before the mastership has been passed.
1456 Current implementation promotes first the nextMaster as top of candidate list and then
1457 transfer the leadership. We can use the BACKUP events to do demote or leverage periodic
1458 checks.*/
1459 } else if (myRole == STANDBY) {
1460 log.info("Demote current instance to the bottom of the candidates list for {}", deviceId);
1461 mastershipAdminService.demote(localNodeId, deviceId);
1462 } else {
1463 log.debug("No valid role for {}", deviceId);
1464 }
1465 }
1466
1467 return nextMaster;
1468 }
1469
1470
1471
1472 /**
Jonghwan Hyun6ecf56d2017-08-01 16:07:44 -07001473 * Port Enable/Disable message sent to the device's MASTER node.
1474 */
1475 private class InternalPortUpDownEvent {
1476 private final DeviceId deviceId;
1477 private final PortNumber portNumber;
1478 private final boolean enable;
1479
1480 protected InternalPortUpDownEvent(
1481 DeviceId deviceId, PortNumber portNumber, boolean enable) {
1482 this.deviceId = deviceId;
1483 this.portNumber = portNumber;
1484 this.enable = enable;
1485 }
1486
1487 public DeviceId deviceId() {
1488 return deviceId;
1489 }
1490 public PortNumber portNumber() {
1491 return portNumber;
1492 }
1493 public boolean isEnable() {
1494 return enable;
1495 }
1496
1497 protected InternalPortUpDownEvent() {
1498 this.deviceId = null;
1499 this.portNumber = null;
1500 this.enable = false;
1501 }
1502 }
tomd3097b02014-08-26 10:40:29 -07001503}