/* * Copyright (c) 2016, 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.genius.itm.listeners; import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.inject.Inject; import javax.inject.Singleton; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase; import org.opendaylight.genius.itm.confighelpers.HwVtep; import org.opendaylight.genius.itm.confighelpers.ItmInternalTunnelAddWorker; import org.opendaylight.genius.itm.confighelpers.ItmInternalTunnelDeleteWorker; import org.opendaylight.genius.itm.confighelpers.ItmTepAddWorker; import org.opendaylight.genius.itm.confighelpers.ItmTepRemoveWorker; import org.opendaylight.genius.itm.confighelpers.ItmTepsNotHostedMoveWorker; import org.opendaylight.genius.itm.confighelpers.ItmTepsNotHostedRemoveWorker; import org.opendaylight.genius.itm.globals.ITMConstants; import org.opendaylight.genius.itm.impl.ItmUtils; import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager; import org.opendaylight.infrautils.jobcoordinator.JobCoordinator; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.ItmConfig; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.DPNTEPsInfo; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.TunnelEndPoints; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.tunnel.end.points.TzMembership; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.TransportZones; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.TransportZonesBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TepsNotHostedInTransportZone; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TepsNotHostedInTransportZoneKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZone; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.tepsnothostedintransportzone.UnknownVteps; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.Subnets; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.DeviceVteps; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.Vteps; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.VtepsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.VtepsKey; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class listens for interface creation/removal/update in Configuration DS. * This is used to handle interfaces for base of-ports. */ @Singleton public class TransportZoneListener extends AsyncDataTreeChangeListenerBase implements AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(TransportZoneListener.class); private final DataBroker dataBroker; private final JobCoordinator jobCoordinator; private final IdManagerService idManagerService; private final IMdsalApiManager mdsalManager; private final ItmConfig itmConfig; private final ItmInternalTunnelDeleteWorker itmInternalTunnelDeleteWorker; private final ItmInternalTunnelAddWorker itmInternalTunnelAddWorker; @Inject public TransportZoneListener(final DataBroker dataBroker, final IdManagerService idManagerService, final IMdsalApiManager mdsalManager, final ItmConfig itmConfig, JobCoordinator jobCoordinator) { super(TransportZone.class, TransportZoneListener.class); this.dataBroker = dataBroker; this.jobCoordinator = jobCoordinator; this.idManagerService = idManagerService; initializeTZNode(dataBroker); this.mdsalManager = mdsalManager; this.itmConfig = itmConfig; this.itmInternalTunnelDeleteWorker = new ItmInternalTunnelDeleteWorker(dataBroker, jobCoordinator); this.itmInternalTunnelAddWorker = new ItmInternalTunnelAddWorker(dataBroker, jobCoordinator); } @PostConstruct public void start() { registerListener(LogicalDatastoreType.CONFIGURATION, this.dataBroker); LOG.info("tzChangeListener Started"); } @Override @PreDestroy public void close() { LOG.info("tzChangeListener Closed"); } @SuppressWarnings("checkstyle:IllegalCatch") private void initializeTZNode(DataBroker db) { ReadWriteTransaction transaction = db.newReadWriteTransaction(); InstanceIdentifier path = InstanceIdentifier.create(TransportZones.class); CheckedFuture, ReadFailedException> tzones = transaction .read(LogicalDatastoreType.CONFIGURATION, path); try { if (!tzones.get().isPresent()) { TransportZonesBuilder tzb = new TransportZonesBuilder(); transaction.put(LogicalDatastoreType.CONFIGURATION, path, tzb.build()); transaction.submit(); } else { transaction.cancel(); } } catch (Exception e) { LOG.error("Error initializing TransportZones {}", e); } } @Override protected InstanceIdentifier getWildCardPath() { return InstanceIdentifier.create(TransportZones.class).child(TransportZone.class); } @Override protected TransportZoneListener getDataTreeChangeListener() { return TransportZoneListener.this; } @Override protected void remove(InstanceIdentifier key, TransportZone tzOld) { LOG.debug("Received Transport Zone Remove Event: {}, {}", key, tzOld); boolean allowTunnelDeletion = false; // check if TZ received for removal is default-transport-zone, // if yes, then check if it is received from northbound, then // do not entertain request and skip tunnels remove operation // if def-tz removal request is due to def-tz-enabled flag is disabled or // due to change in def-tz-tunnel-type, then allow def-tz tunnels deletion if (tzOld.getZoneName().equalsIgnoreCase(ITMConstants.DEFAULT_TRANSPORT_ZONE)) { // Get TunnelTypeBase object for tunnel-type configured in config file Class tunType = ItmUtils.getTunnelType(itmConfig.getDefTzTunnelType()); if (!itmConfig.isDefTzEnabled() || !tzOld.getTunnelType().equals(tunType)) { allowTunnelDeletion = true; } else { // this is case when def-tz removal request is from Northbound. allowTunnelDeletion = false; LOG.error("Deletion of {} is an incorrect usage",ITMConstants.DEFAULT_TRANSPORT_ZONE); } } else { allowTunnelDeletion = true; } if (allowTunnelDeletion) { //TODO : DPList code can be refactor with new specific class // which implement TransportZoneValidator List opDpnList = createDPNTepInfo(tzOld); List hwVtepList = createhWVteps(tzOld); LOG.trace("Delete: Invoking deleteTunnels in ItmManager with DpnList {}", opDpnList); if (!opDpnList.isEmpty() || !hwVtepList.isEmpty()) { LOG.trace("Delete: Invoking ItmManager with hwVtep List {} ", hwVtepList); jobCoordinator.enqueueJob(tzOld.getZoneName(), new ItmTepRemoveWorker(opDpnList, hwVtepList, tzOld, dataBroker, mdsalManager, itmInternalTunnelDeleteWorker)); } } } @Override protected void update(InstanceIdentifier key, TransportZone tzOld, TransportZone tzNew) { LOG.debug("Received Transport Zone Update Event: Key - {}, Old - {}, Updated - {}", key, tzOld, tzNew); List oldDpnTepsList = createDPNTepInfo(tzOld); List newDpnTepsList = createDPNTepInfo(tzNew); List oldDpnTepsListcopy = new ArrayList<>(); oldDpnTepsListcopy.addAll(oldDpnTepsList); LOG.trace("oldcopy0 {}", oldDpnTepsListcopy); List newDpnTepsListcopy = new ArrayList<>(); newDpnTepsListcopy.addAll(newDpnTepsList); LOG.trace("newcopy0 {}", newDpnTepsListcopy); oldDpnTepsList.removeAll(newDpnTepsListcopy); newDpnTepsList.removeAll(oldDpnTepsListcopy); LOG.trace("oldDpnTepsList {}", oldDpnTepsList); LOG.trace("newDpnTepsList {}", newDpnTepsList); LOG.trace("oldcopy {}", oldDpnTepsListcopy); LOG.trace("newcopy {}", newDpnTepsListcopy); LOG.trace("oldcopy Size {}", oldDpnTepsList.size()); LOG.trace("newcopy Size {}", newDpnTepsList.size()); if (!newDpnTepsList.isEmpty()) { LOG.trace("Adding TEPs "); jobCoordinator.enqueueJob(tzNew.getZoneName(), new ItmTepAddWorker(newDpnTepsList, Collections.emptyList(), dataBroker, mdsalManager, itmConfig, itmInternalTunnelAddWorker)); } if (!oldDpnTepsList.isEmpty()) { LOG.trace("Removing TEPs "); jobCoordinator.enqueueJob(tzNew.getZoneName(), new ItmTepRemoveWorker(oldDpnTepsList, Collections.emptyList(), tzOld, dataBroker, mdsalManager, itmInternalTunnelDeleteWorker)); } List oldHwList = createhWVteps(tzOld); List newHwList = createhWVteps(tzNew); List oldHwListcopy = new ArrayList<>(); oldHwListcopy.addAll(oldHwList); LOG.trace("oldHwListcopy0 {}", oldHwListcopy); List newHwListcopy = new ArrayList<>(); newHwListcopy.addAll(newHwList); LOG.trace("newHwListcopy0 {}", newHwListcopy); oldHwList.removeAll(newHwListcopy); newHwList.removeAll(oldHwListcopy); LOG.trace("oldHwList {}", oldHwList); LOG.trace("newHwList {}", newHwList); LOG.trace("oldHwListcopy {}", oldHwListcopy); LOG.trace("newHwListcopy {}", newHwListcopy); if (!newHwList.isEmpty()) { LOG.trace("Adding HW TEPs "); jobCoordinator.enqueueJob(tzNew.getZoneName(), new ItmTepAddWorker(Collections.emptyList(), newHwList, dataBroker, mdsalManager, itmConfig, itmInternalTunnelAddWorker)); } if (!oldHwList.isEmpty()) { LOG.trace("Removing HW TEPs "); jobCoordinator.enqueueJob(tzNew.getZoneName(), new ItmTepRemoveWorker(Collections.emptyList(), oldHwList, tzOld, dataBroker, mdsalManager, itmInternalTunnelDeleteWorker)); } } @Override protected void add(InstanceIdentifier key, TransportZone tzNew) { LOG.debug("Received Transport Zone Add Event: {}, {}", key, tzNew); List opDpnList = createDPNTepInfo(tzNew); List hwVtepList = createhWVteps(tzNew); opDpnList.addAll(getDPNTepInfoFromNotHosted(tzNew)); LOG.trace("Add: Operational dpnTepInfo - Before invoking ItmManager {}", opDpnList); if (!opDpnList.isEmpty() || !hwVtepList.isEmpty()) { LOG.trace("Add: Invoking ItmManager with DPN List {} ", opDpnList); LOG.trace("Add: Invoking ItmManager with hwVtep List {} ", hwVtepList); jobCoordinator.enqueueJob(tzNew.getZoneName(), new ItmTepAddWorker(opDpnList, hwVtepList, dataBroker, mdsalManager, itmConfig, itmInternalTunnelAddWorker)); } } private List getDPNTepInfoFromNotHosted(TransportZone tzNew) { List notHostedOpDpnList = new ArrayList<>(); if (isNewTZExistInNotHostedTZ(tzNew)) { notHostedOpDpnList = createDPNTepInfoFromNotHosted(tzNew); } return notHostedOpDpnList; } private List createDPNTepInfoFromNotHosted(TransportZone tzNew) { Map> mapNotHostedDPNToTunnelEndpt = new ConcurrentHashMap<>(); List notHostedDpnTepInfo = new ArrayList<>(); String newZoneName = tzNew.getZoneName(); List zones = ItmUtils.createTransportZoneMembership(newZoneName); Class tunnelType = tzNew.getTunnelType(); TepsNotHostedInTransportZone tepNotHostedTransportZone = getNotHostedTransportZone(newZoneName).get(); if (tepNotHostedTransportZone == null) { return notHostedDpnTepInfo; } List unVtepsLst = tepNotHostedTransportZone.getUnknownVteps(); List vtepsList = new ArrayList<>(); if (unVtepsLst != null && !unVtepsLst.isEmpty()) { for (UnknownVteps vteps : unVtepsLst) { BigInteger dpnID = vteps.getDpnId(); String port = ITMConstants.DUMMY_PORT; int vlanID = ITMConstants.DUMMY_VLANID; IpPrefix ipPrefix = new IpPrefix(ITMConstants.DUMMY_PREFIX.toCharArray()); IpAddress gatewayIP = new IpAddress(ITMConstants.DUMMY_GATEWAY_IP.toCharArray()); IpAddress ipAddress = vteps.getIpAddress(); boolean useOfTunnel = ItmUtils.falseIfNull(vteps.isOfTunnel()); String tos = vteps.getOptionTunnelTos(); if (tos == null) { tos = itmConfig.getDefaultTunnelTos(); } TunnelEndPoints tunnelEndPoints = ItmUtils.createTunnelEndPoints(dpnID, ipAddress, port, useOfTunnel,vlanID, ipPrefix, gatewayIP, zones, tunnelType, tos); List tunnelEndPointsList = mapNotHostedDPNToTunnelEndpt.get(dpnID); if (tunnelEndPointsList != null) { tunnelEndPointsList.add(tunnelEndPoints); } else { tunnelEndPointsList = new ArrayList<>(); tunnelEndPointsList.add(tunnelEndPoints); mapNotHostedDPNToTunnelEndpt.put(dpnID, tunnelEndPointsList); } Vteps newVtep = createVtepFromUnKnownVteps(dpnID,ipAddress,ITMConstants.DUMMY_PORT); vtepsList.add(newVtep); // Enqueue 'remove TEP from TepsNotHosted list' operation // into DataStoreJobCoordinator ItmTepsNotHostedRemoveWorker removeWorker = new ItmTepsNotHostedRemoveWorker(newZoneName, ipAddress, dpnID, dataBroker); jobCoordinator.enqueueJob(newZoneName, removeWorker); } } // Enqueue 'add TEP received from southbound OVSDB into ITM config DS' operation // into DataStoreJobCoordinator ItmTepsNotHostedMoveWorker moveWorker = new ItmTepsNotHostedMoveWorker(vtepsList, newZoneName, dataBroker); jobCoordinator.enqueueJob(newZoneName, moveWorker); if (mapNotHostedDPNToTunnelEndpt.size() > 0) { Set keys = mapNotHostedDPNToTunnelEndpt.keySet(); for (BigInteger key: keys) { DPNTEPsInfo newDpnTepsInfo = ItmUtils.createDPNTepInfo(key, mapNotHostedDPNToTunnelEndpt.get(key)); notHostedDpnTepInfo.add(newDpnTepsInfo); } } return notHostedDpnTepInfo; } private Vteps createVtepFromUnKnownVteps(BigInteger dpnID, IpAddress ipAddress, String port) { VtepsKey vtepkey = new VtepsKey(dpnID, port); Vteps vtepObj = new VtepsBuilder().setDpnId(dpnID).setIpAddress(ipAddress).setKey(vtepkey) .setPortname(port).build(); return vtepObj; } private boolean isNewTZExistInNotHostedTZ(TransportZone tzNew) { boolean isPresent = false; if (getNotHostedTransportZone(tzNew.getZoneName()).isPresent()) { isPresent = true; } return isPresent; } public Optional getNotHostedTransportZone(String transportZoneName) { InstanceIdentifier tzonePath = InstanceIdentifier.builder(TransportZones.class) .child(TepsNotHostedInTransportZone.class, new TepsNotHostedInTransportZoneKey(transportZoneName)).build(); Optional tzNotHostedOptional = ItmUtils.read(LogicalDatastoreType.CONFIGURATION, tzonePath, dataBroker); return tzNotHostedOptional; } private List createDPNTepInfo(TransportZone transportZone) { Map> mapDPNToTunnelEndpt = new ConcurrentHashMap<>(); List dpnTepInfo = new ArrayList<>(); List zones = ItmUtils.createTransportZoneMembership(transportZone.getZoneName()); Class tunnelType = transportZone.getTunnelType(); LOG.trace("Transport Zone_name: {}", transportZone.getZoneName()); List subnetsList = transportZone.getSubnets(); if (subnetsList != null) { for (Subnets subnet : subnetsList) { IpPrefix ipPrefix = subnet.getPrefix(); IpAddress gatewayIP = subnet.getGatewayIp(); int vlanID = subnet.getVlanId(); LOG.trace("IpPrefix: {}, gatewayIP: {}, vlanID: {} ", ipPrefix, gatewayIP, vlanID); List vtepsList = subnet.getVteps(); if (vtepsList != null && !vtepsList.isEmpty()) { for (Vteps vteps : vtepsList) { BigInteger dpnID = vteps.getDpnId(); String port = vteps.getPortname(); IpAddress ipAddress = vteps.getIpAddress(); boolean useOfTunnel = ItmUtils.falseIfNull(vteps.isOptionOfTunnel()); String tos = vteps.getOptionTunnelTos(); if (tos == null) { tos = itmConfig.getDefaultTunnelTos(); } LOG.trace("DpnID: {}, port: {}, ipAddress: {}", dpnID, port, ipAddress); TunnelEndPoints tunnelEndPoints = ItmUtils.createTunnelEndPoints(dpnID, ipAddress, port, useOfTunnel, vlanID, ipPrefix, gatewayIP, zones, tunnelType, tos); List tunnelEndPointsList = mapDPNToTunnelEndpt.get(dpnID); if (tunnelEndPointsList != null) { LOG.trace("Existing DPN info list in the Map: {} ", dpnID); tunnelEndPointsList.add(tunnelEndPoints); } else { LOG.trace("Adding new DPN info list to the Map: {} ", dpnID); tunnelEndPointsList = new ArrayList<>(); tunnelEndPointsList.add(tunnelEndPoints); mapDPNToTunnelEndpt.put(dpnID, tunnelEndPointsList); } } } } } if (!mapDPNToTunnelEndpt.isEmpty()) { Set keys = mapDPNToTunnelEndpt.keySet(); LOG.trace("List of dpns in the Map: {} ", keys); for (BigInteger key : keys) { DPNTEPsInfo newDpnTepsInfo = ItmUtils.createDPNTepInfo(key, mapDPNToTunnelEndpt.get(key)); dpnTepInfo.add(newDpnTepsInfo); } } return dpnTepInfo; } private List createhWVteps(TransportZone transportZone) { List hwVtepsList = new ArrayList<>(); String zoneName = transportZone.getZoneName(); Class tunnelType = transportZone.getTunnelType(); LOG.trace("Transport Zone_name: {}", zoneName); List subnetsList = transportZone.getSubnets(); if (subnetsList != null) { for (Subnets subnet : subnetsList) { IpPrefix ipPrefix = subnet.getPrefix(); IpAddress gatewayIP = subnet.getGatewayIp(); int vlanID = subnet.getVlanId(); LOG.trace("IpPrefix: {}, gatewayIP: {}, vlanID: {} ", ipPrefix, gatewayIP, vlanID); List deviceVtepsList = subnet.getDeviceVteps(); if (deviceVtepsList != null) { for (DeviceVteps vteps : deviceVtepsList) { String topologyId = vteps.getTopologyId(); String nodeId = vteps.getNodeId(); IpAddress ipAddress = vteps.getIpAddress(); LOG.trace("topo-id: {}, node-id: {}, ipAddress: {}", topologyId, nodeId, ipAddress); HwVtep hwVtep = ItmUtils.createHwVtepObject(topologyId, nodeId, ipAddress, ipPrefix, gatewayIP, vlanID, tunnelType, transportZone); if (hwVtepsList != null) { LOG.trace("Existing hwVteps"); hwVtepsList.add(hwVtep); } else { LOG.trace("Adding new HwVtep {} info ", hwVtep.getHwIp()); hwVtepsList.add(hwVtep); } } } } } LOG.trace("returning hwvteplist {}", hwVtepsList); return hwVtepsList; } }