HC2VPP-379: move NAT validation code out of customizers 30/14430/2
authorMarek Gradzki <[email protected]>
Thu, 23 Aug 2018 08:33:47 +0000 (10:33 +0200)
committerMarek Gradzki <[email protected]>
Thu, 23 Aug 2018 10:01:58 +0000 (12:01 +0200)
Use Validator interface introduced by HONEYCOMB-431:
https://gerrit.fd.io/r/#/c/14022/

Change-Id: I9e4c8d59f299ed7da4a93bbdc70c81f2bea93606
Signed-off-by: Marek Gradzki <[email protected]>
14 files changed:
nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/ExternalIpPoolCustomizer.java
nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/ExternalIpPoolValidator.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/MappingEntryCustomizer.java
nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/MappingEntryValidator.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/Nat64PrefixesCustomizer.java
nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/Nat64PrefixesValidator.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatInstaceCustomizer.java
nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatInstanceValidator.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatWriterFactory.java
nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/PolicyCustomizer.java
nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/PolicyValidator.java [new file with mode: 0644]
nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/MappingEntryCustomizerTest.java
nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/MappingEntryValidatorTest.java [new file with mode: 0644]
nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/PolicyValidatorTest.java [moved from nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/PolicyCustomizerTest.java with 69% similarity]

index d7403c0..0e60481 100644 (file)
@@ -54,8 +54,6 @@ final class ExternalIpPoolCustomizer implements ListWriterCustomizer<ExternalIpA
     public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<ExternalIpAddressPool> id,
                                        @Nonnull final ExternalIpAddressPool dataAfter,
                                        @Nonnull final WriteContext writeContext) throws WriteFailedException {
-        checkArgument(id.firstKeyOf(Instance.class).getId() == 0,
-                "External IP pools are only assignable for nat instance(vrf-id) with ID 0");
         LOG.trace("Adding address range:{}, as: {}", id, dataAfter);
         // TODO check overlaps ? VPP-478 maybe no necessary, depending on how VPP handles them
         configureAddressPool(id, dataAfter, true);
diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/ExternalIpPoolValidator.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/ExternalIpPoolValidator.java
new file mode 100644 (file)
index 0000000..567c8e3
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.nat.write;
+
+import io.fd.honeycomb.translate.write.DataValidationFailedException;
+import io.fd.honeycomb.translate.write.Validator;
+import io.fd.honeycomb.translate.write.WriteContext;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.Instance;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.policy.ExternalIpAddressPool;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+final class ExternalIpPoolValidator implements Validator<ExternalIpAddressPool> {
+    @Override
+    public void validateWrite(@Nonnull final InstanceIdentifier<ExternalIpAddressPool> id,
+                              @Nonnull final ExternalIpAddressPool dataAfter, @Nonnull final WriteContext writeContext)
+        throws DataValidationFailedException.CreateValidationFailedException {
+        if (id.firstKeyOf(Instance.class).getId() != 0) {
+            throw new DataValidationFailedException.CreateValidationFailedException(id, dataAfter,
+                new IllegalArgumentException(
+                    "External IP pools are only assignable for nat instance(vrf-id) with ID 0"));
+        }
+    }
+}
index 8d553b8..e51d610 100644 (file)
@@ -16,9 +16,6 @@
 
 package io.fd.hc2vpp.nat.write;
 
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-
 import com.google.common.base.Optional;
 import io.fd.hc2vpp.common.translate.util.ByteDataTranslator;
 import io.fd.hc2vpp.common.translate.util.Ipv4Translator;
@@ -61,10 +58,6 @@ final class MappingEntryCustomizer implements ListWriterCustomizer<MappingEntry,
                                        @Nonnull final MappingEntry dataAfter,
                                        @Nonnull final WriteContext writeContext)
             throws WriteFailedException {
-        // Only static mapping supported by SNAT for now
-        checkArgument(dataAfter.getType() ==
-                        org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.MappingEntry.Type.Static,
-                "Only static NAT entries are supported currently. Trying to write: %s entry", dataAfter.getType());
         final Long natInstanceId = id.firstKeyOf(Instance.class).getId();
         final Long mappingEntryId = id.firstKeyOf(MappingEntry.class).getIndex();
         LOG.debug("Writing mapping entry: {} for nat-instance(vrf): {}", natInstanceId, mappingEntryId);
@@ -87,13 +80,10 @@ final class MappingEntryCustomizer implements ListWriterCustomizer<MappingEntry,
                                   final boolean isAdd) throws WriteFailedException {
         final IpPrefix internalSrcPrefix = entry.getInternalSrcAddress();
         final Ipv4Prefix internalV4SrcPrefix = internalSrcPrefix.getIpv4Prefix();
-        final Ipv6Prefix internalV6SrcPrefix = internalSrcPrefix.getIpv6Prefix();
         if (internalV4SrcPrefix != null) {
             final Nat44AddDelStaticMapping request = getNat44Request(id, entry, natInstanceId, isAdd);
             getReplyForWrite(jvppNat.nat44AddDelStaticMapping(request).toCompletableFuture(), id);
         } else {
-            checkState(internalV6SrcPrefix != null,
-                    "internalSrcPrefix.getIpv4Prefix() should not return null if v4 prefix is not given");
             final Nat64AddDelStaticBib request = getNat64Request(id, entry, natInstanceId, isAdd);
             getReplyForWrite(jvppNat.nat64AddDelStaticBib(request).toCompletableFuture(), id);
         }
@@ -148,30 +138,22 @@ final class MappingEntryCustomizer implements ListWriterCustomizer<MappingEntry,
         request.vrfId = natInstanceId.intValue();
 
         final Ipv4Prefix internalAddress = mappingEntry.getInternalSrcAddress().getIpv4Prefix();
-        checkArgument(internalAddress != null, "No Ipv4 present in internal-src-address %s",
-            mappingEntry.getInternalSrcAddress());
-        checkArgument(extractPrefix(internalAddress) == 32,
-            "Only /32 prefix in internal-src-address is supported, but was %s", internalAddress);
-
         request.addrOnly = 1;
         request.localIpAddress = ipv4AddressPrefixToArray(internalAddress);
-        request.externalIpAddress = ipv4AddressPrefixToArray(getExternalAddress(mappingEntry));
+        request.externalIpAddress = ipv4AddressPrefixToArray(mappingEntry.getExternalSrcAddress().getIpv4Prefix());
         request.externalSwIfIndex = -1; // external ip address is ignored if externalSwIfIndex is given
         request.protocol = -1;
         final Short protocol = mappingEntry.getTransportProtocol();
         if (protocol != null) {
-            checkArgument(protocol == 1 || protocol == 6 || protocol == 17,
-                    "Unsupported protocol %s only ICMP(1), TCP(6) and UDP(17) are currently supported for Nat44",
-                    protocol);
             request.protocol = protocol.byteValue();
         }
 
-        final Short internalPortNumber = getPortNumber(id, mappingEntry.getInternalSrcPort());
-        final Short externalPortNumber = getPortNumber(id, mappingEntry.getExternalSrcPort());
+        final Integer internalPortNumber = getPortNumber(mappingEntry.getInternalSrcPort());
+        final Integer externalPortNumber = getPortNumber(mappingEntry.getExternalSrcPort());
         if (internalPortNumber != null && externalPortNumber != null) {
             request.addrOnly = 0;
-            request.localPort = internalPortNumber;
-            request.externalPort = externalPortNumber;
+            request.localPort = internalPortNumber.shortValue();
+            request.externalPort = externalPortNumber.shortValue();
         }
         return request;
     }
