add NetworkPolicyListener 94/79294/12
authorSam Hague <shague@redhat.com>
Mon, 7 Jan 2019 13:45:51 +0000 (08:45 -0500)
committerSam Hague <shague@redhat.com>
Thu, 24 Jan 2019 18:45:58 +0000 (18:45 +0000)
Depends-on: Id84841e106352bd4e7937b5493cb3b4cd0df2e8f
Change-Id: I5f801ec88cb75f3590c34a50402935ed74a25794
Signed-off-by: Sam Hague <shague@redhat.com>
Signed-off-by: Stephen Kitt <skitt@redhat.com>
Signed-off-by: Sam Hague <shague@redhat.com>
coe/impl/pom.xml
coe/impl/src/main/java/org/opendaylight/netvirt/coe/listeners/NetworkPolicyListener.java [new file with mode: 0644]
coe/impl/src/main/java/org/opendaylight/netvirt/coe/utils/AceNetworkPolicyUtils.java [new file with mode: 0644]
coe/impl/src/main/java/org/opendaylight/netvirt/coe/utils/AclUtils.java [new file with mode: 0644]
coe/impl/src/main/java/org/opendaylight/netvirt/coe/utils/NetworkPolicyUtils.java [new file with mode: 0644]
coe/impl/src/test/java/org/opendaylight/netvirt/coe/listeners/NetworkPolicyListenerTest.java [new file with mode: 0644]
coe/impl/src/test/java/org/opendaylight/netvirt/coe/listeners/Verifications.java [new file with mode: 0644]

index cf67964f12a3e01e72ce49c67f0fd405aeb8209a..d96edd12233265888939b57c7b3f768a01a74bf7 100644 (file)
@@ -34,11 +34,29 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
             <groupId>org.opendaylight.controller</groupId>
             <artifactId>sal-binding-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.genius</groupId>
