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