From 16d190d24cfb3286ffd941d690c656d7e5d73928 Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Thu, 14 Jun 2018 12:12:07 +0200 Subject: HC2VPP-343: enable validation for acl list 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 RPC, run ncclient tests with: ./edit_config.py acl/copy_config_unsupported-acl-type.xml -v Support for RPC requres: https://gerrit.fd.io/r/#/c/14040/ Change-Id: Iea591a76022e893f6aaf2a52637f45cadb284e4e Signed-off-by: Marek Gradzki --- .../io/fd/hc2vpp/acl/util/acl/AclValidator.java | 84 ---------- .../io/fd/hc2vpp/acl/write/VppAclCustomizer.java | 80 +-------- .../io/fd/hc2vpp/acl/write/VppAclValidator.java | 178 +++++++++++++++++++++ .../acl/write/factory/VppAclWriterFactory.java | 5 +- .../fd/hc2vpp/acl/write/AclReferenceCheckTest.java | 86 ---------- .../fd/hc2vpp/acl/write/VppAclCustomizerTest.java | 28 ---- .../fd/hc2vpp/acl/write/VppAclValidatorTest.java | 143 +++++++++++++++++ .../src/test/resources/acl/ipv4/ipv4-acl.json | 17 ++ .../resources/interface-acl/acl-references.json | 57 +++++++ .../test/resources/reference/acl-references.json | 57 ------- .../acl/copy_config_unsupported-acl-type.xml | 15 ++ examples/ncclient/acl/test_invalid_acl.sh | 18 +++ examples/ncclient/edit_config.py | 43 +++++ 13 files changed, 476 insertions(+), 335 deletions(-) delete mode 100644 acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/acl/AclValidator.java create mode 100644 acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/VppAclValidator.java delete mode 100644 acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/AclReferenceCheckTest.java create mode 100644 acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/VppAclValidatorTest.java create mode 100644 acl/acl-impl/src/test/resources/acl/ipv4/ipv4-acl.json create mode 100644 acl/acl-impl/src/test/resources/interface-acl/acl-references.json delete mode 100644 acl/acl-impl/src/test/resources/reference/acl-references.json create mode 100644 examples/ncclient/acl/copy_config_unsupported-acl-type.xml create mode 100755 examples/ncclient/acl/test_invalid_acl.sh create mode 100755 examples/ncclient/edit_config.py 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 index c73841596..000000000 --- a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/acl/AclValidator.java +++ /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> SUPPORTED_ACL_TYPES = ImmutableSet.of(VppAcl.class, VppMacipAcl.class); - - Map, Class> 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 aces, final String aclName, final Class aceType) { - final Set 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); - } -} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/VppAclCustomizer.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/VppAclCustomizer.java index 7da38bc94..00cd8a56c 100644 --- a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/VppAclCustomizer.java +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/VppAclCustomizer.java @@ -16,45 +16,22 @@ 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, AclValidator, AclDataExtractor, AclWriter { + implements ListWriterCustomizer, 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 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 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 id, @Nonnull final Acl dataBefore, @Nonnull final WriteContext writeContext) throws WriteFailedException { - validateAcl(dataBefore); - - final List 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 checkAclReferenced(@Nonnull final WriteContext writeContext, - @Nonnull final Acl acl) { - final Optional readAfter = writeContext.readAfter(InstanceIdentifier.create(Interfaces.class)); - if (!readAfter.isPresent() || readAfter.get().getInterface() == null) { - return Collections.emptyList(); - } - - final List interfaces = readAfter.get().getInterface(); - final Class 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 index 000000000..e1f4c8dc0 --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/VppAclValidator.java @@ -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, AclDataExtractor { + + private static final Set> SUPPORTED_ACL_TYPES = + ImmutableSet.of(VppAcl.class, VppMacipAcl.class); + private static final Map, Class> ACL_ACE_PAIRS = + ImmutableMap.of(VppAcl.class, VppAce.class, VppMacipAcl.class, VppMacipAce.class); + + @Override + public void validateWrite(final InstanceIdentifier 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 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 id, final Acl dataBefore, final WriteContext ctx) + throws DeleteValidationFailedException { + try { + validateAcl(dataBefore); + final List 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 = 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 aces, final String aclName, + final Class aceType) { + final Set 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 checkAclReferenced(@Nonnull final WriteContext writeContext, + @Nonnull final Acl acl) { + final Optional readAfter = writeContext.readAfter(InstanceIdentifier.create(Interfaces.class)); + if (!readAfter.isPresent() || readAfter.get().getInterface() == null) { + return Collections.emptyList(); + } + + final List interfaces = readAfter.get().getInterface(); + final Class 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)); + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/factory/VppAclWriterFactory.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/factory/VppAclWriterFactory.java index 2b95f0b60..883cf4f1f 100644 --- a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/factory/VppAclWriterFactory.java +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/factory/VppAclWriterFactory.java @@ -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)); } } diff --git a/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/AclReferenceCheckTest.java b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/AclReferenceCheckTest.java deleted file mode 100644 index 8d89845f6..000000000 --- a/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/AclReferenceCheckTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2017 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 io.fd.hc2vpp.acl.write.VppAclCustomizer.AclReferenceCheck.checkAclReferenced; -import static java.util.stream.Collectors.toSet; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.hasSize; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - -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.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.access.lists.AclBuilder; -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.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 { - - @InjectTestData(id = "/ietf-interfaces:interfaces", resourcePath = "/reference/acl-references.json") - private Interfaces interfaces; - - @Mock - private WriteContext writeContext; - - @Before - public void init(){ - initMocks(this); - when(writeContext.readAfter(InstanceIdentifier.create(Interfaces.class))).thenReturn(Optional.of(interfaces)); - } - - @Test - public void testReferencedVppAclFirst() { - final List referenced = checkAclReferenced(writeContext, new AclBuilder() - .setAclName("acl1").setAclType(VppAcl.class).build()); - assertThat(referenced, hasSize(3)); - assertThat(referenced.stream().map(Interface::getName).collect(toSet()), - containsInAnyOrder("eth0", "eth1", "eth2")); - } - - @Test - public void testReferencedVppAclSecond() { - final List referenced = checkAclReferenced(writeContext, new AclBuilder() - .setAclName("acl2").setAclType(VppAcl.class).build()); - assertThat(referenced, hasSize(1)); - assertThat(referenced.stream().map(Interface::getName).collect(toSet()), - containsInAnyOrder("eth1")); - } - - @Test - public void testReferencedMacipAcl() { - final List referenced = checkAclReferenced(writeContext, new AclBuilder() - .setAclName("acl4").setAclType(VppMacipAcl.class).build()); - assertThat(referenced, hasSize(1)); - assertThat(referenced.stream().map(Interface::getName).collect(toSet()), - containsInAnyOrder("eth2")); - } -} \ No newline at end of file diff --git a/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/VppAclCustomizerTest.java b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/VppAclCustomizerTest.java index 63633aa75..a3fa6db0e 100644 --- a/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/VppAclCustomizerTest.java +++ b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/VppAclCustomizerTest.java @@ -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); diff --git a/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/VppAclValidatorTest.java b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/VppAclValidatorTest.java new file mode 100644 index 000000000..287e53f80 --- /dev/null +++ b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/VppAclValidatorTest.java @@ -0,0 +1,143 @@ +/* + * 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 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; +import static org.hamcrest.Matchers.hasSize; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +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 VppAclValidatorTest implements AclTestSchemaContext { + + private static final InstanceIdentifier ID = InstanceIdentifier.create(AccessLists.class) + .child(Acl.class, new AclKey("standard-acl", VppAcl.class)); + + @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 + public void testReferencedVppAclFirst() { + final List referenced = checkAclReferenced(writeContext, new AclBuilder() + .setAclName("acl1").setAclType(VppAcl.class).build()); + assertThat(referenced, hasSize(3)); + assertThat(referenced.stream().map(Interface::getName).collect(toSet()), + containsInAnyOrder("eth0", "eth1", "eth2")); + } + + @Test + public void testReferencedVppAclSecond() { + final List referenced = checkAclReferenced(writeContext, new AclBuilder() + .setAclName("acl2").setAclType(VppAcl.class).build()); + assertThat(referenced, hasSize(1)); + assertThat(referenced.stream().map(Interface::getName).collect(toSet()), + containsInAnyOrder("eth1")); + } + + @Test + public void testReferencedMacipAcl() { + final List referenced = checkAclReferenced(writeContext, new AclBuilder() + .setAclName("acl4").setAclType(VppMacipAcl.class).build()); + assertThat(referenced, hasSize(1)); + assertThat(referenced.stream().map(Interface::getName).collect(toSet()), + containsInAnyOrder("eth2")); + } +} \ No newline at end of file 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 index 000000000..04a08ff44 --- /dev/null +++ b/acl/acl-impl/src/test/resources/acl/ipv4/ipv4-acl.json @@ -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/acl/acl-impl/src/test/resources/interface-acl/acl-references.json b/acl/acl-impl/src/test/resources/interface-acl/acl-references.json new file mode 100644 index 000000000..63c9e20c9 --- /dev/null +++ b/acl/acl-impl/src/test/resources/interface-acl/acl-references.json @@ -0,0 +1,57 @@ +{ + "interfaces": { + "interface": [ + { + "name": "eth0", + "acl": { + "ingress": { + "vpp-acls": [ + { + "type": "vpp-acl:vpp-acl", + "name": "acl1" + } + ] + } + } + }, + { + "name": "eth1", + "acl": { + "egress": { + "vpp-acls": [ + { + "type": "vpp-acl:vpp-acl", + "name": "acl1" + } + ] + }, + "ingress": { + "vpp-acls": [ + { + "type": "vpp-acl:vpp-acl", + "name": "acl2" + } + ] + } + } + }, + { + "name": "eth2", + "acl": { + "ingress": { + "vpp-acls": [ + { + "type": "vpp-acl:vpp-acl", + "name": "acl1" + } + ], + "vpp-macip-acl": { + "name": "acl4", + "type": "vpp-acl:vpp-macip-acl" + } + } + } + } + ] + } +} \ No newline at end of file diff --git a/acl/acl-impl/src/test/resources/reference/acl-references.json b/acl/acl-impl/src/test/resources/reference/acl-references.json deleted file mode 100644 index 63c9e20c9..000000000 --- a/acl/acl-impl/src/test/resources/reference/acl-references.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "interfaces": { - "interface": [ - { - "name": "eth0", - "acl": { - "ingress": { - "vpp-acls": [ - { - "type": "vpp-acl:vpp-acl", - "name": "acl1" - } - ] - } - } - }, - { - "name": "eth1", - "acl": { - "egress": { - "vpp-acls": [ - { - "type": "vpp-acl:vpp-acl", - "name": "acl1" - } - ] - }, - "ingress": { - "vpp-acls": [ - { - "type": "vpp-acl:vpp-acl", - "name": "acl2" - } - ] - } - } - }, - { - "name": "eth2", - "acl": { - "ingress": { - "vpp-acls": [ - { - "type": "vpp-acl:vpp-acl", - "name": "acl1" - } - ], - "vpp-macip-acl": { - "name": "acl4", - "type": "vpp-acl:vpp-macip-acl" - } - } - } - } - ] - } -} \ 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 index 000000000..b86a11924 --- /dev/null +++ b/examples/ncclient/acl/copy_config_unsupported-acl-type.xml @@ -0,0 +1,15 @@ + + + + + acl0 + ipv4-acl + + + diff --git a/examples/ncclient/acl/test_invalid_acl.sh b/examples/ncclient/acl/test_invalid_acl.sh new file mode 100755 index 000000000..aedb842af --- /dev/null +++ b/examples/ncclient/acl/test_invalid_acl.sh @@ -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 index 000000000..cafb7d475 --- /dev/null +++ b/examples/ncclient/edit_config.py @@ -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 RPC") + argparser.add_argument('config_filename', help="name of XML file with element") + argparser.add_argument('-v', '--validate', help="sends RPC is 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) -- cgit 1.2.3-korg