@@ -187,48 +169,26 @@ final class MappingEntryCustomizer implements ListWriterCustomizer<MappingEntry,
         request.vrfId = natInstanceId.intValue();
 
         final Ipv6Prefix internalAddress = mappingEntry.getInternalSrcAddress().getIpv6Prefix();
-        checkArgument(internalAddress != null, "No Ipv6 present in internal-src-address %s",
-                mappingEntry.getInternalSrcAddress());
-        checkArgument(extractPrefix(internalAddress) == (byte)128,
-            "Only /128 prefix in internal-src-address is supported, but was %s", internalAddress);
-
         request.iAddr = ipv6AddressPrefixToArray(internalAddress);
-        request.oAddr = ipv4AddressPrefixToArray(getExternalAddress(mappingEntry));
+        request.oAddr = ipv4AddressPrefixToArray(mappingEntry.getExternalSrcAddress().getIpv4Prefix());
         request.proto = -1;
         final Short protocol = mappingEntry.getTransportProtocol();
         if (protocol != null) {
-            checkArgument(protocol == 1 || protocol == 6 || protocol == 17 || protocol == 58,
-                    "Unsupported protocol %s only ICMP(1), IPv6-ICMP(58), TCP(6) and UDP(17) are currently supported for Nat64",
-                    protocol);
             request.proto = protocol.byteValue();
         }
 
