blob: 55fce2c99b5d87d8106188cb15a711fce4c577c0 [file] [log] [blame]
Hyunsun Moonb974fca2016-06-30 21:20:39 -07001/*
2 * Copyright 2016-present 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.openstacknetworking.switching;
17
18import com.google.common.base.Strings;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070019import com.google.common.collect.Sets;
20import 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.Ip4Address;
27import org.onlab.packet.IpPrefix;
28import org.onlab.packet.VlanId;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070029import org.onosproject.core.CoreService;
30import org.onosproject.dhcp.DhcpService;
31import org.onosproject.dhcp.IpAssignment;
32import org.onosproject.mastership.MastershipService;
33import org.onosproject.net.ConnectPoint;
34import org.onosproject.net.DefaultAnnotations;
35import org.onosproject.net.Device;
36import org.onosproject.net.Host;
37import org.onosproject.net.HostId;
38import org.onosproject.net.HostLocation;
39import org.onosproject.net.Port;
40import org.onosproject.net.device.DeviceEvent;
41import org.onosproject.net.device.DeviceListener;
42import org.onosproject.net.device.DeviceService;
43import org.onosproject.net.host.DefaultHostDescription;
44import org.onosproject.net.host.HostDescription;
45import org.onosproject.net.host.HostProvider;
46import org.onosproject.net.host.HostProviderRegistry;
47import org.onosproject.net.host.HostProviderService;
48import org.onosproject.net.host.HostService;
49import org.onosproject.net.provider.AbstractProvider;
50import org.onosproject.net.provider.ProviderId;
51import org.onosproject.openstackinterface.OpenstackInterfaceService;
52import org.onosproject.openstackinterface.OpenstackNetwork;
53import org.onosproject.openstackinterface.OpenstackPort;
54import org.onosproject.openstackinterface.OpenstackSubnet;
Hyunsun Moon05d9b262016-07-03 18:38:44 -070055import org.onosproject.openstacknode.OpenstackNode;
56import org.onosproject.openstacknode.OpenstackNodeEvent;
57import org.onosproject.openstacknode.OpenstackNodeListener;
58import org.onosproject.openstacknode.OpenstackNodeService;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070059import org.slf4j.Logger;
60import org.slf4j.LoggerFactory;
61
62import java.util.Date;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070063import java.util.concurrent.ExecutorService;
64import java.util.concurrent.Executors;
65
66import static org.onlab.util.Tools.groupedThreads;
67import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
68
69import static com.google.common.base.Preconditions.checkArgument;
70import static com.google.common.base.Preconditions.checkNotNull;
71import static org.onosproject.net.AnnotationKeys.PORT_NAME;
sangho6032f342016-07-07 14:32:03 +090072import static org.onosproject.openstacknetworking.Constants.*;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070073
74@Service
75@Component(immediate = true)
76/**
77 * Populates forwarding rules for VMs created by Openstack.
78 */
79public final class OpenstackSwitchingManager extends AbstractProvider
sangho6032f342016-07-07 14:32:03 +090080 implements HostProvider {
Hyunsun Moonb974fca2016-06-30 21:20:39 -070081
82 private final Logger log = LoggerFactory.getLogger(getClass());
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected CoreService coreService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected DeviceService deviceService;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected HostProviderRegistry hostProviderRegistry;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected DhcpService dhcpService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected HostService hostService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected MastershipService mastershipService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected OpenstackInterfaceService openstackService;
104
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected OpenstackNodeService openstackNodeService;
107
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700108 private final ExecutorService deviceEventExecutor =
109 Executors.newSingleThreadExecutor(groupedThreads("onos/openstackswitching", "device-event"));
110 private final ExecutorService configEventExecutor =
111 Executors.newSingleThreadExecutor(groupedThreads("onos/openstackswitching", "config-event"));
112 private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700113 private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700114
115 private HostProviderService hostProvider;
116
117 /**
118 * Creates OpenStack switching host provider.
119 */
120 public OpenstackSwitchingManager() {
121 super(new ProviderId("host", APP_ID));
122 }
123
124 @Activate
125 protected void activate() {
126 coreService.registerApplication(APP_ID);
127 deviceService.addListener(internalDeviceListener);
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700128 openstackNodeService.addListener(internalNodeListener);
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700129 hostProvider = hostProviderRegistry.register(this);
130
131 log.info("Started");
132 }
133
134 @Deactivate
135 protected void deactivate() {
136 hostProviderRegistry.unregister(this);
137 deviceService.removeListener(internalDeviceListener);
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700138 openstackNodeService.removeListener(internalNodeListener);
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700139
140 deviceEventExecutor.shutdown();
141 configEventExecutor.shutdown();
142
143 log.info("Stopped");
144 }
145
146 @Override
147 public void triggerProbe(Host host) {
148 // no probe is required
149 }
150
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700151 private void processPortAdded(Port port) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700152 // TODO check the node state is COMPLETE
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700153 OpenstackPort osPort = openstackService.port(port);
154 if (osPort == null) {
155 log.warn("Failed to get OpenStack port for {}", port);
156 return;
157 }
158
159 OpenstackNetwork osNet = openstackService.network(osPort.networkId());
160 if (osNet == null) {
161 log.warn("Failed to get OpenStack network {}",
162 osPort.networkId());
163 return;
164 }
165
166 registerDhcpInfo(osPort);
167 ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
168 // TODO remove this and openstackPortInfo
169 String gatewayIp = osNet.subnets().stream().findFirst().get().gatewayIp();
170
171 // Added CREATE_TIME intentionally to trigger HOST_UPDATED event for the
172 // existing instances.
173 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
174 .set(NETWORK_ID, osPort.networkId())
175 .set(PORT_ID, osPort.id())
176 .set(VXLAN_ID, osNet.segmentId())
177 .set(TENANT_ID, osNet.tenantId())
178 // TODO remove this and openstackPortInfo
179 .set(GATEWAY_IP, gatewayIp)
180 .set(CREATE_TIME, String.valueOf(System.currentTimeMillis()));
181
182 HostDescription hostDesc = new DefaultHostDescription(
183 osPort.macAddress(),
184 VlanId.NONE,
185 new HostLocation(connectPoint, System.currentTimeMillis()),
186 Sets.newHashSet(osPort.fixedIps().values()),
187 annotations.build());
188
189 HostId hostId = HostId.hostId(osPort.macAddress());
190 hostProvider.hostDetected(hostId, hostDesc, false);
191 }
192
193 private void processPortRemoved(Port port) {
194 ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700195 removeHosts(connectPoint);
196 }
197
198 private void removeHosts(ConnectPoint connectPoint) {
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700199 hostService.getConnectedHosts(connectPoint).stream()
200 .forEach(host -> {
201 dhcpService.removeStaticMapping(host.mac());
202 hostProvider.hostVanished(host.id());
203 });
204 }
205
206 private void registerDhcpInfo(OpenstackPort openstackPort) {
207 checkNotNull(openstackPort);
208 checkArgument(!openstackPort.fixedIps().isEmpty());
209
210 OpenstackSubnet openstackSubnet = openstackService.subnets().stream()
211 .filter(n -> n.networkId().equals(openstackPort.networkId()))
212 .findFirst().orElse(null);
213 if (openstackSubnet == null) {
214 log.warn("Failed to find subnet for {}", openstackPort);
215 return;
216 }
217
218 Ip4Address ipAddress = openstackPort.fixedIps().values().stream().findFirst().get();
219 IpPrefix subnetPrefix = IpPrefix.valueOf(openstackSubnet.cidr());
220 Ip4Address broadcast = Ip4Address.makeMaskedAddress(
221 ipAddress,
222 subnetPrefix.prefixLength());
223
224 // TODO: supports multiple DNS servers
225 Ip4Address domainServer = openstackSubnet.dnsNameservers().isEmpty() ?
226 DNS_SERVER_IP : openstackSubnet.dnsNameservers().get(0);
227
228 IpAssignment ipAssignment = IpAssignment.builder()
229 .ipAddress(ipAddress)
230 .leasePeriod(DHCP_INFINITE_LEASE)
231 .timestamp(new Date())
232 .subnetMask(Ip4Address.makeMaskPrefix(subnetPrefix.prefixLength()))
233 .broadcast(broadcast)
234 .domainServer(domainServer)
235 .assignmentStatus(Option_RangeNotEnforced)
236 .routerAddress(Ip4Address.valueOf(openstackSubnet.gatewayIp()))
237 .build();
238
239 dhcpService.setStaticMapping(openstackPort.macAddress(), ipAssignment);
240 }
241
242 private class InternalDeviceListener implements DeviceListener {
243
244 @Override
245 public void event(DeviceEvent event) {
246 Device device = event.subject();
247 if (!mastershipService.isLocalMaster(device.id())) {
248 // do not allow to proceed without mastership
249 return;
250 }
251
252 Port port = event.port();
253 if (port == null) {
254 return;
255 }
256
257 String portName = port.annotations().value(PORT_NAME);
258 if (Strings.isNullOrEmpty(portName) ||
259 !portName.startsWith(PORTNAME_PREFIX_VM)) {
260 // handles VM connected port event only
261 return;
262 }
263
264 switch (event.type()) {
265 case PORT_UPDATED:
266 if (!event.port().isEnabled()) {
267 deviceEventExecutor.execute(() -> processPortRemoved(event.port()));
268 }
269 break;
270 case PORT_ADDED:
271 deviceEventExecutor.execute(() -> processPortAdded(event.port()));
272 break;
273 default:
274 break;
275 }
276 }
277 }
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700278
279 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
280
281 @Override
282 public void event(OpenstackNodeEvent event) {
283 OpenstackNode node = event.node();
284 // TODO check leadership of the node and make only the leader process
285
286 switch (event.type()) {
287 case COMPLETE:
288 log.info("COMPLETE node {} detected", node.hostname());
289
290 // adds existing VMs running on the complete state node
291 deviceService.getPorts(node.intBridge()).stream()
292 .filter(port -> port.annotations().value(PORT_NAME)
293 .startsWith(PORTNAME_PREFIX_VM) &&
294 port.isEnabled())
295 .forEach(port -> {
296 deviceEventExecutor.execute(() -> processPortAdded(port));
297 log.info("VM is detected on {}", port);
298 });
299
300 // removes stale VMs
301 hostService.getHosts().forEach(host -> {
302 if (deviceService.getPort(host.location().deviceId(),
303 host.location().port()) == null) {
304 deviceEventExecutor.execute(() -> removeHosts(host.location()));
305 log.info("Removed stale VM {}", host.location());
306 }
307 });
308 break;
309 case INCOMPLETE:
310 log.warn("{} is changed to INCOMPLETE state", node);
311 break;
312 case INIT:
313 case DEVICE_CREATED:
314 default:
315 break;
316 }
317 }
318 }
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700319}