blob: d43a8c65543e2a1c8d74539af20126ce4e5fae7d [file] [log] [blame]
Hyunsun Moon5401aaa2016-06-12 17:40:34 -07001/*
2 * Copyright 2015-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.opencord.cordvtn.impl;
17
18import com.google.common.collect.Sets;
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.Ip4Address;
26import org.onlab.packet.MacAddress;
27import org.onlab.packet.VlanId;
28import org.opencord.cordvtn.api.CordVtnConfig;
29import org.opencord.cordvtn.api.Instance;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
32import org.onosproject.dhcp.DhcpService;
33import org.onosproject.dhcp.IpAssignment;
34import org.onosproject.net.ConnectPoint;
35import org.onosproject.net.DefaultAnnotations;
36import org.onosproject.net.Host;
37import org.onosproject.net.HostId;
38import org.onosproject.net.HostLocation;
39import org.onosproject.net.Port;
40import org.onosproject.net.config.ConfigFactory;
41import org.onosproject.net.config.NetworkConfigEvent;
42import org.onosproject.net.config.NetworkConfigListener;
43import org.onosproject.net.config.NetworkConfigRegistry;
44import org.onosproject.net.config.basics.SubjectFactories;
45import org.onosproject.net.device.DeviceService;
46import org.onosproject.net.host.DefaultHostDescription;
47import org.onosproject.net.host.HostDescription;
48import org.onosproject.net.host.HostProvider;
49import org.onosproject.net.host.HostProviderRegistry;
50import org.onosproject.net.host.HostProviderService;
51import org.onosproject.net.host.HostService;
52import org.onosproject.net.provider.AbstractProvider;
53import org.onosproject.net.provider.ProviderId;
54import org.onosproject.xosclient.api.OpenStackAccess;
55import org.onosproject.xosclient.api.VtnPort;
56import org.onosproject.xosclient.api.VtnPortApi;
57import org.onosproject.xosclient.api.VtnService;
58import org.onosproject.xosclient.api.VtnServiceApi;
59import org.onosproject.xosclient.api.VtnServiceId;
60import org.onosproject.xosclient.api.XosAccess;
61import org.onosproject.xosclient.api.XosClientService;
62import org.opencord.cordvtn.api.InstanceService;
63import org.slf4j.Logger;
64
65import java.util.Date;
66import java.util.concurrent.ExecutorService;
67
68import static com.google.common.base.Preconditions.checkNotNull;
69import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
70import static org.onlab.util.Tools.groupedThreads;
71import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
Hyunsun Moon3ef52492016-06-15 14:56:31 -070072import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Hyunsun Moonacbc8ef2016-06-21 17:58:45 -070073import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.MANAGEMENT;
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070074import static org.opencord.cordvtn.api.Constants.*;
75import static org.slf4j.LoggerFactory.getLogger;
76
77/**
78 * Adds or removes instances to network services.
79 */
80@Component(immediate = true)
81@Service
82public class InstanceManager extends AbstractProvider implements HostProvider,
83 InstanceService {
84
85 protected final Logger log = getLogger(getClass());
86
Hyunsun Moonfb417942016-06-23 14:48:20 -070087 private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
88 private static final int DHCP_INFINITE_LEASE = -1;
89
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070090 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected CoreService coreService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected NetworkConfigRegistry configRegistry;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected HostProviderRegistry hostProviderRegistry;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected DeviceService deviceService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected HostService hostService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected DhcpService dhcpService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected XosClientService xosClient;
110
111 private final ConfigFactory configFactory =
Hyunsun Moonfb417942016-06-23 14:48:20 -0700112 new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, CordVtnConfig.class, "cordvtn") {
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700113 @Override
114 public CordVtnConfig createConfig() {
115 return new CordVtnConfig();
116 }
117 };
118
119 private final ExecutorService eventExecutor =
Hyunsun Moonfb417942016-06-23 14:48:20 -0700120 newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-instance", "event-handler"));
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700121 private final NetworkConfigListener configListener = new InternalConfigListener();
122
123 private ApplicationId appId;
124 private HostProviderService hostProvider;
125 private XosAccess xosAccess = null;
126 private OpenStackAccess osAccess = null;
127
128 /**
129 * Creates an cordvtn host location provider.
130 */
131 public InstanceManager() {
132 super(new ProviderId("host", CORDVTN_APP_ID));
133 }
134
135 @Activate
136 protected void activate() {
137 appId = coreService.registerApplication(CORDVTN_APP_ID);
138
139 hostProvider = hostProviderRegistry.register(this);
140 configRegistry.registerConfigFactory(configFactory);
141 configRegistry.addListener(configListener);
142
143 log.info("Started");
144 }
145
146 @Deactivate
147 protected void deactivate() {
148 hostProviderRegistry.unregister(this);
149
150 configRegistry.unregisterConfigFactory(configFactory);
151 configRegistry.removeListener(configListener);
152
153 eventExecutor.shutdown();
154 log.info("Stopped");
155 }
156
157 @Override
158 public void triggerProbe(Host host) {
159 /*
160 * Note: In CORD deployment, we assume that all hosts are configured.
161 * Therefore no probe is required.
162 */
163 }
164
165 @Override
166 public void addInstance(ConnectPoint connectPoint) {
167 Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
168 if (port == null) {
169 log.debug("No port found from {}", connectPoint);
170 return;
171 }
172
173 VtnPort vtnPort = getVtnPort(port.annotations().value(PORT_NAME));
174 if (vtnPort == null) {
175 return;
176 }
177
178 VtnService vtnService = getVtnService(vtnPort.serviceId());
179 if (vtnService == null) {
180 return;
181 }
182
183 // register DHCP lease for the new instance
184 registerDhcpLease(vtnPort.mac(), vtnPort.ip().getIp4Address(), vtnService);
185
186 // Added CREATE_TIME intentionally to trigger HOST_UPDATED event for the
187 // existing instances.
188 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
189 .set(Instance.SERVICE_TYPE, vtnService.serviceType().toString())
190 .set(Instance.SERVICE_ID, vtnPort.serviceId().id())
191 .set(Instance.PORT_ID, vtnPort.id().id())
192 .set(Instance.CREATE_TIME, String.valueOf(System.currentTimeMillis()));
193
194 HostDescription hostDesc = new DefaultHostDescription(
195 vtnPort.mac(),
196 VlanId.NONE,
197 new HostLocation(connectPoint, System.currentTimeMillis()),
198 Sets.newHashSet(vtnPort.ip()),
199 annotations.build());
200
201 HostId hostId = HostId.hostId(vtnPort.mac());
202 hostProvider.hostDetected(hostId, hostDesc, false);
203 }
204
205 @Override
206 public void addNestedInstance(HostId hostId, HostDescription description) {
207 DefaultAnnotations annotations = DefaultAnnotations.builder()
208 .set(Instance.NESTED_INSTANCE, Instance.TRUE)
209 .build();
210 annotations = annotations.merge(annotations, description.annotations());
211
212 HostDescription nestedHost = new DefaultHostDescription(
213 description.hwAddress(),
214 description.vlan(),
215 description.location(),
216 description.ipAddress(),
217 annotations);
218
219 hostProvider.hostDetected(hostId, nestedHost, false);
220 }
221
222 @Override
223 public void removeInstance(ConnectPoint connectPoint) {
224 hostService.getConnectedHosts(connectPoint).stream()
225 .forEach(host -> {
226 dhcpService.removeStaticMapping(host.mac());
227 hostProvider.hostVanished(host.id());
228 });
229 }
230
231 @Override
232 public void removeNestedInstance(HostId hostId) {
233 hostProvider.hostVanished(hostId);
234 }
235
236 private void registerDhcpLease(MacAddress macAddr, Ip4Address ipAddr, VtnService service) {
237 Ip4Address broadcast = Ip4Address.makeMaskedAddress(
238 ipAddr,
239 service.subnet().prefixLength());
240
241 IpAssignment.Builder ipBuilder = IpAssignment.builder()
242 .ipAddress(ipAddr)
243 .leasePeriod(DHCP_INFINITE_LEASE)
244 .timestamp(new Date())
245 .subnetMask(Ip4Address.makeMaskPrefix(service.subnet().prefixLength()))
246 .broadcast(broadcast)
247 .domainServer(DEFAULT_DNS)
248 .assignmentStatus(Option_RangeNotEnforced);
249
Hyunsun Moonacbc8ef2016-06-21 17:58:45 -0700250 if (service.serviceType() != MANAGEMENT) {
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700251 ipBuilder = ipBuilder.routerAddress(service.serviceIp().getIp4Address());
252 }
253
254 log.debug("Set static DHCP mapping for {} {}", macAddr, ipAddr);
255 dhcpService.setStaticMapping(macAddr, ipBuilder.build());
256 }
257
258 private VtnService getVtnService(VtnServiceId serviceId) {
259 checkNotNull(osAccess, ERROR_OPENSTACK_ACCESS);
260 checkNotNull(xosAccess, ERROR_XOS_ACCESS);
261
262 // TODO remove openstack access when XOS provides all information
263 VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService();
264 VtnService service = serviceApi.service(serviceId, osAccess);
265 if (service == null) {
266 log.warn("Failed to get VtnService for {}", serviceId);
267 }
268 return service;
269 }
270
271 private VtnPort getVtnPort(String portName) {
272 checkNotNull(osAccess, ERROR_OPENSTACK_ACCESS);
273 checkNotNull(xosAccess, ERROR_XOS_ACCESS);
274
275 // TODO remove openstack access when XOS provides all information
276 VtnPortApi portApi = xosClient.getClient(xosAccess).vtnPort();
277 VtnPort vtnPort = portApi.vtnPort(portName, osAccess);
278 if (vtnPort == null) {
279 log.warn("Failed to get port information of {}", portName);
280 }
281 return vtnPort;
282 }
283
284 private void readConfiguration() {
Hyunsun Moonfb417942016-06-23 14:48:20 -0700285 CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700286 if (config == null) {
287 log.debug("No configuration found");
288 return;
289 }
290
291 log.info("Load CORD-VTN configurations");
292 xosAccess = config.xosAccess();
293 osAccess = config.openstackAccess();
294 }
295
296 private class InternalConfigListener implements NetworkConfigListener {
297
298 @Override
299 public void event(NetworkConfigEvent event) {
Hyunsun Moonfb417942016-06-23 14:48:20 -0700300 if (!event.configClass().equals(CordVtnConfig.class)) {
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700301 return;
302 }
303
304 switch (event.type()) {
Hyunsun Moon3a7bf9e2016-06-22 17:35:35 -0700305 case CONFIG_ADDED:
Hyunsun Moonfb417942016-06-23 14:48:20 -0700306 case CONFIG_UPDATED:
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700307 readConfiguration();
308 break;
309 default:
310 break;
311 }
312 }
313 }
314}