HC2VPP-343: enable validation for acl list 90/14090/2
authorMarek Gradzki <[email protected]>
Thu, 14 Jun 2018 10:12:07 +0000 (12:12 +0200)
committerMarek Gradzki <[email protected]>
Fri, 17 Aug 2018 11:42:56 +0000 (11:42 +0000)
This patch moves all validation for acl list to VppAclValidator,
implementation of Validator interface brought by (HONEYCOMB-431):
https://gerrit.fd.io/r/#/c/14022/

To test <validate> RPC, run ncclient tests with:
./edit_config.py acl/copy_config_unsupported-acl-type.xml -v

Support for <validate> RPC requres:
https://gerrit.fd.io/r/#/c/14040/

Change-Id: Iea591a76022e893f6aaf2a52637f45cadb284e4e
Signed-off-by: Marek Gradzki <[email protected]>
acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/acl/AclValidator.java [deleted file]
acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/VppAclCustomizer.java
acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/VppAclValidator.java [new file with mode: 0644]
acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/factory/VppAclWriterFactory.java
acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/VppAclCustomizerTest.java
acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/VppAclValidatorTest.java [moved from acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/AclReferenceCheckTest.java with 50% similarity]
acl/acl-impl/src/test/resources/acl/ipv4/ipv4-acl.json [new file with mode: 0644]
acl/acl-impl/src/test/resources/interface-acl/acl-references.json [moved from acl/acl-impl/src/test/resources/reference/acl-references.json with 100% similarity]
examples/ncclient/acl/copy_config_unsupported-acl-type.xml [new file with mode: 0644]
examples/ncclient/acl/test_invalid_acl.sh [new file with mode: 0755]
examples/ncclient/edit_config.py [new file with mode: 0755]

diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/acl/AclValidator.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/acl/AclValidator.java
deleted file mode 100644 (file)
index c738415..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.hc2vpp.acl.util.acl;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-import javax.annotation.Nonnull;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AclBase;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.Matches;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.AceType;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.VppAcl;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.VppMacipAcl;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.access.lists.acl.access.list.entries.ace.matches.ace.type.VppAce;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.access.lists.acl.access.list.entries.ace.matches.ace.type.VppMacipAce;
-
-/**
- * Validate Acl data if processable by vpp
- */
-public interface AclValidator {
-
-    Set<Class<? extends AclBase>> SUPPORTED_ACL_TYPES = ImmutableSet.of(VppAcl.class, VppMacipAcl.class);
-
-    Map<Class<? extends AclBase>, Class<? extends AceType>> ACL_ACE_PAIRS = ImmutableMap.of(
-            VppAcl.class, VppAce.class,
-            VppMacipAcl.class, VppMacipAce.class);
-
-    static void isSupportedAclType(final Acl acl) {
-        checkArgument(SUPPORTED_ACL_TYPES.contains(acl.getAclType()),
-                "Unsupported Acl type %s detected for acl %s, allowed types are %s", acl.getAclType(),
-                acl.getAclName(), SUPPORTED_ACL_TYPES);
-    }
-
-    static void hasConsistentAceTypeForAclType(final Acl acl) {
-        checkTypesSame(acl.getAccessListEntries().getAce(), acl.getAclName(),
-                checkNotNull(ACL_ACE_PAIRS.get(acl.getAclType()), "Unsupported ACL type %s for ACL %s",
-                        acl.getAclType(), acl.getAclName()));
-    }
-
-    static void checkTypesSame(final List<Ace> aces, final String aclName, final Class<? extends AceType> aceType) {
-        final Set<AceType> unsupportedAceTypes = aces.stream()
-                .map(Ace::getMatches)
-                .map(Matches::getAceType)
-                .filter(aceType::equals)
-                .collect(Collectors.toSet());
-        checkArgument(unsupportedAceTypes.isEmpty(), "Detected unsupported ace types [%s] for ACL %s, expected %s",
-                unsupportedAceTypes, aclName, aceType);
-    }
-
-    static void hasAceList(final Acl acl) {
-        //checks if aces are defined
-        checkArgument(!checkNotNull(checkNotNull(acl.getAccessListEntries(), "No access list entries defined")
-                .getAce(), "No aces defined")
-                .isEmpty(), "Empty ace list defined");
-    }
-
-    default void validateAcl(@Nonnull final Acl acl) {
-        hasAceList(acl);
-        isSupportedAclType(acl);
-        hasConsistentAceTypeForAclType(acl);
-    }
-}
index 7da38bc..00cd8a5 100644 (file)
 
 package io.fd.hc2vpp.acl.write;
 
