2 * Copyright (c) 2016 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 org.apache.commons.lang3.StringUtils;
13 import org.apache.commons.net.util.SubnetUtils;
14 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
15 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
16 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
17 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
19 import org.opendaylight.genius.itm.cli.TepCommandHelper;
20 import org.opendaylight.genius.itm.globals.ITMConstants;
21 import org.opendaylight.genius.itm.impl.ItmUtils;
22 import org.opendaylight.genius.mdsalutil.AbstractDataChangeListener;
23 import org.opendaylight.genius.mdsalutil.MDSALUtil;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.VtepConfigSchemas;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.vtep.config.schemas.VtepConfigSchema;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.vtep.config.schemas.VtepConfigSchemaBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.vtep.config.schemas.vtep.config.schema.DpnIds;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.vtep.ip.pools.VtepIpPool;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.vtep.ip.pools.VtepIpPoolBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.TransportZones;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZone;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZoneKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.Subnets;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.SubnetsKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.Vteps;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.VtepsKey;
40 import org.opendaylight.yangtools.concepts.ListenerRegistration;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
45 import java.math.BigInteger;
46 import java.util.ArrayList;
47 import java.util.List;
49 //import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.config.rev151102.VtepConfigSchemas;
50 //import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.config.rev151102.vtep.config.schemas.VtepConfigSchema;
51 //import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.config.rev151102.vtep.config.schemas.VtepConfigSchemaBuilder;
52 //import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.config.rev151102.vtep.ip.pools.VtepIpPool;
53 //import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.config.rev151102.vtep.ip.pools.VtepIpPoolBuilder;
56 * The listener class interested in processing data change on
57 * {@code VtepConfigSchema} objects.
59 * @see VtepConfigSchema
61 public class VtepConfigSchemaListener extends AbstractDataChangeListener<VtepConfigSchema> implements AutoCloseable {
63 /** The Constant LOG. */
64 private static final Logger LOG = LoggerFactory.getLogger(VtepConfigSchemaListener.class);
66 /** The listener registration. */
67 private ListenerRegistration<DataChangeListener> listenerRegistration;
69 /** The data broker. */
70 private final DataBroker dataBroker;
73 * Instantiates a new vtep config schema listener.
78 public VtepConfigSchemaListener(final DataBroker db) {
79 super(VtepConfigSchema.class);
87 * @see java.lang.AutoCloseable#close()
90 public void close() throws Exception {
91 if (this.listenerRegistration != null) {
93 this.listenerRegistration.close();
94 } catch (final Exception e) {
95 LOG.error("Error when cleaning up DataChangeListener.", e);
97 this.listenerRegistration = null;
99 LOG.info("VtepConfigSchemaListener Closed");
108 private void registerListener(final DataBroker db) {
110 this.listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
111 getWildCardPath(), VtepConfigSchemaListener.this, AsyncDataBroker.DataChangeScope.SUBTREE);
112 } catch (final Exception e) {
113 LOG.error("VtepConfigSchemaListener DataChange listener registration fail!", e);
114 throw new IllegalStateException("VtepConfigSchemaListener registration Listener failed.", e);
119 * Gets the wild card path.
121 * @return the wild card path
123 private InstanceIdentifier<VtepConfigSchema> getWildCardPath() {
124 return ItmUtils.getVtepConfigSchemaIdentifier();
131 * org.opendaylight.genius.mdsalutil.AbstractDataChangeListener#remove(
132 * org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
133 * org.opendaylight.yangtools.yang.binding.DataObject)
136 protected void remove(InstanceIdentifier<VtepConfigSchema> identifier, VtepConfigSchema schema) {
137 LOG.trace("Received notification for VTEP config schema [{}] deleted.", schema.getSchemaName());
139 List<BigInteger> lstDpnIds = ItmUtils.getDpnIdList(schema.getDpnIds());
140 if (lstDpnIds != null && !lstDpnIds.isEmpty()) {
141 deleteVteps(schema, lstDpnIds);
144 // Delete IP pool corresponding to schema
145 // TODO: Ensure no schema exists with same subnet before deleting
146 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
147 deleteVtepIpPool(subnetCidr);
148 } catch (Exception e) {
149 String error = new StringBuilder("Failed to handle DCN for delete VtepConfigSchema: ").append(schema)
159 * org.opendaylight.genius.mdsalutil.AbstractDataChangeListener#update(
160 * org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
161 * org.opendaylight.yangtools.yang.binding.DataObject,
162 * org.opendaylight.yangtools.yang.binding.DataObject)
165 protected void update(InstanceIdentifier<VtepConfigSchema> identifier, VtepConfigSchema original,
166 VtepConfigSchema updated) {
167 LOG.error("Receivedn DCN for updating VTEP Original schema: {}. Updated schema: {}",
169 //LOG.trace("Received notification for VTEP config schema [{}] updated.", original.getSchemaName());
172 VtepConfigSchema orignalSchema = ItmUtils.validateVtepConfigSchema(original);
173 VtepConfigSchema updatedSchema = ItmUtils.validateVtepConfigSchema(updated);
175 if (doesDeleteAndAddSchemaRequired(original, updated)) {
176 LOG.error("Failed to handle DCN for updating VTEP schema {}. Original schema: {}. Updated schema: {}",
178 // TODO: handle updates
182 handleUpdateOfDpnIds(orignalSchema, updatedSchema);
184 } catch (Exception e) {
185 String error = new StringBuilder("Failed to handle DCN for update VtepConfigSchema original:")
186 .append(original).append(", updated: ").append(updated).toString();
195 * org.opendaylight.genius.mdsalutil.AbstractDataChangeListener#add(org.
196 * opendaylight.yangtools.yang.binding.InstanceIdentifier,
197 * org.opendaylight.yangtools.yang.binding.DataObject)
200 protected void add(InstanceIdentifier<VtepConfigSchema> identifier, VtepConfigSchema schema) {
201 // Construct the transport zones from the provided schemas and push it
203 LOG.trace("Add VtepConfigSchema: key: {} , value: {}", identifier, schema);
206 VtepConfigSchema validatedSchema = ItmUtils.validateForAddVtepConfigSchema(schema,
207 getAllVtepConfigSchemas());
209 VtepIpPool vtepIpPool = processAvailableIps(validatedSchema);
210 addVteps(validatedSchema, vtepIpPool);
211 } catch (Exception e) {
212 LOG.error("Failed to handle DCN for add VtepConfigSchema: {}", e);
217 * Handle update of dpn ids.
224 private void handleUpdateOfDpnIds(VtepConfigSchema original, VtepConfigSchema updated) {
225 // Handling add/delete DPNs from schema
226 List<DpnIds> originalDpnIds = (original.getDpnIds() == null) ? new ArrayList<DpnIds>()
227 : original.getDpnIds();
228 List<DpnIds> updatedDpnIds = (updated.getDpnIds() == null) ? new ArrayList<DpnIds>()
229 : updated.getDpnIds();
231 handleDeletedDpnsFromSchema(original, originalDpnIds, updatedDpnIds);
232 handleNewlyAddedDpnsToSchema(original, originalDpnIds, updatedDpnIds);
236 * Does delete and add schema required.
242 * @return true, if successful
244 private boolean doesDeleteAndAddSchemaRequired(VtepConfigSchema original, VtepConfigSchema updated) {
245 boolean delnAddRequired = false;
246 if (!StringUtils.equalsIgnoreCase(original.getPortName(), updated.getPortName())) {
247 delnAddRequired = true;
248 } else if (original.getVlanId() != updated.getVlanId()) {
249 delnAddRequired = true;
250 } else if (original.getSubnet() != null && !original.getSubnet().equals(updated.getSubnet())) {
251 delnAddRequired = true;
252 } else if (original.getGatewayIp() != null && !original.getGatewayIp().equals(updated.getGatewayIp())) {
253 delnAddRequired = true;
254 } else if (!StringUtils.equalsIgnoreCase(original.getTransportZoneName(), updated.getTransportZoneName())) {
255 delnAddRequired = true;
256 } else if (!(original.getTunnelType().equals(updated.getTunnelType()) )) {
257 delnAddRequired = true;
259 return delnAddRequired;
263 * Handle newly added dpns to schema.
267 * @param originalDpnIds
268 * the original dpn ids
269 * @param updatedDpnIds
270 * the updated dpn ids
272 private void handleNewlyAddedDpnsToSchema(VtepConfigSchema original, List<DpnIds> originalDpnIds,
273 List<DpnIds> updatedDpnIds) {
274 LOG.trace("Handle Addition of DPNs from VTEP Original Dpn: {}. Updated Dpn: {}", originalDpnIds, updatedDpnIds) ;
275 ArrayList<DpnIds> newlyAddedDpns = new ArrayList<>(updatedDpnIds);
276 newlyAddedDpns.removeAll(originalDpnIds);
277 LOG.debug("Newly added DPNs {} to VTEP config schema [{}].", newlyAddedDpns, original.getSchemaName());
278 if (!newlyAddedDpns.isEmpty()) {
279 VtepConfigSchema diffSchema = new VtepConfigSchemaBuilder(original).setDpnIds(newlyAddedDpns).build();
280 String subnetCidr = ItmUtils.getSubnetCidrAsString(original.getSubnet());
281 VtepIpPool vtepIpPool = getVtepIpPool(subnetCidr);
282 LOG.debug("Adding of DPNs in Diff Schema: {}", diffSchema) ;
283 addVteps(diffSchema, vtepIpPool);
288 * Handle deleted dpns from schema.
292 * @param originalDpnIds
293 * the original dpn ids
294 * @param updatedDpnIds
295 * the updated dpn ids
297 private void handleDeletedDpnsFromSchema(VtepConfigSchema original, List<DpnIds> originalDpnIds,
298 List<DpnIds> updatedDpnIds) {
299 ArrayList<DpnIds> deletedDpns = new ArrayList<>(originalDpnIds);
300 deletedDpns.removeAll(updatedDpnIds);
301 LOG.debug("DPNs to be removed DPNs {} from VTEP config schema [{}].", deletedDpns, original.getSchemaName());
302 if (!deletedDpns.isEmpty()) {
303 LOG.debug("Deleting of DPNs from VTEP Schema: {}. To be deleted Dpns: {}", original, deletedDpns) ;
304 deleteVteps(original, ItmUtils.getDpnIdList(deletedDpns));
309 * Gets all vtep config schemas.
311 * @return the all vtep config schemas
313 private List<VtepConfigSchema> getAllVtepConfigSchemas() {
314 Optional<VtepConfigSchemas> schemas = ItmUtils.read(LogicalDatastoreType.CONFIGURATION,
315 ItmUtils.getVtepConfigSchemasIdentifier(), this.dataBroker);
316 if (schemas.isPresent()) {
317 return schemas.get().getVtepConfigSchema();
330 private void addVteps(VtepConfigSchema schema, VtepIpPool vtepIpPool) {
331 if (schema.getDpnIds() == null || schema.getDpnIds().isEmpty()) {
332 LOG.debug("DPN list is empty, skipping addVteps for schema: {}", schema);
336 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
337 if (vtepIpPool == null) {
338 LOG.error("VTEP config pool not found for subnetCidr {}. Failed to add VTEPs for schema {}", subnetCidr,
342 TepCommandHelper tepCommandHelper = new TepCommandHelper(this.dataBroker);
345 Class<? extends TunnelTypeBase> tunnelType = schema.getTunnelType() ;
346 if( tunnelType.equals(TunnelTypeVxlan.class))
347 tunType = ITMConstants.TUNNEL_TYPE_VXLAN ;
349 tunType = ITMConstants.TUNNEL_TYPE_GRE;
350 tepCommandHelper.configureTunnelType(schema.getTransportZoneName(),
351 StringUtils.upperCase(tunType));
353 List<IpAddress> availableIps = vtepIpPool.getAvailableIpaddress();
354 List<IpAddress> newlyAllocatedIps = new ArrayList<>();
355 List<BigInteger> skippedDpnIds = new ArrayList<>();
357 String gatewayIp = handleGatewayIp(schema.getGatewayIp());
358 for (BigInteger dpnId : ItmUtils.getDpnIdList(schema.getDpnIds())) {
359 IpAddress ipAddress = getAnAvailableIP(availableIps);
360 if (ipAddress == null) {
361 skippedDpnIds.add(dpnId);
364 tepCommandHelper.createLocalCache(dpnId, schema.getPortName(), schema.getVlanId(),
365 String.valueOf(ipAddress.getValue()), subnetCidr, gatewayIp, schema.getTransportZoneName());
366 newlyAllocatedIps.add(ipAddress);
368 if (!skippedDpnIds.isEmpty()) {
369 LOG.error("No available IP addresses in the VTEP config pool {}, skipping VTEP configurations for DPN's {}",
370 subnetCidr, skippedDpnIds);
373 if (!newlyAllocatedIps.isEmpty()) {
374 LOG.debug( "Delete OnCommit and buildTeps in NewlyAddedDpns");
375 tepCommandHelper.deleteOnCommit();
376 tepCommandHelper.buildTeps();
377 allocateIpAddresses(newlyAllocatedIps, vtepIpPool, subnetCidr);
388 private String handleGatewayIp(IpAddress gatewayIp) {
389 String strGatewayIp = (gatewayIp == null) ? null : String.valueOf(gatewayIp.getValue());
390 if (StringUtils.isBlank(strGatewayIp) || StringUtils.equals(ITMConstants.DUMMY_IP_ADDRESS, strGatewayIp)) {
391 // To avoid a validation exception in TepCommandHelper
402 * @param lstDpnIdsToBeDeleted
403 * the dpn ids list to be deleted
405 private void deleteVteps(VtepConfigSchema schema, List<BigInteger> lstDpnIdsToBeDeleted) {
406 TepCommandHelper tepCommandHelper = new TepCommandHelper(this.dataBroker);
407 List<IpAddress> freeIps = new ArrayList<>();
409 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
410 String gatewayIp = handleGatewayIp(schema.getGatewayIp());
412 for (BigInteger dpnId : lstDpnIdsToBeDeleted) {
413 VtepsKey vtepkey = new VtepsKey(dpnId, schema.getPortName());
415 InstanceIdentifier<Vteps> vpath = InstanceIdentifier.builder(TransportZones.class)
416 .child(TransportZone.class, new TransportZoneKey(schema.getTransportZoneName()))
417 .child(Subnets.class, new SubnetsKey(schema.getSubnet())).child(Vteps.class, vtepkey).build();
420 Optional<Vteps> vtepOptional = ItmUtils.read(LogicalDatastoreType.CONFIGURATION, vpath, dataBroker);
421 if (vtepOptional.isPresent()) {
422 vtep = vtepOptional.get();
424 LOG.warn("VTEP doesn't exist for DPN [{}] and port [{}].", dpnId, schema.getPortName());
428 IpAddress ipAddress = vtep.getIpAddress();
429 tepCommandHelper.deleteVtep(dpnId, vtep.getPortname(), schema.getVlanId(),
430 String.valueOf(ipAddress.getValue()), subnetCidr, gatewayIp, schema.getTransportZoneName());
432 freeIps.add(ipAddress);
434 LOG.debug( "Delete OnCommit in NewlyAddedDpns");
435 tepCommandHelper.deleteOnCommit();
436 deAllocateIpAddresses(freeIps, subnetCidr);
440 * Calculate available IPs from the subnet mask specified in the schema.
441 * Pushes the available and allocated IP address to config DS.
446 private VtepIpPool processAvailableIps(final VtepConfigSchema schema) {
447 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
448 SubnetUtils subnetUtils = new SubnetUtils(subnetCidr);
450 List<IpAddress> availableIps = calculateAvailableIps(subnetUtils, schema.getExcludeIpFilter(),
451 schema.getGatewayIp());
452 VtepIpPool vtepIpPool = new VtepIpPoolBuilder().setSubnetCidr(subnetCidr).setAvailableIpaddress(availableIps)
453 .setAllocatedIpaddress(new ArrayList<IpAddress>()).build();
455 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
456 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), vtepIpPool);
457 LOG.info("Vtep IP Pool with key:{} added to config DS", subnetCidr);
462 * Gets the vtep ip pool.
466 * @return the vtep ip pool
468 private VtepIpPool getVtepIpPool(final String subnetCidr) {
469 Optional<VtepIpPool> vtepIpPool = ItmUtils.read(LogicalDatastoreType.CONFIGURATION,
470 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), this.dataBroker);
471 if (vtepIpPool.isPresent()) {
472 return vtepIpPool.get();
478 * Delete vtep ip pool.
483 private void deleteVtepIpPool(final String subnetCidr) {
484 if (StringUtils.isNotBlank(subnetCidr)) {
485 MDSALUtil.syncDelete(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
486 ItmUtils.getVtepIpPoolIdentifier(subnetCidr));
487 LOG.debug("Deleted Vtep IP Pool with key:{}", subnetCidr);
492 * Gets the an available ip.
494 * @param availableIps
496 * @return the an available ip
498 private IpAddress getAnAvailableIP(List<IpAddress> availableIps) {
499 // TODO: Sort IP Addresses, get the least value
500 IpAddress ipAddress = null;
501 if (availableIps != null && !availableIps.isEmpty()) {
502 ipAddress = availableIps.remove(0);
508 * Allocate ip addresses.
510 * @param allocatedIps
517 private void allocateIpAddresses(List<IpAddress> allocatedIps, VtepIpPool vtepIpPool, String subnetCidr) {
518 if (allocatedIps != null && !allocatedIps.isEmpty() && vtepIpPool != null) {
519 // Remove from the available IP address list and add to allocated IP
521 VtepIpPoolBuilder builder = new VtepIpPoolBuilder(vtepIpPool);
522 if (builder.getAvailableIpaddress() != null) {
523 builder.getAvailableIpaddress().removeAll(allocatedIps);
525 if (builder.getAllocatedIpaddress() == null) {
526 builder.setAllocatedIpaddress(allocatedIps);
528 builder.getAllocatedIpaddress().addAll(allocatedIps);
531 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
532 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), builder.build());
537 * De-allocate ip addresses.
544 private void deAllocateIpAddresses(List<IpAddress> freeIps, String subnetCidr) {
545 VtepIpPool vtepIpPool = getVtepIpPool(subnetCidr);
546 if (freeIps != null && !freeIps.isEmpty() && vtepIpPool != null) {
547 // Remove from the allocated IP address list and add to available IP
549 VtepIpPoolBuilder builder = new VtepIpPoolBuilder(vtepIpPool);
550 if (builder.getAllocatedIpaddress() != null) {
551 builder.getAllocatedIpaddress().removeAll(freeIps);
553 if (builder.getAvailableIpaddress() == null) {
554 builder.setAvailableIpaddress(freeIps);
556 builder.getAvailableIpaddress().addAll(freeIps);
559 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
560 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), builder.build());
561 LOG.debug("Vtep IP Pool with key:{} updated to config DS", subnetCidr);
566 * Calculate available ips.
570 * @param excludeIpFilter
571 * the exclude ip filter
576 private List<IpAddress> calculateAvailableIps(SubnetUtils subnetUtils, String excludeIpFilter,
577 IpAddress gatewayIp) {
578 List<IpAddress> lstAvailableIps = new ArrayList<>();
579 SubnetInfo subnetInfo = subnetUtils.getInfo();
580 String[] arrIpAddresses = subnetInfo.getAllAddresses();
582 for (String ipAddress : arrIpAddresses) {
583 lstAvailableIps.add(new IpAddress(ipAddress.toCharArray()));
585 lstAvailableIps.remove(gatewayIp);
586 lstAvailableIps.removeAll(ItmUtils.getExcludeIpAddresses(excludeIpFilter, subnetInfo));
588 return lstAvailableIps;