blob: 2fe43ba30246f326515bfda4d2f3e9f406088a4f [file] [log] [blame]
Jonathan Hart472062d2014-04-03 10:56:48 -07001package net.onrc.onos.core.topology;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -08002
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -07003import java.nio.ByteBuffer;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -08004import java.util.ArrayList;
5import java.util.Collection;
Pavlin Radoslavov24409672014-08-20 16:45:11 -07006import java.util.HashMap;
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -07007import java.util.LinkedList;
Jonathan Hart369875b2014-02-13 10:00:31 -08008import java.util.List;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -08009import java.util.Map;
Saurav Dasfb93c252014-08-18 20:40:13 -070010import java.util.Map.Entry;
Pavlin Radoslavov24409672014-08-20 16:45:11 -070011import java.util.concurrent.BlockingQueue;
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -070012import java.util.concurrent.ConcurrentHashMap;
13import java.util.concurrent.ConcurrentMap;
Pavlin Radoslavov24409672014-08-20 16:45:11 -070014import java.util.concurrent.LinkedBlockingQueue;
Jonathan Hart369875b2014-02-13 10:00:31 -080015import java.util.concurrent.TimeUnit;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -080016
17import net.floodlightcontroller.core.IFloodlightProviderService;
Pavlin Radoslavov695f8952014-07-23 16:57:01 -070018import net.floodlightcontroller.core.IFloodlightProviderService.Role;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -080019import net.floodlightcontroller.core.IOFSwitch;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070020import net.floodlightcontroller.core.IOFSwitch.PortChangeType;
21import net.floodlightcontroller.core.IOFSwitchListener;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -080022import net.floodlightcontroller.core.module.FloodlightModuleContext;
23import net.floodlightcontroller.core.module.FloodlightModuleException;
24import net.floodlightcontroller.core.module.IFloodlightModule;
25import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hart369875b2014-02-13 10:00:31 -080026import net.floodlightcontroller.core.util.SingletonTask;
27import net.floodlightcontroller.threadpool.IThreadPoolService;
Pavlin Radoslavov24409672014-08-20 16:45:11 -070028import net.onrc.onos.api.batchoperation.BatchOperationEntry;
Saurav Dasfb93c252014-08-18 20:40:13 -070029import net.onrc.onos.core.configmanager.INetworkConfigService;
30import net.onrc.onos.core.configmanager.INetworkConfigService.LinkConfigStatus;
31import net.onrc.onos.core.configmanager.INetworkConfigService.NetworkConfigState;
32import net.onrc.onos.core.configmanager.INetworkConfigService.SwitchConfigStatus;
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -070033import net.onrc.onos.core.datagrid.IDatagridService;
34import net.onrc.onos.core.datagrid.IEventChannel;
Jonathan Hart03102132014-07-01 23:22:04 -070035import net.onrc.onos.core.hostmanager.Host;
36import net.onrc.onos.core.hostmanager.IHostListener;
37import net.onrc.onos.core.hostmanager.IHostService;
Jonathan Hart23701d12014-04-03 10:45:48 -070038import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryListener;
39import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
Jonathan Hart284e70f2014-07-05 12:32:51 -070040import net.onrc.onos.core.linkdiscovery.Link;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070041import net.onrc.onos.core.registry.IControllerRegistryService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070042import net.onrc.onos.core.registry.IControllerRegistryService.ControlChangeCallback;
Jonathan Harta99ec672014-04-03 11:30:34 -070043import net.onrc.onos.core.registry.RegistryException;
Yuta HIGUCHI5bbbaca2014-06-09 16:39:08 -070044import net.onrc.onos.core.util.Dpid;
Jonathan Hart8c802b42014-09-08 15:40:48 -070045import net.onrc.onos.core.util.LinkTuple;
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -070046import net.onrc.onos.core.util.OnosInstanceId;
Yuta HIGUCHIa507baf2014-08-22 13:42:40 -070047import net.onrc.onos.core.util.PortNumberUtils;
Yuta HIGUCHI5c8cbeb2014-06-27 11:13:48 -070048import net.onrc.onos.core.util.SwitchPort;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -080049
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -070050import org.apache.commons.lang3.concurrent.ConcurrentUtils;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070051import org.projectfloodlight.openflow.protocol.OFPortDesc;
52import org.projectfloodlight.openflow.util.HexString;
Toshio Koide2f570c12014-02-06 16:55:32 -080053import org.slf4j.Logger;
54import org.slf4j.LoggerFactory;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -080055
Jonathan Hart88770672014-04-02 18:08:30 -070056/**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -070057 * Class for publishing topology-related events.
58 *
59 * The events are received from the discovery modules, reformatted and
60 * published to the other ONOS instances.
61 *
62 * TODO: Add a synchronization mechanism when publishing the events to
63 * preserve the ordering and to avoid mismatch in the local "published" state,
64 * because each of the caller (the discovery modules) might be running
65 * on a different thread.
Jonathan Hart88770672014-04-02 18:08:30 -070066 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070067public class TopologyPublisher implements IOFSwitchListener,
Ray Milkey269ffb92014-04-03 14:43:30 -070068 ILinkDiscoveryListener,
69 IFloodlightModule,
Pavlin Radoslavov24409672014-08-20 16:45:11 -070070 IHostListener,
71 ITopologyPublisherService {
Jonathan Hart88770672014-04-02 18:08:30 -070072 private static final Logger log =
Jonathan Harte37e4e22014-05-13 19:12:02 -070073 LoggerFactory.getLogger(TopologyPublisher.class);
Yuta HIGUCHIcb951982014-02-11 13:31:44 -080074
Jonathan Hart88770672014-04-02 18:08:30 -070075 private IFloodlightProviderService floodlightProvider;
76 private ILinkDiscoveryService linkDiscovery;
77 private IControllerRegistryService registryService;
Jonathan Harte37e4e22014-05-13 19:12:02 -070078 private ITopologyService topologyService;
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -070079 private IDatagridService datagridService;
Toshio Koide2f570c12014-02-06 16:55:32 -080080
Jonathan Hart03102132014-07-01 23:22:04 -070081 private IHostService hostService;
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -070082 private MutableTopology mutableTopology;
Saurav Dasfb93c252014-08-18 20:40:13 -070083 private INetworkConfigService networkConfigService;
Jonathan Hartb3e1b052014-04-02 16:01:12 -070084
Jonathan Hart88770672014-04-02 18:08:30 -070085 private static final String ENABLE_CLEANUP_PROPERTY = "EnableCleanup";
86 private boolean cleanupEnabled = true;
87 private static final int CLEANUP_TASK_INTERVAL = 60; // in seconds
88 private SingletonTask cleanupTask;
Pavlin Radoslavov24409672014-08-20 16:45:11 -070089 private DelayedOperationsHandler delayedOperationsHandler =
90 new DelayedOperationsHandler();
Toshio Koide2f570c12014-02-06 16:55:32 -080091
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -070092 private IEventChannel<byte[], TopologyEvent> eventChannel;
93
94 //
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -070095 // Local state for keeping track of locally published events so we can
96 // cleanup properly when an entry is removed.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -070097 //
98 // We keep all Port, (incoming) Link and Host events per Switch DPID:
99 // - If a switch goes down, we remove all corresponding Port, Link and
100 // Host events.
101 // - If a port on a switch goes down, we remove all corresponding Link
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700102 // and Host events attached to this port.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700103 //
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700104 // TODO: What to do if the Mastership changes?
105 // - Cleanup state from publishedFoo maps, but do not send REMOVE events?
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700106 //
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700107 private ConcurrentMap<Dpid, MastershipData> publishedMastershipDataEntries =
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700108 new ConcurrentHashMap<>();
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700109 private ConcurrentMap<Dpid, SwitchData> publishedSwitchDataEntries =
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700110 new ConcurrentHashMap<>();
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700111 private ConcurrentMap<Dpid, ConcurrentMap<ByteBuffer, PortData>>
112 publishedPortDataEntries = new ConcurrentHashMap<>();
113 private ConcurrentMap<Dpid, ConcurrentMap<ByteBuffer, LinkData>>
114 publishedLinkDataEntries = new ConcurrentHashMap<>();
115 private ConcurrentMap<Dpid, ConcurrentMap<ByteBuffer, HostData>>
116 publishedHostDataEntries = new ConcurrentHashMap<>();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700117
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700118 private BlockingQueue<TopologyBatchOperation> delayedOperations =
119 new LinkedBlockingQueue<>();
120
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700121
Jonathan Hart369875b2014-02-13 10:00:31 -0800122 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700123 * Gets the ONOS Instance ID.
124 *
125 * @return the ONOS Instance ID.
126 */
127 private OnosInstanceId getOnosInstanceId() {
128 return registryService.getOnosInstanceId();
129 }
130
131 /**
132 * Cleanup old switches from the topology. Old switches are those which
133 * have no controller in the registry.
134 *
135 * TODO: The overall switch cleanup mechanism needs refactoring/redesign.
Jonathan Hart369875b2014-02-13 10:00:31 -0800136 */
137 private class SwitchCleanup implements ControlChangeCallback, Runnable {
138 @Override
139 public void run() {
140 String old = Thread.currentThread().getName();
141 Thread.currentThread().setName("SwitchCleanup@" + old);
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700142
Jonathan Hart369875b2014-02-13 10:00:31 -0800143 try {
Jonathan Hart88770672014-04-02 18:08:30 -0700144 if (log.isTraceEnabled()) {
145 log.trace("Running cleanup thread");
146 }
Jonathan Hart369875b2014-02-13 10:00:31 -0800147 switchCleanup();
Jonathan Hart369875b2014-02-13 10:00:31 -0800148 } finally {
149 cleanupTask.reschedule(CLEANUP_TASK_INTERVAL,
Jonathan Hart88770672014-04-02 18:08:30 -0700150 TimeUnit.SECONDS);
Jonathan Hart369875b2014-02-13 10:00:31 -0800151 Thread.currentThread().setName(old);
152 }
153 }
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700154
Jonathan Hart88770672014-04-02 18:08:30 -0700155 /**
156 * First half of the switch cleanup operation. This method will attempt
157 * to get control of any switch it sees without a controller via the
158 * registry.
159 */
Jonathan Hart369875b2014-02-13 10:00:31 -0800160 private void switchCleanup() {
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -0700161 Iterable<Switch> switches = mutableTopology.getSwitches();
Jonathan Hart369875b2014-02-13 10:00:31 -0800162
Jonathan Hart88770672014-04-02 18:08:30 -0700163 if (log.isTraceEnabled()) {
164 log.trace("Checking for inactive switches");
165 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700166 // For each switch check if a controller exists in controller
167 // registry
Ray Milkey269ffb92014-04-03 14:43:30 -0700168 for (Switch sw : switches) {
Praseed Balakrishnane82adc62014-08-04 10:59:24 -0700169 // FIXME How to handle case where Switch has never been
170 // registered to ZK
171 if (sw.getConfigState() == ConfigState.CONFIGURED) {
172 continue;
173 }
Jonathan Hart88770672014-04-02 18:08:30 -0700174 try {
175 String controller =
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700176 registryService.getControllerForSwitch(sw.getDpid().value());
Jonathan Hart88770672014-04-02 18:08:30 -0700177 if (controller == null) {
178 log.debug("Requesting control to set switch {} INACTIVE",
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700179 sw.getDpid());
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700180 registryService.requestControl(sw.getDpid().value(),
181 this);
Jonathan Hart88770672014-04-02 18:08:30 -0700182 }
183 } catch (RegistryException e) {
184 log.error("Caught RegistryException in cleanup thread", e);
185 }
186 }
Jonathan Hart369875b2014-02-13 10:00:31 -0800187 }
188
Jonathan Hart88770672014-04-02 18:08:30 -0700189 /**
190 * Second half of the switch cleanup operation. If the registry grants
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700191 * control of a switch, we can be sure no other instance is writing
192 * this switch to the topology, so we can remove it now.
193 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700194 * @param dpid the dpid of the switch we requested control for
Jonathan Hart88770672014-04-02 18:08:30 -0700195 * @param hasControl whether we got control or not
196 */
197 @Override
198 public void controlChanged(long dpid, boolean hasControl) {
199 if (hasControl) {
200 log.debug("Got control to set switch {} INACTIVE",
201 HexString.toHexString(dpid));
Jonathan Harte02cf542014-04-02 16:24:44 -0700202
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700203 SwitchData switchData = new SwitchData(new Dpid(dpid));
204 publishRemoveSwitchEvent(switchData);
Jonathan Hart88770672014-04-02 18:08:30 -0700205 registryService.releaseControl(dpid);
206 }
207 }
Jonathan Hart369875b2014-02-13 10:00:31 -0800208 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800209
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700210 /**
211 * A class to deal with Topology Operations that couldn't be pushed
212 * to the Global Log writer, because they need to be delayed.
213 * For example, a link cannot be pushed before the switches on both
214 * ends are in the Global Log.
215 *
216 * TODO: This is an ugly hack that should go away: right now we have to
217 * keep trying periodically.
218 * TODO: Currently, we retry only ADD Link Events, everything else
219 * is thrown away.
220 */
221 private class DelayedOperationsHandler extends Thread {
222 private static final long RETRY_INTERVAL_MS = 10; // 10ms
223
224 @Override
225 public void run() {
226 List<TopologyBatchOperation> operations = new LinkedList<>();
227
228 this.setName("TopologyPublisher.DelayedOperationsHandler " +
229 this.getId());
230 //
231 // The main loop
232 //
233 while (true) {
234 try {
235 //
236 // Block-waiting for an operation to be added, sleep
237 // and try to publish it again.
238 //
239 TopologyBatchOperation firstTbo = delayedOperations.take();
240 Thread.sleep(RETRY_INTERVAL_MS);
241 operations.add(firstTbo);
242 delayedOperations.drainTo(operations);
243
244 // Retry only the appropriate operations
245 for (TopologyBatchOperation tbo : operations) {
246 for (BatchOperationEntry<
247 TopologyBatchOperation.Operator,
248 TopologyEvent> boe : tbo.getOperations()) {
249 TopologyBatchOperation.Operator oper =
250 boe.getOperator();
251 switch (oper) {
252 case ADD:
253 TopologyEvent topologyEvent = boe.getTarget();
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700254 LinkData linkData =
255 topologyEvent.getLinkData();
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700256 //
257 // Test whether the Link Event still can be
258 // published.
259 // TODO: The implementation below has a bug:
260 // If it happens that the same Link Event was
261 // removed in the middle of checking, we might
262 // incorrectly publish it again from here.
263 //
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700264 if (linkData == null) {
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700265 break;
266 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700267 ConcurrentMap<ByteBuffer, LinkData>
268 linkDataEntries = publishedLinkDataEntries.get(
269 linkData.getDst().getDpid());
270 if (linkDataEntries == null) {
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700271 break;
272 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700273 if (linkDataEntries.get(linkData.getIDasByteBuffer()) == null) {
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700274 break;
275 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700276 publishAddLinkEvent(linkData);
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700277 break;
278 case REMOVE:
279 break;
280 default:
281 log.error("Unknown Topology Batch Operation {}", oper);
282 break;
283 }
284 }
285 }
286 } catch (InterruptedException exception) {
287 log.debug("Exception processing delayed operations: ",
288 exception);
289 }
290 }
291 }
292 }
293
Jonathan Hart88770672014-04-02 18:08:30 -0700294 @Override
Jonathan Hart284e70f2014-07-05 12:32:51 -0700295 public void linkAdded(Link link) {
Jonathan Hart8c802b42014-09-08 15:40:48 -0700296 LinkTuple linkTuple = new LinkTuple(
297 new SwitchPort(link.getSrc(), link.getSrcPort()),
298 new SwitchPort(link.getDst(), link.getDstPort()));
299
300 LinkConfigStatus ret = networkConfigService.checkLinkConfig(linkTuple);
Saurav Dasfb93c252014-08-18 20:40:13 -0700301 if (ret.getConfigState() == NetworkConfigState.DENY) {
302 log.warn("Discovered {} denied by configuration. {} "
303 + "Not allowing it to proceed.", link, ret.getMsg());
304 return;
305 }
306
Jonathan Hart8c802b42014-09-08 15:40:48 -0700307 LinkData linkData = new LinkData(linkTuple);
Jonathan Hart284e70f2014-07-05 12:32:51 -0700308
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700309 // FIXME should be merging, with existing attrs, etc..
310 // TODO define attr name as constant somewhere.
311 // TODO populate appropriate attributes.
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700312 linkData.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700313 TopologyElement.TYPE_PACKET_LAYER);
Saurav Dasfb93c252014-08-18 20:40:13 -0700314 if (ret.getConfigState() == NetworkConfigState.ACCEPT_ADD) {
315 Map<String, String> attr = ret.getLinkConfig().getPublishAttributes();
316 for (Entry<String, String> e : attr.entrySet()) {
317 linkData.createStringAttribute(e.getKey(), e.getValue());
318 }
319 linkData.createStringAttribute(TopologyElement.ELEMENT_CONFIG_STATE,
320 ConfigState.CONFIGURED.toString());
321 } else {
322 linkData.createStringAttribute(TopologyElement.ELEMENT_CONFIG_STATE,
323 ConfigState.NOT_CONFIGURED.toString());
324 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700325 linkData.createStringAttribute(TopologyElement.ELEMENT_ADMIN_STATUS,
Praseed Balakrishnan2aa6c0b2014-07-17 11:42:05 -0700326 AdminStatus.ACTIVE.toString());
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700327 linkData.freeze();
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700328
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700329 publishAddLinkEvent(linkData);
Jonathan Hart284e70f2014-07-05 12:32:51 -0700330 }
331
332 @Override
333 public void linkRemoved(Link link) {
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700334 LinkData linkData = new LinkData(
Jonathan Hart284e70f2014-07-05 12:32:51 -0700335 new SwitchPort(link.getSrc(), link.getSrcPort()),
336 new SwitchPort(link.getDst(), link.getDstPort()));
337
338 // FIXME should be merging, with existing attrs, etc..
339 // TODO define attr name as constant somewhere.
340 // TODO populate appropriate attributes.
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700341 linkData.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700342 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700343 linkData.freeze();
Jonathan Hart284e70f2014-07-05 12:32:51 -0700344
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700345 publishRemoveLinkEvent(linkData);
Jonathan Hart88770672014-04-02 18:08:30 -0700346 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800347
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700348 /* *****************
349 * IOFSwitchListener
350 * *****************/
351
Jonathan Hart88770672014-04-02 18:08:30 -0700352 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700353 public void switchActivatedMaster(long swId) {
354 IOFSwitch sw = floodlightProvider.getSwitch(swId);
355 final Dpid dpid = new Dpid(swId);
356 if (sw == null) {
357 log.warn("Added switch not available {} ", dpid);
358 return;
359 }
360
Saurav Dasfb93c252014-08-18 20:40:13 -0700361 SwitchConfigStatus ret = networkConfigService.checkSwitchConfig(dpid);
362 if (ret.getConfigState() == NetworkConfigState.DENY) {
363 log.warn("Activated switch {} denied by network configuration. {} "
364 + "Not allowing it to proceed.", dpid, ret.getMsg());
365 return;
366 }
367
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700368 controllerRoleChanged(dpid, Role.MASTER);
369
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700370 SwitchData switchData = new SwitchData(dpid);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700371 // FIXME should be merging, with existing attrs, etc..
372 // TODO define attr name as constant somewhere.
373 // TODO populate appropriate attributes.
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700374 switchData.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700375 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700376 switchData.createStringAttribute("ConnectedSince",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700377 sw.getConnectedSince().toString());
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700378 switchData.createStringAttribute(TopologyElement.ELEMENT_ADMIN_STATUS,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700379 AdminStatus.ACTIVE.toString());
Saurav Dasd0977442014-09-29 10:36:14 -0700380 // setting name from configuration (if it exists).
381 if (ret.getSwitchConfig() != null) {
382 switchData.createStringAttribute("name",
383 ret.getSwitchConfig().getName());
384 }
Fahad Naeem Khan1f8fb2e2014-09-25 16:39:18 -0700385 //set the switch socketAddress and port
386 switchData.createStringAttribute("remoteAddress",
387 sw.getChannelSocketAddress().toString().substring(1));
Saurav Dasfb93c252014-08-18 20:40:13 -0700388 if (ret.getConfigState() == NetworkConfigState.ACCEPT_ADD) {
389 Map<String, String> attr = ret.getSwitchConfig().getPublishAttributes();
390 for (Entry<String, String> e : attr.entrySet()) {
391 switchData.createStringAttribute(e.getKey(), e.getValue());
392 }
393 switchData.createStringAttribute(TopologyElement.ELEMENT_CONFIG_STATE,
394 ConfigState.CONFIGURED.toString());
395 } else {
396 switchData.createStringAttribute(TopologyElement.ELEMENT_CONFIG_STATE,
397 ConfigState.NOT_CONFIGURED.toString());
398 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700399 switchData.freeze();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700400 // The Port events
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700401 List<PortData> portDataEntries = new ArrayList<PortData>();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700402 for (OFPortDesc port : sw.getPorts()) {
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700403 PortData portData = new PortData(dpid,
Yuta HIGUCHIa507baf2014-08-22 13:42:40 -0700404 PortNumberUtils.openFlow(port));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700405 // FIXME should be merging, with existing attrs, etc..
406 // TODO define attr name as constant somewhere.
407 // TODO populate appropriate attributes.
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700408 portData.createStringAttribute("name", port.getName());
409 portData.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700410 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700411 portData.createStringAttribute(TopologyElement.ELEMENT_CONFIG_STATE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700412 ConfigState.NOT_CONFIGURED.toString());
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700413 portData.createStringAttribute(TopologyElement.ELEMENT_ADMIN_STATUS,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700414 AdminStatus.ACTIVE.toString());
415
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700416 portData.freeze();
417 portDataEntries.add(portData);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700418 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700419 publishAddSwitchEvent(switchData, portDataEntries);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700420 }
421
422 @Override
423 public void switchActivatedEqual(long swId) {
424 final Dpid dpid = new Dpid(swId);
425 controllerRoleChanged(dpid, Role.EQUAL);
426 }
427
428 @Override
429 public void switchMasterToEqual(long swId) {
430 final Dpid dpid = new Dpid(swId);
431 controllerRoleChanged(dpid, Role.EQUAL);
432 }
433
434 @Override
435 public void switchEqualToMaster(long swId) {
436 // for now treat as switchActivatedMaster
437 switchActivatedMaster(swId);
438 }
439
440 @Override
441 public void switchDisconnected(long swId) {
442 final Dpid dpid = new Dpid(swId);
443
444 log.debug("Local switch disconnected: dpid = {} role = {}", dpid);
445
446 Role role = Role.SLAVE; // TODO: Should be Role.UNKNOWN
447
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700448 MastershipData mastershipData =
449 new MastershipData(dpid, getOnosInstanceId(), role);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700450 // FIXME should be merging, with existing attrs, etc..
451 // TODO define attr name as constant somewhere.
452 // TODO populate appropriate attributes.
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700453 mastershipData.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700454 TopologyElement.TYPE_ALL_LAYERS);
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700455 mastershipData.freeze();
456 publishRemoveSwitchMastershipEvent(mastershipData);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700457 }
458
459 @Override
460 public void switchPortChanged(long swId, OFPortDesc port,
461 PortChangeType changeType) {
462 switch (changeType) {
463 case ADD:
464 switchPortAdded(swId, port);
465 break;
466 case DELETE:
467 switchPortRemoved(swId, port);
468 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700469 case UP:
Pavlin Radoslavov7946c612014-08-13 15:06:00 -0700470 // NOTE: Currently, we treat Port UP/DOWN same as Port ADD/DELETE
471 switchPortAdded(swId, port);
472 break;
473 case DOWN:
474 // NOTE: Currently, we treat Port UP/DOWN same as Port ADD/DELETE
475 switchPortRemoved(swId, port);
476 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700477 case OTHER_UPDATE:
478 default:
479 // XXX S what is the right set of port change handlers?
480 log.debug("Topology publisher does not handle these port updates: {}",
481 changeType);
482 }
483 }
484
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700485 /**
486 * Prepares an event for adding a port on a switch.
487 *
488 * @param switchId the switch ID (DPID)
489 * @param port the port to add
490 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700491 private void switchPortAdded(long switchId, OFPortDesc port) {
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700492 final Dpid dpid = new Dpid(switchId);
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700493 final PortData portData = new PortData(dpid,
Yuta HIGUCHIa507baf2014-08-22 13:42:40 -0700494 PortNumberUtils.openFlow(port));
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700495 // FIXME should be merging, with existing attrs, etc..
496 // TODO define attr name as constant somewhere.
497 // TODO populate appropriate attributes.
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700498 portData.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700499 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700500 portData.createStringAttribute("name", port.getName());
501 portData.freeze();
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700502
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700503 publishAddPortEvent(portData);
Jonathan Hart88770672014-04-02 18:08:30 -0700504 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800505
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700506 /**
507 * Prepares an event for removing a port on a switch.
508 *
509 * @param switchId the switch ID (DPID)
510 * @param port the port to remove
511 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700512 private void switchPortRemoved(long switchId, OFPortDesc port) {
Yuta HIGUCHIe2a4e172014-07-03 10:50:39 -0700513 final Dpid dpid = new Dpid(switchId);
Yuta HIGUCHI5bbbaca2014-06-09 16:39:08 -0700514
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700515 final PortData portData = new PortData(dpid,
Yuta HIGUCHIa507baf2014-08-22 13:42:40 -0700516 PortNumberUtils.openFlow(port));
Yuta HIGUCHI1222ac52014-07-09 16:50:28 -0700517 // FIXME should be merging, with existing attrs, etc..
518 // TODO define attr name as constant somewhere.
519 // TODO populate appropriate attributes.
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700520 portData.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700521 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700522 portData.createStringAttribute("name", port.getName());
523 portData.freeze();
Yuta HIGUCHI1222ac52014-07-09 16:50:28 -0700524
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700525 publishRemovePortEvent(portData);
Jonathan Hart88770672014-04-02 18:08:30 -0700526 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800527
Jonathan Hart88770672014-04-02 18:08:30 -0700528 @Override
Jonathan Hart88770672014-04-02 18:08:30 -0700529 public String getName() {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700530 return "topologyPublisher";
Jonathan Hart88770672014-04-02 18:08:30 -0700531 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800532
Jonathan Hart88770672014-04-02 18:08:30 -0700533 /* *****************
534 * IFloodlightModule
535 * *****************/
Toshio Koide2f570c12014-02-06 16:55:32 -0800536
Jonathan Hart88770672014-04-02 18:08:30 -0700537 @Override
538 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700539 List<Class<? extends IFloodlightService>> services =
540 new ArrayList<Class<? extends IFloodlightService>>();
541 services.add(ITopologyPublisherService.class);
542 return services;
Jonathan Hart88770672014-04-02 18:08:30 -0700543 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800544
Jonathan Hart88770672014-04-02 18:08:30 -0700545 @Override
546 public Map<Class<? extends IFloodlightService>, IFloodlightService>
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700547 getServiceImpls() {
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700548 Map<Class<? extends IFloodlightService>, IFloodlightService> impls =
549 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
550 impls.put(ITopologyPublisherService.class, this);
551 return impls;
Jonathan Hart88770672014-04-02 18:08:30 -0700552 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800553
Jonathan Hart88770672014-04-02 18:08:30 -0700554 @Override
555 public Collection<Class<? extends IFloodlightService>>
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700556 getModuleDependencies() {
Jonathan Hart88770672014-04-02 18:08:30 -0700557 Collection<Class<? extends IFloodlightService>> l =
558 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800559 l.add(IFloodlightProviderService.class);
560 l.add(ILinkDiscoveryService.class);
Jonathan Hart369875b2014-02-13 10:00:31 -0800561 l.add(IThreadPoolService.class);
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800562 l.add(IControllerRegistryService.class);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700563 l.add(IDatagridService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700564 l.add(ITopologyService.class);
Jonathan Hart03102132014-07-01 23:22:04 -0700565 l.add(IHostService.class);
Saurav Dasfb93c252014-08-18 20:40:13 -0700566 l.add(INetworkConfigService.class);
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800567 return l;
Jonathan Hart88770672014-04-02 18:08:30 -0700568 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800569
Jonathan Hart88770672014-04-02 18:08:30 -0700570 @Override
571 public void init(FloodlightModuleContext context)
572 throws FloodlightModuleException {
573 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
574 linkDiscovery = context.getServiceImpl(ILinkDiscoveryService.class);
575 registryService = context.getServiceImpl(IControllerRegistryService.class);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700576 datagridService = context.getServiceImpl(IDatagridService.class);
Jonathan Hart03102132014-07-01 23:22:04 -0700577 hostService = context.getServiceImpl(IHostService.class);
Saurav Dasfb93c252014-08-18 20:40:13 -0700578 networkConfigService = context.getServiceImpl(INetworkConfigService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700579 topologyService = context.getServiceImpl(ITopologyService.class);
Jonathan Hart88770672014-04-02 18:08:30 -0700580 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800581
Jonathan Hart88770672014-04-02 18:08:30 -0700582 @Override
583 public void startUp(FloodlightModuleContext context) {
584 floodlightProvider.addOFSwitchListener(this);
585 linkDiscovery.addListener(this);
Jonathan Hart03102132014-07-01 23:22:04 -0700586 hostService.addHostListener(this);
Toshio Koide2f570c12014-02-06 16:55:32 -0800587
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700588 eventChannel = datagridService.createChannel(
589 TopologyManager.EVENT_CHANNEL_NAME,
590 byte[].class,
591 TopologyEvent.class);
592
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -0700593 mutableTopology = topologyService.getTopology();
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700594
Jonathan Hart88770672014-04-02 18:08:30 -0700595 // Run the cleanup thread
596 String enableCleanup =
597 context.getConfigParams(this).get(ENABLE_CLEANUP_PROPERTY);
598 if (enableCleanup != null
599 && enableCleanup.equalsIgnoreCase("false")) {
600 cleanupEnabled = false;
601 }
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700602
Jonathan Hart88770672014-04-02 18:08:30 -0700603 log.debug("Cleanup thread is {}enabled", (cleanupEnabled) ? "" : "not ");
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700604
Jonathan Hart88770672014-04-02 18:08:30 -0700605 if (cleanupEnabled) {
606 IThreadPoolService threadPool =
607 context.getServiceImpl(IThreadPoolService.class);
608 cleanupTask = new SingletonTask(threadPool.getScheduledExecutor(),
609 new SwitchCleanup());
610 // Run the cleanup task immediately on startup
611 cleanupTask.reschedule(0, TimeUnit.SECONDS);
612 }
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700613
614 // Run the Delayed Operations Handler thread
615 delayedOperationsHandler.start();
Jonathan Hart88770672014-04-02 18:08:30 -0700616 }
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700617
Jonathan Hart88770672014-04-02 18:08:30 -0700618 @Override
Jonathan Hart03102132014-07-01 23:22:04 -0700619 public void hostAdded(Host host) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700620 log.debug("Host added with MAC {}", host.getMacAddress());
TeruUd1c5b652014-03-24 13:58:46 -0700621
Jonathan Hart03102132014-07-01 23:22:04 -0700622 SwitchPort sp = new SwitchPort(host.getSwitchDPID(), host.getSwitchPort());
Jonathan Hart88770672014-04-02 18:08:30 -0700623 List<SwitchPort> spLists = new ArrayList<SwitchPort>();
624 spLists.add(sp);
Sangho Shin2f263692014-09-15 14:09:41 -0700625 HostData hostData = new HostData(host.getMacAddress(), host.getIpAddress());
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700626 hostData.setAttachmentPoints(spLists);
627 hostData.setLastSeenTime(host.getLastSeenTimestamp().getTime());
Jonathan Hart88770672014-04-02 18:08:30 -0700628 // Does not use vlan info now.
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700629 hostData.freeze();
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700630
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700631 publishAddHostEvent(hostData);
Jonathan Hart88770672014-04-02 18:08:30 -0700632 }
TeruUd1c5b652014-03-24 13:58:46 -0700633
Jonathan Hart88770672014-04-02 18:08:30 -0700634 @Override
Jonathan Hart03102132014-07-01 23:22:04 -0700635 public void hostRemoved(Host host) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700636 log.debug("Host removed with MAC {}", host.getMacAddress());
637
638 //
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700639 // Remove all previously added HostData for this MAC address
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700640 //
641 // TODO: Currently, the caller of hostRemoved() might not include
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700642 // the correct set of Attachment Points in the HostData entry itself.
643 // Also, we might have multiple HostData entries for the same
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700644 // host (MAC address), each containing a single (different) Attachment
645 // Point.
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700646 // Hence, here we have to cleanup all HostData entries for this
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700647 // particular host, based on its MAC address.
648 //
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700649 List<HostData> removeHostDataEntries = new LinkedList<>();
650 for (ConcurrentMap<ByteBuffer, HostData> cm : publishedHostDataEntries.values()) {
651 for (HostData hostData : cm.values()) {
652 if (hostData.getMac().equals(host.getMacAddress())) {
653 removeHostDataEntries.add(hostData);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700654 }
655 }
656 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700657 for (HostData hostData : removeHostDataEntries) {
658 publishRemoveHostEvent(hostData);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700659 }
Jonathan Hart88770672014-04-02 18:08:30 -0700660 }
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700661
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700662 @Override
663 public boolean publish(TopologyBatchOperation tbo) {
664 publishTopologyOperations(tbo);
665 return true;
666 }
667
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700668 /**
669 * Prepares the Controller role changed event for a switch.
670 *
671 * @param dpid the switch DPID
672 * @param role the new role of the controller
673 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700674 private void controllerRoleChanged(Dpid dpid, Role role) {
675 log.debug("Local switch controller mastership role changed: dpid = {} role = {}",
676 dpid, role);
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700677 MastershipData mastershipData =
678 new MastershipData(dpid, getOnosInstanceId(), role);
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700679 // FIXME should be merging, with existing attrs, etc..
680 // TODO define attr name as constant somewhere.
681 // TODO populate appropriate attributes.
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700682 mastershipData.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700683 TopologyElement.TYPE_ALL_LAYERS);
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700684 mastershipData.freeze();
685 publishAddSwitchMastershipEvent(mastershipData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700686 }
687
688 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700689 * Publishes ADD Mastership Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700690 *
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700691 * @param mastershipData the mastership event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700692 */
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700693 private void publishAddSwitchMastershipEvent(
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700694 MastershipData mastershipData) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700695 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700696 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700697 TopologyEvent topologyEvent =
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700698 new TopologyEvent(mastershipData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700699 tbo.appendAddOperation(topologyEvent);
700 publishTopologyOperations(tbo);
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700701 publishedMastershipDataEntries.put(mastershipData.getDpid(),
702 mastershipData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700703 }
704
705 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700706 * Publishes REMOVE Mastership Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700707 *
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700708 * @param mastershipData the mastership event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700709 */
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700710 private void publishRemoveSwitchMastershipEvent(
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700711 MastershipData mastershipData) {
712 if (publishedMastershipDataEntries.get(mastershipData.getDpid()) == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700713 return; // Nothing to do
714 }
715
716 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700717 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700718 TopologyEvent topologyEvent =
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700719 new TopologyEvent(mastershipData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700720 tbo.appendRemoveOperation(topologyEvent);
721 publishTopologyOperations(tbo);
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700722 publishedMastershipDataEntries.remove(mastershipData.getDpid());
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700723 }
724
725 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700726 * Publishes ADD Switch Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700727 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700728 * @param switchData the switch event to publish
729 * @param portDataEntries the corresponding port events for the switch to
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700730 * publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700731 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700732 private void publishAddSwitchEvent(SwitchData switchData,
733 Collection<PortData> portDataEntries) {
734 if (!registryService.hasControl(switchData.getOriginDpid().value())) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700735 log.debug("Not the master for switch {}. Suppressed switch add event {}.",
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700736 switchData.getOriginDpid(), switchData);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700737 return;
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700738 }
739
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700740 // Keep track of the old Port Events that should be removed
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700741 ConcurrentMap<ByteBuffer, PortData> oldPortDataEntries =
742 publishedPortDataEntries.get(switchData.getDpid());
743 if (oldPortDataEntries == null) {
744 oldPortDataEntries = new ConcurrentHashMap<>();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700745 }
746
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700747 // Publish the information for the switch
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700748 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700749 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700750 new TopologyEvent(switchData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700751 tbo.appendAddOperation(topologyEvent);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700752
753 // Publish the information for each port
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700754 ConcurrentMap<ByteBuffer, PortData> newPortDataEntries =
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700755 new ConcurrentHashMap<>();
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700756 for (PortData portData : portDataEntries) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700757 topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700758 new TopologyEvent(portData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700759 tbo.appendAddOperation(topologyEvent);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700760
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700761 ByteBuffer id = portData.getIDasByteBuffer();
762 newPortDataEntries.put(id, portData);
763 oldPortDataEntries.remove(id);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700764 }
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700765 publishTopologyOperations(tbo);
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700766 publishedSwitchDataEntries.put(switchData.getDpid(), switchData);
767 publishedPortDataEntries.put(switchData.getDpid(), newPortDataEntries);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700768
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700769 // Cleanup for each of the old removed port
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700770 for (PortData portData : oldPortDataEntries.values()) {
771 publishRemovePortEvent(portData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700772 }
773 }
774
775 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700776 * Publishes REMOVE Switch Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700777 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700778 * @param switchData the switch event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700779 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700780 private void publishRemoveSwitchEvent(SwitchData switchData) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700781 //
782 // TODO: Removed the check for now, because currently this method is
783 // also called by the SwitchCleanup thread, and in that case
784 // the Switch Event was published by some other ONOS instance.
785 //
786 /*
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700787 if (publishedSwitchDataEntries.get(switchData.getDpid()) == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700788 return; // Nothing to do
789 }
790 */
791
792 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700793 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700794 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700795 new TopologyEvent(switchData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700796 tbo.appendRemoveOperation(topologyEvent);
797 publishTopologyOperations(tbo);
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700798 publishedSwitchDataEntries.remove(switchData.getDpid());
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700799
800 // Cleanup for each port
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700801 ConcurrentMap<ByteBuffer, PortData> portDataEntries =
802 publishedPortDataEntries.get(switchData.getDpid());
803 if (portDataEntries != null) {
804 for (PortData portData : portDataEntries.values()) {
805 publishRemovePortEvent(portData);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700806 }
807 }
808
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700809 publishedPortDataEntries.remove(switchData.getDpid());
810 publishedLinkDataEntries.remove(switchData.getDpid());
811 publishedHostDataEntries.remove(switchData.getDpid());
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700812 }
813
814 /**
815 * Publishes ADD Port Event.
816 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700817 * @param portData the port event to publish
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700818 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700819 private void publishAddPortEvent(PortData portData) {
820 if (!registryService.hasControl(portData.getOriginDpid().value())) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700821 log.debug("Not the master for switch {}. Suppressed port add event {}.",
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700822 portData.getOriginDpid(), portData);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700823 return;
824 }
825
826 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700827 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700828 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700829 new TopologyEvent(portData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700830 tbo.appendAddOperation(topologyEvent);
831 publishTopologyOperations(tbo);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700832
833 // Store the new Port Event in the local cache
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700834 ConcurrentMap<ByteBuffer, PortData> portDataEntries =
835 ConcurrentUtils.putIfAbsent(publishedPortDataEntries,
836 portData.getDpid(),
837 new ConcurrentHashMap<ByteBuffer, PortData>());
838 portDataEntries.put(portData.getIDasByteBuffer(), portData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700839 }
840
841 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700842 * Publishes REMOVE Port Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700843 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700844 * @param portData the port event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700845 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700846 private void publishRemovePortEvent(PortData portData) {
847 ConcurrentMap<ByteBuffer, PortData> portDataEntries =
848 publishedPortDataEntries.get(portData.getDpid());
849 if (portDataEntries == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700850 return; // Nothing to do
851 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700852 if (portDataEntries.get(portData.getIDasByteBuffer()) == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700853 return; // Nothing to do
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700854 }
855
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700856 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700857 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700858 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700859 new TopologyEvent(portData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700860 tbo.appendRemoveOperation(topologyEvent);
861 publishTopologyOperations(tbo);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700862
863 // Cleanup for the incoming link(s)
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700864 ConcurrentMap<ByteBuffer, LinkData> linkDataEntries =
865 publishedLinkDataEntries.get(portData.getDpid());
866 if (linkDataEntries != null) {
867 for (LinkData linkData : linkDataEntries.values()) {
868 if (linkData.getDst().equals(portData.getSwitchPort())) {
869 publishRemoveLinkEvent(linkData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700870 }
871 }
872 }
873
874 // Cleanup for the connected hosts
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700875 ConcurrentMap<ByteBuffer, HostData> hostDataEntries =
876 publishedHostDataEntries.get(portData.getDpid());
877 if (hostDataEntries != null) {
878 for (HostData hostData : hostDataEntries.values()) {
879 for (SwitchPort swp : hostData.getAttachmentPoints()) {
880 if (swp.equals(portData.getSwitchPort())) {
881 publishRemoveHostEvent(hostData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700882 }
883 }
884 }
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700885 }
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700886
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700887 portDataEntries.remove(portData.getIDasByteBuffer());
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700888 }
889
890 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700891 * Publishes ADD Link Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700892 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700893 * @param linkData the link event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700894 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700895 private void publishAddLinkEvent(LinkData linkData) {
896 if (!registryService.hasControl(linkData.getOriginDpid().value())) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700897 log.debug("Not the master for dst switch {}. Suppressed link add event {}.",
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700898 linkData.getOriginDpid(), linkData);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700899 return;
900 }
901
902 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700903 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700904 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700905 new TopologyEvent(linkData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700906 tbo.appendAddOperation(topologyEvent);
907 publishTopologyOperations(tbo);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700908
909 // Store the new Link Event in the local cache
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700910 ConcurrentMap<ByteBuffer, LinkData> linkDataEntries =
911 ConcurrentUtils.putIfAbsent(publishedLinkDataEntries,
912 linkData.getDst().getDpid(),
913 new ConcurrentHashMap<ByteBuffer, LinkData>());
914 linkDataEntries.put(linkData.getIDasByteBuffer(), linkData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700915 }
916
917 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700918 * Publishes REMOVE Link Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700919 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700920 * @param linkData the link event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700921 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700922 private void publishRemoveLinkEvent(LinkData linkData) {
923 ConcurrentMap<ByteBuffer, LinkData> linkDataEntries =
924 publishedLinkDataEntries.get(linkData.getDst().getDpid());
925 if (linkDataEntries == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700926 return; // Nothing to do
927 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700928 if (linkDataEntries.get(linkData.getIDasByteBuffer()) == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700929 return; // Nothing to do
930 }
931
932 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700933 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700934 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700935 new TopologyEvent(linkData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700936 tbo.appendRemoveOperation(topologyEvent);
937 publishTopologyOperations(tbo);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700938
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700939 linkDataEntries.remove(linkData.getIDasByteBuffer());
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700940 }
941
942 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700943 * Publishes ADD Host Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700944 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700945 * @param hostData the host event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700946 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700947 private void publishAddHostEvent(HostData hostData) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700948 //
949 // NOTE: The implementation below assumes that there is just one
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700950 // attachment point stored in hostData. Currently, this assumption
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700951 // is true based on the existing implementation of the caller
952 // hostAdded().
953 //
954
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700955 if (!registryService.hasControl(hostData.getOriginDpid().value())) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700956 log.debug("Not the master for attachment switch {}. Suppressed host add event {}.",
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700957 hostData.getOriginDpid(), hostData);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700958 return;
959 }
960
961 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700962 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700963 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700964 new TopologyEvent(hostData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700965 tbo.appendAddOperation(topologyEvent);
966 publishTopologyOperations(tbo);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700967
968 // Store the new Host Event in the local cache
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700969 ConcurrentMap<ByteBuffer, HostData> hostDataEntries =
970 ConcurrentUtils.putIfAbsent(publishedHostDataEntries,
971 hostData.getOriginDpid(),
972 new ConcurrentHashMap<ByteBuffer, HostData>());
973 hostDataEntries.put(hostData.getIDasByteBuffer(), hostData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700974 }
975
976 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700977 * Publishes REMOVE Host Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700978 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700979 * @param hostData the host event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700980 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700981 private void publishRemoveHostEvent(HostData hostData) {
982 ConcurrentMap<ByteBuffer, HostData> hostDataEntries =
983 publishedHostDataEntries.get(hostData.getOriginDpid());
984 if (hostDataEntries == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700985 return; // Nothing to do
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700986 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700987 if (hostDataEntries.get(hostData.getIDasByteBuffer()) == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700988 return; // Nothing to do
989 }
990
991 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700992 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700993 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700994 new TopologyEvent(hostData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700995 tbo.appendRemoveOperation(topologyEvent);
996 publishTopologyOperations(tbo);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700997
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700998 hostDataEntries.remove(hostData.getIDasByteBuffer());
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700999 }
Pavlin Radoslavov24409672014-08-20 16:45:11 -07001000
1001 /**
1002 * Publishes Topology Operations.
1003 *
1004 * @param tbo the Topology Operations to publish
1005 */
1006 private void publishTopologyOperations(TopologyBatchOperation tbo) {
1007 // TODO: This flag should be configurable
1008 boolean isGlobalLogWriter = false;
1009
1010 log.debug("Publishing: {}", tbo);
1011
1012 if (isGlobalLogWriter) {
1013 if (!topologyService.publish(tbo)) {
1014 log.debug("Cannot publish: {}", tbo);
1015 delayedOperations.add(tbo);
1016 }
1017 } else {
1018 // TODO: For now we publish each TopologyEvent independently
1019 for (BatchOperationEntry<TopologyBatchOperation.Operator,
1020 TopologyEvent> boe : tbo.getOperations()) {
1021 TopologyBatchOperation.Operator oper = boe.getOperator();
1022 TopologyEvent topologyEvent = boe.getTarget();
1023 switch (oper) {
1024 case ADD:
1025 eventChannel.addEntry(topologyEvent.getID(),
1026 topologyEvent);
1027 break;
1028 case REMOVE:
1029 eventChannel.removeEntry(topologyEvent.getID());
1030 break;
1031 default:
1032 log.error("Unknown Topology Batch Operation {}", oper);
1033 break;
1034 }
1035 }
1036 }
1037 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -08001038}