blob: 6c117b86eb508cc8a63677c6d87edd7a0bec5649 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
tombe988312014-09-19 18:38:47 -070016package org.onlab.onos.net.device.impl;
tomd3097b02014-08-26 10:40:29 -070017
alshabib339a3d92014-09-26 17:54:32 -070018import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHId26354d2014-10-31 14:14:38 -070019import static org.onlab.onos.net.MastershipRole.*;
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -080020import static org.onlab.util.Tools.namedThreads;
alshabib339a3d92014-09-26 17:54:32 -070021import static org.slf4j.LoggerFactory.getLogger;
22
23import java.util.List;
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -080024import java.util.concurrent.Executors;
25import java.util.concurrent.ScheduledExecutorService;
26import java.util.concurrent.TimeUnit;
alshabib339a3d92014-09-26 17:54:32 -070027
alshabibafc514a2014-12-01 14:44:05 -080028
29import com.google.common.collect.Lists;
tomd3097b02014-08-26 10:40:29 -070030import org.apache.felix.scr.annotations.Activate;
31import org.apache.felix.scr.annotations.Component;
32import org.apache.felix.scr.annotations.Deactivate;
tom5f38b3a2014-08-27 23:50:54 -070033import org.apache.felix.scr.annotations.Reference;
34import org.apache.felix.scr.annotations.ReferenceCardinality;
tomd3097b02014-08-26 10:40:29 -070035import org.apache.felix.scr.annotations.Service;
tomb41d1ac2014-09-24 01:51:24 -070036import org.onlab.onos.cluster.ClusterService;
Yuta HIGUCHI8e939d82014-10-08 08:09:18 -070037import org.onlab.onos.cluster.NodeId;
tom96dfcab2014-08-28 09:26:03 -070038import org.onlab.onos.event.AbstractListenerRegistry;
39import org.onlab.onos.event.EventDeliveryService;
Yuta HIGUCHI80912e62014-10-12 00:15:47 -070040import org.onlab.onos.mastership.MastershipEvent;
41import org.onlab.onos.mastership.MastershipListener;
42import org.onlab.onos.mastership.MastershipService;
43import org.onlab.onos.mastership.MastershipTerm;
44import org.onlab.onos.mastership.MastershipTermService;
tom32f66842014-08-27 19:27:47 -070045import org.onlab.onos.net.Device;
tomd3097b02014-08-26 10:40:29 -070046import org.onlab.onos.net.DeviceId;
47import org.onlab.onos.net.MastershipRole;
tom32f66842014-08-27 19:27:47 -070048import org.onlab.onos.net.Port;
49import org.onlab.onos.net.PortNumber;
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -070050import org.onlab.onos.net.device.DefaultDeviceDescription;
alshabibafc514a2014-12-01 14:44:05 -080051import org.onlab.onos.net.device.DefaultPortDescription;
tome5ec3fd2014-09-04 15:18:06 -070052import org.onlab.onos.net.device.DeviceAdminService;
Yuta HIGUCHI093e83e2014-10-10 22:26:11 -070053import org.onlab.onos.net.device.DeviceClockProviderService;
tomd3097b02014-08-26 10:40:29 -070054import org.onlab.onos.net.device.DeviceDescription;
tom32f66842014-08-27 19:27:47 -070055import org.onlab.onos.net.device.DeviceEvent;
56import org.onlab.onos.net.device.DeviceListener;
tomd3097b02014-08-26 10:40:29 -070057import org.onlab.onos.net.device.DeviceProvider;
tom96dfcab2014-08-28 09:26:03 -070058import org.onlab.onos.net.device.DeviceProviderRegistry;
tomd3097b02014-08-26 10:40:29 -070059import org.onlab.onos.net.device.DeviceProviderService;
tom32f66842014-08-27 19:27:47 -070060import org.onlab.onos.net.device.DeviceService;
tom41a2c5f2014-09-19 09:20:35 -070061import org.onlab.onos.net.device.DeviceStore;
tomf80c9722014-09-24 14:49:18 -070062import org.onlab.onos.net.device.DeviceStoreDelegate;
tomd3097b02014-08-26 10:40:29 -070063import org.onlab.onos.net.device.PortDescription;
tom96dfcab2014-08-28 09:26:03 -070064import org.onlab.onos.net.provider.AbstractProviderRegistry;
tomd3097b02014-08-26 10:40:29 -070065import org.onlab.onos.net.provider.AbstractProviderService;
66import org.slf4j.Logger;
tomd3097b02014-08-26 10:40:29 -070067
tomd3097b02014-08-26 10:40:29 -070068/**
tome4729872014-09-23 00:37:37 -070069 * Provides implementation of the device SB & NB APIs.
tomd3097b02014-08-26 10:40:29 -070070 */
71@Component(immediate = true)
72@Service
tom41a2c5f2014-09-19 09:20:35 -070073public class DeviceManager
Thomas Vachuskad16ce182014-10-29 17:25:29 -070074 extends AbstractProviderRegistry<DeviceProvider, DeviceProviderService>
75 implements DeviceService, DeviceAdminService, DeviceProviderRegistry {
tom32f66842014-08-27 19:27:47 -070076
tome5ec3fd2014-09-04 15:18:06 -070077 private static final String DEVICE_ID_NULL = "Device ID cannot be null";
78 private static final String PORT_NUMBER_NULL = "Port number cannot be null";
79 private static final String DEVICE_DESCRIPTION_NULL = "Device description cannot be null";
80 private static final String PORT_DESCRIPTION_NULL = "Port description cannot be null";
81 private static final String ROLE_NULL = "Role cannot be null";
tomd3097b02014-08-26 10:40:29 -070082
tom5f38b3a2014-08-27 23:50:54 -070083 private final Logger log = getLogger(getClass());
tomd3097b02014-08-26 10:40:29 -070084
alshabib271a6202014-09-28 22:11:21 -070085 protected final AbstractListenerRegistry<DeviceEvent, DeviceListener> listenerRegistry =
86 new AbstractListenerRegistry<>();
tom32f66842014-08-27 19:27:47 -070087
alshabib339a3d92014-09-26 17:54:32 -070088 private final DeviceStoreDelegate delegate = new InternalStoreDelegate();
tomf80c9722014-09-24 14:49:18 -070089
tomc78acee2014-09-24 15:16:55 -070090 private final MastershipListener mastershipListener = new InternalMastershipListener();
tomb41d1ac2014-09-24 01:51:24 -070091
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -080092 private ScheduledExecutorService backgroundService;
93
tom41a2c5f2014-09-19 09:20:35 -070094 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected DeviceStore store;
tomd3097b02014-08-26 10:40:29 -070096
tom5f38b3a2014-08-27 23:50:54 -070097 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tome5ec3fd2014-09-04 15:18:06 -070098 protected EventDeliveryService eventDispatcher;
tom5f38b3a2014-08-27 23:50:54 -070099
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tomb41d1ac2014-09-24 01:51:24 -0700101 protected ClusterService clusterService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700104 protected MastershipService mastershipService;
105
Yuta HIGUCHIbcac4992014-11-22 19:27:57 -0800106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700107 protected MastershipTermService termService;
108
Madan Jampani61056bc2014-09-27 09:07:26 -0700109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yuta HIGUCHI093e83e2014-10-10 22:26:11 -0700110 protected DeviceClockProviderService deviceClockProviderService;
Madan Jampani61056bc2014-09-27 09:07:26 -0700111
tomd3097b02014-08-26 10:40:29 -0700112 @Activate
113 public void activate() {
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800114 backgroundService = Executors.newSingleThreadScheduledExecutor(namedThreads("device-manager-background"));
115
tomf80c9722014-09-24 14:49:18 -0700116 store.setDelegate(delegate);
tom96dfcab2014-08-28 09:26:03 -0700117 eventDispatcher.addSink(DeviceEvent.class, listenerRegistry);
tomb41d1ac2014-09-24 01:51:24 -0700118 mastershipService.addListener(mastershipListener);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800119
120 backgroundService.scheduleWithFixedDelay(new Runnable() {
121
122 @Override
123 public void run() {
124 try {
125 mastershipCheck();
126 } catch (Exception e) {
127 log.error("Exception thrown during integrity check", e);
128 }
129 }
130 }, 1, 1, TimeUnit.MINUTES);
tomd3097b02014-08-26 10:40:29 -0700131 log.info("Started");
132 }
133
134 @Deactivate
135 public void deactivate() {
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800136 backgroundService.shutdown();
137
tomf80c9722014-09-24 14:49:18 -0700138 store.unsetDelegate(delegate);
tomb41d1ac2014-09-24 01:51:24 -0700139 mastershipService.removeListener(mastershipListener);
tom5f38b3a2014-08-27 23:50:54 -0700140 eventDispatcher.removeSink(DeviceEvent.class);
tomd3097b02014-08-26 10:40:29 -0700141 log.info("Stopped");
142 }
143
144 @Override
tomad2d2092014-09-06 23:24:20 -0700145 public int getDeviceCount() {
146 return store.getDeviceCount();
tomd3097b02014-08-26 10:40:29 -0700147 }
148
149 @Override
tom32f66842014-08-27 19:27:47 -0700150 public Iterable<Device> getDevices() {
tome5ec3fd2014-09-04 15:18:06 -0700151 return store.getDevices();
tomd3097b02014-08-26 10:40:29 -0700152 }
153
tom32f66842014-08-27 19:27:47 -0700154 @Override
Yuta HIGUCHIf1f2ac02014-11-26 14:02:22 -0800155 public Iterable<Device> getAvailableDevices() {
156 return store.getAvailableDevices();
157 }
158
159 @Override
tom32f66842014-08-27 19:27:47 -0700160 public Device getDevice(DeviceId deviceId) {
161 checkNotNull(deviceId, DEVICE_ID_NULL);
tom132b58a2014-08-28 16:11:28 -0700162 return store.getDevice(deviceId);
tom32f66842014-08-27 19:27:47 -0700163 }
164
165 @Override
tomad2d2092014-09-06 23:24:20 -0700166 public MastershipRole getRole(DeviceId deviceId) {
167 checkNotNull(deviceId, DEVICE_ID_NULL);
tomb41d1ac2014-09-24 01:51:24 -0700168 return mastershipService.getLocalRole(deviceId);
tomad2d2092014-09-06 23:24:20 -0700169 }
170
171 @Override
tom32f66842014-08-27 19:27:47 -0700172 public List<Port> getPorts(DeviceId deviceId) {
173 checkNotNull(deviceId, DEVICE_ID_NULL);
tom132b58a2014-08-28 16:11:28 -0700174 return store.getPorts(deviceId);
tom32f66842014-08-27 19:27:47 -0700175 }
176
177 @Override
178 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
179 checkNotNull(deviceId, DEVICE_ID_NULL);
180 checkNotNull(portNumber, PORT_NUMBER_NULL);
tom132b58a2014-08-28 16:11:28 -0700181 return store.getPort(deviceId, portNumber);
tom32f66842014-08-27 19:27:47 -0700182 }
183
184 @Override
tomff7eb7c2014-09-08 12:49:03 -0700185 public boolean isAvailable(DeviceId deviceId) {
186 checkNotNull(deviceId, DEVICE_ID_NULL);
187 return store.isAvailable(deviceId);
188 }
189
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700190 // Check a device for control channel connectivity.
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700191 private boolean isReachable(DeviceId deviceId) {
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -0800192 if (deviceId == null) {
193 return false;
194 }
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700195 DeviceProvider provider = getProvider(deviceId);
196 if (provider != null) {
197 return provider.isReachable(deviceId);
198 } else {
Yuta HIGUCHI72669c42014-11-13 14:48:17 -0800199 log.debug("Provider not found for {}", deviceId);
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700200 return false;
201 }
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700202 }
203
tome5ec3fd2014-09-04 15:18:06 -0700204 @Override
205 public void removeDevice(DeviceId deviceId) {
206 checkNotNull(deviceId, DEVICE_ID_NULL);
207 DeviceEvent event = store.removeDevice(deviceId);
tom0efbb1d2014-09-09 11:54:28 -0700208 if (event != null) {
209 log.info("Device {} administratively removed", deviceId);
210 post(event);
211 }
tome5ec3fd2014-09-04 15:18:06 -0700212 }
213
tom7869ad92014-09-09 14:32:08 -0700214 @Override
215 public void addListener(DeviceListener listener) {
216 listenerRegistry.addListener(listener);
217 }
218
219 @Override
220 public void removeListener(DeviceListener listener) {
221 listenerRegistry.removeListener(listener);
222 }
223
224 @Override
alshabibb7b40632014-09-28 21:30:00 -0700225 protected DeviceProviderService createProviderService(
226 DeviceProvider provider) {
tom7869ad92014-09-09 14:32:08 -0700227 return new InternalDeviceProviderService(provider);
228 }
229
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800230 /**
231 * Checks if all the reachable devices have a valid mastership role.
232 */
233 private void mastershipCheck() {
234 log.debug("Checking mastership");
235 for (Device device : getDevices()) {
236 final DeviceId deviceId = device.id();
237 log.debug("Checking device {}", deviceId);
238
239 if (!isReachable(deviceId)) {
240 continue;
241 }
242
243 if (mastershipService.getLocalRole(deviceId) != NONE) {
244 continue;
245 }
246
247 log.info("{} is reachable but did not have a valid role, reasserting", deviceId);
248
249 // isReachable but was not MASTER or STANDBY, get a role and apply
250 // Note: NONE triggers request to MastershipService
251 reassertRole(deviceId, NONE);
252 }
253 }
254
tomd3097b02014-08-26 10:40:29 -0700255 // Personalized device provider service issued to the supplied provider.
tomdc361b62014-09-09 20:36:52 -0700256 private class InternalDeviceProviderService
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700257 extends AbstractProviderService<DeviceProvider>
258 implements DeviceProviderService {
tomd3097b02014-08-26 10:40:29 -0700259
tomcfde0622014-09-09 11:02:42 -0700260 InternalDeviceProviderService(DeviceProvider provider) {
tomd3097b02014-08-26 10:40:29 -0700261 super(provider);
262 }
263
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700264 /**
265 * Apply role in reaction to provider event.
266 *
267 * @param deviceId device identifier
268 * @param newRole new role to apply to the device
269 * @return true if the request was sent to provider
270 */
271 private boolean applyRole(DeviceId deviceId, MastershipRole newRole) {
272
273 if (newRole.equals(MastershipRole.NONE)) {
274 //no-op
275 return true;
276 }
277
278 DeviceProvider provider = provider();
279 if (provider == null) {
280 log.warn("Provider for {} was not found. Cannot apply role {}", deviceId, newRole);
281 return false;
282 }
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700283 provider.roleChanged(deviceId, newRole);
284 // not triggering probe when triggered by provider service event
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700285
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700286 return true;
287 }
288
289
tomd3097b02014-08-26 10:40:29 -0700290 @Override
alshabibb7b40632014-09-28 21:30:00 -0700291 public void deviceConnected(DeviceId deviceId,
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700292 DeviceDescription deviceDescription) {
tom32f66842014-08-27 19:27:47 -0700293 checkNotNull(deviceId, DEVICE_ID_NULL);
294 checkNotNull(deviceDescription, DEVICE_DESCRIPTION_NULL);
tomeadbb462014-09-07 16:10:19 -0700295 checkValidity();
Yuta HIGUCHI24b2e2a2014-10-07 15:53:57 -0700296
297 log.info("Device {} connected", deviceId);
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -0700298 final NodeId myNodeId = clusterService.getLocalNode().id();
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700299
300 // check my Role
301 mastershipService.requestRoleFor(deviceId);
302 final MastershipTerm term = termService.getMastershipTerm(deviceId);
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -0700303 if (!myNodeId.equals(term.master())) {
Yuta HIGUCHI2d3cd312014-10-31 11:38:04 -0700304 log.info("Role of this node is STANDBY for {}", deviceId);
305 // TODO: Do we need to explicitly tell the Provider that
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700306 // this instance is not the MASTER
307 applyRole(deviceId, MastershipRole.STANDBY);
Yuta HIGUCHI24b2e2a2014-10-07 15:53:57 -0700308 return;
309 }
Yuta HIGUCHI2d3cd312014-10-31 11:38:04 -0700310 log.info("Role of this node is MASTER for {}", deviceId);
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700311
Yuta HIGUCHIcf603902014-10-07 23:04:32 -0700312 // tell clock provider if this instance is the master
Yuta HIGUCHI093e83e2014-10-10 22:26:11 -0700313 deviceClockProviderService.setMastershipTerm(deviceId, term);
Yuta HIGUCHI24b2e2a2014-10-07 15:53:57 -0700314
tome5ec3fd2014-09-04 15:18:06 -0700315 DeviceEvent event = store.createOrUpdateDevice(provider().id(),
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700316 deviceId, deviceDescription);
tom80c0e5e2014-09-08 18:08:58 -0700317
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700318 applyRole(deviceId, MastershipRole.MASTER);
319
Yuta HIGUCHI24b2e2a2014-10-07 15:53:57 -0700320 // If there was a change of any kind, tell the provider
Yuta HIGUCHIcf603902014-10-07 23:04:32 -0700321 // that this instance is the master.
tom80c0e5e2014-09-08 18:08:58 -0700322 if (event != null) {
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700323 log.trace("event: {} {}", event.type(), event);
tom568581d2014-09-08 20:13:36 -0700324 post(event);
tom80c0e5e2014-09-08 18:08:58 -0700325 }
tomd3097b02014-08-26 10:40:29 -0700326 }
327
328 @Override
329 public void deviceDisconnected(DeviceId deviceId) {
tom32f66842014-08-27 19:27:47 -0700330 checkNotNull(deviceId, DEVICE_ID_NULL);
tomeadbb462014-09-07 16:10:19 -0700331 checkValidity();
Yuta HIGUCHIcf603902014-10-07 23:04:32 -0700332
Yuta HIGUCHI2d3cd312014-10-31 11:38:04 -0700333 log.info("Device {} disconnected from this node", deviceId);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700334
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700335 DeviceEvent event = null;
alshabibafc514a2014-12-01 14:44:05 -0800336 List<Port> ports = store.getPorts(deviceId);
337 List<PortDescription> descs = Lists.newArrayList();
338 ports.forEach(port ->
339 descs.add(new DefaultPortDescription(port.number(),
340 false, port.type(),
341 port.portSpeed())));
342 store.updatePorts(this.provider().id(), deviceId, descs);
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700343 try {
344 event = store.markOffline(deviceId);
345 } catch (IllegalStateException e) {
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700346 log.warn("Failed to mark {} offline", deviceId);
347 // only the MASTER should be marking off-line in normal cases,
348 // but if I was the last STANDBY connection, etc. and no one else
349 // was there to mark the device offline, this instance may need to
350 // temporarily request for Master Role and mark offline.
351
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700352 //there are times when this node will correctly have mastership, BUT
353 //that isn't reflected in the ClockManager before the device disconnects.
354 //we want to let go of the device anyways, so make sure this happens.
Yuta HIGUCHI0722fb22014-10-19 01:16:33 -0700355
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700356 // FIXME: Store semantics leaking out as IllegalStateException.
357 // Consider revising store API to handle this scenario.
358
359 MastershipRole role = mastershipService.requestRoleFor(deviceId);
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700360 MastershipTerm term = termService.getMastershipTerm(deviceId);
Yuta HIGUCHI0722fb22014-10-19 01:16:33 -0700361 final NodeId myNodeId = clusterService.getLocalNode().id();
362 // TODO: Move this type of check inside device clock manager, etc.
363 if (myNodeId.equals(term.master())) {
Yuta HIGUCHI2d3cd312014-10-31 11:38:04 -0700364 log.info("Retry marking {} offline", deviceId);
Yuta HIGUCHI0722fb22014-10-19 01:16:33 -0700365 deviceClockProviderService.setMastershipTerm(deviceId, term);
366 event = store.markOffline(deviceId);
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700367 } else {
Yuta HIGUCHI2d3cd312014-10-31 11:38:04 -0700368 log.info("Failed again marking {} offline. {}", deviceId, role);
Yuta HIGUCHI0722fb22014-10-19 01:16:33 -0700369 }
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700370 } finally {
371 //relinquish master role and ability to be backup.
372 mastershipService.relinquishMastership(deviceId);
373
374 if (event != null) {
Yuta HIGUCHI2d3cd312014-10-31 11:38:04 -0700375 log.info("Device {} disconnected from cluster", deviceId);
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700376 post(event);
377 }
tom0efbb1d2014-09-09 11:54:28 -0700378 }
tomd3097b02014-08-26 10:40:29 -0700379 }
380
381 @Override
alshabibb7b40632014-09-28 21:30:00 -0700382 public void updatePorts(DeviceId deviceId,
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700383 List<PortDescription> portDescriptions) {
tom32f66842014-08-27 19:27:47 -0700384 checkNotNull(deviceId, DEVICE_ID_NULL);
alshabibb7b40632014-09-28 21:30:00 -0700385 checkNotNull(portDescriptions,
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700386 "Port descriptions list cannot be null");
tomeadbb462014-09-07 16:10:19 -0700387 checkValidity();
Yuta HIGUCHI13c0b872014-10-30 18:09:22 -0700388 if (!deviceClockProviderService.isTimestampAvailable(deviceId)) {
389 // Never been a master for this device
390 // any update will be ignored.
391 log.trace("Ignoring {} port updates on standby node. {}", deviceId, portDescriptions);
392 return;
393 }
394
Yuta HIGUCHI5f6739c2014-10-01 14:04:01 -0700395 List<DeviceEvent> events = store.updatePorts(this.provider().id(),
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700396 deviceId, portDescriptions);
tom32f66842014-08-27 19:27:47 -0700397 for (DeviceEvent event : events) {
398 post(event);
399 }
tomd3097b02014-08-26 10:40:29 -0700400 }
401
402 @Override
alshabibb7b40632014-09-28 21:30:00 -0700403 public void portStatusChanged(DeviceId deviceId,
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700404 PortDescription portDescription) {
tom32f66842014-08-27 19:27:47 -0700405 checkNotNull(deviceId, DEVICE_ID_NULL);
406 checkNotNull(portDescription, PORT_DESCRIPTION_NULL);
tomeadbb462014-09-07 16:10:19 -0700407 checkValidity();
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700408
Yuta HIGUCHI13c0b872014-10-30 18:09:22 -0700409 if (!deviceClockProviderService.isTimestampAvailable(deviceId)) {
410 // Never been a master for this device
411 // any update will be ignored.
412 log.trace("Ignoring {} port update on standby node. {}", deviceId, portDescription);
413 return;
414 }
415
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700416 final DeviceEvent event = store.updatePortStatus(this.provider().id(),
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700417 deviceId, portDescription);
tomff7eb7c2014-09-08 12:49:03 -0700418 if (event != null) {
alshabibb7b40632014-09-28 21:30:00 -0700419 log.info("Device {} port {} status changed", deviceId, event
420 .port().number());
tom0efbb1d2014-09-09 11:54:28 -0700421 post(event);
tomff7eb7c2014-09-08 12:49:03 -0700422 }
tomd3097b02014-08-26 10:40:29 -0700423 }
tom3f2bbd72014-09-24 12:07:58 -0700424
425 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700426 public void receivedRoleReply(
427 DeviceId deviceId, MastershipRole requested, MastershipRole response) {
428 // Several things can happen here:
429 // 1. request and response match
430 // 2. request and response don't match
431 // 3. MastershipRole and requested match (and 1 or 2 are true)
432 // 4. MastershipRole and requested don't match (and 1 or 2 are true)
433 //
434 // 2, 4, and 3 with case 2 are failure modes.
435
tom3f2bbd72014-09-24 12:07:58 -0700436 // FIXME: implement response to this notification
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700437
438 log.info("got reply to a role request for {}: asked for {}, and got {}",
439 deviceId, requested, response);
440
441 if (requested == null && response == null) {
442 // something was off with DeviceProvider, maybe check channel too?
443 log.warn("Failed to assert role [{}] onto Device {}", requested, deviceId);
Yuta HIGUCHIcf603902014-10-07 23:04:32 -0700444 mastershipService.relinquishMastership(deviceId);
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700445 return;
Yuta HIGUCHIcf603902014-10-07 23:04:32 -0700446 }
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700447
448 if (requested.equals(response)) {
449 if (requested.equals(mastershipService.getLocalRole(deviceId))) {
450
451 return;
452 } else {
453 return;
454 // FIXME roleManager got the device to comply, but doesn't agree with
455 // the store; use the store's view, then try to reassert.
456 }
457 } else {
458 // we didn't get back what we asked for. Reelect someone else.
459 log.warn("Failed to assert role [{}] onto Device {}", response, deviceId);
460 if (response == MastershipRole.MASTER) {
461 mastershipService.relinquishMastership(deviceId);
462 // TODO: Shouldn't we be triggering event?
463 //final Device device = getDevice(deviceId);
464 //post(new DeviceEvent(DEVICE_MASTERSHIP_CHANGED, device));
465 }
466 }
467
tom3f2bbd72014-09-24 12:07:58 -0700468 }
tomd3097b02014-08-26 10:40:29 -0700469 }
tom32f66842014-08-27 19:27:47 -0700470
tomeadbb462014-09-07 16:10:19 -0700471 // Posts the specified event to the local event dispatcher.
tom32f66842014-08-27 19:27:47 -0700472 private void post(DeviceEvent event) {
473 if (event != null && eventDispatcher != null) {
474 eventDispatcher.post(event);
475 }
476 }
477
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800478 // Applies the specified role to the device; ignores NONE
479 /**
480 * Apply role to device and send probe if MASTER.
481 *
482 * @param deviceId device identifier
483 * @param newRole new role to apply to the device
484 * @return true if the request was sent to provider
485 */
486 private boolean applyRoleAndProbe(DeviceId deviceId, MastershipRole newRole) {
487 if (newRole.equals(MastershipRole.NONE)) {
488 //no-op
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700489 return true;
490 }
Ayaka Koshibe317245a2014-10-29 00:34:43 -0700491
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -0800492 DeviceProvider provider = getProvider(deviceId);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800493 if (provider == null) {
494 log.warn("Provider for {} was not found. Cannot apply role {}", deviceId, newRole);
495 return false;
496 }
497 provider.roleChanged(deviceId, newRole);
498
499 if (newRole.equals(MastershipRole.MASTER)) {
500 // only trigger event when request was sent to provider
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -0800501 provider.triggerProbe(deviceId);
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800502 }
503 return true;
504 }
505
506 /**
507 * Reaasert role for specified device connected to this node.
508 *
509 * @param did device identifier
510 * @param nextRole role to apply. If NONE is specified,
511 * it will ask mastership service for a role and apply it.
512 */
513 private void reassertRole(final DeviceId did,
514 final MastershipRole nextRole) {
515
516 final NodeId myNodeId = clusterService.getLocalNode().id();
517 MastershipRole myNextRole = nextRole;
518 if (myNextRole == NONE) {
519 mastershipService.requestRoleFor(did);
520 MastershipTerm term = termService.getMastershipTerm(did);
521 if (myNodeId.equals(term.master())) {
522 myNextRole = MASTER;
523 } else {
524 myNextRole = STANDBY;
525 }
526 }
527
528 switch (myNextRole) {
529 case MASTER:
530 final Device device = getDevice(did);
531 if ((device != null) && !isAvailable(did)) {
532 //flag the device as online. Is there a better way to do this?
533 DefaultDeviceDescription deviceDescription
534 = new DefaultDeviceDescription(did.uri(),
535 device.type(),
536 device.manufacturer(),
537 device.hwVersion(),
538 device.swVersion(),
539 device.serialNumber(),
540 device.chassisId());
541 DeviceEvent devEvent =
542 store.createOrUpdateDevice(device.providerId(), did,
543 deviceDescription);
544 post(devEvent);
545 }
546 // TODO: should apply role only if there is mismatch
547 log.info("Applying role {} to {}", myNextRole, did);
548 if (!applyRoleAndProbe(did, MASTER)) {
549 // immediately failed to apply role
550 mastershipService.relinquishMastership(did);
551 // FIXME disconnect?
552 }
553 break;
554 case STANDBY:
555 log.info("Applying role {} to {}", myNextRole, did);
556 if (!applyRoleAndProbe(did, STANDBY)) {
557 // immediately failed to apply role
558 mastershipService.relinquishMastership(did);
559 // FIXME disconnect?
560 }
561 break;
562 case NONE:
563 default:
564 // should never reach here
565 log.error("You didn't see anything. I did not exist.");
566 break;
567 }
568 }
569
570 // Intercepts mastership events
571 private class InternalMastershipListener implements MastershipListener {
572
tomb41d1ac2014-09-24 01:51:24 -0700573 @Override
574 public void event(MastershipEvent event) {
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700575
576 if (event.type() != MastershipEvent.Type.MASTER_CHANGED) {
577 // Don't care if backup list changed.
578 return;
579 }
580
Ayaka Koshibe4c891272014-10-08 17:14:16 -0700581 final DeviceId did = event.subject();
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700582 final NodeId myNodeId = clusterService.getLocalNode().id();
Yuta HIGUCHI24b2e2a2014-10-07 15:53:57 -0700583
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700584 // myRole suggested by MastershipService
585 MastershipRole myNextRole;
Yuta HIGUCHI67dce882014-10-21 21:13:26 -0700586 if (myNodeId.equals(event.roleInfo().master())) {
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700587 // confirm latest info
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700588 MastershipTerm term = termService.getMastershipTerm(did);
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700589 final boolean iHaveControl = myNodeId.equals(term.master());
590 if (iHaveControl) {
Yuta HIGUCHI38b4d9d2014-10-30 11:32:41 -0700591 deviceClockProviderService.setMastershipTerm(did, term);
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700592 myNextRole = MASTER;
Yuta HIGUCHI38b4d9d2014-10-30 11:32:41 -0700593 } else {
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700594 myNextRole = STANDBY;
595 }
596 } else if (event.roleInfo().backups().contains(myNodeId)) {
597 myNextRole = STANDBY;
598 } else {
599 myNextRole = NONE;
600 }
601
602
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700603 final boolean isReachable = isReachable(did);
Yuta HIGUCHId26354d2014-10-31 14:14:38 -0700604 if (!isReachable) {
605 // device is not connected to this node
606 if (myNextRole != NONE) {
607 log.warn("Node was instructed to be {} role for {}, "
608 + "but this node cannot reach the device. "
609 + "Relinquishing role. ",
610 myNextRole, did);
611 mastershipService.relinquishMastership(did);
612 }
613 return;
614 }
615
616 // device is connected to this node:
Yuta HIGUCHI63323fd2014-11-11 12:16:58 -0800617 reassertRole(did, myNextRole);
Ayaka Koshibe317245a2014-10-29 00:34:43 -0700618 }
tomb41d1ac2014-09-24 01:51:24 -0700619 }
tomf80c9722014-09-24 14:49:18 -0700620
621 // Store delegate to re-post events emitted from the store.
alshabib271a6202014-09-28 22:11:21 -0700622 private class InternalStoreDelegate
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700623 implements DeviceStoreDelegate {
tomf80c9722014-09-24 14:49:18 -0700624 @Override
625 public void notify(DeviceEvent event) {
626 post(event);
627 }
628 }
tomd3097b02014-08-26 10:40:29 -0700629}