2 * Copyright (c) 2016, 2018 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.genius.itm.listeners;
11 import com.google.common.base.Optional;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.Objects;
16 import java.util.concurrent.ExecutionException;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19 import org.apache.commons.lang3.StringUtils;
20 import org.apache.commons.net.util.SubnetUtils;
21 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.genius.itm.cache.UnprocessedTunnelsStateCache;
26 import org.opendaylight.genius.itm.cli.TepCommandHelper;
27 import org.opendaylight.genius.itm.cli.TepException;
28 import org.opendaylight.genius.itm.globals.ITMConstants;
29 import org.opendaylight.genius.itm.impl.ItmUtils;
30 import org.opendaylight.genius.mdsalutil.MDSALUtil;
31 import org.opendaylight.infrautils.utils.concurrent.Executors;
32 import org.opendaylight.serviceutils.tools.mdsal.listener.AbstractAsyncDataTreeChangeListener;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.ItmConfig;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.VtepConfigSchemas;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.vtep.config.schemas.VtepConfigSchema;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.vtep.config.schemas.VtepConfigSchemaBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.vtep.config.schemas.vtep.config.schema.DpnIds;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.vtep.ip.pools.VtepIpPool;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.vtep.ip.pools.VtepIpPoolBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.TransportZones;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZone;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZoneKey;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.Subnets;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.SubnetsKey;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.Vteps;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.VtepsKey;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
56 * The listener class interested in processing data change on.
57 * {@code VtepConfigSchema} objects.
59 * @see VtepConfigSchema
62 public class VtepConfigSchemaListener extends AbstractAsyncDataTreeChangeListener<VtepConfigSchema> {
64 private static final Logger LOG = LoggerFactory.getLogger(VtepConfigSchemaListener.class);
66 private final DataBroker dataBroker;
68 /** Blueprint XML config file handle. */
69 private final ItmConfig itmConfig;
70 private final UnprocessedTunnelsStateCache unprocessedTunnelsStateCache;
73 * Instantiates a new VTEP config schema listener.
78 * ITM config file handle
81 public VtepConfigSchemaListener(DataBroker dataBroker, ItmConfig itmConfig,
82 UnprocessedTunnelsStateCache unprocessedTunnelsStateCache) {
83 super(dataBroker, LogicalDatastoreType.CONFIGURATION,
84 InstanceIdentifier.create(VtepConfigSchemas.class).child(VtepConfigSchema.class), Executors
85 .newSingleThreadExecutor("VtepConfigSchemaListener", LOG));
86 this.dataBroker = dataBroker;
87 this.itmConfig = itmConfig;
88 this.unprocessedTunnelsStateCache = unprocessedTunnelsStateCache;
92 public void remove(@NonNull InstanceIdentifier<VtepConfigSchema> instanceIdentifier,
93 @NonNull VtepConfigSchema vtepConfigSchema) {
94 LOG.trace("Received notification for VTEP config schema [{}] deleted.", vtepConfigSchema.getSchemaName());
95 List<BigInteger> lstDpnIds = ItmUtils.getDpnIdList(vtepConfigSchema.nonnullDpnIds());
96 if (!lstDpnIds.isEmpty()) {
97 deleteVteps(vtepConfigSchema, lstDpnIds);
99 // Delete IP pool corresponding to schema
100 // TODO: Ensure no schema exists with same subnet before deleting
101 String subnetCidr = ItmUtils.getSubnetCidrAsString(vtepConfigSchema.getSubnet());
102 deleteVtepIpPool(subnetCidr);
106 public void update(@NonNull InstanceIdentifier<VtepConfigSchema> instanceIdentifier,
107 @NonNull VtepConfigSchema originalVtepConfigSchema,
108 @NonNull VtepConfigSchema updatedConfigSchema) {
109 LOG.error("Received DCN for updating VTEP Original schema: {}. Updated schema: {}", originalVtepConfigSchema,
110 updatedConfigSchema);
111 VtepConfigSchema originalSchema = ItmUtils.validateVtepConfigSchema(originalVtepConfigSchema);
112 VtepConfigSchema updatedSchema = ItmUtils.validateVtepConfigSchema(updatedConfigSchema);
114 if (doesDeleteAndAddSchemaRequired(originalVtepConfigSchema, updatedConfigSchema)) {
115 LOG.error("Failed to handle DCN for updating VTEP schema. Original schema: {}. Updated schema: {}",
116 originalVtepConfigSchema, updatedConfigSchema);
117 // TODO: handle updates
121 handleUpdateOfDpnIds(originalSchema, updatedSchema);
125 public void add(@NonNull InstanceIdentifier<VtepConfigSchema> instanceIdentifier,
126 @NonNull VtepConfigSchema vtepConfigSchema) {
127 // Construct the transport zones from the provided schemas and push it to config DS
128 LOG.trace("Add VtepConfigSchema: {}", vtepConfigSchema);
130 VtepConfigSchema validatedSchema = ItmUtils
131 .validateForAddVtepConfigSchema(vtepConfigSchema, getAllVtepConfigSchemas());
133 VtepIpPool vtepIpPool = processAvailableIps(validatedSchema);
135 addVteps(validatedSchema, vtepIpPool);
136 } catch (ExecutionException | InterruptedException e) {
137 LOG.error("Add VtepConfigSchema failed : {}", vtepConfigSchema, e);
142 * Handle update of dpn ids.
149 private void handleUpdateOfDpnIds(VtepConfigSchema original, VtepConfigSchema updated) {
150 // Handling add/delete DPNs from schema
151 List<DpnIds> originalDpnIds = original.getDpnIds() == null ? new ArrayList<>()
152 : original.getDpnIds();
153 List<DpnIds> updatedDpnIds = updated.getDpnIds() == null ? new ArrayList<>()
154 : updated.getDpnIds();
156 handleDeletedDpnsFromSchema(original, originalDpnIds, updatedDpnIds);
157 handleNewlyAddedDpnsToSchema(original, originalDpnIds, updatedDpnIds);
161 * Does delete and add schema required.
167 * @return true, if successful
169 private boolean doesDeleteAndAddSchemaRequired(VtepConfigSchema original, VtepConfigSchema updated) {
170 boolean delnAddRequired = false;
171 if (!StringUtils.equalsIgnoreCase(original.getPortName(), updated.getPortName())) {
172 delnAddRequired = true;
173 } else if (!Objects.equals(original.getVlanId(), updated.getVlanId())) {
174 delnAddRequired = true;
175 } else if (original.getSubnet() != null && !original.getSubnet().equals(updated.getSubnet())) {
176 delnAddRequired = true;
177 } else if (original.getGatewayIp() != null && !original.getGatewayIp().equals(updated.getGatewayIp())) {
178 delnAddRequired = true;
179 } else if (!StringUtils.equalsIgnoreCase(original.getTransportZoneName(), updated.getTransportZoneName())) {
180 delnAddRequired = true;
181 } else if (!Objects.equals(original.getTunnelType(), updated.getTunnelType())) {
182 delnAddRequired = true;
184 return delnAddRequired;
188 * Handle newly added dpns to schema.
192 * @param originalDpnIds
193 * the original dpn ids
194 * @param updatedDpnIds
195 * the updated dpn ids
197 private void handleNewlyAddedDpnsToSchema(VtepConfigSchema original, List<DpnIds> originalDpnIds,
198 List<DpnIds> updatedDpnIds) {
199 LOG.trace("Handle Addition of DPNs from VTEP Original Dpn: {}. Updated Dpn: {}",
200 originalDpnIds, updatedDpnIds) ;
201 ArrayList<DpnIds> newlyAddedDpns = new ArrayList<>(updatedDpnIds);
202 newlyAddedDpns.removeAll(originalDpnIds);
203 LOG.debug("Newly added DPNs {} to VTEP config schema [{}].", newlyAddedDpns, original.getSchemaName());
204 if (!newlyAddedDpns.isEmpty()) {
205 VtepConfigSchema diffSchema = new VtepConfigSchemaBuilder(original).setDpnIds(newlyAddedDpns).build();
206 String subnetCidr = ItmUtils.getSubnetCidrAsString(original.getSubnet());
207 VtepIpPool vtepIpPool = getVtepIpPool(subnetCidr);
208 LOG.debug("Adding of DPNs in Diff Schema: {}", diffSchema) ;
210 addVteps(diffSchema, vtepIpPool);
211 } catch (ExecutionException | InterruptedException e) {
212 LOG.error("Add VtepConfigSchema failed : {}", diffSchema, e);
218 * Handle deleted dpns from schema.
222 * @param originalDpnIds
223 * the original dpn ids
224 * @param updatedDpnIds
225 * the updated dpn ids
227 private void handleDeletedDpnsFromSchema(VtepConfigSchema original, List<DpnIds> originalDpnIds,
228 List<DpnIds> updatedDpnIds) {
229 ArrayList<DpnIds> deletedDpns = new ArrayList<>(originalDpnIds);
230 deletedDpns.removeAll(updatedDpnIds);
231 LOG.debug("DPNs to be removed DPNs {} from VTEP config schema [{}].", deletedDpns, original.getSchemaName());
232 if (!deletedDpns.isEmpty()) {
233 LOG.debug("Deleting of DPNs from VTEP Schema: {}. To be deleted Dpns: {}", original, deletedDpns) ;
234 deleteVteps(original, ItmUtils.getDpnIdList(deletedDpns));
239 * Gets all vtep config schemas.
241 * @return the all vtep config schemas
243 private List<VtepConfigSchema> getAllVtepConfigSchemas() {
244 return ItmUtils.read(LogicalDatastoreType.CONFIGURATION, ItmUtils.getVtepConfigSchemasIdentifier(),
245 this.dataBroker).toJavaUtil().map(VtepConfigSchemas::getVtepConfigSchema).orElse(null);
256 private void addVteps(VtepConfigSchema schema, VtepIpPool vtepIpPool) throws ExecutionException,
257 InterruptedException {
258 if (schema.getDpnIds() == null || schema.getDpnIds().isEmpty()) {
259 LOG.debug("DPN list is empty, skipping addVteps for schema: {}", schema);
263 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
264 if (vtepIpPool == null) {
265 LOG.error("VTEP config pool not found for subnetCidr {}. Failed to add VTEPs for schema {}", subnetCidr,
269 TepCommandHelper tepCommandHelper = new TepCommandHelper(this.dataBroker, itmConfig,
270 unprocessedTunnelsStateCache);
273 Class<? extends TunnelTypeBase> tunnelType = schema.getTunnelType() ;
274 if (TunnelTypeVxlan.class.equals(tunnelType)) {
275 tunType = ITMConstants.TUNNEL_TYPE_VXLAN;
277 tunType = ITMConstants.TUNNEL_TYPE_GRE;
279 tepCommandHelper.configureTunnelType(schema.getTransportZoneName(),
280 StringUtils.upperCase(tunType));
282 List<IpAddress> availableIps = vtepIpPool.getAvailableIpaddress();
283 List<IpAddress> newlyAllocatedIps = new ArrayList<>();
284 List<BigInteger> skippedDpnIds = new ArrayList<>();
286 String gatewayIp = handleGatewayIp(schema.getGatewayIp());
287 for (BigInteger dpnId : ItmUtils.getDpnIdList(schema.getDpnIds())) {
288 IpAddress ipAddress = getAnAvailableIP(availableIps);
289 if (ipAddress == null) {
290 skippedDpnIds.add(dpnId);
294 tepCommandHelper.createLocalCache(dpnId, schema.getPortName(), schema.getVlanId(),
295 ipAddress.stringValue(), subnetCidr, gatewayIp, schema.getTransportZoneName());
296 } catch (TepException e) {
297 LOG.error("create local cache Failed", e);
299 newlyAllocatedIps.add(ipAddress);
301 if (!skippedDpnIds.isEmpty()) {
302 LOG.error("No available IP addresses in the VTEP config pool {}, skipping VTEP configurations for DPN's {}",
303 subnetCidr, skippedDpnIds);
306 if (!newlyAllocatedIps.isEmpty()) {
307 LOG.debug("Delete OnCommit and buildTeps in NewlyAddedDpns");
308 tepCommandHelper.deleteOnCommit();
309 tepCommandHelper.buildTeps();
310 allocateIpAddresses(newlyAllocatedIps, vtepIpPool, subnetCidr);
321 private String handleGatewayIp(IpAddress gatewayIp) {
322 String strGatewayIp = gatewayIp == null ? null : gatewayIp.stringValue();
323 if (StringUtils.isBlank(strGatewayIp) || StringUtils.equals(ITMConstants.DUMMY_IP_ADDRESS, strGatewayIp)) {
324 // To avoid a validation exception in TepCommandHelper
335 * @param lstDpnIdsToBeDeleted
336 * the dpn ids list to be deleted
338 private void deleteVteps(VtepConfigSchema schema, List<BigInteger> lstDpnIdsToBeDeleted) {
339 TepCommandHelper tepCommandHelper = new TepCommandHelper(this.dataBroker, itmConfig,
340 unprocessedTunnelsStateCache);
341 List<IpAddress> freeIps = new ArrayList<>();
343 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
344 String gatewayIp = handleGatewayIp(schema.getGatewayIp());
346 for (BigInteger dpnId : lstDpnIdsToBeDeleted) {
347 VtepsKey vtepkey = new VtepsKey(dpnId, schema.getPortName());
349 InstanceIdentifier<Vteps> vpath = InstanceIdentifier.builder(TransportZones.class)
350 .child(TransportZone.class, new TransportZoneKey(schema.getTransportZoneName()))
351 .child(Subnets.class, new SubnetsKey(schema.getSubnet())).child(Vteps.class, vtepkey).build();
354 Optional<Vteps> vtepOptional = ItmUtils.read(LogicalDatastoreType.CONFIGURATION, vpath, dataBroker);
355 if (vtepOptional.isPresent()) {
356 vtep = vtepOptional.get();
358 LOG.warn("VTEP doesn't exist for DPN [{}] and port [{}].", dpnId, schema.getPortName());
362 IpAddress ipAddress = vtep.getIpAddress();
364 tepCommandHelper.deleteVtep(dpnId, vtep.getPortname(), schema.getVlanId(),
365 ipAddress.stringValue(), subnetCidr, gatewayIp, schema.getTransportZoneName());
366 } catch (TepException e) {
367 LOG.error("delete Vtep Failed", e);
370 freeIps.add(ipAddress);
372 LOG.debug("Delete OnCommit in NewlyAddedDpns");
373 tepCommandHelper.deleteOnCommit();
374 deAllocateIpAddresses(freeIps, subnetCidr);
378 * Calculate available IPs from the subnet mask specified in the schema.
379 * Pushes the available and allocated IP address to config DS.
384 private VtepIpPool processAvailableIps(final VtepConfigSchema schema) {
385 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
386 SubnetUtils subnetUtils = new SubnetUtils(subnetCidr);
388 List<IpAddress> availableIps = calculateAvailableIps(subnetUtils, schema.getExcludeIpFilter(),
389 schema.getGatewayIp());
390 VtepIpPool vtepIpPool = new VtepIpPoolBuilder().setSubnetCidr(subnetCidr).setAvailableIpaddress(availableIps)
391 .setAllocatedIpaddress(new ArrayList<>()).build();
393 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
394 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), vtepIpPool);
395 LOG.info("Vtep IP Pool with key:{} added to config DS", subnetCidr);
400 * Gets the vtep ip pool.
404 * @return the vtep ip pool
406 private VtepIpPool getVtepIpPool(final String subnetCidr) {
407 return ItmUtils.read(LogicalDatastoreType.CONFIGURATION, ItmUtils.getVtepIpPoolIdentifier(subnetCidr),
408 this.dataBroker).orNull();
412 * Delete vtep ip pool.
417 private void deleteVtepIpPool(final String subnetCidr) {
418 if (StringUtils.isNotBlank(subnetCidr)) {
419 MDSALUtil.syncDelete(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
420 ItmUtils.getVtepIpPoolIdentifier(subnetCidr));
421 LOG.debug("Deleted Vtep IP Pool with key:{}", subnetCidr);
426 * Gets the an available ip.
428 * @param availableIps list of all available IPs
430 * @return the an available ip
432 private IpAddress getAnAvailableIP(List<IpAddress> availableIps) {
433 // TODO: Sort IP Addresses, get the least value
434 IpAddress ipAddress = null;
435 if (availableIps != null && !availableIps.isEmpty()) {
436 ipAddress = availableIps.remove(0);
442 * Allocate ip addresses.
444 * @param allocatedIps
451 private void allocateIpAddresses(List<IpAddress> allocatedIps, VtepIpPool vtepIpPool, String subnetCidr) {
452 if (allocatedIps != null && !allocatedIps.isEmpty() && vtepIpPool != null) {
453 // Remove from the available IP address list and add to allocated IP
455 VtepIpPoolBuilder builder = new VtepIpPoolBuilder(vtepIpPool);
456 if (builder.getAvailableIpaddress() != null) {
457 builder.getAvailableIpaddress().removeAll(allocatedIps);
459 if (builder.getAllocatedIpaddress() == null) {
460 builder.setAllocatedIpaddress(allocatedIps);
462 builder.getAllocatedIpaddress().addAll(allocatedIps);
465 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
466 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), builder.build());
471 * De-allocate ip addresses.
478 private void deAllocateIpAddresses(List<IpAddress> freeIps, String subnetCidr) {
479 VtepIpPool vtepIpPool = getVtepIpPool(subnetCidr);
480 if (freeIps != null && !freeIps.isEmpty() && vtepIpPool != null) {
481 // Remove from the allocated IP address list and add to available IP
483 VtepIpPoolBuilder builder = new VtepIpPoolBuilder(vtepIpPool);
484 if (builder.getAllocatedIpaddress() != null) {
485 builder.getAllocatedIpaddress().removeAll(freeIps);
487 if (builder.getAvailableIpaddress() == null) {
488 builder.setAvailableIpaddress(freeIps);
490 builder.getAvailableIpaddress().addAll(freeIps);
493 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
494 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), builder.build());
495 LOG.debug("Vtep IP Pool with key:{} updated to config DS", subnetCidr);
500 * Calculate available ips.
504 * @param excludeIpFilter
505 * the exclude ip filter
510 private List<IpAddress> calculateAvailableIps(SubnetUtils subnetUtils, String excludeIpFilter,
511 IpAddress gatewayIp) {
512 List<IpAddress> lstAvailableIps = new ArrayList<>();
513 SubnetInfo subnetInfo = subnetUtils.getInfo();
514 String[] arrIpAddresses = subnetInfo.getAllAddresses();
516 for (String ipAddress : arrIpAddresses) {
517 lstAvailableIps.add(IpAddressBuilder.getDefaultInstance(ipAddress));
519 lstAvailableIps.remove(gatewayIp);
520 lstAvailableIps.removeAll(ItmUtils.getExcludeIpAddresses(excludeIpFilter, subnetInfo));
522 return lstAvailableIps;