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