blob: 712409df4274170d7ec31898c190f90348bc6e5c [file] [log] [blame]
Hyunsun Moon44aac662017-02-18 02:07:01 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Hyunsun Moon44aac662017-02-18 02:07:01 +09003 *
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.impl;
17
18import com.google.common.base.Strings;
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.onlab.packet.IpAddress;
26import org.onlab.packet.MacAddress;
27import org.onlab.packet.VlanId;
28import org.onlab.util.Tools;
29import org.onosproject.core.CoreService;
30import org.onosproject.mastership.MastershipService;
31import org.onosproject.net.ConnectPoint;
32import org.onosproject.net.DefaultAnnotations;
33import org.onosproject.net.Device;
34import org.onosproject.net.Host;
35import org.onosproject.net.HostId;
36import org.onosproject.net.HostLocation;
37import org.onosproject.net.Port;
38import org.onosproject.net.device.DeviceEvent;
39import org.onosproject.net.device.DeviceListener;
40import org.onosproject.net.device.DeviceService;
41import org.onosproject.net.host.DefaultHostDescription;
42import org.onosproject.net.host.HostDescription;
43import org.onosproject.net.host.HostProvider;
44import org.onosproject.net.host.HostProviderRegistry;
45import org.onosproject.net.host.HostProviderService;
46import org.onosproject.net.host.HostService;
47import org.onosproject.net.provider.AbstractProvider;
48import org.onosproject.net.provider.ProviderId;
49import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090050import org.onosproject.openstacknode.api.OpenstackNode;
51import org.onosproject.openstacknode.api.OpenstackNodeEvent;
52import org.onosproject.openstacknode.api.OpenstackNodeListener;
53import org.onosproject.openstacknode.api.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090054import org.openstack4j.model.network.Network;
55import org.slf4j.Logger;
56import org.slf4j.LoggerFactory;
57
58import java.util.Set;
59import java.util.concurrent.ExecutorService;
60import java.util.concurrent.Executors;
61import java.util.stream.Collectors;
62
63import static org.onlab.util.Tools.groupedThreads;
64import static org.onosproject.net.AnnotationKeys.PORT_NAME;
65import static org.onosproject.openstacknetworking.api.Constants.*;
66import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_CREATE_TIME;
67import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
68import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
69
70@Service
71@Component(immediate = true)
72public final class OpenstackSwitchingHostProvider extends AbstractProvider implements HostProvider {
73
74 private final Logger log = LoggerFactory.getLogger(getClass());
75
76 private static final String PORT_NAME_PREFIX_VM = "tap";
77 private static final String ERR_ADD_HOST = "Failed to add host: ";
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected CoreService coreService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected DeviceService deviceService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected HostProviderRegistry hostProviderRegistry;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected HostService hostService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected MastershipService mastershipService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected OpenstackNetworkService osNetworkService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected OpenstackNodeService osNodeService;
99
100 private final ExecutorService deviceEventExecutor =
101 Executors.newSingleThreadExecutor(groupedThreads("openstacknetworking", "device-event"));
102 private final ExecutorService configEventExecutor =
103 Executors.newSingleThreadExecutor(groupedThreads("openstacknetworking", "config-event"));
104 private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
105 private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
106
107 private HostProviderService hostProvider;
108
109 /**
110 * Creates OpenStack switching host provider.
111 */
112 public OpenstackSwitchingHostProvider() {
Frank Wangad237e72017-06-03 11:39:44 +0800113 super(new ProviderId("sona", OPENSTACK_NETWORKING_APP_ID));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900114 }
115
116 @Activate
117 protected void activate() {
118 coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
119 deviceService.addListener(internalDeviceListener);
120 osNodeService.addListener(internalNodeListener);
121 hostProvider = hostProviderRegistry.register(this);
122
123 log.info("Started");
124 }
125
126 @Deactivate
127 protected void deactivate() {
128 hostProviderRegistry.unregister(this);
129 osNodeService.removeListener(internalNodeListener);
130 deviceService.removeListener(internalDeviceListener);
131
132 deviceEventExecutor.shutdown();
133 configEventExecutor.shutdown();
134
135 log.info("Stopped");
136 }
137
138 @Override
139 public void triggerProbe(Host host) {
140 // no probe is required
141 }
142
143 private void processPortAdded(Port port) {
144 // TODO check the node state is COMPLETE
145 org.openstack4j.model.network.Port osPort = osNetworkService.port(port);
146 if (osPort == null) {
147 log.warn(ERR_ADD_HOST + "OpenStack port for {} not found", port);
148 return;
149 }
150
151 Network osNet = osNetworkService.network(osPort.getNetworkId());
152 if (osNet == null) {
153 log.warn(ERR_ADD_HOST + "OpenStack network {} not found",
154 osPort.getNetworkId());
155 return;
156 }
157
158 if (osPort.getFixedIps().isEmpty()) {
159 log.warn(ERR_ADD_HOST + "no fixed IP for port {}", osPort.getId());
160 return;
161 }
162
163 MacAddress macAddr = MacAddress.valueOf(osPort.getMacAddress());
164 Set<IpAddress> fixedIps = osPort.getFixedIps().stream()
165 .map(ip -> IpAddress.valueOf(ip.getIpAddress()))
166 .collect(Collectors.toSet());
167 ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
168
169 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
170 .set(ANNOTATION_NETWORK_ID, osPort.getNetworkId())
171 .set(ANNOTATION_PORT_ID, osPort.getId())
172 .set(ANNOTATION_CREATE_TIME, String.valueOf(System.currentTimeMillis()));
173
174 HostDescription hostDesc = new DefaultHostDescription(
175 macAddr,
176 VlanId.NONE,
177 new HostLocation(connectPoint, System.currentTimeMillis()),
178 fixedIps,
179 annotations.build());
180
181 HostId hostId = HostId.hostId(macAddr);
182 hostProvider.hostDetected(hostId, hostDesc, false);
183 }
184
185 private void processPortRemoved(Port port) {
186 ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
187 hostService.getConnectedHosts(connectPoint).forEach(host -> {
188 hostProvider.hostVanished(host.id());
189 });
190 }
191
192 private class InternalDeviceListener implements DeviceListener {
193
194 @Override
195 public boolean isRelevant(DeviceEvent event) {
196 Device device = event.subject();
197 if (!mastershipService.isLocalMaster(device.id())) {
198 // do not allow to proceed without mastership
199 return false;
200 }
201 Port port = event.port();
202 if (port == null) {
203 return false;
204 }
205 String portName = port.annotations().value(PORT_NAME);
206 if (Strings.isNullOrEmpty(portName) ||
207 !portName.startsWith(PORT_NAME_PREFIX_VM)) {
208 // handles Nova created port event only
209 return false;
210 }
211 return true;
212 }
213
214 @Override
215 public void event(DeviceEvent event) {
216 switch (event.type()) {
217 case PORT_UPDATED:
218 if (!event.port().isEnabled()) {
219 deviceEventExecutor.execute(() -> {
220 log.debug("Instance port {} is removed from {}",
221 event.port().annotations().value(PORT_NAME),
222 event.subject().id());
223 processPortRemoved(event.port());
224 });
225 } else if (event.port().isEnabled()) {
226 deviceEventExecutor.execute(() -> {
227 log.debug("Instance Port {} is detected from {}",
228 event.port().annotations().value(PORT_NAME),
229 event.subject().id());
230 processPortAdded(event.port());
231 });
232 }
233 break;
234 case PORT_ADDED:
235 deviceEventExecutor.execute(() -> {
236 log.debug("Instance port {} is detected from {}",
237 event.port().annotations().value(PORT_NAME),
238 event.subject().id());
239 processPortAdded(event.port());
240 });
241 break;
Hyunsun Moonb7a9cd22017-02-24 11:12:53 +0900242 case PORT_REMOVED:
243 deviceEventExecutor.execute(() -> {
244 log.debug("Instance port {} is removed from {}",
245 event.port().annotations().value(PORT_NAME),
246 event.subject().id());
247 processPortRemoved(event.port());
248 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900249 default:
250 break;
251 }
252 }
253 }
254
255 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
256
257 @Override
258 public void event(OpenstackNodeEvent event) {
259 OpenstackNode osNode = event.subject();
260 // TODO check leadership of the node and make only the leader process
261
262 switch (event.type()) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900263 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900264 deviceEventExecutor.execute(() -> {
265 log.info("COMPLETE node {} is detected", osNode.hostname());
266 processCompleteNode(event.subject());
267 });
268 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900269 case OPENSTACK_NODE_INCOMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900270 log.warn("{} is changed to INCOMPLETE state", osNode);
271 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900272 case OPENSTACK_NODE_CREATED:
273 case OPENSTACK_NODE_UPDATED:
274 case OPENSTACK_NODE_REMOVED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900275 default:
276 break;
277 }
278 }
279
280 private void processCompleteNode(OpenstackNode osNode) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900281 deviceService.getPorts(osNode.intgBridge()).stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900282 .filter(port -> port.annotations().value(PORT_NAME)
283 .startsWith(PORT_NAME_PREFIX_VM) &&
284 port.isEnabled())
285 .forEach(port -> {
286 log.debug("Instance port {} is detected from {}",
287 port.annotations().value(PORT_NAME),
288 osNode.hostname());
289 processPortAdded(port);
290 });
291
292 Tools.stream(hostService.getHosts())
293 .filter(host -> deviceService.getPort(
294 host.location().deviceId(),
295 host.location().port()) == null)
296 .forEach(host -> {
297 log.info("Remove stale host {}", host.id());
298 hostProvider.hostVanished(host.id());
299 });
300 }
301 }
302}