blob: 0307d4c937fc1115ecd9a60640c8451f7f91728a [file] [log] [blame]
Ray Milkey18b44ac2014-08-22 08:29:47 -07001package net.onrc.onos.core.matchaction;
2
Ray Milkey18b44ac2014-08-22 08:29:47 -07003import java.util.ArrayList;
Ray Milkey18b44ac2014-08-22 08:29:47 -07004import java.util.EventListener;
5import java.util.HashMap;
6import java.util.HashSet;
7import java.util.List;
8import java.util.Map;
9import java.util.Set;
10import java.util.concurrent.ArrayBlockingQueue;
11import java.util.concurrent.BlockingQueue;
12import java.util.concurrent.ConcurrentHashMap;
13import java.util.concurrent.ConcurrentMap;
14import java.util.concurrent.ExecutionException;
15
Brian O'Connoraa5a7b92014-08-29 14:45:18 -070016import net.floodlightcontroller.core.IFloodlightProviderService;
17import net.floodlightcontroller.core.internal.OFMessageFuture;
18import net.floodlightcontroller.core.module.IFloodlightService;
19import net.onrc.onos.api.flowmanager.ConflictDetectionPolicy;
20import net.onrc.onos.core.datagrid.IDatagridService;
21import net.onrc.onos.core.datagrid.IEventChannel;
22import net.onrc.onos.core.datagrid.IEventChannelListener;
23import net.onrc.onos.core.flowprogrammer.IFlowPusherService;
Ray Milkey626a2b12014-09-02 11:19:06 -070024import net.onrc.onos.core.registry.IControllerRegistryService;
Brian O'Connoraa5a7b92014-08-29 14:45:18 -070025import net.onrc.onos.core.util.Dpid;
Ray Milkey626a2b12014-09-02 11:19:06 -070026import net.onrc.onos.core.util.IdBlockAllocator;
Brian O'Connoraa5a7b92014-08-29 14:45:18 -070027import net.onrc.onos.core.util.IdGenerator;
28import net.onrc.onos.core.util.SwitchPort;
29
30import org.apache.commons.lang3.tuple.Pair;
31import org.projectfloodlight.openflow.protocol.OFBarrierReply;
32import org.slf4j.Logger;
33import org.slf4j.LoggerFactory;
Ray Milkey18b44ac2014-08-22 08:29:47 -070034
Ray Milkey18b44ac2014-08-22 08:29:47 -070035/**
36 * Manages Match-Action entries.
37 * <p>
38 * TODO: Make all methods thread-safe
39 */
40public class MatchActionComponent implements MatchActionService, IFloodlightService {
41
42 private static final Logger log = LoggerFactory.getLogger(MatchActionService.class);
Ray Milkeya313cde2014-09-05 09:02:52 -070043 private final IFlowPusherService pusher;
44 private final IFloodlightProviderService provider;
Ray Milkey18b44ac2014-08-22 08:29:47 -070045
Ray Milkeya313cde2014-09-05 09:02:52 -070046 private final ConcurrentMap<MatchActionId, MatchAction> matchActionMap = new ConcurrentHashMap<>();
47 private final ConcurrentMap<MatchActionOperationsId, MatchActionOperations> matchSetMap =
Ray Milkey18b44ac2014-08-22 08:29:47 -070048 new ConcurrentHashMap<>();
49 // TODO - want something better here for the resolved Queue
Ray Milkeya313cde2014-09-05 09:02:52 -070050 private final BlockingQueue<MatchActionOperationsId> resolvedQueue = new ArrayBlockingQueue<>(100);
51 private final BlockingQueue<MatchActionOperations> installationWorkQueue = new ArrayBlockingQueue<>(100);
Ray Milkey18b44ac2014-08-22 08:29:47 -070052
53 private IEventChannel<String, MatchActionOperations> installSetChannel;
54 private IEventChannel<String, SwitchResultList> installSetReplyChannel;
55
Ray Milkey18b44ac2014-08-22 08:29:47 -070056 private final IDatagridService datagrid;
Ray Milkeya313cde2014-09-05 09:02:52 -070057 private final IControllerRegistryService registryService;
Ray Milkey626a2b12014-09-02 11:19:06 -070058
59 private MatchActionIdGeneratorWithIdBlockAllocator matchActionIdGenerator;
60 private MatchActionOperationsIdGeneratorWithIdBlockAllocator matchActionOperationsIdGenerator;
Ray Milkey18b44ac2014-08-22 08:29:47 -070061
Ray Milkeya313cde2014-09-05 09:02:52 -070062 /**
63 * Constructs a MatchActionComponent given the services it depends on.
64 *
65 * @param newDatagrid datagrid dependency
66 * @param newPusher flow pusher dependency
67 * @param newProvider provider used for switch queries
68 * @param newRegistryService registry for ID block allocation
69 */
Ray Milkey18b44ac2014-08-22 08:29:47 -070070 public MatchActionComponent(final IDatagridService newDatagrid,
71 final IFlowPusherService newPusher,
Ray Milkey626a2b12014-09-02 11:19:06 -070072 final IFloodlightProviderService newProvider,
73 final IControllerRegistryService newRegistryService) {
Ray Milkey18b44ac2014-08-22 08:29:47 -070074 datagrid = newDatagrid;
75 pusher = newPusher;
76 provider = newProvider;
Ray Milkey626a2b12014-09-02 11:19:06 -070077 registryService = newRegistryService;
Ray Milkey18b44ac2014-08-22 08:29:47 -070078 }
79
Ray Milkeya313cde2014-09-05 09:02:52 -070080 /**
81 * Starts the component. Created channels used for communication and
82 * creates producer and consumer threads.
83 */
Ray Milkey18b44ac2014-08-22 08:29:47 -070084 public void start() {
Ray Milkey626a2b12014-09-02 11:19:06 -070085 IdBlockAllocator idBlockAllocator = registryService;
86 matchActionIdGenerator =
87 new MatchActionIdGeneratorWithIdBlockAllocator(idBlockAllocator);
88 matchActionOperationsIdGenerator =
89 new MatchActionOperationsIdGeneratorWithIdBlockAllocator(idBlockAllocator);
90
Ray Milkey18b44ac2014-08-22 08:29:47 -070091 installSetChannel = datagrid.createChannel("onos.matchaction.installSetChannel",
92 String.class,
93 MatchActionOperations.class);
94
95 installSetReplyChannel = datagrid.createChannel("onos.matchaction.installSetReplyChannel",
96 String.class,
97 SwitchResultList.class);
98
Ray Milkeya313cde2014-09-05 09:02:52 -070099 final Thread coordinator = new Coordinator();
Ray Milkey18b44ac2014-08-22 08:29:47 -0700100 coordinator.start();
101
Ray Milkeya313cde2014-09-05 09:02:52 -0700102 // TODO Single instance for now, should be a work queue of some sort eventually
103 final Thread installer = new InstallerWorker();
Ray Milkey18b44ac2014-08-22 08:29:47 -0700104 installer.start();
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700105
Ray Milkeya313cde2014-09-05 09:02:52 -0700106 final Installer installerListener = new Installer();
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700107 installerListener.start();
Ray Milkey18b44ac2014-08-22 08:29:47 -0700108 }
109
Ray Milkeya313cde2014-09-05 09:02:52 -0700110 /**
111 * Installs a set of MatchActionOperations.
112 *
113 * @param matchSet the set of MatchActions to install
114 * @return identifier of the installed operations
115 */
Ray Milkey18b44ac2014-08-22 08:29:47 -0700116 public MatchActionOperationsId installMatchActionOperations(MatchActionOperations matchSet) {
117 if (checkResolved(matchSet)) {
118 matchSet.setState(MatchActionOperationsState.RESOLVED);
119 } else {
120 matchSet.setState(MatchActionOperationsState.INIT);
121 }
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700122 log.trace("MatchActionsOperations set added: {} {} {}",
123 matchSet.getOperationsId(),
124 matchSet.getState(),
125 matchSet.toString());
Ray Milkey18b44ac2014-08-22 08:29:47 -0700126 matchSetMap.put(matchSet.getOperationsId(), matchSet);
127 if (matchSet.getState() == MatchActionOperationsState.RESOLVED) {
128 resolvedQueue.add(matchSet.getOperationsId());
129 }
130 return matchSet.getOperationsId();
131 }
132
Ray Milkeya313cde2014-09-05 09:02:52 -0700133 /**
134 * Returns the state of a set of operations.
135 *
136 * @param matchSetId identifier of the MatchActionOperations being queried.
137 * @return state of the given operations
138 */
Ray Milkey18b44ac2014-08-22 08:29:47 -0700139 public MatchActionOperationsState getMatchActionOperationsState(MatchActionOperationsId matchSetId) {
140 MatchActionOperations set = matchSetMap.get(matchSetId);
141 return (set == null) ? null : set.getState();
142 }
143
Ray Milkeya313cde2014-09-05 09:02:52 -0700144 /**
145 * Checks if a given set of operations has all of its dependencies resolved.
146 *
147 * @param matchSet Operations set to check
148 * @return true if all dependencies are resolved, false otherwise
149 */
150 private boolean checkResolved(MatchActionOperations matchSet) {
Ray Milkey18b44ac2014-08-22 08:29:47 -0700151 boolean resolved = true;
152 for (MatchActionOperationsId setId : matchSet.getDependencies()) {
153 MatchActionOperations set = matchSetMap.get(setId);
154 if (set == null || set.getState() != MatchActionOperationsState.RESOLVED) {
155 resolved = false;
156 break;
157 }
158 }
159 return resolved;
160 }
161
Ray Milkeya313cde2014-09-05 09:02:52 -0700162 /**
163 * Producer class for MatchActionOperations. An instance of this runs on
164 * each ONOS node. Requests come in via the resolved queue, and are
165 * distributed to workers running on each ONOS instance via a channel.
166 */
167 private final class Coordinator extends Thread
Ray Milkey18b44ac2014-08-22 08:29:47 -0700168 implements IEventChannelListener<String, SwitchResultList> {
169
Ray Milkeya313cde2014-09-05 09:02:52 -0700170 private final Map<MatchActionOperationsId,
171 Map<Dpid, SwitchResult>>
172 pendingMatchActionOperations = new HashMap<>();
Ray Milkey18b44ac2014-08-22 08:29:47 -0700173
Ray Milkeya313cde2014-09-05 09:02:52 -0700174 /**
175 * Default constructor.
176 */
177 Coordinator() {
Ray Milkey18b44ac2014-08-22 08:29:47 -0700178 installSetReplyChannel.addListener(this);
179 }
180
181 @Override
182 public void run() {
Ray Milkeya313cde2014-09-05 09:02:52 -0700183 //noinspection InfiniteLoopStatement - for IntelliJ
Ray Milkey18b44ac2014-08-22 08:29:47 -0700184 while (true) {
185 // 1. Remove MatchActionOperations(s) from the Global Resolved Queue
186 try {
187 MatchActionOperationsId setId = resolvedQueue.take();
188 processSet(setId);
189 } catch (InterruptedException e) {
190 log.warn("Error taking from resolved queue: {}", e.getMessage());
191 }
192 }
193 }
194
Ray Milkeya313cde2014-09-05 09:02:52 -0700195 /**
196 * Processes an inbound MatchActionOperations object.
197 *
198 * @param setId Identifier of the MatchActionOperations object
199 */
Ray Milkey18b44ac2014-08-22 08:29:47 -0700200 private void processSet(MatchActionOperationsId setId) {
201 MatchActionOperations matchSet = matchSetMap.get(setId);
202 matchSet.setState(MatchActionOperationsState.PENDING);
203 matchSetMap.put(setId, matchSet);
204
205 // TODO apply updates to in-memory flow table and resolve conflicts
206 // TODO generate apply and undo sets, using MatchActionOperations for now...
207
208 // build pending switches set for coordinator tracking
209 Map<Dpid, SwitchResult> switches = new HashMap<>();
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700210 for (MatchActionOperationEntry matchActionOp : matchSet.getOperations()) {
211 MatchAction matchAction = matchActionOp.getTarget();
212 SwitchPort sw = matchAction.getSwitchPort();
Ray Milkey18b44ac2014-08-22 08:29:47 -0700213 switches.put(sw.getDpid(), new SwitchResult(setId, sw.getDpid()));
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700214 switch(matchActionOp.getOperator()) {
215 case ADD:
216 matchActionMap.put(matchAction.getId(), matchAction);
217 break;
218 case REMOVE:
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700219 // TODO we may want to be more careful when removing MatchActions
220 matchActionMap.remove(matchAction.getId());
221 break;
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700222 default:
223 throw new UnsupportedOperationException(
224 "Unsupported MatchAction operation" +
225 matchActionOp.getOperator().toString());
226 }
Ray Milkey18b44ac2014-08-22 08:29:47 -0700227 }
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700228 pendingMatchActionOperations.put(setId, switches);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700229
230 // distribute apply/undo sets to cluster
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700231 installSetChannel.addTransientEntry(setId.toString(), matchSet);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700232 }
233
234 @Override
235 public void entryAdded(SwitchResultList value) {
236 updateSwitchResults(value);
237 }
238
239 @Override
240 public void entryRemoved(SwitchResultList value) {
241 // noop
242 }
243
244 @Override
245 public void entryUpdated(SwitchResultList value) {
246 updateSwitchResults(value);
247 }
248
Ray Milkeya313cde2014-09-05 09:02:52 -0700249 /**
250 * Processes the response from a consumer.
251 *
252 * @param results List of switches modified by the consumer
253 */
Ray Milkey18b44ac2014-08-22 08:29:47 -0700254 private void updateSwitchResults(SwitchResultList results) {
255 if (results == null || results.size() == 0) {
256 return;
257 }
258 MatchActionOperationsId matchSetId = results.get(0).getMatchActionOperationsId();
259
260 // apply updates from results list
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700261 Map<Dpid, SwitchResult> resultMap = pendingMatchActionOperations.get(matchSetId);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700262 for (SwitchResult result : results) {
263 SwitchResult resultToUpdate = resultMap.get(result.getSwitch());
264 if (resultToUpdate != null) {
265 resultToUpdate.setStatus(result.getStatus());
266 }
267 // else {
268 // TODO error!
269 // }
270 }
271
272 // check to see the overall outcome of the install operation
273 SwitchResult.Status setResult = SwitchResult.Status.SUCCESS;
274 for (SwitchResult result : resultMap.values()) {
275 if (result.getStatus().equals(SwitchResult.Status.FAILURE)) {
276 setResult = SwitchResult.Status.FAILURE;
277 // if any switch fails, we fail the installation
278 break;
279 } else if (!setResult.equals(SwitchResult.Status.FAILURE)
280 && result.getStatus().equals(SwitchResult.Status.UNKNOWN)) {
281 setResult = SwitchResult.Status.UNKNOWN;
282 }
283 }
284 switch (setResult) {
285 case SUCCESS:
286 // mark MatchActionOperations as INSTALLED
287 MatchActionOperations matchSet = matchSetMap.get(matchSetId);
288 matchSet.setState(MatchActionOperationsState.INSTALLED);
289 matchSetMap.replace(matchSetId, matchSet);
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700290 pendingMatchActionOperations.remove(matchSetId);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700291
292 // TODO update dependent sets as needed
293 break;
294 case FAILURE:
295 // mark MatchActionOperations as FAILED
296 matchSet = matchSetMap.get(matchSetId);
297 matchSet.setState(MatchActionOperationsState.FAILED);
298 matchSetMap.replace(matchSetId, matchSet);
299
300 // TODO instruct installers to install Undo set
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700301 // TODO the pendingMatchActionOperations state needs to be cleaned-up
Ray Milkey18b44ac2014-08-22 08:29:47 -0700302 break;
303 case UNKNOWN:
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700304 // FALLTHROUGH
Ray Milkey18b44ac2014-08-22 08:29:47 -0700305 default:
306 // noop, still waiting for results
307 // TODO: check to see if installers are dead after timeout
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700308 break;
Ray Milkey18b44ac2014-08-22 08:29:47 -0700309 }
310 }
311 }
312
Ray Milkeya313cde2014-09-05 09:02:52 -0700313 /**
314 * Worker thread that pushes MatchActionOperations to the switches via
315 * the FlowPusher.
316 */
317 private class InstallerWorker extends Thread {
318
319 /**
320 * Default constructor.
321 */
322 InstallerWorker() {
323 // nothing to initialize
324 }
Ray Milkey18b44ac2014-08-22 08:29:47 -0700325
326 // Note: we should consider using an alternative representation for
327 // apply sets
Ray Milkeya313cde2014-09-05 09:02:52 -0700328
329 /**
330 * Installs a set of MatchActionOperations using the Flow Pusher.
331 *
332 * @param matchSet set of MatchActions to install
333 */
334 private void install(MatchActionOperations matchSet) {
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700335 Set<Long> masterDpids = provider.getAllMasterSwitchDpids();
Ray Milkey18b44ac2014-08-22 08:29:47 -0700336
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700337 Set<MatchActionOperationEntry> installSet = new HashSet<>();
338 Set<Dpid> modifiedSwitches = new HashSet<>();
Ray Milkey18b44ac2014-08-22 08:29:47 -0700339
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700340 for (MatchActionOperationEntry matchActionOp : matchSet.getOperations()) {
341 MatchAction matchAction = matchActionOp.getTarget();
342 Dpid dpid = matchAction.getSwitchPort().getDpid();
343 if (masterDpids.contains(dpid.value())) {
344 // only install if we are the master
345 // TODO this optimization will introduce some nice race
346 // conditions on failure requiring mastership change
347 installSet.add(matchActionOp);
348 modifiedSwitches.add(dpid);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700349 }
Ray Milkey18b44ac2014-08-22 08:29:47 -0700350 }
351
352 // push flow entries to switches
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700353 pusher.pushMatchActions(installSet);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700354
355 // insert a barrier after each phase on each modifiedSwitch
356 // wait for confirmation messages before proceeding
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700357 List<Pair<Dpid, OFMessageFuture<OFBarrierReply>>> barriers = new ArrayList<>();
358 for (Dpid dpid : modifiedSwitches) {
359 barriers.add(Pair.of(dpid, pusher.barrierAsync(dpid)));
Ray Milkey18b44ac2014-08-22 08:29:47 -0700360 }
361 List<SwitchResult> switchResults = new ArrayList<>();
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700362 for (Pair<Dpid, OFMessageFuture<OFBarrierReply>> pair : barriers) {
363 Dpid dpid = pair.getLeft();
Ray Milkey18b44ac2014-08-22 08:29:47 -0700364 OFMessageFuture<OFBarrierReply> future = pair.getRight();
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700365 SwitchResult switchResult = new SwitchResult(matchSet.getOperationsId(),
366 dpid);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700367 try {
368 future.get();
369 switchResult.setStatus(SwitchResult.Status.SUCCESS);
370 } catch (InterruptedException | ExecutionException e) {
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700371 log.error("Barrier message not received for sw: {}", dpid);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700372 switchResult.setStatus(SwitchResult.Status.FAILURE);
373 }
374 switchResults.add(switchResult);
375 }
376
377 // send update message to coordinator
378 // TODO: we might want to use another ID here, i.e. GUID, to avoid
379 // overlap
380 final SwitchResultList switchResultList = new SwitchResultList();
381 switchResultList.addAll(switchResults);
382 installSetReplyChannel.addTransientEntry(matchSet.getOperationsId().toString(),
383 switchResultList);
384 }
385
Ray Milkey18b44ac2014-08-22 08:29:47 -0700386 @Override
387 public void run() {
Ray Milkeya313cde2014-09-05 09:02:52 -0700388 //noinspection InfiniteLoopStatement - for IntelliJ
Ray Milkey18b44ac2014-08-22 08:29:47 -0700389 while (true) {
390 // 1. Remove MatchActionOperations(s) from the Global Resolved Queue
391 try {
392 MatchActionOperations operations = installationWorkQueue.take();
393 install(operations);
394 } catch (InterruptedException e) {
395 log.warn("Error taking from installation queue: {}", e.getMessage());
396 }
397 }
398 }
399 }
400
Ray Milkeya313cde2014-09-05 09:02:52 -0700401 /**
402 * Consumer class for MatchActionOperations. Listens on the MatchAction
403 * channel and places inbound requests on a queue to be handled by the
404 * InstallerWorker threads.
405 */
Ray Milkey18b44ac2014-08-22 08:29:47 -0700406 class Installer
407 implements IEventChannelListener<String, MatchActionOperations> {
408
Ray Milkeya313cde2014-09-05 09:02:52 -0700409 /**
410 * Starts the Installer consumer. Adds a listener on the MatchActionOperations
411 * channel.
412 */
413 private void start() {
Ray Milkey18b44ac2014-08-22 08:29:47 -0700414 installSetChannel.addListener(this);
415 }
416
417
418 @Override
419 public void entryAdded(MatchActionOperations value) {
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700420 try {
421 installationWorkQueue.put(value);
422 } catch (InterruptedException e) {
423 log.warn("Error adding to installer work queue: {}",
424 e.getMessage());
425 }
Ray Milkey18b44ac2014-08-22 08:29:47 -0700426 }
427
428 @Override
429 public void entryRemoved(MatchActionOperations value) {
430 // noop
431 }
432
433 @Override
434 public void entryUpdated(MatchActionOperations value) {
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700435 try {
436 installationWorkQueue.put(value);
437 } catch (InterruptedException e) {
438 log.warn("Error adding to installer work queue: {}",
439 e.getMessage());
440 }
Ray Milkey18b44ac2014-08-22 08:29:47 -0700441 }
442 }
443
Ray Milkey18b44ac2014-08-22 08:29:47 -0700444 @Override
445 public boolean addMatchAction(MatchAction matchAction) {
446 return false;
447 }
448
449 @Override
450 public Set<MatchAction> getMatchActions() {
Ray Milkey80dc8c92014-09-03 13:49:46 -0700451 return new HashSet<>(matchActionMap.values());
Ray Milkey18b44ac2014-08-22 08:29:47 -0700452 }
453
454 @Override
455 public boolean executeOperations(final MatchActionOperations operations) {
456 installMatchActionOperations(operations);
Ray Milkey80dc8c92014-09-03 13:49:46 -0700457 // TODO how to generate an actual error response here
458 return true;
Ray Milkey18b44ac2014-08-22 08:29:47 -0700459 }
460
461 @Override
462 public void setConflictDetectionPolicy(ConflictDetectionPolicy policy) {
463 // TODO Auto-generated method stub
464
465 }
466
467 @Override
468 public ConflictDetectionPolicy getConflictDetectionPolicy() {
469 // TODO Auto-generated method stub
470 return null;
471 }
472
473 @Override
474 public void addEventListener(EventListener listener) {
475 // TODO Auto-generated method stub
476
477 }
478
479 @Override
480 public void removeEventListener(EventListener listener) {
481 // TODO Auto-generated method stub
482
483 }
484
485 @Override
486 public IdGenerator<MatchActionId> getMatchActionIdGenerator() {
Ray Milkey626a2b12014-09-02 11:19:06 -0700487 return matchActionIdGenerator;
Ray Milkey18b44ac2014-08-22 08:29:47 -0700488 }
489
490 @Override
491 public IdGenerator<MatchActionOperationsId> getMatchActionOperationsIdGenerator() {
Ray Milkey626a2b12014-09-02 11:19:06 -0700492 return matchActionOperationsIdGenerator;
Ray Milkey18b44ac2014-08-22 08:29:47 -0700493 }
494
495}