/*
* Copyright (c) 2014 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.ofoverlay.flow;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import com.google.common.annotations.VisibleForTesting;
import org.opendaylight.groupbasedpolicy.dto.ConditionGroup;
import org.opendaylight.groupbasedpolicy.dto.EgKey;
import org.opendaylight.groupbasedpolicy.dto.EpKey;
import org.opendaylight.groupbasedpolicy.dto.IndexedTenant;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2FloodDomainId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.NetworkDomainId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubnetId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L2BridgeDomain;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L2FloodDomain;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L3Context;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.Subnet;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.EndpointGroup;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OrdinalFactory {
private final static Logger LOG = LoggerFactory.getLogger(OrdinalFactory.class);
/**
* Counter used to allocate ordinal values for forwarding contexts and VNIDs
*/
private final static AtomicInteger policyOrdinal = new AtomicInteger(1);
private final static ConcurrentMap<String, Integer> ordinals = new ConcurrentHashMap<>();
// XXX - need to garbage collect
private final static ConcurrentMap<ConditionGroup, Integer> cgOrdinals = new ConcurrentHashMap<>();
/**
* Creates an ordinal for the OfOverlay pipeline comparison, based on @TenantId and a
* uniqueID (UUID) associated with any other attribute.
*
* @param tenantId the tenant id
* @param id a unique id
* @return the ordinal
*/
public static int getContextOrdinal(final TenantId tenantId, final UniqueId id) {
if (tenantId == null || id == null)
return 0;
return getContextOrdinalFromString(tenantId.getValue() + "|" + id.getValue());
}
/**
* Get a unique ordinal for the given condition group, suitable for use in
* the data plane. This is unique only for this node, and not globally.
*
* @param cg
* the {@link ConditionGroup}
* @return the unique ID
*/
public static int getCondGroupOrdinal(final ConditionGroup cg) {
if (cg == null)
return 0;
Integer ord = cgOrdinals.get(cg);
if (ord == null) {
ord = policyOrdinal.getAndIncrement();
Integer old = cgOrdinals.putIfAbsent(cg, ord);
if (old != null)
ord = old;
}
return ord.intValue();
}
/**
* Get a 32-bit context ordinal suitable for use in the OF data plane for
* the given policy item.
*
* @param destNode
* destination node ID
* @return the 32-bit ordinal value
* @throws Exception throws all exception
*/
public static int getContextOrdinal(NodeId destNode) throws Exception {
return getContextOrdinalFromString(destNode.getValue());
}
public static int getContextOrdinal(Endpoint ep, NetworkDomainId networkContainment) {
Set<String> epgs = new TreeSet<>();
// Get EPGs and add to ordered Set
if (ep.getEndpointGroup() != null) {
epgs.add(ep.getEndpointGroup().getValue());
}
if (ep.getEndpointGroups() != null) {
for (EndpointGroupId epgId : ep.getEndpointGroups()) {
epgs.add(epgId.getValue());
}
}
StringBuilder key = new StringBuilder(ep.getTenant().getValue());
for (String epg : epgs) {
key.append('|');
key.append(epg);
}
key.append("|").append(networkContainment);
return getContextOrdinalFromString(key.toString());
}
public static int getContextOrdinal(Endpoint ep) {
Set<String> epgs = new TreeSet<>();
// Get EPGs and add to ordered Set
if (ep.getEndpointGroup() != null) {
epgs.add(ep.getEndpointGroup().getValue());
}
if (ep.getEndpointGroups() != null) {
for (EndpointGroupId epgId : ep.getEndpointGroups()) {
epgs.add(epgId.getValue());
}
}
StringBuilder key = new StringBuilder(ep.getTenant().getValue());
for (String epg : epgs) {
key.append('|');
key.append(epg);
}
return getContextOrdinalFromString(key.toString());
}
/**
* Get a 32-bit context ordinal suitable for use in the OF data plane for
* the given policy item.
*
* @param id
* the unique ID for the element
* @return the 32-bit ordinal value
*/
private static int getContextOrdinalFromString(final String id) {
Integer ord = ordinals.get(id);
if (ord == null) {
ord = policyOrdinal.getAndIncrement();
Integer old = ordinals.putIfAbsent(id, ord);
if (old != null)
ord = old;
}
return ord.intValue();
}
public static final EndpointFwdCtxOrdinals getEndpointFwdCtxOrdinals(OfContext ctx,
Endpoint ep) {
IndexedTenant tenant = ctx.getTenant(ep.getTenant());
if (tenant == null) {
LOG.debug("Tenant {} is null", ep.getTenant());
return null;
}
return new EndpointFwdCtxOrdinals(ep, ctx);
}
// TODO alagalah Li: Move to either OrdinalFactory or EndpointManager
public static class EndpointFwdCtxOrdinals {
private NetworkDomainId networkContainment;
private EpKey ep;
private int epgId = 0, bdId = 0, fdId = 0, l3Id = 0, cgId = 0, tunnelId = 0;
private EndpointFwdCtxOrdinals(Endpoint ep, OfContext ctx) {
this.ep = new EpKey(ep.getL2Context(), ep.getMacAddress());
IndexedTenant tenant = ctx.getTenant(ep.getTenant());
// Set network containment either from ep, or from primary EPG
if (ep.getNetworkContainment() != null) {
this.networkContainment = ep.getNetworkContainment();
} else {
EndpointGroup epg = tenant.getEndpointGroup(ep.getEndpointGroup());
if (epg.getNetworkDomain() != null) {
this.networkContainment = epg.getNetworkDomain();
} else {
LOG.info("endPoint ordinals for {} not processed in SourceMapper. Must be able to resolve "
+ "network containment either directly, or from primary EPG", ep.getKey());
return;
}
}
// TODO: alagalah: I have a draft to address structure of mEPG
// conditions, but
// out of scope until broader bugs with conditions are fixed.
List<ConditionName> conds = ctx.getEndpointManager().getConditionsForEndpoint(ep);
ConditionGroup cg = ctx.getCurrentPolicy().getEgCondGroup(new EgKey(ep.getTenant(), ep.getEndpointGroup()), conds);
this.cgId = getCondGroupOrdinal(cg);
// Based on network containment, determine components of
// forwarding context
Subnet s = ctx.getTenant(ep.getTenant()).resolveSubnet(new SubnetId(networkContainment));
L2FloodDomainId l2fdId = new L2FloodDomainId(s.getParent().getValue());
L2BridgeDomain bd = tenant.resolveL2BridgeDomain(l2fdId);
L2FloodDomain fd = tenant.resolveL2FloodDomain(l2fdId);
L3Context l3c = tenant.resolveL3Context(l2fdId);
// Set ordinal id's for use in flows for each forwarding context
// component
this.epgId = getContextOrdinal(ep);
// TODO: alagalah Li/Be: This idea can be extended to include conditions.
this.tunnelId = getContextOrdinal(ep, networkContainment);
if (bd != null)
this.bdId = getContextOrdinal(ep.getTenant(), bd.getId());
if (fd != null)
this.fdId = getContextOrdinal(ep.getTenant(), fd.getId());
if (l3c != null)
this.l3Id = getContextOrdinal(ep.getTenant(), l3c.getId());
}
public int getTunnelId() {
return tunnelId;
}
public int getEpgId() {
return this.epgId;
}
public NetworkDomainId getNetworkContainment() {
return this.networkContainment;
}
public EpKey getEp() {
return this.ep;
}
public int getBdId() {
return this.bdId;
}
public int getFdId() {
return this.fdId;
}
public int getL3Id() {
return this.l3Id;
}
public int getCgId() {
return this.cgId;
}
}
@VisibleForTesting
// Used only for unit testing
public static void resetPolicyOrdinalValue() {
policyOrdinal.set(1);
}
}