blob: 07f7becebbc5302c3806c51a0e007b241f1b1f3f [file] [log] [blame]
Jonathan Hartaa380972014-04-03 10:24:46 -07001package net.onrc.onos.core.intent.runtime;
Brian O'Connor7f8e3012014-02-15 23:59:29 -08002
3import java.util.ArrayList;
4import java.util.Collection;
5import java.util.Collections;
6import java.util.HashMap;
7import java.util.HashSet;
Brian O'Connor8a52cdd2014-02-25 15:35:25 -08008import java.util.LinkedList;
Brian O'Connor7f8e3012014-02-15 23:59:29 -08009import java.util.List;
10import java.util.Map;
11import java.util.Set;
12
13import net.floodlightcontroller.util.MACAddress;
Jonathan Hartaa380972014-04-03 10:24:46 -070014import net.onrc.onos.core.intent.FlowEntry;
15import net.onrc.onos.core.intent.Intent;
16import net.onrc.onos.core.intent.IntentOperation;
Jonathan Harta99ec672014-04-03 11:30:34 -070017import net.onrc.onos.core.intent.IntentOperation.Operator;
Jonathan Hartaa380972014-04-03 10:24:46 -070018import net.onrc.onos.core.intent.IntentOperationList;
19import net.onrc.onos.core.intent.PathIntent;
20import net.onrc.onos.core.intent.ShortestPathIntent;
Jonathan Hart472062d2014-04-03 10:56:48 -070021import net.onrc.onos.core.topology.LinkEvent;
Jonathan Hart472062d2014-04-03 10:56:48 -070022
Brian O'Connor8a52cdd2014-02-25 15:35:25 -080023import org.slf4j.Logger;
24import org.slf4j.LoggerFactory;
25
Brian O'Connor7f8e3012014-02-15 23:59:29 -080026/**
Brian O'Connor762aca32014-06-12 14:18:23 -070027 * The PlanCalcRuntime class receives a list of intents and operations (IntentOperationList),
28 * and does the following:
29 * <ul>
30 * <li> Convert the Intent into FlowEntries.
31 * <li> Determine the dependency between FlowEntries.
32 * <li> Create sets of FlowEntries to be installed by the OpenFlow protocol.
33 * </ul>
Brian O'Connor7f8e3012014-02-15 23:59:29 -080034 */
35
36public class PlanCalcRuntime {
Toshio Koided9fa2a82014-02-19 17:35:18 -080037
Ray Milkeyec838942014-04-09 11:28:43 -070038 private static final Logger log = LoggerFactory.getLogger(PlanCalcRuntime.class);
Brian O'Connor12861f72014-02-19 20:40:32 -080039
Brian O'Connor762aca32014-06-12 14:18:23 -070040 /**
41 * The method produces a plan from a list of IntentOperations.
42 * <p>
43 * In this context, a plan is a list of sets of flow entries. Flow entries within
44 * a set can be installed in parallel. Each set of flow entries is dependent on
45 * all previous sets in the list.
46 *
47 * @param intentOps list of IntentOperations (Intent + Operation) for plan computation
48 * @return plan (list of set of flow entries)
49 */
Brian O'Connor12861f72014-02-19 20:40:32 -080050 public List<Set<FlowEntry>> computePlan(IntentOperationList intentOps) {
Ray Milkey269ffb92014-04-03 14:43:30 -070051 long start = System.nanoTime();
52 List<Collection<FlowEntry>> flowEntries = computeFlowEntries(intentOps);
53 long step1 = System.nanoTime();
54 List<Set<FlowEntry>> plan = buildPhases(flowEntries);
55 long step2 = System.nanoTime();
Pavlin Radoslavov964f8ae2014-04-18 16:44:14 -070056 log.debug("MEASUREMENT: Compute flow entries: {} ns, Build phases: {} ns",
Ray Milkey269ffb92014-04-03 14:43:30 -070057 (step1 - start), (step2 - step1));
58 return plan;
Brian O'Connor12861f72014-02-19 20:40:32 -080059 }
60
Brian O'Connor762aca32014-06-12 14:18:23 -070061 /**
62 * Converts PathIntents to FlowEntries so they can be installed.
63 * <p>
64 * Note: This method only supports ShortestPathIntents at the moment.
65 *
66 * @param intentOps list of IntentOperations
67 * @return a list of FlowEntry objects for each Intent in intentOps
68 */
Brian O'Connor8a52cdd2014-02-25 15:35:25 -080069 private List<Collection<FlowEntry>> computeFlowEntries(IntentOperationList intentOps) {
Ray Milkey269ffb92014-04-03 14:43:30 -070070 List<Collection<FlowEntry>> flowEntries = new LinkedList<>();
71 for (IntentOperation i : intentOps) {
72 if (!(i.intent instanceof PathIntent)) {
73 log.warn("Not a path intent: {}", i);
74 continue;
75 }
76 PathIntent intent = (PathIntent) i.intent;
77 Intent parent = intent.getParentIntent();
78 long srcPort, dstPort;
TeruU30c0c932014-05-15 16:47:41 -070079 long lastDstSw = -1, lastDstPort = -1, firstSrcSw = -1;
Ray Milkey269ffb92014-04-03 14:43:30 -070080 MACAddress srcMac, dstMac;
TeruU30c0c932014-05-15 16:47:41 -070081 int idleTimeout = 0, hardTimeout = 0, firstSwitchIdleTimeout = 0, firstSwitchHardTimeout = 0;
TeruU9e530662014-05-18 11:49:37 -070082 Long cookieId = null;
Komal Shah399a2922014-05-28 01:57:40 -070083 int srcIP, dstIP;
Ray Milkey269ffb92014-04-03 14:43:30 -070084 if (parent instanceof ShortestPathIntent) {
85 ShortestPathIntent pathIntent = (ShortestPathIntent) parent;
Ray Milkey269ffb92014-04-03 14:43:30 -070086 srcPort = pathIntent.getSrcPortNumber();
Komal Shah399a2922014-05-28 01:57:40 -070087
Komal Shah399a2922014-05-28 01:57:40 -070088 if (pathIntent.getSrcMac() != ShortestPathIntent.EMPTYMACADDRESS) {
89 srcMac = MACAddress.valueOf(pathIntent.getSrcMac());
90 } else {
91 srcMac = null;
92 }
93
Komal Shah399a2922014-05-28 01:57:40 -070094 if (pathIntent.getDstMac() != ShortestPathIntent.EMPTYMACADDRESS) {
Jonathan Hart20c801c2014-06-02 11:11:07 -070095 dstMac = MACAddress.valueOf(pathIntent.getDstMac());
Komal Shah399a2922014-05-28 01:57:40 -070096 } else {
97 dstMac = null;
98 }
99
Komal Shah399a2922014-05-28 01:57:40 -0700100 srcIP = pathIntent.getSrcIp();
Komal Shah399a2922014-05-28 01:57:40 -0700101 dstIP = pathIntent.getDstIp();
102
Ray Milkey269ffb92014-04-03 14:43:30 -0700103 lastDstSw = pathIntent.getDstSwitchDpid();
TeruU30c0c932014-05-15 16:47:41 -0700104 firstSrcSw = pathIntent.getSrcSwitchDpid();
Ray Milkey269ffb92014-04-03 14:43:30 -0700105 lastDstPort = pathIntent.getDstPortNumber();
TeruU30c0c932014-05-15 16:47:41 -0700106 idleTimeout = pathIntent.getIdleTimeout();
107 hardTimeout = pathIntent.getHardTimeout();
108 firstSwitchIdleTimeout = pathIntent.getFirstSwitchIdleTimeout();
Jonathan Hart33e1f302014-05-30 14:37:09 -0700109 firstSwitchHardTimeout = pathIntent.getFirstSwitchHardTimeout();
TeruU9e530662014-05-18 11:49:37 -0700110 try {
111 cookieId = Long.valueOf(pathIntent.getId());
112 } catch (NumberFormatException e) {
113 log.trace("NumberFormatException : ", e);
114 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700115 } else {
116 log.warn("Unsupported Intent: {}", parent);
117 continue;
118 }
119 List<FlowEntry> entries = new ArrayList<>();
120 for (LinkEvent linkEvent : intent.getPath()) {
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700121 long sw = linkEvent.getSrc().getDpid().value();
Yuta HIGUCHIb1e2ab72014-06-30 11:01:31 -0700122 dstPort = linkEvent.getSrc().getPortNumber().value();
Komal Shah399a2922014-05-28 01:57:40 -0700123 FlowEntry fe = new FlowEntry(sw, srcPort, dstPort, srcMac, dstMac,
124 srcIP, dstIP, i.operator);
TeruU30c0c932014-05-15 16:47:41 -0700125 if (sw != firstSrcSw) {
126 fe.setIdleTimeout(idleTimeout);
127 fe.setHardTimeout(hardTimeout);
128 } else {
129 fe.setIdleTimeout(firstSwitchIdleTimeout);
130 fe.setHardTimeout(firstSwitchHardTimeout);
131 }
TeruU9e530662014-05-18 11:49:37 -0700132 if (cookieId != null) {
133 log.trace("cookieId is set: {}", cookieId);
134 fe.setFlowEntryId(cookieId);
135 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700136 entries.add(fe);
Yuta HIGUCHIb1e2ab72014-06-30 11:01:31 -0700137 srcPort = linkEvent.getDst().getPortNumber().value();
Ray Milkey269ffb92014-04-03 14:43:30 -0700138 }
139 if (lastDstSw >= 0 && lastDstPort >= 0) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700140 long sw = lastDstSw;
141 dstPort = lastDstPort;
Komal Shah399a2922014-05-28 01:57:40 -0700142 FlowEntry fe = new FlowEntry(sw, srcPort, dstPort, srcMac, dstMac,
143 srcIP, dstIP, i.operator);
TeruUe3bbe2f2014-05-28 16:11:30 -0700144 if (cookieId != null) {
145 log.trace("cookieId is set: {}", cookieId);
146 fe.setFlowEntryId(cookieId);
147 }
TeruUe5d0bf12014-05-28 17:50:02 -0700148 if (sw != firstSrcSw) {
149 fe.setIdleTimeout(idleTimeout);
150 fe.setHardTimeout(hardTimeout);
151 } else {
152 fe.setIdleTimeout(firstSwitchIdleTimeout);
153 fe.setHardTimeout(firstSwitchHardTimeout);
154 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700155 entries.add(fe);
156 }
Brian O'Connor762aca32014-06-12 14:18:23 -0700157 // reverse order of flow entries so they are installed backwards
Ray Milkey269ffb92014-04-03 14:43:30 -0700158 Collections.reverse(entries);
159 flowEntries.add(entries);
160 }
161 return flowEntries;
Brian O'Connor12861f72014-02-19 20:40:32 -0800162 }
Toshio Koided9fa2a82014-02-19 17:35:18 -0800163
Brian O'Connor762aca32014-06-12 14:18:23 -0700164 /**
165 * TODO
166 *
167 * Note: This method is for a testing purpose. Please leave it right now.
168 *
169 * @param flowEntries
Jonathan Hart99ff20a2014-06-15 16:53:00 -0700170 * @return a list of phases, where each phase is a set of FlowEntries
Brian O'Connor762aca32014-06-12 14:18:23 -0700171 */
172 @SuppressWarnings("unused")
TeruU30c0c932014-05-15 16:47:41 -0700173 private List<Set<FlowEntry>> simpleBuildPhases(List<Collection<FlowEntry>> flowEntries) {
174 List<Set<FlowEntry>> plan = new ArrayList<>();
175 Set<FlowEntry> phase = new HashSet<>();
176 for (Collection<FlowEntry> c : flowEntries) {
177 phase.addAll(c);
178 }
179 plan.add(phase);
180
181 return plan;
182 }
183
Brian O'Connor762aca32014-06-12 14:18:23 -0700184 /**
185 * Merges the lists generated by computeFlowEntries() into install phases.
186 * <p>
187 * This function will also remove duplicate entries.
188 *
189 * @param flowEntries list of lists of flowEntries
190 * @return a list of sets of FlowEntries to be installed
191 */
Brian O'Connor8a52cdd2014-02-25 15:35:25 -0800192 private List<Set<FlowEntry>> buildPhases(List<Collection<FlowEntry>> flowEntries) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700193 Map<FlowEntry, Integer> map = new HashMap<>();
194 List<Set<FlowEntry>> plan = new ArrayList<>();
Brian O'Connor762aca32014-06-12 14:18:23 -0700195 // merge equal FlowEntries
196 //TODO: explore ways to merge FlowEntries that contain the same match condition
Ray Milkey269ffb92014-04-03 14:43:30 -0700197 for (Collection<FlowEntry> c : flowEntries) {
198 for (FlowEntry e : c) {
199 Integer i = map.get(e);
200 if (i == null) {
201 i = Integer.valueOf(0);
202 }
203 switch (e.getOperator()) {
204 case ADD:
205 i += 1;
206 break;
207 case REMOVE:
208 i -= 1;
209 break;
210 default:
211 break;
212 }
213 map.put(e, i);
Ray Milkey269ffb92014-04-03 14:43:30 -0700214 }
215 }
Toshio Koidebf875662014-02-24 12:19:15 -0800216
Ray Milkey269ffb92014-04-03 14:43:30 -0700217 // really simple first iteration of plan
218 //TODO: optimize the map in phases
219 Set<FlowEntry> phase = new HashSet<>();
Pavlin Radoslavov45a0ad32014-04-10 12:16:40 -0700220 for (Map.Entry<FlowEntry, Integer> entry : map.entrySet()) {
221 FlowEntry e = entry.getKey();
222 Integer i = entry.getValue();
Ray Milkey269ffb92014-04-03 14:43:30 -0700223 if (i == 0) {
224 continue;
225 } else if (i > 0) {
226 e.setOperator(Operator.ADD);
227 } else if (i < 0) {
228 e.setOperator(Operator.REMOVE);
229 }
230 phase.add(e);
231 }
232 plan.add(phase);
Toshio Koidebf875662014-02-24 12:19:15 -0800233
Ray Milkey269ffb92014-04-03 14:43:30 -0700234 return plan;
Brian O'Connor12861f72014-02-19 20:40:32 -0800235 }
Brian O'Connor7f8e3012014-02-15 23:59:29 -0800236}