-        final Short internalPortNumber = getPortNumber(id, mappingEntry.getInternalSrcPort());
-        final Short externalPortNumber = getPortNumber(id, mappingEntry.getExternalSrcPort());
+        final Integer internalPortNumber = getPortNumber(mappingEntry.getInternalSrcPort());
+        final Integer externalPortNumber = getPortNumber(mappingEntry.getExternalSrcPort());
         if (internalPortNumber != null && externalPortNumber != null) {
-            request.iPort = internalPortNumber;
-            request.oPort = externalPortNumber;
+            request.iPort = internalPortNumber.shortValue();
+            request.oPort = externalPortNumber.shortValue();
         }
         return request;
     }
 
-    private Ipv4Prefix getExternalAddress(final MappingEntry mappingEntry) {
-        final Ipv4Prefix externalAddress = mappingEntry.getExternalSrcAddress().getIpv4Prefix();
-        checkArgument(externalAddress != null, "No Ipv4 present in external-src-address %s",
-            mappingEntry.getExternalSrcAddress());
-        checkArgument(extractPrefix(externalAddress) == 32,
-            "Only /32 prefix in external-src-address is supported, but was %s", externalAddress);
-        return externalAddress;
-    }
-
-    private Short getPortNumber(final InstanceIdentifier<MappingEntry> id, final PortNumber portNumber) {
+    private Integer getPortNumber(final PortNumber portNumber) {
         if (portNumber != null) {
-            if (portNumber.getStartPortNumber() != null && portNumber.getEndPortNumber() == null) {
-                return portNumber.getStartPortNumber().getValue().shortValue();
-            } else {
-                throw new IllegalArgumentException(
-                    String.format("Only single port number supported. Submitted: %s for entry: %s", portNumber, id));
-            }
+            return portNumber.getStartPortNumber().getValue();
         }
         return null;
     }
diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/MappingEntryValidator.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/MappingEntryValidator.java
new file mode 100644 (file)
index 0000000..d059403
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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.nat.write;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.annotations.VisibleForTesting;
+import io.fd.hc2vpp.common.translate.util.Ipv4Translator;
+import io.fd.hc2vpp.common.translate.util.Ipv6Translator;
+import io.fd.honeycomb.translate.write.DataValidationFailedException.CreateValidationFailedException;
+import io.fd.honeycomb.translate.write.Validator;
+import io.fd.honeycomb.translate.write.WriteContext;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
+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.Ipv6Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.mapping.table.MappingEntry;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+final class MappingEntryValidator implements Validator<MappingEntry>, Ipv4Translator, Ipv6Translator {
+    @Override
+    public void validateWrite(@Nonnull final InstanceIdentifier<MappingEntry> id,
+                              @Nonnull final MappingEntry mappingEntry,
+                              @Nonnull final WriteContext writeContext)
+        throws CreateValidationFailedException {
+        try {
+            validateMappingEntry(id, mappingEntry);
+        } catch (RuntimeException e) {
+            throw new CreateValidationFailedException(id, mappingEntry, e);
+        }
+    }
+
+    private void validateMappingEntry(final InstanceIdentifier<MappingEntry> id, final MappingEntry mappingEntry) {
+        validateMappingEntryType(mappingEntry);
+        validateInternalSrcAddress(mappingEntry);
+        validateExternalSrcAddress(mappingEntry);
+        validateTransportProtocol(mappingEntry);
+        validatePortNumber(id, mappingEntry.getInternalSrcPort());
+        validatePortNumber(id, mappingEntry.getExternalSrcPort());
+    }
+
+    @VisibleForTesting
+    void validateMappingEntryType(final MappingEntry mappingEntry) {
+        // Only static mapping are currently supported
+        checkArgument(mappingEntry.getType()
+                == org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.MappingEntry.Type.Static,
+            "Only static NAT entries are supported currently. Trying to write: %s entry", mappingEntry.getType());
+    }
+
+    @VisibleForTesting
+    void validateInternalSrcAddress(final MappingEntry mappingEntry) {
+        final IpPrefix internalSrcPrefix = mappingEntry.getInternalSrcAddress();
+        final Ipv4Prefix internalV4SrcPrefix = internalSrcPrefix.getIpv4Prefix();
+        final Ipv6Prefix internalV6SrcPrefix = internalSrcPrefix.getIpv6Prefix();
+        if (internalV4SrcPrefix != null) {
+            checkArgument(extractPrefix(internalV4SrcPrefix) == 32,
+                "Only /32 prefix in internal-src-address is supported, but was %s", internalV4SrcPrefix);
+        } else {
+            checkState(internalV6SrcPrefix != null,
+                "internalSrcPrefix.getIpv6Prefix() should not return null if Ipv4 prefix is not given");
+            checkArgument(extractPrefix(internalV6SrcPrefix) == (byte) 128,
+                "Only /128 prefix in internal-src-address is supported, but was %s", internalV6SrcPrefix);
+        }
+    }
+
+    @VisibleForTesting
+    void validateExternalSrcAddress(final MappingEntry mappingEntry) {
+        final IpPrefix externalSrcAddress = mappingEntry.getExternalSrcAddress();
+        checkArgument(externalSrcAddress != null, "The external-src-address leaf is missing");
+        final Ipv4Prefix ipv4Prefix = externalSrcAddress.getIpv4Prefix();
+        checkArgument(ipv4Prefix != null, "No Ipv4 present in external-src-address %s", externalSrcAddress);
+        checkArgument(extractPrefix(ipv4Prefix) == 32,
+            "Only /32 prefix in external-src-address is supported, but was %s", ipv4Prefix);
+    }
+
+    @VisibleForTesting
+    static void validateTransportProtocol(final MappingEntry mappingEntry) {
+        final Short protocol = mappingEntry.getTransportProtocol();
+        if (protocol == null) {
+            return;
+        }
+        checkArgument(protocol == 1 || protocol == 6 || protocol == 17 || protocol == 58,
+            "Unsupported protocol %s only ICMP(1), IPv6-ICMP(58), TCP(6) and UDP(17) are currently supported",
+            protocol);
+    }
+
+    @VisibleForTesting
+    static void validatePortNumber(final InstanceIdentifier<MappingEntry> id, final PortNumber portNumber) {
+        if (portNumber == null) {
+            return;
+        }
+        checkArgument(portNumber.getStartPortNumber() != null && portNumber.getEndPortNumber() == null,
+            "Only single port number supported. Submitted: %s for entry: %s", portNumber, id);
+    }
+}
index 8f1869f..13a5845 100644 (file)
@@ -56,12 +56,6 @@ final class Nat64PrefixesCustomizer
                                        @Nonnull final WriteContext writeContext) throws WriteFailedException {
         final int natInstanceId = id.firstKeyOf(Instance.class).getId().intValue();
         LOG.debug("Configuring nat64 prefix: {} for nat-instance(vrf): {}", dataAfter, natInstanceId);
-
-        // VPP does not support configuring different nat64-prefixes depending on ipv4 destination prefix:
-        final List<DestinationIpv4Prefix> destinationIpv4PrefixList = dataAfter.getDestinationIpv4Prefix();
-        checkArgument(destinationIpv4PrefixList == null || destinationIpv4PrefixList.isEmpty(),
-                "destination-ipv4-prefix is not supported by VPP");
-
         addDelPrefix(id, dataAfter, natInstanceId, true);
         LOG.debug("Nat64 prefix written successfully: {} for nat-instance(vrf): {}", dataAfter, natInstanceId);
     }
