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
8 package org.opendaylight.vpnservice.itm.listeners;
10 import java.math.BigInteger;
11 import java.util.ArrayList;
12 import java.util.List;
14 import org.apache.commons.lang3.StringUtils;
15 import org.apache.commons.net.util.SubnetUtils;
16 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
18 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
19 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.vpnservice.itm.cli.TepCommandHelper;
22 import org.opendaylight.vpnservice.itm.globals.ITMConstants;
23 import org.opendaylight.vpnservice.itm.impl.ItmUtils;
24 import org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener;
25 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.config.rev151102.VtepConfigSchemas;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.config.rev151102.vtep.config.schemas.VtepConfigSchema;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.config.rev151102.vtep.config.schemas.VtepConfigSchemaBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.config.rev151102.vtep.ip.pools.VtepIpPool;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.config.rev151102.vtep.ip.pools.VtepIpPoolBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.config.rev151102.vtep.config.schemas.vtep.config.schema.DpnIds;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.config.rev151102.vtep.config.schemas.vtep.config.schema.DpnIdsKey;
34 //import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.config.rev151102.VtepConfigSchemas;
35 //import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.config.rev151102.vtep.config.schemas.VtepConfigSchema;
36 //import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.config.rev151102.vtep.config.schemas.VtepConfigSchemaBuilder;
37 //import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.config.rev151102.vtep.ip.pools.VtepIpPool;
38 //import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.config.rev151102.vtep.ip.pools.VtepIpPoolBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.TunnelTypeBase;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.TunnelTypeGre;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.TunnelTypeVxlan;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rev150701.TransportZones;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rev150701.transport.zones.TransportZone;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rev150701.transport.zones.TransportZoneKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rev150701.transport.zones.transport.zone.Subnets;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rev150701.transport.zones.transport.zone.SubnetsKey;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rev150701.transport.zones.transport.zone.subnets.Vteps;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rev150701.transport.zones.transport.zone.subnets.VtepsKey;
49 import org.opendaylight.yangtools.concepts.ListenerRegistration;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
54 import com.google.common.base.Optional;
57 * The listener class interested in processing data change on
58 * {@code VtepConfigSchema} objects.
60 * @see VtepConfigSchema
62 public class VtepConfigSchemaListener extends AbstractDataChangeListener<VtepConfigSchema> implements AutoCloseable {
64 /** The Constant LOG. */
65 private static final Logger LOG = LoggerFactory.getLogger(VtepConfigSchemaListener.class);
67 /** The listener registration. */
68 private ListenerRegistration<DataChangeListener> listenerRegistration;
70 /** The data broker. */
71 private final DataBroker dataBroker;
74 * Instantiates a new vtep config schema listener.
79 public VtepConfigSchemaListener(final DataBroker db) {
80 super(VtepConfigSchema.class);
88 * @see java.lang.AutoCloseable#close()
91 public void close() throws Exception {
92 if (this.listenerRegistration != null) {
94 this.listenerRegistration.close();
95 } catch (final Exception e) {
96 LOG.error("Error when cleaning up DataChangeListener.", e);
98 this.listenerRegistration = null;
100 LOG.info("VtepConfigSchemaListener Closed");
109 private void registerListener(final DataBroker db) {
111 this.listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
112 getWildCardPath(), VtepConfigSchemaListener.this, AsyncDataBroker.DataChangeScope.SUBTREE);
113 } catch (final Exception e) {
114 LOG.error("VtepConfigSchemaListener DataChange listener registration fail!", e);
115 throw new IllegalStateException("VtepConfigSchemaListener registration Listener failed.", e);
120 * Gets the wild card path.
122 * @return the wild card path
124 private InstanceIdentifier<VtepConfigSchema> getWildCardPath() {
125 return ItmUtils.getVtepConfigSchemaIdentifier();
132 * org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener#remove(
133 * org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
134 * org.opendaylight.yangtools.yang.binding.DataObject)
137 protected void remove(InstanceIdentifier<VtepConfigSchema> identifier, VtepConfigSchema schema) {
138 LOG.trace("Received notification for VTEP config schema [{}] deleted.", schema.getSchemaName());
140 List<BigInteger> lstDpnIds = ItmUtils.getDpnIdList(schema.getDpnIds());
141 if (lstDpnIds != null && !lstDpnIds.isEmpty()) {
142 deleteVteps(schema, lstDpnIds);
145 // Delete IP pool corresponding to schema
146 // TODO: Ensure no schema exists with same subnet before deleting
147 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
148 deleteVtepIpPool(subnetCidr);
149 } catch (Exception e) {
150 String error = new StringBuilder("Failed to handle DCN for delete VtepConfigSchema: ").append(schema)
160 * org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener#update(
161 * org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
162 * org.opendaylight.yangtools.yang.binding.DataObject,
163 * org.opendaylight.yangtools.yang.binding.DataObject)
166 protected void update(InstanceIdentifier<VtepConfigSchema> identifier, VtepConfigSchema original,
167 VtepConfigSchema updated) {
168 LOG.error("Receivedn DCN for updating VTEP Original schema: {}. Updated schema: {}",
170 //LOG.trace("Received notification for VTEP config schema [{}] updated.", original.getSchemaName());
173 VtepConfigSchema orignalSchema = ItmUtils.validateVtepConfigSchema(original);
174 VtepConfigSchema updatedSchema = ItmUtils.validateVtepConfigSchema(updated);
176 if (doesDeleteAndAddSchemaRequired(original, updated)) {
177 LOG.error("Failed to handle DCN for updating VTEP schema {}. Original schema: {}. Updated schema: {}",
179 // TODO: handle updates
183 handleUpdateOfDpnIds(orignalSchema, updatedSchema);
185 } catch (Exception e) {
186 String error = new StringBuilder("Failed to handle DCN for update VtepConfigSchema original:")
187 .append(original).append(", updated: ").append(updated).toString();
196 * org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener#add(org.
197 * opendaylight.yangtools.yang.binding.InstanceIdentifier,
198 * org.opendaylight.yangtools.yang.binding.DataObject)
201 protected void add(InstanceIdentifier<VtepConfigSchema> identifier, VtepConfigSchema schema) {
202 // Construct the transport zones from the provided schemas and push it
204 LOG.trace("Add VtepConfigSchema: key: {} , value: {}", identifier, schema);
207 VtepConfigSchema validatedSchema = ItmUtils.validateForAddVtepConfigSchema(schema,
208 getAllVtepConfigSchemas());
210 VtepIpPool vtepIpPool = processAvailableIps(validatedSchema);
211 addVteps(validatedSchema, vtepIpPool);
212 } catch (Exception e) {
213 LOG.error("Failed to handle DCN for add VtepConfigSchema: {}", e);
218 * Handle update of dpn ids.
225 private void handleUpdateOfDpnIds(VtepConfigSchema original, VtepConfigSchema updated) {
226 // Handling add/delete DPNs from schema
227 List<DpnIds> originalDpnIds = (original.getDpnIds() == null) ? new ArrayList<DpnIds>()
228 : original.getDpnIds();
229 List<DpnIds> updatedDpnIds = (updated.getDpnIds() == null) ? new ArrayList<DpnIds>()
230 : updated.getDpnIds();
232 handleDeletedDpnsFromSchema(original, originalDpnIds, updatedDpnIds);
233 handleNewlyAddedDpnsToSchema(original, originalDpnIds, updatedDpnIds);
237 * Does delete and add schema required.
243 * @return true, if successful
245 private boolean doesDeleteAndAddSchemaRequired(VtepConfigSchema original, VtepConfigSchema updated) {
246 boolean delnAddRequired = false;
247 if (!StringUtils.equalsIgnoreCase(original.getPortName(), updated.getPortName())) {
248 delnAddRequired = true;
249 } else if (original.getVlanId() != updated.getVlanId()) {
250 delnAddRequired = true;
251 } else if (original.getSubnet() != null && !original.getSubnet().equals(updated.getSubnet())) {
252 delnAddRequired = true;
253 } else if (original.getGatewayIp() != null && !original.getGatewayIp().equals(updated.getGatewayIp())) {
254 delnAddRequired = true;
255 } else if (!StringUtils.equalsIgnoreCase(original.getTransportZoneName(), updated.getTransportZoneName())) {
256 delnAddRequired = true;
257 } else if (!(original.getTunnelType().equals(updated.getTunnelType()) )) {
258 delnAddRequired = true;
260 return delnAddRequired;
264 * Handle newly added dpns to schema.
268 * @param originalDpnIds
269 * the original dpn ids
270 * @param updatedDpnIds
271 * the updated dpn ids
273 private void handleNewlyAddedDpnsToSchema(VtepConfigSchema original, List<DpnIds> originalDpnIds,
274 List<DpnIds> updatedDpnIds) {
275 LOG.trace("Handle Addition of DPNs from VTEP Original Dpn: {}. Updated Dpn: {}", originalDpnIds, updatedDpnIds) ;
276 ArrayList<DpnIds> newlyAddedDpns = new ArrayList<>(updatedDpnIds);
277 newlyAddedDpns.removeAll(originalDpnIds);
278 LOG.debug("Newly added DPNs {} to VTEP config schema [{}].", newlyAddedDpns, original.getSchemaName());
279 if (!newlyAddedDpns.isEmpty()) {
280 VtepConfigSchema diffSchema = new VtepConfigSchemaBuilder(original).setDpnIds(newlyAddedDpns).build();
281 String subnetCidr = ItmUtils.getSubnetCidrAsString(original.getSubnet());
282 VtepIpPool vtepIpPool = getVtepIpPool(subnetCidr);
283 LOG.debug("Adding of DPNs in Diff Schema: {}", diffSchema) ;
284 addVteps(diffSchema, vtepIpPool);
289 * Handle deleted dpns from schema.
293 * @param originalDpnIds
294 * the original dpn ids
295 * @param updatedDpnIds
296 * the updated dpn ids
298 private void handleDeletedDpnsFromSchema(VtepConfigSchema original, List<DpnIds> originalDpnIds,
299 List<DpnIds> updatedDpnIds) {
300 ArrayList<DpnIds> deletedDpns = new ArrayList<>(originalDpnIds);
301 deletedDpns.removeAll(updatedDpnIds);
302 LOG.debug("DPNs to be removed DPNs {} from VTEP config schema [{}].", deletedDpns, original.getSchemaName());
303 if (!deletedDpns.isEmpty()) {
304 LOG.debug("Deleting of DPNs from VTEP Schema: {}. To be deleted Dpns: {}", original, deletedDpns) ;
305 deleteVteps(original, ItmUtils.getDpnIdList(deletedDpns));
310 * Gets all vtep config schemas.
312 * @return the all vtep config schemas
314 private List<VtepConfigSchema> getAllVtepConfigSchemas() {
315 Optional<VtepConfigSchemas> schemas = ItmUtils.read(LogicalDatastoreType.CONFIGURATION,
316 ItmUtils.getVtepConfigSchemasIdentifier(), this.dataBroker);
317 if (schemas.isPresent()) {
318 return schemas.get().getVtepConfigSchema();
331 private void addVteps(VtepConfigSchema schema, VtepIpPool vtepIpPool) {
332 if (schema.getDpnIds() == null || schema.getDpnIds().isEmpty()) {
333 LOG.debug("DPN list is empty, skipping addVteps for schema: {}", schema);
337 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
338 if (vtepIpPool == null) {
339 LOG.error("VTEP config pool not found for subnetCidr {}. Failed to add VTEPs for schema {}", subnetCidr,
343 TepCommandHelper tepCommandHelper = new TepCommandHelper(this.dataBroker);
346 Class<? extends TunnelTypeBase> tunnelType = schema.getTunnelType() ;
347 if( tunnelType.equals(TunnelTypeVxlan.class))
348 tunType = ITMConstants.TUNNEL_TYPE_VXLAN ;
350 tunType = ITMConstants.TUNNEL_TYPE_GRE;
351 tepCommandHelper.configureTunnelType(schema.getTransportZoneName(),
352 StringUtils.upperCase(tunType));
354 List<IpAddress> availableIps = vtepIpPool.getAvailableIpaddress();
355 List<IpAddress> newlyAllocatedIps = new ArrayList<>();
356 List<BigInteger> skippedDpnIds = new ArrayList<>();
358 String gatewayIp = handleGatewayIp(schema.getGatewayIp());
359 for (BigInteger dpnId : ItmUtils.getDpnIdList(schema.getDpnIds())) {
360 IpAddress ipAddress = getAnAvailableIP(availableIps);
361 if (ipAddress == null) {
362 skippedDpnIds.add(dpnId);
365 tepCommandHelper.createLocalCache(dpnId, schema.getPortName(), schema.getVlanId(),
366 String.valueOf(ipAddress.getValue()), subnetCidr, gatewayIp, schema.getTransportZoneName());
367 newlyAllocatedIps.add(ipAddress);
369 if (!skippedDpnIds.isEmpty()) {
370 LOG.error("No available IP addresses in the VTEP config pool {}, skipping VTEP configurations for DPN's {}",
371 subnetCidr, skippedDpnIds);
374 if (!newlyAllocatedIps.isEmpty()) {
375 LOG.debug( "Delete OnCommit and buildTeps in NewlyAddedDpns");
376 tepCommandHelper.deleteOnCommit();
377 tepCommandHelper.buildTeps();
378 allocateIpAddresses(newlyAllocatedIps, vtepIpPool, subnetCidr);
389 private String handleGatewayIp(IpAddress gatewayIp) {
390 String strGatewayIp = (gatewayIp == null) ? null : String.valueOf(gatewayIp.getValue());
391 if (StringUtils.isBlank(strGatewayIp) || StringUtils.equals(ITMConstants.DUMMY_IP_ADDRESS, strGatewayIp)) {
392 // To avoid a validation exception in TepCommandHelper
403 * @param lstDpnIdsToBeDeleted
404 * the dpn ids list to be deleted
406 private void deleteVteps(VtepConfigSchema schema, List<BigInteger> lstDpnIdsToBeDeleted) {
407 TepCommandHelper tepCommandHelper = new TepCommandHelper(this.dataBroker);
408 List<IpAddress> freeIps = new ArrayList<>();
410 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
411 String gatewayIp = handleGatewayIp(schema.getGatewayIp());
413 for (BigInteger dpnId : lstDpnIdsToBeDeleted) {
414 VtepsKey vtepkey = new VtepsKey(dpnId, schema.getPortName());
416 InstanceIdentifier<Vteps> vpath = InstanceIdentifier.builder(TransportZones.class)
417 .child(TransportZone.class, new TransportZoneKey(schema.getTransportZoneName()))
418 .child(Subnets.class, new SubnetsKey(schema.getSubnet())).child(Vteps.class, vtepkey).build();
421 Optional<Vteps> vtepOptional = ItmUtils.read(LogicalDatastoreType.CONFIGURATION, vpath, dataBroker);
422 if (vtepOptional.isPresent()) {
423 vtep = vtepOptional.get();
425 LOG.warn("VTEP doesn't exist for DPN [{}] and port [{}].", dpnId, schema.getPortName());
429 IpAddress ipAddress = vtep.getIpAddress();
430 tepCommandHelper.deleteVtep(dpnId, vtep.getPortname(), schema.getVlanId(),
431 String.valueOf(ipAddress.getValue()), subnetCidr, gatewayIp, schema.getTransportZoneName());
433 freeIps.add(ipAddress);
435 LOG.debug( "Delete OnCommit in NewlyAddedDpns");
436 tepCommandHelper.deleteOnCommit();
437 deAllocateIpAddresses(freeIps, subnetCidr);
441 * Calculate available IPs from the subnet mask specified in the schema.
442 * Pushes the available and allocated IP address to config DS.
447 private VtepIpPool processAvailableIps(final VtepConfigSchema schema) {
448 String subnetCidr = ItmUtils.getSubnetCidrAsString(schema.getSubnet());
449 SubnetUtils subnetUtils = new SubnetUtils(subnetCidr);
451 List<IpAddress> availableIps = calculateAvailableIps(subnetUtils, schema.getExcludeIpFilter(),
452 schema.getGatewayIp());
453 VtepIpPool vtepIpPool = new VtepIpPoolBuilder().setSubnetCidr(subnetCidr).setAvailableIpaddress(availableIps)
454 .setAllocatedIpaddress(new ArrayList<IpAddress>()).build();
456 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
457 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), vtepIpPool);
458 LOG.info("Vtep IP Pool with key:{} added to config DS", subnetCidr);
463 * Gets the vtep ip pool.
467 * @return the vtep ip pool
469 private VtepIpPool getVtepIpPool(final String subnetCidr) {
470 Optional<VtepIpPool> vtepIpPool = ItmUtils.read(LogicalDatastoreType.CONFIGURATION,
471 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), this.dataBroker);
472 if (vtepIpPool.isPresent()) {
473 return vtepIpPool.get();
479 * Delete vtep ip pool.
484 private void deleteVtepIpPool(final String subnetCidr) {
485 if (StringUtils.isNotBlank(subnetCidr)) {
486 MDSALUtil.syncDelete(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
487 ItmUtils.getVtepIpPoolIdentifier(subnetCidr));
488 LOG.debug("Deleted Vtep IP Pool with key:{}", subnetCidr);
493 * Gets the an available ip.
495 * @param availableIps
497 * @return the an available ip
499 private IpAddress getAnAvailableIP(List<IpAddress> availableIps) {
500 // TODO: Sort IP Addresses, get the least value
501 IpAddress ipAddress = null;
502 if (availableIps != null && !availableIps.isEmpty()) {
503 ipAddress = availableIps.remove(0);
509 * Allocate ip addresses.
511 * @param allocatedIps
518 private void allocateIpAddresses(List<IpAddress> allocatedIps, VtepIpPool vtepIpPool, String subnetCidr) {
519 if (allocatedIps != null && !allocatedIps.isEmpty() && vtepIpPool != null) {
520 // Remove from the available IP address list and add to allocated IP
522 VtepIpPoolBuilder builder = new VtepIpPoolBuilder(vtepIpPool);
523 if (builder.getAvailableIpaddress() != null) {
524 builder.getAvailableIpaddress().removeAll(allocatedIps);
526 if (builder.getAllocatedIpaddress() == null) {
527 builder.setAllocatedIpaddress(allocatedIps);
529 builder.getAllocatedIpaddress().addAll(allocatedIps);
532 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
533 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), builder.build());
538 * De-allocate ip addresses.
545 private void deAllocateIpAddresses(List<IpAddress> freeIps, String subnetCidr) {
546 VtepIpPool vtepIpPool = getVtepIpPool(subnetCidr);
547 if (freeIps != null && !freeIps.isEmpty() && vtepIpPool != null) {
548 // Remove from the allocated IP address list and add to available IP
550 VtepIpPoolBuilder builder = new VtepIpPoolBuilder(vtepIpPool);
551 if (builder.getAllocatedIpaddress() != null) {
552 builder.getAllocatedIpaddress().removeAll(freeIps);
554 if (builder.getAvailableIpaddress() == null) {
555 builder.setAvailableIpaddress(freeIps);
557 builder.getAvailableIpaddress().addAll(freeIps);
560 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
561 ItmUtils.getVtepIpPoolIdentifier(subnetCidr), builder.build());
562 LOG.debug("Vtep IP Pool with key:{} updated to config DS", subnetCidr);
567 * Calculate available ips.
571 * @param excludeIpFilter
572 * the exclude ip filter
577 private List<IpAddress> calculateAvailableIps(SubnetUtils subnetUtils, String excludeIpFilter,
578 IpAddress gatewayIp) {
579 List<IpAddress> lstAvailableIps = new ArrayList<>();
580 SubnetInfo subnetInfo = subnetUtils.getInfo();
581 String[] arrIpAddresses = subnetInfo.getAllAddresses();
583 for (String ipAddress : arrIpAddresses) {
584 lstAvailableIps.add(new IpAddress(ipAddress.toCharArray()));
586 lstAvailableIps.remove(gatewayIp);
587 lstAvailableIps.removeAll(ItmUtils.getExcludeIpAddresses(excludeIpFilter, subnetInfo));
589 return lstAvailableIps;