blob: 25550f83f7db403800a3846ed6ffd686752bb5c8 [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
106 @Activate
107 protected void activate() {
108 appId = applicationService.getId("org.onosproject.routescale");
Thomas Vachuskafa615492018-03-19 09:11:51 -0700109 componentConfigService.registerProperties(getClass());
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800110 log.info("Started");
111 }
112
113 @Deactivate
114 protected void deactivate() {
Thomas Vachuskafa615492018-03-19 09:11:51 -0700115 componentConfigService.unregisterProperties(getClass(), false);
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800116 log.info("Stopped");
117 }
118
Thomas Vachuskafa615492018-03-19 09:11:51 -0700119 @Modified
120 public void modified(ComponentContext context) {
121 if (context == null) {
122 return;
123 }
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800124
Thomas Vachuskafa615492018-03-19 09:11:51 -0700125 Dictionary<?, ?> properties = context.getProperties();
126 try {
127 String s = get(properties, "flowCount");
128 flowCount = isNullOrEmpty(s) ? flowCount : Integer.parseInt(s.trim());
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800129
Thomas Vachuskafa615492018-03-19 09:11:51 -0700130 s = get(properties, "routeCount");
131 routeCount = isNullOrEmpty(s) ? routeCount : Integer.parseInt(s.trim());
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800132
Thomas Vachuskafa615492018-03-19 09:11:51 -0700133 log.info("Reconfigured; flowCount={}; routeCount={}", flowCount, routeCount);
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800134
Thomas Vachuskafa615492018-03-19 09:11:51 -0700135 adjustFlows();
136 adjustRoutes();
137
138 } catch (NumberFormatException | ClassCastException e) {
139 log.warn("Misconfigured", e);
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800140 }
141 }
142
Thomas Vachuskafa615492018-03-19 09:11:51 -0700143 private void adjustFlows() {
144 int deviceCount = deviceService.getAvailableDeviceCount();
145 if (deviceCount == 0) {
146 return;
147 }
148
149 int flowsPerDevice = flowCount / deviceCount;
150 for (Device device : deviceService.getAvailableDevices()) {
151 DeviceId id = device.id();
152 if (deviceService.getRole(id) != MastershipRole.MASTER ||
153 flowsPerDevice == 0) {
154 continue;
155 }
156
157 int currentFlowCount = flowRuleService.getFlowRuleCount(id);
158 if (flowsPerDevice > currentFlowCount) {
159 addMoreFlows(flowsPerDevice, device, id, currentFlowCount);
160
161 } else if (flowsPerDevice < currentFlowCount) {
162 removeExcessFlows(flowsPerDevice, id, currentFlowCount);
163 }
164 }
165 }
166
167 private void addMoreFlows(int flowsPerDevice, Device device, DeviceId id,
168 int currentFlowCount) {
169 int c = flowsPerDevice - currentFlowCount;
170 log.info("Adding {} flows for device {}", c, id);
171 List<PortNumber> ports = devicePorts(device);
172 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
173 for (int i = 0; i < c; i++) {
174 FlowRule.Builder frb = DefaultFlowRule.builder();
175 frb.fromApp(appId).makePermanent().withPriority(1000 + i);
176 TrafficSelector.Builder tsb = DefaultTrafficSelector.builder();
177 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
178
179 tsb.matchEthType(Ethernet.TYPE_IPV4);
180 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();
191 int c = flowsPerDevice - currentFlowCount;
192 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--;
197 }
198 }
199 flowRuleService.apply(ops.build());
200 }
201
202 private void adjustRoutes() {
203 int currentRouteCount =
204 routeAdminService.getRouteTables().parallelStream()
205 .mapToInt(t -> routeAdminService.getRoutes(t).size()).sum();
206 if (currentRouteCount < routeCount) {
207 createRoutes(routeCount - currentRouteCount);
208 } else if (currentRouteCount > routeCount) {
209 removeRoutes(currentRouteCount - routeCount);
210 }
211 }
212
213 // Returns a list of ports on the given device that have either links or
214 // hosts connected to them.
215 private List<PortNumber> devicePorts(Device device) {
216 DeviceId id = device.id();
217 ImmutableList.Builder<PortNumber> ports = ImmutableList.builder();
218 linkService.getDeviceEgressLinks(id).forEach(l -> ports.add(l.src().port()));
219 hostAdminService.getConnectedHosts(id)
220 .forEach(h -> h.locations().stream()
221 .filter(l -> Objects.equals(id, l.elementId()))
222 .findFirst()
223 .ifPresent(l -> ports.add(l.port())));
224 return ports.build();
225 }
226
227 // Creates the specified number of random routes. Such routes are generated
228 // using random IP prefices with next hop being an IP address of a randomly
229 // chosen hosts.
230 private void createRoutes(int routeCount) {
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800231 List<Host> hosts = ImmutableList.copyOf(hostAdminService.getHosts());
232 ImmutableSet.Builder<Route> routes = ImmutableSet.builder();
233 for (int i = 0; i < routeCount; i++) {
234 IpPrefix prefix = randomIp().toIpPrefix();
235 IpAddress nextHop = randomIp(hosts);
236 routes.add(new Route(Route.Source.STATIC, prefix, nextHop));
237 }
238 routeAdminService.update(routes.build());
239 }
240
Thomas Vachuskafa615492018-03-19 09:11:51 -0700241 // Removes the specified number of routes chosen at random.
242 private void removeRoutes(int routeCount) {
243 log.warn("Not implemented yet");
244 }
245
246 // Generates a random IP address.
Thomas Vachuskaa8e74772018-02-26 11:33:35 -0800247 private IpAddress randomIp() {
248 byte[] bytes = new byte[4];
249 random.nextBytes(bytes);
250 return IpAddress.valueOf(IpAddress.Version.INET, bytes, 0);
251 }
252
Thomas Vachuskafa615492018-03-19 09:11:51 -0700253 // Generates a random MAC address.
254 private MacAddress randomMac() {
255 byte[] bytes = new byte[6];
256 random.nextBytes(bytes);
257 return MacAddress.valueOf(bytes);
258 }
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}