blob: d24a4b6e760cbb7191d89ef33f8e5464f0bebeb8 [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;
64
Ray Milkeyd84f89b2018-08-17 14:54:17 -070065@Component(immediate = true, service = ScaleTestManager.class)
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080066public class ScaleTestManager {
67
68 private Logger log = LoggerFactory.getLogger(getClass());
69
Ray Milkeyd84f89b2018-08-17 14:54:17 -070070 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080071 protected ApplicationService applicationService;
72
Ray Milkeyd84f89b2018-08-17 14:54:17 -070073 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080074 protected DeviceService deviceService;
75
Ray Milkeyd84f89b2018-08-17 14:54:17 -070076 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080077 protected HostAdminService hostAdminService;
78
Ray Milkeyd84f89b2018-08-17 14:54:17 -070079 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskafa615492018-03-19 09:11:51 -070080 protected LinkService linkService;
81
Ray Milkeyd84f89b2018-08-17 14:54:17 -070082 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080083 protected FlowRuleService flowRuleService;
84
Ray Milkeyd84f89b2018-08-17 14:54:17 -070085 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080086 protected RouteAdminService routeAdminService;
87
Ray Milkeyd84f89b2018-08-17 14:54:17 -070088 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskafa615492018-03-19 09:11:51 -070089 protected ComponentConfigService componentConfigService;
90
Ray Milkeyd84f89b2018-08-17 14:54:17 -070091 //@Property(name = "flowCount", intValue = 0,
92 // label = "Number of flows to be maintained in the system")
Thomas Vachuskafa615492018-03-19 09:11:51 -070093 private int flowCount = 0;
94
Ray Milkeyd84f89b2018-08-17 14:54:17 -070095 //@Property(name = "routeCount", intValue = 0,
96 // label = "Number of routes to be maintained in the system")
Thomas Vachuskafa615492018-03-19 09:11:51 -070097 private int routeCount = 0;
98
Thomas Vachuskaa8e74772018-02-26 11:33:35 -080099 private final Random random = new Random(System.currentTimeMillis());
100
101 private ApplicationId appId;
102
Thomas Vachuska6205f692018-03-21 12:22:04 -0700103 private long macBase = System.currentTimeMillis();
104
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800105 @Activate
106 protected void activate() {
107 appId = applicationService.getId("org.onosproject.routescale");
Thomas Vachuskafa615492018-03-19 09:11:51 -0700108 componentConfigService.registerProperties(getClass());
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800109 log.info("Started");
110 }
111
112 @Deactivate
113 protected void deactivate() {
Thomas Vachuskafa615492018-03-19 09:11:51 -0700114 componentConfigService.unregisterProperties(getClass(), false);
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800115 log.info("Stopped");
116 }
117
Thomas Vachuskafa615492018-03-19 09:11:51 -0700118 @Modified
119 public void modified(ComponentContext context) {
120 if (context == null) {
121 return;
122 }
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800123
Thomas Vachuskafa615492018-03-19 09:11:51 -0700124 Dictionary<?, ?> properties = context.getProperties();
125 try {
126 String s = get(properties, "flowCount");
127 flowCount = isNullOrEmpty(s) ? flowCount : Integer.parseInt(s.trim());
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800128
Thomas Vachuskafa615492018-03-19 09:11:51 -0700129 s = get(properties, "routeCount");
130 routeCount = isNullOrEmpty(s) ? routeCount : Integer.parseInt(s.trim());
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800131
Thomas Vachuskafa615492018-03-19 09:11:51 -0700132 log.info("Reconfigured; flowCount={}; routeCount={}", flowCount, routeCount);
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800133
Thomas Vachuskafa615492018-03-19 09:11:51 -0700134 adjustFlows();
135 adjustRoutes();
136
137 } catch (NumberFormatException | ClassCastException e) {
138 log.warn("Misconfigured", e);
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800139 }
140 }
141
Thomas Vachuskafa615492018-03-19 09:11:51 -0700142 private void adjustFlows() {
143 int deviceCount = deviceService.getAvailableDeviceCount();
144 if (deviceCount == 0) {
145 return;
146 }
147
148 int flowsPerDevice = flowCount / deviceCount;
149 for (Device device : deviceService.getAvailableDevices()) {
150 DeviceId id = device.id();
151 if (deviceService.getRole(id) != MastershipRole.MASTER ||
152 flowsPerDevice == 0) {
153 continue;
154 }
155
156 int currentFlowCount = flowRuleService.getFlowRuleCount(id);
157 if (flowsPerDevice > currentFlowCount) {
158 addMoreFlows(flowsPerDevice, device, id, currentFlowCount);
159
160 } else if (flowsPerDevice < currentFlowCount) {
161 removeExcessFlows(flowsPerDevice, id, currentFlowCount);
162 }
163 }
164 }
165
166 private void addMoreFlows(int flowsPerDevice, Device device, DeviceId id,
167 int currentFlowCount) {
168 int c = flowsPerDevice - currentFlowCount;
169 log.info("Adding {} flows for device {}", c, id);
170 List<PortNumber> ports = devicePorts(device);
171 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
172 for (int i = 0; i < c; i++) {
173 FlowRule.Builder frb = DefaultFlowRule.builder();
Thomas Vachuska6205f692018-03-21 12:22:04 -0700174 frb.fromApp(appId).makePermanent().withPriority((currentFlowCount + i) % FlowRule.MAX_PRIORITY);
Thomas Vachuskafa615492018-03-19 09:11:51 -0700175 TrafficSelector.Builder tsb = DefaultTrafficSelector.builder();
176 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
177
178 tsb.matchEthType(Ethernet.TYPE_IPV4);
Thomas Vachuska6205f692018-03-21 12:22:04 -0700179 tsb.matchEthDst(randomMac());
Thomas Vachuskafa615492018-03-19 09:11:51 -0700180 ttb.setEthDst(randomMac()).setEthSrc(randomMac());
181 ttb.setOutput(randomPort(ports));
182 frb.withSelector(tsb.build()).withTreatment(ttb.build());
183 ops.add(frb.forDevice(id).build());
184 }
185 flowRuleService.apply(ops.build());
186 }
187
188 private void removeExcessFlows(int flowsPerDevice, DeviceId id,
189 int currentFlowCount) {
190 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Thomas Vachuska6205f692018-03-21 12:22:04 -0700191 int c = currentFlowCount - flowsPerDevice;
Thomas Vachuskafa615492018-03-19 09:11:51 -0700192 log.info("Removing {} flows from device {}", c, id);
193 for (FlowEntry e : flowRuleService.getFlowEntries(id)) {
194 if (Objects.equals(e.appId(), appId.id()) && c > 0) {
195 ops.remove(e);
196 c--;
Thomas Vachuska6205f692018-03-21 12:22:04 -0700197 } else if (c == 0) {
198 break;
Thomas Vachuskafa615492018-03-19 09:11:51 -0700199 }
200 }
201 flowRuleService.apply(ops.build());
202 }
203
204 private void adjustRoutes() {
205 int currentRouteCount =
206 routeAdminService.getRouteTables().parallelStream()
207 .mapToInt(t -> routeAdminService.getRoutes(t).size()).sum();
208 if (currentRouteCount < routeCount) {
209 createRoutes(routeCount - currentRouteCount);
210 } else if (currentRouteCount > routeCount) {
211 removeRoutes(currentRouteCount - routeCount);
212 }
213 }
214
215 // Returns a list of ports on the given device that have either links or
216 // hosts connected to them.
217 private List<PortNumber> devicePorts(Device device) {
218 DeviceId id = device.id();
219 ImmutableList.Builder<PortNumber> ports = ImmutableList.builder();
220 linkService.getDeviceEgressLinks(id).forEach(l -> ports.add(l.src().port()));
221 hostAdminService.getConnectedHosts(id)
222 .forEach(h -> h.locations().stream()
223 .filter(l -> Objects.equals(id, l.elementId()))
224 .findFirst()
225 .ifPresent(l -> ports.add(l.port())));
226 return ports.build();
227 }
228
229 // Creates the specified number of random routes. Such routes are generated
230 // using random IP prefices with next hop being an IP address of a randomly
231 // chosen hosts.
232 private void createRoutes(int routeCount) {
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800233 List<Host> hosts = ImmutableList.copyOf(hostAdminService.getHosts());
234 ImmutableSet.Builder<Route> routes = ImmutableSet.builder();
235 for (int i = 0; i < routeCount; i++) {
236 IpPrefix prefix = randomIp().toIpPrefix();
237 IpAddress nextHop = randomIp(hosts);
238 routes.add(new Route(Route.Source.STATIC, prefix, nextHop));
239 }
240 routeAdminService.update(routes.build());
241 }
242
Thomas Vachuskafa615492018-03-19 09:11:51 -0700243 // Removes the specified number of routes chosen at random.
244 private void removeRoutes(int routeCount) {
245 log.warn("Not implemented yet");
246 }
247
248 // Generates a random IP address.
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800249 private IpAddress randomIp() {
250 byte[] bytes = new byte[4];
251 random.nextBytes(bytes);
252 return IpAddress.valueOf(IpAddress.Version.INET, bytes, 0);
253 }
254
Thomas Vachuskafa615492018-03-19 09:11:51 -0700255 // Generates a random MAC address.
256 private MacAddress randomMac() {
Thomas Vachuska6205f692018-03-21 12:22:04 -0700257 return MacAddress.valueOf(macBase++);
Thomas Vachuskafa615492018-03-19 09:11:51 -0700258 }
259
260 // Returns IP address of a host randomly chosen from the specified list.
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800261 private IpAddress randomIp(List<Host> hosts) {
262 Host host = hosts.get(random.nextInt(hosts.size()));
263 return host.ipAddresses().iterator().next();
264 }
265
Thomas Vachuskafa615492018-03-19 09:11:51 -0700266 // Returns port number randomly chosen from the given list of port numbers.
267 private PortNumber randomPort(List<PortNumber> ports) {
268 return ports.get(random.nextInt(ports.size()));
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800269 }
270
271}