@@ -83,10 +77,7 @@ final class Nat64PrefixesCustomizer
                               final int vrfId, final boolean isAdd)
             throws WriteFailedException {
 
-        // The nat64-prefix is optional in ietf-nat, but we require it
         final Ipv6Prefix nat64Prefix = data.getNat64Prefix();
-        checkArgument(nat64Prefix != null, "Missing nat64-prefix leaf value.");
-
         final Nat64AddDelPrefix request = new Nat64AddDelPrefix();
         request.prefix = ipv6AddressPrefixToArray(nat64Prefix);
         request.prefixLen = extractPrefix(nat64Prefix);
diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/Nat64PrefixesValidator.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/Nat64PrefixesValidator.java
new file mode 100644 (file)
index 0000000..3b6dc95
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.nat.write;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import io.fd.honeycomb.translate.write.DataValidationFailedException;
+import io.fd.honeycomb.translate.write.Validator;
+import io.fd.honeycomb.translate.write.WriteContext;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.policy.Nat64Prefixes;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.policy.nat64.prefixes.DestinationIpv4Prefix;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+final class Nat64PrefixesValidator implements Validator<Nat64Prefixes> {
+    @Override
+    public void validateWrite(@Nonnull final InstanceIdentifier<Nat64Prefixes> id,
+                              @Nonnull final Nat64Prefixes dataAfter,
+                              @Nonnull final WriteContext writeContext)
+        throws DataValidationFailedException.CreateValidationFailedException {
+        // VPP does not support configuring different nat64-prefixes depending on ipv4 destination prefix:
+        final List<DestinationIpv4Prefix> destinationIpv4PrefixList = dataAfter.getDestinationIpv4Prefix();
+        checkArgument(destinationIpv4PrefixList == null || destinationIpv4PrefixList.isEmpty(),
+            "destination-ipv4-prefix is not supported by VPP");
+    }
+}
index 90caa7c..945a5fa 100644 (file)
@@ -42,12 +42,5 @@ final class NatInstaceCustomizer implements ListWriterCustomizer<Instance, Insta
                                         @Nonnull final Instance dataBefore, @Nonnull final WriteContext writeContext)
             throws WriteFailedException {
         LOG.trace("Deleting NAT instance: {}", id);
-
-        // For consistency with reader, forbid removing default NAT instance:
-        final Long vrfId = id.firstKeyOf(Instance.class).getId();
-        if (vrfId == 0) {
-            throw new WriteFailedException.DeleteFailedException(id,
-                new UnsupportedOperationException("Removing default NAT instance (vrf=0) is not supported."));
-        }
     }
 }
diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatInstanceValidator.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatInstanceValidator.java
new file mode 100644 (file)
index 0000000..1f39755
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.nat.write;
+
+import io.fd.honeycomb.translate.write.DataValidationFailedException;
+import io.fd.honeycomb.translate.write.Validator;
+import io.fd.honeycomb.translate.write.WriteContext;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.Instance;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+final class NatInstanceValidator implements Validator<Instance> {
+    @Override
+    public void validateDelete(@Nonnull final InstanceIdentifier<Instance> id, @Nonnull final Instance dataBefore,
+                               @Nonnull final WriteContext writeContext)
+        throws DataValidationFailedException.DeleteValidationFailedException {
+        // For consistency with reader, forbid removing default NAT instance:
+        final Long vrfId = id.firstKeyOf(Instance.class).getId();
+        if (vrfId == 0) {
+            throw new DataValidationFailedException.DeleteValidationFailedException(id,
+                new UnsupportedOperationException("Removing default NAT instance (vrf=0) is not supported."));
+        }
+    }
+}
index 2671489..5b0478f 100644 (file)
@@ -58,14 +58,16 @@ public final class NatWriterFactory implements WriterFactory {
     public void init(@Nonnull final ModifiableWriterRegistryBuilder registry) {
         // +-- nat
         //    +-- instances/instance
-        registry.add(new GenericListWriter<>(NAT_INSTANCE_ID, new NatInstaceCustomizer()));
+        registry.add(new GenericListWriter<>(NAT_INSTANCE_ID, new NatInstaceCustomizer(), new NatInstanceValidator()));
         //       +-- mapping-table/mapping-entry
         registry.subtreeAdd(Sets.newHashSet(InstanceIdentifier.create(MappingEntry.class).child(ExternalSrcPort.class),
-                InstanceIdentifier.create(MappingEntry.class).child(InternalSrcPort.class)),
-                new GenericListWriter<>(MAPPING_ENTRY_ID, new MappingEntryCustomizer(jvppNat, mappingEntryContext)));
+            InstanceIdentifier.create(MappingEntry.class).child(InternalSrcPort.class)),
+            new GenericListWriter<>(MAPPING_ENTRY_ID,
+                new MappingEntryCustomizer(jvppNat, mappingEntryContext),
+                new MappingEntryValidator()));
 
         //       +-- policy
-        registry.add(new GenericListWriter<>(POLICY_ID, new PolicyCustomizer()));
+        registry.add(new GenericListWriter<>(POLICY_ID, new PolicyCustomizer(), new PolicyValidator()));
 
         //          +-- external-ip-address-pool
         registry.subtreeAddBefore(
@@ -73,11 +75,16 @@ public final class NatWriterFactory implements WriterFactory {
             // requires to already have an IP range predefined ... in some cases
             Sets.newHashSet(InstanceIdentifier.create(ExternalIpAddressPool.class)
                 .augmentation(ExternalIpAddressPoolAugmentation.class)),
-            new GenericListWriter<>(ADDRESS_POOL_ID, new ExternalIpPoolCustomizer(jvppNat)), MAPPING_ENTRY_ID);
+            new GenericListWriter<>(ADDRESS_POOL_ID,
+                new ExternalIpPoolCustomizer(jvppNat),
+                new ExternalIpPoolValidator()),
+            MAPPING_ENTRY_ID);
 
         //          +-- nat64-prefixes
         registry.subtreeAdd(
-                Sets.newHashSet(InstanceIdentifier.create(Nat64Prefixes.class).child(DestinationIpv4Prefix.class)),
-                new GenericListWriter<>(NAT64_PREFIXES_ID, new Nat64PrefixesCustomizer(jvppNat)));
+            Sets.newHashSet(InstanceIdentifier.create(Nat64Prefixes.class).child(DestinationIpv4Prefix.class)),
+            new GenericListWriter<>(NAT64_PREFIXES_ID,
+                new Nat64PrefixesCustomizer(jvppNat),
+                new Nat64PrefixesValidator()));
     }
 }
