blob: b6fc3a20e5078ce084db20ee5d1b3cd9fea536db [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 */
46
TeruU9e530662014-05-18 11:49:37 -070047public class PlanInstallModule implements IFloodlightModule, IOFMessageListener {
Brian O'Connorc89d2862014-06-13 00:51:04 -070048
Brian O'Connor12861f72014-02-19 20:40:32 -080049 protected volatile IFloodlightProviderService floodlightProvider;
Jonathan Harte37e4e22014-05-13 19:12:02 -070050 protected volatile ITopologyService topologyService;
Brian O'Connor12861f72014-02-19 20:40:32 -080051 protected volatile IDatagridService datagridService;
52 protected volatile IFlowPusherService flowPusher;
53 private PlanCalcRuntime planCalc;
54 private PlanInstallRuntime planInstall;
55 private EventListener eventListener;
Brian O'Connor488e5ed2014-02-20 19:50:01 -080056 private IEventChannel<Long, IntentStateList> intentStateChannel;
Ray Milkeyec838942014-04-09 11:28:43 -070057 private static final Logger log = LoggerFactory.getLogger(PlanInstallModule.class);
Brian O'Connor9b712f62014-02-20 14:22:20 -080058
Brian O'Connor12861f72014-02-19 20:40:32 -080059 private static final String PATH_INTENT_CHANNEL_NAME = "onos.pathintent";
Brian O'Connor488e5ed2014-02-20 19:50:01 -080060 private static final String INTENT_STATE_EVENT_CHANNEL_NAME = "onos.pathintent_state";
TeruU9e530662014-05-18 11:49:37 -070061 private ConcurrentMap<String, Intent> parentIntentMap = new ConcurrentHashMap<String, Intent>();
Brian O'Connor12861f72014-02-19 20:40:32 -080062
Brian O'Connorc89d2862014-06-13 00:51:04 -070063 /**
64 * EventListener for Intent updates from the PathCalcRuntime module.
65 */
Brian O'Connor12861f72014-02-19 20:40:32 -080066 class EventListener extends Thread
Ray Milkey269ffb92014-04-03 14:43:30 -070067 implements IEventChannelListener<Long, IntentOperationList> {
Toshio Koide3a52ef32014-02-28 12:10:26 -080068
Ray Milkey269ffb92014-04-03 14:43:30 -070069 private BlockingQueue<IntentOperationList> intentQueue = new LinkedBlockingQueue<>();
70 private Long key = Long.valueOf(0);
Toshio Koide3a52ef32014-02-28 12:10:26 -080071
Brian O'Connorc89d2862014-06-13 00:51:04 -070072 /**
73 * Poll the Intent queue for events.
74 */
Ray Milkey269ffb92014-04-03 14:43:30 -070075 @Override
76 public void run() {
77 while (true) {
78 try {
79 IntentOperationList intents = intentQueue.take();
80 //TODO: consider draining the remaining intent lists
81 // and processing in one big batch
Toshio Koide3a52ef32014-02-28 12:10:26 -080082
Ray Milkey269ffb92014-04-03 14:43:30 -070083 processIntents(intents);
84 } catch (InterruptedException e) {
85 log.warn("Error taking from intent queue: {}", e.getMessage());
86 }
87 }
88 }
Toshio Koide3a52ef32014-02-28 12:10:26 -080089
Brian O'Connorc89d2862014-06-13 00:51:04 -070090 /**
91 * Process events received from the event queue.
92 * <p>
93 * First, compute a plan, and then install it.
94 *
95 * @param intents list of new Intent events
96 */
Ray Milkey269ffb92014-04-03 14:43:30 -070097 private void processIntents(IntentOperationList intents) {
98 log("start_processIntents");
99 log.debug("Processing OperationList {}", intents);
100 log("begin_computePlan");
101 List<Set<FlowEntry>> plan = planCalc.computePlan(intents);
102 log("end_computePlan");
103 log.debug("Plan: {}", plan);
104 log("begin_installPlan");
105 boolean success = planInstall.installPlan(plan);
106 log("end_installPlan");
TeruUf9111652014-05-14 23:10:35 -0700107 Set<Long> domainSwitchDpids = floodlightProvider.getSwitches().keySet();
Ray Milkey269ffb92014-04-03 14:43:30 -0700108 log("begin_sendInstallNotif");
TeruUf9111652014-05-14 23:10:35 -0700109 sendNotifications(intents, true, success, domainSwitchDpids);
Ray Milkey269ffb92014-04-03 14:43:30 -0700110 log("end_sendInstallNotif");
111 log("finish");
112 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800113
TeruUf9111652014-05-14 23:10:35 -0700114 /***
115 * This function is for sending intent state notification to other ONOS instances.
116 * The argument of "domainSwitchDpids" is required for 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) {
129 case REMOVE:
130 if (installed) {
131 newState = success ? IntentState.DEL_ACK : IntentState.DEL_PENDING;
132 } else {
133 newState = IntentState.DEL_REQ;
134 }
135 break;
136 case ADD:
137 default:
138 if (installed) {
TeruUf9111652014-05-14 23:10:35 -0700139 if (domainSwitchDpids != null) {
140 states.domainSwitchDpids.addAll(domainSwitchDpids);
141 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700142 newState = success ? IntentState.INST_ACK : IntentState.INST_NACK;
143 } else {
144 newState = IntentState.INST_REQ;
145 }
146 break;
147 }
148 states.put(i.intent.getId(), newState);
149 }
TeruUf9111652014-05-14 23:10:35 -0700150
151 if (log.isTraceEnabled()) {
TeruU9e530662014-05-18 11:49:37 -0700152 log.trace("sendNotifications, states {}, domainSwitchDpids {}",
153 states, states.domainSwitchDpids);
TeruUf9111652014-05-14 23:10:35 -0700154 }
155
Brian O'Connorc89d2862014-06-13 00:51:04 -0700156 // Send notifications using the same key every time
Ray Milkey269ffb92014-04-03 14:43:30 -0700157 // and receive them by entryAdded() and entryUpdated()
Brian O'Connorc89d2862014-06-13 00:51:04 -0700158 intentStateChannel.addTransientEntry(key, states);
Ray Milkey269ffb92014-04-03 14:43:30 -0700159 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800160
Brian O'Connorc89d2862014-06-13 00:51:04 -0700161 /**
162 * Uses the entryUpdated() method. This should only be called once
163 * because we always use the same key.
164 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700165 @Override
166 public void entryAdded(IntentOperationList value) {
167 entryUpdated(value);
168 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800169
Brian O'Connorc89d2862014-06-13 00:51:04 -0700170 /*
171 * (non-Javadoc)
172 * @see net.onrc.onos.core.datagrid.IEventChannelListener#entryRemoved(java.lang.Object)
173 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700174 @Override
175 public void entryRemoved(IntentOperationList value) {
176 // This channel is a queue, so this method is not needed
177 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800178
Brian O'Connorc89d2862014-06-13 00:51:04 -0700179 /**
180 * Adds the new intents to the event queue.
181 *
182 * @param list of new intents
183 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700184 @Override
185 public void entryUpdated(IntentOperationList value) {
TeruU9e530662014-05-18 11:49:37 -0700186 putIntentOpsInfoInParentMap(value);
Ray Milkey269ffb92014-04-03 14:43:30 -0700187 log("start_intentNotifRecv");
188 log("begin_sendReceivedNotif");
TeruUf9111652014-05-14 23:10:35 -0700189 sendNotifications(value, false, false, null);
Ray Milkey269ffb92014-04-03 14:43:30 -0700190 log("end_sendReceivedNotif");
191 log("finish");
Brian O'Connor85dd8f22014-02-25 11:43:07 -0800192
Ray Milkey269ffb92014-04-03 14:43:30 -0700193 log.debug("Added OperationList {}", value);
194 try {
195 intentQueue.put(value);
196 } catch (InterruptedException e) {
197 log.warn("Error putting to intent queue: {}", e.getMessage());
198 }
199 }
TeruU9e530662014-05-18 11:49:37 -0700200
Brian O'Connorc89d2862014-06-13 00:51:04 -0700201 /**
202 * Add intent information to parent map.
203 *
204 * @param intentOps list of Intents and new states
205 */
TeruU9e530662014-05-18 11:49:37 -0700206 private void putIntentOpsInfoInParentMap(IntentOperationList intentOps) {
207 for (IntentOperation i : intentOps) {
208 if (!(i.intent instanceof PathIntent)) {
209 log.warn("Not a path intent: {}", i);
210 continue;
211 }
212 PathIntent intent = (PathIntent) i.intent;
213 Intent parent = intent.getParentIntent();
214 if (parent instanceof ShortestPathIntent) {
215 parentIntentMap.put(parent.getId(), parent);
216 } else {
217 log.warn("Unsupported Intent: {}", parent);
218 continue;
219 }
220 }
221 }
Brian O'Connor12861f72014-02-19 20:40:32 -0800222 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800223
Brian O'Connorc89d2862014-06-13 00:51:04 -0700224 /**
225 * Formatted log for debugging.
226 * TODO: merge this into debugging framework
227 *
228 * @param step the step of computation
229 */
Brian O'Connor85dd8f22014-02-25 11:43:07 -0800230 public static void log(String step) {
Pavlin Radoslavov964f8ae2014-04-18 16:44:14 -0700231 log.debug("Time:{}, Step:{}", System.nanoTime(), step);
Brian O'Connor85dd8f22014-02-25 11:43:07 -0800232 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800233
Brian O'Connorc89d2862014-06-13 00:51:04 -0700234 /*
235 * (non-Javadoc)
236 * @see net.floodlightcontroller.core.module.IFloodlightModule#init
237 * (net.floodlightcontroller.core.module.FloodlightModuleContext)
238 */
Brian O'Connor12861f72014-02-19 20:40:32 -0800239 @Override
TeruU9e530662014-05-18 11:49:37 -0700240 public void init(FloodlightModuleContext context)
241 throws FloodlightModuleException {
242 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
243 topologyService = context.getServiceImpl(ITopologyService.class);
244 datagridService = context.getServiceImpl(IDatagridService.class);
245 flowPusher = context.getServiceImpl(IFlowPusherService.class);
246 planCalc = new PlanCalcRuntime();
247 planInstall = new PlanInstallRuntime(floodlightProvider, flowPusher);
248 eventListener = new EventListener();
249 }
250
Brian O'Connorc89d2862014-06-13 00:51:04 -0700251 /*
252 * (non-Javadoc)
253 * @see net.floodlightcontroller.core.module.IFloodlightModule#startUp
254 * (net.floodlightcontroller.core.module.FloodlightModuleContext)
255 */
TeruU9e530662014-05-18 11:49:37 -0700256 @Override
Brian O'Connor12861f72014-02-19 20:40:32 -0800257 public void startUp(FloodlightModuleContext context) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700258 // start subscriber
259 datagridService.addListener(PATH_INTENT_CHANNEL_NAME,
260 eventListener,
261 Long.class,
262 IntentOperationList.class);
263 eventListener.start();
264 // start publisher
265 intentStateChannel = datagridService.createChannel(INTENT_STATE_EVENT_CHANNEL_NAME,
266 Long.class,
267 IntentStateList.class);
TeruU9e530662014-05-18 11:49:37 -0700268 floodlightProvider.addOFMessageListener(OFType.FLOW_REMOVED, this);
Brian O'Connor12861f72014-02-19 20:40:32 -0800269 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800270
Brian O'Connorc89d2862014-06-13 00:51:04 -0700271 /*
272 * (non-Javadoc)
273 * @see net.floodlightcontroller.core.module.IFloodlightModule#getModuleDependencies()
274 */
Brian O'Connor12861f72014-02-19 20:40:32 -0800275 @Override
276 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700277 Collection<Class<? extends IFloodlightService>> l =
278 new ArrayList<Class<? extends IFloodlightService>>();
279 l.add(IFloodlightProviderService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700280 l.add(ITopologyService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700281 l.add(IDatagridService.class);
282 l.add(IFlowPusherService.class);
283 return l;
Brian O'Connor12861f72014-02-19 20:40:32 -0800284 }
Toshio Koide3a52ef32014-02-28 12:10:26 -0800285
Brian O'Connorc89d2862014-06-13 00:51:04 -0700286 /*
287 * (non-Javadoc)
288 * @see net.floodlightcontroller.core.module.IFloodlightModule#getModuleServices()
289 */
Brian O'Connor12861f72014-02-19 20:40:32 -0800290 @Override
291 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700292 // no services, for now
293 return null;
Brian O'Connor12861f72014-02-19 20:40:32 -0800294 }
295
Brian O'Connorc89d2862014-06-13 00:51:04 -0700296 /*
297 * (non-Javadoc)
298 * @see net.floodlightcontroller.core.module.IFloodlightModule#getServiceImpls()
299 */
Brian O'Connor12861f72014-02-19 20:40:32 -0800300 @Override
301 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700302 // no services, for now
303 return null;
Brian O'Connor12861f72014-02-19 20:40:32 -0800304 }
TeruU9e530662014-05-18 11:49:37 -0700305
Brian O'Connorc89d2862014-06-13 00:51:04 -0700306 /**
307 * Handles FlowRemoved messages from the switches.
308 */
TeruU9e530662014-05-18 11:49:37 -0700309 @Override
310 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
311 if (msg.getType().equals(OFType.FLOW_REMOVED) &&
312 (msg instanceof OFFlowRemoved)) {
313 OFFlowRemoved flowRemovedMsg = (OFFlowRemoved) msg;
314
315 if (log.isTraceEnabled()) {
316 log.trace("Receive flowRemoved from sw {} : Cookie {}",
317 sw.getId(), flowRemovedMsg.getCookie());
318 }
319
320 String intentParentId = Long.toString(flowRemovedMsg.getCookie());
321 Intent intent = parentIntentMap.get(intentParentId);
322
323 //We assume if the path src sw flow entry is expired,
324 //the path is expired.
325 if (!isFlowSrcRemoved(sw.getId(), intentParentId)) {
326 return Command.CONTINUE;
327 }
328
329 ShortestPathIntent spfIntent = null;
330 if (!(intent instanceof ShortestPathIntent)) {
331 return Command.CONTINUE;
332 }
333 spfIntent = (ShortestPathIntent) intent;
334 String pathIntentId = spfIntent.getPathIntentId();
335
336 IntentStateList states = new IntentStateList();
337 IntentState newState = IntentState.DEL_ACK;
338 states.put(pathIntentId, newState);
339 Set<Long> domainSwitchDpids = floodlightProvider.getSwitches().keySet();
340 if (domainSwitchDpids != null) {
341 states.domainSwitchDpids.addAll(domainSwitchDpids);
342 }
343 parentIntentMap.remove(intentParentId);
344 log.debug("addEntry to intentStateChannel intentId {}, states {}", flowRemovedMsg.getCookie(), states);
345
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
357 * @return
358 */
TeruU9e530662014-05-18 11:49:37 -0700359 private boolean isFlowSrcRemoved(long dpid, String shortestPathIntentId) {
360 Intent intent = parentIntentMap.get(shortestPathIntentId);
361 ShortestPathIntent spfIntent = null;
362 if (intent instanceof ShortestPathIntent) {
363 spfIntent = (ShortestPathIntent) intent;
364 }
365
366 if (spfIntent == null) {
367 return false;
368 }
369
370 long srcSwDpid = spfIntent.getSrcSwitchDpid();
371 if (srcSwDpid == dpid) {
372 return true;
373 }
374
375 return false;
376 }
377
Brian O'Connorc89d2862014-06-13 00:51:04 -0700378 /*
379 * (non-Javadoc)
380 * @see net.floodlightcontroller.core.IListener#getName()
381 */
TeruU9e530662014-05-18 11:49:37 -0700382 @Override
383 public String getName() {
384 // TODO Auto-generated method stub
385 return null;
386 }
387
Brian O'Connorc89d2862014-06-13 00:51:04 -0700388 /*
389 * (non-Javadoc)
390 * @see net.floodlightcontroller.core.IListener#isCallbackOrderingPrereq(java.lang.Object, java.lang.String)
391 */
TeruU9e530662014-05-18 11:49:37 -0700392 @Override
393 public boolean isCallbackOrderingPrereq(OFType type, String name) {
394 // TODO Auto-generated method stub
395 return false;
396 }
397
Brian O'Connorc89d2862014-06-13 00:51:04 -0700398 /*
399 * (non-Javadoc)
400 * @see net.floodlightcontroller.core.IListener#isCallbackOrderingPostreq(java.lang.Object, java.lang.String)
401 */
TeruU9e530662014-05-18 11:49:37 -0700402 @Override
403 public boolean isCallbackOrderingPostreq(OFType type, String name) {
404 // TODO Auto-generated method stub
405 return false;
406 }
Brian O'Connor12861f72014-02-19 20:40:32 -0800407}