-import static com.google.common.base.Preconditions.checkState;
-import static io.fd.hc2vpp.acl.write.VppAclCustomizer.AclReferenceCheck.checkAclReferenced;
-import static java.lang.String.format;
-import static java.util.Collections.emptyList;
-import static java.util.Optional.ofNullable;
-
-import com.google.common.base.Optional;
 import io.fd.hc2vpp.acl.util.AclContextManager;
 import io.fd.hc2vpp.acl.util.FutureJVppAclCustomizer;
 import io.fd.hc2vpp.acl.util.acl.AclDataExtractor;
-import io.fd.hc2vpp.acl.util.acl.AclValidator;
 import io.fd.hc2vpp.acl.util.acl.AclWriter;
 import io.fd.honeycomb.translate.MappingContext;
 import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer;
 import io.fd.honeycomb.translate.write.WriteContext;
 import io.fd.honeycomb.translate.write.WriteFailedException;
 import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AclBase;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclKey;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.InterfaceAclAttributes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.VppAclInterfaceAugmentation;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.VppAclsBaseAttributes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.VppMacipAclsBaseAttributes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.acl.Egress;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.acl.Ingress;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.VppAcl;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.VppMacipAcl;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
 public class VppAclCustomizer extends FutureJVppAclCustomizer
-        implements ListWriterCustomizer<Acl, AclKey>, AclValidator, AclDataExtractor, AclWriter {
+        implements ListWriterCustomizer<Acl, AclKey>, AclDataExtractor, AclWriter {
 
     private final AclContextManager standardAclContext;
     private final AclContextManager macIpAclContext;
@@ -70,8 +47,6 @@ public class VppAclCustomizer extends FutureJVppAclCustomizer
     @Override
     public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataAfter,
                                        @Nonnull final WriteContext writeContext) throws WriteFailedException {
-        validateAcl(dataAfter);
-
         final MappingContext mappingContext = writeContext.getMappingContext();
 
         if (isStandardAcl(dataAfter)) {
@@ -89,8 +64,6 @@ public class VppAclCustomizer extends FutureJVppAclCustomizer
     public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataBefore,
                                         @Nonnull final Acl dataAfter, @Nonnull final WriteContext writeContext)
             throws WriteFailedException {
-        validateAcl(dataAfter);
-
         final MappingContext mappingContext = writeContext.getMappingContext();
 
         if (isStandardAcl(dataAfter)) {
@@ -113,14 +86,6 @@ public class VppAclCustomizer extends FutureJVppAclCustomizer
     @Override
     public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataBefore,
                                         @Nonnull final WriteContext writeContext) throws WriteFailedException {
-        validateAcl(dataBefore);
-
-        final List<Interface> references = checkAclReferenced(writeContext, dataBefore);
-        // references must be check, to not leave dead references in configuration
-        checkState(references.isEmpty(),
-                "%s cannot be removed, it is referenced in following interfaces %s", dataBefore,
-                references);
-
         final MappingContext mappingContext = writeContext.getMappingContext();
 
         if (isStandardAcl(dataBefore)) {
@@ -133,47 +98,4 @@ public class VppAclCustomizer extends FutureJVppAclCustomizer
                     new IllegalArgumentException("Unsupported acl option"));
         }
     }
-
-    static final class AclReferenceCheck {
-
-        static List<Interface> checkAclReferenced(@Nonnull final WriteContext writeContext,
-                                                  @Nonnull final Acl acl) {
-            final Optional<Interfaces> readAfter = writeContext.readAfter(InstanceIdentifier.create(Interfaces.class));
-            if (!readAfter.isPresent() || readAfter.get().getInterface() == null) {
-                return Collections.emptyList();
-            }
-
-            final List<Interface> interfaces = readAfter.get().getInterface();
-            final Class<? extends AclBase> aclType = acl.getAclType();
-            final String aclName = acl.getAclName();
-
-            if (aclType.equals(VppAcl.class)) {
-                return interfaces.stream()
-                        .filter(iface -> ofNullable(iface.getAugmentation(VppAclInterfaceAugmentation.class))
-                                .map(InterfaceAclAttributes::getAcl)
-                                .filter(references ->
-                                        checkVppAcls(references.getIngress(), aclName) ||
-                                                checkVppAcls(references.getEgress(), aclName)).isPresent()
-                        ).collect(Collectors.toList());
-            } else if (aclType.equals(VppMacipAcl.class)) {
-                return interfaces.stream()
-                        .filter(iface -> ofNullable(iface.getAugmentation(VppAclInterfaceAugmentation.class))
-                                .map(InterfaceAclAttributes::getAcl)
-                                .map(aclAttr -> aclAttr.getIngress())
-                                .map(VppMacipAclsBaseAttributes::getVppMacipAcl)
-                                .filter(vppMacipAcl -> vppMacipAcl.getName().equals(aclName))
-                                .isPresent())
-                        .collect(Collectors.toList());
-            } else {
-                throw new IllegalArgumentException(format("Acl type %s not supported", aclType));
-            }
-        }
-
-        static boolean checkVppAcls(@Nullable final VppAclsBaseAttributes attrs, @Nonnull final String name) {
-            return ofNullable(attrs).map(VppAclsBaseAttributes::getVppAcls)
-                    .orElse(emptyList())
-                    .stream().anyMatch(acl -> acl.getName().equals(name));
-
-        }
-    }
 }
diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/VppAclValidator.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/VppAclValidator.java
new file mode 100644 (file)
index 0000000..e1f4c8d
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.acl.write;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
+import static java.util.Collections.emptyList;
+import static java.util.Optional.ofNullable;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import io.fd.hc2vpp.acl.util.acl.AclDataExtractor;
+import io.fd.honeycomb.translate.write.DataValidationFailedException.CreateValidationFailedException;
+import io.fd.honeycomb.translate.write.DataValidationFailedException.DeleteValidationFailedException;
+import io.fd.honeycomb.translate.write.DataValidationFailedException.UpdateValidationFailedException;
+import io.fd.honeycomb.translate.write.Validator;
+import io.fd.honeycomb.translate.write.WriteContext;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AclBase;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.AccessListEntries;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.Matches;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.AceType;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.InterfaceAclAttributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.VppAclInterfaceAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.VppAclsBaseAttributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.VppMacipAclsBaseAttributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.VppAcl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.VppMacipAcl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.access.lists.acl.access.list.entries.ace.matches.ace.type.VppAce;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.access.lists.acl.access.list.entries.ace.matches.ace.type.VppMacipAce;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public final class VppAclValidator implements Validator<Acl>, AclDataExtractor {
+
+    private static final Set<Class<? extends AclBase>> SUPPORTED_ACL_TYPES =
+        ImmutableSet.of(VppAcl.class, VppMacipAcl.class);
+    private static final Map<Class<? extends AclBase>, Class<? extends AceType>> ACL_ACE_PAIRS =
+        ImmutableMap.of(VppAcl.class, VppAce.class, VppMacipAcl.class, VppMacipAce.class);
+
+    @Override
+    public void validateWrite(final InstanceIdentifier<Acl> id, final Acl dataAfter, final WriteContext ctx)
+        throws CreateValidationFailedException {
+        try {
+            validateAcl(dataAfter);
+        } catch (RuntimeException e) {
+            throw new CreateValidationFailedException(id, dataAfter, e);
+        }
+    }
+
+    @Override
+    public void validateUpdate(final InstanceIdentifier<Acl> id, final Acl dataBefore, final Acl dataAfter,
+                               final WriteContext ctx) throws UpdateValidationFailedException {
+        try {
+            validateAcl(dataAfter);
+        } catch (RuntimeException e) {
+            throw new UpdateValidationFailedException(id, dataBefore, dataAfter, e);
+        }
+    }
+
+    @Override
+    public void validateDelete(final InstanceIdentifier<Acl> id, final Acl dataBefore, final WriteContext ctx)
+        throws DeleteValidationFailedException {
+        try {
+            validateAcl(dataBefore);
+            final List<Interface> references = checkAclReferenced(ctx, dataBefore);
+            // references must be check, to not leave dead references in configuration
+            checkState(references.isEmpty(),
+                "%s cannot be removed, it is referenced in following interfaces %s", dataBefore, references);
+        } catch (RuntimeException e) {
+            throw new DeleteValidationFailedException(id, e);
+        }
+    }
+
+    private static void validateAcl(@Nonnull final Acl acl) {
+        hasAceList(acl);
+        isSupportedAclType(acl);
+        hasConsistentAceTypeForAclType(acl);
+    }
+
+    private static void hasAceList(final Acl acl) {
+        final AccessListEntries accessListEntries = acl.getAccessListEntries();
+        checkArgument(accessListEntries != null, "The access-list-entries container is not defined.");
+        final List<Ace> ace = accessListEntries.getAce();
+        checkArgument(ace != null, "The ace list is not defined.");
+        checkArgument(!ace.isEmpty(), "The ace list is empty.");
+    }
+
+    private static void isSupportedAclType(final Acl acl) {
+        checkArgument(SUPPORTED_ACL_TYPES.contains(acl.getAclType()),
+            "Unsupported Acl type %s detected for acl %s, allowed types are %s", acl.getAclType(),
+            acl.getAclName(), SUPPORTED_ACL_TYPES);
+    }
+
+    private static void hasConsistentAceTypeForAclType(final Acl acl) {
+        checkTypesSame(acl.getAccessListEntries().getAce(), acl.getAclName(),
+            checkNotNull(ACL_ACE_PAIRS.get(acl.getAclType()), "Unsupported ACL type %s for ACL %s",
+                acl.getAclType(), acl.getAclName()));
+    }
+
+    private static void checkTypesSame(final List<Ace> aces, final String aclName,
+                                       final Class<? extends AceType> aceType) {
+        final Set<AceType> unsupportedAceTypes = aces.stream()
+            .map(Ace::getMatches)
+            .map(Matches::getAceType)
+            .filter(aceType::equals)
+            .collect(Collectors.toSet());
+        checkArgument(unsupportedAceTypes.isEmpty(), "Detected unsupported ace types [%s] for ACL %s, expected %s",
+            unsupportedAceTypes, aclName, aceType);
+    }
+
+    @VisibleForTesting
+    static List<Interface> checkAclReferenced(@Nonnull final WriteContext writeContext,
+                                              @Nonnull final Acl acl) {
+        final Optional<Interfaces> readAfter = writeContext.readAfter(InstanceIdentifier.create(Interfaces.class));
+        if (!readAfter.isPresent() || readAfter.get().getInterface() == null) {
+            return Collections.emptyList();
+        }
+
+        final List<Interface> interfaces = readAfter.get().getInterface();
+        final Class<? extends AclBase> aclType = acl.getAclType();
+        final String aclName = acl.getAclName();
+
+        if (aclType.equals(VppAcl.class)) {
+            return interfaces.stream()
+                .filter(iface -> ofNullable(iface.getAugmentation(VppAclInterfaceAugmentation.class))
+                    .map(InterfaceAclAttributes::getAcl)
+                    .filter(references ->
+                        checkVppAcls(references.getIngress(), aclName) ||
+                            checkVppAcls(references.getEgress(), aclName)).isPresent()
+                ).collect(Collectors.toList());
+        } else if (aclType.equals(VppMacipAcl.class)) {
+            return interfaces.stream()
+                .filter(iface -> ofNullable(iface.getAugmentation(VppAclInterfaceAugmentation.class))
+                    .map(InterfaceAclAttributes::getAcl)
+                    .map(aclAttr -> aclAttr.getIngress())
+                    .map(VppMacipAclsBaseAttributes::getVppMacipAcl)
+                    .filter(vppMacipAcl -> vppMacipAcl.getName().equals(aclName))
+                    .isPresent())
+                .collect(Collectors.toList());
+        } else {
+            throw new IllegalArgumentException(format("Acl type %s not supported", aclType));
+        }
+    }
+
+    private static boolean checkVppAcls(@Nullable final VppAclsBaseAttributes attrs, @Nonnull final String name) {
+        return ofNullable(attrs).map(VppAclsBaseAttributes::getVppAcls)
+            .orElse(emptyList())
+            .stream().anyMatch(acl -> acl.getName().equals(name));
+    }
+}
index 2b95f0b..883cf4f 100644 (file)
@@ -21,6 +21,7 @@ import static io.fd.hc2vpp.acl.write.factory.InterfaceAclWriterFactory.aclHandle
 
 import io.fd.hc2vpp.acl.util.factory.AclFactory;
 import io.fd.hc2vpp.acl.write.VppAclCustomizer;
+import io.fd.hc2vpp.acl.write.VppAclValidator;
 import io.fd.honeycomb.translate.impl.write.GenericListWriter;
 import io.fd.honeycomb.translate.write.WriterFactory;
 import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder;
@@ -37,7 +38,9 @@ public class VppAclWriterFactory extends AbstractAclWriterFactory implements Wri
 
         registry.subtreeAddBefore(vppAclChildren(InstanceIdentifier.create(Acl.class)),
             new GenericListWriter<>(rootNode.child(Acl.class),
-                new VppAclCustomizer(futureAclFacade, standardAclContext, macIpAClContext)),
+                new VppAclCustomizer(futureAclFacade, standardAclContext, macIpAClContext),
+                new VppAclValidator()
+            ),
             aclHandledChildren(ACL_IID));
     }
 }