index 09e404a..782fa3b 100644 (file)
@@ -38,17 +38,6 @@ final class PolicyCustomizer implements ListWriterCustomizer<Policy, PolicyKey>
                                        @Nonnull final Policy dataAfter, @Nonnull final WriteContext writeContext)
             throws WriteFailedException {
         LOG.trace("Writing NAT policy: {}", id);
-
-        // HC supports only single NAT policy per NAT instance (VRF)
-        // To ensure that (and for simplicity), we require policy id = 0.
-        final Long policyId = id.firstKeyOf(Policy.class).getId();
-        checkArgument(policyId == 0,
-            "Only single policy per NAT instance (VRF) is supported (expected id=0, but %s given)", policyId);
-
-        if (dataAfter.getNat64Prefixes() != null) {
-            final int prefixCount = dataAfter.getNat64Prefixes().size();
-            checkArgument(prefixCount <= 1, "Only single nat64-prefix is supported, but %s given", prefixCount);
-        }
     }
 
     @Override
diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/PolicyValidator.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/PolicyValidator.java
new file mode 100644 (file)
index 0000000..8b13a08
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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.nat.write;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import io.fd.honeycomb.translate.write.DataValidationFailedException;
+import io.fd.honeycomb.translate.write.Validator;
+import io.fd.honeycomb.translate.write.WriteContext;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.Policy;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+final class PolicyValidator implements Validator<Policy> {
+    @Override
+    public void validateWrite(@Nonnull final InstanceIdentifier<Policy> id, @Nonnull final Policy dataAfter,
+                              @Nonnull final WriteContext writeContext)
+        throws DataValidationFailedException.CreateValidationFailedException {
+        try {
+            validatePolicy(id, dataAfter);
+        } catch (RuntimeException e) {
+            throw new DataValidationFailedException.CreateValidationFailedException(id, dataAfter, e);
+        }
+    }
+
+    private void validatePolicy(final InstanceIdentifier<Policy> id, final Policy policy) {
+        // HC supports only single NAT policy per NAT instance (VRF)
+        // To ensure that (and for simplicity), we require policy id = 0.
+        final Long policyId = id.firstKeyOf(Policy.class).getId();
+        checkArgument(policyId == 0,
+            "Only single policy per NAT instance (VRF) is supported (expected id=0, but %s given)", policyId);
+
+        if (policy.getNat64Prefixes() != null) {
+            final int prefixCount = policy.getNat64Prefixes().size();
+            checkArgument(prefixCount <= 1, "Only single nat64-prefix is supported, but %s given", prefixCount);
+        }
+    }
+}
index 60cc643..ede39e4 100644 (file)
@@ -47,11 +47,11 @@ public class MappingEntryCustomizerTest extends WriterCustomizerTest implements
 
     private static final long NAT_INSTANCE_ID = 1;
     private static final long MAPPING_ID = 22;
-    private static final InstanceIdentifier<MappingEntry> IID = NAT_INSTANCES_ID
+    static final InstanceIdentifier<MappingEntry> IID = NAT_INSTANCES_ID
         .child(Instance.class, new InstanceKey(NAT_INSTANCE_ID))
         .child(MappingTable.class).child(MappingEntry.class, new MappingEntryKey(MAPPING_ID));
 
-    private static final String MAPPING_TABLE_PATH = "/ietf-nat:nat/ietf-nat:instances/"
+    static final String MAPPING_TABLE_PATH = "/ietf-nat:nat/ietf-nat:instances/"
         + "ietf-nat:instance[ietf-nat:id='" + NAT_INSTANCE_ID + "']/ietf-nat:mapping-table";
 
     @Mock
