From 54faeb360678987d873282bec5378f2650de29e4 Mon Sep 17 00:00:00 2001 From: Tibor Král Date: Thu, 31 Jan 2019 14:33:42 +0100 Subject: HC2VPP-382: Validation support for DHCP module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I4a5323179d62cfdf12f72e30036a26ab985487ab Signed-off-by: Tibor Král --- .../fd/hc2vpp/dhcp/write/DhcpRelayCustomizer.java | 7 -- .../fd/hc2vpp/dhcp/write/DhcpRelayValidator.java | 112 +++++++++++++++++++++ .../io/fd/hc2vpp/dhcp/write/DhcpWriterFactory.java | 2 +- .../hc2vpp/dhcp/write/DhcpRelayCustomizerTest.java | 6 +- .../hc2vpp/dhcp/write/DhcpRelayValidatorTest.java | 88 ++++++++++++++++ 5 files changed, 204 insertions(+), 11 deletions(-) create mode 100644 dhcp/dhcp-impl/src/main/java/io/fd/hc2vpp/dhcp/write/DhcpRelayValidator.java create mode 100644 dhcp/dhcp-impl/src/test/java/io/fd/hc2vpp/dhcp/write/DhcpRelayValidatorTest.java diff --git a/dhcp/dhcp-impl/src/main/java/io/fd/hc2vpp/dhcp/write/DhcpRelayCustomizer.java b/dhcp/dhcp-impl/src/main/java/io/fd/hc2vpp/dhcp/write/DhcpRelayCustomizer.java index 4c316b573..12a4dc0c7 100644 --- a/dhcp/dhcp-impl/src/main/java/io/fd/hc2vpp/dhcp/write/DhcpRelayCustomizer.java +++ b/dhcp/dhcp-impl/src/main/java/io/fd/hc2vpp/dhcp/write/DhcpRelayCustomizer.java @@ -51,7 +51,6 @@ final class DhcpRelayCustomizer extends FutureJVppCustomizer implements ListWrit public void writeCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final Relay dataAfter, @Nonnull final WriteContext writeContext) throws WriteFailedException { LOG.debug("Writing Relay {} dataAfter={}", id, dataAfter); - checkArgument(dataAfter.getServer() != null && !dataAfter.getServer().isEmpty(), "At least one DHCP server needs to be configured"); for (final Server server : dataAfter.getServer()) { setRelay(id, dataAfter, server, true); } @@ -63,11 +62,7 @@ final class DhcpRelayCustomizer extends FutureJVppCustomizer implements ListWrit throws WriteFailedException { LOG.debug("Updating Relay {} before={} after={}", id, dataBefore, dataAfter); final List serversBefore = dataBefore.getServer(); - checkArgument(serversBefore != null && !serversBefore.isEmpty(), - "At least one DHCP server needs to be configured before update operation"); final List serversAfter = dataAfter.getServer(); - checkArgument(serversAfter != null && !serversAfter.isEmpty(), - "At least one DHCP server needs to be configured after update operation"); // remove old servers (we do not expect many, so no need for efficient search): for (final Server server : serversBefore) { @@ -87,8 +82,6 @@ final class DhcpRelayCustomizer extends FutureJVppCustomizer implements ListWrit public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final Relay dataBefore, @Nonnull final WriteContext writeContext) throws WriteFailedException { LOG.debug("Removing Relay {} dataBefore={}", id, dataBefore); - checkArgument(dataBefore.getServer() != null && !dataBefore.getServer().isEmpty(), - "At least one DHCP server needs to be configured"); for (final Server server : dataBefore.getServer()) { setRelay(id, dataBefore, server, false); } diff --git a/dhcp/dhcp-impl/src/main/java/io/fd/hc2vpp/dhcp/write/DhcpRelayValidator.java b/dhcp/dhcp-impl/src/main/java/io/fd/hc2vpp/dhcp/write/DhcpRelayValidator.java new file mode 100644 index 000000000..b9f093455 --- /dev/null +++ b/dhcp/dhcp-impl/src/main/java/io/fd/hc2vpp/dhcp/write/DhcpRelayValidator.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2019 PANTHEON.tech. + * + * 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.dhcp.write; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.annotations.VisibleForTesting; +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 javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.http.fd.io.hc2vpp.yang.dhcp.rev180629.dhcp.attributes.relays.Relay; +import org.opendaylight.yang.gen.v1.http.fd.io.hc2vpp.yang.dhcp.rev180629.relay.attributes.Server; +import org.opendaylight.yang.gen.v1.http.fd.io.hc2vpp.yang.vpp.fib.table.management.rev180521.Ipv6; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressNoZone; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class DhcpRelayValidator implements Validator { + + @Override + public void validateWrite(@Nonnull final InstanceIdentifier id, + @Nonnull final Relay relay, + @Nonnull final WriteContext writeContext) + throws CreateValidationFailedException { + try { + validateRelay(relay); + } catch (RuntimeException e) { + throw new CreateValidationFailedException(id, relay, e); + } + } + + @Override + public void validateUpdate(@Nonnull final InstanceIdentifier id, + @Nonnull final Relay dataBefore, + @Nonnull final Relay dataAfter, + @Nonnull final WriteContext writeContext) + throws UpdateValidationFailedException { + try { + validateRelay(dataBefore); + validateRelay(dataAfter); + } catch (RuntimeException e) { + throw new UpdateValidationFailedException(id, dataBefore, dataAfter, e); + } + } + + @Override + public void validateDelete(@Nonnull final InstanceIdentifier id, @Nonnull final Relay dataBefore, + @Nonnull final WriteContext writeContext) + throws DeleteValidationFailedException { + try { + validateRelay(dataBefore); + } catch (RuntimeException e) { + throw new DeleteValidationFailedException(id, e); + } + } + + @VisibleForTesting + void validateRelay(final Relay relay) { + final boolean isIpv6 = Ipv6.class == relay.getAddressFamily(); + try { + isAddressCorrect(relay.getGatewayAddress(), isIpv6); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException(String.format("Gateway address validation error: %s", e.getMessage())); + } + + checkArgument(relay.getServer() != null && !relay.getServer().isEmpty(), + "At least one DHCP server needs to be configured"); + for (final Server server : relay.getServer()) { + validateServer(server, isIpv6); + } + } + + private void validateServer(final Server server, boolean isIpv6) { + try { + isAddressCorrect(server.getAddress(), isIpv6); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException( + String.format("Server address %s validation error: %s", server.getAddress().stringValue(), + e.getMessage())); + } + } + + private void isAddressCorrect(final IpAddressNoZone address, final boolean isIpv6) { + if (isIpv6) { + checkArgument(address.getIpv6AddressNoZone() != null, + "Ipv6 address was expected but was not found."); + checkArgument(address.getIpv4AddressNoZone() == null, + "Only Ipv6 address was expected but Ipv4 was found"); + } else { + checkArgument(address.getIpv4AddressNoZone() != null, + "Ipv4 address was expected but was not found."); + checkArgument(address.getIpv6AddressNoZone() == null, + "Only Ipv4 address was expected but Ipv6 was found"); + } + } +} diff --git a/dhcp/dhcp-impl/src/main/java/io/fd/hc2vpp/dhcp/write/DhcpWriterFactory.java b/dhcp/dhcp-impl/src/main/java/io/fd/hc2vpp/dhcp/write/DhcpWriterFactory.java index a09f8a278..53a9b05b3 100644 --- a/dhcp/dhcp-impl/src/main/java/io/fd/hc2vpp/dhcp/write/DhcpWriterFactory.java +++ b/dhcp/dhcp-impl/src/main/java/io/fd/hc2vpp/dhcp/write/DhcpWriterFactory.java @@ -44,7 +44,7 @@ public final class DhcpWriterFactory implements WriterFactory { public void init(@Nonnull final ModifiableWriterRegistryBuilder registry) { registry.subtreeAdd( ImmutableSet.of(InstanceIdentifier.create(Relay.class).child(Server.class)), - new GenericListWriter<>(RELAY_ID, new DhcpRelayCustomizer(vppApi)) + new GenericListWriter<>(RELAY_ID, new DhcpRelayCustomizer(vppApi), new DhcpRelayValidator()) ); } } diff --git a/dhcp/dhcp-impl/src/test/java/io/fd/hc2vpp/dhcp/write/DhcpRelayCustomizerTest.java b/dhcp/dhcp-impl/src/test/java/io/fd/hc2vpp/dhcp/write/DhcpRelayCustomizerTest.java index aed31140d..02a45b970 100644 --- a/dhcp/dhcp-impl/src/test/java/io/fd/hc2vpp/dhcp/write/DhcpRelayCustomizerTest.java +++ b/dhcp/dhcp-impl/src/test/java/io/fd/hc2vpp/dhcp/write/DhcpRelayCustomizerTest.java @@ -42,8 +42,8 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; @RunWith(HoneycombTestRunner.class) public class DhcpRelayCustomizerTest extends WriterCustomizerTest implements SchemaContextTestHelper { - private static final String RELAYS_PATH = "/dhcp:dhcp/dhcp:relays"; - private static final InstanceIdentifier RELAYS_IID = + static final String RELAYS_PATH = "/dhcp:dhcp/dhcp:relays"; + static final InstanceIdentifier RELAYS_IID = InstanceIdentifier.create(Dhcp.class).child(Relays.class); private DhcpRelayCustomizer customizer; @@ -107,7 +107,7 @@ public class DhcpRelayCustomizerTest extends WriterCustomizerTest implements Sch verify(api).dhcpProxyConfig(request); } - private InstanceIdentifier getId(final long rxVrfId, final Class addressType) { + static InstanceIdentifier getId(final long rxVrfId, final Class addressType) { return RELAYS_IID.child(Relay.class, new RelayKey(addressType, new VniReference(rxVrfId))); } } diff --git a/dhcp/dhcp-impl/src/test/java/io/fd/hc2vpp/dhcp/write/DhcpRelayValidatorTest.java b/dhcp/dhcp-impl/src/test/java/io/fd/hc2vpp/dhcp/write/DhcpRelayValidatorTest.java new file mode 100644 index 000000000..e64d3d0a5 --- /dev/null +++ b/dhcp/dhcp-impl/src/test/java/io/fd/hc2vpp/dhcp/write/DhcpRelayValidatorTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019 PANTHEON.tech. + * + * 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.dhcp.write; + +import static io.fd.hc2vpp.dhcp.write.DhcpRelayCustomizerTest.RELAYS_PATH; +import static io.fd.hc2vpp.dhcp.write.DhcpRelayCustomizerTest.getId; +import static org.mockito.MockitoAnnotations.initMocks; + +import io.fd.hc2vpp.dhcp.helpers.SchemaContextTestHelper; +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 org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.opendaylight.yang.gen.v1.http.fd.io.hc2vpp.yang.dhcp.rev180629.dhcp.attributes.Relays; +import org.opendaylight.yang.gen.v1.http.fd.io.hc2vpp.yang.dhcp.rev180629.dhcp.attributes.relays.Relay; +import org.opendaylight.yang.gen.v1.http.fd.io.hc2vpp.yang.dhcp.rev180629.dhcp.attributes.relays.RelayBuilder; +import org.opendaylight.yang.gen.v1.http.fd.io.hc2vpp.yang.vpp.fib.table.management.rev180521.Ipv4; +import org.opendaylight.yang.gen.v1.http.fd.io.hc2vpp.yang.vpp.fib.table.management.rev180521.Ipv6; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressNoZoneBuilder; + +@RunWith(HoneycombTestRunner.class) +public class DhcpRelayValidatorTest implements SchemaContextTestHelper { + + @Mock + private WriteContext writeContext; + private DhcpRelayValidator validator; + + @Before + public void setUp() { + initMocks(this); + validator = new DhcpRelayValidator(); + } + + @Test + public void testWrite(@InjectTestData(resourcePath = "/relay/ipv4DhcpRelay.json", id = RELAYS_PATH) Relays relays) + throws DataValidationFailedException.CreateValidationFailedException { + final int rxVrfId = 0; + validator.validateWrite(getId(rxVrfId, Ipv4.class), extractRelay(relays), writeContext); + } + + @Test + public void testUpdate( + @InjectTestData(resourcePath = "/relay/ipv6DhcpRelayBefore.json", id = RELAYS_PATH) Relays relaysBefore, + @InjectTestData(resourcePath = "/relay/ipv6DhcpRelayAfter.json", id = RELAYS_PATH) Relays relayAfter) + throws DataValidationFailedException.UpdateValidationFailedException { + final int rxVrfId = 1; + validator.validateUpdate(getId(rxVrfId, Ipv6.class), extractRelay(relaysBefore), extractRelay(relayAfter), + writeContext); + } + + @Test + public void testDelete(@InjectTestData(resourcePath = "/relay/ipv4DhcpRelay.json", id = RELAYS_PATH) Relays relays) + throws DataValidationFailedException.DeleteValidationFailedException { + final int rxVrfId = 0; + validator.validateDelete(getId(rxVrfId, Ipv4.class), extractRelay(relays), writeContext); + } + + @Test(expected = IllegalArgumentException.class) + public void testMixedIpAddressFamilies( + @InjectTestData(resourcePath = "/relay/ipv4DhcpRelay.json", id = RELAYS_PATH) Relays relays) { + RelayBuilder builder = new RelayBuilder(); + builder.fieldsFrom(extractRelay(relays)); + builder.setGatewayAddress(IpAddressNoZoneBuilder.getDefaultInstance("2001::10")); + validator.validateRelay(builder.build()); + } + + private Relay extractRelay(Relays relays) { + return relays.getRelay().get(0); + } +} -- cgit 1.2.3-korg