2 * Copyright (c) 2016, 2017 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 javax.annotation.PostConstruct;
17 import javax.annotation.PreDestroy;
18 import javax.inject.Inject;
19 import javax.inject.Singleton;
20 import org.apache.commons.lang3.StringUtils;
21 import org.apache.commons.net.util.SubnetUtils;
22 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
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.datastoreutils.AsyncDataTreeChangeListenerBase;
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.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.ItmConfig;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.VtepConfigSchemas;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.vtep.config.schemas.VtepConfigSchema;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.vtep.config.schemas.VtepConfigSchemaBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.vtep.config.schemas.vtep.config.schema.DpnIds;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.vtep.ip.pools.VtepIpPool;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.vtep.ip.pools.VtepIpPoolBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.TransportZones;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZone;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZoneKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.Subnets;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.SubnetsKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.Vteps;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.VtepsKey;
48 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
53 * The listener class interested in processing data change on.
54 * {@code VtepConfigSchema} objects.
56 * @see VtepConfigSchema
59 public class VtepConfigSchemaListener extends AsyncDataTreeChangeListenerBase<VtepConfigSchema,
60 VtepConfigSchemaListener> implements AutoCloseable {
62 /** The Constant LOG. */
63 private static final Logger LOG = LoggerFactory.getLogger(VtepConfigSchemaListener.class);
65 /** The data broker. */
66 private final DataBroker dataBroker;
67 /** Blueprint XML config file handle. */
68 private final ItmConfig itmConfig;
71 * Instantiates a new vtep config schema listener.
76 * ITM config file handle
79 public VtepConfigSchemaListener(final DataBroker dataBroker, final ItmConfig itmConfig) {
80 super(VtepConfigSchema.class, VtepConfigSchemaListener.class);
81 this.dataBroker = dataBroker;
82 this.itmConfig = itmConfig;
87 registerListener(LogicalDatastoreType.CONFIGURATION, this.dataBroker);
88 LOG.info("VtepConfigSchemaListener Started");
94 * @see java.lang.AutoCloseable#close()
96 @SuppressWarnings("checkstyle:IllegalCatch")
100 LOG.info("VtepConfigSchemaListener Closed");
104 * Gets the wild card path.
106 * @return the wild card path
109 public InstanceIdentifier<VtepConfigSchema> getWildCardPath() {
110 return ItmUtils.getVtepConfigSchemaIdentifier();
117 * org.opendaylight.genius.mdsalutil.AbstractDataChangeListener#remove(
118 * org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
119 * org.opendaylight.yangtools.yang.binding.DataObject)
121 @SuppressWarnings("checkstyle:IllegalCatch")
123 protected void remove(InstanceIdentifier<VtepConfigSchema> identifier, VtepConfigSchema schema) {
124 LOG.trace("Received notification for VTEP config schema [{}] deleted.", schema.getSchemaName());
126 List<BigInteger> lstDpnIds = ItmUtils.getDpnIdList(schema.getDpnIds());
127 if (lstDpnIds != null && !lstDpnIds.isEmpty()) {
128 deleteVteps(schema, lstDpnIds);
131 // Delete IP pool corresponding to schema
132 // TODO: Ensure no schema exists with same subnet before deleting
133 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
134 deleteVtepIpPool(subnetCidr);
135 } catch (Exception e) {
136 String error = "Failed to handle DCN for delete VtepConfigSchema: " + schema;
145 * org.opendaylight.genius.mdsalutil.AbstractDataChangeListener#update(
146 * org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
147 * org.opendaylight.yangtools.yang.binding.DataObject,
148 * org.opendaylight.yangtools.yang.binding.DataObject)
150 @SuppressWarnings("checkstyle:IllegalCatch")
152 protected void update(InstanceIdentifier<VtepConfigSchema> identifier, VtepConfigSchema original,
153 VtepConfigSchema updated) {
154 LOG.error("Received DCN for updating VTEP Original schema: {}. Updated schema: {}", original, updated);
155 //LOG.trace("Received notification for VTEP config schema [{}] updated.", original.getSchemaName());
158 VtepConfigSchema orignalSchema = ItmUtils.validateVtepConfigSchema(original);
159 VtepConfigSchema updatedSchema = ItmUtils.validateVtepConfigSchema(updated);
161 if (doesDeleteAndAddSchemaRequired(original, updated)) {
162 LOG.error("Failed to handle DCN for updating VTEP schema {}. Original schema: {}. Updated schema: {}",
164 // TODO: handle updates
168 handleUpdateOfDpnIds(orignalSchema, updatedSchema);
170 } catch (Exception e) {
171 String error = "Failed to handle DCN for update VtepConfigSchema original:"
172 + original + ", updated: " + updated;
181 * org.opendaylight.genius.mdsalutil.AbstractDataChangeListener#add(org.
182 * opendaylight.yangtools.yang.binding.InstanceIdentifier,
183 * org.opendaylight.yangtools.yang.binding.DataObject)
185 @SuppressWarnings("checkstyle:IllegalCatch")
187 protected void add(InstanceIdentifier<VtepConfigSchema> identifier, VtepConfigSchema schema) {
188 // Construct the transport zones from the provided schemas and push it
190 LOG.trace("Add VtepConfigSchema: key: {} , value: {}", identifier, schema);
193 VtepConfigSchema validatedSchema = ItmUtils.validateForAddVtepConfigSchema(schema,
194 getAllVtepConfigSchemas());
196 VtepIpPool vtepIpPool = processAvailableIps(validatedSchema);
197 addVteps(validatedSchema, vtepIpPool);
198 } catch (Exception e) {
199 LOG.error("Failed to handle DCN for add VtepConfigSchema", e);
204 protected VtepConfigSchemaListener getDataTreeChangeListener() {
205 return VtepConfigSchemaListener.this;
209 * Handle update of dpn ids.
216 private void handleUpdateOfDpnIds(VtepConfigSchema original, VtepConfigSchema updated) {
217 // Handling add/delete DPNs from schema
218 List<DpnIds> originalDpnIds = original.getDpnIds() == null ? new ArrayList<>()
219 : original.getDpnIds();
220 List<DpnIds> updatedDpnIds = updated.getDpnIds() == null ? new ArrayList<>()
221 : updated.getDpnIds();
223 handleDeletedDpnsFromSchema(original, originalDpnIds, updatedDpnIds);
224 handleNewlyAddedDpnsToSchema(original, originalDpnIds, updatedDpnIds);
228 * Does delete and add schema required.
234 * @return true, if successful
236 private boolean doesDeleteAndAddSchemaRequired(VtepConfigSchema original, VtepConfigSchema updated) {
237 boolean delnAddRequired = false;
238 if (!StringUtils.equalsIgnoreCase(original.getPortName(), updated.getPortName())) {
239 delnAddRequired = true;
240 } else if (!Objects.equals(original.getVlanId(), updated.getVlanId())) {
241 delnAddRequired = true;
242 } else if (original.getSubnet() != null && !original.getSubnet().equals(updated.getSubnet())) {
243 delnAddRequired = true;
244 } else if (original.getGatewayIp() != null && !original.getGatewayIp().equals(updated.getGatewayIp())) {
245 delnAddRequired = true;
246 } else if (!StringUtils.equalsIgnoreCase(original.getTransportZoneName(), updated.getTransportZoneName())) {
247 delnAddRequired = true;
248 } else if (!original.getTunnelType().equals(updated.getTunnelType())) {
249 delnAddRequired = true;
251 return delnAddRequired;
255 * Handle newly added dpns to schema.
259 * @param originalDpnIds
260 * the original dpn ids
261 * @param updatedDpnIds
262 * the updated dpn ids
264 private void handleNewlyAddedDpnsToSchema(VtepConfigSchema original, List<DpnIds> originalDpnIds,
265 List<DpnIds> updatedDpnIds) {
266 LOG.trace("Handle Addition of DPNs from VTEP Original Dpn: {}. Updated Dpn: {}",
267 originalDpnIds, updatedDpnIds) ;
268 ArrayList<DpnIds> newlyAddedDpns = new ArrayList<>(updatedDpnIds);
269 newlyAddedDpns.removeAll(originalDpnIds);
270 LOG.debug("Newly added DPNs {} to VTEP config schema [{}].", newlyAddedDpns, original.getSchemaName());
271 if (!newlyAddedDpns.isEmpty()) {
272 VtepConfigSchema diffSchema = new VtepConfigSchemaBuilder(original).setDpnIds(newlyAddedDpns).build();
273 String subnetCidr = ItmUtils.getSubnetCidrAsString(original.getSubnet());
274 VtepIpPool vtepIpPool = getVtepIpPool(subnetCidr);
275 LOG.debug("Adding of DPNs in Diff Schema: {}", diffSchema) ;
276 addVteps(diffSchema, vtepIpPool);
281 * Handle deleted dpns from schema.
285 * @param originalDpnIds
286 * the original dpn ids
287 * @param updatedDpnIds
288 * the updated dpn ids
290 private void handleDeletedDpnsFromSchema(VtepConfigSchema original, List<DpnIds> originalDpnIds,
291 List<DpnIds> updatedDpnIds) {
292 ArrayList<DpnIds> deletedDpns = new ArrayList<>(originalDpnIds);
293 deletedDpns.removeAll(updatedDpnIds);
294 LOG.debug("DPNs to be removed DPNs {} from VTEP config schema [{}].", deletedDpns, original.getSchemaName());
295 if (!deletedDpns.isEmpty()) {
296 LOG.debug("Deleting of DPNs from VTEP Schema: {}. To be deleted Dpns: {}", original, deletedDpns) ;
297 deleteVteps(original, ItmUtils.getDpnIdList(deletedDpns));
302 * Gets all vtep config schemas.
304 * @return the all vtep config schemas
306 private List<VtepConfigSchema> getAllVtepConfigSchemas() {
307 return ItmUtils.read(LogicalDatastoreType.CONFIGURATION, ItmUtils.getVtepConfigSchemasIdentifier(),
308 this.dataBroker).toJavaUtil().map(VtepConfigSchemas::getVtepConfigSchema).orElse(null);
319 private void addVteps(VtepConfigSchema schema, VtepIpPool vtepIpPool) {
320 if (schema.getDpnIds() == null || schema.getDpnIds().isEmpty()) {
321 LOG.debug("DPN list is empty, skipping addVteps for schema: {}", schema);
325 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
326 if (vtepIpPool == null) {
327 LOG.error("VTEP config pool not found for subnetCidr {}. Failed to add VTEPs for schema {}", subnetCidr,
331 TepCommandHelper tepCommandHelper = new TepCommandHelper(this.dataBroker, itmConfig);
334 Class<? extends TunnelTypeBase> tunnelType = schema.getTunnelType() ;
335 if (tunnelType.equals(TunnelTypeVxlan.class)) {
336 tunType = ITMConstants.TUNNEL_TYPE_VXLAN;
338 tunType = ITMConstants.TUNNEL_TYPE_GRE;
340 tepCommandHelper.configureTunnelType(schema.getTransportZoneName(),
341 StringUtils.upperCase(tunType));
343 List<IpAddress> availableIps = vtepIpPool.getAvailableIpaddress();
344 List<IpAddress> newlyAllocatedIps = new ArrayList<>();
345 List<BigInteger> skippedDpnIds = new ArrayList<>();
347 String gatewayIp = handleGatewayIp(schema.getGatewayIp());
348 for (BigInteger dpnId : ItmUtils.getDpnIdList(schema.getDpnIds())) {
349 IpAddress ipAddress = getAnAvailableIP(availableIps);
350 if (ipAddress == null) {
351 skippedDpnIds.add(dpnId);
355 tepCommandHelper.createLocalCache(dpnId, schema.getPortName(), schema.getVlanId(),
356 String.valueOf(ipAddress.getValue()), subnetCidr, gatewayIp,
357 schema.getTransportZoneName(), null);
358 } catch (TepException e) {
359 LOG.error(e.getMessage());
361 newlyAllocatedIps.add(ipAddress);
363 if (!skippedDpnIds.isEmpty()) {
364 LOG.error("No available IP addresses in the VTEP config pool {}, skipping VTEP configurations for DPN's {}",
365 subnetCidr, skippedDpnIds);
368 if (!newlyAllocatedIps.isEmpty()) {
369 LOG.debug("Delete OnCommit and buildTeps in NewlyAddedDpns");
370 tepCommandHelper.deleteOnCommit();
371 tepCommandHelper.buildTeps();
372 allocateIpAddresses(newlyAllocatedIps, vtepIpPool, subnetCidr);
383 private String handleGatewayIp(IpAddress gatewayIp) {
384 String strGatewayIp = gatewayIp == null ? null : String.valueOf(gatewayIp.getValue());
385 if (StringUtils.isBlank(strGatewayIp) || StringUtils.equals(ITMConstants.DUMMY_IP_ADDRESS, strGatewayIp)) {
386 // To avoid a validation exception in TepCommandHelper
397 * @param lstDpnIdsToBeDeleted
398 * the dpn ids list to be deleted
400 private void deleteVteps(VtepConfigSchema schema, List<BigInteger> lstDpnIdsToBeDeleted) {
401 TepCommandHelper tepCommandHelper = new TepCommandHelper(this.dataBroker, itmConfig);
402 List<IpAddress> freeIps = new ArrayList<>();
404 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
405 String gatewayIp = handleGatewayIp(schema.getGatewayIp());
407 for (BigInteger dpnId : lstDpnIdsToBeDeleted) {
408 VtepsKey vtepkey = new VtepsKey(dpnId, schema.getPortName());
410 InstanceIdentifier<Vteps> vpath = InstanceIdentifier.builder(TransportZones.class)
411 .child(TransportZone.class, new TransportZoneKey(schema.getTransportZoneName()))
412 .child(Subnets.class, new SubnetsKey(schema.getSubnet())).child(Vteps.class, vtepkey).build();
415 Optional<Vteps> vtepOptional = ItmUtils.read(LogicalDatastoreType.CONFIGURATION, vpath, dataBroker);
416 if (vtepOptional.isPresent()) {
417 vtep = vtepOptional.get();
419 LOG.warn("VTEP doesn't exist for DPN [{}] and port [{}].", dpnId, schema.getPortName());
423 IpAddress ipAddress = vtep.getIpAddress();
425 tepCommandHelper.deleteVtep(dpnId, vtep.getPortname(), schema.getVlanId(),
426 String.valueOf(ipAddress.getValue()), subnetCidr, gatewayIp, schema.getTransportZoneName(), null);
427 } catch (TepException e) {
428 LOG.error(e.getMessage());
431 freeIps.add(ipAddress);
433 LOG.debug("Delete OnCommit in NewlyAddedDpns");
434 tepCommandHelper.deleteOnCommit();
435 deAllocateIpAddresses(freeIps, subnetCidr);
439 * Calculate available IPs from the subnet mask specified in the schema.
440 * Pushes the available and allocated IP address to config DS.
445 private VtepIpPool processAvailableIps(final VtepConfigSchema schema) {
446 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
447 SubnetUtils subnetUtils = new SubnetUtils(subnetCidr);
449 List<IpAddress> availableIps = calculateAvailableIps(subnetUtils, schema.getExcludeIpFilter(),
450 schema.getGatewayIp());
451 VtepIpPool vtepIpPool = new VtepIpPoolBuilder().setSubnetCidr(subnetCidr).setAvailableIpaddress(availableIps)
452 .setAllocatedIpaddress(new ArrayList<>()).build();
454 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
455 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), vtepIpPool);
456 LOG.info("Vtep IP Pool with key:{} added to config DS", subnetCidr);
461 * Gets the vtep ip pool.
465 * @return the vtep ip pool
467 private VtepIpPool getVtepIpPool(final String subnetCidr) {
468 return ItmUtils.read(LogicalDatastoreType.CONFIGURATION, ItmUtils.getVtepIpPoolIdentifier(subnetCidr),
469 this.dataBroker).orNull();
473 * Delete vtep ip pool.
478 private void deleteVtepIpPool(final String subnetCidr) {
479 if (StringUtils.isNotBlank(subnetCidr)) {
480 MDSALUtil.syncDelete(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
481 ItmUtils.getVtepIpPoolIdentifier(subnetCidr));
482 LOG.debug("Deleted Vtep IP Pool with key:{}", subnetCidr);
487 * Gets the an available ip.
489 * @param availableIps
491 * @return the an available ip
493 private IpAddress getAnAvailableIP(List<IpAddress> availableIps) {
494 // TODO: Sort IP Addresses, get the least value
495 IpAddress ipAddress = null;
496 if (availableIps != null && !availableIps.isEmpty()) {
497 ipAddress = availableIps.remove(0);
503 * Allocate ip addresses.
505 * @param allocatedIps
512 private void allocateIpAddresses(List<IpAddress> allocatedIps, VtepIpPool vtepIpPool, String subnetCidr) {
513 if (allocatedIps != null && !allocatedIps.isEmpty() && vtepIpPool != null) {
514 // Remove from the available IP address list and add to allocated IP
516 VtepIpPoolBuilder builder = new VtepIpPoolBuilder(vtepIpPool);
517 if (builder.getAvailableIpaddress() != null) {
518 builder.getAvailableIpaddress().removeAll(allocatedIps);
520 if (builder.getAllocatedIpaddress() == null) {
521 builder.setAllocatedIpaddress(allocatedIps);
523 builder.getAllocatedIpaddress().addAll(allocatedIps);
526 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
527 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), builder.build());
532 * De-allocate ip addresses.
539 private void deAllocateIpAddresses(List<IpAddress> freeIps, String subnetCidr) {
540 VtepIpPool vtepIpPool = getVtepIpPool(subnetCidr);
541 if (freeIps != null && !freeIps.isEmpty() && vtepIpPool != null) {
542 // Remove from the allocated IP address list and add to available IP
544 VtepIpPoolBuilder builder = new VtepIpPoolBuilder(vtepIpPool);
545 if (builder.getAllocatedIpaddress() != null) {
546 builder.getAllocatedIpaddress().removeAll(freeIps);
548 if (builder.getAvailableIpaddress() == null) {
549 builder.setAvailableIpaddress(freeIps);
551 builder.getAvailableIpaddress().addAll(freeIps);
554 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
555 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), builder.build());
556 LOG.debug("Vtep IP Pool with key:{} updated to config DS", subnetCidr);
561 * Calculate available ips.
565 * @param excludeIpFilter
566 * the exclude ip filter
571 private List<IpAddress> calculateAvailableIps(SubnetUtils subnetUtils, String excludeIpFilter,
572 IpAddress gatewayIp) {
573 List<IpAddress> lstAvailableIps = new ArrayList<>();
574 SubnetInfo subnetInfo = subnetUtils.getInfo();
575 String[] arrIpAddresses = subnetInfo.getAllAddresses();
577 for (String ipAddress : arrIpAddresses) {
578 lstAvailableIps.add(new IpAddress(ipAddress.toCharArray()));
580 lstAvailableIps.remove(gatewayIp);
581 lstAvailableIps.removeAll(ItmUtils.getExcludeIpAddresses(excludeIpFilter, subnetInfo));
583 return lstAvailableIps;