/* * Copyright (c) 2016 Cisco Systems, Inc. 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.groupbasedpolicy.renderer.vpp.policy; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nonnull; import org.opendaylight.controller.config.yang.config.vpp_provider.impl.VppRenderer; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.groupbasedpolicy.renderer.vpp.event.NodeOperEvent; import org.opendaylight.groupbasedpolicy.renderer.vpp.event.RendererPolicyConfEvent; import org.opendaylight.groupbasedpolicy.renderer.vpp.util.KeyFactory; import org.opendaylight.groupbasedpolicy.util.IidFactory; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.RendererPolicy; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.RendererPolicyBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.RendererEndpointKey; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.MapDifference; import com.google.common.collect.MapDifference.ValueDifference; import com.google.common.collect.Maps; import com.google.common.collect.SetMultimap; import com.google.common.collect.Sets; import com.google.common.collect.Sets.SetView; import com.google.common.eventbus.Subscribe; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; public class VppRendererPolicyManager { private static final Logger LOG = LoggerFactory.getLogger(VppRendererPolicyManager.class); private final DataBroker dataProvider; private ForwardingManager fwManager; public VppRendererPolicyManager(@Nonnull ForwardingManager fwManager, @Nonnull DataBroker dataProvider) { this.fwManager = Preconditions.checkNotNull(fwManager); this.dataProvider = Preconditions.checkNotNull(dataProvider); } @Subscribe public void rendererPolicyChanged(RendererPolicyConfEvent event) { RendererPolicyBuilder responseBuilder = new RendererPolicyBuilder(); switch (event.getDtoModificationType()) { case CREATED: LOG.trace("CREATED : {}", event); responseBuilder.setVersion(event.getAfter().get().getVersion()); rendererPolicyCreated(event.getAfter().get()); break; case UPDATED: LOG.trace("UPDATED: {}", event); RendererPolicy rPolicyBefore = event.getBefore().get(); RendererPolicy rPolicyAfter = event.getAfter().get(); responseBuilder.setVersion(rPolicyAfter.getVersion()); if (!isConfigurationChanged(rPolicyBefore, rPolicyAfter)) { LOG.debug("Configuration is not changed only updating config version from {} to {}", rPolicyBefore.getVersion(), rPolicyAfter.getVersion()); } else { // TODO collect unconfigured rules and put them to responseBuilder rendererPolicyUpdated(rPolicyBefore, rPolicyAfter); } break; case DELETED: LOG.trace("DELETED: {}", event); responseBuilder.setVersion(event.getBefore().get().getVersion()); rendererPolicyDeleted(event.getBefore().get()); break; } WriteTransaction wTx = dataProvider.newWriteOnlyTransaction(); RendererPolicy response = responseBuilder.build(); wTx.put(LogicalDatastoreType.OPERATIONAL, IidFactory.rendererIid(VppRenderer.NAME).child(RendererPolicy.class), response, true); Futures.addCallback(wTx.submit(), new FutureCallback() { @Override public void onSuccess(Void result) { LOG.debug("Renderer updated renderer policy {}", response); } @Override public void onFailure(Throwable t) { LOG.warn("Renderer DIDN'T update renderer-policy {}", response); } }); } private boolean isConfigurationChanged(RendererPolicy before, RendererPolicy after) { if (before.getConfiguration() == null && after.getConfiguration() == null) { return false; } return true; } private void rendererPolicyUpdated(RendererPolicy rPolicyBefore, RendererPolicy rPolicyAfter) { PolicyContext policyCtxBefore = new PolicyContext(rPolicyBefore); PolicyContext policyCtxAfter = new PolicyContext(rPolicyAfter); MapDifference> vppNodesByL2FlDiff = createDiffForVppNodesByL2Fd(policyCtxBefore, policyCtxAfter); SetMultimap removedVppNodesByL2Fd = HashMultimap.create(); SetMultimap createdVppNodesByL2Fd = HashMultimap.create(); for (Entry>> entry : vppNodesByL2FlDiff.entriesDiffering() .entrySet()) { String bridgeDomain = entry.getKey(); Collection beforeNodes = entry.getValue().leftValue(); Collection afterNodes = entry.getValue().rightValue(); if (beforeNodes != null && afterNodes != null) { SetView removedNodes = Sets.difference(new HashSet<>(beforeNodes), new HashSet<>(afterNodes)); removedVppNodesByL2Fd.putAll(bridgeDomain, removedNodes); SetView createdNodes = Sets.difference(new HashSet<>(afterNodes), new HashSet<>(beforeNodes)); createdVppNodesByL2Fd.putAll(bridgeDomain, createdNodes); } else if (beforeNodes != null) { removedVppNodesByL2Fd.putAll(bridgeDomain, beforeNodes); } else if (afterNodes != null) { createdVppNodesByL2Fd.putAll(bridgeDomain, afterNodes); } } Map> removedL2Fds = vppNodesByL2FlDiff.entriesOnlyOnLeft(); for (Entry> entry : removedL2Fds.entrySet()) { String bridgeDomain = entry.getKey(); Collection removedNodes = entry.getValue(); if (removedNodes != null) { removedVppNodesByL2Fd.putAll(bridgeDomain, removedNodes); } } Map> createdL2Fds = vppNodesByL2FlDiff.entriesOnlyOnRight(); for (Entry> entry : createdL2Fds.entrySet()) { String bridgeDomain = entry.getKey(); Collection createdNodes = entry.getValue(); if (createdNodes != null) { createdVppNodesByL2Fd.putAll(bridgeDomain, createdNodes); } } ImmutableSet rendEpsBefore = policyCtxBefore.getPolicyTable().rowKeySet(); ImmutableSet rendEpsAfter = policyCtxAfter.getPolicyTable().rowKeySet(); SetView removedRendEps = Sets.difference(rendEpsBefore, rendEpsAfter); removedRendEps.forEach(rEpKey -> fwManager.removeForwardingForEndpoint(rEpKey, policyCtxBefore)); fwManager.removeBridgeDomainOnNodes(removedVppNodesByL2Fd); fwManager.createBridgeDomainOnNodes(createdVppNodesByL2Fd); SetView createdRendEps = Sets.difference(rendEpsAfter, rendEpsBefore); createdRendEps.forEach(rEpKey -> fwManager.createForwardingForEndpoint(rEpKey, policyCtxAfter)); SetView updatedRendEps = Sets.intersection(rendEpsBefore, rendEpsAfter); // TODO think about all cases, but keep it simple for now updatedRendEps.forEach(rEpKey -> fwManager.removeForwardingForEndpoint(rEpKey, policyCtxBefore)); updatedRendEps.forEach(rEpKey -> fwManager.createForwardingForEndpoint(rEpKey, policyCtxAfter)); } private static MapDifference> createDiffForVppNodesByL2Fd(PolicyContext policyCtxBefore, PolicyContext policyCtxAfter) { ImmutableSet rendEpsBefore = policyCtxBefore.getPolicyTable().rowKeySet(); ImmutableSet rendEpsAfter = policyCtxAfter.getPolicyTable().rowKeySet(); SetMultimap vppNodesByL2FdBefore = resolveVppNodesByL2Fd(rendEpsBefore, policyCtxBefore); SetMultimap vppNodesByL2FdAfter = resolveVppNodesByL2Fd(rendEpsAfter, policyCtxBefore); return Maps.difference(vppNodesByL2FdBefore.asMap(), vppNodesByL2FdAfter.asMap()); } private void rendererPolicyCreated(RendererPolicy rPolicy) { PolicyContext policyCtx = new PolicyContext(rPolicy); ImmutableSet rEpKeys = policyCtx.getPolicyTable().rowKeySet(); SetMultimap vppNodesByL2Fd = resolveVppNodesByL2Fd(rEpKeys, policyCtx); fwManager.createBridgeDomainOnNodes(vppNodesByL2Fd); rEpKeys.forEach(rEpKey -> fwManager.createForwardingForEndpoint(rEpKey, policyCtx)); } private void rendererPolicyDeleted(RendererPolicy rendererPolicy) { PolicyContext policyCtx = new PolicyContext(rendererPolicy); ImmutableSet rEpKeys = policyCtx.getPolicyTable().rowKeySet(); rEpKeys.forEach(rEpKey -> fwManager.removeForwardingForEndpoint(rEpKey, policyCtx)); SetMultimap vppNodesByL2Fd = resolveVppNodesByL2Fd(rEpKeys, policyCtx); fwManager.removeBridgeDomainOnNodes(vppNodesByL2Fd); } private static SetMultimap resolveVppNodesByL2Fd(Set rEpKeys, PolicyContext policyCtx) { SetMultimap vppNodesByL2Fd = HashMultimap.create(); rEpKeys.stream() .map(rEpKey -> KeyFactory.addressEndpointKey(rEpKey)) .map(addrEpKey -> policyCtx.getAddrEpByKey().get(addrEpKey)) .collect(Collectors.toSet()) .forEach(addrEpWithLoc -> { Optional optL2Fd = ForwardingManager.resolveL2FloodDomain(addrEpWithLoc.getNetworkContainment()); if (optL2Fd.isPresent()) { ExternalLocationCase rEpLoc = ForwardingManager.resolveAndValidateLocation(addrEpWithLoc); InstanceIdentifier externalNodeMountPoint = rEpLoc.getExternalNodeMountPoint(); NodeId vppNode = externalNodeMountPoint.firstKeyOf(Node.class).getNodeId(); vppNodesByL2Fd.put(optL2Fd.get(), vppNode); } }); return vppNodesByL2Fd; } @Subscribe public void vppNodeChanged(NodeOperEvent event) { switch (event.getDtoModificationType()) { case CREATED: if (event.isAfterConnected()) { // TODO } break; case UPDATED: if (!event.isBeforeConnected() && event.isAfterConnected()) { // TODO } break; case DELETED: if (event.isBeforeConnected()) { // TODO } break; } } }