blob: 8c8b39877ab8cae1fa3ae5a46ee1a42dceef7cfb [file] [log] [blame]
Thomas Vachuskaa8e74772018-02-26 11:33:35 -08001/*
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 */
16
17package org.onosproject.routescale;
18
19import com.google.common.collect.ImmutableList;
20import com.google.common.collect.ImmutableSet;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080021import org.onlab.packet.Ethernet;
22import org.onlab.packet.IpAddress;
23import org.onlab.packet.IpPrefix;
24import org.onlab.packet.MacAddress;
25import org.onosproject.app.ApplicationService;
Thomas Vachuskafa615492018-03-19 09:11:51 -070026import org.onosproject.cfg.ComponentConfigService;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080027import org.onosproject.core.ApplicationId;
28import org.onosproject.net.Device;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.Host;
Thomas Vachuskafa615492018-03-19 09:11:51 -070031import org.onosproject.net.MastershipRole;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080032import org.onosproject.net.PortNumber;
33import org.onosproject.net.device.DeviceService;
34import org.onosproject.net.flow.DefaultFlowRule;
35import org.onosproject.net.flow.DefaultTrafficSelector;
36import org.onosproject.net.flow.DefaultTrafficTreatment;
Thomas Vachuskafa615492018-03-19 09:11:51 -070037import org.onosproject.net.flow.FlowEntry;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080038import org.onosproject.net.flow.FlowRule;
39import org.onosproject.net.flow.FlowRuleOperations;
40import org.onosproject.net.flow.FlowRuleService;
41import org.onosproject.net.flow.TrafficSelector;
42import org.onosproject.net.flow.TrafficTreatment;
43import org.onosproject.net.host.HostAdminService;
Thomas Vachuskafa615492018-03-19 09:11:51 -070044import org.onosproject.net.link.LinkService;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080045import org.onosproject.routeservice.Route;
46import org.onosproject.routeservice.RouteAdminService;
Thomas Vachuskafa615492018-03-19 09:11:51 -070047import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070048import org.osgi.service.component.annotations.Activate;
49import org.osgi.service.component.annotations.Component;
50import org.osgi.service.component.annotations.Deactivate;
51import org.osgi.service.component.annotations.Modified;
52import org.osgi.service.component.annotations.Reference;
53import org.osgi.service.component.annotations.ReferenceCardinality;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080054import org.slf4j.Logger;
55import org.slf4j.LoggerFactory;
56
Thomas Vachuskafa615492018-03-19 09:11:51 -070057import java.util.Dictionary;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080058import java.util.List;
Thomas Vachuskafa615492018-03-19 09:11:51 -070059import java.util.Objects;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080060import java.util.Random;
61
Thomas Vachuskafa615492018-03-19 09:11:51 -070062import static com.google.common.base.Strings.isNullOrEmpty;
63import static org.onlab.util.Tools.get;
Ray Milkey88dd7e22018-10-24 10:04:03 -070064import static org.onosproject.routescale.OsgiPropertyConstants.FLOW_COUNT;
65import static org.onosproject.routescale.OsgiPropertyConstants.FLOW_COUNT_DEFAULT;
66import static org.onosproject.routescale.OsgiPropertyConstants.ROUTE_COUNT;
67import static org.onosproject.routescale.OsgiPropertyConstants.ROUTE_COUNT_DEFAULT;
Thomas Vachuskafa615492018-03-19 09:11:51 -070068
Ray Milkey88dd7e22018-10-24 10:04:03 -070069@Component(
70 immediate = true,
71 service = ScaleTestManager.class,
72 property = {
73 FLOW_COUNT + ":Integer=" + FLOW_COUNT_DEFAULT,
74 ROUTE_COUNT + ":Integer=" + ROUTE_COUNT_DEFAULT,
75 }
76)
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080077public class ScaleTestManager {
78
79 private Logger log = LoggerFactory.getLogger(getClass());
80
Ray Milkeyd84f89b2018-08-17 14:54:17 -070081 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080082 protected ApplicationService applicationService;
83
Ray Milkeyd84f89b2018-08-17 14:54:17 -070084 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080085 protected DeviceService deviceService;
86
Ray Milkeyd84f89b2018-08-17 14:54:17 -070087 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080088 protected HostAdminService hostAdminService;
89
Ray Milkeyd84f89b2018-08-17 14:54:17 -070090 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskafa615492018-03-19 09:11:51 -070091 protected LinkService linkService;
92
Ray Milkeyd84f89b2018-08-17 14:54:17 -070093 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080094 protected FlowRuleService flowRuleService;
95
Ray Milkeyd84f89b2018-08-17 14:54:17 -070096 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080097 protected RouteAdminService routeAdminService;
98
Ray Milkeyd84f89b2018-08-17 14:54:17 -070099 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskafa615492018-03-19 09:11:51 -0700100 protected ComponentConfigService componentConfigService;
101
Ray Milkey88dd7e22018-10-24 10:04:03 -0700102 /** Number of flows to be maintained in the system. */
103 private int flowCount = FLOW_COUNT_DEFAULT;
Thomas Vachuskafa615492018-03-19 09:11:51 -0700104
Ray Milkey88dd7e22018-10-24 10:04:03 -0700105 /** Number of routes to be maintained in the system. */
106 private int routeCount = ROUTE_COUNT_DEFAULT;
Thomas Vachuskafa615492018-03-19 09:11:51 -0700107
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800108 private final Random random = new Random(System.currentTimeMillis());
109
110 private ApplicationId appId;
111
Thomas Vachuska6205f692018-03-21 12:22:04 -0700112 private long macBase = System.currentTimeMillis();
113
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800114 @Activate
115 protected void activate() {
116 appId = applicationService.getId("org.onosproject.routescale");
Thomas Vachuskafa615492018-03-19 09:11:51 -0700117 componentConfigService.registerProperties(getClass());
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800118 log.info("Started");
119 }
120
121 @Deactivate
122 protected void deactivate() {
Thomas Vachuskafa615492018-03-19 09:11:51 -0700123 componentConfigService.unregisterProperties(getClass(), false);
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800124 log.info("Stopped");
125 }
126
Thomas Vachuskafa615492018-03-19 09:11:51 -0700127 @Modified
128 public void modified(ComponentContext context) {
129 if (context == null) {
130 return;
131 }
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800132
Thomas Vachuskafa615492018-03-19 09:11:51 -0700133 Dictionary<?, ?> properties = context.getProperties();
134 try {
Ray Milkey88dd7e22018-10-24 10:04:03 -0700135 String s = get(properties, FLOW_COUNT);
Thomas Vachuskafa615492018-03-19 09:11:51 -0700136 flowCount = isNullOrEmpty(s) ? flowCount : Integer.parseInt(s.trim());
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800137
Ray Milkey88dd7e22018-10-24 10:04:03 -0700138 s = get(properties, ROUTE_COUNT);
Thomas Vachuskafa615492018-03-19 09:11:51 -0700139 routeCount = isNullOrEmpty(s) ? routeCount : Integer.parseInt(s.trim());
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800140
Thomas Vachuskafa615492018-03-19 09:11:51 -0700141 log.info("Reconfigured; flowCount={}; routeCount={}", flowCount, routeCount);
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800142
Thomas Vachuskafa615492018-03-19 09:11:51 -0700143 adjustFlows();
144 adjustRoutes();
145
146 } catch (NumberFormatException | ClassCastException e) {
147 log.warn("Misconfigured", e);
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800148 }
149 }
150
Thomas Vachuskafa615492018-03-19 09:11:51 -0700151 private void adjustFlows() {
152 int deviceCount = deviceService.getAvailableDeviceCount();
153 if (deviceCount == 0) {
154 return;
155 }
156
157 int flowsPerDevice = flowCount / deviceCount;
158 for (Device device : deviceService.getAvailableDevices()) {
159 DeviceId id = device.id();
160 if (deviceService.getRole(id) != MastershipRole.MASTER ||
161 flowsPerDevice == 0) {
162 continue;
163 }
164
165 int currentFlowCount = flowRuleService.getFlowRuleCount(id);
166 if (flowsPerDevice > currentFlowCount) {
167 addMoreFlows(flowsPerDevice, device, id, currentFlowCount);
168
169 } else if (flowsPerDevice < currentFlowCount) {
170 removeExcessFlows(flowsPerDevice, id, currentFlowCount);
171 }
172 }
173 }
174
175 private void addMoreFlows(int flowsPerDevice, Device device, DeviceId id,
176 int currentFlowCount) {
177 int c = flowsPerDevice - currentFlowCount;
178 log.info("Adding {} flows for device {}", c, id);
179 List<PortNumber> ports = devicePorts(device);
180 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
181 for (int i = 0; i < c; i++) {
182 FlowRule.Builder frb = DefaultFlowRule.builder();
Thomas Vachuska6205f692018-03-21 12:22:04 -0700183 frb.fromApp(appId).makePermanent().withPriority((currentFlowCount + i) % FlowRule.MAX_PRIORITY);
Thomas Vachuskafa615492018-03-19 09:11:51 -0700184 TrafficSelector.Builder tsb = DefaultTrafficSelector.builder();
185 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
186
187 tsb.matchEthType(Ethernet.TYPE_IPV4);
Thomas Vachuska6205f692018-03-21 12:22:04 -0700188 tsb.matchEthDst(randomMac());
Thomas Vachuskafa615492018-03-19 09:11:51 -0700189 ttb.setEthDst(randomMac()).setEthSrc(randomMac());
190 ttb.setOutput(randomPort(ports));
191 frb.withSelector(tsb.build()).withTreatment(ttb.build());
192 ops.add(frb.forDevice(id).build());
193 }
194 flowRuleService.apply(ops.build());
195 }
196
197 private void removeExcessFlows(int flowsPerDevice, DeviceId id,
198 int currentFlowCount) {
199 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Thomas Vachuska6205f692018-03-21 12:22:04 -0700200 int c = currentFlowCount - flowsPerDevice;
Thomas Vachuskafa615492018-03-19 09:11:51 -0700201 log.info("Removing {} flows from device {}", c, id);
202 for (FlowEntry e : flowRuleService.getFlowEntries(id)) {
203 if (Objects.equals(e.appId(), appId.id()) && c > 0) {
204 ops.remove(e);
205 c--;
Thomas Vachuska6205f692018-03-21 12:22:04 -0700206 } else if (c == 0) {
207 break;
Thomas Vachuskafa615492018-03-19 09:11:51 -0700208 }
209 }
210 flowRuleService.apply(ops.build());
211 }
212
213 private void adjustRoutes() {
214 int currentRouteCount =
215 routeAdminService.getRouteTables().parallelStream()
216 .mapToInt(t -> routeAdminService.getRoutes(t).size()).sum();
217 if (currentRouteCount < routeCount) {
218 createRoutes(routeCount - currentRouteCount);
219 } else if (currentRouteCount > routeCount) {
220 removeRoutes(currentRouteCount - routeCount);
221 }
222 }
223
224 // Returns a list of ports on the given device that have either links or
225 // hosts connected to them.
226 private List<PortNumber> devicePorts(Device device) {
227 DeviceId id = device.id();
228 ImmutableList.Builder<PortNumber> ports = ImmutableList.builder();
229 linkService.getDeviceEgressLinks(id).forEach(l -> ports.add(l.src().port()));
230 hostAdminService.getConnectedHosts(id)
231 .forEach(h -> h.locations().stream()
232 .filter(l -> Objects.equals(id, l.elementId()))
233 .findFirst()
234 .ifPresent(l -> ports.add(l.port())));
235 return ports.build();
236 }
237
238 // Creates the specified number of random routes. Such routes are generated
239 // using random IP prefices with next hop being an IP address of a randomly
240 // chosen hosts.
241 private void createRoutes(int routeCount) {
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800242 List<Host> hosts = ImmutableList.copyOf(hostAdminService.getHosts());
243 ImmutableSet.Builder<Route> routes = ImmutableSet.builder();
244 for (int i = 0; i < routeCount; i++) {
245 IpPrefix prefix = randomIp().toIpPrefix();
246 IpAddress nextHop = randomIp(hosts);
247 routes.add(new Route(Route.Source.STATIC, prefix, nextHop));
248 }
249 routeAdminService.update(routes.build());
250 }
251
Thomas Vachuskafa615492018-03-19 09:11:51 -0700252 // Removes the specified number of routes chosen at random.
253 private void removeRoutes(int routeCount) {
254 log.warn("Not implemented yet");
255 }
256
257 // Generates a random IP address.
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800258 private IpAddress randomIp() {
259 byte[] bytes = new byte[4];
260 random.nextBytes(bytes);
261 return IpAddress.valueOf(IpAddress.Version.INET, bytes, 0);
262 }
263
Thomas Vachuskafa615492018-03-19 09:11:51 -0700264 // Generates a random MAC address.
265 private MacAddress randomMac() {
Thomas Vachuska6205f692018-03-21 12:22:04 -0700266 return MacAddress.valueOf(macBase++);
Thomas Vachuskafa615492018-03-19 09:11:51 -0700267 }
268
269 // Returns IP address of a host randomly chosen from the specified list.
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800270 private IpAddress randomIp(List<Host> hosts) {
271 Host host = hosts.get(random.nextInt(hosts.size()));
272 return host.ipAddresses().iterator().next();
273 }
274
Thomas Vachuskafa615492018-03-19 09:11:51 -0700275 // Returns port number randomly chosen from the given list of port numbers.
276 private PortNumber randomPort(List<PortNumber> ports) {
277 return ports.get(random.nextInt(ports.size()));
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800278 }
279
280}