blob: 058b2d027f992e152d9e0f2763c2858a8f7c2520 [file] [log] [blame]
Jonathan Hartaa380972014-04-03 10:24:46 -07001package net.onrc.onos.core.intent.runtime;
Brian O'Connor12861f72014-02-19 20:40:32 -08002
3import java.util.ArrayList;
4import java.util.Collection;
5import java.util.List;
6import java.util.Map;
7import java.util.Set;
8import java.util.concurrent.BlockingQueue;
TeruU9e530662014-05-18 11:49:37 -07009import java.util.concurrent.ConcurrentHashMap;
10import java.util.concurrent.ConcurrentMap;
Brian O'Connor12861f72014-02-19 20:40:32 -080011import java.util.concurrent.LinkedBlockingQueue;
12
TeruU9e530662014-05-18 11:49:37 -070013import net.floodlightcontroller.core.FloodlightContext;
Brian O'Connor12861f72014-02-19 20:40:32 -080014import net.floodlightcontroller.core.IFloodlightProviderService;
TeruU9e530662014-05-18 11:49:37 -070015import net.floodlightcontroller.core.IOFMessageListener;
16import net.floodlightcontroller.core.IOFSwitch;
Brian O'Connor12861f72014-02-19 20:40:32 -080017import net.floodlightcontroller.core.module.FloodlightModuleContext;
18import net.floodlightcontroller.core.module.FloodlightModuleException;
19import net.floodlightcontroller.core.module.IFloodlightModule;
20import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hart6df90172014-04-03 10:13:11 -070021import net.onrc.onos.core.datagrid.IDatagridService;
22import net.onrc.onos.core.datagrid.IEventChannel;
23import net.onrc.onos.core.datagrid.IEventChannelListener;
Jonathan Hart23701d12014-04-03 10:45:48 -070024import net.onrc.onos.core.flowprogrammer.IFlowPusherService;
Jonathan Hartaa380972014-04-03 10:24:46 -070025import net.onrc.onos.core.intent.FlowEntry;
TeruU9e530662014-05-18 11:49:37 -070026import net.onrc.onos.core.intent.Intent;
Jonathan Harta99ec672014-04-03 11:30:34 -070027import net.onrc.onos.core.intent.Intent.IntentState;
Jonathan Hartaa380972014-04-03 10:24:46 -070028import net.onrc.onos.core.intent.IntentOperation;
29import net.onrc.onos.core.intent.IntentOperationList;
TeruU9e530662014-05-18 11:49:37 -070030import net.onrc.onos.core.intent.PathIntent;
31import net.onrc.onos.core.intent.ShortestPathIntent;
Jonathan Harte37e4e22014-05-13 19:12:02 -070032import net.onrc.onos.core.topology.ITopologyService;
Jonathan Hart472062d2014-04-03 10:56:48 -070033
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070034import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
35import org.projectfloodlight.openflow.protocol.OFMessage;
36import org.projectfloodlight.openflow.protocol.OFType;
Brian O'Connor5e73c012014-02-20 14:54:34 -080037import org.slf4j.Logger;
38import org.slf4j.LoggerFactory;
39
Brian O'Connorc89d2862014-06-13 00:51:04 -070040/**
41 * The PlanInstallModule contains the PlanCalcRuntime and PlanInstallRuntime.
42 * <p>
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070043 * It is responsible for converting Intents into FlowMods and seeing that they
44 * are properly installed.
Brian O'Connorc89d2862014-06-13 00:51:04 -070045 */
TeruU9e530662014-05-18 11:49:37 -070046public class PlanInstallModule implements IFloodlightModule, IOFMessageListener {
Brian O'Connorc89d2862014-06-13 00:51:04 -070047
Brian O'Connor12861f72014-02-19 20:40:32 -080048 protected volatile IFloodlightProviderService floodlightProvider;
Jonathan Harte37e4e22014-05-13 19:12:02 -070049 protected volatile ITopologyService topologyService;
Brian O'Connor12861f72014-02-19 20:40:32 -080050 protected volatile IDatagridService datagridService;
51 protected volatile IFlowPusherService flowPusher;
52 private PlanCalcRuntime planCalc;
53 private PlanInstallRuntime planInstall;
54 private EventListener eventListener;
Brian O'Connor488e5ed2014-02-20 19:50:01 -080055 private IEventChannel<Long, IntentStateList> intentStateChannel;
Ray Milkeyec838942014-04-09 11:28:43 -070056 private static final Logger log = LoggerFactory.getLogger(PlanInstallModule.class);
Brian O'Connor9b712f62014-02-20 14:22:20 -080057
Brian O'Connor12861f72014-02-19 20:40:32 -080058 private static final String PATH_INTENT_CHANNEL_NAME = "onos.pathintent";
Brian O'Connor488e5ed2014-02-20 19:50:01 -080059 private static final String INTENT_STATE_EVENT_CHANNEL_NAME = "onos.pathintent_state";
TeruU9e530662014-05-18 11:49:37 -070060 private ConcurrentMap<String, Intent> parentIntentMap = new ConcurrentHashMap<String, Intent>();
Brian O'Connor12861f72014-02-19 20:40:32 -080061
Brian O'Connorc89d2862014-06-13 00:51:04 -070062 /**
63 * EventListener for Intent updates from the PathCalcRuntime module.
64 */
Brian O'Connor12861f72014-02-19 20:40:32 -080065 class EventListener extends Thread
Ray Milkey269ffb92014-04-03 14:43:30 -070066 implements IEventChannelListener<Long, IntentOperationList> {
Toshio Koide3a52ef32014-02-28 12:10:26 -080067
Ray Milkey269ffb92014-04-03 14:43:30 -070068 private BlockingQueue<IntentOperationList> intentQueue = new LinkedBlockingQueue<>();
69 private Long key = Long.valueOf(0);
Toshio Koide3a52ef32014-02-28 12:10:26 -080070
Brian O'Connorc89d2862014-06-13 00:51:04 -070071 /**
72 * Poll the Intent queue for events.
73 */
Ray Milkey269ffb92014-04-03 14:43:30 -070074 @Override
75 public void run() {
76 while (true) {
77 try {
78 IntentOperationList intents = intentQueue.take();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070079 // TODO: consider draining the remaining intent lists
80 // and processing in one big batch
Toshio Koide3a52ef32014-02-28 12:10:26 -080081
Ray Milkey269ffb92014-04-03 14:43:30 -070082 processIntents(intents);
83 } catch (InterruptedException e) {
84 log.warn("Error taking from intent queue: {}", e.getMessage());
85 }
86 }
87 }
Toshio Koide3a52ef32014-02-28 12:10:26 -080088
Brian O'Connorc89d2862014-06-13 00:51:04 -070089 /**
90 * Process events received from the event queue.
91 * <p>
92 * First, compute a plan, and then install it.
93 *
94 * @param intents list of new Intent events
95 */
Ray Milkey269ffb92014-04-03 14:43:30 -070096 private void processIntents(IntentOperationList intents) {
97 log("start_processIntents");
98 log.debug("Processing OperationList {}", intents);
99 log("begin_computePlan");
100 List<Set<FlowEntry>> plan = planCalc.computePlan(intents);
101 log("end_computePlan");
102 log.debug("Plan: {}", plan);
103 log("begin_installPlan");
104 boolean success = planInstall.installPlan(plan);
105 log("end_installPlan");
TeruUf9111652014-05-14 23:10:35 -0700106 Set<Long> domainSwitchDpids = floodlightProvider.getSwitches().keySet();
Ray Milkey269ffb92014-04-03 14:43:30 -0700107 log("begin_sendInstallNotif");
TeruUf9111652014-05-14 23:10:35 -0700108 sendNotifications(intents, true, success, domainSwitchDpids);
Ray Milkey269ffb92014-04-03 14:43:30 -0700109 log("end_sendInstallNotif");
110 log("finish");
111 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800112
TeruUf9111652014-05-14 23:10:35 -0700113 /***
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700114 * This function is for sending intent state notification to other ONOS
115 * instances. The argument of "domainSwitchDpids" is required for
116 * dispatching this ONOS's managed switches.
Brian O'Connorc89d2862014-06-13 00:51:04 -0700117 *
118 * @param intents list of intents
119 * @param installed true if Intents were installed
120 * @param success true if updates succeeded
121 * @param domainSwitchDpids set of affected switches
TeruUf9111652014-05-14 23:10:35 -0700122 */
Jonathan Hartc00f5c22014-06-10 15:14:40 -0700123 private void sendNotifications(IntentOperationList intents,
124 boolean installed, boolean success, Set<Long> domainSwitchDpids) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700125 IntentStateList states = new IntentStateList();
126 for (IntentOperation i : intents) {
127 IntentState newState;
128 switch (i.operator) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700129 case REMOVE:
130 if (installed) {
131 newState = success ? IntentState.DEL_ACK
132 : IntentState.DEL_PENDING;
133 } else {
134 newState = IntentState.DEL_REQ;
135 }
136 break;
137 case ADD:
138 default:
139 if (installed) {
140 if (domainSwitchDpids != null) {
141 states.domainSwitchDpids.addAll(domainSwitchDpids);
Ray Milkey269ffb92014-04-03 14:43:30 -0700142 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700143 newState = success ? IntentState.INST_ACK : IntentState.INST_NACK;
144 } else {
145 newState = IntentState.INST_REQ;
146 }
147 break;
Ray Milkey269ffb92014-04-03 14:43:30 -0700148 }
149 states.put(i.intent.getId(), newState);
150 }
TeruUf9111652014-05-14 23:10:35 -0700151
152 if (log.isTraceEnabled()) {
TeruU9e530662014-05-18 11:49:37 -0700153 log.trace("sendNotifications, states {}, domainSwitchDpids {}",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700154 states, states.domainSwitchDpids);
TeruUf9111652014-05-14 23:10:35 -0700155 }
156
Brian O'Connorc89d2862014-06-13 00:51:04 -0700157 // Send notifications using the same key every time
Ray Milkey269ffb92014-04-03 14:43:30 -0700158 // and receive them by entryAdded() and entryUpdated()
Brian O'Connorc89d2862014-06-13 00:51:04 -0700159 intentStateChannel.addTransientEntry(key, states);
Ray Milkey269ffb92014-04-03 14:43:30 -0700160 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800161
Brian O'Connorc89d2862014-06-13 00:51:04 -0700162 /**
163 * Uses the entryUpdated() method. This should only be called once
164 * because we always use the same key.
165 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700166 @Override
167 public void entryAdded(IntentOperationList value) {
168 entryUpdated(value);
169 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800170
Brian O'Connorc89d2862014-06-13 00:51:04 -0700171 /*
172 * (non-Javadoc)
173 * @see net.onrc.onos.core.datagrid.IEventChannelListener#entryRemoved(java.lang.Object)
174 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700175 @Override
176 public void entryRemoved(IntentOperationList value) {
177 // This channel is a queue, so this method is not needed
178 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800179
Brian O'Connorc89d2862014-06-13 00:51:04 -0700180 /**
181 * Adds the new intents to the event queue.
182 *
Sho SHIMIZU69474452014-06-20 09:17:40 -0700183 * @param value list of new intents
Brian O'Connorc89d2862014-06-13 00:51:04 -0700184 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700185 @Override
186 public void entryUpdated(IntentOperationList value) {
TeruU9e530662014-05-18 11:49:37 -0700187 putIntentOpsInfoInParentMap(value);
Ray Milkey269ffb92014-04-03 14:43:30 -0700188 log("start_intentNotifRecv");
189 log("begin_sendReceivedNotif");
TeruUf9111652014-05-14 23:10:35 -0700190 sendNotifications(value, false, false, null);
Ray Milkey269ffb92014-04-03 14:43:30 -0700191 log("end_sendReceivedNotif");
192 log("finish");
Brian O'Connor85dd8f22014-02-25 11:43:07 -0800193
Ray Milkey269ffb92014-04-03 14:43:30 -0700194 log.debug("Added OperationList {}", value);
195 try {
196 intentQueue.put(value);
197 } catch (InterruptedException e) {
198 log.warn("Error putting to intent queue: {}", e.getMessage());
199 }
200 }
TeruU9e530662014-05-18 11:49:37 -0700201
Brian O'Connorc89d2862014-06-13 00:51:04 -0700202 /**
203 * Add intent information to parent map.
204 *
205 * @param intentOps list of Intents and new states
206 */
TeruU9e530662014-05-18 11:49:37 -0700207 private void putIntentOpsInfoInParentMap(IntentOperationList intentOps) {
208 for (IntentOperation i : intentOps) {
209 if (!(i.intent instanceof PathIntent)) {
210 log.warn("Not a path intent: {}", i);
211 continue;
212 }
213 PathIntent intent = (PathIntent) i.intent;
214 Intent parent = intent.getParentIntent();
215 if (parent instanceof ShortestPathIntent) {
216 parentIntentMap.put(parent.getId(), parent);
217 } else {
218 log.warn("Unsupported Intent: {}", parent);
219 continue;
220 }
221 }
222 }
Brian O'Connor12861f72014-02-19 20:40:32 -0800223 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800224
Brian O'Connorc89d2862014-06-13 00:51:04 -0700225 /**
226 * Formatted log for debugging.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700227 * <p>
Brian O'Connorc89d2862014-06-13 00:51:04 -0700228 * TODO: merge this into debugging framework
229 *
230 * @param step the step of computation
231 */
Brian O'Connor85dd8f22014-02-25 11:43:07 -0800232 public static void log(String step) {
Pavlin Radoslavov964f8ae2014-04-18 16:44:14 -0700233 log.debug("Time:{}, Step:{}", System.nanoTime(), step);
Brian O'Connor85dd8f22014-02-25 11:43:07 -0800234 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800235
Brian O'Connorc89d2862014-06-13 00:51:04 -0700236 /*
237 * (non-Javadoc)
238 * @see net.floodlightcontroller.core.module.IFloodlightModule#init
239 * (net.floodlightcontroller.core.module.FloodlightModuleContext)
240 */
Brian O'Connor12861f72014-02-19 20:40:32 -0800241 @Override
TeruU9e530662014-05-18 11:49:37 -0700242 public void init(FloodlightModuleContext context)
243 throws FloodlightModuleException {
244 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
245 topologyService = context.getServiceImpl(ITopologyService.class);
246 datagridService = context.getServiceImpl(IDatagridService.class);
247 flowPusher = context.getServiceImpl(IFlowPusherService.class);
248 planCalc = new PlanCalcRuntime();
249 planInstall = new PlanInstallRuntime(floodlightProvider, flowPusher);
250 eventListener = new EventListener();
251 }
252
Brian O'Connorc89d2862014-06-13 00:51:04 -0700253 /*
254 * (non-Javadoc)
255 * @see net.floodlightcontroller.core.module.IFloodlightModule#startUp
256 * (net.floodlightcontroller.core.module.FloodlightModuleContext)
257 */
TeruU9e530662014-05-18 11:49:37 -0700258 @Override
Brian O'Connor12861f72014-02-19 20:40:32 -0800259 public void startUp(FloodlightModuleContext context) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700260 // start subscriber
261 datagridService.addListener(PATH_INTENT_CHANNEL_NAME,
262 eventListener,
263 Long.class,
264 IntentOperationList.class);
265 eventListener.start();
266 // start publisher
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700267 intentStateChannel = datagridService.createChannel(
268 INTENT_STATE_EVENT_CHANNEL_NAME,
Ray Milkey269ffb92014-04-03 14:43:30 -0700269 Long.class,
270 IntentStateList.class);
TeruU9e530662014-05-18 11:49:37 -0700271 floodlightProvider.addOFMessageListener(OFType.FLOW_REMOVED, this);
Brian O'Connor12861f72014-02-19 20:40:32 -0800272 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800273
Brian O'Connorc89d2862014-06-13 00:51:04 -0700274 /*
275 * (non-Javadoc)
276 * @see net.floodlightcontroller.core.module.IFloodlightModule#getModuleDependencies()
277 */
Brian O'Connor12861f72014-02-19 20:40:32 -0800278 @Override
279 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700280 Collection<Class<? extends IFloodlightService>> l =
281 new ArrayList<Class<? extends IFloodlightService>>();
282 l.add(IFloodlightProviderService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700283 l.add(ITopologyService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700284 l.add(IDatagridService.class);
285 l.add(IFlowPusherService.class);
286 return l;
Brian O'Connor12861f72014-02-19 20:40:32 -0800287 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800288
Brian O'Connorc89d2862014-06-13 00:51:04 -0700289 /*
290 * (non-Javadoc)
291 * @see net.floodlightcontroller.core.module.IFloodlightModule#getModuleServices()
292 */
Brian O'Connor12861f72014-02-19 20:40:32 -0800293 @Override
294 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700295 // no services, for now
296 return null;
Brian O'Connor12861f72014-02-19 20:40:32 -0800297 }
298
Brian O'Connorc89d2862014-06-13 00:51:04 -0700299 /*
300 * (non-Javadoc)
301 * @see net.floodlightcontroller.core.module.IFloodlightModule#getServiceImpls()
302 */
Brian O'Connor12861f72014-02-19 20:40:32 -0800303 @Override
304 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700305 // no services, for now
306 return null;
Brian O'Connor12861f72014-02-19 20:40:32 -0800307 }
TeruU9e530662014-05-18 11:49:37 -0700308
Brian O'Connorc89d2862014-06-13 00:51:04 -0700309 /**
310 * Handles FlowRemoved messages from the switches.
311 */
TeruU9e530662014-05-18 11:49:37 -0700312 @Override
313 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
314 if (msg.getType().equals(OFType.FLOW_REMOVED) &&
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700315 (msg instanceof OFFlowRemoved)) {
TeruU9e530662014-05-18 11:49:37 -0700316 OFFlowRemoved flowRemovedMsg = (OFFlowRemoved) msg;
317
318 if (log.isTraceEnabled()) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700319 log.trace("Receive flowRemoved from sw {} : Cookie {}",
320 sw.getId(), flowRemovedMsg.getCookie());
TeruU9e530662014-05-18 11:49:37 -0700321 }
322
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700323 String intentParentId = Long.toString(flowRemovedMsg.getCookie().getValue());
TeruU9e530662014-05-18 11:49:37 -0700324 Intent intent = parentIntentMap.get(intentParentId);
325
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700326 // We assume if the path src sw flow entry is expired,
327 // the path is expired.
TeruU9e530662014-05-18 11:49:37 -0700328 if (!isFlowSrcRemoved(sw.getId(), intentParentId)) {
329 return Command.CONTINUE;
330 }
331
332 ShortestPathIntent spfIntent = null;
333 if (!(intent instanceof ShortestPathIntent)) {
334 return Command.CONTINUE;
335 }
336 spfIntent = (ShortestPathIntent) intent;
337 String pathIntentId = spfIntent.getPathIntentId();
338
339 IntentStateList states = new IntentStateList();
340 IntentState newState = IntentState.DEL_ACK;
341 states.put(pathIntentId, newState);
342 Set<Long> domainSwitchDpids = floodlightProvider.getSwitches().keySet();
343 if (domainSwitchDpids != null) {
344 states.domainSwitchDpids.addAll(domainSwitchDpids);
345 }
346 parentIntentMap.remove(intentParentId);
TeruU5d2c9392014-06-09 20:02:02 -0700347 log.debug("addEntry to intentStateChannel intentId {}, states {}",
348 pathIntentId, newState);
TeruU9e530662014-05-18 11:49:37 -0700349
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700350 intentStateChannel.addTransientEntry(flowRemovedMsg.getCookie().getValue(),
351 states);
TeruU9e530662014-05-18 11:49:37 -0700352 }
353
354 return Command.CONTINUE;
355 }
356
Brian O'Connorc89d2862014-06-13 00:51:04 -0700357 /**
358 * Check to see if the flow's source switch entry is removed/expired.
359 *
360 * @param dpid DPID of affected switch
361 * @param shortestPathIntentId Intent to check
Jonathan Hart99ff20a2014-06-15 16:53:00 -0700362 * @return true if the flow's source switch entry is removed or expired,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700363 * otherwise false.
Brian O'Connorc89d2862014-06-13 00:51:04 -0700364 */
TeruU9e530662014-05-18 11:49:37 -0700365 private boolean isFlowSrcRemoved(long dpid, String shortestPathIntentId) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700366 Intent intent = parentIntentMap.get(shortestPathIntentId);
TeruU9e530662014-05-18 11:49:37 -0700367 ShortestPathIntent spfIntent = null;
368 if (intent instanceof ShortestPathIntent) {
369 spfIntent = (ShortestPathIntent) intent;
370 }
371
372 if (spfIntent == null) {
373 return false;
374 }
375
376 long srcSwDpid = spfIntent.getSrcSwitchDpid();
377 if (srcSwDpid == dpid) {
378 return true;
379 }
380
381 return false;
382 }
383
384 @Override
385 public String getName() {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700386 return "planInstall";
TeruU9e530662014-05-18 11:49:37 -0700387 }
388
389 @Override
390 public boolean isCallbackOrderingPrereq(OFType type, String name) {
TeruU9e530662014-05-18 11:49:37 -0700391 return false;
392 }
393
394 @Override
395 public boolean isCallbackOrderingPostreq(OFType type, String name) {
TeruU9e530662014-05-18 11:49:37 -0700396 return false;
397 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700398
Brian O'Connor12861f72014-02-19 20:40:32 -0800399}