blob: 4ed59d700e75287bd9720aa6d4ebf613cd9191dc [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001package net.floodlightcontroller.staticflowentry;
2
3import java.io.IOException;
4import java.util.ArrayList;
5import java.util.Collection;
6import java.util.Collections;
7import java.util.Comparator;
8import java.util.Iterator;
9import java.util.HashMap;
10import java.util.List;
11import java.util.Map;
12import java.util.Map.Entry;
13import java.util.Set;
14import java.util.concurrent.ConcurrentHashMap;
15
16import net.floodlightcontroller.core.FloodlightContext;
17import net.floodlightcontroller.core.IFloodlightProviderService;
18import net.floodlightcontroller.core.IFloodlightProviderService.Role;
19import net.floodlightcontroller.core.IHAListener;
20import net.floodlightcontroller.core.IOFMessageListener;
21import net.floodlightcontroller.core.IOFSwitch;
22import net.floodlightcontroller.core.IOFSwitchListener;
23import net.floodlightcontroller.core.annotations.LogMessageCategory;
24import net.floodlightcontroller.core.annotations.LogMessageDoc;
25import net.floodlightcontroller.core.module.FloodlightModuleContext;
26import net.floodlightcontroller.core.module.FloodlightModuleException;
27import net.floodlightcontroller.core.module.IFloodlightModule;
28import net.floodlightcontroller.core.module.IFloodlightService;
29import net.floodlightcontroller.core.util.AppCookie;
30import net.floodlightcontroller.restserver.IRestApiService;
31import net.floodlightcontroller.staticflowentry.web.StaticFlowEntryWebRoutable;
32import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService;
33import net.floodlightcontroller.storage.IResultSet;
34import net.floodlightcontroller.storage.IStorageSourceService;
35import net.floodlightcontroller.storage.IStorageSourceListener;
36
37import net.floodlightcontroller.storage.StorageException;
38import org.openflow.protocol.OFFlowMod;
39import org.openflow.protocol.OFFlowRemoved;
40import org.openflow.protocol.OFMatch;
41import org.openflow.protocol.OFMessage;
42import org.openflow.protocol.OFType;
43import org.openflow.protocol.factory.BasicFactory;
44import org.openflow.util.HexString;
45import org.openflow.util.U16;
46import org.slf4j.Logger;
47import org.slf4j.LoggerFactory;
48
49@LogMessageCategory("Static Flow Pusher")
50/**
51 * This module is responsible for maintaining a set of static flows on
52 * switches. This is just a big 'ol dumb list of flows and something external
53 * is responsible for ensuring they make sense for the network.
54 */
55public class StaticFlowEntryPusher
56 implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
57 IStorageSourceListener, IOFMessageListener, IHAListener {
58 protected static Logger log = LoggerFactory.getLogger(StaticFlowEntryPusher.class);
59 public static final String StaticFlowName = "staticflowentry";
60
61 public static final int STATIC_FLOW_APP_ID = 10;
62
63 public static final String TABLE_NAME = "controller_staticflowtableentry";
64 public static final String COLUMN_NAME = "name";
65 public static final String COLUMN_SWITCH = "switch_id";
66 public static final String COLUMN_ACTIVE = "active";
67 public static final String COLUMN_IDLE_TIMEOUT = "idle_timeout";
68 public static final String COLUMN_HARD_TIMEOUT = "hard_timeout";
69 public static final String COLUMN_PRIORITY = "priority";
70 public static final String COLUMN_COOKIE = "cookie";
71 public static final String COLUMN_WILDCARD = "wildcards";
72 public static final String COLUMN_IN_PORT = "in_port";
73 public static final String COLUMN_DL_SRC = "dl_src";
74 public static final String COLUMN_DL_DST = "dl_dst";
75 public static final String COLUMN_DL_VLAN = "dl_vlan";
76 public static final String COLUMN_DL_VLAN_PCP = "dl_vlan_pcp";
77 public static final String COLUMN_DL_TYPE = "dl_type";
78 public static final String COLUMN_NW_TOS = "nw_tos";
79 public static final String COLUMN_NW_PROTO = "nw_proto";
80 public static final String COLUMN_NW_SRC = "nw_src"; // includes CIDR-style
81 // netmask, e.g.
82 // "128.8.128.0/24"
83 public static final String COLUMN_NW_DST = "nw_dst";
84 public static final String COLUMN_TP_DST = "tp_dst";
85 public static final String COLUMN_TP_SRC = "tp_src";
86 public static final String COLUMN_ACTIONS = "actions";
87 public static String ColumnNames[] = { COLUMN_NAME, COLUMN_SWITCH,
88 COLUMN_ACTIVE, COLUMN_IDLE_TIMEOUT, COLUMN_HARD_TIMEOUT,
89 COLUMN_PRIORITY, COLUMN_COOKIE, COLUMN_WILDCARD, COLUMN_IN_PORT,
90 COLUMN_DL_SRC, COLUMN_DL_DST, COLUMN_DL_VLAN, COLUMN_DL_VLAN_PCP,
91 COLUMN_DL_TYPE, COLUMN_NW_TOS, COLUMN_NW_PROTO, COLUMN_NW_SRC,
92 COLUMN_NW_DST, COLUMN_TP_DST, COLUMN_TP_SRC, COLUMN_ACTIONS };
93
94
95 protected IFloodlightProviderService floodlightProvider;
96 protected IStorageSourceService storageSource;
97 protected IRestApiService restApi;
98
99 // Map<DPID, Map<Name, FlowMod>> ; FlowMod can be null to indicate non-active
100 protected Map<String, Map<String, OFFlowMod>> entriesFromStorage;
101 // Entry Name -> DPID of Switch it's on
102 protected Map<String, String> entry2dpid;
103
104 private BasicFactory ofMessageFactory;
105
106 // Class to sort FlowMod's by priority, from lowest to highest
107 class FlowModSorter implements Comparator<String> {
108 private String dpid;
109 public FlowModSorter(String dpid) {
110 this.dpid = dpid;
111 }
112 @Override
113 public int compare(String o1, String o2) {
114 OFFlowMod f1 = entriesFromStorage.get(dpid).get(o1);
115 OFFlowMod f2 = entriesFromStorage.get(dpid).get(o2);
116 if (f1 == null || f2 == null) // sort active=false flows by key
117 return o1.compareTo(o2);
118 return U16.f(f1.getPriority()) - U16.f(f2.getPriority());
119 }
120 };
121
122 /**
123 * used for debugging and unittests
124 * @return the number of static flow entries as cached from storage
125 */
126 public int countEntries() {
127 int size = 0;
128 if (entriesFromStorage == null)
129 return 0;
130 for (String ofswitch : entriesFromStorage.keySet())
131 size += entriesFromStorage.get(ofswitch).size();
132 return size;
133 }
134
135 public IFloodlightProviderService getFloodlightProvider() {
136 return floodlightProvider;
137 }
138
139 public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) {
140 this.floodlightProvider = floodlightProvider;
141 }
142
143 public void setStorageSource(IStorageSourceService storageSource) {
144 this.storageSource = storageSource;
145 }
146
147 /**
148 * Reads from our entriesFromStorage for the specified switch and
149 * sends the FlowMods down to the controller in <b>sorted</b> order.
150 *
151 * Sorted is important to maintain correctness of the switch:
152 * if a packet would match both a lower and a higher priority
153 * rule, then we want it to match the higher priority or nothing,
154 * but never just the lower priority one. Inserting from high to
155 * low priority fixes this.
156 *
157 * TODO consider adding a "block all" flow mod and then removing it
158 * while starting up.
159 *
160 * @param sw The switch to send entries to
161 */
162 protected void sendEntriesToSwitch(IOFSwitch sw) {
163 String dpid = sw.getStringId();
164
165 if ((entriesFromStorage != null) && (entriesFromStorage.containsKey(dpid))) {
166 Map<String, OFFlowMod> entries = entriesFromStorage.get(dpid);
167 List<String> sortedList = new ArrayList<String>(entries.keySet());
168 // weird that Collections.sort() returns void
169 Collections.sort( sortedList, new FlowModSorter(dpid));
170 for (String entryName : sortedList) {
171 OFFlowMod flowMod = entries.get(entryName);
172 if (flowMod != null) {
173 if (log.isDebugEnabled()) {
174 log.debug("Pushing static entry {} for {}", dpid, entryName);
175 }
176 writeFlowModToSwitch(sw, flowMod);
177 }
178 }
179 }
180 }
181
182 /**
183 * Used only for bundle-local indexing
184 *
185 * @param map
186 * @return
187 */
188
189 protected Map<String, String> computeEntry2DpidMap(
190 Map<String, Map<String, OFFlowMod>> map) {
191 Map<String, String> ret = new HashMap<String, String>();
192 for(String dpid : map.keySet()) {
193 for( String entry: map.get(dpid).keySet())
194 ret.put(entry, dpid);
195 }
196 return ret;
197 }
198
199 /**
200 * Read entries from storageSource, and store them in a hash
201 *
202 * @return
203 */
204 @LogMessageDoc(level="ERROR",
205 message="failed to access storage: {reason}",
206 explanation="Could not retrieve static flows from the system " +
207 "database",
208 recommendation=LogMessageDoc.CHECK_CONTROLLER)
209 private Map<String, Map<String, OFFlowMod>> readEntriesFromStorage() {
210 Map<String, Map<String, OFFlowMod>> entries = new ConcurrentHashMap<String, Map<String, OFFlowMod>>();
211 try {
212 Map<String, Object> row;
213 // null1=no predicate, null2=no ordering
214 IResultSet resultSet = storageSource.executeQuery(TABLE_NAME,
215 ColumnNames, null, null);
216 for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) {
217 row = it.next().getRow();
218 parseRow(row, entries);
219 }
220 } catch (StorageException e) {
221 log.error("failed to access storage: {}", e.getMessage());
222 // if the table doesn't exist, then wait to populate later via
223 // setStorageSource()
224 }
225 return entries;
226 }
227
228 /**
229 * Take a single row, turn it into a flowMod, and add it to the
230 * entries{$dpid}.{$entryName}=FlowMod
231 *
232 * IF an entry is in active, mark it with FlowMod = null
233 *
234 * @param row
235 * @param entries
236 */
237
238 void parseRow(Map<String, Object> row,
239 Map<String, Map<String, OFFlowMod>> entries) {
240 String switchName = null;
241 String entryName = null;
242
243 StringBuffer matchString = new StringBuffer();
244 if (ofMessageFactory == null) // lazy init
245 ofMessageFactory = new BasicFactory();
246
247 OFFlowMod flowMod = (OFFlowMod) ofMessageFactory
248 .getMessage(OFType.FLOW_MOD);
249
250 if (!row.containsKey(COLUMN_SWITCH) || !row.containsKey(COLUMN_NAME)) {
251 log.debug(
252 "skipping entry with missing required 'switch' or 'name' entry: {}",
253 row);
254 return;
255 }
256 // most error checking done with ClassCastException
257 try {
258 // first, snag the required entries, for debugging info
259 switchName = (String) row.get(COLUMN_SWITCH);
260 entryName = (String) row.get(COLUMN_NAME);
261 if (!entries.containsKey(switchName))
262 entries.put(switchName, new HashMap<String, OFFlowMod>());
263 StaticFlowEntries.initDefaultFlowMod(flowMod, entryName);
264
265 for (String key : row.keySet()) {
266 if (row.get(key) == null)
267 continue;
268 if ( key.equals(COLUMN_SWITCH) || key.equals(COLUMN_NAME)
269 || key.equals("id"))
270 continue; // already handled
271 // explicitly ignore timeouts and wildcards
272 if ( key.equals(COLUMN_HARD_TIMEOUT) || key.equals(COLUMN_IDLE_TIMEOUT) ||
273 key.equals(COLUMN_WILDCARD))
274 continue;
275 if ( key.equals(COLUMN_ACTIVE)) {
276 if (! Boolean.valueOf((String) row.get(COLUMN_ACTIVE))) {
277 log.debug("skipping inactive entry {} for switch {}",
278 entryName, switchName);
279 entries.get(switchName).put(entryName, null); // mark this an inactive
280 return;
281 }
282 } else if ( key.equals(COLUMN_ACTIONS)){
283 StaticFlowEntries.parseActionString(flowMod, (String) row.get(COLUMN_ACTIONS), log);
284 } else if ( key.equals(COLUMN_COOKIE)) {
285 flowMod.setCookie(
286 StaticFlowEntries.computeEntryCookie(flowMod,
287 Integer.valueOf((String) row.get(COLUMN_COOKIE)),
288 entryName)
289 );
290 } else if ( key.equals(COLUMN_PRIORITY)) {
291 flowMod.setPriority(U16.t(Integer.valueOf((String) row.get(COLUMN_PRIORITY))));
292 } else { // the rest of the keys are for OFMatch().fromString()
293 if (matchString.length() > 0)
294 matchString.append(",");
295 matchString.append(key + "=" + row.get(key).toString());
296 }
297 }
298 } catch (ClassCastException e) {
299 if (entryName != null && switchName != null)
300 log.debug(
301 "skipping entry {} on switch {} with bad data : "
302 + e.getMessage(), entryName, switchName);
303 else
304 log.debug("skipping entry with bad data: {} :: {} ",
305 e.getMessage(), e.getStackTrace());
306 }
307
308 OFMatch ofMatch = new OFMatch();
309 String match = matchString.toString();
310 try {
311 ofMatch.fromString(match);
312 } catch (IllegalArgumentException e) {
313 log.debug(
314 "ignoring flow entry {} on switch {} with illegal OFMatch() key: "
315 + match, entryName, switchName);
316 return;
317 }
318 flowMod.setMatch(ofMatch);
319
320 entries.get(switchName).put(entryName, flowMod);
321 }
322
323 @Override
324 public void addedSwitch(IOFSwitch sw) {
325 log.debug("addedSwitch {}; processing its static entries", sw);
326 sendEntriesToSwitch(sw);
327 }
328
329 @Override
330 public void removedSwitch(IOFSwitch sw) {
331 log.debug("removedSwitch {}", sw);
332 // do NOT delete from our internal state; we're tracking the rules,
333 // not the switches
334 }
335
336 @Override
337 public void switchPortChanged(Long switchId) {
338 // no-op
339 }
340
341 /**
342 * This handles both rowInsert() and rowUpdate()
343 */
344
345 @Override
346 public void rowsModified(String tableName, Set<Object> rowKeys) {
347 log.debug("Modifying Table {}", tableName);
348
349 HashMap<String, Map<String, OFFlowMod>> entriesToAdd =
350 new HashMap<String, Map<String, OFFlowMod>>();
351 // build up list of what was added
352 for(Object key: rowKeys) {
353 IResultSet resultSet = storageSource.getRow(tableName, key);
354 for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) {
355 Map<String, Object> row = it.next().getRow();
356 parseRow(row, entriesToAdd);
357 }
358 }
359 // batch updates by switch and blast them out
360 for (String dpid : entriesToAdd.keySet()) {
361 if (!entriesFromStorage.containsKey(dpid))
362 entriesFromStorage.put(dpid, new HashMap<String, OFFlowMod>());
363 List<OFMessage> outQueue = new ArrayList<OFMessage>();
364 for(String entry : entriesToAdd.get(dpid).keySet()) {
365 OFFlowMod newFlowMod = entriesToAdd.get(dpid).get(entry);
366 OFFlowMod oldFlowMod = entriesFromStorage.get(dpid).get(entry);
367 if (oldFlowMod != null) { // remove any pre-existing rule
368 oldFlowMod.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
369 outQueue.add(oldFlowMod);
370 }
371 if (newFlowMod != null) {
372 entriesFromStorage.get(dpid).put(entry, newFlowMod);
373 outQueue.add(newFlowMod);
374 entry2dpid.put(entry, dpid);
375 } else {
376 entriesFromStorage.get(dpid).remove(entry);
377 entry2dpid.remove(entry);
378 }
379 }
380
381 writeOFMessagesToSwitch(HexString.toLong(dpid), outQueue);
382 }
383 }
384
385 @Override
386 public void rowsDeleted(String tableName, Set<Object> rowKeys) {
387 if (log.isDebugEnabled()) {
388 log.debug("deleting from Table {}", tableName);
389 }
390
391 for(Object obj : rowKeys) {
392 if (!(obj instanceof String)) {
393 log.debug("tried to delete non-string key {}; ignoring", obj);
394 continue;
395 }
396 deleteStaticFlowEntry((String) obj);
397 }
398 }
399
400 @LogMessageDoc(level="ERROR",
401 message="inconsistent internal state: no switch has rule {rule}",
402 explanation="Inconsistent internat state discovered while " +
403 "deleting a static flow rule",
404 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG)
405 private boolean deleteStaticFlowEntry(String entryName) {
406 String dpid = entry2dpid.get(entryName);
407 if (log.isDebugEnabled()) {
408 log.debug("Deleting flow {} for switch {}", entryName, dpid);
409 }
410 if (dpid == null) {
411 log.error("inconsistent internal state: no switch has rule {}",
412 entryName);
413 return false;
414 }
415
416 // send flow_mod delete
417 OFFlowMod flowMod = entriesFromStorage.get(dpid).get(entryName);
418 flowMod.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
419
420 if (entriesFromStorage.containsKey(dpid) &&
421 entriesFromStorage.get(dpid).containsKey(entryName)) {
422 entriesFromStorage.get(dpid).remove(entryName);
423 } else {
424 log.debug("Tried to delete non-existent entry {} for switch {}",
425 entryName, dpid);
426 return false;
427 }
428
429 writeFlowModToSwitch(HexString.toLong(dpid), flowMod);
430 return true;
431 }
432
433 /**
434 * Writes a list of OFMessages to a switch
435 * @param dpid The datapath ID of the switch to write to
436 * @param messages The list of OFMessages to write.
437 */
438 @LogMessageDoc(level="ERROR",
439 message="Tried to write to switch {switch} but got {error}",
440 explanation="An I/O error occured while trying to write a " +
441 "static flow to a switch",
442 recommendation=LogMessageDoc.CHECK_SWITCH)
443 private void writeOFMessagesToSwitch(long dpid, List<OFMessage> messages) {
444 IOFSwitch ofswitch = floodlightProvider.getSwitches().get(dpid);
445 if (ofswitch != null) { // is the switch connected
446 try {
447 if (log.isDebugEnabled()) {
448 log.debug("Sending {} new entries to {}", messages.size(), dpid);
449 }
450 ofswitch.write(messages, null);
451 ofswitch.flush();
452 } catch (IOException e) {
453 log.error("Tried to write to switch {} but got {}", dpid, e.getMessage());
454 }
455 }
456 }
457
458 /**
459 * Writes an OFFlowMod to a switch. It checks to make sure the switch
460 * exists before it sends
461 * @param dpid The data to write the flow mod to
462 * @param flowMod The OFFlowMod to write
463 */
464 private void writeFlowModToSwitch(long dpid, OFFlowMod flowMod) {
465 Map<Long,IOFSwitch> switches = floodlightProvider.getSwitches();
466 IOFSwitch ofSwitch = switches.get(dpid);
467 if (ofSwitch == null) {
468 if (log.isDebugEnabled()) {
469 log.debug("Not deleting key {} :: switch {} not connected",
470 dpid);
471 }
472 return;
473 }
474 writeFlowModToSwitch(ofSwitch, flowMod);
475 }
476
477 /**
478 * Writes an OFFlowMod to a switch
479 * @param sw The IOFSwitch to write to
480 * @param flowMod The OFFlowMod to write
481 */
482 @LogMessageDoc(level="ERROR",
483 message="Tried to write OFFlowMod to {switch} but got {error}",
484 explanation="An I/O error occured while trying to write a " +
485 "static flow to a switch",
486 recommendation=LogMessageDoc.CHECK_SWITCH)
487 private void writeFlowModToSwitch(IOFSwitch sw, OFFlowMod flowMod) {
488 try {
489 sw.write(flowMod, null);
490 sw.flush();
491 } catch (IOException e) {
492 log.error("Tried to write OFFlowMod to {} but failed: {}",
493 HexString.toHexString(sw.getId()), e.getMessage());
494 }
495 }
496
497 @Override
498 public String getName() {
499 return StaticFlowName;
500 }
501
502 @Override
503 @LogMessageDoc(level="ERROR",
504 message="Got a FlowRemove message for a infinite " +
505 "timeout flow: {flow} from switch {switch}",
506 explanation="Flows with infinite timeouts should not expire. " +
507 "The switch has expired the flow anyway.",
508 recommendation=LogMessageDoc.REPORT_SWITCH_BUG)
509 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
510 switch (msg.getType()) {
511 case FLOW_REMOVED:
512 break;
513 default:
514 return Command.CONTINUE;
515 }
516 OFFlowRemoved flowRemoved = (OFFlowRemoved) msg;
517 long cookie = flowRemoved.getCookie();
518 /**
519 * This is just to sanity check our assumption that static flows
520 * never expire.
521 */
522 if( AppCookie.extractApp(cookie) == STATIC_FLOW_APP_ID) {
523 if (flowRemoved.getReason() !=
524 OFFlowRemoved.OFFlowRemovedReason.OFPRR_DELETE)
525 log.error("Got a FlowRemove message for a infinite " +
526 "timeout flow: {} from switch {}", msg, sw);
527 return Command.STOP; // only for us
528 } else
529 return Command.CONTINUE;
530 }
531
532 @Override
533 public boolean isCallbackOrderingPrereq(OFType type, String name) {
534 return false; // no dependency for non-packet in
535 }
536
537 @Override
538 public boolean isCallbackOrderingPostreq(OFType type, String name) {
539 return false; // no dependency for non-packet in
540 }
541
542 // IFloodlightModule
543
544 @Override
545 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
546 Collection<Class<? extends IFloodlightService>> l =
547 new ArrayList<Class<? extends IFloodlightService>>();
548 l.add(IStaticFlowEntryPusherService.class);
549 return l;
550 }
551
552 @Override
553 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
554 Map<Class<? extends IFloodlightService>,
555 IFloodlightService> m =
556 new HashMap<Class<? extends IFloodlightService>,
557 IFloodlightService>();
558 m.put(IStaticFlowEntryPusherService.class, this);
559 return m;
560 }
561
562 @Override
563 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
564 Collection<Class<? extends IFloodlightService>> l =
565 new ArrayList<Class<? extends IFloodlightService>>();
566 l.add(IFloodlightProviderService.class);
567 l.add(IStorageSourceService.class);
568 l.add(IRestApiService.class);
569 return l;
570 }
571
572 @Override
573 public void init(FloodlightModuleContext context)
574 throws FloodlightModuleException {
575 floodlightProvider =
576 context.getServiceImpl(IFloodlightProviderService.class);
577 storageSource =
578 context.getServiceImpl(IStorageSourceService.class);
579 restApi =
580 context.getServiceImpl(IRestApiService.class);
581 }
582
583 @Override
584 public void startUp(FloodlightModuleContext context) {
585 floodlightProvider.addOFMessageListener(OFType.FLOW_REMOVED, this);
586 floodlightProvider.addOFSwitchListener(this);
587 floodlightProvider.addHAListener(this);
588
589 // assumes no switches connected at startup()
590 storageSource.createTable(TABLE_NAME, null);
591 storageSource.setTablePrimaryKeyName(TABLE_NAME, COLUMN_NAME);
592 storageSource.addListener(TABLE_NAME, this);
593 entriesFromStorage = readEntriesFromStorage();
594 entry2dpid = computeEntry2DpidMap(entriesFromStorage);
595 restApi.addRestletRoutable(new StaticFlowEntryWebRoutable());
596 }
597
598 // IStaticFlowEntryPusherService methods
599
600 @Override
601 public void addFlow(String name, OFFlowMod fm, String swDpid) {
602 Map<String, Object> fmMap = StaticFlowEntries.flowModToStorageEntry(fm, swDpid, name);
603 entry2dpid.put(name, swDpid);
604 Map<String, OFFlowMod> switchEntries = entriesFromStorage.get(swDpid);
605 if (switchEntries == null) {
606 switchEntries = new HashMap<String, OFFlowMod>();
607 entriesFromStorage.put(swDpid, switchEntries);
608 }
609 switchEntries.put(name, fm);
610 storageSource.insertRowAsync(TABLE_NAME, fmMap);
611 }
612
613 @Override
614 public void deleteFlow(String name) {
615 storageSource.deleteRowAsync(TABLE_NAME, name);
616 // TODO - What if there is a delay in storage?
617 }
618
619 @Override
620 public void deleteAllFlows() {
621 for (String entry : entry2dpid.keySet()) {
622 deleteFlow(entry);
623 }
624 }
625
626 @Override
627 public void deleteFlowsForSwitch(long dpid) {
628 String sDpid = HexString.toHexString(dpid);
629
630 for (Entry<String, String> e : entry2dpid.entrySet()) {
631 if (e.getValue().equals(sDpid))
632 deleteFlow(e.getKey());
633 }
634 }
635
636 @Override
637 public Map<String, Map<String, OFFlowMod>> getFlows() {
638 return entriesFromStorage;
639 }
640
641 @Override
642 public Map<String, OFFlowMod> getFlows(String dpid) {
643 return entriesFromStorage.get(dpid);
644 }
645
646
647 // IHAListener
648
649 @Override
650 public void roleChanged(Role oldRole, Role newRole) {
651 switch(newRole) {
652 case MASTER:
653 if (oldRole == Role.SLAVE) {
654 log.debug("Re-reading static flows from storage due " +
655 "to HA change from SLAVE->MASTER");
656 entriesFromStorage = readEntriesFromStorage();
657 entry2dpid = computeEntry2DpidMap(entriesFromStorage);
658 }
659 break;
660 case SLAVE:
661 log.debug("Clearing in-memory flows due to " +
662 "HA change to SLAVE");
663 entry2dpid.clear();
664 entriesFromStorage.clear();
665 break;
666 default:
667 break;
668 }
669 }
670
671 @Override
672 public void controllerNodeIPsChanged(
673 Map<String, String> curControllerNodeIPs,
674 Map<String, String> addedControllerNodeIPs,
675 Map<String, String> removedControllerNodeIPs) {
676 // ignore
677 }
678
679}