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.vpnservice.itm.listeners;
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.List;
15 import org.apache.commons.lang3.StringUtils;
16 import org.apache.commons.net.util.SubnetUtils;
17 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
20 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
21 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
22 import org.opendaylight.vpnservice.itm.cli.TepCommandHelper;
23 import org.opendaylight.vpnservice.itm.globals.ITMConstants;
24 import org.opendaylight.vpnservice.itm.impl.ItmUtils;
25 import org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener;
26 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.config.rev151102.VtepConfigSchemas;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.config.rev151102.vtep.config.schemas.VtepConfigSchema;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.config.rev151102.vtep.config.schemas.VtepConfigSchemaBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.config.rev151102.vtep.ip.pools.VtepIpPool;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.config.rev151102.vtep.ip.pools.VtepIpPoolBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.config.rev151102.vtep.config.schemas.vtep.config.schema.DpnIds;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.config.rev151102.vtep.config.schemas.vtep.config.schema.DpnIdsKey;
35 //import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.config.rev151102.VtepConfigSchemas;
36 //import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.config.rev151102.vtep.config.schemas.VtepConfigSchema;
37 //import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.config.rev151102.vtep.config.schemas.VtepConfigSchemaBuilder;
38 //import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.config.rev151102.vtep.ip.pools.VtepIpPool;
39 //import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.config.rev151102.vtep.ip.pools.VtepIpPoolBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.TunnelTypeBase;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.TunnelTypeGre;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.TunnelTypeVxlan;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rev150701.TransportZones;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rev150701.transport.zones.TransportZone;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rev150701.transport.zones.TransportZoneKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rev150701.transport.zones.transport.zone.Subnets;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rev150701.transport.zones.transport.zone.SubnetsKey;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rev150701.transport.zones.transport.zone.subnets.Vteps;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rev150701.transport.zones.transport.zone.subnets.VtepsKey;
50 import org.opendaylight.yangtools.concepts.ListenerRegistration;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
55 import com.google.common.base.Optional;
58 * The listener class interested in processing data change on
59 * {@code VtepConfigSchema} objects.
61 * @see VtepConfigSchema
63 public class VtepConfigSchemaListener extends AbstractDataChangeListener<VtepConfigSchema> implements AutoCloseable {
65 /** The Constant LOG. */
66 private static final Logger LOG = LoggerFactory.getLogger(VtepConfigSchemaListener.class);
68 /** The listener registration. */
69 private ListenerRegistration<DataChangeListener> listenerRegistration;
71 /** The data broker. */
72 private final DataBroker dataBroker;
75 * Instantiates a new vtep config schema listener.
80 public VtepConfigSchemaListener(final DataBroker db) {
81 super(VtepConfigSchema.class);
89 * @see java.lang.AutoCloseable#close()
92 public void close() throws Exception {
93 if (this.listenerRegistration != null) {
95 this.listenerRegistration.close();
96 } catch (final Exception e) {
97 LOG.error("Error when cleaning up DataChangeListener.", e);
99 this.listenerRegistration = null;
101 LOG.info("VtepConfigSchemaListener Closed");
110 private void registerListener(final DataBroker db) {
112 this.listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
113 getWildCardPath(), VtepConfigSchemaListener.this, AsyncDataBroker.DataChangeScope.SUBTREE);
114 } catch (final Exception e) {
115 LOG.error("VtepConfigSchemaListener DataChange listener registration fail!", e);
116 throw new IllegalStateException("VtepConfigSchemaListener registration Listener failed.", e);
121 * Gets the wild card path.
123 * @return the wild card path
125 private InstanceIdentifier<VtepConfigSchema> getWildCardPath() {
126 return ItmUtils.getVtepConfigSchemaIdentifier();
133 * org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener#remove(
134 * org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
135 * org.opendaylight.yangtools.yang.binding.DataObject)
138 protected void remove(InstanceIdentifier<VtepConfigSchema> identifier, VtepConfigSchema schema) {
139 LOG.trace("Received notification for VTEP config schema [{}] deleted.", schema.getSchemaName());
141 List<BigInteger> lstDpnIds = ItmUtils.getDpnIdList(schema.getDpnIds());
142 if (lstDpnIds != null && !lstDpnIds.isEmpty()) {
143 deleteVteps(schema, lstDpnIds);
146 // Delete IP pool corresponding to schema
147 // TODO: Ensure no schema exists with same subnet before deleting
148 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
149 deleteVtepIpPool(subnetCidr);
150 } catch (Exception e) {
151 String error = new StringBuilder("Failed to handle DCN for delete VtepConfigSchema: ").append(schema)
161 * org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener#update(
162 * org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
163 * org.opendaylight.yangtools.yang.binding.DataObject,
164 * org.opendaylight.yangtools.yang.binding.DataObject)
167 protected void update(InstanceIdentifier<VtepConfigSchema> identifier, VtepConfigSchema original,
168 VtepConfigSchema updated) {
169 LOG.error("Receivedn DCN for updating VTEP Original schema: {}. Updated schema: {}",
171 //LOG.trace("Received notification for VTEP config schema [{}] updated.", original.getSchemaName());
174 VtepConfigSchema orignalSchema = ItmUtils.validateVtepConfigSchema(original);
175 VtepConfigSchema updatedSchema = ItmUtils.validateVtepConfigSchema(updated);
177 if (doesDeleteAndAddSchemaRequired(original, updated)) {
178 LOG.error("Failed to handle DCN for updating VTEP schema {}. Original schema: {}. Updated schema: {}",
180 // TODO: handle updates
184 handleUpdateOfDpnIds(orignalSchema, updatedSchema);
186 } catch (Exception e) {
187 String error = new StringBuilder("Failed to handle DCN for update VtepConfigSchema original:")
188 .append(original).append(", updated: ").append(updated).toString();
197 * org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener#add(org.
198 * opendaylight.yangtools.yang.binding.InstanceIdentifier,
199 * org.opendaylight.yangtools.yang.binding.DataObject)
202 protected void add(InstanceIdentifier<VtepConfigSchema> identifier, VtepConfigSchema schema) {
203 // Construct the transport zones from the provided schemas and push it
205 LOG.trace("Add VtepConfigSchema: key: {} , value: {}", identifier, schema);
208 VtepConfigSchema validatedSchema = ItmUtils.validateForAddVtepConfigSchema(schema,
209 getAllVtepConfigSchemas());
211 VtepIpPool vtepIpPool = processAvailableIps(validatedSchema);
212 addVteps(validatedSchema, vtepIpPool);
213 } catch (Exception e) {
214 LOG.error("Failed to handle DCN for add VtepConfigSchema: {}", e);
219 * Handle update of dpn ids.
226 private void handleUpdateOfDpnIds(VtepConfigSchema original, VtepConfigSchema updated) {
227 // Handling add/delete DPNs from schema
228 List<DpnIds> originalDpnIds = (original.getDpnIds() == null) ? new ArrayList<DpnIds>()
229 : original.getDpnIds();
230 List<DpnIds> updatedDpnIds = (updated.getDpnIds() == null) ? new ArrayList<DpnIds>()
231 : updated.getDpnIds();
233 handleDeletedDpnsFromSchema(original, originalDpnIds, updatedDpnIds);
234 handleNewlyAddedDpnsToSchema(original, originalDpnIds, updatedDpnIds);
238 * Does delete and add schema required.
244 * @return true, if successful
246 private boolean doesDeleteAndAddSchemaRequired(VtepConfigSchema original, VtepConfigSchema updated) {
247 boolean delnAddRequired = false;
248 if (!StringUtils.equalsIgnoreCase(original.getPortName(), updated.getPortName())) {
249 delnAddRequired = true;
250 } else if (original.getVlanId() != updated.getVlanId()) {
251 delnAddRequired = true;
252 } else if (original.getSubnet() != null && !original.getSubnet().equals(updated.getSubnet())) {
253 delnAddRequired = true;
254 } else if (original.getGatewayIp() != null && !original.getGatewayIp().equals(updated.getGatewayIp())) {
255 delnAddRequired = true;
256 } else if (!StringUtils.equalsIgnoreCase(original.getTransportZoneName(), updated.getTransportZoneName())) {
257 delnAddRequired = true;
258 } else if (!(original.getTunnelType().equals(updated.getTunnelType()) )) {
259 delnAddRequired = true;
261 return delnAddRequired;
265 * Handle newly added dpns to schema.
269 * @param originalDpnIds
270 * the original dpn ids
271 * @param updatedDpnIds
272 * the updated dpn ids
274 private void handleNewlyAddedDpnsToSchema(VtepConfigSchema original, List<DpnIds> originalDpnIds,
275 List<DpnIds> updatedDpnIds) {
276 LOG.trace("Handle Addition of DPNs from VTEP Original Dpn: {}. Updated Dpn: {}", originalDpnIds, updatedDpnIds) ;
277 ArrayList<DpnIds> newlyAddedDpns = new ArrayList<>(updatedDpnIds);
278 newlyAddedDpns.removeAll(originalDpnIds);
279 LOG.debug("Newly added DPNs {} to VTEP config schema [{}].", newlyAddedDpns, original.getSchemaName());
280 if (!newlyAddedDpns.isEmpty()) {
281 VtepConfigSchema diffSchema = new VtepConfigSchemaBuilder(original).setDpnIds(newlyAddedDpns).build();
282 String subnetCidr = ItmUtils.getSubnetCidrAsString(original.getSubnet());
283 VtepIpPool vtepIpPool = getVtepIpPool(subnetCidr);
284 LOG.debug("Adding of DPNs in Diff Schema: {}", diffSchema) ;
285 addVteps(diffSchema, vtepIpPool);
290 * Handle deleted dpns from schema.
294 * @param originalDpnIds
295 * the original dpn ids
296 * @param updatedDpnIds
297 * the updated dpn ids
299 private void handleDeletedDpnsFromSchema(VtepConfigSchema original, List<DpnIds> originalDpnIds,
300 List<DpnIds> updatedDpnIds) {
301 ArrayList<DpnIds> deletedDpns = new ArrayList<>(originalDpnIds);
302 deletedDpns.removeAll(updatedDpnIds);
303 LOG.debug("DPNs to be removed DPNs {} from VTEP config schema [{}].", deletedDpns, original.getSchemaName());
304 if (!deletedDpns.isEmpty()) {
305 LOG.debug("Deleting of DPNs from VTEP Schema: {}. To be deleted Dpns: {}", original, deletedDpns) ;
306 deleteVteps(original, ItmUtils.getDpnIdList(deletedDpns));
311 * Gets all vtep config schemas.
313 * @return the all vtep config schemas
315 private List<VtepConfigSchema> getAllVtepConfigSchemas() {
316 Optional<VtepConfigSchemas> schemas = ItmUtils.read(LogicalDatastoreType.CONFIGURATION,
317 ItmUtils.getVtepConfigSchemasIdentifier(), this.dataBroker);
318 if (schemas.isPresent()) {
319 return schemas.get().getVtepConfigSchema();
332 private void addVteps(VtepConfigSchema schema, VtepIpPool vtepIpPool) {
333 if (schema.getDpnIds() == null || schema.getDpnIds().isEmpty()) {
334 LOG.debug("DPN list is empty, skipping addVteps for schema: {}", schema);
338 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
339 if (vtepIpPool == null) {
340 LOG.error("VTEP config pool not found for subnetCidr {}. Failed to add VTEPs for schema {}", subnetCidr,
344 TepCommandHelper tepCommandHelper = new TepCommandHelper(this.dataBroker);
347 Class<? extends TunnelTypeBase> tunnelType = schema.getTunnelType() ;
348 if( tunnelType.equals(TunnelTypeVxlan.class))
349 tunType = ITMConstants.TUNNEL_TYPE_VXLAN ;
351 tunType = ITMConstants.TUNNEL_TYPE_GRE;
352 tepCommandHelper.configureTunnelType(schema.getTransportZoneName(),
353 StringUtils.upperCase(tunType));
355 List<IpAddress> availableIps = vtepIpPool.getAvailableIpaddress();
356 List<IpAddress> newlyAllocatedIps = new ArrayList<>();
357 List<BigInteger> skippedDpnIds = new ArrayList<>();
359 String gatewayIp = handleGatewayIp(schema.getGatewayIp());
360 for (BigInteger dpnId : ItmUtils.getDpnIdList(schema.getDpnIds())) {
361 IpAddress ipAddress = getAnAvailableIP(availableIps);
362 if (ipAddress == null) {
363 skippedDpnIds.add(dpnId);
366 tepCommandHelper.createLocalCache(dpnId, schema.getPortName(), schema.getVlanId(),
367 String.valueOf(ipAddress.getValue()), subnetCidr, gatewayIp, schema.getTransportZoneName());
368 newlyAllocatedIps.add(ipAddress);
370 if (!skippedDpnIds.isEmpty()) {
371 LOG.error("No available IP addresses in the VTEP config pool {}, skipping VTEP configurations for DPN's {}",
372 subnetCidr, skippedDpnIds);
375 if (!newlyAllocatedIps.isEmpty()) {
376 LOG.debug( "Delete OnCommit and buildTeps in NewlyAddedDpns");
377 tepCommandHelper.deleteOnCommit();
378 tepCommandHelper.buildTeps();
379 allocateIpAddresses(newlyAllocatedIps, vtepIpPool, subnetCidr);
390 private String handleGatewayIp(IpAddress gatewayIp) {
391 String strGatewayIp = (gatewayIp == null) ? null : String.valueOf(gatewayIp.getValue());
392 if (StringUtils.isBlank(strGatewayIp) || StringUtils.equals(ITMConstants.DUMMY_IP_ADDRESS, strGatewayIp)) {
393 // To avoid a validation exception in TepCommandHelper
404 * @param lstDpnIdsToBeDeleted
405 * the dpn ids list to be deleted
407 private void deleteVteps(VtepConfigSchema schema, List<BigInteger> lstDpnIdsToBeDeleted) {
408 TepCommandHelper tepCommandHelper = new TepCommandHelper(this.dataBroker);
409 List<IpAddress> freeIps = new ArrayList<>();
411 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
412 String gatewayIp = handleGatewayIp(schema.getGatewayIp());
414 for (BigInteger dpnId : lstDpnIdsToBeDeleted) {
415 VtepsKey vtepkey = new VtepsKey(dpnId, schema.getPortName());
417 InstanceIdentifier<Vteps> vpath = InstanceIdentifier.builder(TransportZones.class)
418 .child(TransportZone.class, new TransportZoneKey(schema.getTransportZoneName()))
419 .child(Subnets.class, new SubnetsKey(schema.getSubnet())).child(Vteps.class, vtepkey).build();
422 Optional<Vteps> vtepOptional = ItmUtils.read(LogicalDatastoreType.CONFIGURATION, vpath, dataBroker);
423 if (vtepOptional.isPresent()) {
424 vtep = vtepOptional.get();
426 LOG.warn("VTEP doesn't exist for DPN [{}] and port [{}].", dpnId, schema.getPortName());
430 IpAddress ipAddress = vtep.getIpAddress();
431 tepCommandHelper.deleteVtep(dpnId, vtep.getPortname(), schema.getVlanId(),
432 String.valueOf(ipAddress.getValue()), subnetCidr, gatewayIp, schema.getTransportZoneName());
434 freeIps.add(ipAddress);
436 LOG.debug( "Delete OnCommit in NewlyAddedDpns");
437 tepCommandHelper.deleteOnCommit();
438 deAllocateIpAddresses(freeIps, subnetCidr);
442 * Calculate available IPs from the subnet mask specified in the schema.
443 * Pushes the available and allocated IP address to config DS.
448 private VtepIpPool processAvailableIps(final VtepConfigSchema schema) {
449 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
450 SubnetUtils subnetUtils = new SubnetUtils(subnetCidr);
452 List<IpAddress> availableIps = calculateAvailableIps(subnetUtils, schema.getExcludeIpFilter(),
453 schema.getGatewayIp());
454 VtepIpPool vtepIpPool = new VtepIpPoolBuilder().setSubnetCidr(subnetCidr).setAvailableIpaddress(availableIps)
455 .setAllocatedIpaddress(new ArrayList<IpAddress>()).build();
457 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
458 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), vtepIpPool);
459 LOG.info("Vtep IP Pool with key:{} added to config DS", subnetCidr);
464 * Gets the vtep ip pool.
468 * @return the vtep ip pool
470 private VtepIpPool getVtepIpPool(final String subnetCidr) {
471 Optional<VtepIpPool> vtepIpPool = ItmUtils.read(LogicalDatastoreType.CONFIGURATION,
472 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), this.dataBroker);
473 if (vtepIpPool.isPresent()) {
474 return vtepIpPool.get();
480 * Delete vtep ip pool.
485 private void deleteVtepIpPool(final String subnetCidr) {
486 if (StringUtils.isNotBlank(subnetCidr)) {
487 MDSALUtil.syncDelete(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
488 ItmUtils.getVtepIpPoolIdentifier(subnetCidr));
489 LOG.debug("Deleted Vtep IP Pool with key:{}", subnetCidr);
494 * Gets the an available ip.
496 * @param availableIps
498 * @return the an available ip
500 private IpAddress getAnAvailableIP(List<IpAddress> availableIps) {
501 // TODO: Sort IP Addresses, get the least value
502 IpAddress ipAddress = null;
503 if (availableIps != null && !availableIps.isEmpty()) {
504 ipAddress = availableIps.remove(0);
510 * Allocate ip addresses.
512 * @param allocatedIps
519 private void allocateIpAddresses(List<IpAddress> allocatedIps, VtepIpPool vtepIpPool, String subnetCidr) {
520 if (allocatedIps != null && !allocatedIps.isEmpty() && vtepIpPool != null) {
521 // Remove from the available IP address list and add to allocated IP
523 VtepIpPoolBuilder builder = new VtepIpPoolBuilder(vtepIpPool);
524 if (builder.getAvailableIpaddress() != null) {
525 builder.getAvailableIpaddress().removeAll(allocatedIps);
527 if (builder.getAllocatedIpaddress() == null) {
528 builder.setAllocatedIpaddress(allocatedIps);
530 builder.getAllocatedIpaddress().addAll(allocatedIps);
533 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
534 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), builder.build());
539 * De-allocate ip addresses.
546 private void deAllocateIpAddresses(List<IpAddress> freeIps, String subnetCidr) {
547 VtepIpPool vtepIpPool = getVtepIpPool(subnetCidr);
548 if (freeIps != null && !freeIps.isEmpty() && vtepIpPool != null) {
549 // Remove from the allocated IP address list and add to available IP
551 VtepIpPoolBuilder builder = new VtepIpPoolBuilder(vtepIpPool);
552 if (builder.getAllocatedIpaddress() != null) {
553 builder.getAllocatedIpaddress().removeAll(freeIps);
555 if (builder.getAvailableIpaddress() == null) {
556 builder.setAvailableIpaddress(freeIps);
558 builder.getAvailableIpaddress().addAll(freeIps);
561 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
562 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), builder.build());
563 LOG.debug("Vtep IP Pool with key:{} updated to config DS", subnetCidr);
568 * Calculate available ips.
572 * @param excludeIpFilter
573 * the exclude ip filter
578 private List<IpAddress> calculateAvailableIps(SubnetUtils subnetUtils, String excludeIpFilter,
579 IpAddress gatewayIp) {
580 List<IpAddress> lstAvailableIps = new ArrayList<>();
581 SubnetInfo subnetInfo = subnetUtils.getInfo();
582 String[] arrIpAddresses = subnetInfo.getAllAddresses();
584 for (String ipAddress : arrIpAddresses) {
585 lstAvailableIps.add(new IpAddress(ipAddress.toCharArray()));
587 lstAvailableIps.remove(gatewayIp);
588 lstAvailableIps.removeAll(ItmUtils.getExcludeIpAddresses(excludeIpFilter, subnetInfo));
590 return lstAvailableIps;