/*
* 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.dto;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.concurrent.Immutable;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionName;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierName;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContractId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2BridgeDomainId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2FloodDomainId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L3ContextId;
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.policy.rev140421.NetworkDomain;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.ForwardingContext;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Policy;
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.Contract;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.EndpointGroup;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.ExternalImplicitGroup;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.SubjectFeatureInstances;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ClassifierInstance;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Table;
/**
* Wrap some convenient indexes around a {@link Tenant} object
* @author readams
*/
@Immutable
public class IndexedTenant {
private final Tenant tenant;
private final int hashCode;
private final Map<EndpointGroupId, EndpointGroup> endpointGroups =
new HashMap<>();
private final Map<ContractId, Contract> contracts =
new HashMap<>();
private final Table<String, Class<? extends NetworkDomainId>, NetworkDomain> networkDomains = HashBasedTable.<String, Class<? extends NetworkDomainId>, NetworkDomain>create();
private final Map<ClassifierName, ClassifierInstance> classifiers =
new HashMap<>();
private final Map<ActionName, ActionInstance> actions =
new HashMap<>();
private final Map<String, Set<SubnetId>> subnetMap = new HashMap<>();
private Set<ExternalImplicitGroup> externalImplicitGroups = Collections.emptySet();
public IndexedTenant(Tenant tenant) {
this.tenant = tenant;
this.hashCode = tenant.hashCode();
if (tenant.getPolicy() != null) {
processPolicy(tenant.getPolicy());
}
if (tenant.getForwardingContext() != null) {
processForwardingContext(tenant.getForwardingContext());
}
}
private void processPolicy(Policy policy) {
if (policy.getEndpointGroup() != null) {
for (EndpointGroup eg : policy.getEndpointGroup()) {
endpointGroups.put(eg.getId(), eg);
}
}
if (policy.getExternalImplicitGroup() != null) {
externalImplicitGroups = ImmutableSet.copyOf(policy.getExternalImplicitGroup());
}
if (policy.getContract() != null) {
for (Contract c : policy.getContract()) {
contracts.put(c.getId(), c);
}
}
if (policy.getSubjectFeatureInstances() != null) {
SubjectFeatureInstances sfi = policy.getSubjectFeatureInstances();
if (sfi.getClassifierInstance() != null) {
for (ClassifierInstance ci : sfi.getClassifierInstance()) {
classifiers.put(ci.getName(), ci);
}
}
if (sfi.getActionInstance() != null) {
for (ActionInstance action : sfi.getActionInstance()) {
actions.put(action.getName(), action);
}
}
}
}
private void processForwardingContext(ForwardingContext fwCtx) {
if (fwCtx.getL3Context() != null) {
for (L3Context c : fwCtx.getL3Context()) {
networkDomains.put(c.getId().getValue(), L3ContextId.class, c);
}
}
if (fwCtx.getL2BridgeDomain() != null) {
for (L2BridgeDomain c : fwCtx.getL2BridgeDomain()) {
networkDomains.put(c.getId().getValue(), L2BridgeDomainId.class, c);
}
}
if (fwCtx.getL2FloodDomain() != null) {
for (L2FloodDomain c : fwCtx.getL2FloodDomain()) {
networkDomains.put(c.getId().getValue(), L2FloodDomainId.class, c);
}
}
if (fwCtx.getSubnet() != null) {
for (Subnet s : fwCtx.getSubnet()) {
networkDomains.put(s.getId().getValue(), SubnetId.class, s);
Set<SubnetId> sset = subnetMap.get(s.getParent().getValue());
if (sset == null) {
subnetMap.put(s.getParent().getValue(), sset = new HashSet<SubnetId>());
}
sset.add(s.getId());
}
}
}
/**
* Get the underlying tenant object
* @return the {@link Tenant}
*/
public Tenant getTenant() {
return tenant;
}
/**
* Gets all external implicit groups in the tenant
* @return immutable set of EIGs
*/
public Set<ExternalImplicitGroup> getExternalImplicitGroups() {
return externalImplicitGroups;
}
/**
* Look up the endpoint group specified
* @param id the {@link EndpointGroupId}
* @return the {@link EndpointGroup} if it exists, or <code>null</code>
* otherwise
*/
public EndpointGroup getEndpointGroup(EndpointGroupId id) {
return endpointGroups.get(id);
}
/**
* Look up the contract specified
* @param id the {@link ContractId}
* @return the {@link Contract} if it exists, or <code>null</code>
* otherwise
*/
public Contract getContract(ContractId id) {
return contracts.get(id);
}
/**
* Look up the classifier instance specified
* @param name the {@link ClassifierName}
* @return the {@link ClassifierInstance} if it exists, or <code>null</code>
* otherwise
*/
public ClassifierInstance getClassifier(ClassifierName name) {
return classifiers.get(name);
}
/**
* Look up the classifier instance specified
* @param name the {@link ActionName}
* @return the {@link ActionInstance} if it exists, or <code>null</code>
* otherwise
*/
public ActionInstance getAction(ActionName name) {
return actions.get(name);
}
/**
* Get the layer 3 context
* @param id the {@link L3ContextId}
* @return the {@link L3Context} or <code>null</code> if it does not exist
*/
public L3Context resolveL3Context(L3ContextId id) {
return resolveDomain(L3Context.class, id);
}
/**
* Get the layer 3 context for the specified l2 bridge domain by walking
* up the hierarchy
* @param id the {@link L2BridgeDomainId}
* @return the {@link L3Context} or <code>null</code> if it does not exist
*/
public L3Context resolveL3Context(L2BridgeDomainId id) {
return resolveDomain(L3Context.class, id);
}
/**
* Get the layer 3 context for the specified l2 flood domain by walking
* up the hierarchy
* @param id the {@link L2FloodDomainId}
* @return the {@link L3Context} or <code>null</code> if it does not exist
*/
public L3Context resolveL3Context(L2FloodDomainId id) {
return resolveDomain(L3Context.class, id);
}
/**
* Get the layer 2 bridge domain
* @param id the {@link L2BridgeDomainId}
* @return the {@link L2BridgeDomain} or <code>null</code> if it does not exist
*/
public L2BridgeDomain resolveL2BridgeDomain(L2BridgeDomainId id) {
return resolveDomain(L2BridgeDomain.class, id);
}
/**
* Get the layer 2 bridge domain for the specified l2 flood domain by walking
* up the hierarchy
* @param id the {@link L2FloodDomainId}
* @return the {@link L2BridgeDomain} or <code>null</code> if it does not exist
*/
public L2BridgeDomain resolveL2BridgeDomain(L2FloodDomainId id) {
return resolveDomain(L2BridgeDomain.class, id);
}
/**
* Get the layer 2 flood domain
* @param id the {@link L2FloodDomainId}
* @return the {@link L2FloodDomain} or <code>null</code> if it does not exist
*/
public L2FloodDomain resolveL2FloodDomain(L2FloodDomainId id) {
return resolveDomain(L2FloodDomain.class, id);
}
/**
* Get the subnet based on it's ID. Since subnet is on the bottom
* of the forwarding hierarchy, there is no other upstream domain
* to resolved.
*
* @param id of the {@link SubnetId}
* @return the {@link Subnet} or <code>null</code> if it does
* not exist
*/
public Subnet resolveSubnet(SubnetId id) {
return resolveDomain(Subnet.class, id);
}
/**
* Resolve all subnets applicable to the given network domain ID
* @param id the {@link NetworkDomainId}
* @return the set of subnets. Cannot be null, but could be empty.
*/
public Collection<Subnet> resolveSubnets(NetworkDomainId id) {
Set<SubnetId> sset = new HashSet<>();
HashSet<NetworkDomainId> visited = new HashSet<>();
while (id != null) {
if (visited.contains(id)) break;
visited.add(id);
Set<SubnetId> cursset = subnetMap.get(id.getValue());
if (cursset != null)
sset.addAll(cursset);
NetworkDomain d = networkDomains.get(id.getValue(), id.getClass());
if (d == null) break;
if (d instanceof Subnet) {
id = ((Subnet)d).getParent();
sset.add(((Subnet) d).getId());
}
else if (d instanceof L2BridgeDomain)
id = ((L2BridgeDomain)d).getParent();
else if (d instanceof L2FloodDomain)
id = ((L2FloodDomain)d).getParent();
else
id = null;
}
return Collections2.transform(sset, new Function<SubnetId, Subnet>() {
@Override
public Subnet apply(SubnetId input) {
return (Subnet)networkDomains.get(input.getValue(), SubnetId.class);
}
});
}
// ******
// Object
// ******
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IndexedTenant other = (IndexedTenant) obj;
if (tenant == null) {
if (other.tenant != null)
return false;
} else if (!tenant.equals(other.tenant))
return false;
return true;
}
// **************
// Implementation
// **************
@SuppressWarnings("unchecked")
private <C extends NetworkDomain, I extends NetworkDomainId> C resolveDomain(Class<C> domainClass, I id) {
SetMultimap<I, Class<? extends NetworkDomainId>> visited = HashMultimap.create();
while (id != null) {
// TODO condition
if (visited.get(id) != null && visited.containsEntry(id, id.getClass())) {
return null;
}
visited.put(id, id.getClass());
NetworkDomain d = networkDomains.get(id.getValue(), id.getClass());
if (d == null)
return null;
if (domainClass.isInstance(d))
return domainClass.cast(d);
if (d instanceof L2BridgeDomain) {
id = (I) ((L2BridgeDomain) d).getParent();
} else if (d instanceof L2FloodDomain) {
id = (I) ((L2FloodDomain) d).getParent();
} else {
id = null;
}
}
return null;
}
}