@@ -87,13 +87,6 @@ public class MappingEntryCustomizerTest extends WriterCustomizerTest implements
         verify(jvppNat).nat64AddDelStaticBib(expectedRequest);
     }
 
-    @Test(expected = IllegalArgumentException.class)
-    public void testWriteNat44UnsupportedProtocol(
-            @InjectTestData(resourcePath = "/nat44/static-mapping-unsupported-proto.json", id = MAPPING_TABLE_PATH) MappingTable data)
-            throws WriteFailedException {
-        customizer.writeCurrentAttributes(IID, extractMappingEntry(data), writeContext);
-    }
-
     @Test(expected = UnsupportedOperationException.class)
     public void testUpdateNat64(
             @InjectTestData(resourcePath = "/nat64/static-mapping.json", id = MAPPING_TABLE_PATH) MappingTable before,
@@ -123,7 +116,7 @@ public class MappingEntryCustomizerTest extends WriterCustomizerTest implements
         verify(jvppNat).nat64AddDelStaticBib(getExpectedNat64Request());
     }
 
-    private static MappingEntry extractMappingEntry(MappingTable data) {
+    static MappingEntry extractMappingEntry(MappingTable data) {
         // assumes single nat instance and single mapping entry
         return data.getMappingEntry().get(0);
     }
diff --git a/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/MappingEntryValidatorTest.java b/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/MappingEntryValidatorTest.java
new file mode 100644 (file)
index 0000000..1cb98cc
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.nat.write;
+
+import static io.fd.hc2vpp.nat.write.MappingEntryCustomizerTest.IID;
+import static io.fd.hc2vpp.nat.write.MappingEntryCustomizerTest.MAPPING_TABLE_PATH;
+import static io.fd.hc2vpp.nat.write.MappingEntryCustomizerTest.extractMappingEntry;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import io.fd.hc2vpp.nat.NatTestSchemaContext;
+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 io.fd.honeycomb.translate.write.WriteFailedException;
+import io.fd.vpp.jvpp.nat.dto.Nat44AddDelStaticMapping;
+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.inet.types.rev130715.IpPrefix;
+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.Ipv6Prefix;
+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.nat.rev180628.MappingEntry.Type;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.mapping.entry.InternalSrcPort;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.mapping.entry.InternalSrcPortBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.MappingTable;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.mapping.table.MappingEntry;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@RunWith(HoneycombTestRunner.class)
+public class MappingEntryValidatorTest implements NatTestSchemaContext {
+    @Mock
+    private WriteContext writeContext;
+    private MappingEntryValidator validator;
+
+    @Before
+    public void setUp() {
+        initMocks(this);
+        validator = new MappingEntryValidator();
+    }
+
+    @Test
+    public void testWriteNat44(
+        @InjectTestData(resourcePath = "/nat44/static-mapping.json", id = MAPPING_TABLE_PATH) MappingTable data)
+        throws WriteFailedException, DataValidationFailedException.CreateValidationFailedException {
+        validator.validateWrite(IID, extractMappingEntry(data), writeContext);
+    }
+
+    @Test(expected = DataValidationFailedException.CreateValidationFailedException.class)
+    public void testWriteNat44UnsupportedProtocol(
+        @InjectTestData(resourcePath = "/nat44/static-mapping-unsupported-proto.json", id = MAPPING_TABLE_PATH) MappingTable data)
+        throws WriteFailedException, DataValidationFailedException.CreateValidationFailedException {
+        validator.validateWrite(IID, extractMappingEntry(data), writeContext);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testUnsupportedMappingEntryType() {
+        final MappingEntry mappingEntry = mock(MappingEntry.class);
+        when(mappingEntry.getType()).thenReturn(Type.DynamicExplicit);
+        validator.validateMappingEntryType(mappingEntry);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidInternalIpv6SrcAddressPrefix() {
+        final MappingEntry mappingEntry = mock(MappingEntry.class);
+        final IpPrefix address = new IpPrefix(new Ipv6Prefix("1::1/127"));
+        when(mappingEntry.getInternalSrcAddress()).thenReturn(address);
+        validator.validateInternalSrcAddress(mappingEntry);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidInternalIpv4SrcAddressPrefix() {
+        final MappingEntry mappingEntry = mock(MappingEntry.class);
+        final IpPrefix address = new IpPrefix(new Ipv4Prefix("1.2.3.4/16"));
+        when(mappingEntry.getInternalSrcAddress()).thenReturn(address);
+        validator.validateInternalSrcAddress(mappingEntry);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidExternalSrcAddress() {
+        final MappingEntry mappingEntry = mock(MappingEntry.class);
+        final IpPrefix address = new IpPrefix(new Ipv4Prefix("1.2.3.4/16"));
+        when(mappingEntry.getExternalSrcAddress()).thenReturn(address);
+        validator.validateExternalSrcAddress(mappingEntry);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidPortNumber() {
+        InternalSrcPort port = new InternalSrcPortBuilder()
+            .setStartPortNumber(new PortNumber(10))
+            .setEndPortNumber(new PortNumber(20))
+            .build();
+        final InstanceIdentifier<MappingEntry> id = InstanceIdentifier.create(MappingEntry.class);
+        MappingEntryValidator.validatePortNumber(id, port);
+    }
+}
\ No newline at end of file
@@ -18,12 +18,15 @@ package io.fd.hc2vpp.nat.write;
 
 import static io.fd.hc2vpp.nat.NatIds.NAT_INSTANCES_ID;
 import static org.mockito.Mockito.mock;
+import static org.mockito.MockitoAnnotations.initMocks;
 
-import io.fd.hc2vpp.common.test.write.WriterCustomizerTest;
-import io.fd.honeycomb.translate.write.WriteFailedException;
+import io.fd.honeycomb.translate.write.DataValidationFailedException.CreateValidationFailedException;
+import io.fd.honeycomb.translate.write.WriteContext;
 import java.util.Arrays;
 import java.util.Collections;
+import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.Instance;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.InstanceKey;
@@ -34,7 +37,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev1806
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.policy.Nat64PrefixesBuilder;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
-public class PolicyCustomizerTest extends WriterCustomizerTest {
+public class PolicyValidatorTest {
 
     private static final long VRF_ID = 123;
     private static final InstanceIdentifier<Instance> NAT_INSTANCE_ID =
@@ -48,32 +51,35 @@ public class PolicyCustomizerTest extends WriterCustomizerTest {
     private static final Nat64Prefixes P2 =
         new Nat64PrefixesBuilder().setNat64Prefix(new Ipv6Prefix("2001:db8::2/32")).build();
 
-    private PolicyCustomizer customizer;
+    @Mock
+    private WriteContext writeContext;
+    private PolicyValidator validator;
 
-    @Override
-    protected void setUpTest() throws Exception {
-        customizer = new PolicyCustomizer();
+    @Before
+    public void setUp() throws Exception {
+        initMocks(this);
+        validator = new PolicyValidator();
     }
 
-    @Test(expected = IllegalArgumentException.class)
-    public void testInvalidPolicyId() throws WriteFailedException {
-        customizer.writeCurrentAttributes(INVALID_POLICY_ID, mock(Policy.class), writeContext);
+    @Test(expected = CreateValidationFailedException.class)
+    public void testInvalidPolicyId() throws CreateValidationFailedException {
+        validator.validateWrite(INVALID_POLICY_ID, mock(Policy.class), writeContext);
     }
 
     @Test
-    public void testNoNat64Prefixes() throws WriteFailedException {
-        customizer.writeCurrentAttributes(DEFAULT_POLICY_ID, mock(Policy.class), writeContext);
+    public void testNoNat64Prefixes() throws CreateValidationFailedException {
+        validator.validateWrite(DEFAULT_POLICY_ID, mock(Policy.class), writeContext);
     }
 
     @Test
-    public void testSingleNat64Prefix() throws WriteFailedException {
+    public void testSingleNat64Prefix() throws CreateValidationFailedException {
         final Policy policy = new PolicyBuilder().setNat64Prefixes(Collections.singletonList(P1)).build();
-        customizer.writeCurrentAttributes(DEFAULT_POLICY_ID, policy, writeContext);
+        validator.validateWrite(DEFAULT_POLICY_ID, policy, writeContext);
     }
 
-    @Test(expected = IllegalArgumentException.class)
-    public void testTwoNat64Prefixes() throws WriteFailedException {
+    @Test(expected = CreateValidationFailedException.class)
+    public void testTwoNat64Prefixes() throws CreateValidationFailedException {
         final Policy policy = new PolicyBuilder().setNat64Prefixes(Arrays.asList(P1, P2)).build();
-        customizer.writeCurrentAttributes(DEFAULT_POLICY_ID, policy, writeContext);
+        validator.validateWrite(DEFAULT_POLICY_ID, policy, writeContext);
     }
 }
\ No newline at end of file