blob: 4f20a81e1eb6a8b3e8c65c1fa89e31a5b9d7ef0c [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());
Fahad Naeem Khan1f8fb2e2014-09-25 16:39:18 -0700380 //setting name from config file.
381 switchData.createStringAttribute("name",
382 ret.getSwitchConfig().getName());
383 //set the switch socketAddress and port
384 switchData.createStringAttribute("remoteAddress",
385 sw.getChannelSocketAddress().toString().substring(1));
Saurav Dasfb93c252014-08-18 20:40:13 -0700386 if (ret.getConfigState() == NetworkConfigState.ACCEPT_ADD) {
387 Map<String, String> attr = ret.getSwitchConfig().getPublishAttributes();
388 for (Entry<String, String> e : attr.entrySet()) {
389 switchData.createStringAttribute(e.getKey(), e.getValue());
390 }
391 switchData.createStringAttribute(TopologyElement.ELEMENT_CONFIG_STATE,
392 ConfigState.CONFIGURED.toString());
393 } else {
394 switchData.createStringAttribute(TopologyElement.ELEMENT_CONFIG_STATE,
395 ConfigState.NOT_CONFIGURED.toString());
396 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700397 switchData.freeze();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700398 // The Port events
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700399 List<PortData> portDataEntries = new ArrayList<PortData>();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700400 for (OFPortDesc port : sw.getPorts()) {
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700401 PortData portData = new PortData(dpid,
Yuta HIGUCHIa507baf2014-08-22 13:42:40 -0700402 PortNumberUtils.openFlow(port));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700403 // FIXME should be merging, with existing attrs, etc..
404 // TODO define attr name as constant somewhere.
405 // TODO populate appropriate attributes.
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700406 portData.createStringAttribute("name", port.getName());
407 portData.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700408 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700409 portData.createStringAttribute(TopologyElement.ELEMENT_CONFIG_STATE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700410 ConfigState.NOT_CONFIGURED.toString());
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700411 portData.createStringAttribute(TopologyElement.ELEMENT_ADMIN_STATUS,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700412 AdminStatus.ACTIVE.toString());
413
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700414 portData.freeze();
415 portDataEntries.add(portData);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700416 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700417 publishAddSwitchEvent(switchData, portDataEntries);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700418 }
419
420 @Override
421 public void switchActivatedEqual(long swId) {
422 final Dpid dpid = new Dpid(swId);
423 controllerRoleChanged(dpid, Role.EQUAL);
424 }
425
426 @Override
427 public void switchMasterToEqual(long swId) {
428 final Dpid dpid = new Dpid(swId);
429 controllerRoleChanged(dpid, Role.EQUAL);
430 }
431
432 @Override
433 public void switchEqualToMaster(long swId) {
434 // for now treat as switchActivatedMaster
435 switchActivatedMaster(swId);
436 }
437
438 @Override
439 public void switchDisconnected(long swId) {
440 final Dpid dpid = new Dpid(swId);
441
442 log.debug("Local switch disconnected: dpid = {} role = {}", dpid);
443
444 Role role = Role.SLAVE; // TODO: Should be Role.UNKNOWN
445
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700446 MastershipData mastershipData =
447 new MastershipData(dpid, getOnosInstanceId(), role);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700448 // FIXME should be merging, with existing attrs, etc..
449 // TODO define attr name as constant somewhere.
450 // TODO populate appropriate attributes.
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700451 mastershipData.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700452 TopologyElement.TYPE_ALL_LAYERS);
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700453 mastershipData.freeze();
454 publishRemoveSwitchMastershipEvent(mastershipData);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700455 }
456
457 @Override
458 public void switchPortChanged(long swId, OFPortDesc port,
459 PortChangeType changeType) {
460 switch (changeType) {
461 case ADD:
462 switchPortAdded(swId, port);
463 break;
464 case DELETE:
465 switchPortRemoved(swId, port);
466 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700467 case UP:
Pavlin Radoslavov7946c612014-08-13 15:06:00 -0700468 // NOTE: Currently, we treat Port UP/DOWN same as Port ADD/DELETE
469 switchPortAdded(swId, port);
470 break;
471 case DOWN:
472 // NOTE: Currently, we treat Port UP/DOWN same as Port ADD/DELETE
473 switchPortRemoved(swId, port);
474 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700475 case OTHER_UPDATE:
476 default:
477 // XXX S what is the right set of port change handlers?
478 log.debug("Topology publisher does not handle these port updates: {}",
479 changeType);
480 }
481 }
482
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700483 /**
484 * Prepares an event for adding a port on a switch.
485 *
486 * @param switchId the switch ID (DPID)
487 * @param port the port to add
488 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700489 private void switchPortAdded(long switchId, OFPortDesc port) {
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700490 final Dpid dpid = new Dpid(switchId);
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700491 final PortData portData = new PortData(dpid,
Yuta HIGUCHIa507baf2014-08-22 13:42:40 -0700492 PortNumberUtils.openFlow(port));
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700493 // FIXME should be merging, with existing attrs, etc..
494 // TODO define attr name as constant somewhere.
495 // TODO populate appropriate attributes.
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700496 portData.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700497 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700498 portData.createStringAttribute("name", port.getName());
499 portData.freeze();
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700500
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700501 publishAddPortEvent(portData);
Jonathan Hart88770672014-04-02 18:08:30 -0700502 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800503
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700504 /**
505 * Prepares an event for removing a port on a switch.
506 *
507 * @param switchId the switch ID (DPID)
508 * @param port the port to remove
509 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700510 private void switchPortRemoved(long switchId, OFPortDesc port) {
Yuta HIGUCHIe2a4e172014-07-03 10:50:39 -0700511 final Dpid dpid = new Dpid(switchId);
Yuta HIGUCHI5bbbaca2014-06-09 16:39:08 -0700512
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700513 final PortData portData = new PortData(dpid,
Yuta HIGUCHIa507baf2014-08-22 13:42:40 -0700514 PortNumberUtils.openFlow(port));
Yuta HIGUCHI1222ac52014-07-09 16:50:28 -0700515 // FIXME should be merging, with existing attrs, etc..
516 // TODO define attr name as constant somewhere.
517 // TODO populate appropriate attributes.
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700518 portData.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700519 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700520 portData.createStringAttribute("name", port.getName());
521 portData.freeze();
Yuta HIGUCHI1222ac52014-07-09 16:50:28 -0700522
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700523 publishRemovePortEvent(portData);
Jonathan Hart88770672014-04-02 18:08:30 -0700524 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800525
Jonathan Hart88770672014-04-02 18:08:30 -0700526 @Override
Jonathan Hart88770672014-04-02 18:08:30 -0700527 public String getName() {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700528 return "topologyPublisher";
Jonathan Hart88770672014-04-02 18:08:30 -0700529 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800530
Jonathan Hart88770672014-04-02 18:08:30 -0700531 /* *****************
532 * IFloodlightModule
533 * *****************/
Toshio Koide2f570c12014-02-06 16:55:32 -0800534
Jonathan Hart88770672014-04-02 18:08:30 -0700535 @Override
536 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700537 List<Class<? extends IFloodlightService>> services =
538 new ArrayList<Class<? extends IFloodlightService>>();
539 services.add(ITopologyPublisherService.class);
540 return services;
Jonathan Hart88770672014-04-02 18:08:30 -0700541 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800542
Jonathan Hart88770672014-04-02 18:08:30 -0700543 @Override
544 public Map<Class<? extends IFloodlightService>, IFloodlightService>
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700545 getServiceImpls() {
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700546 Map<Class<? extends IFloodlightService>, IFloodlightService> impls =
547 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
548 impls.put(ITopologyPublisherService.class, this);
549 return impls;
Jonathan Hart88770672014-04-02 18:08:30 -0700550 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800551
Jonathan Hart88770672014-04-02 18:08:30 -0700552 @Override
553 public Collection<Class<? extends IFloodlightService>>
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700554 getModuleDependencies() {
Jonathan Hart88770672014-04-02 18:08:30 -0700555 Collection<Class<? extends IFloodlightService>> l =
556 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800557 l.add(IFloodlightProviderService.class);
558 l.add(ILinkDiscoveryService.class);
Jonathan Hart369875b2014-02-13 10:00:31 -0800559 l.add(IThreadPoolService.class);
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800560 l.add(IControllerRegistryService.class);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700561 l.add(IDatagridService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700562 l.add(ITopologyService.class);
Jonathan Hart03102132014-07-01 23:22:04 -0700563 l.add(IHostService.class);
Saurav Dasfb93c252014-08-18 20:40:13 -0700564 l.add(INetworkConfigService.class);
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800565 return l;
Jonathan Hart88770672014-04-02 18:08:30 -0700566 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800567
Jonathan Hart88770672014-04-02 18:08:30 -0700568 @Override
569 public void init(FloodlightModuleContext context)
570 throws FloodlightModuleException {
571 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
572 linkDiscovery = context.getServiceImpl(ILinkDiscoveryService.class);
573 registryService = context.getServiceImpl(IControllerRegistryService.class);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700574 datagridService = context.getServiceImpl(IDatagridService.class);
Jonathan Hart03102132014-07-01 23:22:04 -0700575 hostService = context.getServiceImpl(IHostService.class);
Saurav Dasfb93c252014-08-18 20:40:13 -0700576 networkConfigService = context.getServiceImpl(INetworkConfigService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700577 topologyService = context.getServiceImpl(ITopologyService.class);
Jonathan Hart88770672014-04-02 18:08:30 -0700578 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800579
Jonathan Hart88770672014-04-02 18:08:30 -0700580 @Override
581 public void startUp(FloodlightModuleContext context) {
582 floodlightProvider.addOFSwitchListener(this);
583 linkDiscovery.addListener(this);
Jonathan Hart03102132014-07-01 23:22:04 -0700584 hostService.addHostListener(this);
Toshio Koide2f570c12014-02-06 16:55:32 -0800585
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700586 eventChannel = datagridService.createChannel(
587 TopologyManager.EVENT_CHANNEL_NAME,
588 byte[].class,
589 TopologyEvent.class);
590
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -0700591 mutableTopology = topologyService.getTopology();
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700592
Jonathan Hart88770672014-04-02 18:08:30 -0700593 // Run the cleanup thread
594 String enableCleanup =
595 context.getConfigParams(this).get(ENABLE_CLEANUP_PROPERTY);
596 if (enableCleanup != null
597 && enableCleanup.equalsIgnoreCase("false")) {
598 cleanupEnabled = false;
599 }
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700600
Jonathan Hart88770672014-04-02 18:08:30 -0700601 log.debug("Cleanup thread is {}enabled", (cleanupEnabled) ? "" : "not ");
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700602
Jonathan Hart88770672014-04-02 18:08:30 -0700603 if (cleanupEnabled) {
604 IThreadPoolService threadPool =
605 context.getServiceImpl(IThreadPoolService.class);
606 cleanupTask = new SingletonTask(threadPool.getScheduledExecutor(),
607 new SwitchCleanup());
608 // Run the cleanup task immediately on startup
609 cleanupTask.reschedule(0, TimeUnit.SECONDS);
610 }
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700611
612 // Run the Delayed Operations Handler thread
613 delayedOperationsHandler.start();
Jonathan Hart88770672014-04-02 18:08:30 -0700614 }
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700615
Jonathan Hart88770672014-04-02 18:08:30 -0700616 @Override
Jonathan Hart03102132014-07-01 23:22:04 -0700617 public void hostAdded(Host host) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700618 log.debug("Host added with MAC {}", host.getMacAddress());
TeruUd1c5b652014-03-24 13:58:46 -0700619
Jonathan Hart03102132014-07-01 23:22:04 -0700620 SwitchPort sp = new SwitchPort(host.getSwitchDPID(), host.getSwitchPort());
Jonathan Hart88770672014-04-02 18:08:30 -0700621 List<SwitchPort> spLists = new ArrayList<SwitchPort>();
622 spLists.add(sp);
Sangho Shin2f263692014-09-15 14:09:41 -0700623 HostData hostData = new HostData(host.getMacAddress(), host.getIpAddress());
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700624 hostData.setAttachmentPoints(spLists);
625 hostData.setLastSeenTime(host.getLastSeenTimestamp().getTime());
Jonathan Hart88770672014-04-02 18:08:30 -0700626 // Does not use vlan info now.
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700627 hostData.freeze();
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700628
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700629 publishAddHostEvent(hostData);
Jonathan Hart88770672014-04-02 18:08:30 -0700630 }
TeruUd1c5b652014-03-24 13:58:46 -0700631
Jonathan Hart88770672014-04-02 18:08:30 -0700632 @Override
Jonathan Hart03102132014-07-01 23:22:04 -0700633 public void hostRemoved(Host host) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700634 log.debug("Host removed with MAC {}", host.getMacAddress());
635
636 //
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700637 // Remove all previously added HostData for this MAC address
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700638 //
639 // TODO: Currently, the caller of hostRemoved() might not include
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700640 // the correct set of Attachment Points in the HostData entry itself.
641 // Also, we might have multiple HostData entries for the same
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700642 // host (MAC address), each containing a single (different) Attachment
643 // Point.
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700644 // Hence, here we have to cleanup all HostData entries for this
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700645 // particular host, based on its MAC address.
646 //
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700647 List<HostData> removeHostDataEntries = new LinkedList<>();
648 for (ConcurrentMap<ByteBuffer, HostData> cm : publishedHostDataEntries.values()) {
649 for (HostData hostData : cm.values()) {
650 if (hostData.getMac().equals(host.getMacAddress())) {
651 removeHostDataEntries.add(hostData);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700652 }
653 }
654 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700655 for (HostData hostData : removeHostDataEntries) {
656 publishRemoveHostEvent(hostData);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700657 }
Jonathan Hart88770672014-04-02 18:08:30 -0700658 }
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700659
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700660 @Override
661 public boolean publish(TopologyBatchOperation tbo) {
662 publishTopologyOperations(tbo);
663 return true;
664 }
665
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700666 /**
667 * Prepares the Controller role changed event for a switch.
668 *
669 * @param dpid the switch DPID
670 * @param role the new role of the controller
671 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700672 private void controllerRoleChanged(Dpid dpid, Role role) {
673 log.debug("Local switch controller mastership role changed: dpid = {} role = {}",
674 dpid, role);
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700675 MastershipData mastershipData =
676 new MastershipData(dpid, getOnosInstanceId(), role);
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700677 // FIXME should be merging, with existing attrs, etc..
678 // TODO define attr name as constant somewhere.
679 // TODO populate appropriate attributes.
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700680 mastershipData.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700681 TopologyElement.TYPE_ALL_LAYERS);
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700682 mastershipData.freeze();
683 publishAddSwitchMastershipEvent(mastershipData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700684 }
685
686 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700687 * Publishes ADD Mastership Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700688 *
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700689 * @param mastershipData the mastership event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700690 */
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700691 private void publishAddSwitchMastershipEvent(
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700692 MastershipData mastershipData) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700693 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700694 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700695 TopologyEvent topologyEvent =
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700696 new TopologyEvent(mastershipData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700697 tbo.appendAddOperation(topologyEvent);
698 publishTopologyOperations(tbo);
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700699 publishedMastershipDataEntries.put(mastershipData.getDpid(),
700 mastershipData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700701 }
702
703 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700704 * Publishes REMOVE Mastership Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700705 *
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700706 * @param mastershipData the mastership event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700707 */
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700708 private void publishRemoveSwitchMastershipEvent(
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700709 MastershipData mastershipData) {
710 if (publishedMastershipDataEntries.get(mastershipData.getDpid()) == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700711 return; // Nothing to do
712 }
713
714 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700715 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700716 TopologyEvent topologyEvent =
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700717 new TopologyEvent(mastershipData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700718 tbo.appendRemoveOperation(topologyEvent);
719 publishTopologyOperations(tbo);
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700720 publishedMastershipDataEntries.remove(mastershipData.getDpid());
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700721 }
722
723 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700724 * Publishes ADD Switch Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700725 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700726 * @param switchData the switch event to publish
727 * @param portDataEntries the corresponding port events for the switch to
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700728 * publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700729 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700730 private void publishAddSwitchEvent(SwitchData switchData,
731 Collection<PortData> portDataEntries) {
732 if (!registryService.hasControl(switchData.getOriginDpid().value())) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700733 log.debug("Not the master for switch {}. Suppressed switch add event {}.",
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700734 switchData.getOriginDpid(), switchData);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700735 return;
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700736 }
737
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700738 // Keep track of the old Port Events that should be removed
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700739 ConcurrentMap<ByteBuffer, PortData> oldPortDataEntries =
740 publishedPortDataEntries.get(switchData.getDpid());
741 if (oldPortDataEntries == null) {
742 oldPortDataEntries = new ConcurrentHashMap<>();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700743 }
744
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700745 // Publish the information for the switch
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700746 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700747 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700748 new TopologyEvent(switchData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700749 tbo.appendAddOperation(topologyEvent);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700750
751 // Publish the information for each port
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700752 ConcurrentMap<ByteBuffer, PortData> newPortDataEntries =
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700753 new ConcurrentHashMap<>();
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700754 for (PortData portData : portDataEntries) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700755 topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700756 new TopologyEvent(portData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700757 tbo.appendAddOperation(topologyEvent);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700758
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700759 ByteBuffer id = portData.getIDasByteBuffer();
760 newPortDataEntries.put(id, portData);
761 oldPortDataEntries.remove(id);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700762 }
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700763 publishTopologyOperations(tbo);
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700764 publishedSwitchDataEntries.put(switchData.getDpid(), switchData);
765 publishedPortDataEntries.put(switchData.getDpid(), newPortDataEntries);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700766
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700767 // Cleanup for each of the old removed port
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700768 for (PortData portData : oldPortDataEntries.values()) {
769 publishRemovePortEvent(portData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700770 }
771 }
772
773 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700774 * Publishes REMOVE Switch Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700775 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700776 * @param switchData the switch event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700777 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700778 private void publishRemoveSwitchEvent(SwitchData switchData) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700779 //
780 // TODO: Removed the check for now, because currently this method is
781 // also called by the SwitchCleanup thread, and in that case
782 // the Switch Event was published by some other ONOS instance.
783 //
784 /*
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700785 if (publishedSwitchDataEntries.get(switchData.getDpid()) == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700786 return; // Nothing to do
787 }
788 */
789
790 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700791 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700792 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700793 new TopologyEvent(switchData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700794 tbo.appendRemoveOperation(topologyEvent);
795 publishTopologyOperations(tbo);
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700796 publishedSwitchDataEntries.remove(switchData.getDpid());
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700797
798 // Cleanup for each port
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700799 ConcurrentMap<ByteBuffer, PortData> portDataEntries =
800 publishedPortDataEntries.get(switchData.getDpid());
801 if (portDataEntries != null) {
802 for (PortData portData : portDataEntries.values()) {
803 publishRemovePortEvent(portData);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700804 }
805 }
806
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700807 publishedPortDataEntries.remove(switchData.getDpid());
808 publishedLinkDataEntries.remove(switchData.getDpid());
809 publishedHostDataEntries.remove(switchData.getDpid());
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700810 }
811
812 /**
813 * Publishes ADD Port Event.
814 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700815 * @param portData the port event to publish
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700816 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700817 private void publishAddPortEvent(PortData portData) {
818 if (!registryService.hasControl(portData.getOriginDpid().value())) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700819 log.debug("Not the master for switch {}. Suppressed port add event {}.",
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700820 portData.getOriginDpid(), portData);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700821 return;
822 }
823
824 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700825 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700826 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700827 new TopologyEvent(portData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700828 tbo.appendAddOperation(topologyEvent);
829 publishTopologyOperations(tbo);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700830
831 // Store the new Port Event in the local cache
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700832 ConcurrentMap<ByteBuffer, PortData> portDataEntries =
833 ConcurrentUtils.putIfAbsent(publishedPortDataEntries,
834 portData.getDpid(),
835 new ConcurrentHashMap<ByteBuffer, PortData>());
836 portDataEntries.put(portData.getIDasByteBuffer(), portData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700837 }
838
839 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700840 * Publishes REMOVE Port Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700841 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700842 * @param portData the port event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700843 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700844 private void publishRemovePortEvent(PortData portData) {
845 ConcurrentMap<ByteBuffer, PortData> portDataEntries =
846 publishedPortDataEntries.get(portData.getDpid());
847 if (portDataEntries == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700848 return; // Nothing to do
849 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700850 if (portDataEntries.get(portData.getIDasByteBuffer()) == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700851 return; // Nothing to do
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700852 }
853
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700854 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700855 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700856 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700857 new TopologyEvent(portData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700858 tbo.appendRemoveOperation(topologyEvent);
859 publishTopologyOperations(tbo);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700860
861 // Cleanup for the incoming link(s)
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700862 ConcurrentMap<ByteBuffer, LinkData> linkDataEntries =
863 publishedLinkDataEntries.get(portData.getDpid());
864 if (linkDataEntries != null) {
865 for (LinkData linkData : linkDataEntries.values()) {
866 if (linkData.getDst().equals(portData.getSwitchPort())) {
867 publishRemoveLinkEvent(linkData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700868 }
869 }
870 }
871
872 // Cleanup for the connected hosts
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700873 ConcurrentMap<ByteBuffer, HostData> hostDataEntries =
874 publishedHostDataEntries.get(portData.getDpid());
875 if (hostDataEntries != null) {
876 for (HostData hostData : hostDataEntries.values()) {
877 for (SwitchPort swp : hostData.getAttachmentPoints()) {
878 if (swp.equals(portData.getSwitchPort())) {
879 publishRemoveHostEvent(hostData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700880 }
881 }
882 }
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700883 }
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700884
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700885 portDataEntries.remove(portData.getIDasByteBuffer());
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700886 }
887
888 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700889 * Publishes ADD Link Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700890 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700891 * @param linkData the link event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700892 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700893 private void publishAddLinkEvent(LinkData linkData) {
894 if (!registryService.hasControl(linkData.getOriginDpid().value())) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700895 log.debug("Not the master for dst switch {}. Suppressed link add event {}.",
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700896 linkData.getOriginDpid(), linkData);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700897 return;
898 }
899
900 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700901 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700902 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700903 new TopologyEvent(linkData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700904 tbo.appendAddOperation(topologyEvent);
905 publishTopologyOperations(tbo);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700906
907 // Store the new Link Event in the local cache
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700908 ConcurrentMap<ByteBuffer, LinkData> linkDataEntries =
909 ConcurrentUtils.putIfAbsent(publishedLinkDataEntries,
910 linkData.getDst().getDpid(),
911 new ConcurrentHashMap<ByteBuffer, LinkData>());
912 linkDataEntries.put(linkData.getIDasByteBuffer(), linkData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700913 }
914
915 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700916 * Publishes REMOVE Link Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700917 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700918 * @param linkData the link event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700919 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700920 private void publishRemoveLinkEvent(LinkData linkData) {
921 ConcurrentMap<ByteBuffer, LinkData> linkDataEntries =
922 publishedLinkDataEntries.get(linkData.getDst().getDpid());
923 if (linkDataEntries == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700924 return; // Nothing to do
925 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700926 if (linkDataEntries.get(linkData.getIDasByteBuffer()) == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700927 return; // Nothing to do
928 }
929
930 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700931 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700932 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700933 new TopologyEvent(linkData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700934 tbo.appendRemoveOperation(topologyEvent);
935 publishTopologyOperations(tbo);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700936
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700937 linkDataEntries.remove(linkData.getIDasByteBuffer());
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700938 }
939
940 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700941 * Publishes ADD Host Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700942 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700943 * @param hostData the host event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700944 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700945 private void publishAddHostEvent(HostData hostData) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700946 //
947 // NOTE: The implementation below assumes that there is just one
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700948 // attachment point stored in hostData. Currently, this assumption
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700949 // is true based on the existing implementation of the caller
950 // hostAdded().
951 //
952
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700953 if (!registryService.hasControl(hostData.getOriginDpid().value())) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700954 log.debug("Not the master for attachment switch {}. Suppressed host add event {}.",
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700955 hostData.getOriginDpid(), hostData);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700956 return;
957 }
958
959 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700960 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700961 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700962 new TopologyEvent(hostData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700963 tbo.appendAddOperation(topologyEvent);
964 publishTopologyOperations(tbo);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700965
966 // Store the new Host Event in the local cache
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700967 ConcurrentMap<ByteBuffer, HostData> hostDataEntries =
968 ConcurrentUtils.putIfAbsent(publishedHostDataEntries,
969 hostData.getOriginDpid(),
970 new ConcurrentHashMap<ByteBuffer, HostData>());
971 hostDataEntries.put(hostData.getIDasByteBuffer(), hostData);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700972 }
973
974 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700975 * Publishes REMOVE Host Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700976 *
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700977 * @param hostData the host event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700978 */
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700979 private void publishRemoveHostEvent(HostData hostData) {
980 ConcurrentMap<ByteBuffer, HostData> hostDataEntries =
981 publishedHostDataEntries.get(hostData.getOriginDpid());
982 if (hostDataEntries == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700983 return; // Nothing to do
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700984 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700985 if (hostDataEntries.get(hostData.getIDasByteBuffer()) == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700986 return; // Nothing to do
987 }
988
989 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700990 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700991 TopologyEvent topologyEvent =
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700992 new TopologyEvent(hostData, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700993 tbo.appendRemoveOperation(topologyEvent);
994 publishTopologyOperations(tbo);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700995
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700996 hostDataEntries.remove(hostData.getIDasByteBuffer());
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700997 }
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700998
999 /**
1000 * Publishes Topology Operations.
1001 *
1002 * @param tbo the Topology Operations to publish
1003 */
1004 private void publishTopologyOperations(TopologyBatchOperation tbo) {
1005 // TODO: This flag should be configurable
1006 boolean isGlobalLogWriter = false;
1007
1008 log.debug("Publishing: {}", tbo);
1009
1010 if (isGlobalLogWriter) {
1011 if (!topologyService.publish(tbo)) {
1012 log.debug("Cannot publish: {}", tbo);
1013 delayedOperations.add(tbo);
1014 }
1015 } else {
1016 // TODO: For now we publish each TopologyEvent independently
1017 for (BatchOperationEntry<TopologyBatchOperation.Operator,
1018 TopologyEvent> boe : tbo.getOperations()) {
1019 TopologyBatchOperation.Operator oper = boe.getOperator();
1020 TopologyEvent topologyEvent = boe.getTarget();
1021 switch (oper) {
1022 case ADD:
1023 eventChannel.addEntry(topologyEvent.getID(),
1024 topologyEvent);
1025 break;
1026 case REMOVE:
1027 eventChannel.removeEntry(topologyEvent.getID());
1028 break;
1029 default:
1030 log.error("Unknown Topology Batch Operation {}", oper);
1031 break;
1032 }
1033 }
1034 }
1035 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -08001036}