blob: 025b6d7524ad0152d2d563eea96523e64fcab445 [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
TeruU9e530662014-05-18 11:49:37 -070034import org.openflow.protocol.OFFlowRemoved;
35import org.openflow.protocol.OFMessage;
36import org.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>
43 * It is responsible for converting Intents into FlowMods and seeing that
44 * they are properly installed.
45 */
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();
79 //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 /***
114 * This function is for sending intent state notification to other ONOS instances.
115 * The argument of "domainSwitchDpids" is required for dispatching this ONOS's managed switches.
Brian O'Connorc89d2862014-06-13 00:51:04 -0700116 *
117 * @param intents list of intents
118 * @param installed true if Intents were installed
119 * @param success true if updates succeeded
120 * @param domainSwitchDpids set of affected switches
TeruUf9111652014-05-14 23:10:35 -0700121 */
Jonathan Hartc00f5c22014-06-10 15:14:40 -0700122 private void sendNotifications(IntentOperationList intents,
123 boolean installed, boolean success, Set<Long> domainSwitchDpids) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700124 IntentStateList states = new IntentStateList();
125 for (IntentOperation i : intents) {
126 IntentState newState;
127 switch (i.operator) {
128 case REMOVE:
129 if (installed) {
130 newState = success ? IntentState.DEL_ACK : IntentState.DEL_PENDING;
131 } else {
132 newState = IntentState.DEL_REQ;
133 }
134 break;
135 case ADD:
136 default:
137 if (installed) {
TeruUf9111652014-05-14 23:10:35 -0700138 if (domainSwitchDpids != null) {
139 states.domainSwitchDpids.addAll(domainSwitchDpids);
140 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700141 newState = success ? IntentState.INST_ACK : IntentState.INST_NACK;
142 } else {
143 newState = IntentState.INST_REQ;
144 }
145 break;
146 }
147 states.put(i.intent.getId(), newState);
148 }
TeruUf9111652014-05-14 23:10:35 -0700149
150 if (log.isTraceEnabled()) {
TeruU9e530662014-05-18 11:49:37 -0700151 log.trace("sendNotifications, states {}, domainSwitchDpids {}",
152 states, states.domainSwitchDpids);
TeruUf9111652014-05-14 23:10:35 -0700153 }
154
Brian O'Connorc89d2862014-06-13 00:51:04 -0700155 // Send notifications using the same key every time
Ray Milkey269ffb92014-04-03 14:43:30 -0700156 // and receive them by entryAdded() and entryUpdated()
Brian O'Connorc89d2862014-06-13 00:51:04 -0700157 intentStateChannel.addTransientEntry(key, states);
Ray Milkey269ffb92014-04-03 14:43:30 -0700158 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800159
Brian O'Connorc89d2862014-06-13 00:51:04 -0700160 /**
161 * Uses the entryUpdated() method. This should only be called once
162 * because we always use the same key.
163 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700164 @Override
165 public void entryAdded(IntentOperationList value) {
166 entryUpdated(value);
167 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800168
Brian O'Connorc89d2862014-06-13 00:51:04 -0700169 /*
170 * (non-Javadoc)
171 * @see net.onrc.onos.core.datagrid.IEventChannelListener#entryRemoved(java.lang.Object)
172 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700173 @Override
174 public void entryRemoved(IntentOperationList value) {
175 // This channel is a queue, so this method is not needed
176 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800177
Brian O'Connorc89d2862014-06-13 00:51:04 -0700178 /**
179 * Adds the new intents to the event queue.
180 *
Sho SHIMIZU69474452014-06-20 09:17:40 -0700181 * @param value list of new intents
Brian O'Connorc89d2862014-06-13 00:51:04 -0700182 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700183 @Override
184 public void entryUpdated(IntentOperationList value) {
TeruU9e530662014-05-18 11:49:37 -0700185 putIntentOpsInfoInParentMap(value);
Ray Milkey269ffb92014-04-03 14:43:30 -0700186 log("start_intentNotifRecv");
187 log("begin_sendReceivedNotif");
TeruUf9111652014-05-14 23:10:35 -0700188 sendNotifications(value, false, false, null);
Ray Milkey269ffb92014-04-03 14:43:30 -0700189 log("end_sendReceivedNotif");
190 log("finish");
Brian O'Connor85dd8f22014-02-25 11:43:07 -0800191
Ray Milkey269ffb92014-04-03 14:43:30 -0700192 log.debug("Added OperationList {}", value);
193 try {
194 intentQueue.put(value);
195 } catch (InterruptedException e) {
196 log.warn("Error putting to intent queue: {}", e.getMessage());
197 }
198 }
TeruU9e530662014-05-18 11:49:37 -0700199
Brian O'Connorc89d2862014-06-13 00:51:04 -0700200 /**
201 * Add intent information to parent map.
202 *
203 * @param intentOps list of Intents and new states
204 */
TeruU9e530662014-05-18 11:49:37 -0700205 private void putIntentOpsInfoInParentMap(IntentOperationList intentOps) {
206 for (IntentOperation i : intentOps) {
207 if (!(i.intent instanceof PathIntent)) {
208 log.warn("Not a path intent: {}", i);
209 continue;
210 }
211 PathIntent intent = (PathIntent) i.intent;
212 Intent parent = intent.getParentIntent();
213 if (parent instanceof ShortestPathIntent) {
214 parentIntentMap.put(parent.getId(), parent);
215 } else {
216 log.warn("Unsupported Intent: {}", parent);
217 continue;
218 }
219 }
220 }
Brian O'Connor12861f72014-02-19 20:40:32 -0800221 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800222
Brian O'Connorc89d2862014-06-13 00:51:04 -0700223 /**
224 * Formatted log for debugging.
225 * TODO: merge this into debugging framework
226 *
227 * @param step the step of computation
228 */
Brian O'Connor85dd8f22014-02-25 11:43:07 -0800229 public static void log(String step) {
Pavlin Radoslavov964f8ae2014-04-18 16:44:14 -0700230 log.debug("Time:{}, Step:{}", System.nanoTime(), step);
Brian O'Connor85dd8f22014-02-25 11:43:07 -0800231 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800232
Brian O'Connorc89d2862014-06-13 00:51:04 -0700233 /*
234 * (non-Javadoc)
235 * @see net.floodlightcontroller.core.module.IFloodlightModule#init
236 * (net.floodlightcontroller.core.module.FloodlightModuleContext)
237 */
Brian O'Connor12861f72014-02-19 20:40:32 -0800238 @Override
TeruU9e530662014-05-18 11:49:37 -0700239 public void init(FloodlightModuleContext context)
240 throws FloodlightModuleException {
241 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
242 topologyService = context.getServiceImpl(ITopologyService.class);
243 datagridService = context.getServiceImpl(IDatagridService.class);
244 flowPusher = context.getServiceImpl(IFlowPusherService.class);
245 planCalc = new PlanCalcRuntime();
246 planInstall = new PlanInstallRuntime(floodlightProvider, flowPusher);
247 eventListener = new EventListener();
248 }
249
Brian O'Connorc89d2862014-06-13 00:51:04 -0700250 /*
251 * (non-Javadoc)
252 * @see net.floodlightcontroller.core.module.IFloodlightModule#startUp
253 * (net.floodlightcontroller.core.module.FloodlightModuleContext)
254 */
TeruU9e530662014-05-18 11:49:37 -0700255 @Override
Brian O'Connor12861f72014-02-19 20:40:32 -0800256 public void startUp(FloodlightModuleContext context) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700257 // start subscriber
258 datagridService.addListener(PATH_INTENT_CHANNEL_NAME,
259 eventListener,
260 Long.class,
261 IntentOperationList.class);
262 eventListener.start();
263 // start publisher
264 intentStateChannel = datagridService.createChannel(INTENT_STATE_EVENT_CHANNEL_NAME,
265 Long.class,
266 IntentStateList.class);
TeruU9e530662014-05-18 11:49:37 -0700267 floodlightProvider.addOFMessageListener(OFType.FLOW_REMOVED, this);
Brian O'Connor12861f72014-02-19 20:40:32 -0800268 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800269
Brian O'Connorc89d2862014-06-13 00:51:04 -0700270 /*
271 * (non-Javadoc)
272 * @see net.floodlightcontroller.core.module.IFloodlightModule#getModuleDependencies()
273 */
Brian O'Connor12861f72014-02-19 20:40:32 -0800274 @Override
275 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700276 Collection<Class<? extends IFloodlightService>> l =
277 new ArrayList<Class<? extends IFloodlightService>>();
278 l.add(IFloodlightProviderService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700279 l.add(ITopologyService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700280 l.add(IDatagridService.class);
281 l.add(IFlowPusherService.class);
282 return l;
Brian O'Connor12861f72014-02-19 20:40:32 -0800283 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800284
Brian O'Connorc89d2862014-06-13 00:51:04 -0700285 /*
286 * (non-Javadoc)
287 * @see net.floodlightcontroller.core.module.IFloodlightModule#getModuleServices()
288 */
Brian O'Connor12861f72014-02-19 20:40:32 -0800289 @Override
290 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700291 // no services, for now
292 return null;
Brian O'Connor12861f72014-02-19 20:40:32 -0800293 }
294
Brian O'Connorc89d2862014-06-13 00:51:04 -0700295 /*
296 * (non-Javadoc)
297 * @see net.floodlightcontroller.core.module.IFloodlightModule#getServiceImpls()
298 */
Brian O'Connor12861f72014-02-19 20:40:32 -0800299 @Override
300 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700301 // no services, for now
302 return null;
Brian O'Connor12861f72014-02-19 20:40:32 -0800303 }
TeruU9e530662014-05-18 11:49:37 -0700304
Brian O'Connorc89d2862014-06-13 00:51:04 -0700305 /**
306 * Handles FlowRemoved messages from the switches.
307 */
TeruU9e530662014-05-18 11:49:37 -0700308 @Override
309 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
310 if (msg.getType().equals(OFType.FLOW_REMOVED) &&
311 (msg instanceof OFFlowRemoved)) {
312 OFFlowRemoved flowRemovedMsg = (OFFlowRemoved) msg;
313
314 if (log.isTraceEnabled()) {
315 log.trace("Receive flowRemoved from sw {} : Cookie {}",
316 sw.getId(), flowRemovedMsg.getCookie());
317 }
318
319 String intentParentId = Long.toString(flowRemovedMsg.getCookie());
320 Intent intent = parentIntentMap.get(intentParentId);
321
322 //We assume if the path src sw flow entry is expired,
323 //the path is expired.
324 if (!isFlowSrcRemoved(sw.getId(), intentParentId)) {
325 return Command.CONTINUE;
326 }
327
328 ShortestPathIntent spfIntent = null;
329 if (!(intent instanceof ShortestPathIntent)) {
330 return Command.CONTINUE;
331 }
332 spfIntent = (ShortestPathIntent) intent;
333 String pathIntentId = spfIntent.getPathIntentId();
334
335 IntentStateList states = new IntentStateList();
336 IntentState newState = IntentState.DEL_ACK;
337 states.put(pathIntentId, newState);
338 Set<Long> domainSwitchDpids = floodlightProvider.getSwitches().keySet();
339 if (domainSwitchDpids != null) {
340 states.domainSwitchDpids.addAll(domainSwitchDpids);
341 }
342 parentIntentMap.remove(intentParentId);
TeruU5d2c9392014-06-09 20:02:02 -0700343 log.debug("addEntry to intentStateChannel intentId {}, states {}",
344 pathIntentId, newState);
TeruU9e530662014-05-18 11:49:37 -0700345
346 intentStateChannel.addTransientEntry(flowRemovedMsg.getCookie(), states);
347 }
348
349 return Command.CONTINUE;
350 }
351
Brian O'Connorc89d2862014-06-13 00:51:04 -0700352 /**
353 * Check to see if the flow's source switch entry is removed/expired.
354 *
355 * @param dpid DPID of affected switch
356 * @param shortestPathIntentId Intent to check
Jonathan Hart99ff20a2014-06-15 16:53:00 -0700357 * @return true if the flow's source switch entry is removed or expired,
358 * otherwise false.
Brian O'Connorc89d2862014-06-13 00:51:04 -0700359 */
TeruU9e530662014-05-18 11:49:37 -0700360 private boolean isFlowSrcRemoved(long dpid, String shortestPathIntentId) {
361 Intent intent = parentIntentMap.get(shortestPathIntentId);
362 ShortestPathIntent spfIntent = null;
363 if (intent instanceof ShortestPathIntent) {
364 spfIntent = (ShortestPathIntent) intent;
365 }
366
367 if (spfIntent == null) {
368 return false;
369 }
370
371 long srcSwDpid = spfIntent.getSrcSwitchDpid();
372 if (srcSwDpid == dpid) {
373 return true;
374 }
375
376 return false;
377 }
378
Brian O'Connorc89d2862014-06-13 00:51:04 -0700379 /*
380 * (non-Javadoc)
381 * @see net.floodlightcontroller.core.IListener#getName()
382 */
TeruU9e530662014-05-18 11:49:37 -0700383 @Override
384 public String getName() {
385 // TODO Auto-generated method stub
386 return null;
387 }
388
Brian O'Connorc89d2862014-06-13 00:51:04 -0700389 /*
390 * (non-Javadoc)
391 * @see net.floodlightcontroller.core.IListener#isCallbackOrderingPrereq(java.lang.Object, java.lang.String)
392 */
TeruU9e530662014-05-18 11:49:37 -0700393 @Override
394 public boolean isCallbackOrderingPrereq(OFType type, String name) {
395 // TODO Auto-generated method stub
396 return false;
397 }
398
Brian O'Connorc89d2862014-06-13 00:51:04 -0700399 /*
400 * (non-Javadoc)
401 * @see net.floodlightcontroller.core.IListener#isCallbackOrderingPostreq(java.lang.Object, java.lang.String)
402 */
TeruU9e530662014-05-18 11:49:37 -0700403 @Override
404 public boolean isCallbackOrderingPostreq(OFType type, String name) {
405 // TODO Auto-generated method stub
406 return false;
407 }
Brian O'Connor12861f72014-02-19 20:40:32 -0800408}