/* * Copyright (c) 2015 Hewlett Packard Enterprise Development Company, L.P. 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.nic.graph.impl; import com.google.common.collect.Sets; import org.opendaylight.nic.graph.api.CompilerGraph; import org.opendaylight.nic.graph.api.CompilerGraphException; import org.opendaylight.nic.graph.api.InputGraph; import org.opendaylight.nic.mapping.api.IntentMappingService; import org.opendaylight.yang.gen.v1.urn.opendaylight.nic.intent.graph.rev150911.Graph; import org.opendaylight.yang.gen.v1.urn.opendaylight.nic.intent.graph.rev150911.GraphBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.nic.intent.graph.rev150911.graph.Edges; import org.opendaylight.yang.gen.v1.urn.opendaylight.nic.intent.graph.rev150911.graph.IntentIds; import org.opendaylight.yang.gen.v1.urn.opendaylight.nic.intent.graph.rev150911.graph.Nodes; import org.opendaylight.yang.gen.v1.urn.opendaylight.nic.intent.graph.rev150911.graph.NodesBuilder; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceRegistration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; public class CompilerGraphImpl implements CompilerGraph { private static final Logger LOG = LoggerFactory .getLogger(CompilerGraphImpl.class); // Graph representation is utilized for Graph visualization. protected static Collection<InputGraph> policyGraph = new LinkedList<>(); protected static Graph policyGraphMDSAL = new GraphBuilder().build(); protected ServiceRegistration<CompilerGraph> graphRegistration; protected IntentMappingService intentMappingService; public CompilerGraphImpl (IntentMappingService mappingSvc){ init(); this.intentMappingService = mappingSvc; } public void addPolicy(InputGraph graph) { if (!policyGraph.contains(graph)) { policyGraph.add(new InputGraphImpl(graph.src(), graph.dst(), graph.action())); } } public void addPolicy(Graph graph) { if (!policyGraphMDSAL.equals(graph)) { policyGraphMDSAL.getNodes().add((Nodes) graph.getNodes()); policyGraphMDSAL.getEdges().add((Edges) graph.getEdges()); } } public void init() { try { BundleContext context = FrameworkUtil.getBundle(this.getClass()).getBundleContext(); graphRegistration = context.registerService(CompilerGraph.class, this, null); } catch (Exception e) { LOG.error("Exception in graphRegistration"); } LOG.info("Initialization done"); } public void close() throws Exception { // Close active registrations if (graphRegistration != null) { graphRegistration.unregister(); LOG.info("IntentengineImpl: registrations closed"); } } @Override public Set<Nodes> parseEndpointGroup(String csv) { Set<Nodes> endpoints = new LinkedHashSet<>(); String[] labels = csv.split(","); for (String label : labels) { endpoints.add(new NodesBuilder().setName(label).build()); } return endpoints; } // compile the input graph after normalization // TODO store the UUIDs of all the compiled policies in the compiled policy Graph @Override public Collection<InputGraph> compile(Collection<InputGraph> policies) throws CompilerGraphException { InputGraph policy, policy2; // Populate the Mapping services // Assuming that Mapping services are populated by external sources // Normalize the graph NormalizedGraphImpl normalGraph = new NormalizedGraphImpl(intentMappingService); Collection<InputGraph> normalizedPolicies = normalGraph.normalizedGraph(policies); Collection<InputGraph> compiledPolicies = new LinkedList<>(); //Queue<InputGraph> conflictingPolicies = new LinkedList<>(policies); Queue<InputGraph> conflictingPolicies = new LinkedList<>(normalizedPolicies); while (!conflictingPolicies.isEmpty()) { policy = conflictingPolicies.remove(); Iterator<InputGraph> iterator2 = conflictingPolicies.iterator(); Collection<InputGraph> results = new LinkedList<>(); while (iterator2.hasNext()) { policy2 = iterator2.next(); if (conflicts(policy, policy2)) { iterator2.remove(); results.addAll(resolve(policy, policy2)); LOG.info("Conflict occured"); } LOG.info("No Conflicts"); } if (results.isEmpty()) { addPolicy(policy); compiledPolicies.add(policy); } else { conflictingPolicies.addAll(results); } } LOG.info("Compilation done."); //return policyGraph; return compiledPolicies; } @Override public Collection<Graph> storeComposedGraph (Collection<InputGraph> compiledPolicies) { Collection<Graph> composedGraph = new LinkedList<>(); for (InputGraph policy : compiledPolicies) { List<IntentIds> id = new ArrayList<IntentIds>(policy.id()); List<Nodes> src = new ArrayList<Nodes>(policy.src()); List<Nodes> dst = new ArrayList<Nodes>(policy.dst()); List<Edges> edges = new ArrayList<Edges>(policy.action()); Graph graph = new GraphBuilder().setIntentIds(id).setNodes(src).setNodes(dst).setEdges(edges).build(); composedGraph.add(graph); } return composedGraph; } @Override public Graph compile(Collection<Graph> policies, int flag) throws CompilerGraphException { Graph policyGraph1 = new GraphBuilder().build(); // to be utilization for modifications on the graph Queue<Graph> conflictingPolicies = new LinkedList<>(policies); while (!conflictingPolicies.isEmpty()) { Graph policy = conflictingPolicies.remove(); Iterator<Graph> iterator2 = conflictingPolicies.iterator(); Collection<Graph> results = new LinkedList<>(); while (iterator2.hasNext()) { Graph policy2 = iterator2.next(); if (conflicts(policy, policy2)) { iterator2.remove(); results.addAll(resolve(policy, policy2)); } } if (results.isEmpty()) { addPolicy(policy); } else { conflictingPolicies.addAll(results); } } return policyGraphMDSAL; } private boolean conflicts(InputGraph p1, InputGraph p2) throws NullPointerException { ClassifierImpl ci; if (p1.classifier() != null) { if ((p1.classifier().equals(ClassifierImpl.getInstance(ExpressionImpl.EXPRESSION_NULL))) && (p2.classifier().equals(ClassifierImpl.getInstance(ExpressionImpl.EXPRESSION_NULL)))) { if (!Sets.intersection(p1.src(), p2.src()).isEmpty() && !Sets.intersection(p1.dst(), p2.dst()).isEmpty()) { return true; } } else { ci = (p1.classifier()).and(p2.classifier()); if (!Sets.intersection(p1.src(), p2.src()).isEmpty() && !Sets.intersection(p1.dst(), p2.dst()).isEmpty() && !ci.isEmpty()) { return true; } } } else if (!Sets.intersection(p1.src(), p2.src()).isEmpty() && !Sets.intersection(p1.dst(), p2.dst()).isEmpty()) { return true; } return false; } private boolean conflicts(Graph p1, Graph p2) throws NullPointerException { if (p1.getClassifiers() != null) { if ((p1.getClassifiers().equals(ClassifierImpl.getInstance(ExpressionImpl.EXPRESSION_NULL))) && (p2.getClassifiers().equals(ClassifierImpl.getInstance(ExpressionImpl.EXPRESSION_NULL)))) { if (!p1.getEdges().contains(p2.getEdges())) { return true; } } else { if (!p1.getEdges().contains(p2.getEdges()) && !p1.getClassifiers().contains(p2.getClassifiers())) { return true; } } } else { if (p1.getNodes().equals(p2.getNodes())) { return true; } } return false; } /* Classifiers are used to resolve the * @param p1 */ private Collection<InputGraph> resolve(InputGraph p1, InputGraph p2) throws CompilerGraphException { Collection<InputGraph> policies = new LinkedList<>(); Sets.SetView<Nodes> src; Sets.SetView<Nodes> dst; ClassifierImpl ci; // All the possible cases below for classifiers src = Sets.difference(p1.src(), p2.src()); if (!src.isEmpty()) { // Case: S1 and not S2 , D1 and not D2 dst = Sets.difference(p1.dst(), p2.dst()); if (!dst.isEmpty()) { if (p1.classifier() != null && p2.classifier() != null) { // C1 and not C2 ci = (p1.classifier()).sub(p2.classifier()); if (!ci.isEmpty()) { policies.add(new InputGraphImpl(src, dst, p1.action(), ci)); } // C1 and C2 ci = (p1.classifier()).and(p2.classifier()); if (!ci.isEmpty()) { policies.add(new InputGraphImpl(src, dst, p1.action(), ci)); } } else { policies.add(new InputGraphImpl(src, dst, p1.action())); } } // Case: S1 and not S2 , D1 and D2 dst = Sets.intersection(p1.dst(), p2.dst()); if (!dst.isEmpty()) { if (p1.classifier() != null && p2.classifier() != null) { // C1 and not C2 ci = (p1.classifier()).sub(p2.classifier()); if (!ci.isEmpty()) { policies.add(new InputGraphImpl(src, dst, p1.action(), ci)); } // C1 and C2 ci = (p1.classifier()).and(p2.classifier()); if (!ci.isEmpty()) { policies.add(new InputGraphImpl(src, dst, p1.action(), ci)); } } else { policies.add(new InputGraphImpl(src, dst, p1.action())); } } } src = Sets.intersection(p1.src(), p2.src()); if (!src.isEmpty()) { // Case: S1 and S2 , D1 and D2 dst = Sets.intersection(p1.dst(), p2.dst()); if (!dst.isEmpty()) { // C1 and not C2 if (p1.classifier() != null && p2.classifier() != null) { ci = (p1.classifier()).sub(p2.classifier()); if (!ci.isEmpty()) { policies.add(new InputGraphImpl(src, dst, p1.action(), ci)); } // C1 and C2 ci = (p1.classifier()).and(p2.classifier()); if (!ci.isEmpty()) { Set<Edges> mergedActions = merge(p1.action(), p2.action()); if (mergedActions == null) { throw new CompilerGraphException( "Unable to merge exclusive actions", Arrays.asList(p1, p2)); } policies.add(new InputGraphImpl(src, dst, mergedActions, ((p1 .classifier()).and(p2.classifier())))); } // C2 and not C1 ci = (p2.classifier()).sub(p1.classifier()); if (!ci.isEmpty()) { policies.add(new InputGraphImpl(src, dst, p2.action(), ci)); } } else { Set<Edges> mergedActions = merge(p1.action(), p2.action()); if (mergedActions == null) { throw new CompilerGraphException( "Unable to merge exclusive actions", Arrays.asList(p1, p2)); } policies.add(new InputGraphImpl(src, dst, mergedActions)); } } // Case: S1 and S2 , D1 and not D2 dst = Sets.difference(p1.dst(), p2.dst()); if (!dst.isEmpty()) { if (p1.classifier() != null && p2.classifier() != null) { // C1 and not C2 ci = (p1.classifier()).sub(p2.classifier()); if (!ci.isEmpty()) { policies.add(new InputGraphImpl(src, dst, p1.action(), ci)); } // C1 and C2 ci = (p1.classifier()).and(p2.classifier()); if (!ci.isEmpty()) { policies.add(new InputGraphImpl(src, dst, p1.action(), ci)); } } else { policies.add(new InputGraphImpl(src, dst, p1.action())); } } // Case: S1 and S2 , D2 and not D1 dst = Sets.difference(p2.dst(), p1.dst()); if (!dst.isEmpty()) { if (p1.classifier() != null && p2.classifier() != null) { // C1 and C2 ci = (p1.classifier()).and(p2.classifier()); if (!ci.isEmpty()) { policies.add(new InputGraphImpl(src, dst, p2.action(), ci)); } // C2 and not C1 ci = (p2.classifier()).sub(p1.classifier()); if (!ci.isEmpty()) { policies.add(new InputGraphImpl(src, dst, p2.action(), ci)); } } else { policies.add(new InputGraphImpl(src, dst, p2.action())); } } } src = Sets.difference(p2.src(), p1.src()); if (!src.isEmpty()) { // Case: S2 and not S1 , D1 and D2 dst = Sets.intersection(p1.dst(), p2.dst()); if (!dst.isEmpty()) { if (p1.classifier() != null && p2.classifier() != null) { // C1 and C2 ci = (p1.classifier()).and(p2.classifier()); if (!ci.isEmpty()) { policies.add(new InputGraphImpl(src, dst, p2.action(), ci)); } // C2 and not C1 ci = (p2.classifier()).sub(p1.classifier()); if (!ci.isEmpty()) { policies.add(new InputGraphImpl(src, dst, p2.action(), ci)); } } else { policies.add(new InputGraphImpl(src, dst, p2.action())); } } // Case: S2 and not S1 , D2 and not D1 dst = Sets.difference(p2.dst(), p1.dst()); if (!dst.isEmpty()) { if (p1.classifier() != null && p2.classifier() != null) { // C2 and C1 ci = (p1.classifier()).and(p2.classifier()); if (!ci.isEmpty()) { policies.add(new InputGraphImpl(src, dst, p2.action(), ci)); } // C2 and not C1 ci = (p2.classifier()).sub(p1.classifier()); if (!ci.isEmpty()) { policies.add(new InputGraphImpl(src, dst, p2.action(), ci)); } } else { policies.add(new InputGraphImpl(src, dst, p2.action())); } } } return policies; } private Collection<Graph> resolve(Graph p1, Graph p2) throws CompilerGraphException { Collection<Graph> policies = new LinkedList<>(); // TODO All the possible cases below policies.add(new GraphBuilder().setNodes(p1.getNodes()).setNodes(p2.getNodes()).setEdges(p1.getEdges()).setEdges(p2.getEdges()).build()); //policies.add(new InputGraphImpl(p1.src(), p1.dst(), merge(p1.action(),p2.action()), ci)); return policies; } /** * Utilizes the input graph actions or edges types, which can be categorized as different sets that be * composed to create a single edge of allow (white listing). * @param a1 set of edges or intents * @param a2 set of edges or intents * @return a composed list of actions */ private Set<Edges> merge(Set<Edges> a1, Set<Edges> a2) throws CompilerGraphException { Set<Edges> composeableActions = new LinkedHashSet<>(); Set<Edges> observableActions = new LinkedHashSet<>(); Set<Edges> exclusiveActions = new LinkedHashSet<>(); for (Edges action : Sets.union(a1, a2)) { switch (action.getType()) { case MustAllow: composeableActions.add(action); break; case Conditional: composeableActions.add(action); break; case CanAllow: observableActions.add(action); break; case MustDeny: exclusiveActions.add(action); break; default: return null; } } if (!exclusiveActions.isEmpty()) { if (exclusiveActions.size() == 1) { return Sets.union(exclusiveActions, observableActions); } else { return null; } } return Sets.union(composeableActions, observableActions); } @Override public InputGraph createGraph(Set<IntentIds> id, Set<Nodes> source, Set<Nodes> destination, Set<Edges> action) { return new InputGraphImpl (id, source, destination, action); } @Override public InputGraph createGraph(Set<Nodes> source, Set<Nodes> destination, Set<Edges> action) { return new InputGraphImpl (source, destination, action); } @Override public InputGraph createGraph(Set<Nodes> source, Set<Nodes> destination, Set<Edges> action, ClassifierImpl classifier) { return new InputGraphImpl (source, destination, action, classifier); } }