+            <artifactId>networkutils</artifactId>
+            <version>${genius.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.infrautils</groupId>
             <artifactId>jobcoordinator-api</artifactId>
             <version>${infrautils.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal.model</groupId>
+            <artifactId>ietf-access-control-list</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal.model</groupId>
+            <artifactId>yang-ext</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>aclservice-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>coe-api</artifactId>
@@ -59,23 +77,47 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
             <artifactId>utils.southbound-utils</artifactId>
             <version>${ovsdb.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.serviceutils</groupId>
+            <artifactId>listener-api</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.serviceutils</groupId>
             <artifactId>tools-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.aries.blueprint</groupId>
+            <artifactId>blueprint-maven-plugin-annotation</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-binding-broker-impl</artifactId>
+            <version>${controller.mdsal.version}</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.genius</groupId>
-            <artifactId>mdsalutil-api</artifactId>
+            <artifactId>testutils</artifactId>
             <version>${genius.version}</version>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.genius</groupId>
-            <artifactId>networkutils</artifactId>
-            <version>0.6.0-SNAPSHOT</version>
+            <artifactId>mdsalutil-testutils</artifactId>
+            <version>${genius.version}</version>
+            <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.aries.blueprint</groupId>
-            <artifactId>blueprint-maven-plugin-annotation</artifactId>
+            <groupId>org.opendaylight.serviceutils</groupId>
+            <artifactId>tools-testutils</artifactId>
+            <version>${serviceutils.version}</version>
+            <scope>test</scope>
         </dependency>
     </dependencies>
 
diff --git a/coe/impl/src/main/java/org/opendaylight/netvirt/coe/listeners/NetworkPolicyListener.java b/coe/impl/src/main/java/org/opendaylight/netvirt/coe/listeners/NetworkPolicyListener.java
new file mode 100644 (file)
index 0000000..6e5a7cf
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2018 Red Hat, 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.netvirt.coe.listeners;
+
+import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
+import static org.opendaylight.netvirt.coe.utils.AceNetworkPolicyUtils.buildAcl;
+import static org.opendaylight.netvirt.coe.utils.AceNetworkPolicyUtils.getAclNameFromPolicy;
+import static org.opendaylight.netvirt.coe.utils.AclUtils.getAclIid;
+
+import javax.annotation.Nonnull;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import org.apache.aries.blueprint.annotation.service.Reference;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner;
+import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
+import org.opendaylight.serviceutils.tools.mdsal.listener.AbstractSyncDataTreeChangeListener;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.NetworkPolicies;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.network.policies.NetworkPolicy;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.rev181205.K8s;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class NetworkPolicyListener extends AbstractSyncDataTreeChangeListener<NetworkPolicy> {
+    private static final Logger LOG = LoggerFactory.getLogger(NetworkPolicyListener.class);
+    private final RetryingManagedNewTransactionRunner txRunner;
+
+    @Inject
+    public NetworkPolicyListener(@Reference DataBroker dataBroker) {
+        super(dataBroker, LogicalDatastoreType.CONFIGURATION,
+            InstanceIdentifier.create(K8s.class).child(NetworkPolicies.class).child(NetworkPolicy.class));
+        this.txRunner = new RetryingManagedNewTransactionRunner(dataBroker);
+    }
+
+    @Override
+    public void add(@Nonnull InstanceIdentifier<NetworkPolicy> instanceIdentifier, @Nonnull NetworkPolicy policy) {
+        LOG.info("add: id: {}\npolicy: {}", instanceIdentifier, policy);
+        updateAcl(policy, false);
+    }
+
+    @Override
+    public void remove(@Nonnull InstanceIdentifier<NetworkPolicy> instanceIdentifier, @Nonnull NetworkPolicy policy) {
+        LOG.info("remove: id: {}\npolicy: {}", instanceIdentifier, policy);
+        updateAcl(policy, true);
+    }
+
+    @Override
+    public void update(@Nonnull InstanceIdentifier<NetworkPolicy> instanceIdentifier,
+                       @Nonnull NetworkPolicy oldPolicy, @Nonnull NetworkPolicy policy) {
+        LOG.info("update: id: {}\nold policy: {}\nnew policy: {}", instanceIdentifier, oldPolicy, policy);
+        updateAcl(policy, false);
+    }
+
+    private void updateAcl(@Nonnull NetworkPolicy policy, boolean isDeleted) {
+        LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
+            String aclName = getAclNameFromPolicy(policy);
+            Acl acl = buildAcl(policy, isDeleted);
+            InstanceIdentifier<Acl> aclIid = getAclIid(aclName);
+            // write the ace, and the api will create the acl parent if needed
+            tx.put(aclIid, acl, true);
+        }), LOG, "Failed to add acl from policy: {}", policy);
+    }
+}
diff --git a/coe/impl/src/main/java/org/opendaylight/netvirt/coe/utils/AceNetworkPolicyUtils.java b/coe/impl/src/main/java/org/opendaylight/netvirt/coe/utils/AceNetworkPolicyUtils.java
new file mode 100644 (file)
index 0000000..477d360
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2018 Red Hat, 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.netvirt.coe.utils;
+
+import static org.opendaylight.netvirt.coe.utils.AclUtils.DIRECTION_MAP;
+import static org.opendaylight.netvirt.coe.utils.AclUtils.buildName;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.PROTOCOL_MAP;
+
+import java.util.ArrayList;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.Ipv4Acl;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.AclBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.AclKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.AccessListEntriesBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.Ace;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.AceBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.AceKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.ActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.MatchesBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.actions.packet.handling.PermitBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4Builder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160218.acl.transport.header.fields.DestinationPortRangeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.egress.rule.NetworkPolicyEgressRule;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.egress.rule.network.policy.egress.rule.EgressPorts;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.egress.rule.network.policy.egress.rule.To;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.ingress.rule.NetworkPolicyIngressRule;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.ingress.rule.network.policy.ingress.rule.From;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.ingress.rule.network.policy.ingress.rule.IngressPorts;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.network.policies.NetworkPolicy;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.peer.NetworkPolicyPeer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.port.NetworkPolicyPort;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.spec.NetworkPolicySpec;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.spec.network.policy.spec.Egress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.spec.network.policy.spec.Ingress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionBase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionEgress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionIngress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.SecurityRuleAttr;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.SecurityRuleAttrBuilder;
+
+public final class AceNetworkPolicyUtils {
+    private AceNetworkPolicyUtils() {}
+
+    @Nonnull
+    public static String getAclNameFromPolicy(@Nonnull NetworkPolicy policy) {
+        String aclName = "";
+        if (policy.getUuid() != null) {
+            aclName = policy.getUuid().getValue();
+        }
+        return aclName;
+    }
+
+    // TODO map empty rules:
+    // ingress:empty - no incoming allowed
+    // egress:empty - no outgoing allowed
+    @Nonnull
+    public static Acl buildAcl(@Nonnull NetworkPolicy policy, boolean isDeleted) {
+        String aclName = getAclNameFromPolicy(policy);
+        ArrayList<Ace> aceList = new ArrayList<>();
+
+        if (policy.getNetworkPolicySpec() != null) {
+            NetworkPolicySpec spec = policy.getNetworkPolicySpec();
+            if (spec.getIngress() != null) {
+                for (Ingress ingress : spec.getIngress()) {
+                    NetworkPolicyIngressRule rule = ingress.getNetworkPolicyIngressRule();
+                    if (rule.getIngressPorts() != null) {
+                        for (IngressPorts port : rule.getIngressPorts()) {
+                            if (port.getNetworkPolicyPort() != null) {
+                                Ace ace = buildPortAce(isDeleted, aclName,
+                                    DirectionIngress.class, port.getNetworkPolicyPort());
+                                aceList.add(ace);
+                            }
+                        }
+                    }
+                    if (rule.getFrom() != null) {
+                        for (From from: rule.getFrom()) {
+                            if (from.getNetworkPolicyPeer() != null) {
+                                Ace ace = buildPolicyAce(isDeleted, aclName,
+                                    DirectionIngress.class, from.getNetworkPolicyPeer());
+                                aceList.add(ace);
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (spec.getEgress() != null) {
+                for (Egress egress : spec.getEgress()) {
+                    NetworkPolicyEgressRule rule = egress.getNetworkPolicyEgressRule();
+                    if (rule.getEgressPorts() != null) {
+                        for (EgressPorts port : rule.getEgressPorts()) {
+                            if (port.getNetworkPolicyPort() != null) {
+                                Ace ace = buildPortAce(isDeleted, aclName,
+                                    DirectionEgress.class, port.getNetworkPolicyPort());
+                                aceList.add(ace);
+                            }
+                        }
+                    }
+                    if (rule.getTo() != null) {
+                        for (To to: rule.getTo()) {
+                            if (to.getNetworkPolicyPeer() != null) {
+                                Ace ace = buildPolicyAce(isDeleted, aclName,
+                                    DirectionEgress.class, to.getNetworkPolicyPeer());
+                                aceList.add(ace);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        AccessListEntriesBuilder accessListEntriesBuilder = new AccessListEntriesBuilder();
+        accessListEntriesBuilder.setAce(aceList);
+
+        AclBuilder aclBuilder = new AclBuilder();
+        aclBuilder.setAclName(aclName);
+        aclBuilder.setAclType(Ipv4Acl.class);
+        aclBuilder.setAccessListEntries(accessListEntriesBuilder.build());
+        aclBuilder.withKey(new AclKey(aclBuilder.getAclName(), aclBuilder.getAclType()));
+        return aclBuilder.build();
+    }
+
+    @Nonnull
+    public static AceBuilder getAceBuilder(boolean isDeleted, String ruleName,
+                                           @Nonnull Class<? extends DirectionBase> direction,
+                                           @Nonnull AceIpBuilder aceIpBuilder) {
+        MatchesBuilder matchesBuilder = new MatchesBuilder();
+        matchesBuilder.setAceType(aceIpBuilder.build());
+        ActionsBuilder actionsBuilder = new ActionsBuilder();
+        actionsBuilder.setPacketHandling(new PermitBuilder().setPermit(true).build());
+
+        AceBuilder aceBuilder = new AceBuilder();
+        aceBuilder.setRuleName(ruleName);
+        aceBuilder.withKey(new AceKey(aceBuilder.getRuleName()));
+        aceBuilder.setMatches(matchesBuilder.build());
+        aceBuilder.setActions(actionsBuilder.build());
+
+        SecurityRuleAttrBuilder securityRuleAttrBuilder = new SecurityRuleAttrBuilder();
+        securityRuleAttrBuilder.setDeleted(isDeleted);
+        securityRuleAttrBuilder.setDirection(direction);
+        aceBuilder.addAugmentation(SecurityRuleAttr.class, securityRuleAttrBuilder.build());
+        return aceBuilder;
+    }
+
+    @Nonnull
+    public static Ace buildPortAce(boolean isDeleted, @Nonnull String aclName,
+                                   @Nonnull Class<? extends DirectionBase> direction,
+                                   @Nonnull NetworkPolicyPort port) {
+        AceIpBuilder aceIpBuilder = new AceIpBuilder();
+        String ruleName = AclUtils.buildName(aclName, DIRECTION_MAP.get(direction), "port");
+        if (port.getProtocol() != null) {
+            aceIpBuilder.setProtocol(PROTOCOL_MAP.get(port.getProtocol()));
+            ruleName = buildName(ruleName, port.getProtocol().toString());
+        }
+
+        // TODO: map a named port
+        if (port.getPort() != null) {
+            DestinationPortRangeBuilder portRangeBuilder = new DestinationPortRangeBuilder();
+            PortNumber portNumber = new PortNumber(Integer.parseInt(port.getPort()));
+            portRangeBuilder.setLowerPort(portNumber);
+            portRangeBuilder.setUpperPort(portNumber);
+            aceIpBuilder.setDestinationPortRange(portRangeBuilder.build());
+            ruleName = buildName(ruleName, portNumber.getValue().toString());
+        }
+
+        AceBuilder aceBuilder = getAceBuilder(isDeleted, ruleName, direction, aceIpBuilder);
+
+        return aceBuilder.build();
+    }
+
+    @Nonnull
+    public static Ace buildPolicyAce(boolean isDeleted, @Nonnull String aclName,
+                                     @Nonnull Class<? extends DirectionBase> direction,
+                                     @Nonnull NetworkPolicyPeer peer) {
+        AceIpBuilder aceIpBuilder = new AceIpBuilder();
+        String ruleName = AclUtils.buildName(aclName, DIRECTION_MAP.get(direction), "peer");
+
+        if (peer.getIpBlock() != null) {
+            // TODO handle except
+            String  cidr = peer.getIpBlock().getCidr();
+            ruleName = AclUtils.buildName(ruleName, "cidr", cidr);
+            AceIpv4Builder aceIpv4Builder = new AceIpv4Builder();
+            if (direction == DirectionIngress.class) {
+                aceIpv4Builder.setSourceIpv4Network(new Ipv4Prefix(cidr));
+            } else {
+                aceIpv4Builder.setDestinationIpv4Network(new Ipv4Prefix(cidr));
+            }
+            aceIpBuilder.setAceIpVersion(aceIpv4Builder.build());
+        }
+        // TODO handle pod-selector and namespace-selector
+
+        AceBuilder aceBuilder = getAceBuilder(isDeleted, ruleName, direction, aceIpBuilder);
+
+        return aceBuilder.build();
+    }
+}
diff --git a/coe/impl/src/main/java/org/opendaylight/netvirt/coe/utils/AclUtils.java b/coe/impl/src/main/java/org/opendaylight/netvirt/coe/utils/AclUtils.java
new file mode 100644 (file)
index 0000000..1a6e673
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2018 Red Hat, 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.netvirt.coe.utils;
+
+import com.google.common.collect.ImmutableBiMap;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.AccessLists;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.Ipv4Acl;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.AclKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.AccessListEntries;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.Ace;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.AceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionBase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionEgress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionIngress;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public final class AclUtils {
+    public static final String INGRESS = "ingress";
+    public static final String EGRESS = "egress";
+    public static final ImmutableBiMap<Class<? extends DirectionBase>, String> DIRECTION_MAP = ImmutableBiMap.of(
+        DirectionIngress.class, INGRESS,
+        DirectionEgress.class, EGRESS
+    );
+
+    private AclUtils() {}
+
+    @Nonnull
+    public static InstanceIdentifier<Acl> getAclIid(@Nonnull String aclName) {
+        return InstanceIdentifier
+            .builder(AccessLists.class)
+            .child(Acl.class, new AclKey(aclName, Ipv4Acl.class))
+            .build();
+    }
+
+    @Nonnull
+    public static InstanceIdentifier<Ace> getAceIid(@Nonnull String aclName, @Nonnull String ruleName) {
+        return getAclIid(aclName).builder()
+            .child(AccessListEntries.class)
+            .child(Ace.class, new AceKey(ruleName))
+            .build();
+    }
+
+    @Nonnull
+    public static String buildName(String... args) {
+        return String.join("_", args);
+    }
+}
diff --git a/coe/impl/src/main/java/org/opendaylight/netvirt/coe/utils/NetworkPolicyUtils.java b/coe/impl/src/main/java/org/opendaylight/netvirt/coe/utils/NetworkPolicyUtils.java
new file mode 100644 (file)
index 0000000..eb1ba99
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2018 Red Hat, 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.netvirt.coe.utils;
+
+import com.google.common.collect.ImmutableBiMap;
+import java.util.List;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.core.rev181205.Protocol;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.meta.v1.rev181205.label.selector.MatchLabels;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.meta.v1.rev181205.label.selector.MatchLabelsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.PolicyType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.ip.block.IpBlock;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.ip.block.IpBlockBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.NetworkPolicies;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.egress.rule.NetworkPolicyEgressRule;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.egress.rule.NetworkPolicyEgressRuleBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.egress.rule.network.policy.egress.rule.EgressPorts;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.egress.rule.network.policy.egress.rule.EgressPortsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.egress.rule.network.policy.egress.rule.To;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.egress.rule.network.policy.egress.rule.ToBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.ingress.rule.NetworkPolicyIngressRule;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.ingress.rule.NetworkPolicyIngressRuleBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.ingress.rule.network.policy.ingress.rule.From;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.ingress.rule.network.policy.ingress.rule.FromBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.ingress.rule.network.policy.ingress.rule.IngressPorts;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.ingress.rule.network.policy.ingress.rule.IngressPortsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.network.policies.NetworkPolicy;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.network.policies.NetworkPolicyBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.network.policies.NetworkPolicyKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.peer.NetworkPolicyPeer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.peer.NetworkPolicyPeerBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.port.NetworkPolicyPort;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.port.NetworkPolicyPortBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.spec.NetworkPolicySpec;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.spec.NetworkPolicySpecBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.spec.network.policy.spec.Egress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.spec.network.policy.spec.EgressBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.spec.network.policy.spec.Ingress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.spec.network.policy.spec.IngressBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.spec.network.policy.spec.PodSelector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.spec.network.policy.spec.PodSelectorBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.rev181205.K8s;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public final class NetworkPolicyUtils {
+    public static final ImmutableBiMap<Protocol, Short> PROTOCOL_MAP = ImmutableBiMap.of(
+        Protocol.TCP, (short)6,
+        Protocol.UDP, (short)17,
+        Protocol.SCTP, (short)132
+    );
+
+    private NetworkPolicyUtils() {}
+
+    @Nonnull
+    public static MatchLabels buildMatchLabels(@Nonnull String key, @Nonnull String value) {
+        return new MatchLabelsBuilder().setKey(key).setValue(value).build();
+    }
+
+    @Nonnull
+    public static PodSelector buildPodSelector(@Nonnull List<MatchLabels> matchLabels) {
+        return new PodSelectorBuilder().setMatchLabels(matchLabels).build();
+    }
+
+    @Nonnull
+    public static InstanceIdentifier<NetworkPolicy> getNetworkPolicyIid(@Nonnull String uuid) {
+        return InstanceIdentifier.create(K8s.class).child(NetworkPolicies.class)
+            .child(NetworkPolicy.class, new NetworkPolicyKey(new Uuid(uuid)));
+    }
+
+    @Nonnull
+    public static IpBlock buildIpBlock(@Nonnull String cidr, @Nullable List<String> except) {
+        IpBlockBuilder ipBlockBuilder = new IpBlockBuilder().setCidr(cidr);
+
+        if (except != null && !except.isEmpty()) {
+            ipBlockBuilder.setExcept(except);
+        }
+
+        return new IpBlockBuilder().setCidr(cidr).setExcept(except).build();
+    }
+
+    // TODO add pod and namespace selector handling
+    @Nonnull
+    public static NetworkPolicyPeer buildNetworkPolicyPeer(@Nonnull IpBlock ipBlock) {
+        return new NetworkPolicyPeerBuilder().setIpBlock(ipBlock).build();
+    }
+
+    @Nonnull
+    public static NetworkPolicyPort buildNetworkPolicyPort(@Nonnull String port, @Nonnull Protocol protocol) {
+        return new NetworkPolicyPortBuilder().setPort(port).setProtocol(protocol).build();
+    }
+
+    @Nonnull
+    public static IngressPorts buildIngressPorts(@Nonnull NetworkPolicyPort port) {
+        return new IngressPortsBuilder().setNetworkPolicyPort(port).build();
+    }
+
+    @Nonnull
+    public static From buildFrom(@Nonnull NetworkPolicyPeer peer) {
+        return new FromBuilder().setNetworkPolicyPeer(peer).build();
+    }
+
+    @Nonnull
+    public static EgressPorts buildEgressPorts(@Nonnull NetworkPolicyPort port) {
+        return new EgressPortsBuilder().setNetworkPolicyPort(port).build();
+    }
+
+    @Nonnull
+    public static To buildTo(@Nonnull NetworkPolicyPeer peer) {
+        return new ToBuilder().setNetworkPolicyPeer(peer).build();
+    }
+
+    @Nonnull
+    public static NetworkPolicyIngressRule buildNetworkPolicyIngressRule(@Nullable List<IngressPorts> ports,
+                                                                         @Nullable List<From> fromList) {
+
+        NetworkPolicyIngressRuleBuilder networkPolicyIngressRuleBuilder = new NetworkPolicyIngressRuleBuilder();
+
+        if (ports != null && !ports.isEmpty()) {
+            networkPolicyIngressRuleBuilder.setIngressPorts(ports);
+        }
+        if (fromList != null && !fromList.isEmpty()) {
+            networkPolicyIngressRuleBuilder.setFrom(fromList);
+        }
+
+        return networkPolicyIngressRuleBuilder.build();
+    }
+
+    @Nonnull
+    public static NetworkPolicyEgressRule buildNetworkPolicyEgressRule(@Nullable List<EgressPorts> ports,
+                                                                       @Nullable List<To> toList) {
+
+        NetworkPolicyEgressRuleBuilder networkPolicyEgressRuleBuilder = new NetworkPolicyEgressRuleBuilder();
+
+        if (ports != null && !ports.isEmpty()) {
+            networkPolicyEgressRuleBuilder.setEgressPorts(ports);
+        }
+        if (toList != null && !toList.isEmpty()) {
+            networkPolicyEgressRuleBuilder.setTo(toList);
+        }
+
+        return networkPolicyEgressRuleBuilder.build();
+    }
+
+    @Nonnull
+    public static Ingress buildIngress(@Nonnull NetworkPolicyIngressRule rule) {
+        return new IngressBuilder().setNetworkPolicyIngressRule(rule).build();
+    }
+
+    @Nonnull
+    public static Egress buildEgress(@Nonnull NetworkPolicyEgressRule rule) {
+        return new EgressBuilder().setNetworkPolicyEgressRule(rule).build();
+    }
+
+    @Nonnull
+    public static NetworkPolicySpec buildNetworkPolicySpec(@Nonnull PodSelector podSelector,
+                                                           @Nullable List<Ingress> ingress,
+                                                           @Nullable List<Egress> egress,
+                                                           @Nullable List<PolicyType> policyTypes) {
+        NetworkPolicySpecBuilder networkPolicySpecBuilder = new NetworkPolicySpecBuilder().setPodSelector(podSelector);
+
+        if (ingress != null && !ingress.isEmpty()) {
+            networkPolicySpecBuilder.setIngress(ingress);
+        }
+        if (egress != null && !egress.isEmpty()) {
+            networkPolicySpecBuilder.setEgress(egress);
+        }
+        if (policyTypes != null && !policyTypes.isEmpty()) {
+            networkPolicySpecBuilder.setPolicyTypes(policyTypes);
+        }
+
+        return networkPolicySpecBuilder.build();
+    }
+
+    @Nonnull
+    public static NetworkPolicy buildNetworkPolicy(@Nonnull String uuid, @Nullable String name,
+                                                   @Nullable NetworkPolicySpec spec) {
+        NetworkPolicyBuilder networkPolicyBuilder = new NetworkPolicyBuilder().setUuid(new Uuid(uuid));
+        if (name != null) {
+            networkPolicyBuilder.setName(name);
+        }
+        if (spec != null) {
+            networkPolicyBuilder.setNetworkPolicySpec(spec);
+        }
+
+        return networkPolicyBuilder.build();
+    }
+}
diff --git a/coe/impl/src/test/java/org/opendaylight/netvirt/coe/listeners/NetworkPolicyListenerTest.java b/coe/impl/src/test/java/org/opendaylight/netvirt/coe/listeners/NetworkPolicyListenerTest.java
new file mode 100644 (file)
index 0000000..b6a1b46
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2018 Red Hat, 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.netvirt.coe.listeners;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
+import static org.opendaylight.netvirt.coe.utils.AclUtils.getAceIid;
+import static org.opendaylight.netvirt.coe.utils.AclUtils.getAclIid;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.buildEgress;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.buildEgressPorts;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.buildFrom;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.buildIngress;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.buildIngressPorts;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.buildIpBlock;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.buildMatchLabels;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.buildNetworkPolicy;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.buildNetworkPolicyEgressRule;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.buildNetworkPolicyIngressRule;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.buildNetworkPolicyPeer;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.buildNetworkPolicyPort;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.buildNetworkPolicySpec;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.buildPodSelector;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.buildTo;
+import static org.opendaylight.netvirt.coe.utils.NetworkPolicyUtils.getNetworkPolicyIid;
+
+import com.google.common.base.Optional;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.test.AbstractConcurrentDataBrokerTest;
+import org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner;
+import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.Ace;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.core.rev181205.Protocol;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.meta.v1.rev181205.label.selector.MatchLabels;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.PolicyType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.egress.rule.NetworkPolicyEgressRule;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.egress.rule.network.policy.egress.rule.EgressPorts;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.egress.rule.network.policy.egress.rule.To;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.ingress.rule.NetworkPolicyIngressRule;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.ingress.rule.network.policy.ingress.rule.From;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.ingress.rule.network.policy.ingress.rule.IngressPorts;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.network.policies.NetworkPolicy;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.peer.NetworkPolicyPeer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.spec.NetworkPolicySpec;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.spec.network.policy.spec.Egress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.spec.network.policy.spec.Ingress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.k8s.network.policy.rev181205.network.policy.spec.network.policy.spec.PodSelector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.SecurityRuleAttr;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NetworkPolicyListenerTest extends AbstractConcurrentDataBrokerTest {
+    private static final Logger LOG = LoggerFactory.getLogger(NetworkPolicyListenerTest.class);
+    private RetryingManagedNewTransactionRunner txRunner;
+    private Verifications verifications;
+
+    @Before
+    public void setUp() {
+        DataBroker dataBroker = getDataBroker();
+        NetworkPolicyListener networkPolicyListener = new NetworkPolicyListener(dataBroker);
+        networkPolicyListener.register();
+        verifications = new Verifications(dataBroker);
+        txRunner = new RetryingManagedNewTransactionRunner(dataBroker, 3);
+    }
+
+    @Test
+    public void testNetworkPolicy() throws ExecutionException, InterruptedException {
+        String policyUuid = "11111111-1111-1111-1111-111111111111";
+        String policyName = "network-policy-1";
+
+        NetworkPolicy networkPolicy1 = buildNetworkPolicy1(policyUuid, policyName);
+        InstanceIdentifier<NetworkPolicy> networkPolicyIid = getNetworkPolicyIid(policyUuid);
+
+        // write and verify NetworkPolicy to ds
+        LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
+            tx.put(networkPolicyIid, networkPolicy1, true);
+        }), LOG, "writing network policy").get();
+
+        LoggingFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
+            Optional<NetworkPolicy> networkPolicyOptional = tx.read(networkPolicyIid).get();
+            assertTrue(networkPolicyOptional.isPresent());
+            assertEquals(networkPolicyOptional.get().getName(), policyName);
+        }), LOG, "reading network policy {}", networkPolicyIid);
+
+        // wait and verify for the NetworkPolicyListener to write an acl from the policy just written
+        InstanceIdentifier<Acl> aclIid = getAclIid(policyUuid);
+        verifications.awaitForData(CONFIGURATION, aclIid);
+        verifyAcl(policyUuid);
+
+        // verify children Ace's have been written
+        verifyAce(policyUuid, "11111111-1111-1111-1111-111111111111_ingress_port_TCP_53", false);
+        verifyAce(policyUuid, "11111111-1111-1111-1111-111111111111_ingress_port_UDP_53", false);
+        verifyAce(policyUuid, "11111111-1111-1111-1111-111111111111_ingress_peer_cidr_10.1.1.1/16", false);
+        verifyAce(policyUuid, "11111111-1111-1111-1111-111111111111_egress_port_TCP_53", false);
+        verifyAce(policyUuid, "11111111-1111-1111-1111-111111111111_egress_port_UDP_53", false);
+        verifyAce(policyUuid, "11111111-1111-1111-1111-111111111111_egress_peer_cidr_10.1.1.1/16", false);
+
+        // Update the policy and verify the acl is updated
+
+        NetworkPolicy networkPolicy2 = buildNetworkPolicy2(policyUuid, policyName + "updated");
+        LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
+            tx.put(networkPolicyIid, networkPolicy2, true);
+        }), LOG, "writing network policy").get();
+
+        LoggingFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
+            Optional<NetworkPolicy> networkPolicyOptional = tx.read(networkPolicyIid).get();
+            assertTrue(networkPolicyOptional.isPresent());
+            assertEquals(networkPolicyOptional.get().getName(), policyName + "updated");
+        }), LOG, "reading network policy {}", networkPolicyIid);
+
+        // wait and verify for the NetworkPolicyListener to write an acl from the policy just written
+        verifications.awaitForData(CONFIGURATION, aclIid);
+        verifyAcl(policyUuid);
+        // verify children Ace's have been written
+        verifyAce(policyUuid, "11111111-1111-1111-1111-111111111111_ingress_port_TCP_69", false);
+        verifyAce(policyUuid, "11111111-1111-1111-1111-111111111111_ingress_port_UDP_69", false);
+        verifyAce(policyUuid, "11111111-1111-1111-1111-111111111111_ingress_peer_cidr_10.2.1.1/16", false);
+
+        // wait and verify a deleted policy will updates acls to deleted
+        LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
+            tx.delete(networkPolicyIid);
+        }), LOG, "deleting network policy").get();
+        verifications.awaitForData(CONFIGURATION, aclIid);
+        verifyAce(policyUuid, "11111111-1111-1111-1111-111111111111_ingress_port_TCP_69", true);
+    }
+
+    private void verifyAcl(String policyUuid) {
+        InstanceIdentifier<Acl> aclIid = getAclIid(policyUuid);
+        LoggingFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
+            Optional<Acl> aclOptional = tx.read(aclIid).get();
+            assertTrue(aclOptional.isPresent());
+            LOG.info("acl: {}", aclOptional.get());
+        }), LOG, "reading acl {}", aclIid);
+    }
+
+    private void verifyAce(String policyUuid, String ruleName, boolean isDeleted) {
+        InstanceIdentifier<Ace> aceIid = getAceIid(policyUuid, ruleName);
+        LoggingFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
+            Optional<Ace> aceOptional = tx.read(aceIid).get();
+            assertTrue(aceOptional.isPresent());
+            assertEquals(aceOptional.get().augmentation(SecurityRuleAttr.class).isDeleted(), isDeleted);
+            LOG.info("ace: {}", aceOptional.get());
+        }), LOG, "reading ace {}", aceIid);
+    }
+
+    private NetworkPolicy buildNetworkPolicy1(String policyUuid, String policyName) {
+        List<IngressPorts> ingressPorts = Arrays.asList(
+            buildIngressPorts(buildNetworkPolicyPort("53", Protocol.TCP)),
+            buildIngressPorts(buildNetworkPolicyPort("53", Protocol.UDP)));
+        List<String> except = Arrays.asList("10.1.2.1/24", "10.1.3.1/24");
+        NetworkPolicyPeer ingressNetworkPolicyPeer = buildNetworkPolicyPeer(buildIpBlock("10.1.1.1/16", except));
+        List<From> fromList = Collections.singletonList(buildFrom(ingressNetworkPolicyPeer));
+        NetworkPolicyIngressRule networkPolicyIngressRule = buildNetworkPolicyIngressRule(ingressPorts, fromList);
+        List<Ingress> ingressList = Collections.singletonList(buildIngress(networkPolicyIngressRule));
+
+        List<EgressPorts> egressPorts = Arrays.asList(
+            buildEgressPorts(buildNetworkPolicyPort("53", Protocol.TCP)),
+            buildEgressPorts(buildNetworkPolicyPort("53", Protocol.UDP)));
+        List<String> exceptEgress = Arrays.asList("10.1.2.1/24", "10.1.3.1/24");
+        NetworkPolicyPeer egressNetworkPolicyPeer = buildNetworkPolicyPeer(buildIpBlock("10.1.1.1/16", exceptEgress));
+        List<To> toList = Collections.singletonList(buildTo(egressNetworkPolicyPeer));
+        NetworkPolicyEgressRule networkPolicyEgressRule = buildNetworkPolicyEgressRule(egressPorts, toList);
+        List<Egress> egressList = Collections.singletonList(buildEgress(networkPolicyEgressRule));
+
+        List<PolicyType> policyTypes = Collections.singletonList(PolicyType.Egress);
+        MatchLabels matchLabels = buildMatchLabels("app", "db");
+        PodSelector podSelector = buildPodSelector(Collections.singletonList(matchLabels));
+        NetworkPolicySpec networkPolicySpec = buildNetworkPolicySpec(podSelector, ingressList, egressList, policyTypes);
+        return buildNetworkPolicy(policyUuid, policyName, networkPolicySpec);
+    }
+
+    private NetworkPolicy buildNetworkPolicy2(String policyUuid, String policyName) {
+        List<IngressPorts> ingressPorts = Arrays.asList(
+            buildIngressPorts(buildNetworkPolicyPort("69", Protocol.TCP)),
+            buildIngressPorts(buildNetworkPolicyPort("69", Protocol.UDP)));
+        List<String> except = Arrays.asList("10.2.2.1/24", "10.2.3.1/24");
+        NetworkPolicyPeer ingressNetworkPolicyPeer = buildNetworkPolicyPeer(buildIpBlock("10.2.1.1/16", except));
+        List<From> fromList = Collections.singletonList(buildFrom(ingressNetworkPolicyPeer));
+        NetworkPolicyIngressRule networkPolicyIngressRule = buildNetworkPolicyIngressRule(ingressPorts, fromList);
+        List<Ingress> ingressList = Collections.singletonList(buildIngress(networkPolicyIngressRule));
+
+        List<EgressPorts> egressPorts = Arrays.asList(
+            buildEgressPorts(buildNetworkPolicyPort("53", Protocol.TCP)),
+            buildEgressPorts(buildNetworkPolicyPort("53", Protocol.UDP)));
+        List<String> exceptEgress = Arrays.asList("10.1.2.1/24", "10.1.3.1/24");
+        NetworkPolicyPeer egressNetworkPolicyPeer = buildNetworkPolicyPeer(buildIpBlock("10.1.1.1/16", exceptEgress));
+        List<To> toList = Collections.singletonList(buildTo(egressNetworkPolicyPeer));
+        NetworkPolicyEgressRule networkPolicyEgressRule = buildNetworkPolicyEgressRule(egressPorts, toList);
+        List<Egress> egressList = Collections.singletonList(buildEgress(networkPolicyEgressRule));
+
+        List<PolicyType> policyTypes = Collections.singletonList(PolicyType.Egress);
+        MatchLabels matchLabels = buildMatchLabels("app", "db");
+        PodSelector podSelector = buildPodSelector(Collections.singletonList(matchLabels));
+        NetworkPolicySpec networkPolicySpec = buildNetworkPolicySpec(podSelector, ingressList, egressList, policyTypes);
+        return buildNetworkPolicy(policyUuid, policyName, networkPolicySpec);
+    }
+}
diff --git a/coe/impl/src/test/java/org/opendaylight/netvirt/coe/listeners/Verifications.java b/coe/impl/src/test/java/org/opendaylight/netvirt/coe/listeners/Verifications.java
new file mode 100644 (file)
index 0000000..5e6b7bb
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2018 Red Hat, 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.netvirt.coe.listeners;
+
+import java.util.concurrent.TimeUnit;
+import org.awaitility.Awaitility;
+import org.awaitility.core.ConditionFactory;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.genius.infra.Datastore;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class Verifications {
+    private static final Logger LOG = LoggerFactory.getLogger(Verifications.class);
+    private static final int AWAIT_TIMEOUT = 10;
+    private static final int AWAIT_INTERVAL = 1000;
+    private final ConditionFactory awaiter;
+    private final ManagedNewTransactionRunner txRunner;
+
+    Verifications(final DataBroker dataBroker) {
+        this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
+        this.awaiter = getAwaiter();
+    }
+
+    private ConditionFactory getAwaiter() {
+        return Awaitility.await("TestableListener")
+            .atMost(AWAIT_TIMEOUT, TimeUnit.SECONDS)//TODO constant
+            .pollInterval(AWAIT_INTERVAL, TimeUnit.MILLISECONDS);
+    }
+
+    <D extends Datastore> void awaitForData(Class<D> datastoreType, InstanceIdentifier<? extends DataObject> iid) {
+        awaiter.with()
+            .conditionEvaluationListener(condition -> LOG.info("{} ({} ms of {} s)",
+                condition.getDescription(), condition.getElapsedTimeInMS(), AWAIT_TIMEOUT))
+            .until(() ->
+                txRunner.applyWithNewReadOnlyTransactionAndClose(datastoreType, tx -> tx.read(iid)).get().isPresent());
+    }
+
+    <D extends Datastore> void awaitForDataDelete(Class<D> datastoreType,
+                                                  InstanceIdentifier<? extends DataObject> iid) {
+        awaiter.with()
+            .conditionEvaluationListener(condition -> LOG.info("{} ({} ms of {} s)",
+                condition.getDescription(), condition.getElapsedTimeInMS(), AWAIT_TIMEOUT))
+            .until(() ->
+                !txRunner.applyWithNewReadOnlyTransactionAndClose(datastoreType, tx -> tx.read(iid)).get().isPresent());
+    }
+}