blob: da24d4d586292ca71de8ad8a90a4a261716c2ba7 [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);
43 IFlowPusherService pusher;
44 IFloodlightProviderService provider;
45
46 private ConcurrentMap<MatchActionId, MatchAction> matchActionMap = new ConcurrentHashMap<>();
47 private ConcurrentMap<MatchActionOperationsId, MatchActionOperations> matchSetMap =
48 new ConcurrentHashMap<>();
49 // TODO - want something better here for the resolved Queue
50 private BlockingQueue<MatchActionOperationsId> resolvedQueue = new ArrayBlockingQueue<>(100);
51 private BlockingQueue<MatchActionOperations> installationWorkQueue = new ArrayBlockingQueue<>(100);
52
53 private IEventChannel<String, MatchActionOperations> installSetChannel;
54 private IEventChannel<String, SwitchResultList> installSetReplyChannel;
55
Ray Milkey18b44ac2014-08-22 08:29:47 -070056 // TODO Single instance for now, should be a work queue of some sort eventually
57 private Thread coordinator;
58 private Thread installer;
Brian O'Connor7e2d9142014-09-02 17:24:27 -070059 private Installer installerListener;
Ray Milkey18b44ac2014-08-22 08:29:47 -070060 private final IDatagridService datagrid;
Ray Milkey626a2b12014-09-02 11:19:06 -070061 private IControllerRegistryService registryService;
62
63 private MatchActionIdGeneratorWithIdBlockAllocator matchActionIdGenerator;
64 private MatchActionOperationsIdGeneratorWithIdBlockAllocator matchActionOperationsIdGenerator;
Ray Milkey18b44ac2014-08-22 08:29:47 -070065
66 public MatchActionComponent(final IDatagridService newDatagrid,
67 final IFlowPusherService newPusher,
Ray Milkey626a2b12014-09-02 11:19:06 -070068 final IFloodlightProviderService newProvider,
69 final IControllerRegistryService newRegistryService) {
Ray Milkey18b44ac2014-08-22 08:29:47 -070070 datagrid = newDatagrid;
71 pusher = newPusher;
72 provider = newProvider;
Ray Milkey626a2b12014-09-02 11:19:06 -070073 registryService = newRegistryService;
Ray Milkey18b44ac2014-08-22 08:29:47 -070074 }
75
76 public void start() {
Ray Milkey626a2b12014-09-02 11:19:06 -070077 IdBlockAllocator idBlockAllocator = registryService;
78 matchActionIdGenerator =
79 new MatchActionIdGeneratorWithIdBlockAllocator(idBlockAllocator);
80 matchActionOperationsIdGenerator =
81 new MatchActionOperationsIdGeneratorWithIdBlockAllocator(idBlockAllocator);
82
Ray Milkey18b44ac2014-08-22 08:29:47 -070083 installSetChannel = datagrid.createChannel("onos.matchaction.installSetChannel",
84 String.class,
85 MatchActionOperations.class);
86
87 installSetReplyChannel = datagrid.createChannel("onos.matchaction.installSetReplyChannel",
88 String.class,
89 SwitchResultList.class);
90
91 coordinator = new Coordinator();
92 coordinator.start();
93
94 installer = new InstallerWorker();
95 installer.start();
Brian O'Connor7e2d9142014-09-02 17:24:27 -070096
97 installerListener = new Installer();
98 installerListener.start();
Ray Milkey18b44ac2014-08-22 08:29:47 -070099 }
100
101 public MatchActionOperationsId installMatchActionOperations(MatchActionOperations matchSet) {
102 if (checkResolved(matchSet)) {
103 matchSet.setState(MatchActionOperationsState.RESOLVED);
104 } else {
105 matchSet.setState(MatchActionOperationsState.INIT);
106 }
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700107 log.trace("MatchActionsOperations set added: {} {} {}",
108 matchSet.getOperationsId(),
109 matchSet.getState(),
110 matchSet.toString());
Ray Milkey18b44ac2014-08-22 08:29:47 -0700111 matchSetMap.put(matchSet.getOperationsId(), matchSet);
112 if (matchSet.getState() == MatchActionOperationsState.RESOLVED) {
113 resolvedQueue.add(matchSet.getOperationsId());
114 }
115 return matchSet.getOperationsId();
116 }
117
118 public MatchActionOperationsState getMatchActionOperationsState(MatchActionOperationsId matchSetId) {
119 MatchActionOperations set = matchSetMap.get(matchSetId);
120 return (set == null) ? null : set.getState();
121 }
122
123 protected boolean checkResolved(MatchActionOperations matchSet) {
124 boolean resolved = true;
125 for (MatchActionOperationsId setId : matchSet.getDependencies()) {
126 MatchActionOperations set = matchSetMap.get(setId);
127 if (set == null || set.getState() != MatchActionOperationsState.RESOLVED) {
128 resolved = false;
129 break;
130 }
131 }
132 return resolved;
133 }
134
Ray Milkey18b44ac2014-08-22 08:29:47 -0700135 class Coordinator extends Thread
136 implements IEventChannelListener<String, SwitchResultList> {
137
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700138 private Map<MatchActionOperationsId, Map<Dpid, SwitchResult>> pendingMatchActionOperations = new HashMap<>();
Ray Milkey18b44ac2014-08-22 08:29:47 -0700139
140 protected Coordinator() {
141 installSetReplyChannel.addListener(this);
142 }
143
144 @Override
145 public void run() {
146 while (true) {
147 // 1. Remove MatchActionOperations(s) from the Global Resolved Queue
148 try {
149 MatchActionOperationsId setId = resolvedQueue.take();
150 processSet(setId);
151 } catch (InterruptedException e) {
152 log.warn("Error taking from resolved queue: {}", e.getMessage());
153 }
154 }
155 }
156
157 private void processSet(MatchActionOperationsId setId) {
158 MatchActionOperations matchSet = matchSetMap.get(setId);
159 matchSet.setState(MatchActionOperationsState.PENDING);
160 matchSetMap.put(setId, matchSet);
161
162 // TODO apply updates to in-memory flow table and resolve conflicts
163 // TODO generate apply and undo sets, using MatchActionOperations for now...
164
165 // build pending switches set for coordinator tracking
166 Map<Dpid, SwitchResult> switches = new HashMap<>();
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700167 for (MatchActionOperationEntry matchActionOp : matchSet.getOperations()) {
168 MatchAction matchAction = matchActionOp.getTarget();
169 SwitchPort sw = matchAction.getSwitchPort();
Ray Milkey18b44ac2014-08-22 08:29:47 -0700170 switches.put(sw.getDpid(), new SwitchResult(setId, sw.getDpid()));
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700171 switch(matchActionOp.getOperator()) {
172 case ADD:
173 matchActionMap.put(matchAction.getId(), matchAction);
174 break;
175 case REMOVE:
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700176 // TODO we may want to be more careful when removing MatchActions
177 matchActionMap.remove(matchAction.getId());
178 break;
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700179 default:
180 throw new UnsupportedOperationException(
181 "Unsupported MatchAction operation" +
182 matchActionOp.getOperator().toString());
183 }
Ray Milkey18b44ac2014-08-22 08:29:47 -0700184 }
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700185 pendingMatchActionOperations.put(setId, switches);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700186
187 // distribute apply/undo sets to cluster
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700188 installSetChannel.addTransientEntry(setId.toString(), matchSet);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700189 }
190
191 @Override
192 public void entryAdded(SwitchResultList value) {
193 updateSwitchResults(value);
194 }
195
196 @Override
197 public void entryRemoved(SwitchResultList value) {
198 // noop
199 }
200
201 @Override
202 public void entryUpdated(SwitchResultList value) {
203 updateSwitchResults(value);
204 }
205
206 private void updateSwitchResults(SwitchResultList results) {
207 if (results == null || results.size() == 0) {
208 return;
209 }
210 MatchActionOperationsId matchSetId = results.get(0).getMatchActionOperationsId();
211
212 // apply updates from results list
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700213 Map<Dpid, SwitchResult> resultMap = pendingMatchActionOperations.get(matchSetId);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700214 for (SwitchResult result : results) {
215 SwitchResult resultToUpdate = resultMap.get(result.getSwitch());
216 if (resultToUpdate != null) {
217 resultToUpdate.setStatus(result.getStatus());
218 }
219 // else {
220 // TODO error!
221 // }
222 }
223
224 // check to see the overall outcome of the install operation
225 SwitchResult.Status setResult = SwitchResult.Status.SUCCESS;
226 for (SwitchResult result : resultMap.values()) {
227 if (result.getStatus().equals(SwitchResult.Status.FAILURE)) {
228 setResult = SwitchResult.Status.FAILURE;
229 // if any switch fails, we fail the installation
230 break;
231 } else if (!setResult.equals(SwitchResult.Status.FAILURE)
232 && result.getStatus().equals(SwitchResult.Status.UNKNOWN)) {
233 setResult = SwitchResult.Status.UNKNOWN;
234 }
235 }
236 switch (setResult) {
237 case SUCCESS:
238 // mark MatchActionOperations as INSTALLED
239 MatchActionOperations matchSet = matchSetMap.get(matchSetId);
240 matchSet.setState(MatchActionOperationsState.INSTALLED);
241 matchSetMap.replace(matchSetId, matchSet);
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700242 pendingMatchActionOperations.remove(matchSetId);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700243
244 // TODO update dependent sets as needed
245 break;
246 case FAILURE:
247 // mark MatchActionOperations as FAILED
248 matchSet = matchSetMap.get(matchSetId);
249 matchSet.setState(MatchActionOperationsState.FAILED);
250 matchSetMap.replace(matchSetId, matchSet);
251
252 // TODO instruct installers to install Undo set
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700253 // TODO the pendingMatchActionOperations state needs to be cleaned-up
Ray Milkey18b44ac2014-08-22 08:29:47 -0700254 break;
255 case UNKNOWN:
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700256 // FALLTHROUGH
Ray Milkey18b44ac2014-08-22 08:29:47 -0700257 default:
258 // noop, still waiting for results
259 // TODO: check to see if installers are dead after timeout
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700260 break;
Ray Milkey18b44ac2014-08-22 08:29:47 -0700261 }
262 }
263 }
264
Ray Milkey18b44ac2014-08-22 08:29:47 -0700265 class InstallerWorker extends Thread {
266
267 // Note: we should consider using an alternative representation for
268 // apply sets
269 protected void install(MatchActionOperations matchSet) {
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700270 Set<Long> masterDpids = provider.getAllMasterSwitchDpids();
Ray Milkey18b44ac2014-08-22 08:29:47 -0700271
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700272 Set<MatchActionOperationEntry> installSet = new HashSet<>();
273 Set<Dpid> modifiedSwitches = new HashSet<>();
Ray Milkey18b44ac2014-08-22 08:29:47 -0700274
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700275 for (MatchActionOperationEntry matchActionOp : matchSet.getOperations()) {
276 MatchAction matchAction = matchActionOp.getTarget();
277 Dpid dpid = matchAction.getSwitchPort().getDpid();
278 if (masterDpids.contains(dpid.value())) {
279 // only install if we are the master
280 // TODO this optimization will introduce some nice race
281 // conditions on failure requiring mastership change
282 installSet.add(matchActionOp);
283 modifiedSwitches.add(dpid);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700284 }
Ray Milkey18b44ac2014-08-22 08:29:47 -0700285 }
286
287 // push flow entries to switches
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700288 pusher.pushMatchActions(installSet);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700289
290 // insert a barrier after each phase on each modifiedSwitch
291 // wait for confirmation messages before proceeding
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700292 List<Pair<Dpid, OFMessageFuture<OFBarrierReply>>> barriers = new ArrayList<>();
293 for (Dpid dpid : modifiedSwitches) {
294 barriers.add(Pair.of(dpid, pusher.barrierAsync(dpid)));
Ray Milkey18b44ac2014-08-22 08:29:47 -0700295 }
296 List<SwitchResult> switchResults = new ArrayList<>();
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700297 for (Pair<Dpid, OFMessageFuture<OFBarrierReply>> pair : barriers) {
298 Dpid dpid = pair.getLeft();
Ray Milkey18b44ac2014-08-22 08:29:47 -0700299 OFMessageFuture<OFBarrierReply> future = pair.getRight();
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700300 SwitchResult switchResult = new SwitchResult(matchSet.getOperationsId(),
301 dpid);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700302 try {
303 future.get();
304 switchResult.setStatus(SwitchResult.Status.SUCCESS);
305 } catch (InterruptedException | ExecutionException e) {
Brian O'Connoraa5a7b92014-08-29 14:45:18 -0700306 log.error("Barrier message not received for sw: {}", dpid);
Ray Milkey18b44ac2014-08-22 08:29:47 -0700307 switchResult.setStatus(SwitchResult.Status.FAILURE);
308 }
309 switchResults.add(switchResult);
310 }
311
312 // send update message to coordinator
313 // TODO: we might want to use another ID here, i.e. GUID, to avoid
314 // overlap
315 final SwitchResultList switchResultList = new SwitchResultList();
316 switchResultList.addAll(switchResults);
317 installSetReplyChannel.addTransientEntry(matchSet.getOperationsId().toString(),
318 switchResultList);
319 }
320
Ray Milkey18b44ac2014-08-22 08:29:47 -0700321 @Override
322 public void run() {
323 while (true) {
324 // 1. Remove MatchActionOperations(s) from the Global Resolved Queue
325 try {
326 MatchActionOperations operations = installationWorkQueue.take();
327 install(operations);
328 } catch (InterruptedException e) {
329 log.warn("Error taking from installation queue: {}", e.getMessage());
330 }
331 }
332 }
333 }
334
335 class Installer
336 implements IEventChannelListener<String, MatchActionOperations> {
337
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700338 protected void start() {
Ray Milkey18b44ac2014-08-22 08:29:47 -0700339 installSetChannel.addListener(this);
340 }
341
342
343 @Override
344 public void entryAdded(MatchActionOperations value) {
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700345 try {
346 installationWorkQueue.put(value);
347 } catch (InterruptedException e) {
348 log.warn("Error adding to installer work queue: {}",
349 e.getMessage());
350 }
Ray Milkey18b44ac2014-08-22 08:29:47 -0700351 }
352
353 @Override
354 public void entryRemoved(MatchActionOperations value) {
355 // noop
356 }
357
358 @Override
359 public void entryUpdated(MatchActionOperations value) {
Brian O'Connor7e2d9142014-09-02 17:24:27 -0700360 try {
361 installationWorkQueue.put(value);
362 } catch (InterruptedException e) {
363 log.warn("Error adding to installer work queue: {}",
364 e.getMessage());
365 }
Ray Milkey18b44ac2014-08-22 08:29:47 -0700366 }
367 }
368
Ray Milkey18b44ac2014-08-22 08:29:47 -0700369 @Override
370 public boolean addMatchAction(MatchAction matchAction) {
371 return false;
372 }
373
374 @Override
375 public Set<MatchAction> getMatchActions() {
Ray Milkey80dc8c92014-09-03 13:49:46 -0700376 return new HashSet<>(matchActionMap.values());
Ray Milkey18b44ac2014-08-22 08:29:47 -0700377 }
378
379 @Override
380 public boolean executeOperations(final MatchActionOperations operations) {
381 installMatchActionOperations(operations);
Ray Milkey80dc8c92014-09-03 13:49:46 -0700382 // TODO how to generate an actual error response here
383 return true;
Ray Milkey18b44ac2014-08-22 08:29:47 -0700384 }
385
386 @Override
387 public void setConflictDetectionPolicy(ConflictDetectionPolicy policy) {
388 // TODO Auto-generated method stub
389
390 }
391
392 @Override
393 public ConflictDetectionPolicy getConflictDetectionPolicy() {
394 // TODO Auto-generated method stub
395 return null;
396 }
397
398 @Override
399 public void addEventListener(EventListener listener) {
400 // TODO Auto-generated method stub
401
402 }
403
404 @Override
405 public void removeEventListener(EventListener listener) {
406 // TODO Auto-generated method stub
407
408 }
409
410 @Override
411 public IdGenerator<MatchActionId> getMatchActionIdGenerator() {
Ray Milkey626a2b12014-09-02 11:19:06 -0700412 return matchActionIdGenerator;
Ray Milkey18b44ac2014-08-22 08:29:47 -0700413 }
414
415 @Override
416 public IdGenerator<MatchActionOperationsId> getMatchActionOperationsIdGenerator() {
Ray Milkey626a2b12014-09-02 11:19:06 -0700417 return matchActionOperationsIdGenerator;
Ray Milkey18b44ac2014-08-22 08:29:47 -0700418 }
419
420}