blob: 9025224ea62a0f194bbc756d2cca2faac7ff805f [file] [log] [blame]
Boyoung Jeong9e8faec2018-06-17 21:19:23 +09001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.openstacktelemetry.impl;
17
Jian Li0bbbb1c2018-06-22 22:01:17 +090018import com.google.common.collect.Sets;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +090019import org.apache.commons.lang3.exception.ExceptionUtils;
20import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
Jian Li753280e2018-07-03 02:24:34 +090023import org.apache.felix.scr.annotations.Modified;
24import org.apache.felix.scr.annotations.Property;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +090025import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.apache.felix.scr.annotations.Service;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +090028import org.onlab.packet.IpAddress;
Jian Li753280e2018-07-03 02:24:34 +090029import org.onlab.packet.IpPrefix;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +090030import org.onlab.packet.MacAddress;
31import org.onlab.packet.VlanId;
Jian Lie6110b72018-07-06 19:06:36 +090032import org.onosproject.cfg.ComponentConfigService;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +090033import org.onosproject.core.ApplicationId;
34import org.onosproject.core.CoreService;
Jian Li85573f42018-06-27 22:29:14 +090035import org.onosproject.mastership.MastershipService;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +090036import org.onosproject.net.DeviceId;
37import org.onosproject.net.Host;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +090038import org.onosproject.net.flow.DefaultFlowRule;
39import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.FlowEntry;
42import org.onosproject.net.flow.FlowRule;
43import org.onosproject.net.flow.FlowRuleOperations;
44import org.onosproject.net.flow.FlowRuleOperationsContext;
45import org.onosproject.net.flow.FlowRuleService;
46import org.onosproject.net.flow.IndexTableId;
47import org.onosproject.net.flow.TrafficSelector;
48import org.onosproject.net.flow.TrafficTreatment;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +090049import org.onosproject.net.flow.criteria.IPCriterion;
50import org.onosproject.net.flow.criteria.IPProtocolCriterion;
51import org.onosproject.net.flow.criteria.TcpPortCriterion;
52import org.onosproject.net.flow.criteria.UdpPortCriterion;
53import org.onosproject.net.host.HostService;
Jian Li753280e2018-07-03 02:24:34 +090054import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +090055import org.onosproject.openstacktelemetry.api.FlowInfo;
Jian Li0bbbb1c2018-06-22 22:01:17 +090056import org.onosproject.openstacktelemetry.api.OpenstackTelemetryService;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +090057import org.onosproject.openstacktelemetry.api.StatsFlowRule;
58import org.onosproject.openstacktelemetry.api.StatsFlowRuleAdminService;
59import org.onosproject.openstacktelemetry.api.StatsInfo;
Jian Li753280e2018-07-03 02:24:34 +090060import org.osgi.service.component.ComponentContext;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +090061import org.slf4j.Logger;
62import org.slf4j.LoggerFactory;
63
Jian Li753280e2018-07-03 02:24:34 +090064import java.util.Dictionary;
Jian Li85573f42018-06-27 22:29:14 +090065import java.util.Optional;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +090066import java.util.Set;
67import java.util.Timer;
68import java.util.TimerTask;
69
Jian Li0bbbb1c2018-06-22 22:01:17 +090070import static org.onlab.packet.Ethernet.TYPE_IPV4;
71import static org.onlab.packet.IPv4.PROTOCOL_TCP;
72import static org.onlab.packet.IPv4.PROTOCOL_UDP;
73import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
74import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_SRC;
75import static org.onosproject.net.flow.criteria.Criterion.Type.IP_PROTO;
76import static org.onosproject.net.flow.criteria.Criterion.Type.TCP_DST;
77import static org.onosproject.net.flow.criteria.Criterion.Type.TCP_SRC;
78import static org.onosproject.net.flow.criteria.Criterion.Type.UDP_DST;
79import static org.onosproject.net.flow.criteria.Criterion.Type.UDP_SRC;
Jian Li753280e2018-07-03 02:24:34 +090080import static org.onosproject.openstacknetworking.api.Constants.STAT_FLAT_OUTBOUND_TABLE;
Jian Li0bbbb1c2018-06-22 22:01:17 +090081import static org.onosproject.openstacknetworking.api.Constants.STAT_INBOUND_TABLE;
82import static org.onosproject.openstacknetworking.api.Constants.STAT_OUTBOUND_TABLE;
Jian Li753280e2018-07-03 02:24:34 +090083import static org.onosproject.openstacknetworking.api.Constants.VTAP_FLAT_OUTBOUND_TABLE;
Jian Li87ded822018-07-02 18:31:22 +090084import static org.onosproject.openstacknetworking.api.Constants.VTAP_INBOUND_TABLE;
85import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_TABLE;
Jian Lie6110b72018-07-06 19:06:36 +090086import static org.onosproject.openstacktelemetry.api.Constants.FLAT;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +090087import static org.onosproject.openstacktelemetry.api.Constants.OPENSTACK_TELEMETRY_APP_ID;
Jian Lie6110b72018-07-06 19:06:36 +090088import static org.onosproject.openstacktelemetry.api.Constants.VLAN;
89import static org.onosproject.openstacktelemetry.api.Constants.VXLAN;
Jian Li753280e2018-07-03 02:24:34 +090090import static org.onosproject.openstacktelemetry.util.OpenstackTelemetryUtil.getBooleanProperty;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +090091
Boyoung Jeong9e8faec2018-06-17 21:19:23 +090092/**
93 * Flow rule manager for network statistics of a VM.
94 */
95@Component(immediate = true)
96@Service
97public class StatsFlowRuleManager implements StatsFlowRuleAdminService {
98
99 private final Logger log = LoggerFactory.getLogger(getClass());
100
101 private static final byte FLOW_TYPE_SONA = 1; // VLAN
102
Ray Milkeybcc53d32018-07-02 10:22:57 -0700103 private static final long MILLISECONDS = 1000L;
104 private static final long REFRESH_INTERVAL = 5L;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900105
Jian Li753280e2018-07-03 02:24:34 +0900106 private static final String REVERSE_PATH_STATS = "reversePathStats";
107 private static final String EGRESS_STATS = "egressStats";
108
109 private static final boolean DEFAULT_REVERSE_PATH_STATS = false;
110 private static final boolean DEFAULT_EGRESS_STATS = false;
111
112 private static final String MAC_NOT_NULL = "MAC should not be null";
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected CoreService coreService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected FlowRuleService flowRuleService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected HostService hostService;
122
Jian Li0bbbb1c2018-06-22 22:01:17 +0900123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lie6110b72018-07-06 19:06:36 +0900124 protected ComponentConfigService componentConfigService;
Jian Li0bbbb1c2018-06-22 22:01:17 +0900125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li85573f42018-06-27 22:29:14 +0900127 protected MastershipService mastershipService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li753280e2018-07-03 02:24:34 +0900130 protected OpenstackNetworkService osNetworkService;
131
132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li0bbbb1c2018-06-22 22:01:17 +0900133 protected OpenstackTelemetryService telemetryService;
134
Jian Li753280e2018-07-03 02:24:34 +0900135 @Property(name = REVERSE_PATH_STATS, boolValue = DEFAULT_REVERSE_PATH_STATS,
136 label = "A flag which indicates whether to install the rules for " +
137 "collecting the flow-based stats for reversed path.")
138 private boolean reversePathStats = DEFAULT_REVERSE_PATH_STATS;
139
140 @Property(name = EGRESS_STATS, boolValue = DEFAULT_EGRESS_STATS,
141 label = "A flag which indicates whether to install the rules for " +
142 "collecting the flow-based stats for egress port.")
143 private boolean egressStats = DEFAULT_EGRESS_STATS;
144
145 private ApplicationId appId;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900146 private Timer timer;
147 private TimerTask task;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900148
Jian Li0bbbb1c2018-06-22 22:01:17 +0900149 private final Set<FlowInfo> gFlowInfoSet = Sets.newHashSet();
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900150 private int loopCount = 0;
151
152 private static final int SOURCE_ID = 1;
153 private static final int TARGET_ID = 2;
154 private static final int PRIORITY_BASE = 10000;
155 private static final int METRIC_PRIORITY_SOURCE = SOURCE_ID * PRIORITY_BASE;
156 private static final int METRIC_PRIORITY_TARGET = TARGET_ID * PRIORITY_BASE;
157
Jian Li0bbbb1c2018-06-22 22:01:17 +0900158 private static final MacAddress NO_HOST_MAC = MacAddress.valueOf("00:00:00:00:00:00");
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900159
160 public StatsFlowRuleManager() {
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900161 this.timer = new Timer("openstack-telemetry-sender");
162 }
163
164 @Activate
165 protected void activate() {
166 appId = coreService.registerApplication(OPENSTACK_TELEMETRY_APP_ID);
Jian Li0bbbb1c2018-06-22 22:01:17 +0900167
Jian Lie6110b72018-07-06 19:06:36 +0900168 componentConfigService.registerProperties(getClass());
169
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900170 this.start();
Jian Li0bbbb1c2018-06-22 22:01:17 +0900171
172 log.info("Started");
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900173 }
174
175 @Deactivate
176 protected void deactivate() {
Jian Lie6110b72018-07-06 19:06:36 +0900177
178 componentConfigService.unregisterProperties(getClass(), false);
179
Jian Li0bbbb1c2018-06-22 22:01:17 +0900180 log.info("Stopped");
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900181 }
182
Jian Li753280e2018-07-03 02:24:34 +0900183 @Modified
184 protected void modified(ComponentContext context) {
185 readComponentConfiguration(context);
186
187 log.info("Modified");
188 }
189
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900190 @Override
191 public void start() {
192 log.info("Start publishing thread");
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900193 task = new InternalTimerTask();
194 timer.scheduleAtFixedRate(task, MILLISECONDS * REFRESH_INTERVAL,
Jian Li753280e2018-07-03 02:24:34 +0900195 MILLISECONDS * REFRESH_INTERVAL);
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900196 }
197
198 @Override
199 public void stop() {
200 log.info("Stop data publishing thread");
201 task.cancel();
202 task = null;
203 }
204
Jian Li0bbbb1c2018-06-22 22:01:17 +0900205 @Override
206 public void createStatFlowRule(StatsFlowRule statsFlowRule) {
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900207
Jian Li0bbbb1c2018-06-22 22:01:17 +0900208 setStatFlowRule(statsFlowRule, true);
Jian Li0bbbb1c2018-06-22 22:01:17 +0900209 }
210
211 @Override
212 public void deleteStatFlowRule(StatsFlowRule statsFlowRule) {
213 // FIXME: following code might not be necessary
214 flowRuleService.removeFlowRulesById(appId);
215
216 setStatFlowRule(statsFlowRule, false);
Jian Li0bbbb1c2018-06-22 22:01:17 +0900217 }
218
219 private void connectTables(DeviceId deviceId, int fromTable, int toTable,
220 StatsFlowRule statsFlowRule, int rulePriority,
221 boolean install) {
222
223 log.debug("Table Transition: {} -> {}", fromTable, toTable);
224 int srcPrefixLength = statsFlowRule.srcIpPrefix().prefixLength();
225 int dstPrefixLength = statsFlowRule.dstIpPrefix().prefixLength();
226 int prefixLength = rulePriority + srcPrefixLength + dstPrefixLength;
227 byte protocol = statsFlowRule.ipProtocol();
228
229 TrafficSelector.Builder selectorBuilder =
Jian Li753280e2018-07-03 02:24:34 +0900230 DefaultTrafficSelector.builder()
231 .matchEthType(TYPE_IPV4)
232 .matchIPSrc(statsFlowRule.srcIpPrefix())
233 .matchIPDst(statsFlowRule.dstIpPrefix());
Jian Li0bbbb1c2018-06-22 22:01:17 +0900234
235 if (protocol == PROTOCOL_TCP) {
236 selectorBuilder = selectorBuilder
Jian Li753280e2018-07-03 02:24:34 +0900237 .matchIPProtocol(statsFlowRule.ipProtocol())
238 .matchTcpSrc(statsFlowRule.srcTpPort())
239 .matchTcpDst(statsFlowRule.dstTpPort());
Jian Li0bbbb1c2018-06-22 22:01:17 +0900240
241 } else if (protocol == PROTOCOL_UDP) {
242 selectorBuilder = selectorBuilder
Jian Li753280e2018-07-03 02:24:34 +0900243 .matchIPProtocol(statsFlowRule.ipProtocol())
244 .matchUdpSrc(statsFlowRule.srcTpPort())
245 .matchUdpDst(statsFlowRule.dstTpPort());
Jian Li0bbbb1c2018-06-22 22:01:17 +0900246 } else {
247 log.warn("Unsupported protocol {}", statsFlowRule.ipProtocol());
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900248 }
Jian Li0bbbb1c2018-06-22 22:01:17 +0900249
250 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
251
252 treatmentBuilder.transition(toTable);
253
254 FlowRule flowRule = DefaultFlowRule.builder()
Jian Li753280e2018-07-03 02:24:34 +0900255 .forDevice(deviceId)
256 .withSelector(selectorBuilder.build())
257 .withTreatment(treatmentBuilder.build())
258 .withPriority(prefixLength)
259 .fromApp(appId)
260 .makePermanent()
261 .forTable(fromTable)
262 .build();
Jian Li0bbbb1c2018-06-22 22:01:17 +0900263
264 applyRule(flowRule, install);
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900265 }
266
267 /**
Jian Li0bbbb1c2018-06-22 22:01:17 +0900268 * Installs stats related flow rule to switch.
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900269 *
Jian Li0bbbb1c2018-06-22 22:01:17 +0900270 * @param flowRule flow rule
271 * @param install flag to install or not
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900272 */
273 private void applyRule(FlowRule flowRule, boolean install) {
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900274 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
Jian Li0bbbb1c2018-06-22 22:01:17 +0900275 flowOpsBuilder = install ?
276 flowOpsBuilder.add(flowRule) : flowOpsBuilder.remove(flowRule);
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900277
278 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
279 @Override
280 public void onSuccess(FlowRuleOperations ops) {
281 log.debug("Provisioned vni or forwarding table: \n {}", ops.toString());
282 }
283
284 @Override
285 public void onError(FlowRuleOperations ops) {
286 log.debug("Failed to provision vni or forwarding table: \n {}", ops.toString());
287 }
288 }));
289 }
290
291 /**
Jian Li0bbbb1c2018-06-22 22:01:17 +0900292 * Gets a set of the flow infos.
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900293 *
Jian Li0bbbb1c2018-06-22 22:01:17 +0900294 * @return a set of flow infos
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900295 */
Jian Li0bbbb1c2018-06-22 22:01:17 +0900296 public Set<FlowInfo> getFlowInfo() {
297 Set<FlowInfo> flowInfos = Sets.newConcurrentHashSet();
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900298
Jian Li0bbbb1c2018-06-22 22:01:17 +0900299 // obtain all flow rule entries installed by telemetry app
300 for (FlowEntry entry : flowRuleService.getFlowEntriesById(appId)) {
301 FlowInfo.Builder fBuilder = new DefaultFlowInfo.DefaultBuilder();
302 TrafficSelector selector = entry.selector();
303
304 IPCriterion srcIp = (IPCriterion) selector.getCriterion(IPV4_SRC);
305 IPCriterion dstIp = (IPCriterion) selector.getCriterion(IPV4_DST);
306 IPProtocolCriterion ipProtocol =
Jian Li753280e2018-07-03 02:24:34 +0900307 (IPProtocolCriterion) selector.getCriterion(IP_PROTO);
Jian Li0bbbb1c2018-06-22 22:01:17 +0900308
309 log.debug("[FlowInfo] TableID:{} SRC_IP:{} DST_IP:{} Pkt:{} Byte:{}",
Jian Li753280e2018-07-03 02:24:34 +0900310 ((IndexTableId) entry.table()).id(),
311 srcIp.ip().toString(),
312 dstIp.ip().toString(),
313 entry.packets(),
314 entry.bytes());
Jian Li0bbbb1c2018-06-22 22:01:17 +0900315
316 fBuilder.withFlowType(FLOW_TYPE_SONA)
317 .withSrcIp(srcIp.ip())
Jian Li85573f42018-06-27 22:29:14 +0900318 .withDstIp(dstIp.ip());
Jian Li0bbbb1c2018-06-22 22:01:17 +0900319
Jian Li85573f42018-06-27 22:29:14 +0900320 if (ipProtocol != null) {
321 fBuilder.withProtocol((byte) ipProtocol.protocol());
Jian Li0bbbb1c2018-06-22 22:01:17 +0900322
Jian Li85573f42018-06-27 22:29:14 +0900323 if (ipProtocol.protocol() == PROTOCOL_TCP) {
324 TcpPortCriterion tcpSrc =
325 (TcpPortCriterion) selector.getCriterion(TCP_SRC);
326 TcpPortCriterion tcpDst =
327 (TcpPortCriterion) selector.getCriterion(TCP_DST);
Jian Li0bbbb1c2018-06-22 22:01:17 +0900328
Jian Li85573f42018-06-27 22:29:14 +0900329 log.debug("TCP SRC Port: {}, DST Port: {}",
330 tcpSrc.tcpPort().toInt(),
331 tcpDst.tcpPort().toInt());
Jian Li0bbbb1c2018-06-22 22:01:17 +0900332
Jian Li85573f42018-06-27 22:29:14 +0900333 fBuilder.withSrcPort(tcpSrc.tcpPort());
334 fBuilder.withDstPort(tcpDst.tcpPort());
Jian Li0bbbb1c2018-06-22 22:01:17 +0900335
Jian Li85573f42018-06-27 22:29:14 +0900336 } else if (ipProtocol.protocol() == PROTOCOL_UDP) {
Jian Li0bbbb1c2018-06-22 22:01:17 +0900337
Jian Li85573f42018-06-27 22:29:14 +0900338 UdpPortCriterion udpSrc =
339 (UdpPortCriterion) selector.getCriterion(UDP_SRC);
340 UdpPortCriterion udpDst =
341 (UdpPortCriterion) selector.getCriterion(UDP_DST);
Jian Li0bbbb1c2018-06-22 22:01:17 +0900342
Jian Li85573f42018-06-27 22:29:14 +0900343 log.debug("UDP SRC Port: {}, DST Port: {}",
344 udpSrc.udpPort().toInt(),
345 udpDst.udpPort().toInt());
346
347 fBuilder.withSrcPort(udpSrc.udpPort());
348 fBuilder.withDstPort(udpDst.udpPort());
349 } else {
350 log.debug("Other protocol: {}", ipProtocol.protocol());
351 }
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900352 }
Jian Li0bbbb1c2018-06-22 22:01:17 +0900353
354 fBuilder.withSrcMac(getMacAddress(srcIp.ip().address()))
355 .withDstMac(getMacAddress(dstIp.ip().address()))
356 .withInputInterfaceId(getInterfaceId(srcIp.ip().address()))
357 .withOutputInterfaceId(getInterfaceId(dstIp.ip().address()))
358 .withVlanId(getVlanId(srcIp.ip().address()))
359 .withDeviceId(entry.deviceId());
360
361 StatsInfo.Builder sBuilder = new DefaultStatsInfo.DefaultBuilder();
362
363 // TODO: need to collect error and drop packets stats
364 // TODO: need to make the refresh interval configurable
Jian Lide4ef402018-06-27 19:21:14 +0900365 sBuilder.withStartupTime(System.currentTimeMillis())
366 .withFstPktArrTime(System.currentTimeMillis())
Ray Milkeybcc53d32018-07-02 10:22:57 -0700367 .withLstPktOffset((int) (REFRESH_INTERVAL * MILLISECONDS))
Jian Li0bbbb1c2018-06-22 22:01:17 +0900368 .withCurrAccPkts((int) entry.packets())
369 .withCurrAccBytes(entry.bytes())
370 .withErrorPkts((short) 0)
Jian Lide4ef402018-06-27 19:21:14 +0900371 .withDropPkts((short) 0);
Jian Li0bbbb1c2018-06-22 22:01:17 +0900372
373 fBuilder.withStatsInfo(sBuilder.build());
374
375 FlowInfo flowInfo = mergeFlowInfo(fBuilder.build(), fBuilder, sBuilder);
376
377 flowInfos.add(flowInfo);
378
379 log.debug("FlowInfo: \n{}", flowInfo.toString());
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900380 }
Jian Li0bbbb1c2018-06-22 22:01:17 +0900381
382 return flowInfos;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900383 }
384
385 /**
Jian Li0bbbb1c2018-06-22 22:01:17 +0900386 * Merges old FlowInfo.StatsInfo and current FlowInfo.StatsInfo.
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900387 *
388 * @param flowInfo current FlowInfo object
389 * @param fBuilder Builder for FlowInfo
390 * @param sBuilder Builder for StatsInfo
391 * @return Merged FlowInfo object
392 */
393 private FlowInfo mergeFlowInfo(FlowInfo flowInfo,
394 FlowInfo.Builder fBuilder,
395 StatsInfo.Builder sBuilder) {
Jian Li0bbbb1c2018-06-22 22:01:17 +0900396 for (FlowInfo gFlowInfo : gFlowInfoSet) {
397 log.debug("Old FlowInfo:\n{}", gFlowInfo.toString());
398 if (gFlowInfo.roughEquals(flowInfo)) {
399
400 // Get old StatsInfo object and merge the value to current object.
401 StatsInfo oldStatsInfo = gFlowInfo.statsInfo();
402 sBuilder.withPrevAccPkts(oldStatsInfo.currAccPkts());
403 sBuilder.withPrevAccBytes(oldStatsInfo.currAccBytes());
404 FlowInfo newFlowInfo = fBuilder.withStatsInfo(sBuilder.build())
405 .build();
406
407 gFlowInfoSet.remove(gFlowInfo);
408 gFlowInfoSet.add(newFlowInfo);
Jian Li85573f42018-06-27 22:29:14 +0900409 log.debug("Old FlowInfo found, Merge this {}", newFlowInfo.toString());
Jian Li0bbbb1c2018-06-22 22:01:17 +0900410 return newFlowInfo;
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900411 }
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900412 }
Jian Li0bbbb1c2018-06-22 22:01:17 +0900413
414 // No such record, then build the FlowInfo object and return this object.
Jian Li85573f42018-06-27 22:29:14 +0900415 log.debug("No FlowInfo found, add new FlowInfo {}", flowInfo.toString());
Jian Li0bbbb1c2018-06-22 22:01:17 +0900416 FlowInfo newFlowInfo = fBuilder.withStatsInfo(sBuilder.build()).build();
417 gFlowInfoSet.add(newFlowInfo);
418 return newFlowInfo;
419 }
420
Jian Li753280e2018-07-03 02:24:34 +0900421 /**
422 * Installs flow rules for collecting both normal and reverse path flow stats.
423 *
424 * @param statsFlowRule flow rule used for collecting stats
425 * @param install flow rule installation flag
426 */
Jian Li0bbbb1c2018-06-22 22:01:17 +0900427 private void setStatFlowRule(StatsFlowRule statsFlowRule, boolean install) {
Jian Li753280e2018-07-03 02:24:34 +0900428 setStatFlowRuleBase(statsFlowRule, install);
Jian Li0bbbb1c2018-06-22 22:01:17 +0900429
Jian Li753280e2018-07-03 02:24:34 +0900430 // if reverse path stats is enabled, we will install flow rules for
431 // collecting reverse path vFlow stats
432 if (reversePathStats) {
433 StatsFlowRule reverseFlowRule = DefaultStatsFlowRule.builder()
434 .srcIpPrefix(statsFlowRule.dstIpPrefix())
435 .dstIpPrefix(statsFlowRule.srcIpPrefix())
436 .ipProtocol(statsFlowRule.ipProtocol())
437 .srcTpPort(statsFlowRule.dstTpPort())
438 .dstTpPort(statsFlowRule.srcTpPort())
439 .build();
440 setStatFlowRuleBase(reverseFlowRule, install);
441 }
442 }
443
444 /**
445 * A base method which is for installing flow rules for collecting stats.
446 *
447 * @param statsFlowRule flow rule used for collecting stats
448 * @param install flow rule installation flag
449 */
450 private void setStatFlowRuleBase(StatsFlowRule statsFlowRule, boolean install) {
Jian Lie6110b72018-07-06 19:06:36 +0900451
452 IpPrefix srcIp = statsFlowRule.srcIpPrefix();
453 IpPrefix dstIp = statsFlowRule.dstIpPrefix();
454 DeviceId srcDeviceId = getDeviceId(srcIp.address());
455 DeviceId dstDeviceId = getDeviceId(dstIp.address());
Jian Li0bbbb1c2018-06-22 22:01:17 +0900456
Jian Li998ec7b2018-06-29 15:15:49 +0900457 if (srcDeviceId == null && dstDeviceId == null) {
Jian Li85573f42018-06-27 22:29:14 +0900458 return;
459 }
460
Jian Li998ec7b2018-06-29 15:15:49 +0900461 if (srcDeviceId != null) {
Jian Li87ded822018-07-02 18:31:22 +0900462 connectTables(srcDeviceId, STAT_INBOUND_TABLE, VTAP_INBOUND_TABLE,
Jian Li998ec7b2018-06-29 15:15:49 +0900463 statsFlowRule, METRIC_PRIORITY_SOURCE, install);
Jian Li998ec7b2018-06-29 15:15:49 +0900464
Jian Lie6110b72018-07-06 19:06:36 +0900465 if (install) {
466 log.info("Install ingress stat flow rule for SrcIp:{} DstIp:{}",
467 srcIp.toString(), dstIp.toString());
468 } else {
469 log.info("Remove ingress stat flow rule for SrcIp:{} DstIp:{}",
470 srcIp.toString(), dstIp.toString());
Jian Li753280e2018-07-03 02:24:34 +0900471 }
Jian Li998ec7b2018-06-29 15:15:49 +0900472 }
Jian Li85573f42018-06-27 22:29:14 +0900473
Jian Lie6110b72018-07-06 19:06:36 +0900474 Set<IpPrefix> vxlanIps = osNetworkService.getFixedIpsByNetworkType(VXLAN);
475 Set<IpPrefix> vlanIps = osNetworkService.getFixedIpsByNetworkType(VLAN);
476 Set<IpPrefix> flatIps = osNetworkService.getFixedIpsByNetworkType(FLAT);
Jian Li753280e2018-07-03 02:24:34 +0900477
Jian Lie6110b72018-07-06 19:06:36 +0900478 int fromTable, toTable;
Jian Li753280e2018-07-03 02:24:34 +0900479
Jian Lie6110b72018-07-06 19:06:36 +0900480 if (dstDeviceId != null && egressStats) {
481
482 IpPrefix dstIpPrefix = statsFlowRule.dstIpPrefix();
483
484 if (vxlanIps.contains(dstIpPrefix) || vlanIps.contains(dstIpPrefix)) {
485 fromTable = STAT_OUTBOUND_TABLE;
486 toTable = VTAP_OUTBOUND_TABLE;
487 } else if (flatIps.contains(dstIpPrefix)) {
488 fromTable = STAT_FLAT_OUTBOUND_TABLE;
489 toTable = VTAP_FLAT_OUTBOUND_TABLE;
490 } else {
491 return;
492 }
493
494 connectTables(dstDeviceId, fromTable, toTable,
495 statsFlowRule, METRIC_PRIORITY_TARGET, install);
496
497 if (install) {
498 log.info("Install egress stat flow rule for SrcIp:{} DstIp:{}",
499 srcIp.toString(), dstIp.toString());
500 } else {
501 log.info("Remove egress stat flow rule for SrcIp:{} DstIp:{}",
502 srcIp.toString(), dstIp.toString());
503 }
Jian Li753280e2018-07-03 02:24:34 +0900504 }
Jian Li753280e2018-07-03 02:24:34 +0900505 }
506
507 /**
Jian Li85573f42018-06-27 22:29:14 +0900508 * Get Device ID which the VM is located.
509 *
510 * @param ipAddress IP Address of host
511 * @return Device ID
512 */
513 private DeviceId getDeviceId(IpAddress ipAddress) {
514 if (!hostService.getHostsByIp(ipAddress).isEmpty()) {
515 Optional<Host> host = hostService.getHostsByIp(ipAddress).stream().findAny();
516 return host.map(host1 -> host1.location().deviceId()).orElse(null);
517 } else {
Jian Li998ec7b2018-06-29 15:15:49 +0900518 log.warn("Failed to get DeviceID which is connected to {}. " +
519 "The destination is either a bare-metal or located out of DC",
Jian Li85573f42018-06-27 22:29:14 +0900520 ipAddress.toString());
521 return null;
Jian Li0bbbb1c2018-06-22 22:01:17 +0900522 }
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900523 }
524
525 /**
526 * Get VLAN ID with respect to IP Address.
527 *
528 * @param ipAddress IP Address of host
529 * @return VLAN ID
530 */
Jian Li0bbbb1c2018-06-22 22:01:17 +0900531 private VlanId getVlanId(IpAddress ipAddress) {
532 if (!hostService.getHostsByIp(ipAddress).isEmpty()) {
533 Host host = hostService.getHostsByIp(ipAddress).stream().findAny().get();
534 return host.vlan();
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900535 }
536 return VlanId.vlanId();
537 }
538
539 /**
540 * Get Interface ID of Switch which is connected to a host.
541 *
542 * @param ipAddress IP Address of host
543 * @return Interface ID of Switch
544 */
Jian Li0bbbb1c2018-06-22 22:01:17 +0900545 private int getInterfaceId(IpAddress ipAddress) {
546 if (!hostService.getHostsByIp(ipAddress).isEmpty()) {
547 Host host = hostService.getHostsByIp(ipAddress).stream().findAny().get();
548 return (int) host.location().port().toLong();
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900549 }
550 return -1;
551 }
552
553 /**
554 * Get MAC Address of host.
555 *
556 * @param ipAddress IP Address of host
557 * @return MAC Address of host
558 */
Jian Li0bbbb1c2018-06-22 22:01:17 +0900559 private MacAddress getMacAddress(IpAddress ipAddress) {
560 if (!hostService.getHostsByIp(ipAddress).isEmpty()) {
561 Host host = hostService.getHostsByIp(ipAddress).stream().findAny().get();
562 return host.mac();
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900563 }
Jian Li0bbbb1c2018-06-22 22:01:17 +0900564
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900565 return NO_HOST_MAC;
566 }
Jian Li0bbbb1c2018-06-22 22:01:17 +0900567
Jian Li753280e2018-07-03 02:24:34 +0900568 /**
569 * Extracts properties from the component configuration context.
570 *
571 * @param context the component context
572 */
573 private void readComponentConfiguration(ComponentContext context) {
574 Dictionary<?, ?> properties = context.getProperties();
575
576 Boolean reversePathStatsConfigured =
577 getBooleanProperty(properties, REVERSE_PATH_STATS);
578 if (reversePathStatsConfigured == null) {
579 reversePathStats = DEFAULT_REVERSE_PATH_STATS;
580 log.info("Reversed path stats flag is NOT " +
581 "configured, default value is {}", reversePathStats);
582 } else {
583 reversePathStats = reversePathStatsConfigured;
584 log.info("Configured. Reversed path stats flag is {}", reversePathStats);
585 }
586
587 Boolean egressStatsConfigured = getBooleanProperty(properties, EGRESS_STATS);
588 if (egressStatsConfigured == null) {
589 egressStats = DEFAULT_EGRESS_STATS;
590 log.info("Egress stats flag is NOT " +
591 "configured, default value is {}", egressStats);
592 } else {
593 egressStats = egressStatsConfigured;
594 log.info("Configured. Egress stats flag is {}", egressStats);
595 }
596 }
597
Jian Li0bbbb1c2018-06-22 22:01:17 +0900598 private class InternalTimerTask extends TimerTask {
599 @Override
600 public void run() {
Jian Li753280e2018-07-03 02:24:34 +0900601 log.debug("Timer task thread starts ({})", loopCount++);
Jian Li85573f42018-06-27 22:29:14 +0900602
603 Set<FlowInfo> filteredFlowInfos = Sets.newConcurrentHashSet();
604
605 // we only let the master controller of the device where the
606 // stats flow rules are installed send kafka message
607 getFlowInfo().forEach(f -> {
608 DeviceId deviceId = getDeviceId(f.srcIp().address());
609 if (mastershipService.isLocalMaster(deviceId)) {
610 filteredFlowInfos.add(f);
611 }
612 });
613
Jian Li0bbbb1c2018-06-22 22:01:17 +0900614 try {
Jian Li85573f42018-06-27 22:29:14 +0900615 telemetryService.publish(filteredFlowInfos);
Jian Li0bbbb1c2018-06-22 22:01:17 +0900616 } catch (Exception ex) {
617 log.error("Exception Stack:\n{}", ExceptionUtils.getStackTrace(ex));
618 }
619 }
620 }
Boyoung Jeong9e8faec2018-06-17 21:19:23 +0900621}