blob: 5a19ed0b7ff2cfdc8711a53c4ec7f3b6bac6a938 [file] [log] [blame]
sanghoshin94872a12015-10-16 18:04:34 +09001/*
2 * Copyright 2015 Open Networking Laboratory
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.openstackswitching;
17
sanghoshin46297d22015-11-03 17:51:24 +090018import com.google.common.collect.ImmutableSet;
sanghoshin94872a12015-10-16 18:04:34 +090019import com.google.common.collect.Lists;
sanghoshin94872a12015-10-16 18:04:34 +090020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
26import org.onlab.packet.Ethernet;
sanghoshin94872a12015-10-16 18:04:34 +090027import org.onlab.packet.Ip4Address;
sanghoshin94872a12015-10-16 18:04:34 +090028import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
danielbb83ebc2015-10-29 15:13:06 +090030import org.onosproject.dhcp.DhcpService;
sanghoshin94872a12015-10-16 18:04:34 +090031import org.onosproject.net.Device;
32import org.onosproject.net.DeviceId;
33import org.onosproject.net.Port;
sanghoshin46297d22015-11-03 17:51:24 +090034import org.onosproject.net.config.ConfigFactory;
35import org.onosproject.net.config.NetworkConfigEvent;
36import org.onosproject.net.config.NetworkConfigListener;
37import org.onosproject.net.config.NetworkConfigRegistry;
sanghoshin94872a12015-10-16 18:04:34 +090038import org.onosproject.net.device.DeviceEvent;
39import org.onosproject.net.device.DeviceListener;
40import org.onosproject.net.device.DeviceService;
sanghoshinf25d2e02015-11-11 23:07:17 +090041import org.onosproject.net.driver.DriverService;
sanghoshin94872a12015-10-16 18:04:34 +090042import org.onosproject.net.flowobjective.FlowObjectiveService;
43import org.onosproject.net.packet.InboundPacket;
44import org.onosproject.net.packet.PacketContext;
45import org.onosproject.net.packet.PacketProcessor;
46import org.onosproject.net.packet.PacketService;
47import org.slf4j.Logger;
48import org.slf4j.LoggerFactory;
sanghoshin94872a12015-10-16 18:04:34 +090049import java.util.List;
sanghoshin46297d22015-11-03 17:51:24 +090050import java.util.Collection;
51import java.util.Set;
sanghoshin94872a12015-10-16 18:04:34 +090052import java.util.concurrent.ExecutorService;
53import java.util.concurrent.Executors;
sanghoshin46297d22015-11-03 17:51:24 +090054import java.util.stream.Collectors;
55
56import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
sanghoshin94872a12015-10-16 18:04:34 +090057
58@SuppressWarnings("ALL")
59@Service
60@Component(immediate = true)
61/**
sanghoshin46297d22015-11-03 17:51:24 +090062 * Populates forwarding rules for VMs created by Openstack.
sanghoshin94872a12015-10-16 18:04:34 +090063 */
64public class OpenstackSwitchingManager implements OpenstackSwitchingService {
65
66 private static Logger log = LoggerFactory
67 .getLogger(OpenstackSwitchingManager.class);
68
69 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 protected CoreService coreService;
71
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected PacketService packetService;
74
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected DeviceService deviceService;
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected FlowObjectiveService flowObjectiveService;
80
danielbb83ebc2015-10-29 15:13:06 +090081 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sanghoshinf25d2e02015-11-11 23:07:17 +090082 protected DhcpService dhcpService;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sanghoshin46297d22015-11-03 17:51:24 +090085 protected NetworkConfigRegistry cfgService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sanghoshinf25d2e02015-11-11 23:07:17 +090088 protected DriverService driverService;
sanghoshin94872a12015-10-16 18:04:34 +090089
90 private ApplicationId appId;
sanghoshin46297d22015-11-03 17:51:24 +090091 private boolean doNotPushFlows;
sanghoshinf25d2e02015-11-11 23:07:17 +090092 private Ip4Address neutronServer;
93 private Ip4Address keystoneServer;
94 private String userName;
95 private String password;
sanghoshin94872a12015-10-16 18:04:34 +090096 private OpenstackArpHandler arpHandler;
sanghoshinf25d2e02015-11-11 23:07:17 +090097 private OpenstackRestHandler restHandler;
danielbb83ebc2015-10-29 15:13:06 +090098
sanghoshin94872a12015-10-16 18:04:34 +090099 private ExecutorService deviceEventExcutorService = Executors.newFixedThreadPool(10);
100
101 private InternalPacketProcessor internalPacketProcessor = new InternalPacketProcessor();
102 private InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
sanghoshin46297d22015-11-03 17:51:24 +0900103 private InternalConfigListener internalConfigListener = new InternalConfigListener();
104 private final Set<ConfigFactory> factories = ImmutableSet.of(
105 new ConfigFactory<ApplicationId, OpenstackSwitchingConfig>(APP_SUBJECT_FACTORY,
106 OpenstackSwitchingConfig.class,
107 "openstackswitching") {
108 @Override
109 public OpenstackSwitchingConfig createConfig() {
110 return new OpenstackSwitchingConfig();
111 }
112 }
113 );
sanghoshin94872a12015-10-16 18:04:34 +0900114
115 @Activate
116 protected void activate() {
117 appId = coreService
118 .registerApplication("org.onosproject.openstackswitching");
sanghoshinf25d2e02015-11-11 23:07:17 +0900119
120 factories.forEach(cfgService::registerConfigFactory);
sanghoshin94872a12015-10-16 18:04:34 +0900121 packetService.addProcessor(internalPacketProcessor, PacketProcessor.director(1));
122 deviceService.addListener(internalDeviceListener);
sanghoshin46297d22015-11-03 17:51:24 +0900123 cfgService.addListener(internalConfigListener);
danielbb83ebc2015-10-29 15:13:06 +0900124
sanghoshinf25d2e02015-11-11 23:07:17 +0900125 internalConfigListener.configureNetwork();
sanghoshin46297d22015-11-03 17:51:24 +0900126
sanghoshin94872a12015-10-16 18:04:34 +0900127 log.info("Started");
128 }
129
130 @Deactivate
131 protected void deactivate() {
132 packetService.removeProcessor(internalPacketProcessor);
133 deviceService.removeListener(internalDeviceListener);
sanghoshin46297d22015-11-03 17:51:24 +0900134 cfgService.removeListener(internalConfigListener);
sanghoshin94872a12015-10-16 18:04:34 +0900135
136 deviceEventExcutorService.shutdown();
137
138 log.info("Stopped");
139 }
140
141 @Override
142 public void createPorts(OpenstackPort openstackPort) {
sanghoshin46297d22015-11-03 17:51:24 +0900143 registerDhcpInfo(openstackPort);
sanghoshin94872a12015-10-16 18:04:34 +0900144 }
145
146 @Override
147 public void deletePorts() {
148
149 }
150
151 @Override
152 public void updatePorts() {
153
154 }
155
156 @Override
157 public void createNetwork(OpenstackNetwork openstackNetwork) {
sanghoshin94872a12015-10-16 18:04:34 +0900158 }
159
danielbb83ebc2015-10-29 15:13:06 +0900160 @Override
161 public void createSubnet(OpenstackSubnet openstackSubnet) {
danielbb83ebc2015-10-29 15:13:06 +0900162 }
163
sanghoshin46297d22015-11-03 17:51:24 +0900164 @Override
165 public Collection<OpenstackPort> ports(String networkId) {
sanghoshinf25d2e02015-11-11 23:07:17 +0900166 Collection<OpenstackPort> ports = restHandler.getPorts();
167 List<OpenstackPort> portList = ports.stream()
sanghoshin46297d22015-11-03 17:51:24 +0900168 .filter(p -> p.networkId().equals(networkId))
169 .collect(Collectors.toList());
170
171 return portList;
172 }
173
174 @Override
sanghoshinf25d2e02015-11-11 23:07:17 +0900175 public OpenstackPort port(Port port) {
176 Collection<OpenstackPort> ports = restHandler.getPorts();
177 String uuid = port.annotations().value("portName").substring(3);
178 return ports.stream()
sanghoshin46297d22015-11-03 17:51:24 +0900179 .filter(p -> p.id().startsWith(uuid))
sanghoshinf25d2e02015-11-11 23:07:17 +0900180 .findFirst().orElse(null);
181 }
182
183 @Override
184 public OpenstackPort port(String portId) {
185 Collection<OpenstackPort> ports = restHandler.getPorts();
186 return ports.stream()
187 .filter(p -> p.id().equals(portId))
188 .findFirst().orElse(null);
sanghoshin46297d22015-11-03 17:51:24 +0900189 }
190
191 @Override
192 public OpenstackNetwork network(String networkId) {
sanghoshinf25d2e02015-11-11 23:07:17 +0900193 Collection<OpenstackNetwork> networks = restHandler.getNetworks();
194 return networks.stream()
195 .filter(n -> n.id().equals(networkId))
196 .findFirst().orElse(null);
sanghoshin46297d22015-11-03 17:51:24 +0900197 }
198
sanghoshin94872a12015-10-16 18:04:34 +0900199 private void processDeviceAdded(Device device) {
danielbb83ebc2015-10-29 15:13:06 +0900200 log.debug("device {} is added", device.id());
sanghoshin94872a12015-10-16 18:04:34 +0900201 }
202
203 private void processPortAdded(Device device, Port port) {
sanghoshinf25d2e02015-11-11 23:07:17 +0900204 if (!port.annotations().value("portName").equals("vxlan")) {
205 OpenstackSwitchingRulePopulator rulePopulator =
206 new OpenstackSwitchingRulePopulator(appId, flowObjectiveService,
207 deviceService, restHandler, driverService);
208 rulePopulator.populateSwitchingRules(device, port);
sanghoshin94872a12015-10-16 18:04:34 +0900209 }
210 }
211
212 private void processPortRemoved(Device device, Port port) {
sanghoshinf25d2e02015-11-11 23:07:17 +0900213 // TODO: Remove flow rules for the VM removed
danielbb83ebc2015-10-29 15:13:06 +0900214 log.debug("port {} is removed", port.toString());
sanghoshin94872a12015-10-16 18:04:34 +0900215 }
216
sanghoshin075e3e72015-11-25 16:34:29 +0900217 private void initializeFlowRules() {
218 OpenstackSwitchingRulePopulator rulePopulator =
219 new OpenstackSwitchingRulePopulator(appId, flowObjectiveService,
220 deviceService, restHandler, driverService);
221
222 deviceService.getDevices().forEach(device -> {
223 log.debug("device {} num of ports {} ", device.id(),
224 deviceService.getPorts(device.id()).size());
225 deviceService.getPorts(device.id()).stream()
226 .filter(port -> port.annotations().value("portName").startsWith("tap"))
227 .forEach(vmPort -> {
228 OpenstackPort osPort = rulePopulator.openstackPort(vmPort);
229 if (osPort != null) {
230 rulePopulator.populateSwitchingRules(device, vmPort);
231 registerDhcpInfo(osPort);
232 } else {
233 log.warn("No openstackPort information for port {}", vmPort);
234 }
235 }
236 );
237 }
238 );
239 }
240
sanghoshin46297d22015-11-03 17:51:24 +0900241 private void registerDhcpInfo(OpenstackPort openstackPort) {
242 Ip4Address ip4Address;
243 Ip4Address subnetMask;
244 Ip4Address dhcpServer;
245 Ip4Address gatewayIPAddress;
246 Ip4Address domainServer;
247 OpenstackSubnet openstackSubnet;
248
249 ip4Address = (Ip4Address) openstackPort.fixedIps().values().toArray()[0];
250
sanghoshinf25d2e02015-11-11 23:07:17 +0900251 openstackSubnet = restHandler.getSubnets().stream()
sanghoshin46297d22015-11-03 17:51:24 +0900252 .filter(n -> n.networkId().equals(openstackPort.networkId()))
253 .findFirst().get();
254
255 subnetMask = Ip4Address.valueOf(buildSubnetMask(openstackSubnet.cidr()));
256 gatewayIPAddress = Ip4Address.valueOf(openstackSubnet.gatewayIp());
257 dhcpServer = gatewayIPAddress;
258 // TODO: supports multiple DNS servers
259 if (openstackSubnet.dnsNameservers().isEmpty()) {
260 domainServer = Ip4Address.valueOf("8.8.8.8");
261 } else {
262 domainServer = openstackSubnet.dnsNameservers().get(0);
263 }
264 List<Ip4Address> options = Lists.newArrayList();
265 options.add(subnetMask);
266 options.add(dhcpServer);
267 options.add(gatewayIPAddress);
268 options.add(domainServer);
269
270 dhcpService.setStaticMapping(openstackPort.macAddress(), ip4Address, true, options);
271 }
272
273 private byte[] buildSubnetMask(String cidr) {
274 int prefix;
275 String[] parts = cidr.split("/");
276 prefix = Integer.parseInt(parts[1]);
277 int mask = 0xffffffff << (32 - prefix);
278 byte[] bytes = new byte[]{(byte) (mask >>> 24),
279 (byte) (mask >> 16 & 0xff), (byte) (mask >> 8 & 0xff), (byte) (mask & 0xff)};
280
281 return bytes;
282 }
283
sanghoshin94872a12015-10-16 18:04:34 +0900284
sanghoshin94872a12015-10-16 18:04:34 +0900285
286 private class InternalPacketProcessor implements PacketProcessor {
287
288 @Override
289 public void process(PacketContext context) {
290
291 if (context.isHandled()) {
292 return;
293 }
294
295 InboundPacket pkt = context.inPacket();
296 Ethernet ethernet = pkt.parsed();
297
298 if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
299 arpHandler.processPacketIn(pkt);
sanghoshin94872a12015-10-16 18:04:34 +0900300 }
301 }
302 }
303
304 private class InternalDeviceListener implements DeviceListener {
305
306 @Override
sanghoshin46297d22015-11-03 17:51:24 +0900307 public void event(DeviceEvent deviceEvent) {
308 deviceEventExcutorService.execute(new InternalEventHandler(deviceEvent));
sanghoshin94872a12015-10-16 18:04:34 +0900309 }
310 }
311
312 private class InternalEventHandler implements Runnable {
313
314 volatile DeviceEvent deviceEvent;
315
316 InternalEventHandler(DeviceEvent deviceEvent) {
317 this.deviceEvent = deviceEvent;
318 }
319
320 @Override
321 public void run() {
sanghoshin46297d22015-11-03 17:51:24 +0900322
323 if (doNotPushFlows) {
324 return;
325 }
326
sanghoshin94872a12015-10-16 18:04:34 +0900327 switch (deviceEvent.type()) {
328 case DEVICE_ADDED:
329 processDeviceAdded((Device) deviceEvent.subject());
330 break;
331 case DEVICE_UPDATED:
332 Port port = (Port) deviceEvent.subject();
333 if (port.isEnabled()) {
334 processPortAdded((Device) deviceEvent.subject(), deviceEvent.port());
335 }
336 break;
337 case DEVICE_AVAILABILITY_CHANGED:
338 Device device = (Device) deviceEvent.subject();
339 if (deviceService.isAvailable(device.id())) {
340 processDeviceAdded(device);
341 }
342 break;
343 case PORT_ADDED:
344 processPortAdded((Device) deviceEvent.subject(), deviceEvent.port());
345 break;
346 case PORT_UPDATED:
347 processPortAdded((Device) deviceEvent.subject(), deviceEvent.port());
348 break;
349 case PORT_REMOVED:
350 processPortRemoved((Device) deviceEvent.subject(), deviceEvent.port());
351 break;
352 default:
353 break;
354 }
355 }
356 }
357
sanghoshin46297d22015-11-03 17:51:24 +0900358 private class InternalConfigListener implements NetworkConfigListener {
359
sanghoshinf25d2e02015-11-11 23:07:17 +0900360 public void configureNetwork() {
361 OpenstackSwitchingConfig cfg =
362 cfgService.getConfig(appId, OpenstackSwitchingConfig.class);
363 if (cfg == null) {
364 log.error("There is no openstack server information in config.");
365 return;
366 }
367 doNotPushFlows = cfg.doNotPushFlows();
368 restHandler = new OpenstackRestHandler(cfg);
369 arpHandler = new OpenstackArpHandler(restHandler, packetService);
sanghoshin075e3e72015-11-25 16:34:29 +0900370 initializeFlowRules();
sanghoshinf25d2e02015-11-11 23:07:17 +0900371 }
372
sanghoshin46297d22015-11-03 17:51:24 +0900373 @Override
374 public void event(NetworkConfigEvent event) {
375 if (((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
376 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)) &&
377 event.configClass().equals(OpenstackSwitchingConfig.class)) {
sanghoshinf25d2e02015-11-11 23:07:17 +0900378 configureNetwork();
sanghoshin46297d22015-11-03 17:51:24 +0900379 }
380 }
sanghoshinf25d2e02015-11-11 23:07:17 +0900381
382 }
sanghoshin46297d22015-11-03 17:51:24 +0900383
sanghoshin94872a12015-10-16 18:04:34 +0900384 private final class PortInfo {
385 DeviceId deviceId;
386 String portName;
387 Ip4Address fixedIp;
388 Ip4Address hostIp;
389
390 private PortInfo(DeviceId deviceId, String portName, Ip4Address fixedIp,
391 Ip4Address hostIp) {
392 this.deviceId = deviceId;
393 this.portName = portName;
394 this.fixedIp = fixedIp;
395 this.hostIp = hostIp;
396 }
397 }
398
399}