index 63633aa..a3fa6db 100644 (file)
@@ -18,9 +18,7 @@ package io.fd.hc2vpp.acl.write;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -43,7 +41,6 @@ import io.fd.vpp.jvpp.acl.types.AclRule;
 import io.fd.vpp.jvpp.acl.types.MacipAclRule;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
-import java.util.Collections;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -53,10 +50,6 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.cont
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclKey;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesBuilder;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.VppAclInterfaceAugmentation;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.VppAclInterfaceStateAugmentation;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.VppAcl;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
@@ -278,27 +271,6 @@ public class VppAclCustomizerTest extends WriterCustomizerTest implements AclTes
         assertEquals(aclIndex, aclDelRequestCaptor.getValue().aclIndex);
     }
 
-    @Test
-    public void deleteCurrentAttributesUdpReferenced(
-            @InjectTestData(resourcePath = "/acl/standard/standard-acl-udp.json")
-                    AccessLists standardAcls,
-            @InjectTestData(resourcePath = "/acl/standard/interface-ref-acl-udp.json")
-                    Interfaces references) throws Exception {
-        // TODO - HONEYCOMB-349 - change after resolving to specific node injection
-        when(writeContext.readAfter(InstanceIdentifier.create(Interfaces.class))).thenReturn(
-                Optional.of(new InterfacesBuilder().setInterface(references.getInterface()).build()));
-
-        final int aclIndex = 4;
-        when(standardAclContext.getAclIndex("standard-acl", mappingContext)).thenReturn(aclIndex);
-        try {
-            aclCustomizer.deleteCurrentAttributes(validId, standardAcls.getAcl().get(0), writeContext);
-        } catch (IllegalStateException e) {
-            verify(aclApi, never()).aclDel(any(AclDel.class));
-            return;
-        }
-        fail("IllegalStateException should have been thrown");
-    }
-
     private void verifyUdpRequest(final int aclIndex) {
         final AclAddReplace request = aclAddReplaceRequestCaptor.getValue();
         assertEquals(aclIndex, request.aclIndex);
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Copyright (c) 2018 Cisco and/or its affiliates.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
 
 package io.fd.hc2vpp.acl.write;
 
-import static io.fd.hc2vpp.acl.write.VppAclCustomizer.AclReferenceCheck.checkAclReferenced;
+import static io.fd.hc2vpp.acl.write.VppAclValidator.checkAclReferenced;
 import static java.util.stream.Collectors.toSet;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsInAnyOrder;
@@ -28,33 +28,90 @@ import com.google.common.base.Optional;
 import io.fd.hc2vpp.acl.AclTestSchemaContext;
 import io.fd.honeycomb.test.tools.HoneycombTestRunner;
 import io.fd.honeycomb.test.tools.annotations.InjectTestData;
+import io.fd.honeycomb.translate.write.DataValidationFailedException;
 import io.fd.honeycomb.translate.write.WriteContext;
 import java.util.List;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AccessLists;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclKey;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesBuilder;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.VppAcl;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.VppMacipAcl;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
-
 @RunWith(HoneycombTestRunner.class)
-public class AclReferenceCheckTest implements AclTestSchemaContext {
+public class VppAclValidatorTest implements AclTestSchemaContext {
+
+    private static final InstanceIdentifier<Acl> ID = InstanceIdentifier.create(AccessLists.class)
+        .child(Acl.class, new AclKey("standard-acl", VppAcl.class));
 
-    @InjectTestData(id = "/ietf-interfaces:interfaces", resourcePath = "/reference/acl-references.json")
+    @InjectTestData(id = "/ietf-interfaces:interfaces", resourcePath = "/interface-acl/acl-references.json")
     private Interfaces interfaces;
 
     @Mock
     private WriteContext writeContext;
 
+    private VppAclValidator validator;
+
     @Before
     public void init(){
         initMocks(this);
         when(writeContext.readAfter(InstanceIdentifier.create(Interfaces.class))).thenReturn(Optional.of(interfaces));
+        validator = new VppAclValidator();
+    }
+
+    @Test
+    public void testValidateWrite(
+        @InjectTestData(resourcePath = "/acl/standard/standard-acl-icmp.json") AccessLists acls)
+        throws DataValidationFailedException.CreateValidationFailedException {
+        validator.validateWrite(ID, acls.getAcl().get(0), writeContext);
+    }
+
+    @Test(expected = DataValidationFailedException.CreateValidationFailedException.class)
+    public void testValidateWriteEmptyAcl()
+        throws DataValidationFailedException.CreateValidationFailedException {
+        validator.validateWrite(ID, new AclBuilder().build(), writeContext);
+    }
+
+    @Test
+    public void testValidateUpdate(
+        @InjectTestData(resourcePath = "/acl/standard/standard-acl-icmp.json") AccessLists acls)
+        throws DataValidationFailedException.UpdateValidationFailedException {
+        final Acl data = acls.getAcl().get(0);
+        validator.validateUpdate(ID, data, data, writeContext);
+    }
+
+    @Test(expected = DataValidationFailedException.UpdateValidationFailedException.class)
+    public void testValidateUpdateUnsupportedType(
+        @InjectTestData(resourcePath = "/acl/ipv4/ipv4-acl.json") AccessLists acls)
+        throws DataValidationFailedException.UpdateValidationFailedException {
+        final Acl data = acls.getAcl().get(0);
+        validator.validateUpdate(ID, data, data, writeContext);
+    }
+
+    @Test
+    public void testValidateDelete(
+        @InjectTestData(resourcePath = "/acl/standard/standard-acl-icmp.json") AccessLists acls)
+        throws DataValidationFailedException.DeleteValidationFailedException {
+        validator.validateDelete(ID, acls.getAcl().get(0), writeContext);
+    }
+
+    @Test(expected = DataValidationFailedException.DeleteValidationFailedException.class)
+    public void testValidateDeleteReferenced(
+        @InjectTestData(resourcePath = "/acl/standard/standard-acl-udp.json")
+            AccessLists standardAcls,
+        @InjectTestData(resourcePath = "/acl/standard/interface-ref-acl-udp.json")
+            Interfaces references) throws Exception {
+        when(writeContext.readAfter(InstanceIdentifier.create(Interfaces.class))).thenReturn(
+            Optional.of(new InterfacesBuilder().setInterface(references.getInterface()).build()));
+        validator.validateDelete(ID, standardAcls.getAcl().get(0), writeContext);
     }
 
     @Test
diff --git a/acl/acl-impl/src/test/resources/acl/ipv4/ipv4-acl.json b/acl/acl-impl/src/test/resources/acl/ipv4/ipv4-acl.json
new file mode 100644 (file)
index 0000000..04a08ff
--- /dev/null
@@ -0,0 +1,17 @@
+{
+  "access-lists": {
+    "acl": [
+      {
+        "acl-name": "standard-acl",
+        "acl-type": "ipv4-acl",
+        "access-list-entries": {
+          "ace": [
+            {
+              "rule-name": "rule1"
+            }
+          ]
+        }
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/examples/ncclient/acl/copy_config_unsupported-acl-type.xml b/examples/ncclient/acl/copy_config_unsupported-acl-type.xml
new file mode 100644 (file)
index 0000000..b86a119
--- /dev/null
@@ -0,0 +1,15 @@
+<!--
+  ~ Copyright (c) 2018 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
+  -->
+<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <access-lists xmlns="urn:ietf:params:xml:ns:yang:ietf-access-control-list" xc:operation="create">
+        <acl>
+            <acl-name>acl0</acl-name>
+            <acl-type>ipv4-acl</acl-type>
+        </acl>
+    </access-lists>
+</config>
diff --git a/examples/ncclient/acl/test_invalid_acl.sh b/examples/ncclient/acl/test_invalid_acl.sh
new file mode 100755 (executable)
index 0000000..aedb842
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+DIR_NAME=$(dirname $0)
+
+${DIR_NAME}/../test_copy_config.sh ${DIR_NAME}/copy_config_acl.xml ${DIR_NAME}/expected_config_acl.xml
diff --git a/examples/ncclient/edit_config.py b/examples/ncclient/edit_config.py
new file mode 100755 (executable)
index 0000000..cafb7d4
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import logging
+from ncclient import manager
+
+
+def _edit_config(config_filename, host='localhost', port=2831, username='admin', password='admin',
+                 validate=False, commit=False):
+    with manager.connect(host=host, port=port, username=username, password=password, hostkey_verify=False) as m:
+        logger.info("Connected to HC")
+        with open(config_filename, 'r') as f:
+            ret = m.edit_config(config=f.read())
+            logger.debug("EditConfig successful:\n%s" % ret)
+            validate = m.validate()
+            logger.debug("Validate successful:\n%s" % validate)
+            commit = m.commit()
+            logger.debug("Commit successful:\n%s" % commit)
+
+if __name__ == '__main__':
+    logger = logging.getLogger("hc2vpp.examples.edit_config")
+    logging.basicConfig(level=logging.WARNING)
+    argparser = argparse.ArgumentParser(description="Configures VPP using <edit-config> RPC")
+    argparser.add_argument('config_filename', help="name of XML file with <config> element")
+    argparser.add_argument('-v', '--validate', help="sends <validate> RPC is <edit-config> was successful",
+                           action="store_true")
+    argparser.add_argument('-c', '--commit', help="commits candidate configuration",
+                           action="store_true")
+    args = argparser.parse_args()
+    _edit_config(args.config_filename, validate=args.validate, commit=args.commit)