blob: cfc7ad621d3b3be23da2838d1cafa38fce742fa0 [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;
21import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuskafa615492018-03-19 09:11:51 -070024import org.apache.felix.scr.annotations.Modified;
25import org.apache.felix.scr.annotations.Property;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080026import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
28import org.apache.felix.scr.annotations.Service;
29import org.onlab.packet.Ethernet;
30import org.onlab.packet.IpAddress;
31import org.onlab.packet.IpPrefix;
32import org.onlab.packet.MacAddress;
33import org.onosproject.app.ApplicationService;
Thomas Vachuskafa615492018-03-19 09:11:51 -070034import org.onosproject.cfg.ComponentConfigService;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080035import org.onosproject.core.ApplicationId;
36import org.onosproject.net.Device;
37import org.onosproject.net.DeviceId;
38import org.onosproject.net.Host;
Thomas Vachuskafa615492018-03-19 09:11:51 -070039import org.onosproject.net.MastershipRole;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080040import org.onosproject.net.PortNumber;
41import org.onosproject.net.device.DeviceService;
42import org.onosproject.net.flow.DefaultFlowRule;
43import org.onosproject.net.flow.DefaultTrafficSelector;
44import org.onosproject.net.flow.DefaultTrafficTreatment;
Thomas Vachuskafa615492018-03-19 09:11:51 -070045import org.onosproject.net.flow.FlowEntry;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080046import org.onosproject.net.flow.FlowRule;
47import org.onosproject.net.flow.FlowRuleOperations;
48import org.onosproject.net.flow.FlowRuleService;
49import org.onosproject.net.flow.TrafficSelector;
50import org.onosproject.net.flow.TrafficTreatment;
51import org.onosproject.net.host.HostAdminService;
Thomas Vachuskafa615492018-03-19 09:11:51 -070052import org.onosproject.net.link.LinkService;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080053import org.onosproject.routeservice.Route;
54import org.onosproject.routeservice.RouteAdminService;
Thomas Vachuskafa615492018-03-19 09:11:51 -070055import org.osgi.service.component.ComponentContext;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080056import org.slf4j.Logger;
57import org.slf4j.LoggerFactory;
58
Thomas Vachuskafa615492018-03-19 09:11:51 -070059import java.util.Dictionary;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080060import java.util.List;
Thomas Vachuskafa615492018-03-19 09:11:51 -070061import java.util.Objects;
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080062import java.util.Random;
63
Thomas Vachuskafa615492018-03-19 09:11:51 -070064import static com.google.common.base.Strings.isNullOrEmpty;
65import static org.onlab.util.Tools.get;
66
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080067@Component(immediate = true)
68@Service(value = ScaleTestManager.class)
69public class ScaleTestManager {
70
71 private Logger log = LoggerFactory.getLogger(getClass());
72
73 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74 protected ApplicationService applicationService;
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected DeviceService deviceService;
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected HostAdminService hostAdminService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Thomas Vachuskafa615492018-03-19 09:11:51 -070083 protected LinkService linkService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080086 protected FlowRuleService flowRuleService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected RouteAdminService routeAdminService;
90
Thomas Vachuskafa615492018-03-19 09:11:51 -070091 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected ComponentConfigService componentConfigService;
93
94 @Property(name = "flowCount", intValue = 0,
95 label = "Number of flows to be maintained in the system")
96 private int flowCount = 0;
97
98 @Property(name = "routeCount", intValue = 0,
99 label = "Number of routes to be maintained in the system")
100 private int routeCount = 0;
101
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800102 private final Random random = new Random(System.currentTimeMillis());
103
104 private ApplicationId appId;
105
Thomas Vachuska6205f692018-03-21 12:22:04 -0700106 private long macBase = System.currentTimeMillis();
107
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800108 @Activate
109 protected void activate() {
110 appId = applicationService.getId("org.onosproject.routescale");
Thomas Vachuskafa615492018-03-19 09:11:51 -0700111 componentConfigService.registerProperties(getClass());
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800112 log.info("Started");
113 }
114
115 @Deactivate
116 protected void deactivate() {
Thomas Vachuskafa615492018-03-19 09:11:51 -0700117 componentConfigService.unregisterProperties(getClass(), false);
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800118 log.info("Stopped");
119 }
120
Thomas Vachuskafa615492018-03-19 09:11:51 -0700121 @Modified
122 public void modified(ComponentContext context) {
123 if (context == null) {
124 return;
125 }
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800126
Thomas Vachuskafa615492018-03-19 09:11:51 -0700127 Dictionary<?, ?> properties = context.getProperties();
128 try {
129 String s = get(properties, "flowCount");
130 flowCount = isNullOrEmpty(s) ? flowCount : Integer.parseInt(s.trim());
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800131
Thomas Vachuskafa615492018-03-19 09:11:51 -0700132 s = get(properties, "routeCount");
133 routeCount = isNullOrEmpty(s) ? routeCount : Integer.parseInt(s.trim());
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800134
Thomas Vachuskafa615492018-03-19 09:11:51 -0700135 log.info("Reconfigured; flowCount={}; routeCount={}", flowCount, routeCount);
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800136
Thomas Vachuskafa615492018-03-19 09:11:51 -0700137 adjustFlows();
138 adjustRoutes();
139
140 } catch (NumberFormatException | ClassCastException e) {
141 log.warn("Misconfigured", e);
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800142 }
143 }
144
Thomas Vachuskafa615492018-03-19 09:11:51 -0700145 private void adjustFlows() {
146 int deviceCount = deviceService.getAvailableDeviceCount();
147 if (deviceCount == 0) {
148 return;
149 }
150
151 int flowsPerDevice = flowCount / deviceCount;
152 for (Device device : deviceService.getAvailableDevices()) {
153 DeviceId id = device.id();
154 if (deviceService.getRole(id) != MastershipRole.MASTER ||
155 flowsPerDevice == 0) {
156 continue;
157 }
158
159 int currentFlowCount = flowRuleService.getFlowRuleCount(id);
160 if (flowsPerDevice > currentFlowCount) {
161 addMoreFlows(flowsPerDevice, device, id, currentFlowCount);
162
163 } else if (flowsPerDevice < currentFlowCount) {
164 removeExcessFlows(flowsPerDevice, id, currentFlowCount);
165 }
166 }
167 }
168
169 private void addMoreFlows(int flowsPerDevice, Device device, DeviceId id,
170 int currentFlowCount) {
171 int c = flowsPerDevice - currentFlowCount;
172 log.info("Adding {} flows for device {}", c, id);
173 List<PortNumber> ports = devicePorts(device);
174 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
175 for (int i = 0; i < c; i++) {
176 FlowRule.Builder frb = DefaultFlowRule.builder();
Thomas Vachuska6205f692018-03-21 12:22:04 -0700177 frb.fromApp(appId).makePermanent().withPriority((currentFlowCount + i) % FlowRule.MAX_PRIORITY);
Thomas Vachuskafa615492018-03-19 09:11:51 -0700178 TrafficSelector.Builder tsb = DefaultTrafficSelector.builder();
179 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
180
181 tsb.matchEthType(Ethernet.TYPE_IPV4);
Thomas Vachuska6205f692018-03-21 12:22:04 -0700182 tsb.matchEthDst(randomMac());
Thomas Vachuskafa615492018-03-19 09:11:51 -0700183 ttb.setEthDst(randomMac()).setEthSrc(randomMac());
184 ttb.setOutput(randomPort(ports));
185 frb.withSelector(tsb.build()).withTreatment(ttb.build());
186 ops.add(frb.forDevice(id).build());
187 }
188 flowRuleService.apply(ops.build());
189 }
190
191 private void removeExcessFlows(int flowsPerDevice, DeviceId id,
192 int currentFlowCount) {
193 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Thomas Vachuska6205f692018-03-21 12:22:04 -0700194 int c = currentFlowCount - flowsPerDevice;
Thomas Vachuskafa615492018-03-19 09:11:51 -0700195 log.info("Removing {} flows from device {}", c, id);
196 for (FlowEntry e : flowRuleService.getFlowEntries(id)) {
197 if (Objects.equals(e.appId(), appId.id()) && c > 0) {
198 ops.remove(e);
199 c--;
Thomas Vachuska6205f692018-03-21 12:22:04 -0700200 } else if (c == 0) {
201 break;
Thomas Vachuskafa615492018-03-19 09:11:51 -0700202 }
203 }
204 flowRuleService.apply(ops.build());
205 }
206
207 private void adjustRoutes() {
208 int currentRouteCount =
209 routeAdminService.getRouteTables().parallelStream()
210 .mapToInt(t -> routeAdminService.getRoutes(t).size()).sum();
211 if (currentRouteCount < routeCount) {
212 createRoutes(routeCount - currentRouteCount);
213 } else if (currentRouteCount > routeCount) {
214 removeRoutes(currentRouteCount - routeCount);
215 }
216 }
217
218 // Returns a list of ports on the given device that have either links or
219 // hosts connected to them.
220 private List<PortNumber> devicePorts(Device device) {
221 DeviceId id = device.id();
222 ImmutableList.Builder<PortNumber> ports = ImmutableList.builder();
223 linkService.getDeviceEgressLinks(id).forEach(l -> ports.add(l.src().port()));
224 hostAdminService.getConnectedHosts(id)
225 .forEach(h -> h.locations().stream()
226 .filter(l -> Objects.equals(id, l.elementId()))
227 .findFirst()
228 .ifPresent(l -> ports.add(l.port())));
229 return ports.build();
230 }
231
232 // Creates the specified number of random routes. Such routes are generated
233 // using random IP prefices with next hop being an IP address of a randomly
234 // chosen hosts.
235 private void createRoutes(int routeCount) {
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800236 List<Host> hosts = ImmutableList.copyOf(hostAdminService.getHosts());
237 ImmutableSet.Builder<Route> routes = ImmutableSet.builder();
238 for (int i = 0; i < routeCount; i++) {
239 IpPrefix prefix = randomIp().toIpPrefix();
240 IpAddress nextHop = randomIp(hosts);
241 routes.add(new Route(Route.Source.STATIC, prefix, nextHop));
242 }
243 routeAdminService.update(routes.build());
244 }
245
Thomas Vachuskafa615492018-03-19 09:11:51 -0700246 // Removes the specified number of routes chosen at random.
247 private void removeRoutes(int routeCount) {
248 log.warn("Not implemented yet");
249 }
250
251 // Generates a random IP address.
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800252 private IpAddress randomIp() {
253 byte[] bytes = new byte[4];
254 random.nextBytes(bytes);
255 return IpAddress.valueOf(IpAddress.Version.INET, bytes, 0);
256 }
257
Thomas Vachuskafa615492018-03-19 09:11:51 -0700258 // Generates a random MAC address.
259 private MacAddress randomMac() {
Thomas Vachuska6205f692018-03-21 12:22:04 -0700260 return MacAddress.valueOf(macBase++);
Thomas Vachuskafa615492018-03-19 09:11:51 -0700261 }
262
263 // Returns IP address of a host randomly chosen from the specified list.
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800264 private IpAddress randomIp(List<Host> hosts) {
265 Host host = hosts.get(random.nextInt(hosts.size()));
266 return host.ipAddresses().iterator().next();
267 }
268
Thomas Vachuskafa615492018-03-19 09:11:51 -0700269 // Returns port number randomly chosen from the given list of port numbers.
270 private PortNumber randomPort(List<PortNumber> ports) {
271 return ports.get(random.nextInt(ports.size()));
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800272 }
273
274}