diff options
author | Maros Marsalek <mmarsale@cisco.com> | 2016-11-10 13:31:25 +0100 |
---|---|---|
committer | Marek Gradzki <mgradzki@cisco.com> | 2016-11-19 21:29:39 +0100 |
commit | 175197da8ea43335df3daeba4c6296fcd83a057c (patch) | |
tree | 110298a990c6191933c2ad65d1a06333bab76cee /v3po/v3po2vpp/src/main/java/io/fd/hc2vpp | |
parent | 1813bf59fa53e8eb913d34b212d45b227dead799 (diff) |
Post split cleanup
- change groupIds
- change packages
- update poms
Change-Id: I343c5a292a67de1dd50687870ca4ab5b7276e93e
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
Diffstat (limited to 'v3po/v3po2vpp/src/main/java/io/fd/hc2vpp')
103 files changed, 12172 insertions, 0 deletions
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/AclWriterFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/AclWriterFactory.java new file mode 100644 index 000000000..dbac5f342 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/AclWriterFactory.java @@ -0,0 +1,58 @@ +/* + * 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.v3po; + +import static io.fd.hc2vpp.v3po.InterfacesWriterFactory.IETF_ACL_ID; +import static io.fd.hc2vpp.v3po.SubinterfaceAugmentationWriterFactory.SUBIF_IETF_ACL_ID; + +import com.google.common.collect.Sets; +import io.fd.honeycomb.translate.impl.write.GenericListWriter; +import io.fd.hc2vpp.v3po.interfaces.acl.IetfAclWriter; +import io.fd.honeycomb.translate.write.WriterFactory; +import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; +import javax.annotation.Nonnull; +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.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.Actions; +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.packet.fields.rev160708.acl.transport.header.fields.DestinationPortRange; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.acl.transport.header.fields.SourcePortRange; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public final class AclWriterFactory implements WriterFactory { + + public static final InstanceIdentifier<Acl> ACL_ID = + InstanceIdentifier.create(AccessLists.class).child(Acl.class); + + @Override + public void init(@Nonnull final ModifiableWriterRegistryBuilder registry) { + + final InstanceIdentifier<Acl> aclIdRelative = InstanceIdentifier.create(Acl.class); + + final InstanceIdentifier<Ace> aceId = aclIdRelative.child(AccessListEntries.class).child(Ace.class); + final InstanceIdentifier<Actions> actionsId = aceId.child(Actions.class); + final InstanceIdentifier<Matches> matchesId = aceId.child(Matches.class); + final InstanceIdentifier<SourcePortRange> srcPortId = matchesId.child((Class)SourcePortRange.class); + final InstanceIdentifier<DestinationPortRange> dstPortId = matchesId.child((Class)DestinationPortRange.class); + + registry.subtreeAddBefore(Sets.newHashSet(aceId, actionsId, matchesId, srcPortId, dstPortId), + new GenericListWriter<>(ACL_ID, new IetfAclWriter()), + Sets.newHashSet(IETF_ACL_ID, SUBIF_IETF_ACL_ID)); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/DisabledInterfacesManager.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/DisabledInterfacesManager.java new file mode 100644 index 000000000..97bab706d --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/DisabledInterfacesManager.java @@ -0,0 +1,106 @@ +/* + * 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.v3po; + +import com.google.common.base.Optional; +import com.google.inject.Inject; +import com.google.inject.name.Named; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import io.fd.honeycomb.translate.util.read.BindingBrokerReader; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.context.rev160909.DisabledInterfaces; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.context.rev160909.DisabledInterfacesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.context.rev160909.disabled.interfaces.DisabledInterfaceIndex; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.context.rev160909.disabled.interfaces.DisabledInterfaceIndexBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.context.rev160909.disabled.interfaces.DisabledInterfaceIndexKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; + +/** + * Facade on top of {@link MappingContext} making access to {@link DisabledInterfaces} easier. + */ +public class DisabledInterfacesManager { + + private static final InstanceIdentifier<DisabledInterfaces> + DISABLED_IFCS_ROOT = InstanceIdentifier.create(DisabledInterfaces.class); + + /** + * Read the list of currently disabled interfaces. + */ + public List<Integer> getDisabledInterfaces(@Nonnull final MappingContext ctx) { + final Optional<DisabledInterfaces> read = ctx.read(DISABLED_IFCS_ROOT); + if (read.isPresent()) { + return read.get().getDisabledInterfaceIndex().stream() + .map(DisabledInterfaceIndex::getIndex) + .collect(Collectors.toList()); + } else { + return Collections.emptyList(); + } + } + + /** + * Check whether a specific interface is disabled. + */ + public boolean isInterfaceDisabled(final int index, @Nonnull final MappingContext ctx) { + return ctx.read(getKeyedId(index)) + .isPresent(); + } + + /** + * Make a specific interface disabled. + */ + public void disableInterface(final int index, @Nonnull final MappingContext ctx) { + ctx.put(getKeyedId(index), getDisabledInterfaceIndex(index)); + } + + /** + * Remove interface disability. + */ + public void removeDisabledInterface(final int index, @Nonnull final MappingContext ctx) { + ctx.delete(getKeyedId(index)); + } + + private static DisabledInterfaceIndex getDisabledInterfaceIndex(final int index) { + return new DisabledInterfaceIndexBuilder().setIndex(index).build(); + } + + private static KeyedInstanceIdentifier<DisabledInterfaceIndex, DisabledInterfaceIndexKey> getKeyedId(final int id) { + return DISABLED_IFCS_ROOT.child(DisabledInterfaceIndex.class, new DisabledInterfaceIndexKey(id)); + } + + public static final class ContextsReaderFactory implements ReaderFactory { + + @Inject + @Named("honeycomb-context") + private DataBroker contextBindingBrokerDependency; + + @Override + public void init(final ModifiableReaderRegistryBuilder registry) { + registry.add(new BindingBrokerReader<>(DISABLED_IFCS_ROOT, + contextBindingBrokerDependency, + LogicalDatastoreType.OPERATIONAL, DisabledInterfacesBuilder.class)); + } + } + +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/EgressIetfAClWriterProvider.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/EgressIetfAClWriterProvider.java new file mode 100644 index 000000000..0e4aaa80d --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/EgressIetfAClWriterProvider.java @@ -0,0 +1,39 @@ +/* + * 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.v3po; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import io.fd.hc2vpp.v3po.interfaces.acl.egress.EgressIetfAclWriter; +import io.fd.hc2vpp.v3po.interfaces.acl.common.AclTableContextManagerImpl; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.MappingTable; + +class EgressIetfAClWriterProvider implements Provider<EgressIetfAclWriter> { + + private final FutureJVppCore jvpp; + + @Inject + public EgressIetfAClWriterProvider(final FutureJVppCore jvpp) { + this.jvpp = jvpp; + } + + @Override + public EgressIetfAclWriter get() { + return new EgressIetfAclWriter(jvpp, new AclTableContextManagerImpl(MappingTable.Direction.Egress)); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/IngressIetfAClWriterProvider.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/IngressIetfAClWriterProvider.java new file mode 100644 index 000000000..12349a356 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/IngressIetfAClWriterProvider.java @@ -0,0 +1,39 @@ +/* + * 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.v3po; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import io.fd.hc2vpp.v3po.interfaces.acl.common.AclTableContextManagerImpl; +import io.fd.hc2vpp.v3po.interfaces.acl.ingress.IngressIetfAclWriter; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.MappingTable; + +class IngressIetfAClWriterProvider implements Provider<IngressIetfAclWriter> { + + private final FutureJVppCore jvpp; + + @Inject + public IngressIetfAClWriterProvider(final FutureJVppCore jvpp) { + this.jvpp = jvpp; + } + + @Override + public IngressIetfAclWriter get() { + return new IngressIetfAclWriter(jvpp, new AclTableContextManagerImpl(MappingTable.Direction.Ingress)); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/InterfacesStateReaderFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/InterfacesStateReaderFactory.java new file mode 100644 index 000000000..f01cd9e99 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/InterfacesStateReaderFactory.java @@ -0,0 +1,187 @@ +/* + * 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.v3po; + +import com.google.common.collect.Sets; +import com.google.inject.Inject; +import com.google.inject.name.Named; +import io.fd.hc2vpp.v3po.interfacesstate.ip.Ipv4AddressCustomizer; +import io.fd.honeycomb.translate.impl.read.GenericInitListReader; +import io.fd.honeycomb.translate.impl.read.GenericInitReader; +import io.fd.honeycomb.translate.impl.read.GenericListReader; +import io.fd.honeycomb.translate.impl.read.GenericReader; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import io.fd.hc2vpp.v3po.interfacesstate.EthernetCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.GreCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.InterfaceCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.L2Customizer; +import io.fd.hc2vpp.v3po.interfacesstate.ProxyArpCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.TapCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.VhostUserCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.VxlanCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.VxlanGpeCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.acl.ingress.AclCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.ip.Ipv4Customizer; +import io.fd.hc2vpp.v3po.interfacesstate.ip.Ipv4NeighbourCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.ip.Ipv6Customizer; +import io.fd.hc2vpp.v3po.interfacesstate.pbb.PbbRewriteStateCustomizer; +import io.fd.hc2vpp.v3po.vppclassifier.VppClassifierContextManager; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesStateBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface2; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface2Builder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.Ipv4; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.Ipv6; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.Neighbor; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceStateAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceStateAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.AclBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.Ethernet; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.Gre; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.L2; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.ProxyArp; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.Tap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.VhostUser; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.Vxlan; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.VxlanGpe; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.acl.Ingress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.Ip4Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.Ip6Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.L2Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.pbb.rev161214.PbbRewriteStateInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.pbb.rev161214.PbbRewriteStateInterfaceAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.pbb.rev161214.interfaces.state._interface.PbbRewriteState; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public final class InterfacesStateReaderFactory implements ReaderFactory { + + private final NamingContext ifcNamingCtx; + private final NamingContext bdNamingCtx; + private final VppClassifierContextManager classifyContext; + private final DisabledInterfacesManager ifcDisableContext; + private final FutureJVppCore jvpp; + + static final InstanceIdentifier<InterfacesState> IFC_STATE_ID = + InstanceIdentifier.create(InterfacesState.class); + static final InstanceIdentifier<Interface> IFC_ID = IFC_STATE_ID.child(Interface.class); + + @Inject + public InterfacesStateReaderFactory(final FutureJVppCore jvpp, + @Named("interface-context") final NamingContext ifcNamingCtx, + @Named("bridge-domain-context") final NamingContext bdNamingCtx, + @Named("classify-table-context") final VppClassifierContextManager classifyContext, + final DisabledInterfacesManager ifcDisableContext) { + this.jvpp = jvpp; + this.ifcNamingCtx = ifcNamingCtx; + this.bdNamingCtx = bdNamingCtx; + this.classifyContext = classifyContext; + this.ifcDisableContext = ifcDisableContext; + } + + @Override + public void init(final ModifiableReaderRegistryBuilder registry) { + // InterfacesState(Structural) + registry.addStructuralReader(IFC_STATE_ID, InterfacesStateBuilder.class); + // Interface + registry.add(new GenericInitListReader<>(IFC_ID, new InterfaceCustomizer(jvpp, ifcNamingCtx, ifcDisableContext))); + + // v3po.yang + initVppIfcAugmentationReaders(registry, IFC_ID); + // ietf-ip.yang + initInterface2AugmentationReaders(registry, IFC_ID); + // vpp-vlan.yang + new SubinterfaceStateAugmentationReaderFactory(jvpp, ifcNamingCtx, bdNamingCtx, classifyContext).init(registry); + //vpp-pbb.yang + initPbbRewriteAugmentation(registry, IFC_ID); + } + + private void initInterface2AugmentationReaders(final ModifiableReaderRegistryBuilder registry, + final InstanceIdentifier<Interface> ifcId) { + // Interface2Augmentation(Structural) + final InstanceIdentifier<Interface2> ifc2AugId = ifcId.augmentation(Interface2.class); + registry.addStructuralReader(ifc2AugId, Interface2Builder.class); + // Ipv4 + final InstanceIdentifier<Ipv4> ipv4Id = ifc2AugId.child(Ipv4.class); + registry.add(new GenericReader<>(ipv4Id, new Ipv4Customizer(jvpp))); + // Address + final InstanceIdentifier<Address> ipv4AddrId = ipv4Id.child(Address.class); + registry.add(new GenericInitListReader<>(ipv4AddrId, new Ipv4AddressCustomizer(jvpp, ifcNamingCtx))); + // Neighbor + final InstanceIdentifier<Neighbor> neighborId = ipv4Id.child(Neighbor.class); + registry.add(new GenericListReader<>(neighborId, new Ipv4NeighbourCustomizer(jvpp))); + // Ipv6 + final InstanceIdentifier<Ipv6> ipv6Id = ifc2AugId.child(Ipv6.class); + registry.add(new GenericReader<>(ipv6Id, new Ipv6Customizer(jvpp, ifcNamingCtx))); + } + + private void initVppIfcAugmentationReaders(final ModifiableReaderRegistryBuilder registry, + final InstanceIdentifier<Interface> ifcId) { + // VppInterfaceStateAugmentation + final InstanceIdentifier<VppInterfaceStateAugmentation> vppIfcAugId = + ifcId.augmentation(VppInterfaceStateAugmentation.class); + registry.addStructuralReader(vppIfcAugId, VppInterfaceStateAugmentationBuilder.class); + // Ethernet + registry.add(new GenericInitReader<>(vppIfcAugId.child(Ethernet.class), + new EthernetCustomizer(jvpp, ifcNamingCtx))); + // Tap + registry.add(new GenericInitReader<>(vppIfcAugId.child(Tap.class), new TapCustomizer(jvpp, ifcNamingCtx))); + // VhostUser + registry.add(new GenericInitReader<>(vppIfcAugId.child(VhostUser.class), + new VhostUserCustomizer(jvpp, ifcNamingCtx))); + // Vxlan + registry.add(new GenericInitReader<>(vppIfcAugId.child(Vxlan.class), new VxlanCustomizer(jvpp, ifcNamingCtx))); + // VxlanGpe + registry.add(new GenericInitReader<>(vppIfcAugId.child(VxlanGpe.class), + new VxlanGpeCustomizer(jvpp, ifcNamingCtx))); + // Gre + registry.add(new GenericInitReader<>(vppIfcAugId.child(Gre.class), new GreCustomizer(jvpp, ifcNamingCtx))); + // L2 + registry.add(new GenericInitReader<>(vppIfcAugId.child(L2.class), + new L2Customizer(jvpp, ifcNamingCtx, bdNamingCtx))); + + // Acl(Structural) + final InstanceIdentifier<Acl> aclIid = vppIfcAugId.child(Acl.class); + registry.addStructuralReader(aclIid, AclBuilder.class); + // Ingress(Subtree) + final InstanceIdentifier<Ingress> ingressIdRelative = InstanceIdentifier.create(Ingress.class); + registry.subtreeAdd( + Sets.newHashSet(ingressIdRelative.child(L2Acl.class), ingressIdRelative.child(Ip4Acl.class), + ingressIdRelative.child(Ip6Acl.class)), + new GenericInitReader<>(aclIid.child(Ingress.class), + new AclCustomizer(jvpp, ifcNamingCtx, classifyContext))); + + // Proxy ARP + registry.add(new GenericReader<>(vppIfcAugId.child(ProxyArp.class), new ProxyArpCustomizer(jvpp, + ifcNamingCtx))); + } + + private void initPbbRewriteAugmentation(final ModifiableReaderRegistryBuilder registry, + final InstanceIdentifier<Interface> ifcId) { + registry.addStructuralReader(ifcId.augmentation(PbbRewriteStateInterfaceAugmentation.class), + PbbRewriteStateInterfaceAugmentationBuilder.class); + + registry.add(new GenericReader<>(ifcId.augmentation(PbbRewriteStateInterfaceAugmentation.class).child( + PbbRewriteState.class), new PbbRewriteStateCustomizer(jvpp))); + } + +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/InterfacesWriterFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/InterfacesWriterFactory.java new file mode 100644 index 000000000..eb68b6fbf --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/InterfacesWriterFactory.java @@ -0,0 +1,244 @@ +/* + * 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.v3po; + +import static io.fd.hc2vpp.v3po.VppClassifierHoneycombWriterFactory.CLASSIFY_SESSION_ID; +import static io.fd.hc2vpp.v3po.VppClassifierHoneycombWriterFactory.CLASSIFY_TABLE_ID; + +import com.google.common.collect.Sets; +import com.google.inject.Inject; +import com.google.inject.name.Named; +import io.fd.hc2vpp.v3po.interfaces.EthernetCustomizer; +import io.fd.hc2vpp.v3po.interfaces.GreCustomizer; +import io.fd.hc2vpp.v3po.interfaces.L2Customizer; +import io.fd.hc2vpp.v3po.interfaces.LoopbackCustomizer; +import io.fd.hc2vpp.v3po.interfaces.TapCustomizer; +import io.fd.hc2vpp.v3po.interfaces.VxlanCustomizer; +import io.fd.hc2vpp.v3po.interfaces.VxlanGpeCustomizer; +import io.fd.hc2vpp.v3po.interfaces.acl.egress.EgressIetfAclWriter; +import io.fd.hc2vpp.v3po.interfaces.acl.ingress.AclCustomizer; +import io.fd.hc2vpp.v3po.interfaces.ip.Ipv4NeighbourCustomizer; +import io.fd.hc2vpp.v3po.vppclassifier.VppClassifierContextManager; +import io.fd.honeycomb.translate.impl.write.GenericListWriter; +import io.fd.honeycomb.translate.impl.write.GenericWriter; +import io.fd.hc2vpp.v3po.interfaces.InterfaceCustomizer; +import io.fd.hc2vpp.v3po.interfaces.ProxyArpCustomizer; +import io.fd.hc2vpp.v3po.interfaces.RoutingCustomizer; +import io.fd.hc2vpp.v3po.interfaces.VhostUserCustomizer; +import io.fd.hc2vpp.v3po.interfaces.acl.ingress.IngressIetfAclWriter; +import io.fd.hc2vpp.v3po.interfaces.ip.Ipv4AddressCustomizer; +import io.fd.hc2vpp.v3po.interfaces.ip.Ipv4Customizer; +import io.fd.hc2vpp.v3po.interfaces.ip.Ipv6Customizer; +import io.fd.hc2vpp.v3po.interfaces.pbb.PbbRewriteCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriterFactory; +import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.Set; +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.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface1; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv6; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Neighbor; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Ethernet; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Gre; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.IetfAcl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.L2; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Loopback; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.ProxyArp; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Routing; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Tap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.VhostUser; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Vxlan; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.VxlanGpe; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.acl.Ingress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.Ip4Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.Ip6Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.L2Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.pbb.rev161214.PbbRewriteInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.pbb.rev161214.interfaces._interface.PbbRewrite; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public final class InterfacesWriterFactory implements WriterFactory { + + public static final InstanceIdentifier<Interface> IFC_ID = + InstanceIdentifier.create(Interfaces.class).child(Interface.class); + public static final InstanceIdentifier<VppInterfaceAugmentation> VPP_IFC_AUG_ID = + IFC_ID.augmentation(VppInterfaceAugmentation.class); + public static final InstanceIdentifier<Acl> ACL_ID = VPP_IFC_AUG_ID.child(Acl.class); + public static final InstanceIdentifier<Ingress> INGRESS_ACL_ID = ACL_ID.child(Ingress.class); + public static final InstanceIdentifier<L2> L2_ID = VPP_IFC_AUG_ID.child(L2.class); + public static final InstanceIdentifier<IetfAcl> IETF_ACL_ID = VPP_IFC_AUG_ID.child(IetfAcl.class); + public static final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.ietf.acl.Ingress> + INGRESS_IETF_ACL_ID = IETF_ACL_ID.child( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.ietf.acl.Ingress.class); + public static final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.ietf.acl.Egress> + EGRESS_IETF_ACL_ID = IETF_ACL_ID.child( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.ietf.acl.Egress.class); + + private final FutureJVppCore jvpp; + private final IngressIetfAclWriter ingressAclWriter; + private final EgressIetfAclWriter egressAclWriter; + private final NamingContext bdNamingContext; + private final NamingContext ifcNamingContext; + private final VppClassifierContextManager classifyTableContext; + private final DisabledInterfacesManager ifcDisableContext; + + @Inject + public InterfacesWriterFactory(final FutureJVppCore vppJvppIfcDependency, + final IngressIetfAclWriter ingressAclWriter, + final EgressIetfAclWriter egressAclWriter, + @Named("bridge-domain-context") final NamingContext bridgeDomainContextDependency, + @Named("interface-context") final NamingContext interfaceContextDependency, + @Named("classify-table-context") final VppClassifierContextManager classifyTableContext, + final DisabledInterfacesManager ifcDisableContext) { + this.jvpp = vppJvppIfcDependency; + this.ingressAclWriter = ingressAclWriter; + this.egressAclWriter = egressAclWriter; + this.bdNamingContext = bridgeDomainContextDependency; + this.ifcNamingContext = interfaceContextDependency; + this.ifcDisableContext = ifcDisableContext; + this.classifyTableContext = classifyTableContext; + } + + @Override + public void init(final ModifiableWriterRegistryBuilder registry) { + // Interfaces + // Interface = + registry.add(new GenericListWriter<>(IFC_ID, new InterfaceCustomizer(jvpp, ifcNamingContext))); + // VppInterfaceAugmentation + addVppInterfaceAgmentationWriters(IFC_ID, registry); + // Interface1 (ietf-ip augmentation) + addInterface1AugmentationWriters(IFC_ID, registry); + // SubinterfaceAugmentation + new SubinterfaceAugmentationWriterFactory(jvpp, ingressAclWriter, egressAclWriter, ifcNamingContext, bdNamingContext, + classifyTableContext).init(registry); + + addPbbAugmentationWriters(IFC_ID, registry); + } + + private void addInterface1AugmentationWriters(final InstanceIdentifier<Interface> ifcId, + final ModifiableWriterRegistryBuilder registry) { + final InstanceIdentifier<Interface1> ifc1AugId = ifcId.augmentation(Interface1.class); + // Ipv6(after interface) = + registry.addAfter(new GenericWriter<>(ifc1AugId.child(Ipv6.class), new Ipv6Customizer(jvpp)), + ifcId); + // Ipv4(after interface) + final InstanceIdentifier<Ipv4> ipv4Id = ifc1AugId.child(Ipv4.class); + registry.addAfter(new GenericWriter<>(ipv4Id, new Ipv4Customizer(jvpp)), + ifcId); + // Address(after Ipv4) = + final InstanceIdentifier<Address> ipv4AddressId = ipv4Id.child(Address.class); + registry.addAfter(new GenericListWriter<>(ipv4AddressId, new Ipv4AddressCustomizer(jvpp, ifcNamingContext)), + ipv4Id); + // Neighbor(after ipv4Address) + registry.addAfter(new GenericListWriter<>(ipv4Id.child(Neighbor.class), new Ipv4NeighbourCustomizer(jvpp, + ifcNamingContext)), + ipv4AddressId); + } + + private void addVppInterfaceAgmentationWriters(final InstanceIdentifier<Interface> ifcId, + final ModifiableWriterRegistryBuilder registry) { + // VhostUser(Needs to be executed before Interface customizer) = + final InstanceIdentifier<VhostUser> vhostId = VPP_IFC_AUG_ID.child(VhostUser.class); + registry.addBefore(new GenericWriter<>(vhostId, new VhostUserCustomizer(jvpp, ifcNamingContext)), + ifcId); + // Vxlan(Needs to be executed before Interface customizer) = + final InstanceIdentifier<Vxlan> vxlanId = VPP_IFC_AUG_ID.child(Vxlan.class); + registry.addBefore(new GenericWriter<>(vxlanId, new VxlanCustomizer(jvpp, ifcNamingContext, ifcDisableContext)), + ifcId); + // VxlanGpe(Needs to be executed before Interface customizer) = + final InstanceIdentifier<VxlanGpe> vxlanGpeId = VPP_IFC_AUG_ID.child(VxlanGpe.class); + registry.addBefore(new GenericWriter<>(vxlanGpeId, + new VxlanGpeCustomizer(jvpp, ifcNamingContext, ifcDisableContext)), ifcId); + // Tap(Needs to be executed before Interface customizer) = + final InstanceIdentifier<Tap> tapId = VPP_IFC_AUG_ID.child(Tap.class); + registry.addBefore(new GenericWriter<>(tapId, new TapCustomizer(jvpp, ifcNamingContext)), + ifcId); + // Loopback(Needs to be executed before Interface customizer) = + final InstanceIdentifier<Loopback> loopbackId = VPP_IFC_AUG_ID.child(Loopback.class); + registry.addBefore(new GenericWriter<>(loopbackId, new LoopbackCustomizer(jvpp, ifcNamingContext)), + ifcId); + + // Gre(Needs to be executed before Interface customizer) = + final InstanceIdentifier<Gre> greId = VPP_IFC_AUG_ID.child(Gre.class); + registry.addBefore(new GenericWriter<>(greId, new GreCustomizer(jvpp, ifcNamingContext)), + ifcId); + + + final Set<InstanceIdentifier<?>> specificIfcTypes = Sets.newHashSet(vhostId, vxlanGpeId, vxlanGpeId, tapId); + + // Ethernet = + registry.add(new GenericWriter<>(VPP_IFC_AUG_ID.child(Ethernet.class), new EthernetCustomizer(jvpp))); + // Routing(Execute only after specific interface customizers) = + registry.addAfter( + new GenericWriter<>(VPP_IFC_AUG_ID.child(Routing.class), new RoutingCustomizer(jvpp, ifcNamingContext)), + specificIfcTypes); + // L2(Execute only after subinterface (and all other ifc types) = + registry.addAfter(new GenericWriter<>(L2_ID, new L2Customizer(jvpp, ifcNamingContext, bdNamingContext)), + SubinterfaceAugmentationWriterFactory.SUB_IFC_ID); + // Proxy Arp (execute after specific interface customizers) + registry.addAfter( + new GenericWriter<>(VPP_IFC_AUG_ID.child(ProxyArp.class), new ProxyArpCustomizer(jvpp)), + specificIfcTypes); + // Ingress (execute after classify table and session writers) + // also handles L2Acl, Ip4Acl and Ip6Acl: + final InstanceIdentifier<Ingress> ingressId = InstanceIdentifier.create(Ingress.class); + registry + .subtreeAddAfter( + Sets.newHashSet(ingressId.child(L2Acl.class), ingressId.child(Ip4Acl.class), + ingressId.child(Ip6Acl.class)), + new GenericWriter<>(INGRESS_ACL_ID, + new AclCustomizer(jvpp, ifcNamingContext, classifyTableContext)), + Sets.newHashSet(CLASSIFY_TABLE_ID, CLASSIFY_SESSION_ID)); + + // Ingress IETF-ACL, also handles AccessLists and Acl: + final InstanceIdentifier<AccessLists> accessListsIdIngress = InstanceIdentifier.create( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.ietf.acl.Ingress.class) + .child(AccessLists.class); + final InstanceIdentifier<?> aclIdIngress = accessListsIdIngress.child( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl.class); + registry.subtreeAdd( + Sets.newHashSet(accessListsIdIngress, aclIdIngress), + new GenericWriter<>(INGRESS_IETF_ACL_ID, new io.fd.hc2vpp.v3po.interfaces.acl.ingress.IetfAclCustomizer(ingressAclWriter, ifcNamingContext))); + + // Ingress IETF-ACL, also handles AccessLists and Acl: + final InstanceIdentifier<AccessLists> accessListsIdEgress = InstanceIdentifier.create( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.ietf.acl.Egress.class) + .child(AccessLists.class); + final InstanceIdentifier<?> aclIdEgress = accessListsIdEgress.child( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl.class); + registry.subtreeAdd( + Sets.newHashSet(accessListsIdEgress, aclIdEgress), + new GenericWriter<>(EGRESS_IETF_ACL_ID, + new io.fd.hc2vpp.v3po.interfaces.acl.egress.IetfAclCustomizer(egressAclWriter, + ifcNamingContext))); + } + + private void addPbbAugmentationWriters(final InstanceIdentifier<Interface> ifcId, + final ModifiableWriterRegistryBuilder registry) { + final InstanceIdentifier<PbbRewrite> pbbRewriteId = + ifcId.augmentation(PbbRewriteInterfaceAugmentation.class).child(PbbRewrite.class); + + registry.add(new GenericWriter<>(pbbRewriteId, new PbbRewriteCustomizer(jvpp, ifcNamingContext))); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/SubinterfaceAugmentationWriterFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/SubinterfaceAugmentationWriterFactory.java new file mode 100644 index 000000000..c8a7d281c --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/SubinterfaceAugmentationWriterFactory.java @@ -0,0 +1,158 @@ +/* + * 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.v3po; + +import static io.fd.hc2vpp.v3po.VppClassifierHoneycombWriterFactory.CLASSIFY_SESSION_ID; +import static io.fd.hc2vpp.v3po.VppClassifierHoneycombWriterFactory.CLASSIFY_TABLE_ID; + +import com.google.common.collect.Sets; +import io.fd.hc2vpp.v3po.interfaces.acl.egress.EgressIetfAclWriter; +import io.fd.hc2vpp.v3po.interfaces.acl.ingress.SubInterfaceAclCustomizer; +import io.fd.hc2vpp.v3po.interfaces.acl.ingress.SubInterfaceIetfAclCustomizer; +import io.fd.hc2vpp.v3po.vppclassifier.VppClassifierContextManager; +import io.fd.honeycomb.translate.impl.write.GenericListWriter; +import io.fd.honeycomb.translate.impl.write.GenericWriter; +import io.fd.hc2vpp.v3po.interfaces.RewriteCustomizer; +import io.fd.hc2vpp.v3po.interfaces.SubInterfaceCustomizer; +import io.fd.hc2vpp.v3po.interfaces.SubInterfaceL2Customizer; +import io.fd.hc2vpp.v3po.interfaces.acl.ingress.IngressIetfAclWriter; +import io.fd.hc2vpp.v3po.interfaces.ip.SubInterfaceIpv4AddressCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriterFactory; +import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.dot1q.tag.or.any.Dot1qTag; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.Ip4Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.Ip6Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.L2Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.SubinterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.SubInterfaces; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.match.attributes.match.type.vlan.tagged.VlanTagged; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.IetfAcl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.L2; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.Match; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.Tags; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.acl.Ingress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.l2.Rewrite; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.tags.Tag; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.Ipv4; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.ipv4.Address; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.tag.rewrite.PushTags; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public final class SubinterfaceAugmentationWriterFactory implements WriterFactory { + + private final FutureJVppCore jvpp; + private final IngressIetfAclWriter ingressAclWriter; + private final EgressIetfAclWriter egressAclWriter; + private final NamingContext ifcContext; + private final NamingContext bdContext; + private final VppClassifierContextManager classifyTableContext; + + public static final InstanceIdentifier<SubinterfaceAugmentation> SUB_IFC_AUG_ID = + InterfacesWriterFactory.IFC_ID.augmentation(SubinterfaceAugmentation.class); + public static final InstanceIdentifier<SubInterface> SUB_IFC_ID = + SUB_IFC_AUG_ID.child(SubInterfaces.class).child(SubInterface.class); + public static final InstanceIdentifier<L2> L2_ID = SUB_IFC_ID.child( + L2.class); + public static final InstanceIdentifier<Acl> SUBIF_ACL_ID = SUB_IFC_ID.child(Acl.class); + public static final InstanceIdentifier<Ingress> SUBIF_INGRESS_ACL_ID = SUBIF_ACL_ID.child(Ingress.class); + public static final InstanceIdentifier<IetfAcl> SUBIF_IETF_ACL_ID = SUB_IFC_ID.child(IetfAcl.class); + public static final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.ietf.acl.Ingress> SUBIF_INGRESS_IETF_ACL_ID = SUBIF_IETF_ACL_ID.child( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.ietf.acl.Ingress.class); + public static final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.ietf.acl.Egress> SUBIF_EGRESS_IETF_ACL_ID = SUBIF_IETF_ACL_ID.child( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.ietf.acl.Egress.class); + + public SubinterfaceAugmentationWriterFactory(final FutureJVppCore jvpp, + final IngressIetfAclWriter ingressAclWriter, + final EgressIetfAclWriter egressAclWriter, + final NamingContext ifcContext, final NamingContext bdContext, final VppClassifierContextManager classifyTableContext) { + this.jvpp = jvpp; + this.ingressAclWriter = ingressAclWriter; + this.egressAclWriter = egressAclWriter; + this.ifcContext = ifcContext; + this.bdContext = bdContext; + this.classifyTableContext = classifyTableContext; + } + + @Override + public void init(final ModifiableWriterRegistryBuilder registry) { + // Subinterfaces + // Subinterface(Handle only after all interface related stuff gets processed) = + registry.subtreeAddAfter( + // TODO HONEYCOMB-188 this customizer covers quite a lot of complex child nodes (maybe refactor ?) + Sets.newHashSet( + InstanceIdentifier.create(SubInterface.class).child(Tags.class), + InstanceIdentifier.create(SubInterface.class).child(Tags.class).child(Tag.class), + InstanceIdentifier.create(SubInterface.class).child(Tags.class).child(Tag.class).child( + Dot1qTag.class), + InstanceIdentifier.create(SubInterface.class).child(Match.class), + InstanceIdentifier.create(SubInterface.class).child(Match.class).child(VlanTagged.class)), + new GenericListWriter<>(SUB_IFC_ID, new SubInterfaceCustomizer(jvpp, ifcContext)), + InterfacesWriterFactory.IFC_ID); + // L2 = + registry.addAfter(new GenericWriter<>(L2_ID, new SubInterfaceL2Customizer(jvpp, ifcContext, bdContext)), + SUB_IFC_ID); + // Rewrite(also handles pushTags + pushTags/dot1qtag) = + final InstanceIdentifier<Rewrite> rewriteId = L2_ID.child(Rewrite.class); + registry.subtreeAddAfter( + Sets.newHashSet( + InstanceIdentifier.create(Rewrite.class).child(PushTags.class), + InstanceIdentifier.create(Rewrite.class).child(PushTags.class) + .child(org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.dot1q.tag.Dot1qTag.class)), + new GenericWriter<>(rewriteId, new RewriteCustomizer(jvpp, ifcContext)), + L2_ID); + // Ipv4(handled after L2 and L2/rewrite is done) = + final InstanceIdentifier<Address> ipv4SubifcAddressId = SUB_IFC_ID.child(Ipv4.class).child(Address.class); + registry.addAfter(new GenericListWriter<>(ipv4SubifcAddressId, + new SubInterfaceIpv4AddressCustomizer(jvpp, ifcContext)), + rewriteId); + + // Ingress (execute after classify table and session writers) + // also handles L2Acl, Ip4Acl and Ip6Acl: + final InstanceIdentifier<Ingress> aclId = InstanceIdentifier.create(Ingress.class); + registry + .subtreeAddAfter( + Sets.newHashSet(aclId.child(L2Acl.class), aclId.child(Ip4Acl.class), aclId.child(Ip6Acl.class)), + new GenericWriter<>(SUBIF_INGRESS_ACL_ID, new SubInterfaceAclCustomizer(jvpp, ifcContext, classifyTableContext)), + Sets.newHashSet(CLASSIFY_TABLE_ID, CLASSIFY_SESSION_ID)); + + // Ingress IETF-ACL, also handles AccessLists and Acl: + final InstanceIdentifier<AccessLists> accessListsIdIngress = InstanceIdentifier.create( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.ietf.acl.Ingress.class) + .child(AccessLists.class); + final InstanceIdentifier<?> aclIdIngress = accessListsIdIngress.child( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl.class); + registry.subtreeAdd( + Sets.newHashSet(accessListsIdIngress, aclIdIngress), + new GenericWriter<>(SUBIF_INGRESS_IETF_ACL_ID, new SubInterfaceIetfAclCustomizer(ingressAclWriter, ifcContext))); + + // Egress IETF-ACL, also handles AccessLists and Acl: + final InstanceIdentifier<AccessLists> accessListsIdEgress = InstanceIdentifier.create( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.ietf.acl.Egress.class) + .child(AccessLists.class); + final InstanceIdentifier<?> aclIdEgress = accessListsIdEgress.child( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl.class); + registry.subtreeAdd( + Sets.newHashSet(accessListsIdEgress, aclIdEgress), + new GenericWriter<>(SUBIF_EGRESS_IETF_ACL_ID, new io.fd.hc2vpp.v3po.interfaces.acl.egress.SubInterfaceIetfAclCustomizer( + egressAclWriter, ifcContext))); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/SubinterfaceStateAugmentationReaderFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/SubinterfaceStateAugmentationReaderFactory.java new file mode 100644 index 000000000..ec4c50659 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/SubinterfaceStateAugmentationReaderFactory.java @@ -0,0 +1,118 @@ +/* + * 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.v3po; + +import com.google.common.collect.Sets; +import io.fd.hc2vpp.v3po.interfacesstate.RewriteCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.SubInterfaceCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.SubInterfaceL2Customizer; +import io.fd.hc2vpp.v3po.interfacesstate.acl.ingress.SubInterfaceAclCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.ip.SubInterfaceIpv4AddressCustomizer; +import io.fd.hc2vpp.v3po.vppclassifier.VppClassifierContextManager; +import io.fd.honeycomb.translate.impl.read.GenericInitListReader; +import io.fd.honeycomb.translate.impl.read.GenericInitReader; +import io.fd.honeycomb.translate.impl.read.GenericReader; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.dot1q.tag.or.any.Dot1qTag; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.Ip4Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.Ip6Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.L2Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.SubinterfaceStateAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.SubinterfaceStateAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces.state._interface.SubInterfaces; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces.state._interface.SubInterfacesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces.state._interface.sub.interfaces.SubInterface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.match.attributes.match.type.vlan.tagged.VlanTagged; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.AclBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.L2; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.Match; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.Tags; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.acl.Ingress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.l2.Rewrite; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.tags.Tag; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.Ipv4; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.Ipv4Builder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.ipv4.Address; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.tag.rewrite.PushTags; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +final class SubinterfaceStateAugmentationReaderFactory implements ReaderFactory { + + private final FutureJVppCore jvpp; + private final NamingContext ifcCtx; + private final NamingContext bdCtx; + private final VppClassifierContextManager classifyCtx; + + SubinterfaceStateAugmentationReaderFactory(final FutureJVppCore jvpp, final NamingContext ifcCtx, + final NamingContext bdCtx, + final VppClassifierContextManager classifyCtx) { + this.jvpp = jvpp; + this.ifcCtx = ifcCtx; + this.bdCtx = bdCtx; + this.classifyCtx = classifyCtx; + } + + @Override + public void init(final ModifiableReaderRegistryBuilder registry) { + // SubinterfaceStateAugmentation(Structural) + final InstanceIdentifier<SubinterfaceStateAugmentation> subIfcAugId = + InterfacesStateReaderFactory.IFC_ID.augmentation(SubinterfaceStateAugmentation.class); + registry.addStructuralReader(subIfcAugId, SubinterfaceStateAugmentationBuilder.class); + // SubInterfaces(Structural) + final InstanceIdentifier<SubInterfaces> subIfcsId = subIfcAugId.child(SubInterfaces.class); + registry.addStructuralReader(subIfcsId, SubInterfacesBuilder.class); + // SubInterface(Subtree) + final InstanceIdentifier<SubInterface> subIfcId = subIfcsId.child(SubInterface.class); + registry.subtreeAdd(Sets.newHashSet( + InstanceIdentifier.create(SubInterface.class).child(Tags.class), + InstanceIdentifier.create(SubInterface.class).child(Tags.class).child(Tag.class), + InstanceIdentifier.create(SubInterface.class).child(Tags.class).child(Tag.class).child(Dot1qTag.class), + InstanceIdentifier.create(SubInterface.class).child(Match.class), + InstanceIdentifier.create(SubInterface.class).child(Match.class).child(VlanTagged.class)), + new GenericInitListReader<>(subIfcId, new SubInterfaceCustomizer(jvpp, ifcCtx))); + // L2 + final InstanceIdentifier<L2> l2Id = subIfcId.child(L2.class); + registry.add(new GenericInitReader<>(l2Id, new SubInterfaceL2Customizer(jvpp, ifcCtx, bdCtx))); + // Rewrite(Subtree) + registry.subtreeAdd(Sets.newHashSet( + InstanceIdentifier.create(Rewrite.class).child(PushTags.class), + InstanceIdentifier.create(Rewrite.class).child(PushTags.class) + .child( + org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.dot1q.tag.Dot1qTag.class)), + new GenericReader<>(l2Id.child(Rewrite.class), new RewriteCustomizer(jvpp, ifcCtx))); + // Ipv4(Structural) + final InstanceIdentifier<Ipv4> ipv4Id = subIfcId.child(Ipv4.class); + registry.addStructuralReader(ipv4Id, Ipv4Builder.class); + // Address + registry.add( + new GenericInitListReader<>(ipv4Id.child(Address.class), new SubInterfaceIpv4AddressCustomizer(jvpp, ifcCtx))); + // Acl(Structural) + final InstanceIdentifier<Acl> aclIid = subIfcId.child(Acl.class); + registry.addStructuralReader(aclIid, AclBuilder.class); + // Ingress(Subtree) + final InstanceIdentifier<Ingress> ingressIdRelative = InstanceIdentifier.create(Ingress.class); + registry.subtreeAdd( + Sets.newHashSet(ingressIdRelative.child(L2Acl.class), ingressIdRelative.child(Ip4Acl.class), + ingressIdRelative.child(Ip6Acl.class)), + new GenericInitReader<>(aclIid.child(Ingress.class), + new SubInterfaceAclCustomizer(jvpp, ifcCtx, classifyCtx))); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/V3poModule.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/V3poModule.java new file mode 100644 index 000000000..ad9d04888 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/V3poModule.java @@ -0,0 +1,92 @@ +/* + * 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.v3po; + +import com.google.inject.AbstractModule; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.name.Names; +import io.fd.hc2vpp.v3po.interfaces.acl.egress.EgressIetfAclWriter; +import io.fd.hc2vpp.v3po.notification.InterfaceChangeNotificationProducer; +import io.fd.hc2vpp.v3po.vppclassifier.VppClassifierContextManager; +import io.fd.hc2vpp.v3po.vppclassifier.VppClassifierContextManagerImpl; +import io.fd.honeycomb.notification.ManagedNotificationProducer; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.hc2vpp.v3po.cfgattrs.V3poConfiguration; +import io.fd.hc2vpp.v3po.interfaces.acl.ingress.IngressIetfAclWriter; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriterFactory; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import net.jmob.guice.conf.core.ConfigurationModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class V3poModule extends AbstractModule { + + private static final Logger LOG = LoggerFactory.getLogger(V3poModule.class); + + @Override + protected void configure() { + LOG.debug("Installing V3PO module"); + install(ConfigurationModule.create()); + requestInjection(V3poConfiguration.class); + + // TODO HONEYCOMB-173 put into constants + // Naming contexts + bind(NamingContext.class) + .annotatedWith(Names.named("interface-context")) + .toInstance(new NamingContext("interface-", "interface-context")); + bind(NamingContext.class) + .annotatedWith(Names.named("bridge-domain-context")) + .toInstance(new NamingContext("bridge-domain-", "bridge-domain-context")); + bind(VppClassifierContextManager.class) + .annotatedWith(Names.named("classify-table-context")) + .toInstance(new VppClassifierContextManagerImpl("classify-table-")); + + // Executor needed for keepalives + bind(ScheduledExecutorService.class).toInstance(Executors.newScheduledThreadPool(1)); + + // Utils + bind(IngressIetfAclWriter.class).toProvider(IngressIetfAClWriterProvider.class); + bind(EgressIetfAclWriter.class).toProvider(EgressIetfAClWriterProvider.class); + // Context utility for deleted interfaces + bind(DisabledInterfacesManager.class).toInstance(new DisabledInterfacesManager()); + + // Readers + final Multibinder<ReaderFactory> readerFactoryBinder = Multibinder.newSetBinder(binder(), ReaderFactory.class); + readerFactoryBinder.addBinding().to(InterfacesStateReaderFactory.class); + readerFactoryBinder.addBinding().to(VppStateHoneycombReaderFactory.class); + readerFactoryBinder.addBinding().to(VppClassifierReaderFactory.class); + // Expose disabled interfaces in operational data + readerFactoryBinder.addBinding().to(DisabledInterfacesManager.ContextsReaderFactory.class); + // Expose vpp-classfier-context interfaces in operational data + readerFactoryBinder.addBinding().to(VppClassifierContextManagerImpl.ContextsReaderFactory.class); + + // Writers + final Multibinder<WriterFactory> writerFactoryBinder = Multibinder.newSetBinder(binder(), WriterFactory.class); + writerFactoryBinder.addBinding().to(InterfacesWriterFactory.class); + writerFactoryBinder.addBinding().to(VppHoneycombWriterFactory.class); + writerFactoryBinder.addBinding().to(VppClassifierHoneycombWriterFactory.class); + writerFactoryBinder.addBinding().to(AclWriterFactory.class); + + // Notifications + final Multibinder<ManagedNotificationProducer> notifiersBinder = + Multibinder.newSetBinder(binder(), ManagedNotificationProducer.class); + notifiersBinder.addBinding().to(InterfaceChangeNotificationProducer.class); + LOG.info("Module V3PO successfully configured"); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/VppClassifierHoneycombWriterFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/VppClassifierHoneycombWriterFactory.java new file mode 100644 index 000000000..1443c5576 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/VppClassifierHoneycombWriterFactory.java @@ -0,0 +1,64 @@ +/* + * 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.v3po; + +import com.google.inject.Inject; +import com.google.inject.name.Named; +import io.fd.hc2vpp.v3po.vppclassifier.ClassifySessionWriter; +import io.fd.hc2vpp.v3po.vppclassifier.ClassifyTableWriter; +import io.fd.hc2vpp.v3po.vppclassifier.VppClassifierContextManager; +import io.fd.honeycomb.translate.impl.write.GenericListWriter; +import io.fd.honeycomb.translate.write.WriterFactory; +import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.VppClassifier; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.classify.table.base.attributes.ClassifySession; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.ClassifyTable; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; + +public final class VppClassifierHoneycombWriterFactory implements WriterFactory { + + public static final InstanceIdentifier<ClassifyTable> CLASSIFY_TABLE_ID = + InstanceIdentifier.create(VppClassifier.class).child(ClassifyTable.class); + + public static final InstanceIdentifier<ClassifySession> CLASSIFY_SESSION_ID = + CLASSIFY_TABLE_ID.child(ClassifySession.class); + + private final FutureJVppCore jvpp; + private final VppClassifierContextManager classifyTableContext; + + @Inject + public VppClassifierHoneycombWriterFactory(@Nonnull final FutureJVppCore jvpp, + @Named("classify-table-context") @Nonnull final VppClassifierContextManager classifyTableContext) { + this.jvpp = jvpp; + this.classifyTableContext = classifyTableContext; + } + + @Override + public void init(@Nonnull final ModifiableWriterRegistryBuilder registry) { + // Ordering here is: First create table, then create sessions and then assign as ACL + // ClassifyTable + registry.addBefore( + new GenericListWriter<>(CLASSIFY_TABLE_ID, new ClassifyTableWriter(jvpp, classifyTableContext)), + CLASSIFY_SESSION_ID); + // ClassifyTableSession + registry.addBefore( + new GenericListWriter<>(CLASSIFY_SESSION_ID, new ClassifySessionWriter(jvpp, classifyTableContext)), + InterfacesWriterFactory.ACL_ID); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/VppClassifierReaderFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/VppClassifierReaderFactory.java new file mode 100644 index 000000000..d612314e1 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/VppClassifierReaderFactory.java @@ -0,0 +1,60 @@ +/* + * 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.v3po; + +import com.google.inject.Inject; +import com.google.inject.name.Named; +import io.fd.honeycomb.translate.impl.read.GenericInitListReader; +import io.fd.honeycomb.translate.impl.read.GenericListReader; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import io.fd.hc2vpp.v3po.vppclassifier.ClassifySessionReader; +import io.fd.hc2vpp.v3po.vppclassifier.ClassifyTableReader; +import io.fd.hc2vpp.v3po.vppclassifier.VppClassifierContextManager; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.VppClassifierState; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.VppClassifierStateBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.classify.table.base.attributes.ClassifySession; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.state.ClassifyTable; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public final class VppClassifierReaderFactory implements ReaderFactory { + + private final FutureJVppCore jvpp; + private final VppClassifierContextManager classifyCtx; + + @Inject + public VppClassifierReaderFactory(final FutureJVppCore jvpp, + @Named("classify-table-context") final VppClassifierContextManager classifyCtx) { + this.jvpp = jvpp; + this.classifyCtx = classifyCtx; + } + + @Override + public void init(@Nonnull final ModifiableReaderRegistryBuilder registry) { + // VppClassifierState + final InstanceIdentifier<VppClassifierState> vppStateId = InstanceIdentifier.create(VppClassifierState.class); + registry.addStructuralReader(vppStateId, VppClassifierStateBuilder.class); + // ClassifyTable + final InstanceIdentifier<ClassifyTable> classTblId = vppStateId.child(ClassifyTable.class); + registry.add(new GenericInitListReader<>(classTblId, new ClassifyTableReader(jvpp, classifyCtx))); + // ClassifySession + final InstanceIdentifier<ClassifySession> classSesId = classTblId.child(ClassifySession.class); + registry.add(new GenericListReader<>(classSesId, new ClassifySessionReader(jvpp, classifyCtx))); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/VppHoneycombWriterFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/VppHoneycombWriterFactory.java new file mode 100644 index 000000000..0f836d632 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/VppHoneycombWriterFactory.java @@ -0,0 +1,81 @@ +/* + * 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.v3po; + +import static io.fd.hc2vpp.v3po.InterfacesWriterFactory.L2_ID; + +import com.google.common.collect.Sets; +import com.google.inject.Inject; +import com.google.inject.name.Named; +import io.fd.hc2vpp.v3po.vpp.ArpTerminationTableEntryCustomizer; +import io.fd.hc2vpp.v3po.vpp.BridgeDomainCustomizer; +import io.fd.hc2vpp.v3po.vpp.L2FibEntryCustomizer; +import io.fd.honeycomb.translate.impl.write.GenericListWriter; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriterFactory; +import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.Vpp; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.bridge.domain.attributes.ArpTerminationTable; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.bridge.domain.attributes.arp.termination.table.ArpTerminationTableEntry; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.fib.attributes.L2FibTable; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.fib.attributes.l2.fib.table.L2FibEntry; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.BridgeDomains; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.bridge.domains.BridgeDomain; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; + +public final class VppHoneycombWriterFactory implements WriterFactory { + + private final FutureJVppCore jvpp; + private final NamingContext bdContext; + private final NamingContext ifcContext; + + @Inject + public VppHoneycombWriterFactory(final FutureJVppCore vppJvppWriterDependency, + @Named("bridge-domain-context") final NamingContext bridgeDomainContextVppDependency, + @Named("interface-context") final NamingContext interfaceContextVppDependency) { + this.jvpp = vppJvppWriterDependency; + this.bdContext = bridgeDomainContextVppDependency; + this.ifcContext = interfaceContextVppDependency; + } + + @Override + public void init(final ModifiableWriterRegistryBuilder registry) { + // Vpp has no handlers + // BridgeDomains has no handlers + // BridgeDomain = + final InstanceIdentifier<BridgeDomain> bdId = + InstanceIdentifier.create(Vpp.class).child(BridgeDomains.class).child(BridgeDomain.class); + registry.add(new GenericListWriter<>(bdId, new BridgeDomainCustomizer(jvpp, bdContext))); + // L2FibTable has no handlers + // L2FibEntry(handled after BridgeDomain and L2 of ifc and subifc) = + final InstanceIdentifier<L2FibEntry> l2FibEntryId = bdId.child(L2FibTable.class).child(L2FibEntry.class); + registry.addAfter( + new GenericListWriter<>(l2FibEntryId, new L2FibEntryCustomizer(jvpp, bdContext, ifcContext)), + Sets.newHashSet( + bdId, + L2_ID, + SubinterfaceAugmentationWriterFactory.L2_ID)); + // ArpTerminationTable has no handlers + // ArpTerminationTableEntry(handled after BridgeDomain) = + final InstanceIdentifier<ArpTerminationTableEntry> arpEntryId = + bdId.child(ArpTerminationTable.class).child(ArpTerminationTableEntry.class); + registry.addAfter( + new GenericListWriter<>(arpEntryId, new ArpTerminationTableEntryCustomizer(jvpp, bdContext)), + bdId); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/VppStateHoneycombReaderFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/VppStateHoneycombReaderFactory.java new file mode 100644 index 000000000..03da2c755 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/VppStateHoneycombReaderFactory.java @@ -0,0 +1,90 @@ +/* + * 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.v3po; + +import com.google.inject.Inject; +import com.google.inject.name.Named; +import io.fd.hc2vpp.v3po.vppstate.BridgeDomainCustomizer; +import io.fd.hc2vpp.v3po.vppstate.L2FibEntryCustomizer; +import io.fd.hc2vpp.v3po.vppstate.VersionCustomizer; +import io.fd.honeycomb.translate.impl.read.GenericInitListReader; +import io.fd.honeycomb.translate.impl.read.GenericReader; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import io.fd.honeycomb.translate.util.read.KeepaliveReaderWrapper; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.hc2vpp.common.translate.util.ReadTimeoutException; +import io.fd.hc2vpp.common.translate.util.VppStatusListener; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.concurrent.ScheduledExecutorService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppState; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppStateBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.fib.attributes.L2FibTable; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.fib.attributes.L2FibTableBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.fib.attributes.l2.fib.table.L2FibEntry; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.state.BridgeDomains; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.state.BridgeDomainsBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.state.Version; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.state.bridge.domains.BridgeDomain; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public final class VppStateHoneycombReaderFactory implements ReaderFactory { + + private final FutureJVppCore jVpp; + private final NamingContext ifcCtx; + private final NamingContext bdCtx; + private final ScheduledExecutorService keepaliveExecutor; + private final VppStatusListener vppStatusListener; + + @Inject + public VppStateHoneycombReaderFactory(final FutureJVppCore jVpp, + @Named("interface-context") final NamingContext ifcCtx, + @Named("bridge-domain-context") final NamingContext bdCtx, + final ScheduledExecutorService keepaliveExecutorDependency, + final VppStatusListener vppStatusListener) { + this.jVpp = jVpp; + this.ifcCtx = ifcCtx; + this.bdCtx = bdCtx; + this.keepaliveExecutor = keepaliveExecutorDependency; + this.vppStatusListener = vppStatusListener; + } + + @Override + public void init(final ModifiableReaderRegistryBuilder registry) { + // VppState(Structural) + final InstanceIdentifier<VppState> vppStateId = InstanceIdentifier.create(VppState.class); + registry.addStructuralReader(vppStateId, VppStateBuilder.class); + // Version + // Wrap with keepalive reader to detect connection issues + // Relying on VersionCustomizer to provide a "timing out read" + registry.add(new KeepaliveReaderWrapper<>( + new GenericReader<>(vppStateId.child(Version.class), new VersionCustomizer(jVpp)), + keepaliveExecutor, ReadTimeoutException.class, 30, vppStatusListener)); + // BridgeDomains(Structural) + final InstanceIdentifier<BridgeDomains> bridgeDomainsId = vppStateId.child(BridgeDomains.class); + registry.addStructuralReader(bridgeDomainsId, BridgeDomainsBuilder.class); + // BridgeDomain + final InstanceIdentifier<BridgeDomain> bridgeDomainId = bridgeDomainsId.child(BridgeDomain.class); + registry.add(new GenericInitListReader<>(bridgeDomainId, new BridgeDomainCustomizer(jVpp, bdCtx))); + // L2FibTable(Structural) + final InstanceIdentifier<L2FibTable> l2FibTableId = bridgeDomainId.child(L2FibTable.class); + registry.addStructuralReader(l2FibTableId, L2FibTableBuilder.class); + // L2FibEntry + registry.add(new GenericInitListReader<>(l2FibTableId.child(L2FibEntry.class), + new L2FibEntryCustomizer(jVpp, bdCtx, ifcCtx))); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/cfgattrs/V3poConfiguration.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/cfgattrs/V3poConfiguration.java new file mode 100644 index 000000000..6676956d8 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/cfgattrs/V3poConfiguration.java @@ -0,0 +1,24 @@ +/* + * 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.v3po.cfgattrs; + +import net.jmob.guice.conf.core.BindConfig; +import net.jmob.guice.conf.core.Syntax; + +@BindConfig(value = "v3po", syntax = Syntax.JSON) +public class V3poConfiguration { +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/EthernetCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/EthernetCustomizer.java new file mode 100644 index 000000000..ec83df831 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/EthernetCustomizer.java @@ -0,0 +1,58 @@ +/* + * 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.v3po.interfaces; + +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Ethernet; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class EthernetCustomizer extends FutureJVppCustomizer implements WriterCustomizer<Ethernet> { + + private static final Logger LOG = LoggerFactory.getLogger(EthernetCustomizer.class); + + public EthernetCustomizer(final FutureJVppCore vppApi) { + super(vppApi); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Ethernet> id, + @Nonnull final Ethernet dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + LOG.warn("Unsupported, ignoring configuration {}", dataAfter); + // VPP API does not support setting MTU + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Ethernet> id, + @Nonnull final Ethernet dataBefore, @Nonnull final Ethernet dataAfter, + @Nonnull final WriteContext writeContext) { + LOG.warn("Unsupported, ignoring configuration {}", dataAfter); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Ethernet> id, + @Nonnull final Ethernet dataBefore, @Nonnull final WriteContext writeContext) { + LOG.warn("Unsupported, ignoring configuration delete {}", id); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/GreCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/GreCustomizer.java new file mode 100644 index 000000000..b91d26364 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/GreCustomizer.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2016 Intel 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.v3po.interfaces; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.net.InetAddresses; +import io.fd.hc2vpp.common.translate.util.AbstractInterfaceTypeCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.GreAddDelTunnel; +import io.fd.vpp.jvpp.core.dto.GreAddDelTunnelReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.net.InetAddress; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType; +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.v3po.rev161214.GreTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Gre; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GreCustomizer extends AbstractInterfaceTypeCustomizer<Gre> implements JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(GreCustomizer.class); + private final NamingContext interfaceContext; + + public GreCustomizer(final FutureJVppCore vppApi, final NamingContext interfaceContext) { + super(vppApi); + this.interfaceContext = interfaceContext; + } + + private static GreAddDelTunnel getGreTunnelRequest(final byte isAdd, final byte[] srcAddr, final byte[] dstAddr, + final int outerFibId, final byte isIpv6) { + final GreAddDelTunnel greAddDelTunnel = new GreAddDelTunnel(); + greAddDelTunnel.isAdd = isAdd; + greAddDelTunnel.srcAddress = srcAddr; + greAddDelTunnel.dstAddress = dstAddr; + greAddDelTunnel.outerFibId = outerFibId; + greAddDelTunnel.isIpv6 = isIpv6; + return greAddDelTunnel; + } + + @Override + protected Class<? extends InterfaceType> getExpectedInterfaceType() { + return GreTunnel.class; + } + + @Override + protected final void writeInterface(@Nonnull final InstanceIdentifier<Gre> id, @Nonnull final Gre dataAfter, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String swIfName = id.firstKeyOf(Interface.class).getName(); + + createGreTunnel(id, swIfName, dataAfter, writeContext); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Gre> id, @Nonnull final Gre dataBefore, + @Nonnull final Gre dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException.UpdateFailedException { + throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, + new UnsupportedOperationException("Gre tunnel update is not supported")); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Gre> id, @Nonnull final Gre dataBefore, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String swIfName = id.firstKeyOf(Interface.class).getName(); + deleteGreTunnel(id, swIfName, dataBefore, writeContext); + } + + private void createGreTunnel(final InstanceIdentifier<Gre> id, final String swIfName, final Gre gre, + final WriteContext writeContext) throws WriteFailedException { + final byte isIpv6 = (byte) (isIpv6(gre) + ? 1 + : 0); + final InetAddress srcAddress = InetAddresses.forString(getAddressString(gre.getSrc())); + final InetAddress dstAddress = InetAddresses.forString(getAddressString(gre.getDst())); + + int outerFibId = gre.getOuterFibId().intValue(); + + LOG.debug("Setting gre tunnel for interface: {}. Gre: {}", swIfName, gre); + final CompletionStage<GreAddDelTunnelReply> greAddDelTunnelReplyCompletionStage = + getFutureJVpp().greAddDelTunnel(getGreTunnelRequest((byte) 1 /* is add */, srcAddress.getAddress(), + dstAddress.getAddress(), outerFibId, isIpv6)); + + final GreAddDelTunnelReply reply = + getReplyForCreate(greAddDelTunnelReplyCompletionStage.toCompletableFuture(), id, gre); + LOG.debug("Gre tunnel set successfully for: {}, gre: {}", swIfName, gre); + if (interfaceContext.containsName(reply.swIfIndex, writeContext.getMappingContext())) { + // VPP keeps gre tunnels present even after they are delete(reserving ID for next tunnel) + // This may cause inconsistencies in mapping context when configuring tunnels like this: + // 1. Add tunnel 2. Delete tunnel 3. Read interfaces (reserved mapping e.g. gre_tunnel0 -> 6 + // will get into mapping context) 4. Add tunnel (this will add another mapping with the same + // reserved ID and context is invalid) + // That's why a check has to be performed here removing mapping gre_tunnel0 -> 6 mapping and storing + // new name for that ID + final String formerName = interfaceContext.getName(reply.swIfIndex, writeContext.getMappingContext()); + LOG.debug("Removing updated mapping of a gre tunnel, id: {}, former name: {}, new name: {}", + reply.swIfIndex, formerName, swIfName); + interfaceContext.removeName(formerName, writeContext.getMappingContext()); + } + // Add new interface to our interface context + interfaceContext.addName(reply.swIfIndex, swIfName, writeContext.getMappingContext()); + } + + private boolean isIpv6(final Gre gre) { + if (gre.getSrc().getIpv4Address() == null) { + checkArgument(gre.getDst().getIpv4Address() == null, "Inconsistent ip addresses: %s, %s", gre.getSrc(), + gre.getDst()); + return true; + } else { + checkArgument(gre.getDst().getIpv6Address() == null, "Inconsistent ip addresses: %s, %s", gre.getSrc(), + gre.getDst()); + return false; + } + } + + private String getAddressString(final IpAddress addr) { + return addr.getIpv4Address() == null + ? addr.getIpv6Address().getValue() + : addr.getIpv4Address().getValue(); + } + + private void deleteGreTunnel(final InstanceIdentifier<Gre> id, final String swIfName, final Gre gre, + final WriteContext writeContext) throws WriteFailedException { + final byte isIpv6 = (byte) (isIpv6(gre) + ? 1 + : 0); + final InetAddress srcAddress = InetAddresses.forString(getAddressString(gre.getSrc())); + final InetAddress dstAddress = InetAddresses.forString(getAddressString(gre.getDst())); + + int outerFibId = gre.getOuterFibId().intValue(); + + LOG.debug("Deleting gre tunnel for interface: {}. Gre: {}", swIfName, gre); + final CompletionStage<GreAddDelTunnelReply> greAddDelTunnelReplyCompletionStage = + getFutureJVpp().greAddDelTunnel(getGreTunnelRequest((byte) 0 /* is add */, srcAddress.getAddress(), + dstAddress.getAddress(), outerFibId, isIpv6)); + + getReplyForDelete(greAddDelTunnelReplyCompletionStage.toCompletableFuture(), id); + LOG.debug("Gre tunnel deleted successfully for: {}, gre: {}", swIfName, gre); + // Remove interface from our interface context + interfaceContext.removeName(swIfName, writeContext.getMappingContext()); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/InterconnectionWriteUtils.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/InterconnectionWriteUtils.java new file mode 100644 index 000000000..0702717bc --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/InterconnectionWriteUtils.java @@ -0,0 +1,159 @@ +/* + * 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.v3po.interfaces; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetL2Bridge; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetL2BridgeReply; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetL2Xconnect; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetL2XconnectReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.Interconnection; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.interconnection.BridgeBased; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.interconnection.XconnectBased; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility class providing Interconnection CUD support. + */ +final class InterconnectionWriteUtils implements JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(InterconnectionWriteUtils.class); + + private final FutureJVppCore futureJVppCore; + private final NamingContext interfaceContext; + private final NamingContext bridgeDomainContext; + + InterconnectionWriteUtils(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final NamingContext interfaceContext, + @Nonnull final NamingContext bridgeDomainContext) { + this.futureJVppCore = requireNonNull(futureJVppCore, "futureJVppCore should not be null"); + this.interfaceContext = requireNonNull(interfaceContext, "interfaceContext should not be null"); + this.bridgeDomainContext = requireNonNull(bridgeDomainContext, "bridgeDomainContext should not be null"); + } + + void setInterconnection(final InstanceIdentifier<? extends DataObject> id, final int swIfIndex, + final String ifcName, final Interconnection ic, final WriteContext writeContext) + throws WriteFailedException { + + if (ic == null) { // TODO in case of update we should delete interconnection + LOG.trace("Interconnection is not set. Skipping"); + } else if (ic instanceof XconnectBased) { + setXconnectBasedL2(id, swIfIndex, ifcName, (XconnectBased) ic, writeContext, (byte) 1 /*enable*/); + } else if (ic instanceof BridgeBased) { + setBridgeBasedL2(id, swIfIndex, ifcName, (BridgeBased) ic, writeContext, (byte) 1 /*enable*/); + } else { + // Choices&cases are not data objects, so they cannot have a dedicated Reader/Writer + // This choice is already from augment, so its not possible to augment augmented choice + LOG.error("Unable to handle Interconnection of type {}", ic.getClass()); + throw new WriteFailedException(id, "Unable to handle Interconnection of type " + ic.getClass()); + } + } + + void deleteInterconnection(final InstanceIdentifier<? extends DataObject> id, final int swIfIndex, + final String ifcName, final Interconnection ic, final WriteContext writeContext) + throws WriteFailedException { + + if (ic == null) { // TODO in case of update we should delete interconnection + LOG.trace("Interconnection is not set. Skipping"); + } else if (ic instanceof XconnectBased) { + setXconnectBasedL2(id, swIfIndex, ifcName, (XconnectBased) ic, writeContext, (byte) 0 /*disable*/); + } else if (ic instanceof BridgeBased) { + setBridgeBasedL2(id, swIfIndex, ifcName, (BridgeBased) ic, writeContext, (byte) 0 /*disable*/); + } else { + LOG.error("Unable to delete Interconnection of type {}", ic.getClass()); + throw new WriteFailedException(id, "Unable to delete Interconnection of type " + ic.getClass()); + } + } + + private void setBridgeBasedL2(final InstanceIdentifier<? extends DataObject> id, final int swIfIndex, + final String ifcName, final BridgeBased bb, + final WriteContext writeContext, final byte enabled) throws WriteFailedException { + LOG.debug("Setting bridge based interconnection(bridge-domain={}) for interface: {}", bb.getBridgeDomain(), + ifcName); + + String bdName = bb.getBridgeDomain(); + + int bdId = bridgeDomainContext.getIndex(bdName, writeContext.getMappingContext()); + checkArgument(bdId > 0, "Unable to set Interconnection for Interface: %s, bridge domain: %s does not exist", + ifcName, bdName); + + byte bvi = bb.isBridgedVirtualInterface() + ? (byte) 1 + : (byte) 0; + byte shg = 0; + if (bb.getSplitHorizonGroup() != null) { + shg = bb.getSplitHorizonGroup().byteValue(); + } + + final CompletionStage<SwInterfaceSetL2BridgeReply> swInterfaceSetL2BridgeReplyCompletionStage = futureJVppCore + .swInterfaceSetL2Bridge(getL2BridgeRequest(swIfIndex, bdId, shg, bvi, enabled)); + getReplyForWrite(swInterfaceSetL2BridgeReplyCompletionStage.toCompletableFuture(), id); + + LOG.debug("Bridge based interconnection updated successfully for: {}, interconnection: {}", ifcName, bb); + } + + private SwInterfaceSetL2Bridge getL2BridgeRequest(final int swIfIndex, final int bdId, final byte shg, + final byte bvi, final byte enabled) { + final SwInterfaceSetL2Bridge swInterfaceSetL2Bridge = new SwInterfaceSetL2Bridge(); + swInterfaceSetL2Bridge.rxSwIfIndex = swIfIndex; + swInterfaceSetL2Bridge.bdId = bdId; + swInterfaceSetL2Bridge.shg = shg; + swInterfaceSetL2Bridge.bvi = bvi; + swInterfaceSetL2Bridge.enable = enabled; + return swInterfaceSetL2Bridge; + } + + private void setXconnectBasedL2(final InstanceIdentifier<? extends DataObject> id, final int swIfIndex, + final String ifcName, final XconnectBased ic, + final WriteContext writeContext, final byte enabled) throws WriteFailedException { + String outSwIfName = ic.getXconnectOutgoingInterface(); + LOG.debug("Setting xconnect based interconnection(outgoing ifc={}) for interface: {}", outSwIfName, ifcName); + + int outSwIfIndex = interfaceContext.getIndex(outSwIfName, writeContext.getMappingContext()); + checkArgument(outSwIfIndex > 0, + "Unable to set Interconnection for Interface: %s, outgoing interface: %s does not exist", + ifcName, outSwIfIndex); + + final CompletionStage<SwInterfaceSetL2XconnectReply> swInterfaceSetL2XconnectReplyCompletionStage = + futureJVppCore + .swInterfaceSetL2Xconnect(getL2XConnectRequest(swIfIndex, outSwIfIndex, enabled)); + getReplyForWrite(swInterfaceSetL2XconnectReplyCompletionStage.toCompletableFuture(), id); + LOG.debug("Xconnect based interconnection updated successfully for: {}, interconnection: {}", ifcName, ic); + } + + private SwInterfaceSetL2Xconnect getL2XConnectRequest(final int rxIfc, final int txIfc, + final byte enabled) { + + final SwInterfaceSetL2Xconnect swInterfaceSetL2Xconnect = new SwInterfaceSetL2Xconnect(); + swInterfaceSetL2Xconnect.enable = enabled; + swInterfaceSetL2Xconnect.rxSwIfIndex = rxIfc; + swInterfaceSetL2Xconnect.txSwIfIndex = txIfc; + return swInterfaceSetL2Xconnect; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/InterfaceCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/InterfaceCustomizer.java new file mode 100644 index 000000000..336a66152 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/InterfaceCustomizer.java @@ -0,0 +1,121 @@ +/* + * 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.v3po.interfaces; + +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlags; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Ietf interface write customizer that only caches interface objects for child writers + */ +public class InterfaceCustomizer extends FutureJVppCustomizer + implements ListWriterCustomizer<Interface, InterfaceKey>, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(InterfaceCustomizer.class); + private final NamingContext interfaceContext; + + public InterfaceCustomizer(final FutureJVppCore vppApi, final NamingContext interfaceContext) { + super(vppApi); + this.interfaceContext = interfaceContext; + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Interface> id, + @Nonnull final Interface dataAfter, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + + setInterface(id, dataAfter, writeContext); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Interface> id, + @Nonnull final Interface dataBefore, + @Nonnull final Interface dataAfter, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + updateInterface(id, dataBefore, dataAfter, writeContext); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Interface> id, + @Nonnull final Interface dataBefore, + @Nonnull final WriteContext writeContext) { + // Nothing to be done here, customizers for specific interface types e.g. vxlan handle the delete + } + + private void setInterface(final InstanceIdentifier<Interface> id, final Interface swIf, + final WriteContext writeContext) throws WriteFailedException { + LOG.debug("Setting interface: {} to: {}", id, swIf); + setInterfaceAttributes(id, swIf, swIf.getName(), writeContext); + } + + private void setInterfaceAttributes(final InstanceIdentifier<Interface> id, final Interface swIf, + final String swIfName, final WriteContext writeContext) + throws WriteFailedException { + + setInterfaceFlags(id, swIfName, interfaceContext.getIndex(swIfName, writeContext.getMappingContext()), + swIf.isEnabled() + ? (byte) 1 + : (byte) 0); + } + + private void updateInterface(final InstanceIdentifier<Interface> id, + final Interface dataBefore, + final Interface dataAfter, final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("Updating interface:{} to: {}", id, dataAfter); + setInterfaceAttributes(id, dataAfter, dataAfter.getName(), writeContext); + } + + private void setInterfaceFlags(final InstanceIdentifier<Interface> id, final String swIfName, final int swIfIndex, + final byte enabled) throws WriteFailedException { + final CompletionStage<SwInterfaceSetFlagsReply> swInterfaceSetFlagsReplyFuture = + getFutureJVpp().swInterfaceSetFlags( + getSwInterfaceSetFlagsInput(swIfIndex, enabled, (byte) 0 /* deleted */)); + + LOG.debug("Updating interface flags for: {}, index: {}, enabled: {}", swIfName, swIfIndex, enabled); + + getReplyForWrite(swInterfaceSetFlagsReplyFuture.toCompletableFuture(), id); + LOG.debug("Interface flags updated successfully for: {}, index: {}, enabled: {}", + swIfName, swIfIndex, enabled); + } + + private SwInterfaceSetFlags getSwInterfaceSetFlagsInput(final int swIfIndex, final byte enabled, + final byte deleted) { + final SwInterfaceSetFlags swInterfaceSetFlags = new SwInterfaceSetFlags(); + swInterfaceSetFlags.swIfIndex = swIfIndex; + swInterfaceSetFlags.adminUpDown = enabled; + swInterfaceSetFlags.linkUpDown = enabled; + swInterfaceSetFlags.deleted = deleted; + return swInterfaceSetFlags; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/L2Customizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/L2Customizer.java new file mode 100644 index 000000000..cb47725b5 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/L2Customizer.java @@ -0,0 +1,89 @@ +/* + * 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.v3po.interfaces; + +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import javax.annotation.Nonnull; +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.v3po.rev161214.interfaces._interface.L2; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class L2Customizer extends FutureJVppCustomizer implements WriterCustomizer<L2> { + + private static final Logger LOG = LoggerFactory.getLogger(L2Customizer.class); + private final NamingContext interfaceContext; + private final InterconnectionWriteUtils icWriteUtils; + + public L2Customizer(final FutureJVppCore vppApi, final NamingContext interfaceContext, + final NamingContext bridgeDomainContext) { + super(vppApi); + this.interfaceContext = interfaceContext; + this.icWriteUtils = new InterconnectionWriteUtils(vppApi, interfaceContext, bridgeDomainContext); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<L2> id, @Nonnull final L2 dataAfter, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + + final String ifcName = id.firstKeyOf(Interface.class).getName(); + final int swIfc = interfaceContext.getIndex(ifcName, writeContext.getMappingContext()); + setL2(id, swIfc, ifcName, dataAfter, writeContext); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<L2> id, @Nonnull final L2 dataBefore, + @Nonnull final L2 dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + + final String ifcName = id.firstKeyOf(Interface.class).getName(); + final int swIfc = interfaceContext.getIndex(ifcName, writeContext.getMappingContext()); + // No update, again calling set + setL2(id, swIfc, ifcName, dataAfter, writeContext); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<L2> id, @Nonnull final L2 dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final String ifcName = id.firstKeyOf(Interface.class).getName(); + final int swIfc = interfaceContext.getIndex(ifcName, writeContext.getMappingContext()); + deleteL2(id, swIfc, ifcName, dataBefore, writeContext); + } + + private void setL2(final InstanceIdentifier<L2> id, final int swIfIndex, final String ifcName, final L2 l2, + final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("Setting L2 for interface: {}", ifcName); + // Nothing besides interconnection here + icWriteUtils.setInterconnection(id, swIfIndex, ifcName, l2.getInterconnection(), writeContext); + } + + private void deleteL2(final InstanceIdentifier<L2> id, final int swIfIndex, final String ifcName, final L2 l2Before, + final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("Deleting L2 for interface: {}", ifcName); + // Nothing besides interconnection here + icWriteUtils.deleteInterconnection(id, swIfIndex, ifcName, l2Before.getInterconnection(), writeContext); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/LoopbackCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/LoopbackCustomizer.java new file mode 100644 index 000000000..a1b3184ed --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/LoopbackCustomizer.java @@ -0,0 +1,112 @@ +/* + * 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.v3po.interfaces; + +import io.fd.hc2vpp.common.translate.util.AbstractInterfaceTypeCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.MacTranslator; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.CreateLoopback; +import io.fd.vpp.jvpp.core.dto.CreateLoopbackReply; +import io.fd.vpp.jvpp.core.dto.DeleteLoopback; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType; +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.v3po.rev161214.interfaces._interface.Loopback; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class LoopbackCustomizer extends AbstractInterfaceTypeCustomizer<Loopback> + implements MacTranslator, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(LoopbackCustomizer.class); + private final NamingContext interfaceContext; + + public LoopbackCustomizer(final FutureJVppCore vppApi, final NamingContext interfaceContext) { + super(vppApi); + this.interfaceContext = interfaceContext; + } + + @Override + protected Class<? extends InterfaceType> getExpectedInterfaceType() { + return org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.Loopback.class; + } + + @Override + protected final void writeInterface(@Nonnull final InstanceIdentifier<Loopback> id, @Nonnull final Loopback dataAfter, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String ifcName = id.firstKeyOf(Interface.class).getName(); + createLoopback(id, ifcName, dataAfter, writeContext); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Loopback> id, @Nonnull final Loopback dataBefore, + @Nonnull final Loopback dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, + new UnsupportedOperationException("Modification of loopback interface is not supported")); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Loopback> id, @Nonnull final Loopback dataBefore, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String ifcName = id.firstKeyOf(Interface.class).getName(); + + final int index; + try { + index = interfaceContext.getIndex(ifcName, writeContext.getMappingContext()); + } catch (IllegalArgumentException e) { + throw new WriteFailedException.DeleteFailedException(id, e); + } + + deleteLoopback(id, ifcName, index, dataBefore, writeContext); + } + + private void createLoopback(final InstanceIdentifier<Loopback> id, final String swIfName, final Loopback loopback, + final WriteContext writeContext) throws WriteFailedException { + LOG.debug("Setting loopback interface: {}. Loopback: {}", swIfName, loopback); + + final CreateLoopback createLoopback = new CreateLoopback(); + if (loopback.getMac() != null) { + createLoopback.macAddress = parseMac(loopback.getMac().getValue()); + } + final CreateLoopbackReply reply = + getReplyForCreate(getFutureJVpp().createLoopback(createLoopback).toCompletableFuture(), id, loopback); + + LOG.debug("Loopback set successfully for: {}, loopback: {}", swIfName, loopback); + // Add new interface to our interface context + interfaceContext.addName(reply.swIfIndex, swIfName, writeContext.getMappingContext()); + } + + private void deleteLoopback(final InstanceIdentifier<Loopback> id, final String swIfName, final int index, + final Loopback dataBefore, final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("Deleting loopback interface: {}. Loopback: {}", swIfName, dataBefore); + final DeleteLoopback deleteLoopback = new DeleteLoopback(); + deleteLoopback.swIfIndex = index; + getReplyForDelete(getFutureJVpp().deleteLoopback(deleteLoopback).toCompletableFuture(), id); + LOG.debug("Loopback deleted successfully for: {}, loopback: {}", swIfName, dataBefore); + // Remove deleted interface from interface context + interfaceContext.removeName(swIfName, writeContext.getMappingContext()); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ProxyArpCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ProxyArpCustomizer.java new file mode 100644 index 000000000..66f2e3e81 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ProxyArpCustomizer.java @@ -0,0 +1,108 @@ +/* + * 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.v3po.interfaces; + +import com.google.common.net.InetAddresses; +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.ProxyArpAddDel; +import io.fd.vpp.jvpp.core.dto.ProxyArpAddDelReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.net.InetAddress; +import java.util.concurrent.Future; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; +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.v3po.rev161214.interfaces._interface.ProxyArp; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ProxyArpCustomizer extends FutureJVppCustomizer implements WriterCustomizer<ProxyArp>, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(ProxyArpCustomizer.class); + + public ProxyArpCustomizer(final FutureJVppCore vppApi) { + super(vppApi); + } + + @Override + public void writeCurrentAttributes(@Nonnull InstanceIdentifier<ProxyArp> id, @Nonnull ProxyArp dataAfter, + @Nonnull WriteContext writeContext) throws WriteFailedException { + final String swIfName = id.firstKeyOf(Interface.class).getName(); + createProxyArp(getProxyArpRequestFuture(id, swIfName, dataAfter, (byte) 1 /* 1 is add */), id, dataAfter); + } + + @Override + public void updateCurrentAttributes(@Nonnull InstanceIdentifier<ProxyArp> id, @Nonnull ProxyArp dataBefore, + @Nonnull ProxyArp dataAfter, @Nonnull WriteContext writeContext) + throws WriteFailedException.UpdateFailedException { + throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, + new UnsupportedOperationException("ARP proxy update is not supported")); + } + + @Override + public void deleteCurrentAttributes(@Nonnull InstanceIdentifier<ProxyArp> id, @Nonnull ProxyArp dataBefore, + @Nonnull WriteContext writeContext) throws WriteFailedException { + + final String swIfName = id.firstKeyOf(Interface.class).getName(); + deleteProxyArp(getProxyArpRequestFuture(id, swIfName, dataBefore, (byte) 0 /* 0 is delete */), id); + } + + private Future<ProxyArpAddDelReply> getProxyArpRequestFuture(InstanceIdentifier<ProxyArp> id, String swIfName, + ProxyArp proxyArp, byte operation) + throws WriteFailedException { + LOG.debug("Setting Proxy ARP settings for interface: {}", swIfName); + final InetAddress srcAddress = InetAddresses.forString(getv4AddressString(proxyArp.getLowAddr())); + final InetAddress dstAddress = InetAddresses.forString(getv4AddressString(proxyArp.getHighAddr())); + final int vrfId = proxyArp.getVrfId().intValue(); + return getFutureJVpp().proxyArpAddDel( + getProxyArpConfRequest(operation, srcAddress.getAddress(), dstAddress.getAddress(), vrfId)) + .toCompletableFuture(); + } + + private void createProxyArp(final Future<ProxyArpAddDelReply> future, final InstanceIdentifier<ProxyArp> identifier, + final ProxyArp data) + throws WriteFailedException { + final ProxyArpAddDelReply reply = getReplyForCreate(future, identifier, data); + LOG.debug("Proxy ARP setting create successful, with reply context:", reply.context); + } + + private void deleteProxyArp(final Future<ProxyArpAddDelReply> future, final InstanceIdentifier<ProxyArp> identifier) + throws WriteFailedException { + final ProxyArpAddDelReply reply = getReplyForDelete(future, identifier); + LOG.debug("Proxy ARP setting delete successful, with reply context:", reply.context); + } + + private static ProxyArpAddDel getProxyArpConfRequest(final byte isAdd, final byte[] lAddr, final byte[] hAddr, + final int vrfId) { + + final ProxyArpAddDel proxyArpAddDel = new ProxyArpAddDel(); + proxyArpAddDel.isAdd = isAdd; + proxyArpAddDel.lowAddress = lAddr; + proxyArpAddDel.hiAddress = hAddr; + proxyArpAddDel.vrfId = vrfId; + return proxyArpAddDel; + } + + private String getv4AddressString(@Nonnull final Ipv4Address addr) { + return addr.getValue(); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/RewriteCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/RewriteCustomizer.java new file mode 100644 index 000000000..4ad632166 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/RewriteCustomizer.java @@ -0,0 +1,136 @@ +/* + * 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.v3po.interfaces; + +import com.google.common.base.Preconditions; +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.hc2vpp.v3po.util.SubInterfaceUtils; +import io.fd.hc2vpp.common.translate.util.TagRewriteOperation; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.L2InterfaceVlanTagRewrite; +import io.fd.vpp.jvpp.core.dto.L2InterfaceVlanTagRewriteReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.List; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +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.vlan.rev161214._802dot1q; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.l2.Rewrite; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.l2.RewriteBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.tag.rewrite.PushTags; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Writer Customizer responsible for vlan tag rewrite.<br> Sends {@code l2_interface_vlan_tag_rewrite} message to + * VPP.<br> Equivalent of invoking {@code vppctl set interface l2 tag-rewrite} command. + */ +public class RewriteCustomizer extends FutureJVppCustomizer + implements WriterCustomizer<Rewrite>, ByteDataTranslator, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(RewriteCustomizer.class); + private final NamingContext interfaceContext; + + public RewriteCustomizer(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final NamingContext interfaceContext) { + super(futureJVppCore); + this.interfaceContext = Preconditions.checkNotNull(interfaceContext, "interfaceContext should not be null"); + } + + @Override + public void writeCurrentAttributes(final InstanceIdentifier<Rewrite> id, final Rewrite dataAfter, + final WriteContext writeContext) + throws WriteFailedException { + final String subifName = getSubInterfaceName(id); + setTagRewrite(id, subifName, dataAfter, writeContext); + } + + private static String getSubInterfaceName(final InstanceIdentifier<Rewrite> id) { + return SubInterfaceUtils.getSubInterfaceName(id.firstKeyOf(Interface.class).getName(), + Math.toIntExact(id.firstKeyOf(SubInterface.class).getIdentifier())); + } + + private void setTagRewrite(final InstanceIdentifier<Rewrite> id, final String ifname, final Rewrite rewrite, + final WriteContext writeContext) throws WriteFailedException { + final int swIfIndex = interfaceContext.getIndex(ifname, writeContext.getMappingContext()); + LOG.debug("Setting tag rewrite for interface {}(id=): {}", ifname, swIfIndex, rewrite); + + final CompletionStage<L2InterfaceVlanTagRewriteReply> replyCompletionStage = + getFutureJVpp().l2InterfaceVlanTagRewrite(getTagRewriteRequest(swIfIndex, rewrite)); + + getReplyForWrite(replyCompletionStage.toCompletableFuture(), id); + LOG.debug("Tag rewrite for interface {}(id=) set successfully: {}", ifname, swIfIndex, rewrite); + } + + private L2InterfaceVlanTagRewrite getTagRewriteRequest(final int swIfIndex, final Rewrite rewrite) { + final L2InterfaceVlanTagRewrite request = new L2InterfaceVlanTagRewrite(); + request.swIfIndex = swIfIndex; + request.pushDot1Q = booleanToByte(_802dot1q.class == rewrite.getVlanType()); + + final List<PushTags> pushTags = rewrite.getPushTags(); + final Short popTags = rewrite.getPopTags(); + + final int numberOfTagsToPop = popTags == null + ? 0 + : popTags.intValue(); + final int numberOfTagsToPush = pushTags == null + ? 0 + : pushTags.size(); + + request.vtrOp = TagRewriteOperation.get(numberOfTagsToPop, numberOfTagsToPush).ordinal(); + + if (numberOfTagsToPush > 0) { + for (final PushTags tag : pushTags) { + if (tag.getIndex() == 0) { + request.tag1 = tag.getDot1qTag().getVlanId().getValue(); + } else { + request.tag2 = tag.getDot1qTag().getVlanId().getValue(); + } + } + } + + LOG.debug("Generated tag rewrite request: {}", request); + return request; + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Rewrite> id, + @Nonnull final Rewrite dataBefore, + @Nonnull final Rewrite dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String subifName = getSubInterfaceName(id); + setTagRewrite(id, subifName, dataAfter, writeContext); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Rewrite> id, + @Nonnull final Rewrite dataBefore, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String subifName = getSubInterfaceName(id); + + LOG.debug("Disabling tag rewrite for interface {}", subifName); + final Rewrite rewrite = new RewriteBuilder().build(); // rewrite without push and pops will cause delete + setTagRewrite(id, subifName, rewrite, writeContext); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/RoutingCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/RoutingCustomizer.java new file mode 100644 index 000000000..574fcebec --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/RoutingCustomizer.java @@ -0,0 +1,122 @@ +/* + * 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.v3po.interfaces; + +import static com.google.common.base.Preconditions.checkArgument; + +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetTable; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetTableReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +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.v3po.rev161214.interfaces._interface.Routing; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RoutingCustomizer extends FutureJVppCustomizer implements WriterCustomizer<Routing>, JvppReplyConsumer, + ByteDataTranslator { + + private static final Logger LOG = LoggerFactory.getLogger(RoutingCustomizer.class); + private final NamingContext interfaceContext; + + public RoutingCustomizer(final FutureJVppCore vppApi, final NamingContext interfaceContext) { + super(vppApi); + this.interfaceContext = interfaceContext; + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Routing> id, + @Nonnull final Routing dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + + final String ifName = id.firstKeyOf(Interface.class).getName(); + setRouting(id, ifName, dataAfter, writeContext); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Routing> id, + @Nonnull final Routing dataBefore, @Nonnull final Routing dataAfter, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + + final String ifName = id.firstKeyOf(Interface.class).getName(); + setRouting(id, ifName, dataAfter, writeContext); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Routing> id, + @Nonnull final Routing dataBefore, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String ifName = id.firstKeyOf(Interface.class).getName(); + disableRouting(id, ifName, writeContext); + } + + private void setRouting(@Nonnull final InstanceIdentifier<Routing> id, @Nonnull final String name, + @Nonnull final Routing rt, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final int swIfc = interfaceContext.getIndex(name, writeContext.getMappingContext()); + LOG.debug("Setting routing for interface: {}, {}. Routing: {}", name, swIfc, rt); + checkArgument(rt.getIpv4VrfId() != null || rt.getIpv6VrfId() != null, "No vrf-id given"); + + setVrfId(id, swIfc, rt.getIpv4VrfId(), false); + setVrfId(id, swIfc, rt.getIpv6VrfId(), true); + + LOG.debug("Routing set successfully for interface: {}, {}, routing: {}", name, swIfc, rt); + } + + private void setVrfId(final InstanceIdentifier<Routing> id, final int swIfc, final Long vrfId, boolean isIp6) + throws WriteFailedException { + if (vrfId == null) { + return; + } + final CompletionStage<SwInterfaceSetTableReply> cs = getFutureJVpp() + .swInterfaceSetTable(getInterfaceSetTableRequest(swIfc, booleanToByte(isIp6), vrfId.intValue())); + getReplyForWrite(cs.toCompletableFuture(), id); + } + + /** + * In this case, there is no such thing as delete routing,only thing that can be done is to disable it by setting + * default value 0 + */ + private void disableRouting(final InstanceIdentifier<Routing> id, final String name, + final WriteContext writeContext) throws WriteFailedException { + final int swIfc = interfaceContext.getIndex(name, writeContext.getMappingContext()); + LOG.debug("Disabling routing for interface: {}, {}.", name, swIfc); + + getReplyForDelete(getFutureJVpp() + .swInterfaceSetTable(getInterfaceSetTableRequest(swIfc, (byte) 0, 0)).toCompletableFuture(), id); + LOG.debug("Routing for interface: {}, {} successfully disabled", name, swIfc); + + } + + private SwInterfaceSetTable getInterfaceSetTableRequest(final int swIfc, final byte isIpv6, final int vrfId) { + final SwInterfaceSetTable swInterfaceSetTable = new SwInterfaceSetTable(); + swInterfaceSetTable.isIpv6 = isIpv6; + swInterfaceSetTable.swIfIndex = swIfc; + swInterfaceSetTable.vrfId = vrfId; + return swInterfaceSetTable; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/SubInterfaceCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/SubInterfaceCustomizer.java new file mode 100644 index 000000000..c39cfa6ec --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/SubInterfaceCustomizer.java @@ -0,0 +1,193 @@ +/* + * 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.v3po.interfaces; + +import static com.google.common.base.Preconditions.checkState; +import static io.fd.hc2vpp.v3po.util.SubInterfaceUtils.getNumberOfTags; +import static io.fd.hc2vpp.v3po.util.SubInterfaceUtils.getSubInterfaceName; + +import com.google.common.base.Preconditions; +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.CreateSubif; +import io.fd.vpp.jvpp.core.dto.CreateSubifReply; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlags; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.CVlan; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.Dot1qVlanId; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.SVlan; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.dot1q.tag.or.any.Dot1qTag; +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.vlan.rev161214._802dot1ad; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.match.attributes.MatchType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.match.attributes.match.type.Default; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.match.attributes.match.type.vlan.tagged.VlanTagged; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.tags.Tag; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Writer Customizer responsible for sub interface creation.<br> Sends {@code create_subif} message to VPP.<br> + * Equivalent of invoking {@code vppclt create subif} command. + */ +public class SubInterfaceCustomizer extends FutureJVppCustomizer + implements ListWriterCustomizer<SubInterface, SubInterfaceKey>, ByteDataTranslator, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceCustomizer.class); + private final NamingContext interfaceContext; + + public SubInterfaceCustomizer(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final NamingContext interfaceContext) { + super(futureJVppCore); + this.interfaceContext = Preconditions.checkNotNull(interfaceContext, "interfaceContext should not be null"); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<SubInterface> id, + @Nonnull final SubInterface dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String superIfName = id.firstKeyOf(Interface.class).getName(); + createSubInterface(id, superIfName, dataAfter, writeContext); + } + + private void createSubInterface(final InstanceIdentifier<SubInterface> id, @Nonnull final String superIfName, + @Nonnull final SubInterface subInterface, final WriteContext writeContext) + throws WriteFailedException { + final int superIfIndex = interfaceContext.getIndex(superIfName, writeContext.getMappingContext()); + final CompletionStage<CreateSubifReply> createSubifReplyCompletionStage = + getFutureJVpp().createSubif(getCreateSubifRequest(subInterface, superIfIndex)); + + final CreateSubifReply reply = + getReplyForCreate(createSubifReplyCompletionStage.toCompletableFuture(), id, subInterface); + + setInterfaceState(id, reply.swIfIndex, booleanToByte(subInterface.isEnabled())); + interfaceContext.addName(reply.swIfIndex, + getSubInterfaceName(superIfName, Math.toIntExact(subInterface.getIdentifier())), + writeContext.getMappingContext()); + LOG.debug("Sub interface created successfully for: {}, subInterface: {}", superIfName, subInterface); + } + + private CreateSubif getCreateSubifRequest(@Nonnull final SubInterface subInterface, final int swIfIndex) { + // TODO HONEYCOMB-183 add validation + CreateSubif request = new CreateSubif(); + request.subId = Math.toIntExact(subInterface.getIdentifier().intValue()); + request.swIfIndex = swIfIndex; + + final int numberOfTags = getNumberOfTags(subInterface.getTags()); + switch (numberOfTags) { + case 0: + request.noTags = 1; + break; + case 1: + request.oneTag = 1; + break; + case 2: + request.twoTags = 1; + break; + } + request.dot1Ad = booleanToByte(_802dot1ad.class == subInterface.getVlanType()); + + // TODO HONEYCOMB-183 match should be mandatory + final MatchType matchType = subInterface.getMatch().getMatchType(); + request.exactMatch = + booleanToByte(matchType instanceof VlanTagged && ((VlanTagged) matchType).isMatchExactTags()); + request.defaultSub = booleanToByte(matchType instanceof Default); + + if (numberOfTags > 0) { + for (final Tag tag : subInterface.getTags().getTag()) { + if (tag.getIndex() == 0) { + setOuterTag(request, tag); + } else if (tag.getIndex() == 1) { + setInnerTag(request, tag); + } + } + } + return request; + } + + private void setOuterTag(final CreateSubif request, final Tag outerTag) { + checkState(SVlan.class == outerTag.getDot1qTag().getTagType(), "Service Tag expected at index 0"); + final Dot1qTag.VlanId vlanId = outerTag.getDot1qTag().getVlanId(); + + request.outerVlanId = dot1qVlanIdToShort(vlanId.getDot1qVlanId()); + request.outerVlanIdAny = booleanToByte(Dot1qTag.VlanId.Enumeration.Any.equals(vlanId.getEnumeration())); + } + + private void setInnerTag(final CreateSubif request, final Tag innerTag) { + checkState(CVlan.class == innerTag.getDot1qTag().getTagType(), "Customer Tag expected at index 1"); + final Dot1qTag.VlanId vlanId = innerTag.getDot1qTag().getVlanId(); + + request.innerVlanId = dot1qVlanIdToShort(vlanId.getDot1qVlanId()); + request.innerVlanIdAny = booleanToByte(Dot1qTag.VlanId.Enumeration.Any.equals(vlanId.getEnumeration())); + } + + private static short dot1qVlanIdToShort(@Nullable Dot1qVlanId dot1qVlanId) { + if (dot1qVlanId == null) { + return 0; // tell VPP that optional argument is missing + } else { + return dot1qVlanId.getValue().shortValue(); + } + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<SubInterface> id, + @Nonnull final SubInterface dataBefore, @Nonnull final SubInterface dataAfter, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String subIfaceName = getSubInterfaceName(id.firstKeyOf(Interface.class).getName(), + Math.toIntExact(dataAfter.getIdentifier())); + setInterfaceState(id, interfaceContext.getIndex(subIfaceName, writeContext.getMappingContext()), + booleanToByte(dataAfter.isEnabled())); + } + + private void setInterfaceState(final InstanceIdentifier<SubInterface> id, final int swIfIndex, final byte enabled) + throws WriteFailedException { + final SwInterfaceSetFlags swInterfaceSetFlags = new SwInterfaceSetFlags(); + swInterfaceSetFlags.swIfIndex = swIfIndex; + swInterfaceSetFlags.adminUpDown = enabled; + + final CompletionStage<SwInterfaceSetFlagsReply> swInterfaceSetFlagsReplyFuture = + getFutureJVpp().swInterfaceSetFlags(swInterfaceSetFlags); + + LOG.debug("Updating interface state for interface if={}, enabled: {}", swIfIndex, enabled); + + SwInterfaceSetFlagsReply reply = + getReplyForWrite(swInterfaceSetFlagsReplyFuture.toCompletableFuture(), id); + LOG.debug("Interface state updated successfully for interface index: {}, enabled: {}, ctxId: {}", + swIfIndex, enabled, reply.context); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<SubInterface> id, + @Nonnull final SubInterface dataBefore, + @Nonnull final WriteContext writeContext) + throws WriteFailedException.DeleteFailedException { + throw new UnsupportedOperationException("Sub interface delete is not supported"); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/SubInterfaceL2Customizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/SubInterfaceL2Customizer.java new file mode 100644 index 000000000..4a3e4435d --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/SubInterfaceL2Customizer.java @@ -0,0 +1,100 @@ +/* + * 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.v3po.interfaces; + +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.hc2vpp.v3po.util.SubInterfaceUtils; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.L2; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Customizer for writing vlan sub interface l2 configuration + */ +public class SubInterfaceL2Customizer extends FutureJVppCustomizer implements WriterCustomizer<L2> { + + private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceL2Customizer.class); + private final NamingContext interfaceContext; + private final InterconnectionWriteUtils icWriterUtils; + + public SubInterfaceL2Customizer(final FutureJVppCore vppApi, final NamingContext interfaceContext, + final NamingContext bridgeDomainContext) { + super(vppApi); + this.interfaceContext = interfaceContext; + this.icWriterUtils = new InterconnectionWriteUtils(vppApi, interfaceContext, bridgeDomainContext); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<L2> id, @Nonnull final L2 dataAfter, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String subInterfaceName = getSubInterfaceName(id); + final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext()); + setL2(id, subInterfaceIndex, subInterfaceName, dataAfter, writeContext); + } + + private String getSubInterfaceName(@Nonnull final InstanceIdentifier<L2> id) { + final InterfaceKey parentInterfacekey = id.firstKeyOf(Interface.class); + final SubInterfaceKey subInterfacekey = id.firstKeyOf(SubInterface.class); + return SubInterfaceUtils + .getSubInterfaceName(parentInterfacekey.getName(), subInterfacekey.getIdentifier().intValue()); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<L2> id, @Nonnull final L2 dataBefore, + @Nonnull final L2 dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + + final String subInterfaceName = getSubInterfaceName(id); + final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext()); + // Setting L2 to new values + setL2(id, subInterfaceIndex, subInterfaceName, dataAfter, writeContext); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<L2> id, @Nonnull final L2 dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final String subInterfaceName = getSubInterfaceName(id); + final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext()); + deleteL2(id, subInterfaceIndex, subInterfaceName, dataBefore, writeContext); + } + + private void setL2(final InstanceIdentifier<L2> id, final int swIfIndex, final String ifcName, final L2 l2, + final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("Setting L2 for sub-interface: {}", ifcName); + icWriterUtils.setInterconnection(id, swIfIndex, ifcName, l2.getInterconnection(), writeContext); + } + + private void deleteL2(final InstanceIdentifier<L2> id, final int swIfIndex, final String ifcName, final L2 l2Before, + final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("Deleting L2 for sub-interface: {}", ifcName); + icWriterUtils.deleteInterconnection(id, swIfIndex, ifcName, l2Before.getInterconnection(), writeContext); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/TapCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/TapCustomizer.java new file mode 100644 index 000000000..220fb95e8 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/TapCustomizer.java @@ -0,0 +1,182 @@ +/* + * 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.v3po.interfaces; + +import io.fd.hc2vpp.common.translate.util.AbstractInterfaceTypeCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.MacTranslator; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.TapConnect; +import io.fd.vpp.jvpp.core.dto.TapConnectReply; +import io.fd.vpp.jvpp.core.dto.TapDelete; +import io.fd.vpp.jvpp.core.dto.TapDeleteReply; +import io.fd.vpp.jvpp.core.dto.TapModify; +import io.fd.vpp.jvpp.core.dto.TapModifyReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Tap; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TapCustomizer extends AbstractInterfaceTypeCustomizer<Tap> implements MacTranslator, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(TapCustomizer.class); + private final NamingContext interfaceContext; + + public TapCustomizer(final FutureJVppCore vppApi, final NamingContext interfaceContext) { + super(vppApi); + this.interfaceContext = interfaceContext; + } + + @Override + protected Class<? extends InterfaceType> getExpectedInterfaceType() { + return org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.Tap.class; + } + + @Override + protected final void writeInterface(@Nonnull final InstanceIdentifier<Tap> id, @Nonnull final Tap dataAfter, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String ifcName = id.firstKeyOf(Interface.class).getName(); + createTap(id, ifcName, dataAfter, writeContext); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Tap> id, @Nonnull final Tap dataBefore, + @Nonnull final Tap dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String ifcName = id.firstKeyOf(Interface.class).getName(); + + final int index; + try { + index = interfaceContext.getIndex(ifcName, writeContext.getMappingContext()); + } catch (IllegalArgumentException e) { + throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, e); + } + + modifyTap(id, ifcName, index, dataBefore, dataAfter); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Tap> id, @Nonnull final Tap dataBefore, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String ifcName = id.firstKeyOf(Interface.class).getName(); + + final int index; + try { + index = interfaceContext.getIndex(ifcName, writeContext.getMappingContext()); + } catch (IllegalArgumentException e) { + throw new WriteFailedException.DeleteFailedException(id, e); + } + + deleteTap(id, ifcName, index, dataBefore, writeContext); + } + + private void createTap(final InstanceIdentifier<Tap> id, final String swIfName, final Tap tap, + final WriteContext writeContext) throws WriteFailedException { + LOG.debug("Setting tap interface: {}. Tap: {}", swIfName, tap); + final CompletionStage<TapConnectReply> tapConnectFuture = getFutureJVpp() + .tapConnect(getTapConnectRequest(tap.getTapName(), tap.getMac(), tap.getDeviceInstance())); + final TapConnectReply reply = getReplyForCreate(tapConnectFuture.toCompletableFuture(), id, tap); + LOG.debug("Tap set successfully for: {}, tap: {}", swIfName, tap); + // Add new interface to our interface context + interfaceContext.addName(reply.swIfIndex, swIfName, writeContext.getMappingContext()); + } + + private void modifyTap(final InstanceIdentifier<Tap> id, final String swIfName, final int index, + final Tap tapBefore, final Tap tapAfter) throws WriteFailedException { + LOG.debug("Modifying tap interface: {}. Tap: {}", swIfName, tapAfter); + final CompletionStage<TapModifyReply> vxlanAddDelTunnelReplyCompletionStage = + getFutureJVpp() + .tapModify(getTapModifyRequest(tapAfter.getTapName(), index, tapAfter.getMac(), + tapAfter.getDeviceInstance())); + getReplyForUpdate(vxlanAddDelTunnelReplyCompletionStage.toCompletableFuture(), id, tapBefore, tapAfter); + LOG.debug("Tap modified successfully for: {}, tap: {}", swIfName, tapAfter); + } + + private void deleteTap(final InstanceIdentifier<Tap> id, final String swIfName, final int index, + final Tap dataBefore, final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("Deleting tap interface: {}. Tap: {}", swIfName, dataBefore); + final CompletionStage<TapDeleteReply> vxlanAddDelTunnelReplyCompletionStage = + getFutureJVpp().tapDelete(getTapDeleteRequest(index)); + getReplyForDelete(vxlanAddDelTunnelReplyCompletionStage.toCompletableFuture(), id); + LOG.debug("Tap deleted successfully for: {}, tap: {}", swIfName, dataBefore); + // Remove deleted interface from interface context + interfaceContext.removeName(swIfName, writeContext.getMappingContext()); + } + + private TapConnect getTapConnectRequest(final String tapName, final PhysAddress mac, final Long deviceInstance) { + final TapConnect tapConnect = new TapConnect(); + tapConnect.tapName = tapName.getBytes(); + + if (mac == null) { + tapConnect.useRandomMac = 1; + tapConnect.macAddress = new byte[6]; + } else { + tapConnect.useRandomMac = 0; + tapConnect.macAddress = parseMac(mac.getValue()); + } + + if (deviceInstance == null) { + tapConnect.renumber = 0; + } else { + tapConnect.renumber = 1; + tapConnect.customDevInstance = Math.toIntExact(deviceInstance); + } + + return tapConnect; + } + + private TapModify getTapModifyRequest(final String tapName, final int swIndex, final PhysAddress mac, + final Long deviceInstance) { + final TapModify tapConnect = new TapModify(); + tapConnect.tapName = tapName.getBytes(); + tapConnect.swIfIndex = swIndex; + + if (mac == null) { + tapConnect.useRandomMac = 1; + tapConnect.macAddress = new byte[6]; + } else { + tapConnect.useRandomMac = 0; + tapConnect.macAddress = parseMac(mac.getValue()); + } + + if (deviceInstance == null) { + tapConnect.renumber = 0; + } else { + tapConnect.renumber = 1; + tapConnect.customDevInstance = Math.toIntExact(deviceInstance); + } + + return tapConnect; + } + + private TapDelete getTapDeleteRequest(final int swIndex) { + final TapDelete tapConnect = new TapDelete(); + tapConnect.swIfIndex = swIndex; + return tapConnect; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/VhostUserCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/VhostUserCustomizer.java new file mode 100644 index 000000000..dc8fade2b --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/VhostUserCustomizer.java @@ -0,0 +1,157 @@ +/* + * 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.v3po.interfaces; + +import com.google.common.base.Preconditions; +import io.fd.hc2vpp.common.translate.util.AbstractInterfaceTypeCustomizer; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.CreateVhostUserIf; +import io.fd.vpp.jvpp.core.dto.CreateVhostUserIfReply; +import io.fd.vpp.jvpp.core.dto.DeleteVhostUserIf; +import io.fd.vpp.jvpp.core.dto.DeleteVhostUserIfReply; +import io.fd.vpp.jvpp.core.dto.ModifyVhostUserIf; +import io.fd.vpp.jvpp.core.dto.ModifyVhostUserIfReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType; +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.v3po.rev161214.VhostUserRole; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.VhostUser; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Writer Customizer responsible for passing vhost user interface CRD operations to VPP + */ +public class VhostUserCustomizer extends AbstractInterfaceTypeCustomizer<VhostUser> + implements ByteDataTranslator, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(VhostUserCustomizer.class); + private final NamingContext interfaceContext; + + public VhostUserCustomizer(@Nonnull final FutureJVppCore vppApi, @Nonnull final NamingContext interfaceContext) { + super(vppApi); + this.interfaceContext = Preconditions.checkNotNull(interfaceContext, "interfaceContext should not be null"); + } + + @Override + protected Class<? extends InterfaceType> getExpectedInterfaceType() { + return org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VhostUser.class; + } + + @Override + protected final void writeInterface(@Nonnull final InstanceIdentifier<VhostUser> id, + @Nonnull final VhostUser dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String swIfName = id.firstKeyOf(Interface.class).getName(); + createVhostUserIf(id, swIfName, dataAfter, writeContext); + } + + private void createVhostUserIf(final InstanceIdentifier<VhostUser> id, final String swIfName, + final VhostUser vhostUser, final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("Creating vhost user interface: name={}, vhostUser={}", swIfName, vhostUser); + + final CompletionStage<CreateVhostUserIfReply> createVhostUserIfReplyCompletionStage = + getFutureJVpp().createVhostUserIf(getCreateVhostUserIfRequest(vhostUser)); + final CreateVhostUserIfReply reply = + getReplyForCreate(createVhostUserIfReplyCompletionStage.toCompletableFuture(), id, vhostUser); + LOG.debug("Vhost user interface created successfully for: {}, vhostUser: {}", swIfName, vhostUser); + // Add new interface to our interface context + interfaceContext.addName(reply.swIfIndex, swIfName, writeContext.getMappingContext()); + } + + private CreateVhostUserIf getCreateVhostUserIfRequest(final VhostUser vhostUser) { + CreateVhostUserIf request = new CreateVhostUserIf(); + request.isServer = booleanToByte(VhostUserRole.Server.equals(vhostUser.getRole())); + request.sockFilename = vhostUser.getSocket().getBytes(); + // TODO HONEYCOMB-177 expose device instance attribute just like for TAP + request.renumber = 0; + request.customDevInstance = 0; + request.useCustomMac = 0; + request.macAddress = new byte[]{}; + return request; + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<VhostUser> id, + @Nonnull final VhostUser dataBefore, @Nonnull final VhostUser dataAfter, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String swIfName = id.firstKeyOf(Interface.class).getName(); + modifyVhostUserIf(id, swIfName, dataBefore, dataAfter, writeContext); + } + + private void modifyVhostUserIf(final InstanceIdentifier<VhostUser> id, final String swIfName, + final VhostUser vhostUserBefore, final VhostUser vhostUserAfter, + final WriteContext writeContext) throws WriteFailedException { + LOG.debug("Updating vhost user interface: name={}, vhostUser={}", swIfName, vhostUserAfter); + final CompletionStage<ModifyVhostUserIfReply> modifyVhostUserIfReplyCompletionStage = + getFutureJVpp() + .modifyVhostUserIf(getModifyVhostUserIfRequest(vhostUserAfter, + interfaceContext.getIndex(swIfName, writeContext.getMappingContext()))); + + getReplyForUpdate(modifyVhostUserIfReplyCompletionStage.toCompletableFuture(), id, vhostUserBefore, + vhostUserAfter); + LOG.debug("Vhost user interface updated successfully for: {}, vhostUser: {}", swIfName, vhostUserAfter); + } + + private ModifyVhostUserIf getModifyVhostUserIfRequest(final VhostUser vhostUser, final int swIfIndex) { + ModifyVhostUserIf request = new ModifyVhostUserIf(); + request.isServer = booleanToByte(VhostUserRole.Server.equals(vhostUser.getRole())); + request.sockFilename = vhostUser.getSocket().getBytes(); + // TODO HONEYCOMB-177 + request.renumber = 0; + request.customDevInstance = 0; + request.swIfIndex = swIfIndex; + return request; + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<VhostUser> id, + @Nonnull final VhostUser dataBefore, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String swIfName = id.firstKeyOf(Interface.class).getName(); + deleteVhostUserIf(id, swIfName, dataBefore, writeContext); + } + + private void deleteVhostUserIf(final InstanceIdentifier<VhostUser> id, final String swIfName, + final VhostUser vhostUser, final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("Deleting vhost user interface: name={}, vhostUser={}", swIfName, vhostUser); + final CompletionStage<DeleteVhostUserIfReply> deleteVhostUserIfReplyCompletionStage = + getFutureJVpp().deleteVhostUserIf(getDeleteVhostUserIfRequest( + interfaceContext.getIndex(swIfName, writeContext.getMappingContext()))); + + getReplyForDelete(deleteVhostUserIfReplyCompletionStage.toCompletableFuture(), id); + LOG.debug("Vhost user interface deleted successfully for: {}, vhostUser: {}", swIfName, vhostUser); + // Remove interface from our interface context + interfaceContext.removeName(swIfName, writeContext.getMappingContext()); + } + + private DeleteVhostUserIf getDeleteVhostUserIfRequest(final int swIfIndex) { + DeleteVhostUserIf request = new DeleteVhostUserIf(); + request.swIfIndex = swIfIndex; + return request; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/VxlanCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/VxlanCustomizer.java new file mode 100644 index 000000000..ec2193476 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/VxlanCustomizer.java @@ -0,0 +1,192 @@ +/* + * 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.v3po.interfaces; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.net.InetAddresses; +import io.fd.hc2vpp.v3po.DisabledInterfacesManager; +import io.fd.hc2vpp.common.translate.util.AbstractInterfaceTypeCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.VxlanAddDelTunnel; +import io.fd.vpp.jvpp.core.dto.VxlanAddDelTunnelReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.net.InetAddress; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType; +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.v3po.rev161214.VxlanTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Vxlan; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class VxlanCustomizer extends AbstractInterfaceTypeCustomizer<Vxlan> implements JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(VxlanCustomizer.class); + + private final NamingContext interfaceNamingContext; + private final DisabledInterfacesManager interfaceDisableContext; + + public VxlanCustomizer(@Nonnull final FutureJVppCore vppApi, + @Nonnull final NamingContext interfaceNamingContext, + @Nonnull final DisabledInterfacesManager interfaceDisableContext) { + super(vppApi); + this.interfaceNamingContext = interfaceNamingContext; + this.interfaceDisableContext = interfaceDisableContext; + } + + @Override + protected Class<? extends InterfaceType> getExpectedInterfaceType() { + return VxlanTunnel.class; + } + + @Override + protected final void writeInterface(@Nonnull final InstanceIdentifier<Vxlan> id, @Nonnull final Vxlan dataAfter, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String swIfName = id.firstKeyOf(Interface.class).getName(); + createVxlanTunnel(id, swIfName, dataAfter, writeContext); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Vxlan> id, @Nonnull final Vxlan dataBefore, + @Nonnull final Vxlan dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException.UpdateFailedException { + throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, + new UnsupportedOperationException("Vxlan tunnel update is not supported")); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Vxlan> id, @Nonnull final Vxlan dataBefore, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String swIfName = id.firstKeyOf(Interface.class).getName(); + deleteVxlanTunnel(id, swIfName, dataBefore, writeContext); + } + + private void createVxlanTunnel(final InstanceIdentifier<Vxlan> id, final String swIfName, final Vxlan vxlan, + final WriteContext writeContext) + throws WriteFailedException { + final byte isIpv6 = (byte) (isIpv6(vxlan) + ? 1 + : 0); + final InetAddress srcAddress = InetAddresses.forString(getAddressString(vxlan.getSrc())); + final InetAddress dstAddress = InetAddresses.forString(getAddressString(vxlan.getDst())); + + int encapVrfId = vxlan.getEncapVrfId().intValue(); + int vni = vxlan.getVni().getValue().intValue(); + + LOG.debug("Setting vxlan tunnel for interface: {}. Vxlan: {}", swIfName, vxlan); + final CompletionStage<VxlanAddDelTunnelReply> vxlanAddDelTunnelReplyCompletionStage = + getFutureJVpp().vxlanAddDelTunnel(getVxlanTunnelRequest((byte) 1 /* is add */, srcAddress.getAddress(), + dstAddress.getAddress(), encapVrfId, -1, vni, isIpv6)); + + final VxlanAddDelTunnelReply reply = + getReplyForCreate(vxlanAddDelTunnelReplyCompletionStage.toCompletableFuture(), id, vxlan); + LOG.debug("Vxlan tunnel set successfully for: {}, vxlan: {}", swIfName, vxlan); + if (interfaceNamingContext.containsName(reply.swIfIndex, writeContext.getMappingContext())) { + // VPP keeps vxlan tunnels present even after they are delete(reserving ID for next tunnel) + // This may cause inconsistencies in mapping context when configuring tunnels like this: + // 1. Add tunnel 2. Delete tunnel 3. Read interfaces (reserved mapping e.g. vxlan_tunnel0 -> 6 + // will get into mapping context) 4. Add tunnel (this will add another mapping with the same + // reserved ID and context is invalid) + // That's why a check has to be performed here removing mapping vxlan_tunnel0 -> 6 mapping and storing + // new name for that ID + final String formerName = interfaceNamingContext.getName(reply.swIfIndex, writeContext.getMappingContext()); + LOG.debug("Removing updated mapping of a vxlan tunnel, id: {}, former name: {}, new name: {}", + reply.swIfIndex, formerName, swIfName); + interfaceNamingContext.removeName(formerName, writeContext.getMappingContext()); + + } + + // Removing disability of an interface in case a vxlan tunnel formerly deleted is being reused in VPP + // further details in above comment + if (interfaceDisableContext.isInterfaceDisabled(reply.swIfIndex, writeContext.getMappingContext())) { + LOG.debug("Removing disability of vxlan tunnel, id: {}, name: {}", reply.swIfIndex, swIfName); + interfaceDisableContext.removeDisabledInterface(reply.swIfIndex, writeContext.getMappingContext()); + } + + // Add new interface to our interface context + interfaceNamingContext.addName(reply.swIfIndex, swIfName, writeContext.getMappingContext()); + } + + private boolean isIpv6(final Vxlan vxlan) { + if (vxlan.getSrc().getIpv4Address() == null) { + checkArgument(vxlan.getDst().getIpv4Address() == null, "Inconsistent ip addresses: %s, %s", vxlan.getSrc(), + vxlan.getDst()); + return true; + } else { + checkArgument(vxlan.getDst().getIpv6Address() == null, "Inconsistent ip addresses: %s, %s", vxlan.getSrc(), + vxlan.getDst()); + return false; + } + } + + private String getAddressString(final IpAddress addr) { + return addr.getIpv4Address() == null + ? addr.getIpv6Address().getValue() + : addr.getIpv4Address().getValue(); + } + + private void deleteVxlanTunnel(final InstanceIdentifier<Vxlan> id, final String swIfName, final Vxlan vxlan, + final WriteContext writeContext) throws WriteFailedException { + final byte isIpv6 = (byte) (isIpv6(vxlan) + ? 1 + : 0); + final InetAddress srcAddress = InetAddresses.forString(getAddressString(vxlan.getSrc())); + final InetAddress dstAddress = InetAddresses.forString(getAddressString(vxlan.getDst())); + + int encapVrfId = vxlan.getEncapVrfId().intValue(); + int vni = vxlan.getVni().getValue().intValue(); + + LOG.debug("Deleting vxlan tunnel for interface: {}. Vxlan: {}", swIfName, vxlan); + final CompletionStage<VxlanAddDelTunnelReply> vxlanAddDelTunnelReplyCompletionStage = + getFutureJVpp().vxlanAddDelTunnel(getVxlanTunnelRequest((byte) 0 /* is add */, srcAddress.getAddress(), + dstAddress.getAddress(), encapVrfId, -1, vni, isIpv6)); + + getReplyForDelete(vxlanAddDelTunnelReplyCompletionStage.toCompletableFuture(), id); + LOG.debug("Vxlan tunnel deleted successfully for: {}, vxlan: {}", swIfName, vxlan); + + final int index = interfaceNamingContext.getIndex(swIfName, writeContext.getMappingContext()); + // Mark this interface as disabled to not include it in operational reads + // because VPP will keep the interface there + LOG.debug("Marking vxlan tunnel as disabled, id: {}, name: {}", index, swIfName); + interfaceDisableContext.disableInterface(index, writeContext.getMappingContext()); + // Remove interface from our interface naming context + interfaceNamingContext.removeName(swIfName, writeContext.getMappingContext()); + } + + private static VxlanAddDelTunnel getVxlanTunnelRequest(final byte isAdd, final byte[] srcAddr, final byte[] dstAddr, + final int encapVrfId, + final int decapNextIndex, final int vni, final byte isIpv6) { + final VxlanAddDelTunnel vxlanAddDelTunnel = new VxlanAddDelTunnel(); + vxlanAddDelTunnel.isAdd = isAdd; + vxlanAddDelTunnel.srcAddress = srcAddr; + vxlanAddDelTunnel.dstAddress = dstAddr; + vxlanAddDelTunnel.encapVrfId = encapVrfId; + vxlanAddDelTunnel.vni = vni; + vxlanAddDelTunnel.decapNextIndex = decapNextIndex; + vxlanAddDelTunnel.isIpv6 = isIpv6; + return vxlanAddDelTunnel; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/VxlanGpeCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/VxlanGpeCustomizer.java new file mode 100644 index 000000000..59ec958f2 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/VxlanGpeCustomizer.java @@ -0,0 +1,194 @@ +/* + * 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.v3po.interfaces; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.net.InetAddresses; +import io.fd.hc2vpp.v3po.DisabledInterfacesManager; +import io.fd.hc2vpp.common.translate.util.AbstractInterfaceTypeCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.VxlanGpeAddDelTunnel; +import io.fd.vpp.jvpp.core.dto.VxlanGpeAddDelTunnelReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.net.InetAddress; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType; +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.v3po.rev161214.VxlanGpeTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.VxlanGpe; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class VxlanGpeCustomizer extends AbstractInterfaceTypeCustomizer<VxlanGpe> implements JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(VxlanGpeCustomizer.class); + private final NamingContext interfaceNamingContext; + private final DisabledInterfacesManager interfaceDisableContext; + + public VxlanGpeCustomizer(@Nonnull final FutureJVppCore vppApi, + @Nonnull final NamingContext interfaceNamingContext, + @Nonnull final DisabledInterfacesManager interfaceDisableContext) { + super(vppApi); + this.interfaceNamingContext = interfaceNamingContext; + this.interfaceDisableContext = interfaceDisableContext; + } + + @Override + protected Class<? extends InterfaceType> getExpectedInterfaceType() { + return VxlanGpeTunnel.class; + } + + @Override + protected final void writeInterface(@Nonnull final InstanceIdentifier<VxlanGpe> id, + @Nonnull final VxlanGpe dataAfter, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String swIfName = id.firstKeyOf(Interface.class).getName(); + createVxlanGpeTunnel(id, swIfName, dataAfter, writeContext); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<VxlanGpe> id, + @Nonnull final VxlanGpe dataBefore, + @Nonnull final VxlanGpe dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException.UpdateFailedException { + throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, + new UnsupportedOperationException("VxlanGpe tunnel update is not supported")); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<VxlanGpe> id, + @Nonnull final VxlanGpe dataBefore, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String swIfName = id.firstKeyOf(Interface.class).getName(); + deleteVxlanGpeTunnel(id, swIfName, dataBefore, writeContext); + } + + private void createVxlanGpeTunnel(final InstanceIdentifier<VxlanGpe> id, final String swIfName, + final VxlanGpe vxlanGpe, final WriteContext writeContext) + throws WriteFailedException { + final byte isIpv6 = (byte) (isIpv6(vxlanGpe) + ? 1 + : 0); + final InetAddress Local = InetAddresses.forString(getAddressString(vxlanGpe.getLocal())); + final InetAddress Remote = InetAddresses.forString(getAddressString(vxlanGpe.getRemote())); + + int vni = vxlanGpe.getVni().getValue().intValue(); + byte protocol = (byte) vxlanGpe.getNextProtocol().getIntValue(); + int encapVrfId = vxlanGpe.getEncapVrfId().intValue(); + int decapVrfId = vxlanGpe.getDecapVrfId().intValue(); + + LOG.debug("Setting VxlanGpe tunnel for interface: {}. VxlanGpe: {}", swIfName, vxlanGpe); + final CompletionStage<VxlanGpeAddDelTunnelReply> VxlanGpeAddDelTunnelReplyCompletionStage = + getFutureJVpp().vxlanGpeAddDelTunnel(getVxlanGpeTunnelRequest((byte) 1 /* is add */, Local.getAddress(), + Remote.getAddress(), vni, protocol, encapVrfId, decapVrfId, isIpv6)); + + final VxlanGpeAddDelTunnelReply reply = + getReplyForCreate(VxlanGpeAddDelTunnelReplyCompletionStage.toCompletableFuture(), id, vxlanGpe); + LOG.debug("VxlanGpe tunnel set successfully for: {}, VxlanGpe: {}", swIfName, vxlanGpe); + if (interfaceNamingContext.containsName(reply.swIfIndex, writeContext.getMappingContext())) { + final String formerName = interfaceNamingContext.getName(reply.swIfIndex, writeContext.getMappingContext()); + LOG.debug("Removing updated mapping of a vxlan-gpe tunnel, id: {}, former name: {}, new name: {}", + reply.swIfIndex, formerName, swIfName); + interfaceNamingContext.removeName(formerName, writeContext.getMappingContext()); + } + + // Removing disability of an interface in case a vxlan-gpe tunnel formerly deleted is being reused in VPP + // further details in above comment + if (interfaceDisableContext.isInterfaceDisabled(reply.swIfIndex, writeContext.getMappingContext())) { + LOG.debug("Removing disability of vxlan tunnel, id: {}, name: {}", reply.swIfIndex, swIfName); + interfaceDisableContext.removeDisabledInterface(reply.swIfIndex, writeContext.getMappingContext()); + } + + // Add new interface to our interface context + interfaceNamingContext.addName(reply.swIfIndex, swIfName, writeContext.getMappingContext()); + } + + private boolean isIpv6(final VxlanGpe vxlanGpe) { + if (vxlanGpe.getLocal().getIpv4Address() == null) { + checkArgument(vxlanGpe.getRemote().getIpv4Address() == null, "Inconsistent ip addresses: %s, %s", + vxlanGpe.getLocal(), + vxlanGpe.getRemote()); + return true; + } else { + checkArgument(vxlanGpe.getRemote().getIpv6Address() == null, "Inconsistent ip addresses: %s, %s", + vxlanGpe.getLocal(), + vxlanGpe.getRemote()); + return false; + } + } + + private String getAddressString(final IpAddress addr) { + return addr.getIpv4Address() == null + ? addr.getIpv6Address().getValue() + : addr.getIpv4Address().getValue(); + } + + private void deleteVxlanGpeTunnel(final InstanceIdentifier<VxlanGpe> id, final String swIfName, + final VxlanGpe vxlanGpe, final WriteContext writeContext) + throws WriteFailedException { + final byte isIpv6 = (byte) (isIpv6(vxlanGpe) + ? 1 + : 0); + final InetAddress local = InetAddresses.forString(getAddressString(vxlanGpe.getLocal())); + final InetAddress remote = InetAddresses.forString(getAddressString(vxlanGpe.getRemote())); + + int vni = vxlanGpe.getVni().getValue().intValue(); + byte protocol = (byte) vxlanGpe.getNextProtocol().getIntValue(); + int encapVrfId = vxlanGpe.getEncapVrfId().intValue(); + int decapVrfId = vxlanGpe.getDecapVrfId().intValue(); + + LOG.debug("Deleting VxlanGpe tunnel for interface: {}. VxlanGpe: {}", swIfName, vxlanGpe); + final CompletionStage<VxlanGpeAddDelTunnelReply> VxlanGpeAddDelTunnelReplyCompletionStage = + getFutureJVpp() + .vxlanGpeAddDelTunnel(getVxlanGpeTunnelRequest((byte) 0 /* is delete */, local.getAddress(), + remote.getAddress(), vni, protocol, encapVrfId, decapVrfId, isIpv6)); + getReplyForDelete(VxlanGpeAddDelTunnelReplyCompletionStage.toCompletableFuture(), id); + final int index = interfaceNamingContext.getIndex(swIfName, writeContext.getMappingContext()); + // Mark this interface as disabled to not include it in operational reads + // because VPP will keep the interface there + LOG.debug("Marking vxlan tunnel as disabled, id: {}, name: {}", index, swIfName); + interfaceDisableContext.disableInterface(index, writeContext.getMappingContext()); + // Remove interface from our interface naming context + interfaceNamingContext.removeName(swIfName, writeContext.getMappingContext()); + } + + private static VxlanGpeAddDelTunnel getVxlanGpeTunnelRequest(final byte isAdd, final byte[] local, + final byte[] remote, + final int vni, final byte protocol, + final int encapVrfId, final int decapVrfId, + final byte isIpv6) { + final VxlanGpeAddDelTunnel VxlanGpeAddDelTunnel = new VxlanGpeAddDelTunnel(); + VxlanGpeAddDelTunnel.isAdd = isAdd; + VxlanGpeAddDelTunnel.local = local; + VxlanGpeAddDelTunnel.remote = remote; + VxlanGpeAddDelTunnel.vni = vni; + VxlanGpeAddDelTunnel.protocol = protocol; + VxlanGpeAddDelTunnel.encapVrfId = encapVrfId; + VxlanGpeAddDelTunnel.decapVrfId = decapVrfId; + VxlanGpeAddDelTunnel.isIpv6 = isIpv6; + return VxlanGpeAddDelTunnel; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/IetfAclWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/IetfAclWriter.java new file mode 100644 index 000000000..d97bdee54 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/IetfAclWriter.java @@ -0,0 +1,106 @@ +/* + * 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.v3po.interfaces.acl; + +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import java.util.Optional; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +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.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.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceAugmentation; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Writer customizer responsible for Access Control Lists management. Does not send any messages to VPP. All the config + * data are stored in HC and used when acl is assigned/unassigned to/from an interface. + * + * ACLs that are currently assigned to an interface cannot be updated/deleted. + */ +public class IetfAclWriter implements ListWriterCustomizer<Acl, AclKey> { + + public static final InstanceIdentifier<AccessLists> ACL_ID = + InstanceIdentifier.create(AccessLists.class); + + private static final Logger LOG = LoggerFactory.getLogger(IetfAclWriter.class); + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + LOG.debug("Creating ACL: iid={} dataAfter={}", id, dataAfter); + + // no vpp call, just updates DataTree + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataBefore, + @Nonnull final Acl dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("Updating ACL: iid={} dataBefore={} dataAfter={}", id, dataBefore, dataAfter); + + if (isAssigned(dataAfter, writeContext)) { + throw new WriteFailedException(id, + String.format("Failed to update data at %s: acl %s is already assigned", id, dataAfter)); + } + + LOG.debug("Updating unassigned ACL: iid={} dataBefore={} dataAfter={}", id, dataBefore, dataAfter); + + // no vpp call, just updates DataTree + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + LOG.debug("Deleting ACL: iid={} dataBefore={}", id, dataBefore); + + if (isAssigned(dataBefore, writeContext)) { + throw new WriteFailedException(id, + String.format("Failed to delete data at %s: acl %s is already assigned", id, dataBefore)); + } + + LOG.debug("Deleting unassigned ACL: iid={} dataBefore={}", id, dataBefore); + + // no vpp call, just updates DataTree + } + + private static boolean isAssigned(@Nonnull final Acl acl, + @Nonnull final WriteContext writeContext) { + final String aclName = acl.getAclName(); + final Class<? extends AclBase> aclType = acl.getAclType(); + final Interfaces interfaces = writeContext.readAfter(InstanceIdentifier.create(Interfaces.class)).get(); + + return interfaces.getInterface().stream() + .map(i -> Optional.ofNullable(i.getAugmentation(VppInterfaceAugmentation.class)) + .map(aug -> aug.getIetfAcl()) + .map(ietfAcl -> ietfAcl.getIngress()) + .map(ingress -> ingress.getAccessLists()) + .map(accessLists -> accessLists.getAcl()) + ) + .flatMap(iacl -> iacl.isPresent() + ? iacl.get().stream() + : Stream.empty()) + .filter(assignedAcl -> aclName.equals(assignedAcl.getName()) && aclType.equals(assignedAcl.getType())) + .findFirst().isPresent(); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AbstractIetfAclWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AbstractIetfAclWriter.java new file mode 100644 index 000000000..29e92ae2a --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AbstractIetfAclWriter.java @@ -0,0 +1,251 @@ +/* + * 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.v3po.interfaces.acl.common; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSessionReply; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.concurrent.CompletionStage; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +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.AclKey; +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.actions.PacketHandling; +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.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceEth; +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.ace.type.AceIp; +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.ace.type.ace.ip.AceIpVersion; +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.ace.type.ace.ip.ace.ip.version.AceIpv4; +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.ace.type.ace.ip.ace.ip.version.AceIpv6; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntry; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpAndEth; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class AbstractIetfAclWriter implements IetfAclWriter, JvppReplyConsumer, AclTranslator { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractIetfAclWriter.class); + protected static final int NOT_DEFINED = -1; + protected final FutureJVppCore jvpp; + + private Map<AclType, AceWriter<? extends AceType>> aceWriters = new HashMap<>(); + + public AbstractIetfAclWriter(@Nonnull final FutureJVppCore futureJVppCore) { + this.jvpp = Preconditions.checkNotNull(futureJVppCore, "futureJVppCore should not be null"); + aceWriters.put(AclType.ETH, new AceEthWriter()); + aceWriters.put(AclType.IP4, new AceIp4Writer()); + aceWriters.put(AclType.IP6, new AceIp6Writer()); + aceWriters.put(AclType.ETH_AND_IP, new AceIpAndEthWriter()); + } + + private static Stream<Ace> aclToAceStream(@Nonnull final Acl assignedAcl, + @Nonnull final WriteContext writeContext) { + final String aclName = assignedAcl.getName(); + final Class<? extends AclBase> aclType = assignedAcl.getType(); + + // ietf-acl updates are handled first, so we use writeContext.readAfter + final Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl> + aclOptional = + writeContext.readAfter(io.fd.hc2vpp.v3po.interfaces.acl.IetfAclWriter.ACL_ID.child( + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl.class, + new AclKey(aclName, aclType))); + checkArgument(aclOptional.isPresent(), "Acl lists not configured"); + final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl + acl = aclOptional.get(); + + final AccessListEntries accessListEntries = acl.getAccessListEntries(); + checkArgument(accessListEntries != null, "access list entries not configured"); + + return accessListEntries.getAce().stream(); + } + + protected void removeClassifyTables(@Nonnull final InstanceIdentifier<?> id, @Nonnull final MappingEntry entry) + throws WriteFailedException { + removeClassifyTable(id, entry.getL2TableId()); + removeClassifyTable(id, entry.getIp4TableId()); + removeClassifyTable(id, entry.getIp6TableId()); + } + + private void removeClassifyTable(@Nonnull final InstanceIdentifier<?> id, final int tableIndex) + throws WriteFailedException { + + if (tableIndex == -1) { + return; // classify table id is absent + } + final ClassifyAddDelTable request = new ClassifyAddDelTable(); + request.tableIndex = tableIndex; + final CompletionStage<ClassifyAddDelTableReply> cs = jvpp.classifyAddDelTable(request); + getReplyForDelete(cs.toCompletableFuture(), id); + } + + protected static boolean appliesToIp4Path(final Ace ace) { + final AceType aceType = ace.getMatches().getAceType(); + final AclType aclType = AclType.fromAce(ace); + if (aclType == AclType.IP4) { + return true; + } + if (aclType == AclType.ETH) { + return true; // L2 only rules are possible for IP4 traffic + } + if (aclType == AclType.ETH_AND_IP && ((AceIpAndEth) aceType) + .getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv4) { + return true; + } + return false; + } + + protected static boolean appliesToIp6Path(final Ace ace) { + final AceType aceType = ace.getMatches().getAceType(); + final AclType aclType = AclType.fromAce(ace); + if (aclType == AclType.IP6) { + return true; + } + if (aclType == AclType.ETH) { + return true; // L2 only rules are possible for IP6 traffic + } + if (aclType == AclType.ETH_AND_IP && ((AceIpAndEth) aceType) + .getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv6) { + return true; + } + return false; + } + + protected static List<Ace> getACEs(@Nonnull final List<Acl> acls, @Nonnull final WriteContext writeContext, + final Predicate<? super Ace> filter) { + return acls.stream().flatMap(acl -> aclToAceStream(acl, writeContext)).filter(filter) + .collect(Collectors.toList()); + } + + protected int writeAces(final InstanceIdentifier<?> id, final List<Ace> aces, + final AccessLists.DefaultAction defaultAction, final InterfaceMode mode, + final int vlanTags) throws WriteFailedException { + if (aces.isEmpty()) { + return NOT_DEFINED; + } + + int nextTableIndex = configureDefaultAction(id, defaultAction); + final ListIterator<Ace> iterator = aces.listIterator(aces.size()); + while (iterator.hasPrevious()) { + final Ace ace = iterator.previous(); + LOG.trace("Processing ACE: {}", ace); + + final AceWriter aceWriter = + aceWriters.get(AclType.fromAce(ace)); + if (aceWriter == null) { + LOG.warn("AceProcessor for {} not registered. Skipping ACE.", ace.getClass()); + } else { + final AceType aceType = ace.getMatches().getAceType(); + final PacketHandling action = ace.getActions().getPacketHandling(); + final ClassifyAddDelTable ctRequest = aceWriter.createTable(aceType, mode, nextTableIndex, vlanTags); + nextTableIndex = createClassifyTable(id, ctRequest); + final List<ClassifyAddDelSession> sessionRequests = + aceWriter.createSession(action, aceType, mode, nextTableIndex, vlanTags); + for (ClassifyAddDelSession csRequest : sessionRequests) { + createClassifySession(id, csRequest); + } + } + } + return nextTableIndex; + } + + private int configureDefaultAction(@Nonnull final InstanceIdentifier<?> id, + final AccessLists.DefaultAction defaultAction) + throws WriteFailedException { + ClassifyAddDelTable ctRequest = createTable(-1); + if (AccessLists.DefaultAction.Permit.equals(defaultAction)) { + ctRequest.missNextIndex = -1; + } else { + ctRequest.missNextIndex = 0; + } + ctRequest.mask = new byte[16]; + ctRequest.skipNVectors = 0; + ctRequest.matchNVectors = 1; + return createClassifyTable(id, ctRequest); + } + + private int createClassifyTable(@Nonnull final InstanceIdentifier<?> id, + @Nonnull final ClassifyAddDelTable request) + throws WriteFailedException { + final CompletionStage<ClassifyAddDelTableReply> cs = jvpp.classifyAddDelTable(request); + + final ClassifyAddDelTableReply reply = getReplyForWrite(cs.toCompletableFuture(), id); + return reply.newTableIndex; + } + + private void createClassifySession(@Nonnull final InstanceIdentifier<?> id, + @Nonnull final ClassifyAddDelSession request) + throws WriteFailedException { + final CompletionStage<ClassifyAddDelSessionReply> cs = jvpp.classifyAddDelSession(request); + + getReplyForWrite(cs.toCompletableFuture(), id); + } + + private enum AclType { + ETH, IP4, IP6, ETH_AND_IP; + + @Nonnull + private static AclType fromAce(final Ace ace) { + AclType result = null; + final AceType aceType; + try { + aceType = ace.getMatches().getAceType(); + if (aceType instanceof AceEth) { + result = ETH; + } else if (aceType instanceof AceIp) { + final AceIpVersion aceIpVersion = ((AceIp) aceType).getAceIpVersion(); + if (aceIpVersion == null) { + throw new IllegalArgumentException("Incomplete ACE (ip-version was not provided): " + ace); + } + if (aceIpVersion instanceof AceIpv4) { + result = IP4; + } else if (aceIpVersion instanceof AceIpv6) { + result = IP6; + } + } else if (aceType instanceof AceIpAndEth) { + result = ETH_AND_IP; + } + } catch (NullPointerException e) { + throw new IllegalArgumentException("Incomplete ACE: " + ace, e); + } + if (result == null) { + throw new IllegalArgumentException(String.format("Not supported ace type %s", aceType)); + } + return result; + } + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceEthWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceEthWriter.java new file mode 100644 index 000000000..67351e0c6 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceEthWriter.java @@ -0,0 +1,85 @@ +/* + * 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.v3po.interfaces.acl.common; + +import com.google.common.annotations.VisibleForTesting; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; +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.ace.type.AceEth; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class AceEthWriter implements AceWriter<AceEth>, AclTranslator, L2AclTranslator { + + @VisibleForTesting + static final int MATCH_N_VECTORS = 1; + private static final Logger LOG = LoggerFactory.getLogger(AceEthWriter.class); + + @Override + public ClassifyAddDelTable createTable(@Nonnull final AceEth aceEth, + @Nullable final InterfaceMode mode, + final int nextTableIndex, + final int vlanTags) { + final ClassifyAddDelTable request = createTable(nextTableIndex); + + request.mask = new byte[16]; + boolean aceIsEmpty = + destinationMacAddressMask(aceEth.getDestinationMacAddressMask(), aceEth.getDestinationMacAddress(), + request); + aceIsEmpty &= + sourceMacAddressMask(aceEth.getSourceMacAddressMask(), aceEth.getSourceMacAddress(), request); + + if (aceIsEmpty) { + throw new IllegalArgumentException( + String.format("Ace %s does not define packet field match values", aceEth.toString())); + } + + request.skipNVectors = 0; + request.matchNVectors = MATCH_N_VECTORS; + + LOG.debug("ACE rule={} translated to table={}.", aceEth, request); + return request; + } + + @Override + public List<ClassifyAddDelSession> createSession(@Nonnull final PacketHandling action, + @Nonnull final AceEth aceEth, + @Nullable final InterfaceMode mode, + final int tableIndex, + final int vlanTags) { + final ClassifyAddDelSession request = createSession(action, tableIndex); + + request.match = new byte[16]; + boolean noMatch = destinationMacAddressMatch(aceEth.getDestinationMacAddress(), request); + noMatch &= sourceMacAddressMatch(aceEth.getSourceMacAddress(), request); + + if (noMatch) { + throw new IllegalArgumentException( + String.format("Ace %s does not define neither source nor destination MAC address", + aceEth.toString())); + } + + LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceEth, request); + return Collections.singletonList(request); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIp4Writer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIp4Writer.java new file mode 100644 index 000000000..78ed33aa4 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIp4Writer.java @@ -0,0 +1,94 @@ +/* + * 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.v3po.interfaces.acl.common; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.annotations.VisibleForTesting; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; +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.ace.type.AceIp; +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.ace.type.ace.ip.ace.ip.version.AceIpv4; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class AceIp4Writer implements AceWriter<AceIp>, AclTranslator, Ip4AclTranslator { + + @VisibleForTesting + static final int MATCH_N_VECTORS = 3; // number of 16B vectors + private static final int TABLE_MASK_LENGTH = 48; + private static final Logger LOG = LoggerFactory.getLogger(AceIp4Writer.class); + + @Override + public ClassifyAddDelTable createTable(@Nonnull final AceIp aceIp, + @Nullable final InterfaceMode mode, + final int nextTableIndex, + final int vlanTags) { + checkArgument(aceIp.getAceIpVersion() instanceof AceIpv4, "Expected AceIpv4 version, but was %", aceIp); + final AceIpv4 ipVersion = (AceIpv4) aceIp.getAceIpVersion(); + + final int numberOfSessions = PortPair.fromRange(aceIp.getSourcePortRange(), aceIp.getDestinationPortRange()).size(); + final ClassifyAddDelTable request = createTable(nextTableIndex, numberOfSessions); + request.skipNVectors = 0; // match entire L2 and L3 header + request.matchNVectors = MATCH_N_VECTORS; + request.mask = new byte[TABLE_MASK_LENGTH]; + + final int baseOffset = getVlanTagsLen(vlanTags); + boolean aceIsEmpty = ip4Mask(baseOffset, mode, aceIp, ipVersion, request); + if (aceIsEmpty) { + throw new IllegalArgumentException( + String.format("Ace %s does not define packet field match values", aceIp.toString())); + } + + LOG.debug("ACE rule={} translated to table={}.", aceIp, request); + return request; + } + + @Override + public List<ClassifyAddDelSession> createSession(@Nonnull final PacketHandling action, + @Nonnull final AceIp aceIp, + @Nullable final InterfaceMode mode, + final int tableIndex, + final int vlanTags) { + checkArgument(aceIp.getAceIpVersion() instanceof AceIpv4, "Expected AceIpv4 version, but was %", aceIp); + final AceIpv4 ipVersion = (AceIpv4) aceIp.getAceIpVersion(); + + final List<PortPair> portPairs = PortPair.fromRange(aceIp.getSourcePortRange(), aceIp.getDestinationPortRange()); + final List<ClassifyAddDelSession> requests = new ArrayList<>(portPairs.size()); + for (final PortPair pair : portPairs) { + final ClassifyAddDelSession request = createSession(action, tableIndex); + request.match = new byte[TABLE_MASK_LENGTH]; + + final int baseOffset = getVlanTagsLen(vlanTags); + boolean noMatch = ip4Match(baseOffset, mode, aceIp, ipVersion, pair.getSrc(), pair.getDst(), request); + if (noMatch) { + throw new IllegalArgumentException( + String.format("Ace %s does not define packet field match values", aceIp.toString())); + } + + LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceIp, request); + requests.add(request); + } + return requests; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIp6Writer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIp6Writer.java new file mode 100644 index 000000000..d7cbb0009 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIp6Writer.java @@ -0,0 +1,99 @@ +/* + * 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.v3po.interfaces.acl.common; + +import static com.google.common.base.Preconditions.checkArgument; + +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; +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.ace.type.AceIp; +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.ace.type.ace.ip.ace.ip.version.AceIpv6; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class AceIp6Writer implements AceWriter<AceIp>, AclTranslator, Ip6AclTranslator { + + private static final Logger LOG = LoggerFactory.getLogger(AceIp6Writer.class); + + @Override + public ClassifyAddDelTable createTable(@Nonnull final AceIp aceIp, + @Nullable final InterfaceMode mode, + final int nextTableIndex, + final int vlanTags) { + checkArgument(aceIp.getAceIpVersion() instanceof AceIpv6, "Expected AceIpv6 version, but was %", aceIp); + final AceIpv6 ipVersion = (AceIpv6) aceIp.getAceIpVersion(); + + final int numberOfSessions = PortPair.fromRange(aceIp.getSourcePortRange(), aceIp.getDestinationPortRange()).size(); + final ClassifyAddDelTable request = createTable(nextTableIndex, numberOfSessions); + request.skipNVectors = 0; // match entire L2 and L3 header + request.mask = new byte[getTableMaskLength(vlanTags)]; + request.matchNVectors = request.mask.length/16; + + final int baseOffset = getVlanTagsLen(vlanTags); + boolean aceIsEmpty = ip6Mask(baseOffset, mode, aceIp, ipVersion, request); + if (aceIsEmpty) { + throw new IllegalArgumentException( + String.format("Ace %s does not define packet field match values", aceIp.toString())); + } + + LOG.debug("ACE rule={} translated to table={}.", aceIp, request); + return request; + } + + private static int getTableMaskLength(final int vlanTags) { + if (vlanTags == 2) { + return 80; + } else { + return 64; + } + } + + @Override + public List<ClassifyAddDelSession> createSession(@Nonnull final PacketHandling action, + @Nonnull final AceIp aceIp, + @Nullable final InterfaceMode mode, + final int tableIndex, + final int vlanTags) { + checkArgument(aceIp.getAceIpVersion() instanceof AceIpv6, "Expected AceIpv6 version, but was %", aceIp); + final AceIpv6 ipVersion = (AceIpv6) aceIp.getAceIpVersion(); + final List<PortPair> portPairs = + PortPair.fromRange(aceIp.getSourcePortRange(), aceIp.getDestinationPortRange()); + + final List<ClassifyAddDelSession> requests = new ArrayList<>(portPairs.size()); + for (final PortPair pair : portPairs) { + final ClassifyAddDelSession request = createSession(action, tableIndex); + request.match = new byte[getTableMaskLength(vlanTags)]; + + final int baseOffset = getVlanTagsLen(vlanTags); + boolean noMatch = ip6Match(baseOffset, mode, aceIp, ipVersion, pair.getSrc(), pair.getDst(), request); + if (noMatch) { + throw new IllegalArgumentException( + String.format("Ace %s does not define packet field match values", aceIp.toString())); + } + + LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceIp, request); + requests.add(request); + } + return requests; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIpAndEthWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIpAndEthWriter.java new file mode 100644 index 000000000..164493cc5 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIpAndEthWriter.java @@ -0,0 +1,129 @@ +/* + * 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.v3po.interfaces.acl.common; + +import static com.google.common.base.Preconditions.checkArgument; + +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpAndEth; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.AceIpVersion; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv4; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv6; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class AceIpAndEthWriter + implements AceWriter<AceIpAndEth>, AclTranslator, L2AclTranslator, Ip4AclTranslator, Ip6AclTranslator { + + private static final Logger LOG = LoggerFactory.getLogger(AceIpAndEthWriter.class); + + private static int maskLength(@Nonnull final AceIpAndEth ace, final int vlanTags) { + if (ace.getAceIpVersion() != null) { + if (ace.getAceIpVersion() instanceof AceIpv4) { + return 48; + } else { + return vlanTags == 2 + ? 80 + : 64; + } + } + return 16; + } + + @Override + public ClassifyAddDelTable createTable(@Nonnull final AceIpAndEth ace, @Nullable final InterfaceMode mode, + final int nextTableIndex, final int vlanTags) { + final int numberOfSessions = PortPair.fromRange(ace.getSourcePortRange(), ace.getDestinationPortRange()).size(); + final ClassifyAddDelTable request = createTable(nextTableIndex, numberOfSessions); + final int maskLength = maskLength(ace, vlanTags); + request.mask = new byte[maskLength]; + request.skipNVectors = 0; + request.matchNVectors = maskLength / 16; + + boolean aceIsEmpty = + destinationMacAddressMask(ace.getDestinationMacAddressMask(), ace.getDestinationMacAddress(), request); + aceIsEmpty &= sourceMacAddressMask(ace.getSourceMacAddressMask(), ace.getSourceMacAddress(), request); + + // if we use classifier API, we need to know ip version (fields common for ip4 and ip6 have different offsets): + final AceIpVersion aceIpVersion = ace.getAceIpVersion(); + checkArgument(aceIpVersion != null, "AceIpAndEth have to define IpVersion"); + + final int baseOffset = getVlanTagsLen(vlanTags); + if (aceIpVersion instanceof AceIpv4) { + final AceIpv4 ipVersion = (AceIpv4) aceIpVersion; + aceIsEmpty &= ip4Mask(baseOffset, mode, ace, ipVersion, request); + } else if (aceIpVersion instanceof AceIpv6) { + final AceIpv6 ipVersion = (AceIpv6) aceIpVersion; + aceIsEmpty &= ip6Mask(baseOffset, mode, ace, ipVersion, request); + } else { + throw new IllegalArgumentException(String.format("Unsupported IP version %s", aceIpVersion)); + } + + if (aceIsEmpty) { + throw new IllegalArgumentException( + String.format("Ace %s does not define packet field match values", ace.toString())); + } + + LOG.debug("ACE rule={} translated to table={}.", ace, request); + return request; + } + + @Override + public List<ClassifyAddDelSession> createSession(@Nonnull final PacketHandling action, + @Nonnull final AceIpAndEth ace, + @Nullable final InterfaceMode mode, final int tableIndex, + final int vlanTags) { + final List<PortPair> portPairs = PortPair.fromRange(ace.getSourcePortRange(), ace.getDestinationPortRange()); + final List<ClassifyAddDelSession> requests = new ArrayList<>(portPairs.size()); + for (final PortPair pair : portPairs) { + final ClassifyAddDelSession request = createSession(action, tableIndex); + request.match = new byte[maskLength(ace, vlanTags)]; + + boolean noMatch = destinationMacAddressMatch(ace.getDestinationMacAddress(), request); + noMatch &= sourceMacAddressMatch(ace.getSourceMacAddress(), request); + + final AceIpVersion aceIpVersion = ace.getAceIpVersion(); + checkArgument(aceIpVersion != null, "AceIpAndEth have to define IpVersion"); + + final int baseOffset = getVlanTagsLen(vlanTags); + if (aceIpVersion instanceof AceIpv4) { + final AceIpv4 ipVersion = (AceIpv4) aceIpVersion; + noMatch &= ip4Match(baseOffset, mode, ace, ipVersion, pair.getSrc(), pair.getDst(), request); + } else if (aceIpVersion instanceof AceIpv6) { + final AceIpv6 ipVersion = (AceIpv6) aceIpVersion; + noMatch &= ip6Match(baseOffset, mode, ace, ipVersion, pair.getSrc(), pair.getDst(), request); + } else { + throw new IllegalArgumentException(String.format("Unsupported IP version %s", aceIpVersion)); + } + + if (noMatch) { + throw new IllegalArgumentException( + String.format("Ace %s does not define packet field match values", ace.toString())); + } + LOG.debug("ACE action={}, rule={} translated to session={}.", action, ace, request); + requests.add(request); + } + return requests; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceWriter.java new file mode 100644 index 000000000..75a10e40f --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceWriter.java @@ -0,0 +1,54 @@ +/* + * 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.v3po.interfaces.acl.common; + +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; +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.rev161214.InterfaceMode; + +/** + * Writer responsible for translation of ietf-acl model ACEs to VPP's classify tables and sessions. + * + * @param <T> type of access control list entry + */ +interface AceWriter<T extends AceType> { + /** + * @param ace access list entry + * @param mode interface mode (L2/L3) + * @param nextTableIndex index of the next classify table in chain + * @param vlanTags number of vlan tags + */ + @Nonnull + ClassifyAddDelTable createTable(@Nonnull final T ace, @Nullable final InterfaceMode mode, final int nextTableIndex, + final int vlanTags); + + /** + * @param action to be taken when packet does match the specified ace + * @param ace access list entry + * @param mode interface mode (L2/L3) + * @param tableIndex index of corresponding classify table + * @param vlanTags number of vlan tags + */ + @Nonnull + List<ClassifyAddDelSession> createSession(@Nonnull final PacketHandling action, @Nonnull T ace, + @Nullable final InterfaceMode mode, final int tableIndex, final int vlanTags); +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTableContextManager.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTableContextManager.java new file mode 100644 index 000000000..0728944f2 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTableContextManager.java @@ -0,0 +1,53 @@ +/* + * 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.v3po.interfaces.acl.common; + +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.MappingContext; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntry; + +/** + * Manages interface metadata for ietf-acl model. + */ +public interface AclTableContextManager { + + /** + * Obtains mapping entry for given interface. + * + * @param index interface index + * @param mappingContext mapping context providing context data for current transaction + * @return ietf-acl metadata for given interface + */ + Optional<MappingEntry> getEntry(final int index, @Nonnull final MappingContext mappingContext); + + /** + * Adds mapping entry. + * + * @param entry to be added + * @param mappingContext mapping context providing context data for current transaction + */ + void addEntry(@Nonnull final MappingEntry entry, @Nonnull final MappingContext mappingContext); + + /** + * Removes entry for given interface (if present). + * + * @param index interface index + * @param mappingContext mapping context providing context data for current transaction + */ + void removeEntry(final int index, @Nonnull final MappingContext mappingContext); +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTableContextManagerImpl.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTableContextManagerImpl.java new file mode 100644 index 000000000..659244fda --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTableContextManagerImpl.java @@ -0,0 +1,68 @@ +/* + * 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.v3po.interfaces.acl.common; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.MappingContext; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.Contexts; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.AclMappingEntryCtxAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.AclMappingEntryContext; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.MappingTable; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.MappingTableKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntry; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntryKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +@ThreadSafe +public class AclTableContextManagerImpl implements AclTableContextManager { + + private MappingTable.Direction direction; + + public AclTableContextManagerImpl(@Nonnull final MappingTable.Direction direction) { + this.direction = checkNotNull(direction, "direction should not be null"); + } + + @Nonnull + @Override + public synchronized Optional<MappingEntry> getEntry(final int swIfIndex, @Nonnull final MappingContext mappingContext) { + return mappingContext.read(getId(swIfIndex)); + } + + @Override + public synchronized void addEntry(@Nonnull final MappingEntry entry, @Nonnull final MappingContext mappingContext) { + mappingContext.put(getId(entry.getIndex()), entry); + } + + @Override + public synchronized void removeEntry(final int swIfIndex, @Nonnull final MappingContext mappingContext) { + mappingContext.delete(getId(swIfIndex)); + } + + @VisibleForTesting + protected InstanceIdentifier<MappingEntry> getId(final int index) { + return InstanceIdentifier.create(Contexts.class) + .augmentation(AclMappingEntryCtxAugmentation.class) + .child(AclMappingEntryContext.class) + .child(MappingTable.class, new MappingTableKey(direction)) + .child(MappingEntry.class, new MappingEntryKey(index)); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTranslator.java new file mode 100644 index 000000000..e4291122d --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTranslator.java @@ -0,0 +1,74 @@ +/* + * 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.v3po.interfaces.acl.common; + +import static com.google.common.base.Preconditions.checkArgument; + +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.packet.handling.Permit; + +/** + * Utility that helps translating of ietf-acl model ACEs to VPP's classify tables and sessions. + */ +interface AclTranslator { + int TABLE_MEM_SIZE = 8 * 1024; + int VLAN_TAG_LEN = 4; + + default ClassifyAddDelTable createTable(final int nextTableIndex) { + return createTable(nextTableIndex, 1); + } + + default ClassifyAddDelTable createTable(final int nextTableIndex, @Nonnegative final int numberOfSessions) { + final ClassifyAddDelTable request = new ClassifyAddDelTable(); + request.isAdd = 1; + request.tableIndex = -1; // value not present + request.nbuckets = numberOfSessions; + request.nextTableIndex = nextTableIndex; + + + // TODO: HONEYCOMB-181 minimise memory used by classify tables (we create a lot of them to make ietf-acl model + // mapping more convenient): + // according to https://wiki.fd.io/view/VPP/Introduction_To_N-tuple_Classifiers#Creating_a_classifier_table, + // classify table needs 16*(1 + match_n_vectors) bytes, but this does not quite work, + // so setting 8K +1k*numberOfSessions for now + checkArgument(numberOfSessions>0, "negative numberOfSessions %s", numberOfSessions); + request.memorySize = TABLE_MEM_SIZE+1024*(numberOfSessions-1); + request.missNextIndex = -1; // value not set, but anyway it is ignored for tables in chain + return request; + } + + default ClassifyAddDelSession createSession(@Nonnull final PacketHandling action, final int tableIndex) { + final ClassifyAddDelSession request = new ClassifyAddDelSession(); + request.isAdd = 1; + request.tableIndex = tableIndex; + request.opaqueIndex = ~0; // value not used + + if (action instanceof Permit) { + request.hitNextIndex = -1; + } // deny (0) is default value + + return request; + } + + default int getVlanTagsLen(final int vlanTags) { + return vlanTags * VLAN_TAG_LEN; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/IetfAclWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/IetfAclWriter.java new file mode 100644 index 000000000..df0b65c2e --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/IetfAclWriter.java @@ -0,0 +1,47 @@ +/* + * 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.v3po.interfaces.acl.common; + +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import java.util.List; +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public interface IetfAclWriter { + default void write(@Nonnull final InstanceIdentifier<?> id, final int ifIndex, @Nonnull final List<Acl> acls, + final AccessLists.DefaultAction defaultAction, @Nullable final InterfaceMode mode, + @Nonnull final WriteContext writeContext, @Nonnull final MappingContext mappingContext) + throws WriteFailedException { + write(id, ifIndex, acls, defaultAction, mode, writeContext, 0, mappingContext); + } + + void write(@Nonnull final InstanceIdentifier<?> id, int ifIndex, @Nonnull final List<Acl> acls, + final AccessLists.DefaultAction defaultAction, @Nullable InterfaceMode mode, + @Nonnull final WriteContext writeContext, @Nonnegative final int numberOfTags, + @Nonnull final MappingContext mappingContext) + throws WriteFailedException; + + void deleteAcl(@Nonnull final InstanceIdentifier<?> id, int ifIndex, @Nonnull final MappingContext mappingContext) + throws WriteFailedException; +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/Ip4AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/Ip4AclTranslator.java new file mode 100644 index 000000000..c1bad0735 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/Ip4AclTranslator.java @@ -0,0 +1,149 @@ +/* + * 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.v3po.interfaces.acl.common; + +import com.google.common.primitives.Ints; +import io.fd.hc2vpp.common.translate.util.Ipv4Translator; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +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.packet.fields.rev160708.AclIpHeaderFields; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.AclIpv4HeaderFields; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; + +interface Ip4AclTranslator extends Ipv4Translator { + int ETHER_TYPE_OFFSET = 12; // first 14 bytes represent L2 header (2x6) + int DSCP_OFFSET = 15; + int DSCP_MASK = 0xfc; + + int IP_PROTOCOL_OFFSET = ETHER_TYPE_OFFSET + 11; + int IP_PROTOCOL_MASK = 0xff; + + int IP4_LEN = 4; + int IP4_MASK_BIT_LENGTH = 32; + int SRC_IP_OFFSET = ETHER_TYPE_OFFSET + 14; + int DST_IP_OFFSET = SRC_IP_OFFSET + IP4_LEN; + int SRC_PORT_OFFSET = DST_IP_OFFSET + IP4_LEN; + int DST_PORT_OFFSET = SRC_PORT_OFFSET + 2; + + default boolean ip4Mask(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header, + final AclIpv4HeaderFields ip4, final ClassifyAddDelTable request) { + boolean aceIsEmpty = true; + if (InterfaceMode.L2.equals(mode)) { + // in L2 mode we need to match ether type + request.mask[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0xff; + request.mask[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xff; + } + if (header.getDscp() != null) { + aceIsEmpty = false; + request.mask[baseOffset + DSCP_OFFSET] = (byte) DSCP_MASK; // first 6 bits + } + if (header.getProtocol() != null) { // Internet Protocol number + aceIsEmpty = false; + request.mask[baseOffset + IP_PROTOCOL_OFFSET] = (byte) IP_PROTOCOL_MASK; + } + if (header.getSourcePortRange() != null) { + // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present + aceIsEmpty = false; + request.mask[baseOffset + SRC_PORT_OFFSET] = (byte) 0xff; + request.mask[baseOffset + SRC_PORT_OFFSET + 1] = (byte) 0xff; + } + if (header.getDestinationPortRange() != null) { + // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present + aceIsEmpty = false; + request.mask[baseOffset + DST_PORT_OFFSET] = (byte) 0xff; + request.mask[baseOffset + DST_PORT_OFFSET + 1] = (byte) 0xff; + } + if (ip4.getSourceIpv4Network() != null) { + aceIsEmpty = false; + System.arraycopy(Impl.toByteMask(ip4.getSourceIpv4Network()), 0, request.mask, + baseOffset + SRC_IP_OFFSET, IP4_LEN); + } + if (ip4.getDestinationIpv4Network() != null) { + aceIsEmpty = false; + System.arraycopy(Impl.toByteMask(ip4.getDestinationIpv4Network()), 0, request.mask, + baseOffset + DST_IP_OFFSET, IP4_LEN); + } + return aceIsEmpty; + } + + default boolean ip4Match(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header, + final AclIpv4HeaderFields ip4, final Integer srcPort, + final Integer dstPort, final ClassifyAddDelSession request) { + boolean noMatch = true; + if (InterfaceMode.L2.equals(mode)) { + // match IP4 etherType (0x0800) + request.match[baseOffset + ETHER_TYPE_OFFSET] = 0x08; + request.match[baseOffset + ETHER_TYPE_OFFSET + 1] = 0x00; + } + if (header.getDscp() != null) { + noMatch = false; + request.match[baseOffset + DSCP_OFFSET] = (byte) (DSCP_MASK & (header.getDscp().getValue() << 2)); + } + if (header.getProtocol() != null) { // Internet Protocol number + noMatch = false; + request.match[baseOffset + IP_PROTOCOL_OFFSET] = (byte) (IP_PROTOCOL_MASK & header.getProtocol()); + } + if (srcPort != null) { + // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present + noMatch = false; + request.match[baseOffset + SRC_PORT_OFFSET] = (byte) (0xff & srcPort >> 8); + request.match[baseOffset + SRC_PORT_OFFSET + 1] = (byte) (0xff & srcPort); + } + if (header.getDestinationPortRange() != null) { + // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present + noMatch = false; + request.match[baseOffset + DST_PORT_OFFSET] = (byte) (0xff & dstPort >> 8); + request.match[baseOffset + DST_PORT_OFFSET + 1] = (byte) (0xff & dstPort); + } + if (ip4.getSourceIpv4Network() != null) { + noMatch = false; + System.arraycopy(Impl.toMatchValue(ip4.getSourceIpv4Network()), 0, request.match, + baseOffset + SRC_IP_OFFSET, IP4_LEN); + + } + if (ip4.getDestinationIpv4Network() != null) { + noMatch = false; + System.arraycopy(Impl.toMatchValue(ip4.getDestinationIpv4Network()), 0, request.match, + baseOffset + DST_IP_OFFSET, IP4_LEN); + + } + return noMatch; + } + + class Impl { + private static byte[] toByteMask(final int prefixLength) { + final long mask = ((1L << prefixLength) - 1) << (IP4_MASK_BIT_LENGTH - prefixLength); + return Ints.toByteArray((int) mask); + } + + private static byte[] toByteMask(final Ipv4Prefix ipv4Prefix) { + final int prefixLength = Byte.valueOf(ipv4Prefix.getValue().split("/")[1]); + return toByteMask(prefixLength); + } + + private static byte[] toMatchValue(final Ipv4Prefix ipv4Prefix) { + final String[] split = ipv4Prefix.getValue().split("/"); + final byte[] addressBytes = Ipv4Translator.INSTANCE.ipv4AddressNoZoneToArray(split[0]); + final byte[] mask = Impl.toByteMask(Byte.valueOf(split[1])); + for (int i = 0; i < addressBytes.length; ++i) { + addressBytes[i] &= mask[i]; + } + return addressBytes; + } + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/Ip6AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/Ip6AclTranslator.java new file mode 100644 index 000000000..ec9bebe7e --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/Ip6AclTranslator.java @@ -0,0 +1,182 @@ +/* + * 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.v3po.interfaces.acl.common; + +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.BitSet; +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.packet.fields.rev160708.AclIpHeaderFields; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.AclIpv6HeaderFields; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; + +interface Ip6AclTranslator { + + int ETHER_TYPE_OFFSET = 12; // first 14 bytes represent L2 header (2x6) + int IP_VERSION_OFFSET = ETHER_TYPE_OFFSET + 2; + int DSCP_MASK1 = 0x0f; + int DSCP_MASK2 = 0xc0; + int IP_PROTOCOL_OFFSET = IP_VERSION_OFFSET + 6; + int IP_PROTOCOL_MASK = 0xff; + int IP6_LEN = 16; + int SRC_IP_OFFSET = IP_VERSION_OFFSET + 8; + int DST_IP_OFFSET = SRC_IP_OFFSET + IP6_LEN; + int SRC_PORT_OFFSET = DST_IP_OFFSET + IP6_LEN; + int DST_PORT_OFFSET = SRC_PORT_OFFSET + 2; + + default boolean ip6Mask(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header, + final AclIpv6HeaderFields ip6, final ClassifyAddDelTable request) { + boolean aceIsEmpty = true; + if (InterfaceMode.L2.equals(mode)) { + // in L2 mode we need to match ether type + request.mask[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0xff; + request.mask[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xff; + } + if (header.getDscp() != null) { + aceIsEmpty = false; + // DCSP (bits 4-9 of IP6 header) + request.mask[baseOffset + IP_VERSION_OFFSET] |= DSCP_MASK1; + request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= DSCP_MASK2; + } + if (header.getProtocol() != null) { // Internet Protocol number + aceIsEmpty = false; + request.mask[baseOffset + IP_PROTOCOL_OFFSET] = (byte) IP_PROTOCOL_MASK; + } + if (ip6.getFlowLabel() != null) { + aceIsEmpty = false; + // bits 12-31 + request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) 0x0f; + request.mask[baseOffset + IP_VERSION_OFFSET + 2] = (byte) 0xff; + request.mask[baseOffset + IP_VERSION_OFFSET + 3] = (byte) 0xff; + } + if (header.getSourcePortRange() != null) { + // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present + aceIsEmpty = false; + request.mask[baseOffset + SRC_PORT_OFFSET] = (byte) 0xff; + request.mask[baseOffset + SRC_PORT_OFFSET + 1] = (byte) 0xff; + } + if (header.getDestinationPortRange() != null) { + // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present + aceIsEmpty = false; + request.mask[baseOffset + DST_PORT_OFFSET] = (byte) 0xff; + request.mask[baseOffset + DST_PORT_OFFSET + 1] = (byte) 0xff; + } + if (ip6.getSourceIpv6Network() != null) { + aceIsEmpty = false; + final byte[] mask = Impl.toByteMask(ip6.getSourceIpv6Network()); + System.arraycopy(mask, 0, request.mask, baseOffset + SRC_IP_OFFSET, mask.length); + } + if (ip6.getDestinationIpv6Network() != null) { + aceIsEmpty = false; + final byte[] mask = Impl.toByteMask(ip6.getDestinationIpv6Network()); + System.arraycopy(mask, 0, request.mask, baseOffset + DST_IP_OFFSET, mask.length); + } + return aceIsEmpty; + } + + default boolean ip6Match(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header, + final AclIpv6HeaderFields ip6, final Integer srcPort, final Integer dstPort, final ClassifyAddDelSession request) { + boolean noMatch = true; + if (InterfaceMode.L2.equals(mode)) { + // match IP6 etherType (0x86dd) + request.match[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0x86; + request.match[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xdd; + } + if (header.getDscp() != null) { + noMatch = false; + final int dcsp = header.getDscp().getValue(); + // set bits 4-9 of IP6 header: + request.match[baseOffset + IP_VERSION_OFFSET] |= (byte) (DSCP_MASK1 & (dcsp >> 2)); + request.match[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) (DSCP_MASK2 & (dcsp << 6)); + } + if (header.getProtocol() != null) { // Internet Protocol number + noMatch = false; + request.match[baseOffset + IP_PROTOCOL_OFFSET] = (byte) (IP_PROTOCOL_MASK & header.getProtocol()); + } + if (ip6.getFlowLabel() != null) { + noMatch = false; + final int flowLabel = ip6.getFlowLabel().getValue().intValue(); + // bits 12-31 + request.match[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) (0x0f & (flowLabel >> 16)); + request.match[baseOffset + IP_VERSION_OFFSET + 2] = (byte) (0xff & (flowLabel >> 8)); + request.match[baseOffset + IP_VERSION_OFFSET + 3] = (byte) (0xff & flowLabel); + } + if (header.getSourcePortRange() != null) { + // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present + noMatch = false; + request.match[baseOffset + SRC_PORT_OFFSET] = (byte) (0xff & srcPort >> 8); + request.match[baseOffset + SRC_PORT_OFFSET + 1] = (byte) (0xff & srcPort); + } + if (header.getDestinationPortRange() != null) { + // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present + noMatch = false; + request.match[baseOffset + DST_PORT_OFFSET] = (byte) (0xff & dstPort >> 8); + request.match[baseOffset + DST_PORT_OFFSET + 1] = (byte) (0xff & dstPort); + } + if (ip6.getSourceIpv6Network() != null) { + noMatch = false; + final byte[] match = Impl.toMatchValue(ip6.getSourceIpv6Network()); + System.arraycopy(match, 0, request.match, baseOffset + SRC_IP_OFFSET, IP6_LEN); + } + if (ip6.getDestinationIpv6Network() != null) { + noMatch = false; + final byte[] match = Impl.toMatchValue(ip6.getDestinationIpv6Network()); + System.arraycopy(match, 0, request.match, baseOffset + DST_IP_OFFSET, IP6_LEN); + } + return noMatch; + } + + class Impl { + private static final int IP6_MASK_BIT_LENGTH = 128; + + private static byte[] toByteMask(final int prefixLength) { + final BitSet mask = new BitSet(IP6_MASK_BIT_LENGTH); + mask.set(0, prefixLength, true); + if (prefixLength < IP6_MASK_BIT_LENGTH) { + mask.set(prefixLength, IP6_MASK_BIT_LENGTH, false); + } + return mask.toByteArray(); + } + + private static byte[] toByteMask(final Ipv6Prefix ipv6Prefix) { + final int prefixLength = Short.valueOf(ipv6Prefix.getValue().split("/")[1]); + return toByteMask(prefixLength); + } + + private static byte[] toMatchValue(final Ipv6Prefix ipv6Prefix) { + final String[] split = ipv6Prefix.getValue().split("/"); + final byte[] addressBytes; + try { + addressBytes = InetAddress.getByName(split[0]).getAddress(); + } catch (UnknownHostException e) { + throw new IllegalArgumentException("Invalid IP6 address", e); + } + final byte[] mask = toByteMask(Short.valueOf(split[1])); + int pos = 0; + for (; pos < mask.length; ++pos) { + addressBytes[pos] &= mask[pos]; + } + // mask can be shorter that address, so we need to clear rest of the address: + for (; pos < addressBytes.length; ++pos) { + addressBytes[pos] = 0; + } + return addressBytes; + } + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/L2AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/L2AclTranslator.java new file mode 100644 index 000000000..5a6807bba --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/L2AclTranslator.java @@ -0,0 +1,90 @@ +/* + * 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.v3po.interfaces.acl.common; + +import io.fd.hc2vpp.common.translate.util.MacTranslator; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import java.util.List; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; + +interface L2AclTranslator extends MacTranslator { + + default boolean destinationMacAddressMask(final MacAddress dstMask, final MacAddress dstAddress, + final ClassifyAddDelTable request) { + // destination-mac-address or destination-mac-address-mask is present => + // ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00 + if (dstMask != null) { + final List<String> parts = COLON_SPLITTER.splitToList(dstMask.getValue()); + int i = 0; + for (String part : parts) { + request.mask[i++] = parseHexByte(part); + } + return false; + } else if (dstAddress != null) { + for (int i = 0; i < 6; ++i) { + request.mask[i] = (byte) 0xff; + } + return false; + } + return true; + } + + default boolean sourceMacAddressMask(final MacAddress srcMask, final MacAddress srcAddress, + final ClassifyAddDelTable request) { + // source-mac-address or source-mac-address-mask => + // 00:00:00:00:00:00:ff:ff:ff:ff:ff:ff:00:00:00:00 + if (srcMask != null) { + final List<String> parts = COLON_SPLITTER.splitToList(srcMask.getValue()); + int i = 6; + for (String part : parts) { + request.mask[i++] = parseHexByte(part); + } + return false; + } else if (srcAddress != null) { + for (int i = 6; i < 12; ++i) { + request.mask[i] = (byte) 0xff; + } + return false; + } + return true; + } + + default boolean destinationMacAddressMatch(final MacAddress dstAddress, final ClassifyAddDelSession request) { + if (dstAddress != null) { + final List<String> parts = COLON_SPLITTER.splitToList(dstAddress.getValue()); + int i = 0; + for (String part : parts) { + request.match[i++] = parseHexByte(part); + } + return false; + } + return true; + } + + default boolean sourceMacAddressMatch(final MacAddress srcAddress, final ClassifyAddDelSession request) { + if (srcAddress != null) { + final List<String> parts = COLON_SPLITTER.splitToList(srcAddress.getValue()); + int i = 6; + for (String part : parts) { + request.match[i++] = parseHexByte(part); + } + return false; + } + return true; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/PortPair.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/PortPair.java new file mode 100644 index 000000000..1630f58fd --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/PortPair.java @@ -0,0 +1,126 @@ +/* + * 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.v3po.interfaces.acl.common; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.BiFunction; +import javax.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.acl.transport.header.fields.DestinationPortRange; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.acl.transport.header.fields.SourcePortRange; + +/** + * Utility that produces cartesian product out of src and dst port ranges (used to translate ranges into + * list of classify sessions). + */ +final class PortPair { + private final Integer src; + private final Integer dst; + + PortPair(@Nullable final Integer src, @Nullable final Integer dst) { + this.src = src; + this.dst = dst; + } + + Integer getSrc() { + return src; + } + + Integer getDst() { + return dst; + } + + @Override + public String toString() { + return "(" + src + "," + dst + ")"; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final PortPair that = (PortPair) o; + if (!Objects.equals(src, that.src)) { + return false; + } + if (!Objects.equals(dst, that.dst)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return Objects.hash(src, dst); + } + + static List<PortPair> fromRange(final SourcePortRange srcRange, + final DestinationPortRange dstRange) { + final List<PortPair> result = new ArrayList<>(); + if (srcRange == null && dstRange == null) { + result.add(new PortPair(null, null)); + } else if (srcRange != null && dstRange == null) { + processSingleRange(result, srcRange.getLowerPort(), srcRange.getUpperPort(), PortPair::new); + } else if (srcRange == null && dstRange != null) { + processSingleRange(result, dstRange.getLowerPort(), dstRange.getUpperPort(), + (dst, src) -> new PortPair(src, dst)); + } else { + processDoubleRange(result, srcRange, dstRange); + } + return result; + } + + private static void processSingleRange(final List<PortPair> result, + final PortNumber lowerPort, + final PortNumber upperPort, + final BiFunction<Integer, Integer, PortPair> f) { + int low = lowerPort.getValue(); // mandatory + int hi = low; + if (upperPort != null) { + hi = upperPort.getValue(); + } + for (; low <= hi; ++low) { + result.add(f.apply(low, null)); + } + } + + private static void processDoubleRange(final List<PortPair> result, final SourcePortRange srcRange, + final DestinationPortRange dstRange) { + int srcL = srcRange.getLowerPort().getValue(); + int srcH = srcL; + if (srcRange.getUpperPort() != null) { + srcH = srcRange.getUpperPort().getValue(); + } + int dstL = dstRange.getLowerPort().getValue(); + int dstH = dstL; + if (dstRange.getUpperPort() != null) { + dstH = dstRange.getUpperPort().getValue(); + } + for (int i=srcL; i <= srcH; ++i) { + for (int j=dstL; j <= dstH; ++j) { + result.add(new PortPair(i, j)); + } + } + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/egress/EgressIetfAclWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/egress/EgressIetfAclWriter.java new file mode 100644 index 000000000..adb19e360 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/egress/EgressIetfAclWriter.java @@ -0,0 +1,120 @@ +/* + * 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.v3po.interfaces.acl.egress; + +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 com.google.common.base.Optional; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.hc2vpp.v3po.interfaces.acl.common.AbstractIetfAclWriter; +import io.fd.hc2vpp.v3po.interfaces.acl.common.AclTableContextManager; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.ClassifySetInterfaceL2Tables; +import io.fd.vpp.jvpp.core.dto.ClassifySetInterfaceL2TablesReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.List; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnegative; +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.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.ace.type.AceEth; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntry; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntryBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public final class EgressIetfAclWriter extends AbstractIetfAclWriter { + private final AclTableContextManager aclCtx; + + public EgressIetfAclWriter(@Nonnull final FutureJVppCore futureJVppCore, @Nonnull AclTableContextManager aclCtx) { + super(futureJVppCore); + this.aclCtx = checkNotNull(aclCtx, "aclCtx should not be null"); + } + + @Override + public void deleteAcl(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex, + @Nonnull final MappingContext mappingContext) + throws WriteFailedException { + Optional<MappingEntry> optional = aclCtx.getEntry(swIfIndex, mappingContext); + checkState(optional.isPresent(), "Removing ACL id=%s, but acl mapping entry is not present", id); + final MappingEntry entry = optional.get(); + unassignClassifyTables(id, swIfIndex); + removeClassifyTables(id, entry); + aclCtx.removeEntry(swIfIndex, mappingContext); + } + + private void unassignClassifyTables(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex) + throws WriteFailedException { + final ClassifySetInterfaceL2Tables request = new ClassifySetInterfaceL2Tables(); + request.swIfIndex = swIfIndex; + request.ip4TableIndex = NOT_DEFINED; + request.ip6TableIndex = NOT_DEFINED; + request.otherTableIndex = NOT_DEFINED; + request.isInput = 0; // egress + final CompletionStage<ClassifySetInterfaceL2TablesReply> cs = jvpp.classifySetInterfaceL2Tables(request); + getReplyForDelete(cs.toCompletableFuture(), id); + } + + @Override + public void write(@Nonnull final InstanceIdentifier<?> id, int swIfIndex, @Nonnull final List<Acl> acls, + @Nonnull final AccessLists.DefaultAction defaultAction, @Nullable InterfaceMode mode, + @Nonnull final WriteContext writeContext, @Nonnegative final int numberOfTags, + @Nonnull final MappingContext mappingContext) + throws WriteFailedException { + checkArgument(numberOfTags >= 0 && numberOfTags <= 2, "Number of vlan tags %s is not in [0,2] range"); + checkArgument(InterfaceMode.L2.equals(mode), "Writing egress Acls is supported only in L2 mode"); + + final ClassifySetInterfaceL2Tables request = new ClassifySetInterfaceL2Tables(); + request.isInput = 0; // egress + request.swIfIndex = swIfIndex; + + // applied to packets according to their ether type + final List<Ace> ip4Aces = getACEs(acls, writeContext, (AbstractIetfAclWriter::appliesToIp4Path)); + request.ip4TableIndex = writeAces(id, ip4Aces, defaultAction, mode, numberOfTags); + final List<Ace> ip6Aces = getACEs(acls, writeContext, (AbstractIetfAclWriter::appliesToIp6Path)); + request.ip6TableIndex = writeAces(id, ip6Aces, defaultAction, mode, numberOfTags); + final List<Ace> aces = getACEs(acls, writeContext, EgressIetfAclWriter::isNotIpRule); + request.otherTableIndex = writeAces(id, aces, defaultAction, mode, numberOfTags); + + final MappingEntry entry = new MappingEntryBuilder().setIndex(swIfIndex) + .setIp4TableId(request.ip4TableIndex) + .setIp6TableId(request.ip6TableIndex) + .setL2TableId(request.otherTableIndex) + .build(); + aclCtx.addEntry(entry, mappingContext); + + try { + getReplyForWrite(jvpp.classifySetInterfaceL2Tables(request).toCompletableFuture(), id); + } catch (WriteFailedException e) { + removeClassifyTables(id, entry); + throw e; + } + } + + private static boolean isNotIpRule(final Ace ace) { + final Matches matches = ace.getMatches(); + checkArgument(matches != null, "Incomplete ACE: %s", ace); + return matches.getAceType() instanceof AceEth; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/egress/IetfAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/egress/IetfAclCustomizer.java new file mode 100644 index 000000000..3cef7a697 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/egress/IetfAclCustomizer.java @@ -0,0 +1,85 @@ +/* + * 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.v3po.interfaces.acl.egress; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.hc2vpp.v3po.interfaces.acl.common.IetfAclWriter; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import javax.annotation.Nonnull; +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.v3po.rev161214.interfaces._interface.ietf.acl.Egress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class IetfAclCustomizer implements WriterCustomizer<Egress> { + private static final Logger LOG = LoggerFactory.getLogger(IetfAclCustomizer.class); + private final IetfAclWriter aclWriter; + private final NamingContext interfaceContext; + + public IetfAclCustomizer(final IetfAclWriter aclWriter, final NamingContext interfaceContext) { + this.aclWriter = checkNotNull(aclWriter, "aclWriter should not be null"); + this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Egress> id, @Nonnull final Egress dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final String ifName = id.firstKeyOf(Interface.class).getName(); + final int ifIndex = interfaceContext.getIndex(ifName, writeContext.getMappingContext()); + LOG.debug("Adding egress ACLs for interface={}(id={}): {}", ifName, ifIndex, dataAfter); + + final AccessLists accessLists = dataAfter.getAccessLists(); + checkArgument(accessLists != null && accessLists.getAcl() != null, + "ietf-acl container does not define acl list"); + + if (!InterfaceMode.L2.equals(accessLists.getMode())) { + LOG.debug("Writing egress Acls is supported only in L2 mode. Ignoring config: {}", dataAfter); + return; + } + + aclWriter.write(id, ifIndex, accessLists.getAcl(), accessLists.getDefaultAction(), accessLists.getMode(), + writeContext, writeContext.getMappingContext()); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Egress> id, @Nonnull final Egress dataBefore, + @Nonnull final Egress dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("ACLs update: removing previously configured ACLs"); + deleteCurrentAttributes(id, dataBefore, writeContext); + LOG.debug("ACLs update: adding updated ACLs"); + writeCurrentAttributes(id, dataAfter, writeContext); + LOG.debug("ACLs update was successful"); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Egress> id, @Nonnull final Egress dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final String ifName = id.firstKeyOf(Interface.class).getName(); + final int ifIndex = interfaceContext.getIndex(ifName, writeContext.getMappingContext()); + LOG.debug("Removing ACLs for interface={}(id={}): {}", ifName, ifIndex, dataBefore); + aclWriter.deleteAcl(id, ifIndex, writeContext.getMappingContext()); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/egress/SubInterfaceIetfAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/egress/SubInterfaceIetfAclCustomizer.java new file mode 100644 index 000000000..3ea40436b --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/egress/SubInterfaceIetfAclCustomizer.java @@ -0,0 +1,105 @@ +/* + * 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.v3po.interfaces.acl.egress; + +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 io.fd.hc2vpp.v3po.util.SubInterfaceUtils.getNumberOfTags; + +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.hc2vpp.v3po.interfaces.acl.common.IetfAclWriter; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.hc2vpp.v3po.util.SubInterfaceUtils; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.ietf.acl.Egress; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SubInterfaceIetfAclCustomizer implements WriterCustomizer<Egress> { + private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceIetfAclCustomizer.class); + private final IetfAclWriter aclWriter; + private final NamingContext interfaceContext; + + public SubInterfaceIetfAclCustomizer(final IetfAclWriter aclWriter, final NamingContext interfaceContext) { + this.aclWriter = checkNotNull(aclWriter, "aclWriter should not be null"); + this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); + } + + private String getSubInterfaceName(@Nonnull final InstanceIdentifier<Egress> id) { + final InterfaceKey parentInterfacekey = id.firstKeyOf(Interface.class); + final SubInterfaceKey subInterfacekey = id.firstKeyOf(SubInterface.class); + return SubInterfaceUtils + .getSubInterfaceName(parentInterfacekey.getName(), subInterfacekey.getIdentifier().intValue()); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Egress> id, @Nonnull final Egress dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final String subInterfaceName = getSubInterfaceName(id); + final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext()); + LOG.debug("Adding IETF-ACL for sub-interface: {}(id={}): {}", subInterfaceName, subInterfaceIndex, dataAfter); + + final AccessLists accessLists = dataAfter.getAccessLists(); + checkArgument(accessLists != null && accessLists.getAcl() != null, + "ietf-acl container does not define acl list"); + + final Optional<SubInterface> subInterfaceOptional = + writeContext.readAfter(id.firstIdentifierOf(SubInterface.class)); + checkState(subInterfaceOptional.isPresent(), "Could not read SubInterface data object for %s", id); + final SubInterface subInterface = subInterfaceOptional.get(); + + if (!InterfaceMode.L2.equals(accessLists.getMode())) { + LOG.debug("Writing egress Acls is supported only in L2 mode. Ignoring config: {}", dataAfter); + return; + } + + aclWriter + .write(id, subInterfaceIndex, accessLists.getAcl(), accessLists.getDefaultAction(), accessLists.getMode(), + writeContext, getNumberOfTags(subInterface.getTags()), writeContext.getMappingContext()); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Egress> id, @Nonnull final Egress dataBefore, + @Nonnull final Egress dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("Sub-interface ACLs update: removing previously configured ACLs"); + deleteCurrentAttributes(id, dataBefore, writeContext); + LOG.debug("Sub-interface ACLs update: adding updated ACLs"); + writeCurrentAttributes(id, dataAfter, writeContext); + LOG.debug("Sub-interface ACLs update was successful"); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Egress> id, @Nonnull final Egress dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final String subInterfaceName = getSubInterfaceName(id); + final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext()); + LOG.debug("Removing ACLs for sub-interface={}(id={}): {}", subInterfaceName, subInterfaceIndex, dataBefore); + aclWriter.deleteAcl(id, subInterfaceIndex, writeContext.getMappingContext()); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/AclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/AclCustomizer.java new file mode 100644 index 000000000..05233ce40 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/AclCustomizer.java @@ -0,0 +1,83 @@ +/* + * 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.v3po.interfaces.acl.ingress; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.hc2vpp.v3po.vppclassifier.VppClassifierContextManager; +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import javax.annotation.Nonnull; +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.v3po.rev161214.interfaces._interface.acl.Ingress; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Customizer for enabling/disabling ingress ACLs on given interface based on low lever classfier model. + */ +public class AclCustomizer extends FutureJVppCustomizer implements WriterCustomizer<Ingress>, AclWriter { + + private static final Logger LOG = LoggerFactory.getLogger(AclCustomizer.class); + private final NamingContext interfaceContext; + private final VppClassifierContextManager classifyTableContext; + + public AclCustomizer(@Nonnull final FutureJVppCore vppApi, @Nonnull final NamingContext interfaceContext, + @Nonnull final VppClassifierContextManager classifyTableContext) { + super(vppApi); + this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); + this.classifyTableContext = checkNotNull(classifyTableContext, "classifyTableContext should not be null"); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, @Nonnull final Ingress dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + setAcl(true, id, dataAfter, writeContext); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, + @Nonnull final Ingress dataBefore, + @Nonnull final Ingress dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + throw new UnsupportedOperationException("Acl update is not supported. Please delete Acl container first."); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, + @Nonnull final Ingress dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + setAcl(false, id, dataBefore, writeContext); + } + + private void setAcl(final boolean isAdd, @Nonnull final InstanceIdentifier<Ingress> id, @Nonnull final Ingress acl, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final String ifName = id.firstKeyOf(Interface.class).getName(); + final int ifIndex = interfaceContext.getIndex(ifName, writeContext.getMappingContext()); + + LOG.debug("Setting ACL(isAdd={}) on interface={}(id={}): {}", isAdd, ifName, ifIndex, acl); + + inputAclSetInterface(getFutureJVpp(), isAdd, id, acl, ifIndex, classifyTableContext, + writeContext.getMappingContext()); + LOG.debug("Successfully set ACL(isAdd={}) on interface={}(id={}): {}", isAdd, ifName, ifIndex, acl); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/AclWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/AclWriter.java new file mode 100644 index 000000000..da74f0a8d --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/AclWriter.java @@ -0,0 +1,73 @@ +/* + * 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.v3po.interfaces.acl.ingress; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.hc2vpp.v3po.vppclassifier.VppClassifierContextManager; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.honeycomb.translate.write.WriteFailedException; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.AclBaseAttributes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.Ip4Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.Ip6Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.L2Acl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; +import io.fd.vpp.jvpp.core.dto.InputAclSetInterfaceReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; + +interface AclWriter extends ByteDataTranslator, JvppReplyConsumer { + + default void inputAclSetInterface(@Nonnull final FutureJVppCore futureJVppCore, final boolean isAdd, + @Nonnull final InstanceIdentifier<?> id, @Nonnull final AclBaseAttributes acl, + @Nonnegative final int ifIndex, + @Nonnull final VppClassifierContextManager classifyTableContext, + @Nonnull final MappingContext mappingContext) throws WriteFailedException { + final InputAclSetInterface request = new InputAclSetInterface(); + request.isAdd = booleanToByte(isAdd); + request.swIfIndex = ifIndex; + request.l2TableIndex = ~0; // skip + request.ip4TableIndex = ~0; // skip + request.ip6TableIndex = ~0; // skip + + final L2Acl l2Acl = acl.getL2Acl(); + if (l2Acl != null) { + final String tableName = checkNotNull(l2Acl.getClassifyTable(), "L2 classify table is null"); + request.l2TableIndex = classifyTableContext.getTableIndex(tableName, mappingContext); + } + final Ip4Acl ip4Acl = acl.getIp4Acl(); + if (ip4Acl != null) { + final String tableName = checkNotNull(ip4Acl.getClassifyTable(), "IPv4 classify table is null"); + request.ip4TableIndex = classifyTableContext.getTableIndex(tableName, mappingContext); + } + final Ip6Acl ip6Acl = acl.getIp6Acl(); + if (ip6Acl != null) { + final String tableName = checkNotNull(ip6Acl.getClassifyTable(), "IPv6 classify table is null"); + request.ip6TableIndex = classifyTableContext.getTableIndex(tableName, mappingContext); + } + + final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage = + futureJVppCore.inputAclSetInterface(request); + + getReplyForWrite(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/IetfAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/IetfAclCustomizer.java new file mode 100644 index 000000000..e4003fc9d --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/IetfAclCustomizer.java @@ -0,0 +1,89 @@ +/* + * 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.v3po.interfaces.acl.ingress; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import javax.annotation.Nonnull; +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.v3po.rev161214.interfaces._interface.ietf.acl.Ingress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Customizer for enabling/disabling ingress ACLs for given interface (as defined in ietf-acl model). + * + * The customizer assumes it owns classify table management for interfaces where ietf-acl container is present. Using + * low level classifier model or direct changes to classify tables in combination with ietf-acls are not supported and + * can result in unpredictable behaviour. + */ +public class IetfAclCustomizer implements WriterCustomizer<Ingress> { + + private static final Logger LOG = LoggerFactory.getLogger(IetfAclCustomizer.class); + private final IngressIetfAclWriter aclWriter; + private final NamingContext interfaceContext; + + public IetfAclCustomizer(@Nonnull final IngressIetfAclWriter aclWriter, + @Nonnull final NamingContext interfaceContext) { + this.aclWriter = checkNotNull(aclWriter, "aclWriter should not be null"); + this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, @Nonnull final Ingress dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final String ifName = id.firstKeyOf(Interface.class).getName(); + final int ifIndex = interfaceContext.getIndex(ifName, writeContext.getMappingContext()); + LOG.debug("Adding ACLs for interface={}(id={}): {}", ifName, ifIndex, dataAfter); + + final AccessLists accessLists = dataAfter.getAccessLists(); + checkArgument(accessLists != null && accessLists.getAcl() != null, + "ietf-acl container does not define acl list"); + + aclWriter.write(id, ifIndex, accessLists.getAcl(), accessLists.getDefaultAction(), accessLists.getMode(), + writeContext, writeContext.getMappingContext()); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, + @Nonnull final Ingress dataBefore, @Nonnull final Ingress dataAfter, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("ACLs update: removing previously configured ACLs"); + deleteCurrentAttributes(id, dataBefore, writeContext); + LOG.debug("ACLs update: adding updated ACLs"); + writeCurrentAttributes(id, dataAfter, writeContext); + LOG.debug("ACLs update was successful"); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, + @Nonnull final Ingress dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final String ifName = id.firstKeyOf(Interface.class).getName(); + final int ifIndex = interfaceContext.getIndex(ifName, writeContext.getMappingContext()); + LOG.debug("Removing ACLs for interface={}(id={}): {}", ifName, ifIndex, dataBefore); + aclWriter.deleteAcl(id, ifIndex, writeContext.getMappingContext()); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/IngressIetfAclWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/IngressIetfAclWriter.java new file mode 100644 index 000000000..022d21861 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/IngressIetfAclWriter.java @@ -0,0 +1,118 @@ +/* + * 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.v3po.interfaces.acl.ingress; + +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 com.google.common.base.Optional; +import io.fd.hc2vpp.v3po.interfaces.acl.common.AbstractIetfAclWriter; +import io.fd.hc2vpp.v3po.interfaces.acl.common.AclTableContextManager; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; +import io.fd.vpp.jvpp.core.dto.InputAclSetInterfaceReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.List; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnegative; +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.access.lists.acl.access.list.entries.Ace; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntry; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntryBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public final class IngressIetfAclWriter extends AbstractIetfAclWriter { + private final AclTableContextManager aclCtx; + + public IngressIetfAclWriter(@Nonnull final FutureJVppCore futureJVppCore, @Nonnull AclTableContextManager aclCtx) { + super(futureJVppCore); + this.aclCtx = checkNotNull(aclCtx, "aclCtx should not be null"); + } + + @Override + public void deleteAcl(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex, + @Nonnull final MappingContext mappingContext) + throws WriteFailedException { + Optional<MappingEntry> optional = aclCtx.getEntry(swIfIndex, mappingContext); + checkState(optional.isPresent(), "Removing ACL id=%s, but acl mapping entry is not present", id); + final MappingEntry entry = optional.get(); + unassignClassifyTables(id, entry); + removeClassifyTables(id, entry); + aclCtx.removeEntry(swIfIndex, mappingContext); + } + + private void unassignClassifyTables(@Nonnull final InstanceIdentifier<?> id, + @Nonnull final MappingEntry entry) + throws WriteFailedException { + final InputAclSetInterface request = new InputAclSetInterface(); + request.isAdd = 0; + request.swIfIndex = entry.getIndex(); + request.l2TableIndex = entry.getL2TableId(); + request.ip4TableIndex = entry.getIp4TableId(); + request.ip6TableIndex = entry.getIp6TableId(); + final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage = + jvpp.inputAclSetInterface(request); + getReplyForDelete(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id); + } + + @Override + public void write(@Nonnull final InstanceIdentifier<?> id, int swIfIndex, @Nonnull final List<Acl> acls, + @Nonnull final AccessLists.DefaultAction defaultAction, @Nullable final InterfaceMode mode, + @Nonnull final WriteContext writeContext, @Nonnegative final int numberOfTags, + @Nonnull final MappingContext mappingContext) + throws WriteFailedException { + checkArgument(numberOfTags >= 0 && numberOfTags <= 2, "Number of vlan tags %s is not in [0,2] range"); + + final InputAclSetInterface request = new InputAclSetInterface(); + request.isAdd = 1; + request.swIfIndex = swIfIndex; + request.l2TableIndex = NOT_DEFINED; + request.ip4TableIndex = NOT_DEFINED; + request.ip6TableIndex = NOT_DEFINED; + + if (InterfaceMode.L2.equals(mode)) { + final List<Ace> aces = getACEs(acls, writeContext, ace -> true); + request.l2TableIndex = writeAces(id, aces, defaultAction, mode, numberOfTags); + } else { + final List<Ace> ip4Aces = getACEs(acls, writeContext, (AbstractIetfAclWriter::appliesToIp4Path)); + request.ip4TableIndex = writeAces(id, ip4Aces, defaultAction, mode, numberOfTags); + final List<Ace> ip6Aces = getACEs(acls, writeContext, (AbstractIetfAclWriter::appliesToIp6Path)); + request.ip6TableIndex = writeAces(id, ip6Aces, defaultAction, mode, numberOfTags); + } + + final MappingEntry entry = new MappingEntryBuilder().setIndex(swIfIndex) + .setIp4TableId(request.ip4TableIndex) + .setIp6TableId(request.ip6TableIndex) + .setL2TableId(request.l2TableIndex) + .build(); + aclCtx.addEntry(entry, mappingContext); + + try { + getReplyForWrite(jvpp.inputAclSetInterface(request).toCompletableFuture(), id); + } catch (WriteFailedException e) { + removeClassifyTables(id, entry); + throw e; + } + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/SubInterfaceAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/SubInterfaceAclCustomizer.java new file mode 100644 index 000000000..b0be5d71f --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/SubInterfaceAclCustomizer.java @@ -0,0 +1,93 @@ +/* + * 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.v3po.interfaces.acl.ingress; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.hc2vpp.v3po.vppclassifier.VppClassifierContextManager; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.hc2vpp.v3po.util.SubInterfaceUtils; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.acl.Ingress; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Customizer for enabling/disabling ingress ACLs on given sub-interface. + */ +public class SubInterfaceAclCustomizer extends FutureJVppCustomizer + implements WriterCustomizer<Ingress>, AclWriter { + + private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceAclCustomizer.class); + private final NamingContext interfaceContext; + private final VppClassifierContextManager classifyTableContext; + + public SubInterfaceAclCustomizer(@Nonnull final FutureJVppCore vppApi, + @Nonnull final NamingContext interfaceContext, + @Nonnull final VppClassifierContextManager classifyTableContext) { + super(vppApi); + this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); + this.classifyTableContext = checkNotNull(classifyTableContext, "classifyTableContext should not be null"); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, @Nonnull final Ingress dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + setAcl(true, id, dataAfter, writeContext); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, + @Nonnull final Ingress dataBefore, + @Nonnull final Ingress dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + throw new UnsupportedOperationException("Acl update is not supported. Please delete Acl container first."); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, + @Nonnull final Ingress dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + setAcl(false, id, dataBefore, writeContext); + } + + private void setAcl(final boolean isAdd, @Nonnull final InstanceIdentifier<Ingress> id, @Nonnull final Ingress acl, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final InterfaceKey parentInterfacekey = id.firstKeyOf(Interface.class); + final SubInterfaceKey subInterfacekey = id.firstKeyOf(SubInterface.class); + final String subInterfaceName = SubInterfaceUtils + .getSubInterfaceName(parentInterfacekey.getName(), subInterfacekey.getIdentifier().intValue()); + final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext()); + + LOG.debug("Setting ACL(isAdd={}) on sub-interface={}(id={}): {}", + isAdd, subInterfaceName, subInterfaceIndex, acl); + inputAclSetInterface(getFutureJVpp(), isAdd, id, acl, subInterfaceIndex, classifyTableContext, + writeContext.getMappingContext()); + LOG.debug("Successfully set ACL(isAdd={}) on sub-interface={}(id={}): {}", + isAdd, subInterfaceName, subInterfaceIndex, acl); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizer.java new file mode 100644 index 000000000..4f24906a1 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizer.java @@ -0,0 +1,108 @@ +/* + * 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.v3po.interfaces.acl.ingress; + +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 io.fd.hc2vpp.v3po.util.SubInterfaceUtils.getNumberOfTags; + +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.hc2vpp.v3po.util.SubInterfaceUtils; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.ietf.acl.Ingress; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Customizer for enabling/disabling ingress ACLs for given sub-interface (as defined in ietf-acl model). + * + * The customizer assumes it owns classify table management for sub-interfaces where ietf-acl container is present. + * Using low level classifier model or direct changes to classify tables in combination with ietf-acls are not supported + * and can result in unpredictable behaviour. + */ +public class SubInterfaceIetfAclCustomizer implements WriterCustomizer<Ingress> { + + private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceIetfAclCustomizer.class); + private final IngressIetfAclWriter aclWriter; + private final NamingContext interfaceContext; + + public SubInterfaceIetfAclCustomizer(@Nonnull final IngressIetfAclWriter aclWriter, + @Nonnull final NamingContext interfaceContext) { + this.aclWriter = checkNotNull(aclWriter, "aclWriter should not be null"); + this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); + } + + private String getSubInterfaceName(@Nonnull final InstanceIdentifier<Ingress> id) { + final InterfaceKey parentInterfacekey = id.firstKeyOf(Interface.class); + final SubInterfaceKey subInterfacekey = id.firstKeyOf(SubInterface.class); + return SubInterfaceUtils + .getSubInterfaceName(parentInterfacekey.getName(), subInterfacekey.getIdentifier().intValue()); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, @Nonnull final Ingress dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final String subInterfaceName = getSubInterfaceName(id); + final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext()); + LOG.debug("Adding IETF-ACL for sub-interface: {}(id={}): {}", subInterfaceName, subInterfaceIndex, dataAfter); + + final AccessLists accessLists = dataAfter.getAccessLists(); + checkArgument(accessLists != null && accessLists.getAcl() != null, + "ietf-acl container does not define acl list"); + + final Optional<SubInterface> subInterfaceOptional = + writeContext.readAfter(id.firstIdentifierOf(SubInterface.class)); + checkState(subInterfaceOptional.isPresent(), "Could not read SubInterface data object for %s", id); + final SubInterface subInterface = subInterfaceOptional.get(); + + aclWriter + .write(id, subInterfaceIndex, accessLists.getAcl(), accessLists.getDefaultAction(), accessLists.getMode(), + writeContext, getNumberOfTags(subInterface.getTags()), writeContext.getMappingContext()); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, + @Nonnull final Ingress dataBefore, @Nonnull final Ingress dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + LOG.debug("Sub-interface ACLs update: removing previously configured ACLs"); + deleteCurrentAttributes(id, dataBefore, writeContext); + LOG.debug("Sub-interface ACLs update: adding updated ACLs"); + writeCurrentAttributes(id, dataAfter, writeContext); + LOG.debug("Sub-interface ACLs update was successful"); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, + @Nonnull final Ingress dataBefore, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String subInterfaceName = getSubInterfaceName(id); + final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext()); + LOG.debug("Removing ACLs for sub-interface={}(id={}): {}", subInterfaceName, subInterfaceIndex, dataBefore); + aclWriter.deleteAcl(id, subInterfaceIndex, writeContext.getMappingContext()); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/Ipv4AddressCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/Ipv4AddressCustomizer.java new file mode 100644 index 000000000..6a6bb6c35 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/Ipv4AddressCustomizer.java @@ -0,0 +1,151 @@ +/* + * 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.v3po.interfaces.ip; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Optional; +import io.fd.hc2vpp.v3po.interfaces.ip.subnet.validation.SubnetValidationException; +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.hc2vpp.v3po.interfaces.ip.subnet.validation.SubnetValidator; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.AddressKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.Subnet; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.Netmask; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.PrefixLength; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DottedQuad; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Customizer for writing {@link Address} + */ +public class Ipv4AddressCustomizer extends FutureJVppCustomizer + implements ListWriterCustomizer<Address, AddressKey>, Ipv4Writer { + + private static final Logger LOG = LoggerFactory.getLogger(Ipv4AddressCustomizer.class); + private final NamingContext interfaceContext; + private final SubnetValidator subnetValidator; + + Ipv4AddressCustomizer(@Nonnull final FutureJVppCore futureJVppCore, @Nonnull final NamingContext interfaceContext, + @Nonnull final SubnetValidator subnetValidator) { + super(futureJVppCore); + this.interfaceContext = checkNotNull(interfaceContext, "Interface context cannot be null"); + this.subnetValidator = checkNotNull(subnetValidator, "Subnet validator cannot be null"); + } + + public Ipv4AddressCustomizer(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final NamingContext interfaceContext) { + this(futureJVppCore, interfaceContext, new SubnetValidator()); + } + + @Override + public void writeCurrentAttributes(InstanceIdentifier<Address> id, Address dataAfter, WriteContext writeContext) + throws WriteFailedException { + + final String interfaceName = id.firstKeyOf(Interface.class).getName(); + final int interfaceIndex = interfaceContext.getIndex(interfaceName, writeContext.getMappingContext()); + + // checks whether address is not from same subnet of some address already defined on this interface + try { + + final InstanceIdentifier<Ipv4> parentId = RWUtils.cutId(id, InstanceIdentifier.create(Ipv4.class)); + final Optional<Ipv4> ipv4Optional = writeContext.readAfter(parentId); + + //no need to check isPresent() - we are inside of address customizer, therefore there must be Address data + //that is being processed by infrastructure + + subnetValidator.checkNotAddingToSameSubnet(ipv4Optional.get().getAddress()); + } catch (SubnetValidationException e) { + throw new WriteFailedException(id, e); + } + + setAddress(true, id, interfaceName, interfaceIndex, dataAfter, writeContext); + } + + @Override + public void updateCurrentAttributes(InstanceIdentifier<Address> id, Address dataBefore, Address dataAfter, + WriteContext writeContext) throws WriteFailedException { + throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, + new UnsupportedOperationException("Operation not supported")); + } + + @Override + public void deleteCurrentAttributes(InstanceIdentifier<Address> id, Address dataBefore, WriteContext writeContext) + throws WriteFailedException { + + final String interfaceName = id.firstKeyOf(Interface.class).getName(); + final int interfaceIndex = interfaceContext.getIndex(interfaceName, writeContext.getMappingContext()); + + setAddress(false, id, interfaceName, interfaceIndex, dataBefore, writeContext); + } + + private void setAddress(boolean add, final InstanceIdentifier<Address> id, final String interfaceName, + final int interfaceIndex, final Address address, + final WriteContext writeContext) throws WriteFailedException { + + Subnet subnet = address.getSubnet(); + + if (subnet instanceof PrefixLength) { + setPrefixLengthSubnet(add, id, interfaceName, interfaceIndex, address, (PrefixLength) subnet); + } else if (subnet instanceof Netmask) { + setNetmaskSubnet(add, id, interfaceName, interfaceIndex, address, (Netmask) subnet); + } else { + LOG.error("Unable to handle subnet of type {}", subnet.getClass()); + throw new WriteFailedException(id, "Unable to handle subnet of type " + subnet.getClass()); + } + } + + private void setNetmaskSubnet(final boolean add, @Nonnull final InstanceIdentifier<Address> id, + @Nonnull final String interfaceName, final int interfaceIndex, + @Nonnull final Address address, @Nonnull final Netmask subnet) + throws WriteFailedException { + + LOG.debug("Setting Subnet(subnet-mask) for interface: {}(id={}). Subnet: {}, address: {}", + interfaceName, interfaceIndex, subnet, address); + + final DottedQuad netmask = subnet.getNetmask(); + checkNotNull(netmask, "netmask value should not be null"); + + final byte subnetLength = getSubnetMaskLength(netmask.getValue()); + addDelAddress(getFutureJVpp(), add, id, interfaceIndex, address.getIp(), subnetLength); + } + + private void setPrefixLengthSubnet(final boolean add, @Nonnull final InstanceIdentifier<Address> id, + @Nonnull final String interfaceName, final int interfaceIndex, + @Nonnull final Address address, @Nonnull final PrefixLength subnet) + throws WriteFailedException { + LOG.debug("Setting Subnet(prefix-length) for interface: {}(id={}). Subnet: {}, address: {}", + interfaceName, interfaceIndex, subnet, address); + + addDelAddress(getFutureJVpp(), add, id, interfaceIndex, address.getIp(), + subnet.getPrefixLength().byteValue()); + + LOG.debug("Subnet(prefix-length) set successfully for interface: {}(id={}). Subnet: {}, address: {}", + interfaceName, interfaceIndex, subnet, address); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/Ipv4Customizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/Ipv4Customizer.java new file mode 100644 index 000000000..ff86331b1 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/Ipv4Customizer.java @@ -0,0 +1,56 @@ +/* + * 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.v3po.interfaces.ip; + +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.honeycomb.translate.write.WriteContext; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Ipv4Customizer extends FutureJVppCustomizer implements WriterCustomizer<Ipv4> { + + private static final Logger LOG = LoggerFactory.getLogger(Ipv4Customizer.class); + + public Ipv4Customizer(final FutureJVppCore vppApi) { + super(vppApi); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Ipv4> id, + @Nonnull final Ipv4 dataAfter, @Nonnull final WriteContext writeContext) { + LOG.debug("Handling Ipv4 leaves (mtu, forwarding) is not supported by VPP API. Ignoring configuration"); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Ipv4> id, + @Nonnull final Ipv4 dataBefore, @Nonnull final Ipv4 dataAfter, + @Nonnull final WriteContext writeContext) { + LOG.debug("Handling Ipv4 leaves (mtu, forwarding) is not supported by VPP API. Ignoring configuration"); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Ipv4> id, + @Nonnull final Ipv4 dataBefore, @Nonnull final WriteContext writeContext) { + LOG.debug("Handling Ipv4 leaves (mtu, forwarding) is not supported by VPP API. Ignoring configuration"); + } + +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/Ipv4NeighbourCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/Ipv4NeighbourCustomizer.java new file mode 100644 index 000000000..1a1c2d2c0 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/Ipv4NeighbourCustomizer.java @@ -0,0 +1,126 @@ +/* + * 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.v3po.interfaces.ip; + +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 io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.hc2vpp.common.translate.util.AddressTranslator; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.IpNeighborAddDel; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Neighbor; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.NeighborKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Customizer for writing {@link Neighbor} for {@link Ipv4}. + */ +public class Ipv4NeighbourCustomizer extends FutureJVppCustomizer + implements ListWriterCustomizer<Neighbor, NeighborKey>, ByteDataTranslator, AddressTranslator, + JvppReplyConsumer { + + + private static final Logger LOG = LoggerFactory.getLogger(Ipv4NeighbourCustomizer.class); + final NamingContext interfaceContext; + + public Ipv4NeighbourCustomizer(final FutureJVppCore futureJVppCore, final NamingContext interfaceContext) { + super(futureJVppCore); + this.interfaceContext = interfaceContext; + } + + @Override + public void writeCurrentAttributes(@Nonnull InstanceIdentifier<Neighbor> id, @Nonnull Neighbor dataAfter, + @Nonnull WriteContext writeContext) + throws WriteFailedException { + + checkNotNull(dataAfter, "Cannot write null neighbour"); + checkArgument(id.firstKeyOf(Interface.class) != null, "No parent interface key found"); + + LOG.debug("Processing request for Neigbour write"); + String interfaceName = id.firstKeyOf(Interface.class).getName(); + MappingContext mappingContext = writeContext.getMappingContext(); + + checkState(interfaceContext.containsIndex(interfaceName, mappingContext), + "Mapping does not contains mapping for provider interface name ".concat(interfaceName)); + + LOG.debug("Parent interface index found"); + addDelNeighbourAndReply(id, true, + interfaceContext.getIndex(interfaceName, mappingContext), dataAfter); + LOG.info("Neighbour successfully written"); + } + + @Override + public void updateCurrentAttributes(@Nonnull InstanceIdentifier<Neighbor> id, @Nonnull Neighbor dataBefore, + @Nonnull Neighbor dataAfter, + @Nonnull WriteContext writeContext) throws WriteFailedException { + throw new UnsupportedOperationException("Operation not supported"); + } + + @Override + public void deleteCurrentAttributes(@Nonnull InstanceIdentifier<Neighbor> id, @Nonnull Neighbor dataBefore, + @Nonnull WriteContext writeContext) + throws WriteFailedException { + + checkNotNull(dataBefore, "Cannot delete null neighbour"); + checkArgument(id.firstKeyOf(Interface.class) != null, "No parent interface key found"); + + LOG.debug("Processing request for Neigbour delete"); + String interfaceName = id.firstKeyOf(Interface.class).getName(); + MappingContext mappingContext = writeContext.getMappingContext(); + + checkState(interfaceContext.containsIndex(interfaceName, mappingContext), + "Mapping does not contains mapping for provider interface name %s", interfaceName); + + LOG.debug("Parent interface[{}] index found", interfaceName); + + addDelNeighbourAndReply(id, false, + interfaceContext.getIndex(interfaceName, mappingContext), dataBefore); + LOG.info("Neighbour {} successfully deleted", id); + } + + private void addDelNeighbourAndReply(InstanceIdentifier<Neighbor> id, boolean add, int parentInterfaceIndex, + Neighbor data) throws WriteFailedException { + + IpNeighborAddDel request = new IpNeighborAddDel(); + + request.isAdd = booleanToByte(add); + request.isIpv6 = 0; + request.isStatic = 1; + request.dstAddress = ipv4AddressNoZoneToArray(data.getIp()); + request.macAddress = parseMac(data.getLinkLayerAddress().getValue()); + request.swIfIndex = parentInterfaceIndex; + + //TODO HONEYCOMB-182 if it is necessary for future use ,make adjustments to be able to set vrfid + //request.vrfId + getReplyForWrite(getFutureJVpp().ipNeighborAddDel(request).toCompletableFuture(), id); + } +}
\ No newline at end of file diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/Ipv4Writer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/Ipv4Writer.java new file mode 100644 index 000000000..36e1800e1 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/Ipv4Writer.java @@ -0,0 +1,108 @@ +/* + * 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.v3po.interfaces.ip; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.Ipv4Translator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.SwInterfaceAddDelAddress; +import io.fd.vpp.jvpp.core.dto.SwInterfaceAddDelAddressReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Utility class providing Ipv4 CUD support. + */ +public interface Ipv4Writer extends ByteDataTranslator, Ipv4Translator, JvppReplyConsumer { + + int DOTTED_QUAD_MASK_LENGTH = 4; + int IPV4_ADDRESS_PART_BITS_COUNT = 8; + int NETMASK_PART_LIMIT = 256; // 2 power to 8 + + default void addDelAddress(@Nonnull final FutureJVppCore futureJVppCore, final boolean add, + final InstanceIdentifier<?> id, + @Nonnegative final int ifaceId, + @Nonnull final Ipv4AddressNoZone address, @Nonnegative final byte prefixLength) + throws WriteFailedException { + checkArgument(prefixLength > 0, "Invalid prefix length"); + checkNotNull(address, "address should not be null"); + + final byte[] addressBytes = ipv4AddressNoZoneToArray(address); + + final CompletionStage<SwInterfaceAddDelAddressReply> swInterfaceAddDelAddressReplyCompletionStage = + futureJVppCore.swInterfaceAddDelAddress( + getSwInterfaceAddDelAddressRequest(ifaceId, booleanToByte(add) /* isAdd */, + (byte) 0 /* isIpv6 */, (byte) 0 /* delAll */, prefixLength, addressBytes)); + + getReplyForWrite(swInterfaceAddDelAddressReplyCompletionStage.toCompletableFuture(), id); + } + + default SwInterfaceAddDelAddress getSwInterfaceAddDelAddressRequest(final int swIfc, final byte isAdd, + final byte ipv6, final byte deleteAll, + final byte length, final byte[] addr) { + final SwInterfaceAddDelAddress swInterfaceAddDelAddress = new SwInterfaceAddDelAddress(); + swInterfaceAddDelAddress.swIfIndex = swIfc; + swInterfaceAddDelAddress.isAdd = isAdd; + swInterfaceAddDelAddress.isIpv6 = ipv6; + swInterfaceAddDelAddress.delAll = deleteAll; + swInterfaceAddDelAddress.address = addr; + swInterfaceAddDelAddress.addressLength = length; + return swInterfaceAddDelAddress; + } + + /** + * Returns the prefix size in bits of the specified subnet mask. Example: For the subnet mask 255.255.255.128 it + * returns 25 while for 255.0.0.0 it returns 8. If the passed subnetMask array is not complete or contains not only + * leading ones, IllegalArgumentExpression is thrown + * + * @param mask the subnet mask in dot notation 255.255.255.255 + * @return the prefix length as number of bits + */ + default byte getSubnetMaskLength(final String mask) { + String[] maskParts = mask.split("\\."); + + checkArgument(maskParts.length == DOTTED_QUAD_MASK_LENGTH, + "Network mask %s is not in Quad Dotted Decimal notation!", mask); + + long maskAsNumber = 0; + for (int i = 0; i < DOTTED_QUAD_MASK_LENGTH; i++) { + maskAsNumber <<= IPV4_ADDRESS_PART_BITS_COUNT; + int value = Integer.parseInt(maskParts[i]); + checkArgument(value < NETMASK_PART_LIMIT, "Network mask %s contains invalid number(s) over 255!", mask); + checkArgument(value >= 0, "Network mask %s contains invalid negative number(s)!", mask); + maskAsNumber += value; + } + + String bits = Long.toBinaryString(maskAsNumber); + checkArgument(bits.length() == IPV4_ADDRESS_PART_BITS_COUNT * DOTTED_QUAD_MASK_LENGTH, + "Incorrect network mask %s", mask); + final int leadingOnes = bits.indexOf('0'); + checkArgument(leadingOnes != -1, "Broadcast address %s is not allowed!", mask); + checkArgument(bits.substring(leadingOnes).indexOf('1') == -1, + "Non-contiguous network mask %s is not allowed!", mask); + return (byte) leadingOnes; + } + +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/Ipv6Customizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/Ipv6Customizer.java new file mode 100644 index 000000000..85f190d83 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/Ipv6Customizer.java @@ -0,0 +1,55 @@ +/* + * 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.v3po.interfaces.ip; + +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.honeycomb.translate.write.WriteContext; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv6; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Ipv6Customizer extends FutureJVppCustomizer implements WriterCustomizer<Ipv6> { + + private static final Logger LOG = LoggerFactory.getLogger(Ipv6Customizer.class); + + public Ipv6Customizer(final FutureJVppCore vppApi) { + super(vppApi); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Ipv6> id, + @Nonnull final Ipv6 dataAfter, @Nonnull final WriteContext writeContext) { + LOG.warn("Unsupported, ignoring configuration {}", dataAfter); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Ipv6> id, + @Nonnull final Ipv6 dataBefore, @Nonnull final Ipv6 dataAfter, + @Nonnull final WriteContext writeContext) { + LOG.warn("Unsupported, ignoring configuration {}", dataAfter); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Ipv6> id, + @Nonnull final Ipv6 dataBefore, @Nonnull final WriteContext writeContext) { + LOG.warn("Unsupported, ignoring configuration delete {}", id); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/SubInterfaceIpv4AddressCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/SubInterfaceIpv4AddressCustomizer.java new file mode 100644 index 000000000..214ff36c4 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/SubInterfaceIpv4AddressCustomizer.java @@ -0,0 +1,131 @@ +/* + * 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.v3po.interfaces.ip; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.hc2vpp.v3po.util.SubInterfaceUtils; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DottedQuad; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.ipv4.Address; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.ipv4.AddressKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.ipv4.address.Subnet; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.ipv4.address.subnet.Netmask; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.ipv4.address.subnet.PrefixLength; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Write customizer for sub-interface {@link Address} + */ +public class SubInterfaceIpv4AddressCustomizer extends FutureJVppCustomizer + implements ListWriterCustomizer<Address, AddressKey>, Ipv4Writer { + + private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceIpv4AddressCustomizer.class); + private final NamingContext interfaceContext; + + public SubInterfaceIpv4AddressCustomizer(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final NamingContext interfaceContext) { + super(futureJVppCore); + this.interfaceContext = checkNotNull(interfaceContext, "interface context should not be null"); + } + + @Override + public void writeCurrentAttributes(InstanceIdentifier<Address> id, Address dataAfter, WriteContext writeContext) + throws WriteFailedException { + setAddress(true, id, dataAfter, writeContext); + } + + @Override + public void updateCurrentAttributes(InstanceIdentifier<Address> id, Address dataBefore, Address dataAfter, + WriteContext writeContext) throws WriteFailedException { + throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, + new UnsupportedOperationException("Operation not supported")); + } + + @Override + public void deleteCurrentAttributes(InstanceIdentifier<Address> id, Address dataBefore, WriteContext writeContext) + throws WriteFailedException { + setAddress(false, id, dataBefore, writeContext); + } + + private void setAddress(boolean add, final InstanceIdentifier<Address> id, final Address address, + final WriteContext writeContext) throws WriteFailedException { + + final String interfaceName = id.firstKeyOf(Interface.class).getName(); + final String subInterfaceName = getSubInterfaceName(id); + final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext()); + + Subnet subnet = address.getSubnet(); + + if (subnet instanceof PrefixLength) { + setPrefixLengthSubnet(add, id, interfaceName, subInterfaceIndex, address, (PrefixLength) subnet); + } else if (subnet instanceof Netmask) { + setNetmaskSubnet(add, id, interfaceName, subInterfaceIndex, address, (Netmask) subnet); + } else { + LOG.error("Unable to handle subnet of type {}", subnet.getClass()); + throw new WriteFailedException(id, "Unable to handle subnet of type " + subnet.getClass()); + } + } + + private String getSubInterfaceName(@Nonnull final InstanceIdentifier<Address> id) { + final InterfaceKey parentInterfacekey = id.firstKeyOf(Interface.class); + final SubInterfaceKey subInterfacekey = id.firstKeyOf(SubInterface.class); + return SubInterfaceUtils + .getSubInterfaceName(parentInterfacekey.getName(), subInterfacekey.getIdentifier().intValue()); + } + + private void setNetmaskSubnet(final boolean add, @Nonnull final InstanceIdentifier<Address> id, + @Nonnull final String subInterfaceName, final int subInterfaceIndex, + @Nonnull final Address address, @Nonnull final Netmask subnet) + throws WriteFailedException { + + LOG.debug("Setting Subnet(subnet-mask) for sub-interface: {}(id={}). Subnet: {}, address: {}", + subInterfaceName, subInterfaceIndex, subnet, address); + + final DottedQuad netmask = subnet.getNetmask(); + checkNotNull(netmask, "netmask value should not be null"); + + final byte subnetLength = getSubnetMaskLength(netmask.getValue()); + addDelAddress(getFutureJVpp(), add, id, subInterfaceIndex, address.getIp(), subnetLength); + } + + private void setPrefixLengthSubnet(final boolean add, @Nonnull final InstanceIdentifier<Address> id, + @Nonnull final String subInterfaceName, final int subInterfaceIndex, + @Nonnull final Address address, @Nonnull final PrefixLength subnet) + throws WriteFailedException { + LOG.debug("Setting Subnet(prefix-length) for sub-interface: {}(id={}). Subnet: {}, address: {}", + subInterfaceName, subInterfaceIndex, subnet, address); + + addDelAddress(getFutureJVpp(), add, id, subInterfaceIndex, address.getIp(), + subnet.getPrefixLength().byteValue()); + + LOG.debug("Subnet(prefix-length) set successfully for sub-interface: {}(id={}). Subnet: {}, address: {}", + subInterfaceName, subInterfaceIndex, subnet, address); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/subnet/validation/SubnetValidationException.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/subnet/validation/SubnetValidationException.java new file mode 100644 index 000000000..001923093 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/subnet/validation/SubnetValidationException.java @@ -0,0 +1,50 @@ +/* + * 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.v3po.interfaces.ip.subnet.validation; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collection; +import javax.annotation.Nonnull; +import org.apache.commons.lang3.StringUtils; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Address; + +/** + * Thrown as negative result of subnet validation + */ +public class SubnetValidationException extends Exception { + + private SubnetValidationException(@Nonnull final String message) { + super(message); + } + + public static SubnetValidationException forConflictingData(@Nonnull final Short prefix, @Nonnull Collection<Address> addresses) { + return new SubnetValidationException( + "Attempt to define multiple addresses for same subnet[prefixLen = " + prefixToString(prefix) + "], " + + "addresses : " + addressesToString(addresses)); + } + + private static String prefixToString(final Short prefix) { + return checkNotNull(prefix, "Cannot create " + SubnetValidationException.class.getName() + " for null prefix") + .toString(); + } + + private static String addressesToString(final Collection<Address> addresses) { + return StringUtils.join(checkNotNull(addresses, + "Cannot create " + SubnetValidationException.class.getName() + " for null address list"), " | "); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/subnet/validation/SubnetValidator.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/subnet/validation/SubnetValidator.java new file mode 100644 index 000000000..c5b011e07 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/ip/subnet/validation/SubnetValidator.java @@ -0,0 +1,80 @@ +/* + * 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.v3po.interfaces.ip.subnet.validation; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Function; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import io.fd.hc2vpp.v3po.interfaces.ip.Ipv4Writer; +import java.util.List; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.Subnet; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.Netmask; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.PrefixLength; + +/** + * Validator for detecting if there is an attempt to assign multiple addresses from same subnet + */ +public class SubnetValidator implements Ipv4Writer { + + /** + * Checks whether data provided for writing are not in collision with already existing data + */ + public void checkNotAddingToSameSubnet(@Nonnull final List<Address> addresses) + throws SubnetValidationException { + + final Multimap<Short, Address> prefixLengthRegister = Multimaps.index(addresses, toPrefixLength()); + final int keySetSize = prefixLengthRegister.keySet().size(); + + if (keySetSize == 0 || keySetSize == addresses.size()) { + //this means that every key is unique(has only one value) or no addresses were prefix-length based ,so there is no conflict + return; + } + + //finds conflicting prefix + final Short conflictingPrefix = prefixLengthRegister.keySet() + .stream() + .filter(a -> prefixLengthRegister.get(a).size() > 1) + .findFirst() + .get(); + + //and reports it with affected addresses + throw SubnetValidationException + .forConflictingData(conflictingPrefix, prefixLengthRegister.get(conflictingPrefix)); + } + + private Function<Address, Short> toPrefixLength() { + return (final Address address) -> { + final Subnet subnet = address.getSubnet(); + + if (subnet instanceof PrefixLength) { + return ((PrefixLength) subnet).getPrefixLength(); + } + + if (address.getSubnet() instanceof Netmask) { + return (short) getSubnetMaskLength( + checkNotNull(((Netmask) subnet).getNetmask(), "No netmask defined for %s", subnet) + .getValue()); + } + + throw new IllegalArgumentException("Unsupported subnet : " + subnet); + }; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/pbb/PbbRewriteCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/pbb/PbbRewriteCustomizer.java new file mode 100644 index 000000000..3e8d82399 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/pbb/PbbRewriteCustomizer.java @@ -0,0 +1,130 @@ +/* + * 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.v3po.interfaces.pbb; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.MacTranslator; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.VppBaseCallException; +import io.fd.vpp.jvpp.core.dto.L2InterfacePbbTagRewrite; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.concurrent.TimeoutException; +import javax.annotation.Nonnull; +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.pbb.rev161214.interfaces._interface.PbbRewrite; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class PbbRewriteCustomizer extends FutureJVppCustomizer + implements WriterCustomizer<PbbRewrite>, MacTranslator, JvppReplyConsumer { + + private static final int OPERATION_DISABLE = 0; + + private final NamingContext interfaceNamingContext; + + public PbbRewriteCustomizer(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final NamingContext interfaceNamingContext) { + super(futureJVppCore); + this.interfaceNamingContext = checkNotNull(interfaceNamingContext, "Interface naming context cannot be null"); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<PbbRewrite> id, + @Nonnull final PbbRewrite dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + try { + setPbbRewrite(id, dataAfter, writeContext, false); + } catch (TimeoutException | VppBaseCallException e) { + throw new WriteFailedException.CreateFailedException(id, dataAfter, e); + } + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<PbbRewrite> id, + @Nonnull final PbbRewrite dataBefore, @Nonnull final PbbRewrite dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + try { + setPbbRewrite(id, dataAfter, writeContext, false); + } catch (TimeoutException | VppBaseCallException e) { + throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, e); + } + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<PbbRewrite> id, + @Nonnull final PbbRewrite dataBefore, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + + try { + setPbbRewrite(id, dataBefore, writeContext, true); + } catch (TimeoutException | VppBaseCallException e) { + throw new WriteFailedException.DeleteFailedException(id, e); + } + } + + private void setPbbRewrite(final InstanceIdentifier<PbbRewrite> id, final PbbRewrite data, + final WriteContext writeContext, final boolean disable) + throws TimeoutException, VppBaseCallException { + final String interfaceName = checkNotNull(id.firstKeyOf(Interface.class), "Interface key not found").getName(); + + final L2InterfacePbbTagRewrite request = new L2InterfacePbbTagRewrite(); + + //checking all attributes in preconditions(pbb-rewrite is subcontainer, so there can't be mandatory statements) + request.swIfIndex = interfaceNamingContext.getIndex(interfaceName, writeContext.getMappingContext()); + request.bDmac = parseMac(verifiedDestinationAddress(data)); + request.bSmac = parseMac(verifiedSourceAddress(data)); + request.bVlanid = verifiedBVlanId(data); + request.iSid = verifiedISid(data); + request.vtrOp = verifiedOperation(data, disable); + + //not sure whats gonna happen to this attribute, so its left optional for now + if (data.getOuterTag() != null) { + request.outerTag = data.getOuterTag().shortValue(); + } + + getReply(getFutureJVpp().l2InterfacePbbTagRewrite(request).toCompletableFuture()); + } + + private String verifiedDestinationAddress(final PbbRewrite data) { + return checkNotNull(data.getDestinationAddress(), "Destination address cannot be null").getValue(); + } + + private String verifiedSourceAddress(final PbbRewrite data) { + return checkNotNull(data.getSourceAddress(), "Destination address cannot be null").getValue(); + } + + private byte verifiedBVlanId(final PbbRewrite data) { + return (byte) checkNotNull(data.getBVlanTagVlanId(), "BVlan id cannot be null").shortValue(); + } + + private int verifiedISid(final PbbRewrite data) { + return checkNotNull(data.getITagIsid(), "ISid cannot be null").intValue(); + } + + // if disabled ,then uses non-public allowed value 0, which is equal to operation disable + private int verifiedOperation(final PbbRewrite data, final boolean disable) { + + return disable + ? OPERATION_DISABLE + : checkNotNull(data.getInterfaceOperation(), "Operation cannot be null").getIntValue(); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/EthernetCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/EthernetCustomizer.java new file mode 100644 index 000000000..33b71b1e4 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/EthernetCustomizer.java @@ -0,0 +1,108 @@ +/* + * 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.v3po.interfacesstate; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingReaderCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceStateAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.Ethernet; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.EthernetBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class EthernetCustomizer extends FutureJVppCustomizer + implements InitializingReaderCustomizer<Ethernet, EthernetBuilder>, InterfaceDataTranslator { + + private static final Logger LOG = LoggerFactory.getLogger(EthernetCustomizer.class); + private NamingContext interfaceContext; + + public EthernetCustomizer(@Nonnull final FutureJVppCore jvpp, + @Nonnull final NamingContext interfaceContext) { + super(jvpp); + this.interfaceContext = interfaceContext; + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, + @Nonnull final Ethernet readValue) { + ((VppInterfaceStateAugmentationBuilder) parentBuilder).setEthernet(readValue); + } + + @Nonnull + @Override + public EthernetBuilder getBuilder(@Nonnull InstanceIdentifier<Ethernet> id) { + return new EthernetBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Ethernet> id, + @Nonnull final EthernetBuilder builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + + final InterfaceKey key = id.firstKeyOf(Interface.class); + final SwInterfaceDetails iface = getVppInterfaceDetails(getFutureJVpp(), id, key.getName(), + interfaceContext.getIndex(key.getName(), ctx.getMappingContext()), ctx.getModificationCache(), LOG); + + if (iface.linkMtu != 0) { + builder.setMtu((int) iface.linkMtu); + } + + switch (iface.linkDuplex) { + case 1: + builder.setDuplex(Ethernet.Duplex.Half); + break; + case 2: + builder.setDuplex(Ethernet.Duplex.Full); + break; + default: + break; + } + } + + @Override + public Initialized<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Ethernet> init( + @Nonnull final InstanceIdentifier<Ethernet> id, + @Nonnull final Ethernet readValue, + @Nonnull final ReadContext ctx) { + return Initialized.create(getCfgId(id), + new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.EthernetBuilder() + .setMtu(readValue.getMtu()) + .build()); + } + + private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Ethernet> getCfgId( + final InstanceIdentifier<Ethernet> id) { + return InterfaceCustomizer.getCfgId(RWUtils.cutId(id, Interface.class)) + .augmentation(VppInterfaceAugmentation.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Ethernet.class); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/GreCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/GreCustomizer.java new file mode 100644 index 000000000..b31ab74d5 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/GreCustomizer.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016 Intel 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.v3po.interfacesstate; + +import static com.google.common.base.Preconditions.checkState; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingReaderCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.dto.GreTunnelDetails; +import io.fd.vpp.jvpp.core.dto.GreTunnelDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.GreTunnelDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.GreTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceStateAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.Gre; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.GreBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GreCustomizer extends FutureJVppCustomizer + implements InitializingReaderCustomizer<Gre, GreBuilder>, InterfaceDataTranslator { + + private static final Logger LOG = LoggerFactory.getLogger(GreCustomizer.class); + private NamingContext interfaceContext; + + public GreCustomizer(@Nonnull final FutureJVppCore jvpp, @Nonnull final NamingContext interfaceContext) { + super(jvpp); + this.interfaceContext = interfaceContext; + } + + @Override + public void merge(@Nonnull Builder<? extends DataObject> parentBuilder, + @Nonnull Gre readValue) { + ((VppInterfaceStateAugmentationBuilder) parentBuilder).setGre(readValue); + } + + @Nonnull + @Override + public GreBuilder getBuilder(@Nonnull InstanceIdentifier<Gre> id) { + return new GreBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Gre> id, + @Nonnull final GreBuilder builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + final InterfaceKey key = id.firstKeyOf(Interface.class); + final int index = interfaceContext.getIndex(key.getName(), ctx.getMappingContext()); + if (!isInterfaceOfType(getFutureJVpp(), ctx.getModificationCache(), id, index, GreTunnel.class, LOG)) { + return; + } + + LOG.debug("Reading attributes for gre tunnel: {}", key.getName()); + // Dump just a single + final GreTunnelDump request = new GreTunnelDump(); + request.swIfIndex = index; + + final CompletionStage<GreTunnelDetailsReplyDump> swInterfaceGreDetailsReplyDumpCompletionStage = + getFutureJVpp().greTunnelDump(request); + final GreTunnelDetailsReplyDump reply = + getReplyForRead(swInterfaceGreDetailsReplyDumpCompletionStage.toCompletableFuture(), id); + + // VPP keeps gre tunnel interfaces even after they were deleted (optimization) + // However there ar no longer any gre tunnel specific fields assigned to it and this call + // returns nothing + if (reply == null || reply.greTunnelDetails == null || reply.greTunnelDetails.isEmpty()) { + LOG.debug( + "Gre tunnel {}, id {} has no attributes assigned in VPP. Probably is a leftover interface placeholder" + + "after delete", key.getName(), index); + return; + } + + checkState(reply.greTunnelDetails.size() == 1, + "Unexpected number of returned gre tunnels: {} for tunnel: {}", reply.greTunnelDetails, + key.getName()); + LOG.trace("Gre tunnel: {} attributes returned from VPP: {}", key.getName(), reply); + + final GreTunnelDetails swInterfaceGreDetails = reply.greTunnelDetails.get(0); + if (swInterfaceGreDetails.isIpv6 == 1) { + final Ipv6Address dstIpv6 = + new Ipv6Address(parseAddress(swInterfaceGreDetails.dstAddress).getHostAddress()); + builder.setDst(new IpAddress(dstIpv6)); + final Ipv6Address srcIpv6 = + new Ipv6Address(parseAddress(swInterfaceGreDetails.srcAddress).getHostAddress()); + builder.setSrc(new IpAddress(srcIpv6)); + } else { + final byte[] dstBytes = Arrays.copyOfRange(swInterfaceGreDetails.dstAddress, 0, 4); + final Ipv4Address dstIpv4 = new Ipv4Address(parseAddress(dstBytes).getHostAddress()); + builder.setDst(new IpAddress(dstIpv4)); + final byte[] srcBytes = Arrays.copyOfRange(swInterfaceGreDetails.srcAddress, 0, 4); + final Ipv4Address srcIpv4 = new Ipv4Address(parseAddress(srcBytes).getHostAddress()); + builder.setSrc(new IpAddress(srcIpv4)); + } + builder.setOuterFibId((long) swInterfaceGreDetails.outerFibId); + LOG.debug("Gre tunnel: {}, id: {} attributes read as: {}", key.getName(), index, builder); + } + + @Nonnull + private static InetAddress parseAddress(@Nonnull final byte[] addr) { + try { + return InetAddress.getByAddress(addr); + } catch (UnknownHostException e) { + throw new IllegalArgumentException("Cannot create InetAddress from " + Arrays.toString(addr), e); + } + } + + @Override + public Initialized<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Gre> init( + @Nonnull final InstanceIdentifier<Gre> id, @Nonnull final Gre readValue, @Nonnull final ReadContext ctx) { + return Initialized.create(getCfgId(id), + new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.GreBuilder() + .setDst(readValue.getDst()) + .setSrc(readValue.getSrc()) + .setOuterFibId(readValue.getOuterFibId()) + .build()); + } + + private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Gre> getCfgId( + final InstanceIdentifier<Gre> id) { + return InterfaceCustomizer.getCfgId(RWUtils.cutId(id, Interface.class)) + .augmentation(VppInterfaceAugmentation.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Gre.class); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/InterconnectionReadUtils.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/InterconnectionReadUtils.java new file mode 100644 index 000000000..3893d37a1 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/InterconnectionReadUtils.java @@ -0,0 +1,125 @@ +/* + * 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.v3po.interfacesstate; + +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.dto.BridgeDomainDetails; +import io.fd.vpp.jvpp.core.dto.BridgeDomainDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.BridgeDomainDump; +import io.fd.vpp.jvpp.core.dto.BridgeDomainSwIfDetails; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.Interconnection; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.interconnection.BridgeBasedBuilder; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility class providing Interconnection read support. + */ +final class InterconnectionReadUtils implements InterfaceDataTranslator { + + private static final Logger LOG = LoggerFactory.getLogger(InterconnectionReadUtils.class); + + private final FutureJVppCore futureJVppCore; + private final NamingContext interfaceContext; + private final NamingContext bridgeDomainContext; + + InterconnectionReadUtils(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final NamingContext interfaceContext, + @Nonnull final NamingContext bridgeDomainContext) { + this.futureJVppCore = requireNonNull(futureJVppCore, "futureJVppCore should not be null"); + this.interfaceContext = requireNonNull(interfaceContext, "interfaceContext should not be null"); + this.bridgeDomainContext = requireNonNull(bridgeDomainContext, "bridgeDomainContext should not be null"); + } + + @Nullable + Interconnection readInterconnection(@Nonnull final InstanceIdentifier<?> id, @Nonnull final String ifaceName, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + final int ifaceId = interfaceContext.getIndex(ifaceName, ctx.getMappingContext()); + + final SwInterfaceDetails iface = getVppInterfaceDetails(futureJVppCore, id, ifaceName, + ifaceId, ctx.getModificationCache(), LOG); + LOG.debug("Interface details for interface: {}, details: {}", ifaceName, iface); + + final BridgeDomainDetailsReplyDump dumpReply = getDumpReply(id); + final Optional<BridgeDomainSwIfDetails> bdForInterface = getBridgeDomainForInterface(ifaceId, dumpReply); + if (bdForInterface.isPresent()) { + final BridgeDomainSwIfDetails bdSwIfDetails = bdForInterface.get(); + final BridgeBasedBuilder bbBuilder = new BridgeBasedBuilder(); + bbBuilder.setBridgeDomain(bridgeDomainContext.getName(bdSwIfDetails.bdId, ctx.getMappingContext())); + + // Set BVI if the bridgeDomainDetails.bviSwIfIndex == current sw if index + final Optional<BridgeDomainDetails> bridgeDomainForInterface = + getBridgeDomainForInterface(dumpReply, bdForInterface.get().bdId); + // Since we already found an interface assigned to a bridge domain, the details for BD must be present + checkState(bridgeDomainForInterface.isPresent()); + if (bridgeDomainForInterface.get().bviSwIfIndex == ifaceId) { + bbBuilder.setBridgedVirtualInterface(true); + } else { + bbBuilder.setBridgedVirtualInterface(false); + } + + if (bdSwIfDetails.shg != 0) { + bbBuilder.setSplitHorizonGroup((short) bdSwIfDetails.shg); + } + return bbBuilder.build(); + } + // TODO HONEYCOMB-190 is there a way to check if interconnection is XconnectBased? + + return null; + } + + private Optional<BridgeDomainSwIfDetails> getBridgeDomainForInterface(final int ifaceId, + final BridgeDomainDetailsReplyDump reply) { + if (null == reply || null == reply.bridgeDomainSwIfDetails || reply.bridgeDomainSwIfDetails.isEmpty()) { + return Optional.empty(); + } + // interface can be added to only one BD only + return reply.bridgeDomainSwIfDetails.stream().filter(a -> a.swIfIndex == ifaceId).findFirst(); + } + + private Optional<BridgeDomainDetails> getBridgeDomainForInterface(final BridgeDomainDetailsReplyDump reply, + int bdId) { + return reply.bridgeDomainDetails.stream().filter(a -> a.bdId == bdId).findFirst(); + } + + private BridgeDomainDetailsReplyDump getDumpReply(@Nonnull final InstanceIdentifier<?> id) + throws ReadFailedException { + // We need to perform full bd dump, because there is no way + // to ask VPP for BD details given interface id/name (TODO HONEYCOMB-190 add it to vpp.api?) + // TODO HONEYCOMB-190 cache dump result + final BridgeDomainDump request = new BridgeDomainDump(); + request.bdId = -1; + + final CompletableFuture<BridgeDomainDetailsReplyDump> bdCompletableFuture = + futureJVppCore.bridgeDomainSwIfDump(request).toCompletableFuture(); + return getReplyForRead(bdCompletableFuture, id); + + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceCustomizer.java new file mode 100644 index 000000000..70bc0c62b --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceCustomizer.java @@ -0,0 +1,232 @@ +/* + * 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.v3po.interfacesstate; + +import io.fd.hc2vpp.v3po.DisabledInterfacesManager; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.ModificationCache; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingListReaderCustomizer; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +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.InterfacesStateBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.AdminStatus; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Customizer for reading ietf-interfaces:interfaces-state/interface. + */ +public class InterfaceCustomizer extends FutureJVppCustomizer + implements InitializingListReaderCustomizer<Interface, InterfaceKey, InterfaceBuilder>, ByteDataTranslator, + InterfaceDataTranslator { + + public static final String DUMPED_IFCS_CONTEXT_KEY = + InterfaceCustomizer.class.getName() + "dumpedInterfacesDuringGetAllIds"; + private static final Logger LOG = LoggerFactory.getLogger(InterfaceCustomizer.class); + private final NamingContext interfaceNamingContext; + private final DisabledInterfacesManager interfaceDisableContext; + + public InterfaceCustomizer(@Nonnull final FutureJVppCore jvpp, + @Nonnull final NamingContext interfaceNamingContext, + @Nonnull final DisabledInterfacesManager interfaceDisableContext) { + super(jvpp); + this.interfaceNamingContext = interfaceNamingContext; + this.interfaceDisableContext = interfaceDisableContext; + } + + public static void cacheInterfaceDump(final @Nonnull ReadContext context, final SwInterfaceDetailsReplyDump ifaces) { + context.getModificationCache().put(DUMPED_IFCS_CONTEXT_KEY, ifaces.swInterfaceDetails.stream() + .collect(Collectors.toMap(t -> t.swIfIndex, swInterfaceDetails -> swInterfaceDetails))); + } + + @Nonnull + @SuppressWarnings("unchecked") + public static Map<Integer, SwInterfaceDetails> getCachedInterfaceDump(@Nonnull final ModificationCache ctx) { + return ctx.get(DUMPED_IFCS_CONTEXT_KEY) == null + ? new HashMap<>() + // allow customizers to update the cache + : (Map<Integer, SwInterfaceDetails>) ctx.get(DUMPED_IFCS_CONTEXT_KEY); + } + + private static boolean isRegularInterface(final SwInterfaceDetails iface) { + return iface.subId == 0; + } + + @Nonnull + @Override + public InterfaceBuilder getBuilder(@Nonnull InstanceIdentifier<Interface> id) { + return new InterfaceBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull InstanceIdentifier<Interface> id, @Nonnull InterfaceBuilder builder, + @Nonnull ReadContext ctx) throws ReadFailedException { + LOG.debug("Reading attributes for interface: {}", id); + final String ifaceName = id.firstKeyOf(id.getTargetType()).getName(); + + final int index = interfaceNamingContext.getIndex(ifaceName, ctx.getMappingContext()); + + // Ignore disabled interface (such as deleted VXLAN tunnels) + if (interfaceDisableContext.isInterfaceDisabled(index, ctx.getMappingContext())) { + LOG.debug("Skipping disabled interface: {}", id); + return; + } + + // Pass cached details from getAllIds to getDetails to avoid additional dumps + final SwInterfaceDetails iface = getVppInterfaceDetails(getFutureJVpp(), id, ifaceName, + index, ctx.getModificationCache(), LOG); + LOG.debug("Interface details for interface: {}, details: {}", ifaceName, iface); + + if (!isRegularInterface(iface)) { + LOG.debug("Interface: {} is a sub-interface. Ignoring read request.", ifaceName); + return; + } + + builder.setName(ifaceName); + builder.setType(getInterfaceType(new String(iface.interfaceName).intern())); + builder.setIfIndex(vppIfIndexToYang(iface.swIfIndex)); + builder.setAdminStatus(1 == iface.adminUpDown + ? AdminStatus.Up + : AdminStatus.Down); + builder.setOperStatus(1 == iface.linkUpDown + ? OperStatus.Up + : OperStatus.Down); + if (0 != iface.linkSpeed) { + builder.setSpeed(vppInterfaceSpeedToYang(iface.linkSpeed)); + } + if (iface.l2AddressLength == 6) { + builder.setPhysAddress(new PhysAddress(vppPhysAddrToYang(iface.l2Address))); + } + LOG.trace("Base attributes read for interface: {} as: {}", ifaceName, builder); + } + + @Nonnull + @Override + public List<InterfaceKey> getAllIds(@Nonnull final InstanceIdentifier<Interface> id, + @Nonnull final ReadContext context) throws ReadFailedException { + final List<InterfaceKey> interfacesKeys; + LOG.trace("Dumping all interfaces to get all IDs"); + + final SwInterfaceDump request = new SwInterfaceDump(); + request.nameFilter = "".getBytes(); + request.nameFilterValid = 0; + + final CompletableFuture<SwInterfaceDetailsReplyDump> swInterfaceDetailsReplyDumpCompletableFuture = + getFutureJVpp().swInterfaceDump(request).toCompletableFuture(); + final SwInterfaceDetailsReplyDump ifaces = + getReplyForRead(swInterfaceDetailsReplyDumpCompletableFuture, id); + + if (null == ifaces || null == ifaces.swInterfaceDetails) { + LOG.debug("No interfaces for :{} found in VPP", id); + return Collections.emptyList(); + } + + // Cache interfaces dump in per-tx context to later be used in readCurrentAttributes + cacheInterfaceDump(context, ifaces); + + final MappingContext mappingCtx = context.getMappingContext(); + final Set<Integer> interfacesIdxs = ifaces.swInterfaceDetails.stream() + .filter(elt -> elt != null) + // Filter out disabled interfaces, dont read them + // This also prevents child readers in being invoked such as vxlan (which relies on disabling interfaces) + .filter(elt -> !interfaceDisableContext + .isInterfaceDisabled(elt.swIfIndex, mappingCtx)) + .map((elt) -> { + // Store interface name from VPP in context if not yet present + if (!interfaceNamingContext.containsName(elt.swIfIndex, mappingCtx)) { + interfaceNamingContext.addName(elt.swIfIndex, toString(elt.interfaceName), + mappingCtx); + } + LOG.trace("Interface with name: {}, VPP name: {} and index: {} found in VPP", + interfaceNamingContext.getName(elt.swIfIndex, mappingCtx), + elt.interfaceName, + elt.swIfIndex); + + return elt; + }) + // filter out sub-interfaces + .filter(InterfaceCustomizer::isRegularInterface) + .map(elt -> elt.swIfIndex) + .collect(Collectors.toSet()); + + // Clean disabled interfaces list + interfaceDisableContext.getDisabledInterfaces(mappingCtx).stream() + // Find indices not currently in VPP + .filter(interfacesIdxs::contains) + // Remove from disabled list ... not disabled if not existing + .forEach(idx -> interfaceDisableContext.removeDisabledInterface(idx, mappingCtx)); + + // Transform indices to keys + interfacesKeys = interfacesIdxs.stream() + .map(index -> new InterfaceKey(interfaceNamingContext.getName(index, context.getMappingContext()))) + .collect(Collectors.toList()); + + LOG.debug("Interfaces found in VPP: {}", interfacesKeys); + return interfacesKeys; + } + + @Override + public void merge(@Nonnull final org.opendaylight.yangtools.concepts.Builder<? extends DataObject> builder, + @Nonnull final List<Interface> readData) { + ((InterfacesStateBuilder) builder).setInterface(readData); + } + + @Override + public Initialized<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface> init( + @Nonnull final InstanceIdentifier<Interface> id, @Nonnull final Interface readValue, @Nonnull final ReadContext ctx) { + return Initialized.create(getCfgId(id), + new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder() + .setName(readValue.getName()) + .setType(readValue.getType()) + .setEnabled(AdminStatus.Up.equals(readValue.getAdminStatus())) + // Not present in interfaces-state + // .setLinkUpDownTrapEnable() + .build()); + } + + public static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface> getCfgId( + final InstanceIdentifier<Interface> id) { + return InstanceIdentifier.create(Interfaces.class).child( + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface.class, + new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey( + id.firstKeyOf(Interface.class).getName())); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceDataTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceDataTranslator.java new file mode 100644 index 000000000..a0d292a4f --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceDataTranslator.java @@ -0,0 +1,287 @@ +/* + * 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.v3po.interfacesstate; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +import io.fd.honeycomb.translate.ModificationCache; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.math.BigInteger; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletionStage; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.EthernetCsmacd; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Gauge64; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.GreTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.Loopback; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.Tap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VhostUser; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VxlanGpeTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VxlanTunnel; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; + +public interface InterfaceDataTranslator extends ByteDataTranslator, JvppReplyConsumer { + + Gauge64 vppLinkSpeed0 = new Gauge64(BigInteger.ZERO); + Gauge64 vppLinkSpeed1 = new Gauge64(BigInteger.valueOf(10L * 1000000)); + Gauge64 vppLinkSpeed2 = new Gauge64(BigInteger.valueOf(100L * 1000000)); + Gauge64 vppLinkSpeed4 = new Gauge64(BigInteger.valueOf(1000L * 1000000)); + Gauge64 vppLinkSpeed8 = new Gauge64(BigInteger.valueOf(10000L * 1000000)); + Gauge64 vppLinkSpeed16 = new Gauge64(BigInteger.valueOf(40000L * 1000000)); + Gauge64 vppLinkSpeed32 = new Gauge64(BigInteger.valueOf(100000L * 1000000)); + + char[] HEX_CHARS = "0123456789abcdef".toCharArray(); + + int PHYSICAL_ADDRESS_LENGTH = 6; + + Collector<SwInterfaceDetails, ?, SwInterfaceDetails> SINGLE_ITEM_COLLECTOR = + RWUtils.singleItemCollector(); + + /** + * Convert VPP's link speed bitmask to Yang type. 1 = 10M, 2 = 100M, 4 = 1G, 8 = 10G, 16 = 40G, 32 = 100G + * + * @param vppLinkSpeed Link speed in bitmask format from VPP. + * @return Converted value from VPP link speed + */ + default Gauge64 vppInterfaceSpeedToYang(byte vppLinkSpeed) { + switch (vppLinkSpeed) { + case 1: + return vppLinkSpeed1; + case 2: + return vppLinkSpeed2; + case 4: + return vppLinkSpeed4; + case 8: + return vppLinkSpeed8; + case 16: + return vppLinkSpeed16; + case 32: + return vppLinkSpeed32; + default: + return vppLinkSpeed0; + } + } + + default void appendHexByte(final StringBuilder sb, final byte b) { + final int v = b & 0xFF; + sb.append(HEX_CHARS[v >>> 4]); + sb.append(HEX_CHARS[v & 15]); + } + + /** + * Reads first 6 bytes of supplied byte array and converts to string as Yang dictates <p> Replace later with + * https://git.opendaylight.org/gerrit/#/c/34869/10/model/ietf/ietf-type- util/src/main/ + * java/org/opendaylight/mdsal/model/ietf/util/AbstractIetfYangUtil.java + * + * @param vppPhysAddress byte array of bytes in big endian order, constructing the network IF physical address. + * @return String like "aa:bb:cc:dd:ee:ff" + * @throws NullPointerException if vppPhysAddress is null + * @throws IllegalArgumentException if vppPhysAddress.length < 6 + */ + default String vppPhysAddrToYang(@Nonnull final byte[] vppPhysAddress) { + return vppPhysAddrToYang(vppPhysAddress, 0); + } + + default String vppPhysAddrToYang(@Nonnull final byte[] vppPhysAddress, final int startIndex) { + Objects.requireNonNull(vppPhysAddress, "Empty physical address bytes"); + final int endIndex = startIndex + PHYSICAL_ADDRESS_LENGTH; + checkArgument(endIndex <= vppPhysAddress.length, + "Invalid physical address size (%s) for given startIndex (%s), expected >= %s", vppPhysAddress.length, + startIndex, endIndex); + return printHexBinary(vppPhysAddress, startIndex, endIndex); + } + + default String printHexBinary(@Nonnull final byte[] bytes) { + Objects.requireNonNull(bytes, "bytes array should not be null"); + return printHexBinary(bytes, 0, bytes.length); + } + + default String printHexBinary(@Nonnull final byte[] bytes, final int startIndex, final int endIndex) { + StringBuilder str = new StringBuilder(); + + appendHexByte(str, bytes[startIndex]); + for (int i = startIndex + 1; i < endIndex; i++) { + str.append(":"); + appendHexByte(str, bytes[i]); + } + + return str.toString(); + } + + /** + * VPP's interface index is counted from 0, whereas ietf-interface's if-index is from 1. This function converts from + * VPP's interface index to YANG's interface index. + * + * @param vppIfIndex the sw interface index VPP reported. + * @return VPP's interface index incremented by one + */ + default int vppIfIndexToYang(int vppIfIndex) { + return vppIfIndex + 1; + } + + /** + * This function does the opposite of what {@link #vppIfIndexToYang(int)} does. + * + * @param yangIfIndex if-index from ietf-interfaces. + * @return VPP's representation of the if-index + */ + default int yangIfIndexToVpp(int yangIfIndex) { + checkArgument(yangIfIndex >= 1, "YANG if-index has invalid value %s", yangIfIndex); + return yangIfIndex - 1; + } + + + /** + * Queries VPP for interface description given interface key. + * + * @param futureJVppCore VPP Java Future API + * @param id InstanceIdentifier, which is passed in ReadFailedException + * @param name interface name + * @param index VPP index of the interface + * @param ctx per-tx scope context containing cached dump with all the interfaces. If the cache is not + * available or outdated, another dump will be performed. + * @return SwInterfaceDetails DTO or null if interface was not found + * @throws IllegalArgumentException If interface cannot be found + * @throws ReadFailedException If read operation had failed + */ + @Nonnull + default SwInterfaceDetails getVppInterfaceDetails(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final InstanceIdentifier<?> id, + @Nonnull final String name, final int index, + @Nonnull final ModificationCache ctx, + @Nonnull final Logger callerLogger) + throws ReadFailedException { + requireNonNull(futureJVppCore, "futureJVppCore should not be null"); + requireNonNull(name, "name should not be null"); + requireNonNull(ctx, "ctx should not be null"); + + final SwInterfaceDump request = new SwInterfaceDump(); + request.nameFilter = name.getBytes(); + request.nameFilterValid = 1; + + final Map<Integer, SwInterfaceDetails> allInterfaces = InterfaceCustomizer.getCachedInterfaceDump(ctx); + + // Returned cached if available + if (allInterfaces.containsKey(index)) { + return allInterfaces.get(index); + } + + SwInterfaceDetailsReplyDump ifaces; + + CompletionStage<SwInterfaceDetailsReplyDump> requestFuture = futureJVppCore.swInterfaceDump(request); + ifaces = getReplyForRead(requestFuture.toCompletableFuture(), id); + if (null == ifaces || null == ifaces.swInterfaceDetails || ifaces.swInterfaceDetails.isEmpty()) { + request.nameFilterValid = 0; + + callerLogger.warn("VPP returned null instead of interface by key {} and its not cached", name); + callerLogger.warn("Iterating through all the interfaces to find interface: {}", name); + + // Or else just perform full dump and do inefficient filtering + requestFuture = futureJVppCore.swInterfaceDump(request); + ifaces = getReplyForRead(requestFuture.toCompletableFuture(), id); + + // Update the cache + allInterfaces.clear(); + allInterfaces + .putAll(ifaces.swInterfaceDetails.stream().collect(Collectors.toMap(d -> d.swIfIndex, d -> d))); + + if (allInterfaces.containsKey(index)) { + return allInterfaces.get(index); + } + throw new IllegalArgumentException("Unable to find interface " + name); + } + + // SwInterfaceDump's name filter does prefix match, so we need additional filtering: + final SwInterfaceDetails iface = + ifaces.swInterfaceDetails.stream().filter(d -> d.swIfIndex == index).collect(SINGLE_ITEM_COLLECTOR); + allInterfaces.put(index, iface); // update the cache + return iface; + } + + /** + * Determine interface type based on its VPP name (relying on VPP's interface naming conventions) + * + * @param interfaceName VPP generated interface name + * @return Interface type + */ + @Nonnull + default Class<? extends InterfaceType> getInterfaceType(@Nonnull final String interfaceName) { + if (interfaceName.startsWith("tap")) { + return Tap.class; + } + + if (interfaceName.startsWith("vxlan_gpe")) { + return VxlanGpeTunnel.class; + } + + if (interfaceName.startsWith("vxlan")) { + return VxlanTunnel.class; + } + + if (interfaceName.startsWith("gre")) { + return GreTunnel.class; + } + + if (interfaceName.startsWith("VirtualEthernet")) { + return VhostUser.class; + } + + if (interfaceName.startsWith("loop")) { + return Loopback.class; + } + + return EthernetCsmacd.class; + } + + /** + * Check interface type. Uses interface details from VPP to determine. Uses {@link + * #getVppInterfaceDetails(FutureJVppCore, InstanceIdentifier, String, int, ModificationCache, Logger)} internally + * so tries to utilize cache before asking VPP. + */ + default boolean isInterfaceOfType(@Nonnull final FutureJVppCore jvpp, + @Nonnull final ModificationCache cache, + @Nonnull final InstanceIdentifier<?> id, + final int index, + @Nonnull final Class<? extends InterfaceType> ifcType, + @Nonnull final Logger callerLogger) + throws ReadFailedException { + final String name = id.firstKeyOf(Interface.class).getName(); + final SwInterfaceDetails vppInterfaceDetails = + getVppInterfaceDetails(jvpp, id, name, index, cache, callerLogger); + + return isInterfaceOfType(ifcType, vppInterfaceDetails); + } + + default boolean isInterfaceOfType(final Class<? extends InterfaceType> ifcType, + final SwInterfaceDetails cachedDetails) { + return ifcType.equals(getInterfaceType(toString(cachedDetails.interfaceName))); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/L2Customizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/L2Customizer.java new file mode 100644 index 000000000..ed88370f5 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/L2Customizer.java @@ -0,0 +1,116 @@ +/* + * 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.v3po.interfacesstate; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingReaderCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceStateAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.L2; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.L2Builder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.Interconnection; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.interconnection.BridgeBased; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.interconnection.BridgeBasedBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.interconnection.XconnectBased; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.interconnection.XconnectBasedBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Customizer for reading ietf-interfaces:interfaces-state/interface/iface_name/v3po:l2 + */ +public class L2Customizer extends FutureJVppCustomizer implements InitializingReaderCustomizer<L2, L2Builder> { + + private static final Logger LOG = LoggerFactory.getLogger(L2Customizer.class); + private final InterconnectionReadUtils icReadUtils; + + public L2Customizer(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final NamingContext interfaceContext, + @Nonnull final NamingContext bridgeDomainContext) { + super(futureJVppCore); + this.icReadUtils = new InterconnectionReadUtils(futureJVppCore, interfaceContext, bridgeDomainContext); + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final L2 readValue) { + ((VppInterfaceStateAugmentationBuilder) parentBuilder).setL2(readValue); + } + + @Nonnull + @Override + public L2Builder getBuilder(@Nonnull final InstanceIdentifier<L2> id) { + return new L2Builder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<L2> id, @Nonnull final L2Builder builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + + LOG.debug("Reading attributes for L2: {}", id); + final InterfaceKey key = id.firstKeyOf(Interface.class); + final String ifaceName = key.getName(); + builder.setInterconnection(icReadUtils.readInterconnection(id, ifaceName, ctx)); + } + + @Override + public Initialized<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.L2> init( + @Nonnull final InstanceIdentifier<L2> id, + @Nonnull final L2 readValue, + @Nonnull final ReadContext ctx) { + final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.L2Builder + l2Builder = + new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.L2Builder(); + + final Interconnection interconnection = readValue.getInterconnection(); + if (interconnection != null) { + if (interconnection instanceof XconnectBased) { + final XconnectBasedBuilder xconnectBasedBuilder = new XconnectBasedBuilder(); + xconnectBasedBuilder.setXconnectOutgoingInterface( + ((XconnectBased) interconnection).getXconnectOutgoingInterface()); + l2Builder.setInterconnection(xconnectBasedBuilder.build()); + } else if (interconnection instanceof BridgeBased) { + final BridgeBasedBuilder bridgeBasedBuilder = new BridgeBasedBuilder(); + bridgeBasedBuilder.setBridgeDomain(((BridgeBased) interconnection).getBridgeDomain()); + bridgeBasedBuilder + .setBridgedVirtualInterface(((BridgeBased) interconnection).isBridgedVirtualInterface()); + bridgeBasedBuilder.setSplitHorizonGroup(((BridgeBased) interconnection).getSplitHorizonGroup()); + l2Builder.setInterconnection(bridgeBasedBuilder.build()); + } + } + + return Initialized.create(getCfgId(id), l2Builder.build()); + } + + private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.L2> getCfgId( + final InstanceIdentifier<L2> id) { + return InterfaceCustomizer.getCfgId(RWUtils.cutId(id, Interface.class)) + .augmentation(VppInterfaceAugmentation.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.L2.class); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ProxyArpCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ProxyArpCustomizer.java new file mode 100644 index 000000000..028e8e0d8 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ProxyArpCustomizer.java @@ -0,0 +1,79 @@ +/* + * 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.v3po.interfacesstate; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.ReaderCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceStateAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.ProxyArp; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ProxyArpCustomizer extends FutureJVppCustomizer + implements ReaderCustomizer<ProxyArp, + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.ProxyArpBuilder> { + + private static final Logger LOG = LoggerFactory.getLogger(ProxyArpCustomizer.class); + private final NamingContext interfaceContext; + + public ProxyArpCustomizer(final FutureJVppCore vppApi, final NamingContext interfaceContext) { + super(vppApi); + this.interfaceContext = interfaceContext; + } + + @Override + public void merge(@Nonnull Builder<? extends DataObject> parentBuilder, + @Nonnull org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214 + .interfaces.state._interface.ProxyArp readValue) { + + ((VppInterfaceStateAugmentationBuilder) parentBuilder).setProxyArp(readValue); + } + + @Nonnull + @Override + public org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state + ._interface.ProxyArpBuilder getBuilder( + @Nonnull InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight + .params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.ProxyArp> id) { + + return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces + .state._interface.ProxyArpBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight + .params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.ProxyArp> id, + @Nonnull org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po + .rev161214.interfaces.state._interface.ProxyArpBuilder builder, + @Nonnull ReadContext ctx) throws ReadFailedException { + + //TODO: VPP-225 Implement fully when VPP Proxy ARP read API is available + add initializing + final InterfaceKey key = id.firstKeyOf(Interface.class); + final int index = interfaceContext.getIndex(key.getName(), ctx.getMappingContext()); + LOG.warn("Reading of ARP data not (yet) supported by VPP API"); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/RewriteCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/RewriteCustomizer.java new file mode 100644 index 000000000..6906ebc0c --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/RewriteCustomizer.java @@ -0,0 +1,147 @@ +/* + * 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.v3po.interfacesstate; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.base.Preconditions; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.ReaderCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.hc2vpp.v3po.util.SubInterfaceUtils; +import io.fd.hc2vpp.common.translate.util.TagRewriteOperation; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.CVlan; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.Dot1qTagVlanType; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.Dot1qVlanId; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.SVlan; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.dot1q.tag.Dot1qTagBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214._802dot1ad; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214._802dot1q; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces.state._interface.sub.interfaces.SubInterface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.L2Builder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.l2.Rewrite; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.l2.RewriteBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.tag.rewrite.PushTags; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.tag.rewrite.PushTagsBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.tag.rewrite.PushTagsKey; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Customizer for reading vlan tag-rewrite configuration state form the VPP. + */ +public class RewriteCustomizer extends FutureJVppCustomizer + implements ReaderCustomizer<Rewrite, RewriteBuilder>, InterfaceDataTranslator { + + // No initialization necessary since its parent Subinterface L2 customzier sets the entire subtree during + // initialization + + private static final Logger LOG = LoggerFactory.getLogger(RewriteCustomizer.class); + private final NamingContext interfaceContext; + + public RewriteCustomizer(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final NamingContext interfaceContext) { + super(futureJVppCore); + this.interfaceContext = Preconditions.checkNotNull(interfaceContext, "interfaceContext should not be null"); + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, + @Nonnull final Rewrite readValue) { + ((L2Builder) parentBuilder).setRewrite(readValue); + } + + @Nonnull + @Override + public RewriteBuilder getBuilder(@Nonnull final InstanceIdentifier<Rewrite> id) { + return new RewriteBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Rewrite> id, + @Nonnull final RewriteBuilder builder, @Nonnull final ReadContext ctx) + throws ReadFailedException { + final String subInterfaceName = getSubInterfaceName(id); + LOG.debug("Reading attributes for sub interface: {}", subInterfaceName); + + final SwInterfaceDetails iface = getVppInterfaceDetails(getFutureJVpp(), id, subInterfaceName, + interfaceContext.getIndex(subInterfaceName, ctx.getMappingContext()), ctx.getModificationCache(), LOG); + LOG.debug("VPP sub-interface details: {}", iface); + + checkState(iface.subId != 0, "Interface returned by the VPP is not a sub-interface"); + + final TagRewriteOperation operation = TagRewriteOperation.get(iface.vtrOp); + if (TagRewriteOperation.disabled == operation) { + LOG.debug("Tag rewrite operation is disabled for "); + return; + } + + builder.setVlanType(iface.vtrPushDot1Q == 1 + ? _802dot1q.class + : _802dot1ad.class); + + setPushTags(builder, iface); + setPopTags(builder, operation); + } + + private static String getSubInterfaceName(final InstanceIdentifier<Rewrite> id) { + return SubInterfaceUtils.getSubInterfaceName(id.firstKeyOf(Interface.class).getName(), + Math.toIntExact(id.firstKeyOf(SubInterface.class).getIdentifier())); + } + + private void setPopTags(final RewriteBuilder builder, final TagRewriteOperation operation) { + final byte numberOfTagsToPop = operation.getPopTags(); + if (numberOfTagsToPop != 0) { + builder.setPopTags(Short.valueOf(numberOfTagsToPop)); + } + } + + private void setPushTags(final RewriteBuilder builder, final SwInterfaceDetails iface) { + final List<PushTags> tags = new ArrayList<>(); + if (iface.vtrTag1 != 0) { + tags.add(buildTag((short) 0, SVlan.class, iface.vtrTag1)); + } + if (iface.vtrTag2 != 0) { + tags.add(buildTag((short) 1, CVlan.class, iface.vtrTag2)); + } + if (tags.size() > 0) { + builder.setPushTags(tags); + } + } + + private PushTags buildTag(final short index, final Class<? extends Dot1qTagVlanType> tagType, final int vlanId) { + final PushTagsBuilder tag = new PushTagsBuilder(); + tag.setIndex(index); + tag.setKey(new PushTagsKey(index)); + final Dot1qTagBuilder dtag = new Dot1qTagBuilder(); + dtag.setTagType(tagType); + dtag.setVlanId(new Dot1qVlanId(vlanId)); + tag.setDot1qTag(dtag.build()); + return tag.build(); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/SubInterfaceCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/SubInterfaceCustomizer.java new file mode 100644 index 000000000..9c13b382a --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/SubInterfaceCustomizer.java @@ -0,0 +1,264 @@ +/* + * 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.v3po.interfacesstate; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.base.Preconditions; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingListReaderCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.hc2vpp.v3po.util.SubInterfaceUtils; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.CVlan; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.Dot1qTagVlanType; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.Dot1qVlanId; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.SVlan; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.dot1q.tag.or.any.Dot1qTag; +import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.dot1q.tag.or.any.Dot1qTagBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.SubInterfaceStatus; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.SubinterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.SubInterfaces; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces.state._interface.SubInterfacesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces.state._interface.sub.interfaces.SubInterface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces.state._interface.sub.interfaces.SubInterfaceBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces.state._interface.sub.interfaces.SubInterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.match.attributes.match.type.DefaultBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.match.attributes.match.type.UntaggedBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.match.attributes.match.type.vlan.tagged.VlanTaggedBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.Match; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.MatchBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.Tags; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.TagsBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.tags.Tag; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.tags.TagBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.tags.TagKey; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Customizer for reading sub interfaces form the VPP. + */ +public class SubInterfaceCustomizer extends FutureJVppCustomizer + implements InitializingListReaderCustomizer<SubInterface, SubInterfaceKey, SubInterfaceBuilder>, ByteDataTranslator, + InterfaceDataTranslator { + + private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceCustomizer.class); + private static final Dot1qTag.VlanId ANY_VLAN_ID = new Dot1qTag.VlanId(Dot1qTag.VlanId.Enumeration.Any); + private NamingContext interfaceContext; + + public SubInterfaceCustomizer(@Nonnull final FutureJVppCore jvpp, + @Nonnull final NamingContext interfaceContext) { + super(jvpp); + this.interfaceContext = Preconditions.checkNotNull(interfaceContext, "interfaceContext should not be null"); + } + + private static String getSubInterfaceName(final InstanceIdentifier<SubInterface> id) { + return SubInterfaceUtils.getSubInterfaceName(id.firstKeyOf(Interface.class).getName(), + Math.toIntExact(id.firstKeyOf(id.getTargetType()).getIdentifier())); + } + + private static Tag buildTag(final short index, final Class<? extends Dot1qTagVlanType> tagType, + final Dot1qTag.VlanId vlanId) { + TagBuilder tag = new TagBuilder(); + tag.setIndex(index); + tag.setKey(new TagKey(index)); + final Dot1qTagBuilder dtag = new Dot1qTagBuilder(); + dtag.setTagType(tagType); + dtag.setVlanId(vlanId); + tag.setDot1qTag(dtag.build()); + return tag.build(); + } + + private static Dot1qTag.VlanId buildVlanId(final short vlanId) { + // treat vlanId as unsigned value: + return new Dot1qTag.VlanId(new Dot1qVlanId(0xffff & vlanId)); + } + + @Nonnull + @Override + public List<SubInterfaceKey> getAllIds(@Nonnull final InstanceIdentifier<SubInterface> id, + @Nonnull final ReadContext context) throws ReadFailedException { + // Relying here that parent InterfaceCustomizer was invoked first (PREORDER) + // to fill in the context with initial ifc mapping + final InterfaceKey key = id.firstKeyOf(Interface.class); + final String ifaceName = key.getName(); + final int ifaceId = interfaceContext.getIndex(ifaceName, context.getMappingContext()); + + // TODO HONEYCOMB-189 if we know that full dump was already performed we could use cache + // (checking if getCachedInterfaceDump() returns non empty map is not enough, because + // we could be part of particular iface state read + final SwInterfaceDump request = new SwInterfaceDump(); + request.nameFilter = "".getBytes(); + request.nameFilterValid = 0; + + final CompletableFuture<SwInterfaceDetailsReplyDump> swInterfaceDetailsReplyDumpCompletableFuture = + getFutureJVpp().swInterfaceDump(request).toCompletableFuture(); + final SwInterfaceDetailsReplyDump ifaces = + getReplyForRead(swInterfaceDetailsReplyDumpCompletableFuture, id); + + if (null == ifaces || null == ifaces.swInterfaceDetails) { + LOG.warn("Looking for sub-interfaces, but no interfaces found in VPP"); + return Collections.emptyList(); + } + + // Cache interfaces dump in per-tx context to later be used in readCurrentAttributes + context.getModificationCache() + .put(InterfaceCustomizer.DUMPED_IFCS_CONTEXT_KEY, ifaces.swInterfaceDetails.stream() + .collect(Collectors.toMap(t -> t.swIfIndex, swInterfaceDetails -> swInterfaceDetails))); + + final List<SubInterfaceKey> interfacesKeys = ifaces.swInterfaceDetails.stream() + .filter(elt -> elt != null) + // accept only sub-interfaces for current iface: + .filter(elt -> elt.subId != 0 && elt.supSwIfIndex == ifaceId) + .map(details -> new SubInterfaceKey(new Long(details.subId))) + .collect(Collectors.toList()); + + LOG.debug("Sub-interfaces of {} found in VPP: {}", ifaceName, interfacesKeys); + return interfacesKeys; + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> builder, + @Nonnull final List<SubInterface> readData) { + ((SubInterfacesBuilder) builder).setSubInterface(readData); + } + + @Nonnull + @Override + public SubInterfaceBuilder getBuilder(@Nonnull final InstanceIdentifier<SubInterface> id) { + return new SubInterfaceBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<SubInterface> id, + @Nonnull final SubInterfaceBuilder builder, @Nonnull final ReadContext ctx) + throws ReadFailedException { + final String subInterfaceName = getSubInterfaceName(id); + LOG.debug("Reading attributes for sub interface: {}", subInterfaceName); + + final SwInterfaceDetails iface = getVppInterfaceDetails(getFutureJVpp(), id, subInterfaceName, + interfaceContext.getIndex(subInterfaceName, ctx.getMappingContext()), ctx.getModificationCache(), LOG); + LOG.debug("VPP sub-interface details: {}", iface); + + checkState(iface.subId != 0, "Interface returned by the VPP is not a sub-interface"); + + builder.setIdentifier(Long.valueOf(iface.subId)); + builder.setKey(new SubInterfaceKey(builder.getIdentifier())); + + // sub-interface-base-attributes: + builder.setTags(readTags(iface)); + builder.setMatch(readMatch(iface)); + + // sub-interface-operational-attributes: + builder.setAdminStatus(1 == iface.adminUpDown + ? SubInterfaceStatus.Up + : SubInterfaceStatus.Down); + builder.setOperStatus(1 == iface.linkUpDown + ? SubInterfaceStatus.Up + : SubInterfaceStatus.Down); + builder.setIfIndex(vppIfIndexToYang(iface.swIfIndex)); + if (iface.l2AddressLength == 6) { + builder.setPhysAddress(new PhysAddress(vppPhysAddrToYang(iface.l2Address))); + } + if (0 != iface.linkSpeed) { + builder.setSpeed(vppInterfaceSpeedToYang(iface.linkSpeed)); + } + } + + private Tags readTags(final SwInterfaceDetails iface) { + final TagsBuilder tags = new TagsBuilder(); + final List<Tag> list = new ArrayList<>(); + if (iface.subNumberOfTags > 0) { + if (iface.subOuterVlanIdAny == 1) { + list.add(buildTag((short) 0, SVlan.class, ANY_VLAN_ID)); + } else { + list.add(buildTag((short) 0, SVlan.class, buildVlanId(iface.subOuterVlanId))); + } + // inner tag (customer tag): + if (iface.subNumberOfTags == 2) { + if (iface.subInnerVlanIdAny == 1) { + list.add(buildTag((short) 1, CVlan.class, ANY_VLAN_ID)); + } else { + list.add(buildTag((short) 1, CVlan.class, buildVlanId(iface.subInnerVlanId))); + } + } + } + tags.setTag(list); + return tags.build(); + } + + private Match readMatch(final SwInterfaceDetails iface) { + final MatchBuilder match = new MatchBuilder(); + if (iface.subDefault == 1) { + match.setMatchType(new DefaultBuilder().build()); + } else if (iface.subNumberOfTags == 0) { + match.setMatchType(new UntaggedBuilder().build()); + } else { + final VlanTaggedBuilder tagged = new VlanTaggedBuilder(); + tagged.setMatchExactTags(byteToBoolean(iface.subExactMatch)); + match.setMatchType( + new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.match.attributes.match.type.VlanTaggedBuilder() + .setVlanTagged(tagged.build()).build()); + } + return match.build(); + } + + @Override + public Initialized<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterface> init( + @Nonnull final InstanceIdentifier<SubInterface> id, @Nonnull final SubInterface readValue, + @Nonnull final ReadContext ctx) { + return Initialized.create(getCfgId(id), + new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterfaceBuilder() + .setEnabled(SubInterfaceStatus.Up.equals(readValue.getAdminStatus())) + .setIdentifier(readValue.getIdentifier()) + .setMatch(readValue.getMatch()) + .setTags(readValue.getTags()) + .setVlanType(readValue.getVlanType()) + .build()); + } + + public static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterface> getCfgId( + final InstanceIdentifier<SubInterface> id) { + return InterfaceCustomizer.getCfgId(RWUtils.cutId(id, Interface.class)) + .augmentation(SubinterfaceAugmentation.class) + .child(SubInterfaces.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterface.class, + new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterfaceKey( + id.firstKeyOf(SubInterface.class).getIdentifier())); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/SubInterfaceL2Customizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/SubInterfaceL2Customizer.java new file mode 100644 index 000000000..1b98b41ef --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/SubInterfaceL2Customizer.java @@ -0,0 +1,93 @@ +/* + * 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.v3po.interfacesstate; + +import static io.fd.hc2vpp.v3po.util.SubInterfaceUtils.getSubInterfaceName; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingReaderCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces.state._interface.sub.interfaces.SubInterface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces.state._interface.sub.interfaces.SubInterfaceBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces.state._interface.sub.interfaces.SubInterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.L2; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.L2Builder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Customizer for reading vlan sub interface L2 operational state + */ +public class SubInterfaceL2Customizer extends FutureJVppCustomizer + implements InitializingReaderCustomizer<L2, L2Builder> { + + private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceL2Customizer.class); + private final InterconnectionReadUtils icReadUtils; + + public SubInterfaceL2Customizer(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final NamingContext interfaceContext, + @Nonnull final NamingContext bridgeDomainContext) { + super(futureJVppCore); + this.icReadUtils = new InterconnectionReadUtils(futureJVppCore, interfaceContext, bridgeDomainContext); + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final L2 readValue) { + ((SubInterfaceBuilder) parentBuilder).setL2(readValue); + } + + @Nonnull + @Override + public L2Builder getBuilder(@Nonnull final InstanceIdentifier<L2> id) { + return new L2Builder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<L2> id, @Nonnull final L2Builder builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + LOG.debug("Reading attributes for sub-interface L2: {}", id); + final InterfaceKey parentInterfacekey = id.firstKeyOf(Interface.class); + final SubInterfaceKey subInterfacekey = id.firstKeyOf(SubInterface.class); + final String subInterfaceName = getSubInterfaceName(parentInterfacekey.getName(), subInterfacekey.getIdentifier().intValue()); + + builder.setInterconnection(icReadUtils.readInterconnection(id, subInterfaceName, ctx)); + } + + @Override + public Initialized<L2> init( + @Nonnull final InstanceIdentifier<L2> id, + @Nonnull final L2 readValue, + @Nonnull final ReadContext ctx) { + return Initialized.create(getCfgId(id), readValue); + } + + static InstanceIdentifier<L2> getCfgId(final InstanceIdentifier<L2> id) { + return SubInterfaceCustomizer.getCfgId(RWUtils.cutId(id, SubInterface.class)) + .child(L2.class); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/TapCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/TapCustomizer.java new file mode 100644 index 000000000..683450da3 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/TapCustomizer.java @@ -0,0 +1,143 @@ +/* + * 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.v3po.interfacesstate; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingReaderCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails; +import io.fd.vpp.jvpp.core.dto.SwInterfaceTapDetails; +import io.fd.vpp.jvpp.core.dto.SwInterfaceTapDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.SwInterfaceTapDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletionStage; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceStateAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.Tap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.TapBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class TapCustomizer extends FutureJVppCustomizer + implements InitializingReaderCustomizer<Tap, TapBuilder>, InterfaceDataTranslator, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(TapCustomizer.class); + public static final String DUMPED_TAPS_CONTEXT_KEY = TapCustomizer.class.getName() + "dumpedTapsDuringGetAllIds"; + private NamingContext interfaceContext; + + public TapCustomizer(@Nonnull final FutureJVppCore jvpp, @Nonnull final NamingContext interfaceContext) { + super(jvpp); + this.interfaceContext = interfaceContext; + } + + @Override + public void merge(@Nonnull Builder<? extends DataObject> parentBuilder, @Nonnull Tap readValue) { + ((VppInterfaceStateAugmentationBuilder) parentBuilder).setTap(readValue); + } + + @Nonnull + @Override + public TapBuilder getBuilder(@Nonnull InstanceIdentifier<Tap> id) { + return new TapBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Tap> id, + @Nonnull final TapBuilder builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + + final InterfaceKey key = id.firstKeyOf(Interface.class); + final int index = interfaceContext.getIndex(key.getName(), ctx.getMappingContext()); + if (!isInterfaceOfType(getFutureJVpp(), ctx.getModificationCache(), id, index, + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.Tap.class, LOG)) { + return; + } + + LOG.debug("Reading attributes for tap interface: {}", key.getName()); + + @SuppressWarnings("unchecked") + Map<Integer, SwInterfaceTapDetails> mappedTaps = + (Map<Integer, SwInterfaceTapDetails>) ctx.getModificationCache().get(DUMPED_TAPS_CONTEXT_KEY); + + if (mappedTaps == null) { + // Full Tap dump has to be performed here, no filter or anything is here to help so at least we cache it + final SwInterfaceTapDump request = new SwInterfaceTapDump(); + final CompletionStage<SwInterfaceTapDetailsReplyDump> swInterfaceTapDetailsReplyDumpCompletionStage = + getFutureJVpp().swInterfaceTapDump(request); + final SwInterfaceTapDetailsReplyDump reply = + getReplyForRead(swInterfaceTapDetailsReplyDumpCompletionStage.toCompletableFuture(), id); + + if (null == reply || null == reply.swInterfaceTapDetails) { + mappedTaps = Collections.emptyMap(); + } else { + final List<SwInterfaceTapDetails> swInterfaceTapDetails = reply.swInterfaceTapDetails; + // Cache interfaces dump in per-tx context to later be used in readCurrentAttributes + mappedTaps = swInterfaceTapDetails.stream() + .collect(Collectors.toMap(t -> t.swIfIndex, swInterfaceDetails -> swInterfaceDetails)); + } + + ctx.getModificationCache().put(DUMPED_TAPS_CONTEXT_KEY, mappedTaps); + } + + final SwInterfaceTapDetails swInterfaceTapDetails = mappedTaps.get(index); + LOG.trace("Tap interface: {} attributes returned from VPP: {}", key.getName(), swInterfaceTapDetails); + + builder.setTapName(toString(swInterfaceTapDetails.devName)); + LOG.debug("Tap interface: {}, id: {} attributes read as: {}", key.getName(), index, builder); + } + + @Override + public Initialized<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Tap> init( + @Nonnull final InstanceIdentifier<Tap> id, @Nonnull final Tap readValue, @Nonnull final ReadContext ctx) { + // The MAC address is set from interface details, those details are retrieved from cache + final InterfaceKey key = id.firstKeyOf(Interface.class); + final int index = interfaceContext.getIndex(key.getName(), ctx.getMappingContext()); + final SwInterfaceDetails ifcDetails = + InterfaceCustomizer.getCachedInterfaceDump(ctx.getModificationCache()).get(index); + + return Initialized.create(getCfgId(id), + new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.TapBuilder() + .setMac(new PhysAddress(vppPhysAddrToYang(ifcDetails.l2Address))) + .setTapName(readValue.getTapName()) +// tapBuilder.setDeviceInstance(); + .build()); + } + + private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Tap> getCfgId( + final InstanceIdentifier<Tap> id) { + return InterfaceCustomizer.getCfgId(RWUtils.cutId(id, Interface.class)) + .augmentation(VppInterfaceAugmentation.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Tap.class); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/VhostUserCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/VhostUserCustomizer.java new file mode 100644 index 000000000..6a918e842 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/VhostUserCustomizer.java @@ -0,0 +1,154 @@ +/* + * 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.v3po.interfacesstate; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingReaderCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.dto.SwInterfaceVhostUserDetails; +import io.fd.vpp.jvpp.core.dto.SwInterfaceVhostUserDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.SwInterfaceVhostUserDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.math.BigInteger; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletionStage; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VhostUserRole; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceStateAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.VhostUser; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.VhostUserBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class VhostUserCustomizer extends FutureJVppCustomizer + implements InitializingReaderCustomizer<VhostUser, VhostUserBuilder>, InterfaceDataTranslator, JvppReplyConsumer { + + public static final String DUMPED_VHOST_USERS_CONTEXT_KEY = + VhostUserCustomizer.class.getName() + "dumpedVhostUsersDuringGetAllIds"; + private static final Logger LOG = LoggerFactory.getLogger(VhostUserCustomizer.class); + private NamingContext interfaceContext; + + public VhostUserCustomizer(@Nonnull final FutureJVppCore jvpp, @Nonnull final NamingContext interfaceContext) { + super(jvpp); + this.interfaceContext = interfaceContext; + } + + @Override + public void merge(@Nonnull Builder<? extends DataObject> parentBuilder, @Nonnull VhostUser readValue) { + ((VppInterfaceStateAugmentationBuilder) parentBuilder).setVhostUser(readValue); + } + + @Nonnull + @Override + public VhostUserBuilder getBuilder(@Nonnull InstanceIdentifier<VhostUser> id) { + return new VhostUserBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<VhostUser> id, + @Nonnull final VhostUserBuilder builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + + final InterfaceKey key = id.firstKeyOf(Interface.class); + final int index = interfaceContext.getIndex(key.getName(), ctx.getMappingContext()); + if (!isInterfaceOfType(getFutureJVpp(), ctx.getModificationCache(), id, index, + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VhostUser.class, + LOG)) { + return; + } + + LOG.debug("Reading attributes for vhpost user interface: {}", key.getName()); + + @SuppressWarnings("unchecked") + Map<Integer, SwInterfaceVhostUserDetails> mappedVhostUsers = + (Map<Integer, SwInterfaceVhostUserDetails>) ctx.getModificationCache() + .get(DUMPED_VHOST_USERS_CONTEXT_KEY); + + if (mappedVhostUsers == null) { + // Full VhostUser dump has to be performed here, no filter or anything is here to help so at least we cache it + final SwInterfaceVhostUserDump request = new SwInterfaceVhostUserDump(); + final CompletionStage<SwInterfaceVhostUserDetailsReplyDump> + swInterfaceVhostUserDetailsReplyDumpCompletionStage = + getFutureJVpp().swInterfaceVhostUserDump(request); + final SwInterfaceVhostUserDetailsReplyDump reply = + getReplyForRead(swInterfaceVhostUserDetailsReplyDumpCompletionStage.toCompletableFuture(), id); + + if (null == reply || null == reply.swInterfaceVhostUserDetails) { + mappedVhostUsers = Collections.emptyMap(); + } else { + final List<SwInterfaceVhostUserDetails> swInterfaceVhostUserDetails = + reply.swInterfaceVhostUserDetails; + // Cache interfaces dump in per-tx context to later be used in readCurrentAttributes + mappedVhostUsers = swInterfaceVhostUserDetails.stream() + .collect(Collectors.toMap(t -> t.swIfIndex, swInterfaceDetails -> swInterfaceDetails)); + } + + ctx.getModificationCache().put(DUMPED_VHOST_USERS_CONTEXT_KEY, mappedVhostUsers); + } + + // Relying here that parent InterfaceCustomizer was invoked first to fill in the context with initial ifc mapping + final SwInterfaceVhostUserDetails swInterfaceVhostUserDetails = mappedVhostUsers.get(index); + LOG.trace("Vhost user interface: {} attributes returned from VPP: {}", key.getName(), + swInterfaceVhostUserDetails); + + builder.setRole(swInterfaceVhostUserDetails.isServer == 1 + ? VhostUserRole.Server + : VhostUserRole.Client); + builder.setFeatures(BigInteger.valueOf(swInterfaceVhostUserDetails.features)); + builder.setNumMemoryRegions((long) swInterfaceVhostUserDetails.numRegions); + builder.setSocket(toString(swInterfaceVhostUserDetails.sockFilename)); + builder.setVirtioNetHdrSize((long) swInterfaceVhostUserDetails.virtioNetHdrSz); + // TODO: map error code to meaningful message after VPP-436 is done + builder.setConnectError(Integer.toString(swInterfaceVhostUserDetails.sockErrno)); + + LOG.debug("Vhost user interface: {}, id: {} attributes read as: {}", key.getName(), index, builder); + } + + @Override + public Initialized<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.VhostUser> init( + @Nonnull final InstanceIdentifier<VhostUser> id, + @Nonnull final VhostUser readValue, + @Nonnull final ReadContext ctx) { + return Initialized.create(getCfgId(id), + new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.VhostUserBuilder() + .setRole(readValue.getRole()) + .setSocket(readValue.getSocket()) + .build()); + } + + private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.VhostUser> getCfgId( + final InstanceIdentifier<VhostUser> id) { + return InterfaceCustomizer.getCfgId(RWUtils.cutId(id, Interface.class)) + .augmentation(VppInterfaceAugmentation.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.VhostUser.class); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/VxlanCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/VxlanCustomizer.java new file mode 100644 index 000000000..d3174e643 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/VxlanCustomizer.java @@ -0,0 +1,162 @@ +/* + * 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.v3po.interfacesstate; + +import static com.google.common.base.Preconditions.checkState; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingReaderCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.dto.VxlanTunnelDetails; +import io.fd.vpp.jvpp.core.dto.VxlanTunnelDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.VxlanTunnelDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceStateAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VxlanTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VxlanVni; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.Vxlan; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.VxlanBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class VxlanCustomizer extends FutureJVppCustomizer + implements InitializingReaderCustomizer<Vxlan, VxlanBuilder>, InterfaceDataTranslator, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(VxlanCustomizer.class); + private final NamingContext interfaceContext; + + public VxlanCustomizer(@Nonnull final FutureJVppCore jvpp, @Nonnull final NamingContext interfaceContext) { + super(jvpp); + this.interfaceContext = interfaceContext; + } + + @Override + public void merge(@Nonnull Builder<? extends DataObject> parentBuilder, + @Nonnull Vxlan readValue) { + ((VppInterfaceStateAugmentationBuilder) parentBuilder).setVxlan(readValue); + } + + @Nonnull + @Override + public VxlanBuilder getBuilder(@Nonnull InstanceIdentifier<Vxlan> id) { + return new VxlanBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Vxlan> id, + @Nonnull final VxlanBuilder builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + + final InterfaceKey key = id.firstKeyOf(Interface.class); + final int index = interfaceContext.getIndex(key.getName(), ctx.getMappingContext()); + if (!isInterfaceOfType(getFutureJVpp(), ctx.getModificationCache(), id, index, VxlanTunnel.class, LOG)) { + return; + } + + LOG.debug("Reading attributes for vxlan tunnel: {}", key.getName()); + // Dump just a single + final VxlanTunnelDump request = new VxlanTunnelDump(); + request.swIfIndex = index; + + final CompletionStage<VxlanTunnelDetailsReplyDump> swInterfaceVxlanDetailsReplyDumpCompletionStage = + getFutureJVpp().vxlanTunnelDump(request); + final VxlanTunnelDetailsReplyDump reply = + getReplyForRead(swInterfaceVxlanDetailsReplyDumpCompletionStage.toCompletableFuture(), id); + + // VPP keeps vxlan tunnel interfaces even after they were deleted (optimization) + // However there ar no longer any vxlan tunnel specific fields assigned to it and this call + // returns nothing + if (reply == null || reply.vxlanTunnelDetails == null || reply.vxlanTunnelDetails.isEmpty()) { + LOG.debug( + "Vxlan tunnel {}, id {} has no attributes assigned in VPP. Probably is a leftover interface placeholder" + + "after delete", key.getName(), index); + return; + } + + checkState(reply.vxlanTunnelDetails.size() == 1, + "Unexpected number of returned vxlan tunnels: {} for tunnel: {}", reply.vxlanTunnelDetails, + key.getName()); + LOG.trace("Vxlan tunnel: {} attributes returned from VPP: {}", key.getName(), reply); + + final VxlanTunnelDetails swInterfaceVxlanDetails = reply.vxlanTunnelDetails.get(0); + if (swInterfaceVxlanDetails.isIpv6 == 1) { + final Ipv6Address dstIpv6 = + new Ipv6Address(parseAddress(swInterfaceVxlanDetails.dstAddress).getHostAddress()); + builder.setDst(new IpAddress(dstIpv6)); + final Ipv6Address srcIpv6 = + new Ipv6Address(parseAddress(swInterfaceVxlanDetails.srcAddress).getHostAddress()); + builder.setSrc(new IpAddress(srcIpv6)); + } else { + final byte[] dstBytes = Arrays.copyOfRange(swInterfaceVxlanDetails.dstAddress, 0, 4); + final Ipv4Address dstIpv4 = new Ipv4Address(parseAddress(dstBytes).getHostAddress()); + builder.setDst(new IpAddress(dstIpv4)); + final byte[] srcBytes = Arrays.copyOfRange(swInterfaceVxlanDetails.srcAddress, 0, 4); + final Ipv4Address srcIpv4 = new Ipv4Address(parseAddress(srcBytes).getHostAddress()); + builder.setSrc(new IpAddress(srcIpv4)); + } + builder.setEncapVrfId((long) swInterfaceVxlanDetails.encapVrfId); + builder.setVni(new VxlanVni((long) swInterfaceVxlanDetails.vni)); + LOG.debug("Vxlan tunnel: {}, id: {} attributes read as: {}", key.getName(), index, builder); + } + + @Nonnull + private static InetAddress parseAddress(@Nonnull final byte[] addr) { + try { + return InetAddress.getByAddress(addr); + } catch (UnknownHostException e) { + throw new IllegalArgumentException("Cannot create InetAddress from " + Arrays.toString(addr), e); + } + } + + @Override + public Initialized<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Vxlan> init( + @Nonnull final InstanceIdentifier<Vxlan> id, @Nonnull final Vxlan readValue, @Nonnull final ReadContext ctx) { + return Initialized.create(getCfgId(id), + new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.VxlanBuilder() + .setDst(readValue.getDst()) + .setSrc(readValue.getSrc()) + .setEncapVrfId(readValue.getEncapVrfId()) + .setVni(new VxlanVni(readValue.getVni())) + .build()); + } + + private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Vxlan> getCfgId( + final InstanceIdentifier<Vxlan> id) { + return InterfaceCustomizer.getCfgId(RWUtils.cutId(id, Interface.class)) + .augmentation(VppInterfaceAugmentation.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Vxlan.class); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/VxlanGpeCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/VxlanGpeCustomizer.java new file mode 100644 index 000000000..b10fef3e8 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/VxlanGpeCustomizer.java @@ -0,0 +1,168 @@ +/* + * 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.v3po.interfacesstate; + +import static com.google.common.base.Preconditions.checkState; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingReaderCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.dto.VxlanGpeTunnelDetails; +import io.fd.vpp.jvpp.core.dto.VxlanGpeTunnelDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.VxlanGpeTunnelDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceStateAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VxlanGpeNextProtocol; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VxlanGpeTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VxlanGpeVni; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.VxlanGpe; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.VxlanGpeBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class VxlanGpeCustomizer extends FutureJVppCustomizer + implements InitializingReaderCustomizer<VxlanGpe, VxlanGpeBuilder>, InterfaceDataTranslator, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(VxlanGpeCustomizer.class); + private NamingContext interfaceContext; + + public VxlanGpeCustomizer(@Nonnull final FutureJVppCore jvpp, @Nonnull final NamingContext interfaceContext) { + super(jvpp); + this.interfaceContext = interfaceContext; + } + + @Override + public void merge(@Nonnull Builder<? extends DataObject> parentBuilder, + @Nonnull VxlanGpe readValue) { + ((VppInterfaceStateAugmentationBuilder) parentBuilder).setVxlanGpe(readValue); + } + + @Nonnull + @Override + public VxlanGpeBuilder getBuilder(@Nonnull InstanceIdentifier<VxlanGpe> id) { + return new VxlanGpeBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<VxlanGpe> id, + @Nonnull final VxlanGpeBuilder builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + + final InterfaceKey key = id.firstKeyOf(Interface.class); + final int index = interfaceContext.getIndex(key.getName(), ctx.getMappingContext()); + if (!isInterfaceOfType(getFutureJVpp(), ctx.getModificationCache(), id, index, VxlanGpeTunnel.class, LOG)) { + return; + } + + LOG.debug("Reading attributes for VxlanGpe tunnel: {}", key.getName()); + // Dump just a single + final VxlanGpeTunnelDump request = new VxlanGpeTunnelDump(); + request.swIfIndex = index; + + final CompletionStage<VxlanGpeTunnelDetailsReplyDump> swInterfaceVxlanGpeDetailsReplyDumpCompletionStage = + getFutureJVpp().vxlanGpeTunnelDump(request); + final VxlanGpeTunnelDetailsReplyDump reply = + getReplyForRead(swInterfaceVxlanGpeDetailsReplyDumpCompletionStage.toCompletableFuture(), + id); + + // VPP keeps VxlanGpe tunnel interfaces even after they were deleted (optimization) + // However there are no longer any VxlanGpe tunnel specific fields assigned to it and this call + // returns nothing + if (reply == null || reply.vxlanGpeTunnelDetails == null || reply.vxlanGpeTunnelDetails.isEmpty()) { + LOG.debug( + "VxlanGpe tunnel {}, id {} has no attributes assigned in VPP. Probably is a leftover interface placeholder" + + "after delete", key.getName(), index); + return; + } + + checkState(reply.vxlanGpeTunnelDetails.size() == 1, + "Unexpected number of returned VxlanGpe tunnels: {} for tunnel: {}", reply.vxlanGpeTunnelDetails, + key.getName()); + LOG.trace("VxlanGpe tunnel: {} attributes returned from VPP: {}", key.getName(), reply); + + final VxlanGpeTunnelDetails swInterfaceVxlanGpeDetails = reply.vxlanGpeTunnelDetails.get(0); + if (swInterfaceVxlanGpeDetails.isIpv6 == 1) { + final Ipv6Address remote6 = + new Ipv6Address(parseAddress(swInterfaceVxlanGpeDetails.remote).getHostAddress()); + builder.setRemote(new IpAddress(remote6)); + final Ipv6Address local6 = + new Ipv6Address(parseAddress(swInterfaceVxlanGpeDetails.local).getHostAddress()); + builder.setLocal(new IpAddress(local6)); + } else { + final byte[] dstBytes = Arrays.copyOfRange(swInterfaceVxlanGpeDetails.remote, 0, 4); + final Ipv4Address remote4 = new Ipv4Address(parseAddress(dstBytes).getHostAddress()); + builder.setRemote(new IpAddress(remote4)); + final byte[] srcBytes = Arrays.copyOfRange(swInterfaceVxlanGpeDetails.local, 0, 4); + final Ipv4Address local4 = new Ipv4Address(parseAddress(srcBytes).getHostAddress()); + builder.setLocal(new IpAddress(local4)); + } + builder.setVni(new VxlanGpeVni((long) swInterfaceVxlanGpeDetails.vni)); + builder.setNextProtocol(VxlanGpeNextProtocol.forValue(swInterfaceVxlanGpeDetails.protocol)); + builder.setEncapVrfId((long) swInterfaceVxlanGpeDetails.encapVrfId); + builder.setDecapVrfId((long) swInterfaceVxlanGpeDetails.decapVrfId); + LOG.debug("VxlanGpe tunnel: {}, id: {} attributes read as: {}", key.getName(), index, builder); + } + + @Nonnull + private static InetAddress parseAddress(@Nonnull final byte[] addr) { + try { + return InetAddress.getByAddress(addr); + } catch (UnknownHostException e) { + throw new IllegalArgumentException("Cannot create InetAddress from " + Arrays.toString(addr), e); + } + } + + @Override + public Initialized<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.VxlanGpe> init( + @Nonnull final InstanceIdentifier<VxlanGpe> id, @Nonnull final VxlanGpe readValue, @Nonnull final ReadContext ctx) { + return Initialized.create(getCfgId(id), + new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.VxlanGpeBuilder() + .setLocal(readValue.getLocal()) + .setRemote(readValue.getRemote()) + .setVni(new VxlanGpeVni(readValue.getVni())) + .setNextProtocol(readValue.getNextProtocol()) + .setEncapVrfId(readValue.getEncapVrfId()) + .setDecapVrfId(readValue.getDecapVrfId()) + .build()); + } + + private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.VxlanGpe> getCfgId( + final InstanceIdentifier<VxlanGpe> id) { + return InterfaceCustomizer.getCfgId(RWUtils.cutId(id, Interface.class)) + .augmentation(VppInterfaceAugmentation.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.VxlanGpe.class); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/acl/ingress/AclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/acl/ingress/AclCustomizer.java new file mode 100644 index 000000000..713868d2e --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/acl/ingress/AclCustomizer.java @@ -0,0 +1,118 @@ +/* + * 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.v3po.interfacesstate.acl.ingress; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.hc2vpp.v3po.interfacesstate.InterfaceCustomizer; +import io.fd.hc2vpp.v3po.vppclassifier.VppClassifierContextManager; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingReaderCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterface; +import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterfaceReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.AclBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.acl.Ingress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces.state._interface.acl.IngressBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Customizer for reading ingress ACLs enabled on given interface. + */ +public class AclCustomizer extends FutureJVppCustomizer + implements InitializingReaderCustomizer<Ingress, IngressBuilder>, AclReader, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(AclCustomizer.class); + private final NamingContext interfaceContext; + private final VppClassifierContextManager classifyTableContext; + + public AclCustomizer(@Nonnull final FutureJVppCore jvpp, @Nonnull final NamingContext interfaceContext, + @Nonnull final VppClassifierContextManager classifyTableContext) { + super(jvpp); + this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); + this.classifyTableContext = checkNotNull(classifyTableContext, "classifyTableContext should not be null"); + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final Ingress readValue) { + ((AclBuilder) parentBuilder).setIngress(readValue); + } + + @Nonnull + @Override + public IngressBuilder getBuilder(@Nonnull final InstanceIdentifier<Ingress> id) { + return new IngressBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, + @Nonnull final IngressBuilder builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + LOG.debug("Reading attributes for interface ACL: {}", id); + final InterfaceKey interfaceKey = id.firstKeyOf(Interface.class); + checkArgument(interfaceKey != null, "No parent interface key found"); + + final ClassifyTableByInterface request = new ClassifyTableByInterface(); + request.swIfIndex = interfaceContext.getIndex(interfaceKey.getName(), ctx.getMappingContext()); + + final ClassifyTableByInterfaceReply reply = + getReplyForRead(getFutureJVpp().classifyTableByInterface(request).toCompletableFuture(), id); + + builder.setL2Acl(readL2Acl(reply.l2TableId, classifyTableContext, ctx.getMappingContext())); + builder.setIp4Acl(readIp4Acl(reply.ip4TableId, classifyTableContext, ctx.getMappingContext())); + builder.setIp6Acl(readIp6Acl(reply.ip6TableId, classifyTableContext, ctx.getMappingContext())); + + if (LOG.isTraceEnabled()) { + LOG.trace("Attributes for ACL {} successfully read: {}", id, builder.build()); + } + } + + @Override + public Initialized<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.acl.Ingress> init( + @Nonnull final InstanceIdentifier<Ingress> id, @Nonnull final Ingress readValue, + @Nonnull final ReadContext ctx) { + return Initialized.create(getCfgId(id), + new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.acl.IngressBuilder() + .setL2Acl(readValue.getL2Acl()) + .setIp4Acl(readValue.getIp4Acl()) + .setIp6Acl(readValue.getIp6Acl()) + .build()); + } + + private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.acl.Ingress> getCfgId( + final InstanceIdentifier<Ingress> id) { + return InterfaceCustomizer.getCfgId(RWUtils.cutId(id, Interface.class)) + .augmentation(VppInterfaceAugmentation.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.Acl.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.acl.Ingress.class); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/acl/ingress/AclReader.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/acl/ingress/AclReader.java new file mode 100644 index 000000000..8f1b428b9 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/acl/ingress/AclReader.java @@ -0,0 +1,60 @@ +/* + * 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.v3po.interfacesstate.acl.ingress; + +import io.fd.hc2vpp.v3po.vppclassifier.VppClassifierContextManager; +import io.fd.honeycomb.translate.MappingContext; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.Ip4Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.Ip4AclBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.Ip6Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.Ip6AclBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.L2Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.base.attributes.L2AclBuilder; + +interface AclReader { + + @Nonnull + default L2Acl readL2Acl(final int l2TableId, @Nonnull final VppClassifierContextManager classifyTableContext, + @Nonnull final MappingContext mappingContext) { + if (l2TableId == ~0) { + return null; + } + return new L2AclBuilder() + .setClassifyTable(classifyTableContext.getTableName(l2TableId, mappingContext)).build(); + } + + @Nonnull + default Ip4Acl readIp4Acl(final int ip4TableId, @Nonnull final VppClassifierContextManager classifyTableContext, + @Nonnull final MappingContext mappingContext) { + if (ip4TableId == ~0) { + return null; + } + return new Ip4AclBuilder() + .setClassifyTable(classifyTableContext.getTableName(ip4TableId, mappingContext)).build(); + } + + @Nonnull + default Ip6Acl readIp6Acl(final int ip6TableId, @Nonnull final VppClassifierContextManager classifyTableContext, + @Nonnull final MappingContext mappingContext) { + if (ip6TableId == ~0) { + return null; + } + return new Ip6AclBuilder() + .setClassifyTable(classifyTableContext.getTableName(ip6TableId, mappingContext)).build(); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/acl/ingress/SubInterfaceAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/acl/ingress/SubInterfaceAclCustomizer.java new file mode 100644 index 000000000..4190619b8 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/acl/ingress/SubInterfaceAclCustomizer.java @@ -0,0 +1,120 @@ +/* + * 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.v3po.interfacesstate.acl.ingress; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static io.fd.hc2vpp.v3po.util.SubInterfaceUtils.getSubInterfaceName; + +import io.fd.hc2vpp.v3po.vppclassifier.VppClassifierContextManager; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingReaderCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.hc2vpp.v3po.interfacesstate.SubInterfaceCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterface; +import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterfaceReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces.state._interface.sub.interfaces.SubInterface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces.state._interface.sub.interfaces.SubInterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.AclBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.acl.Ingress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.acl.IngressBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Customizer for reading ingress ACLs enabled on given sub-interface. + */ +public class SubInterfaceAclCustomizer extends FutureJVppCustomizer + implements InitializingReaderCustomizer<Ingress, IngressBuilder>, AclReader, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceAclCustomizer.class); + private final NamingContext interfaceContext; + private final VppClassifierContextManager classifyTableContext; + + public SubInterfaceAclCustomizer(@Nonnull final FutureJVppCore jvpp, @Nonnull final NamingContext interfaceContext, + @Nonnull final VppClassifierContextManager classifyTableContext) { + super(jvpp); + this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); + this.classifyTableContext = checkNotNull(classifyTableContext, "classifyTableContext should not be null"); + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final Ingress readValue) { + ((AclBuilder) parentBuilder).setIngress(readValue); + } + + @Nonnull + @Override + public IngressBuilder getBuilder(@Nonnull final InstanceIdentifier<Ingress> id) { + return new IngressBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, + @Nonnull final IngressBuilder builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + LOG.debug("Reading attributes for sub-interface ACL: {}", id); + final InterfaceKey parentInterfacekey = id.firstKeyOf(Interface.class); + checkArgument(parentInterfacekey != null, "No parent interface key found"); + final SubInterfaceKey subInterfacekey = id.firstKeyOf(SubInterface.class); + checkArgument(subInterfacekey != null, "No sub-interface key found"); + final String subInterfaceName = + getSubInterfaceName(parentInterfacekey.getName(), subInterfacekey.getIdentifier().intValue()); + + final ClassifyTableByInterface request = new ClassifyTableByInterface(); + request.swIfIndex = interfaceContext.getIndex(subInterfaceName, ctx.getMappingContext()); + + final ClassifyTableByInterfaceReply reply = + getReplyForRead(getFutureJVpp().classifyTableByInterface(request).toCompletableFuture(), id); + + builder.setL2Acl(readL2Acl(reply.l2TableId, classifyTableContext, ctx.getMappingContext())); + builder.setIp4Acl(readIp4Acl(reply.ip4TableId, classifyTableContext, ctx.getMappingContext())); + builder.setIp6Acl(readIp6Acl(reply.ip6TableId, classifyTableContext, ctx.getMappingContext())); + + if (LOG.isTraceEnabled()) { + LOG.trace("Attributes for ACL {} successfully read: {}", id, builder.build()); + } + } + + @Override + public Initialized<Ingress> init( + @Nonnull final InstanceIdentifier<Ingress> id, + @Nonnull final Ingress readValue, + @Nonnull final ReadContext ctx) { + return Initialized.create(getCfgId(id), readValue); + } + + static InstanceIdentifier<Ingress> getCfgId( + final InstanceIdentifier<Ingress> id) { + return SubInterfaceCustomizer.getCfgId(RWUtils.cutId(id, SubInterface.class)) + .child(Acl.class) + .child(Ingress.class); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/Ipv4AddressCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/Ipv4AddressCustomizer.java new file mode 100644 index 000000000..4a577ae60 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/Ipv4AddressCustomizer.java @@ -0,0 +1,166 @@ +/* + * 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.v3po.interfacesstate.ip; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableSet; +import com.google.common.base.Preconditions; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingListReaderCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager; +import io.fd.honeycomb.translate.util.read.cache.IdentifierCacheKeyFactory; +import io.fd.hc2vpp.v3po.interfacesstate.InterfaceCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.ip.dump.params.AddressDumpParams; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.dto.IpAddressDetails; +import io.fd.vpp.jvpp.core.dto.IpAddressDetailsReplyDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.List; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface1; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.Subnet; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.Ipv4Builder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.AddressBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.AddressKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.address.subnet.PrefixLengthBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Read customizer for interface Ipv4 addresses. + */ +public class Ipv4AddressCustomizer extends FutureJVppCustomizer + implements InitializingListReaderCustomizer<Address, AddressKey, AddressBuilder>, Ipv4Reader { + + private static final Logger LOG = LoggerFactory.getLogger(Ipv4AddressCustomizer.class); + private static final String CACHE_KEY = Ipv4AddressCustomizer.class.getName(); + + private final NamingContext interfaceContext; + private final DumpCacheManager<IpAddressDetailsReplyDump, AddressDumpParams> dumpManager; + + public Ipv4AddressCustomizer(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final NamingContext interfaceContext) { + super(futureJVppCore); + this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); + this.dumpManager = + new DumpCacheManager.DumpCacheManagerBuilder<IpAddressDetailsReplyDump, AddressDumpParams>() + .withExecutor(createExecutor(futureJVppCore)) + // Key needs to contain interface ID to distinguish dumps between interfaces + .withCacheKeyFactory(new IdentifierCacheKeyFactory(ImmutableSet.of(Interface.class))) + .build(); + } + + @Override + @Nonnull + public AddressBuilder getBuilder(@Nonnull InstanceIdentifier<Address> id) { + return new AddressBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull InstanceIdentifier<Address> id, @Nonnull AddressBuilder builder, + @Nonnull ReadContext ctx) + throws ReadFailedException { + LOG.debug("Reading attributes for interface address: {}", id); + + final String interfaceName = id.firstKeyOf(Interface.class).getName(); + final int interfaceIndex = interfaceContext.getIndex(interfaceName, ctx.getMappingContext()); + final Optional<IpAddressDetailsReplyDump> dumpOptional = + dumpManager.getDump(id, ctx.getModificationCache(), new AddressDumpParams(interfaceIndex, false)); + + if (!dumpOptional.isPresent() || dumpOptional.get().ipAddressDetails.isEmpty()) { + return; + } + final Optional<IpAddressDetails> ipAddressDetails = + findIpAddressDetailsByIp(dumpOptional, id.firstKeyOf(Address.class).getIp()); + + if (ipAddressDetails.isPresent()) { + final IpAddressDetails detail = ipAddressDetails.get(); + builder.setIp(arrayToIpv4AddressNoZone(detail.ip)) + .setSubnet( + new PrefixLengthBuilder().setPrefixLength(Short.valueOf(detail.prefixLength)).build()); + + if (LOG.isDebugEnabled()) { + LOG.debug("Attributes for {} interface (id={}) address {} successfully read: {}", + interfaceName, interfaceIndex, id, builder.build()); + } + } + } + + @Override + public List<AddressKey> getAllIds(@Nonnull InstanceIdentifier<Address> id, @Nonnull ReadContext ctx) + throws ReadFailedException { + LOG.debug("Reading list of keys for interface addresses: {}", id); + + final String interfaceName = id.firstKeyOf(Interface.class).getName(); + final int interfaceIndex = interfaceContext.getIndex(interfaceName, ctx.getMappingContext()); + final Optional<IpAddressDetailsReplyDump> dumpOptional = + dumpManager.getDump(id, ctx.getModificationCache(), new AddressDumpParams(interfaceIndex, false)); + + return getAllIpv4AddressIds(dumpOptional, AddressKey::new); + } + + @Override + public void merge(@Nonnull Builder<? extends DataObject> builder, @Nonnull List<Address> readData) { + ((Ipv4Builder) builder).setAddress(readData); + } + + @Override + public Initialized<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Address> init( + @Nonnull final InstanceIdentifier<Address> id, @Nonnull final Address readValue, + @Nonnull final ReadContext ctx) { + return Initialized.create(getCfgId(id), + new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.AddressBuilder() + .setIp(readValue.getIp()) + .setSubnet(getSubnet(readValue)) + .build()); + } + + private static Subnet getSubnet(final Address address) { + final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.address.Subnet + subnet = address.getSubnet(); + + // Only prefix length supported + Preconditions.checkArgument( + subnet instanceof org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.address.subnet.PrefixLength); + + return new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.PrefixLengthBuilder() + .setPrefixLength( + ((org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.address.subnet.PrefixLength) subnet) + .getPrefixLength()).build(); + } + + static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Address> getCfgId( + final InstanceIdentifier<Address> id) { + return InterfaceCustomizer.getCfgId(RWUtils.cutId(id, Interface.class)) + .augmentation(Interface1.class) + .child(Ipv4.class) + .child(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Address.class, + new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.AddressKey(id.firstKeyOf(Address.class).getIp())); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/Ipv4Customizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/Ipv4Customizer.java new file mode 100644 index 000000000..316f8ec47 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/Ipv4Customizer.java @@ -0,0 +1,59 @@ +/* + * 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.v3po.interfacesstate.ip; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.ReaderCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface2Builder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.Ipv4; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.Ipv4Builder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Ipv4Customizer extends FutureJVppCustomizer implements ReaderCustomizer<Ipv4, Ipv4Builder> { + + private static final Logger LOG = LoggerFactory.getLogger(Ipv4Customizer.class); + + public Ipv4Customizer(@Nonnull final FutureJVppCore futureJVppCore) { + super(futureJVppCore); + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final Ipv4 readValue) { + ((Interface2Builder) parentBuilder).setIpv4(readValue); + } + + @Nonnull + @Override + public Ipv4Builder getBuilder(@Nonnull final InstanceIdentifier<Ipv4> id) { + return new Ipv4Builder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Ipv4> id, @Nonnull final Ipv4Builder builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + LOG.debug("Reading Ipv4 leaves (mtu, forwarding) is not supported by VPP API"); + } + +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/Ipv4NeighbourCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/Ipv4NeighbourCustomizer.java new file mode 100644 index 000000000..a32f8f20c --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/Ipv4NeighbourCustomizer.java @@ -0,0 +1,73 @@ +/* + * 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.v3po.interfacesstate.ip; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.ListReaderCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.Collections; +import java.util.List; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.Ipv4Builder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.Neighbor; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.NeighborBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.NeighborKey; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Operational data read operation customizer for {@link Neighbor}<br> + * Currently not supported in jvpp, so this is only dummy implementation<br> + */ +public class Ipv4NeighbourCustomizer extends FutureJVppCustomizer + implements ListReaderCustomizer<Neighbor, NeighborKey, NeighborBuilder> { + + private static final Logger LOG = LoggerFactory.getLogger(Ipv4NeighbourCustomizer.class); + + public Ipv4NeighbourCustomizer(FutureJVppCore futureJVppCore) { + super(futureJVppCore); + } + + @Override + public NeighborBuilder getBuilder(InstanceIdentifier<Neighbor> id) { + return new NeighborBuilder(); + } + + @Override + public void readCurrentAttributes(InstanceIdentifier<Neighbor> id, NeighborBuilder builder, ReadContext ctx) + throws ReadFailedException { + //TODO - not supported, implement https://jira.fd.io/browse/VPP-164 first + implement init + LOG.warn("Operation not supported"); + } + + @Override + public List<NeighborKey> getAllIds(InstanceIdentifier<Neighbor> id, ReadContext context) + throws ReadFailedException { + //TODO - not supported, implement https://jira.fd.io/browse/VPP-164 first + LOG.warn("Operation not supported,returning empty List"); + return Collections.emptyList(); + } + + @Override + public void merge(Builder<? extends DataObject> builder, List<Neighbor> readData) { + ((Ipv4Builder) builder).setNeighbor(readData); + } +}
\ No newline at end of file diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/Ipv4Reader.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/Ipv4Reader.java new file mode 100644 index 000000000..729ca097d --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/Ipv4Reader.java @@ -0,0 +1,84 @@ +/* + * 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.v3po.interfacesstate.ip; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.util.read.cache.EntityDumpExecutor; +import io.fd.hc2vpp.v3po.interfacesstate.ip.dump.params.AddressDumpParams; +import io.fd.hc2vpp.common.translate.util.Ipv4Translator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.vpp.jvpp.core.dto.IpAddressDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone; +import org.opendaylight.yangtools.yang.binding.Identifier; +import io.fd.vpp.jvpp.core.dto.IpAddressDetails; +import io.fd.vpp.jvpp.core.dto.IpAddressDetailsReplyDump; + +/** + * Utility class providing Ipv4 read support. + */ +interface Ipv4Reader extends Ipv4Translator, JvppReplyConsumer { + + @Nonnull + default <T extends Identifier> List<T> getAllIpv4AddressIds( + final Optional<IpAddressDetailsReplyDump> dumpOptional, + @Nonnull final Function<Ipv4AddressNoZone, T> keyConstructor) { + if (dumpOptional.isPresent() && dumpOptional.get().ipAddressDetails != null) { + return dumpOptional.get().ipAddressDetails.stream() + .map(detail -> keyConstructor.apply(arrayToIpv4AddressNoZone(detail.ip))) + .collect(Collectors.toList()); + } else { + return Collections.emptyList(); + } + } + + default Optional<IpAddressDetails> findIpAddressDetailsByIp( + final Optional<IpAddressDetailsReplyDump> dump, + @Nonnull final Ipv4AddressNoZone ip) { + checkNotNull(ip, "ip address should not be null"); + + if (dump.isPresent() && dump.get().ipAddressDetails != null) { + final List<IpAddressDetails> details = dump.get().ipAddressDetails; + + return Optional.of(details.stream() + .filter(singleDetail -> ip.equals(arrayToIpv4AddressNoZone(singleDetail.ip))) + .collect(RWUtils.singleItemCollector())); + } + return Optional.absent(); + } + + default EntityDumpExecutor<IpAddressDetailsReplyDump, AddressDumpParams> createExecutor( + @Nonnull final FutureJVppCore vppApi) { + return (identifier, params) -> { + checkNotNull(params, "Address dump params cannot be null"); + + final IpAddressDump dumpRequest = new IpAddressDump(); + dumpRequest.isIpv6 = booleanToByte(params.isIpv6()); + dumpRequest.swIfIndex = params.getInterfaceIndex(); + + return getReplyForRead(vppApi.ipAddressDump(dumpRequest).toCompletableFuture(), identifier); + }; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/Ipv6Customizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/Ipv6Customizer.java new file mode 100644 index 000000000..97f223a8c --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/Ipv6Customizer.java @@ -0,0 +1,66 @@ +/* + * 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.v3po.interfacesstate.ip; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.ReaderCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface2Builder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.Ipv6; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.Ipv6Builder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; + +public class Ipv6Customizer extends FutureJVppCustomizer implements ReaderCustomizer<Ipv6, Ipv6Builder> { + + private final NamingContext interfaceContext; + + public Ipv6Customizer(@Nonnull final FutureJVppCore futureJVppCore, final NamingContext interfaceContext) { + super(futureJVppCore); + this.interfaceContext = interfaceContext; + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final Ipv6 readValue) { + ((Interface2Builder) parentBuilder).setIpv6(readValue); + } + + @Nonnull + @Override + public Ipv6Builder getBuilder(@Nonnull final InstanceIdentifier<Ipv6> id) { + return new Ipv6Builder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Ipv6> id, @Nonnull final Ipv6Builder builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + // TODO HONEYCOMB-102 implement + init +// final IpAddressDump dumpRequest = new IpAddressDump(); +// dumpRequest.isIpv6 = 1; +// dumpRequest.swIfIndex = interfaceContext.getIndex(id.firstKeyOf(Interface.class).getName(), +// ctx.getMappingContext()); +// final CompletionStage<IpAddressDetailsReplyDump> addressDumpFuture = getFutureJVpp(). +// ipAddressDump(dumpRequest); +// final IpAddressDetailsReplyDump reply = TranslateUtils.getReply(addressDumpFuture.toCompletableFuture()); + } + +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/SubInterfaceIpv4AddressCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/SubInterfaceIpv4AddressCustomizer.java new file mode 100644 index 000000000..eee49799b --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/SubInterfaceIpv4AddressCustomizer.java @@ -0,0 +1,143 @@ +/* + * 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.v3po.interfacesstate.ip; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableSet; +import io.fd.hc2vpp.v3po.interfacesstate.SubInterfaceCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.ip.dump.params.AddressDumpParams; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingListReaderCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager; +import io.fd.honeycomb.translate.util.read.cache.IdentifierCacheKeyFactory; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.hc2vpp.v3po.util.SubInterfaceUtils; +import io.fd.vpp.jvpp.core.dto.IpAddressDetails; +import io.fd.vpp.jvpp.core.dto.IpAddressDetailsReplyDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.List; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces.state._interface.sub.interfaces.SubInterface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.Ipv4; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.Ipv4Builder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.ipv4.Address; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.ipv4.AddressBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.ipv4.AddressKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.ip4.attributes.ipv4.address.subnet.PrefixLengthBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Read customizer for sub-interface Ipv4 addresses. + */ +public class SubInterfaceIpv4AddressCustomizer extends FutureJVppCustomizer + implements InitializingListReaderCustomizer<Address, AddressKey, AddressBuilder>, Ipv4Reader { + + private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceIpv4AddressCustomizer.class); + + private final NamingContext interfaceContext; + private final DumpCacheManager<IpAddressDetailsReplyDump, AddressDumpParams> dumpManager; + + public SubInterfaceIpv4AddressCustomizer(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final NamingContext interfaceContext) { + super(futureJVppCore); + this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); + this.dumpManager = new DumpCacheManager.DumpCacheManagerBuilder<IpAddressDetailsReplyDump, AddressDumpParams>() + .withExecutor(createExecutor(futureJVppCore)) + //same as with ipv4 addresses for interfaces, these must have cache scope of their parent sub-interface + .withCacheKeyFactory(new IdentifierCacheKeyFactory(ImmutableSet.of(SubInterface.class))) + .build(); + } + + @Override + @Nonnull + public AddressBuilder getBuilder(@Nonnull InstanceIdentifier<Address> id) { + return new AddressBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull InstanceIdentifier<Address> id, @Nonnull AddressBuilder builder, + @Nonnull ReadContext ctx) + throws ReadFailedException { + LOG.debug("Reading attributes for sub-interface address: {}", id); + + final String subInterfaceName = getSubInterfaceName(id); + final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, ctx.getMappingContext()); + final Optional<IpAddressDetailsReplyDump> dumpOptional = dumpManager + .getDump(id, ctx.getModificationCache(), new AddressDumpParams(subInterfaceIndex, false)); + + final Optional<IpAddressDetails> ipAddressDetails = + findIpAddressDetailsByIp(dumpOptional, id.firstKeyOf(Address.class).getIp()); + + if (ipAddressDetails.isPresent()) { + final IpAddressDetails detail = ipAddressDetails.get(); + builder.setIp(arrayToIpv4AddressNoZone(detail.ip)); + builder.setSubnet(new PrefixLengthBuilder().setPrefixLength(Short.valueOf(detail.prefixLength)).build()); + + if (LOG.isDebugEnabled()) { + LOG.debug("Attributes for {} sub-interface (id={}) address {} successfully read: {}", + subInterfaceName, subInterfaceIndex, id, builder.build()); + } + } + } + + @Override + public List<AddressKey> getAllIds(@Nonnull InstanceIdentifier<Address> id, @Nonnull ReadContext ctx) + throws ReadFailedException { + LOG.debug("Reading list of keys for sub-interface addresses: {}", id); + + final String subInterfaceName = getSubInterfaceName(id); + final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, ctx.getMappingContext()); + final Optional<IpAddressDetailsReplyDump> dumpOptional = dumpManager + .getDump(id, ctx.getModificationCache(), new AddressDumpParams(subInterfaceIndex, false)); + + return getAllIpv4AddressIds(dumpOptional, AddressKey::new); + } + + @Override + public void merge(@Nonnull Builder<? extends DataObject> builder, @Nonnull List<Address> readData) { + ((Ipv4Builder) builder).setAddress(readData); + } + + private static String getSubInterfaceName(@Nonnull final InstanceIdentifier<Address> id) { + return SubInterfaceUtils.getSubInterfaceName(id.firstKeyOf(Interface.class).getName(), + Math.toIntExact(id.firstKeyOf(SubInterface.class).getIdentifier())); + } + + @Override + public Initialized<Address> init( + @Nonnull final InstanceIdentifier<Address> id, @Nonnull final Address readValue, + @Nonnull final ReadContext ctx) { + return Initialized.create(getCfgId(id), readValue); + } + + private InstanceIdentifier<Address> getCfgId(final InstanceIdentifier<Address> id) { + return SubInterfaceCustomizer.getCfgId(RWUtils.cutId(id, SubInterface.class)) + .child(Ipv4.class) + .child(Address.class, new AddressKey(id.firstKeyOf(Address.class))); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/dump/params/AddressDumpParams.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/dump/params/AddressDumpParams.java new file mode 100644 index 000000000..cb80893be --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/ip/dump/params/AddressDumpParams.java @@ -0,0 +1,44 @@ +/* + * 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.v3po.interfacesstate.ip.dump.params; + +public class AddressDumpParams { + + private final int interfaceIndex; + private final boolean isIpv6; + + public AddressDumpParams(final int interfaceIndex, final boolean isIpv6) { + this.interfaceIndex = interfaceIndex; + this.isIpv6 = isIpv6; + } + + public int getInterfaceIndex() { + return interfaceIndex; + } + + public boolean isIpv6() { + return isIpv6; + } + + @Override + public String toString() { + return "AddressDumpParams{" + + "interfaceIndex=" + interfaceIndex + + ", isIpv6=" + isIpv6 + + '}'; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/pbb/PbbRewriteStateCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/pbb/PbbRewriteStateCustomizer.java new file mode 100644 index 000000000..4d560a55b --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/pbb/PbbRewriteStateCustomizer.java @@ -0,0 +1,57 @@ +/* + * 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.v3po.interfacesstate.pbb; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.ReaderCustomizer; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.pbb.rev161214.PbbRewriteStateInterfaceAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.pbb.rev161214.interfaces.state._interface.PbbRewriteState; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.pbb.rev161214.interfaces.state._interface.PbbRewriteStateBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class PbbRewriteStateCustomizer extends FutureJVppCustomizer + implements ReaderCustomizer<PbbRewriteState, PbbRewriteStateBuilder> { + + public PbbRewriteStateCustomizer(@Nonnull final FutureJVppCore futureJVppCore) { + super(futureJVppCore); + } + + @Nonnull + @Override + public PbbRewriteStateBuilder getBuilder(@Nonnull final InstanceIdentifier<PbbRewriteState> id) { + return new PbbRewriteStateBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<PbbRewriteState> id, + @Nonnull final PbbRewriteStateBuilder builder, @Nonnull final ReadContext ctx) + throws ReadFailedException { + //TODO implement read after https://jira.fd.io/browse/VPP-468 + init + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, + @Nonnull final PbbRewriteState readValue) { + ((PbbRewriteStateInterfaceAugmentationBuilder) parentBuilder).setPbbRewriteState(readValue); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/notification/InterfaceChangeNotificationProducer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/notification/InterfaceChangeNotificationProducer.java new file mode 100644 index 000000000..f7a159de7 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/notification/InterfaceChangeNotificationProducer.java @@ -0,0 +1,163 @@ +/* + * 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.v3po.notification; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import com.google.inject.Inject; +import com.google.inject.name.Named; +import io.fd.honeycomb.notification.ManagedNotificationProducer; +import io.fd.honeycomb.notification.NotificationCollector; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.TimeoutException; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.InterfaceDeleted; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.InterfaceDeletedBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.InterfaceNameOrIndex; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.InterfaceStateChange; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.InterfaceStateChangeBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.InterfaceStatus; +import org.opendaylight.yangtools.yang.binding.Notification; +import io.fd.vpp.jvpp.VppBaseCallException; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsNotification; +import io.fd.vpp.jvpp.core.dto.WantInterfaceEvents; +import io.fd.vpp.jvpp.core.dto.WantInterfaceEventsReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Notification producer for interface events. It starts interface notification stream and for every + * received notification, it transforms it into its BA equivalent and pushes into HC's notification collector. + */ +@NotThreadSafe +public final class InterfaceChangeNotificationProducer implements ManagedNotificationProducer, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(InterfaceChangeNotificationProducer.class); + + private final FutureJVppCore jvpp; + private final NamingContext interfaceContext; + private final MappingContext mappingContext; + @Nullable + private AutoCloseable notificationListenerReg; + + @Inject + public InterfaceChangeNotificationProducer(@Nonnull final FutureJVppCore jvpp, + @Named("interface-context") @Nonnull final NamingContext interfaceContext, + @Named("honeycomb-context") @Nonnull final MappingContext mappingContext) { + this.jvpp = jvpp; + this.interfaceContext = interfaceContext; + this.mappingContext = mappingContext; + } + + @Override + public void start(final NotificationCollector collector) { + LOG.trace("Starting interface notifications"); + enableDisableIfcNotifications(1); + LOG.debug("Interface notifications started successfully"); + notificationListenerReg = jvpp.getNotificationRegistry().registerSwInterfaceSetFlagsNotificationCallback( + swInterfaceSetFlagsNotification -> { + LOG.trace("Interface notification received: {}", swInterfaceSetFlagsNotification); + // TODO HONEYCOMB-166 this should be lazy + collector.onNotification(transformNotification(swInterfaceSetFlagsNotification)); + } + ); + } + + private Notification transformNotification(final SwInterfaceSetFlagsNotification swInterfaceSetFlagsNotification) { + if (swInterfaceSetFlagsNotification.deleted == 1) { + return new InterfaceDeletedBuilder().setName(getIfcName(swInterfaceSetFlagsNotification)).build(); + } else { + return new InterfaceStateChangeBuilder() + .setName(getIfcName(swInterfaceSetFlagsNotification)) + .setAdminStatus(swInterfaceSetFlagsNotification.adminUpDown == 1 + ? InterfaceStatus.Up + : InterfaceStatus.Down) + .setOperStatus(swInterfaceSetFlagsNotification.linkUpDown == 1 + ? InterfaceStatus.Up + : InterfaceStatus.Down) + .build(); + } + } + + /** + * Get mapped name for the interface. Best effort only! The mapping might not yet be stored in context + * data tree (write transaction is still in progress and context changes have not been committed yet, or + * VPP sends the notification before it returns create request(that would store mapping)). + * <p/> + * In case mapping is not available, index is used as name. + */ + private InterfaceNameOrIndex getIfcName(final SwInterfaceSetFlagsNotification swInterfaceSetFlagsNotification) { + final Optional<String> optionalName = + interfaceContext.getNameIfPresent(swInterfaceSetFlagsNotification.swIfIndex, mappingContext); + return optionalName.isPresent() + ? new InterfaceNameOrIndex(optionalName.get()) + : new InterfaceNameOrIndex((long) swInterfaceSetFlagsNotification.swIfIndex); + } + + @Override + public void stop() { + LOG.trace("Stopping interface notifications"); + enableDisableIfcNotifications(0); + LOG.debug("Interface notifications stopped successfully"); + try { + if (notificationListenerReg != null) { + notificationListenerReg.close(); + } + } catch (Exception e) { + LOG.warn("Unable to properly close notification registration: {}", notificationListenerReg, e); + } + } + + private void enableDisableIfcNotifications(int enableDisable) { + final WantInterfaceEvents wantInterfaceEvents = new WantInterfaceEvents(); + wantInterfaceEvents.pid = 1; + wantInterfaceEvents.enableDisable = enableDisable; + final CompletionStage<WantInterfaceEventsReply> wantInterfaceEventsReplyCompletionStage; + try { + wantInterfaceEventsReplyCompletionStage = jvpp.wantInterfaceEvents(wantInterfaceEvents); + getReply(wantInterfaceEventsReplyCompletionStage.toCompletableFuture()); + } catch (VppBaseCallException | TimeoutException e) { + LOG.warn("Unable to {} interface notifications", enableDisable == 1 + ? "enable" + : "disable", e); + throw new IllegalStateException("Unable to control interface notifications", e); + } + } + + @Nonnull + @Override + public Collection<Class<? extends Notification>> getNotificationTypes() { + final ArrayList<Class<? extends Notification>> classes = Lists.newArrayList(); + classes.add(InterfaceStateChange.class); + classes.add(InterfaceDeleted.class); + return classes; + } + + @Override + public void close() throws Exception { + LOG.trace("Closing interface notifications producer"); + stop(); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/util/SubInterfaceUtils.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/util/SubInterfaceUtils.java new file mode 100644 index 000000000..cb4e11af5 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/util/SubInterfaceUtils.java @@ -0,0 +1,52 @@ +/* + * 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.v3po.util; + +import java.util.List; +import javax.annotation.Nonnegative; +import javax.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.Tags; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.tags.Tag; + +public final class SubInterfaceUtils { + + private SubInterfaceUtils() { + throw new UnsupportedOperationException("Utility class cannot be instantiated."); + } + + public static String getSubInterfaceName(final String superIfName, final int subIfaceId) { + return String.format("%s.%d", superIfName, subIfaceId); + } + + /** + * Returns number of sub-interface tags. + * + * @param tags data object that represents sub-interface tags + * @return number of sub interface tags + */ + @Nonnegative + public static int getNumberOfTags(@Nullable final Tags tags) { + if (tags == null) { + return 0; + } + final List<Tag> tagList = tags.getTag(); + if (tagList == null) { + return 0; + } + return tagList.size(); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vpp/ArpTerminationTableEntryCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vpp/ArpTerminationTableEntryCustomizer.java new file mode 100644 index 000000000..e7c1dea3d --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vpp/ArpTerminationTableEntryCustomizer.java @@ -0,0 +1,118 @@ +/* + * 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.v3po.vpp; + +import com.google.common.base.Preconditions; +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.hc2vpp.common.translate.util.AddressTranslator; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.BdIpMacAddDel; +import io.fd.vpp.jvpp.core.dto.BdIpMacAddDelReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.bridge.domain.attributes.arp.termination.table.ArpTerminationTableEntry; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.bridge.domain.attributes.arp.termination.table.ArpTerminationTableEntryKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.bridge.domains.BridgeDomain; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Writer Customizer responsible for ARP termination table management.<br> Sends {@code bd_ip_mac_add_del} message to + * VPP.<br> Equivalent of invoking {@code vppctl set bridge-domain arp term} command. + */ +public class ArpTerminationTableEntryCustomizer extends FutureJVppCustomizer + implements ListWriterCustomizer<ArpTerminationTableEntry, ArpTerminationTableEntryKey>, ByteDataTranslator, + AddressTranslator, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(ArpTerminationTableEntryCustomizer.class); + + private final NamingContext bdContext; + + public ArpTerminationTableEntryCustomizer(@Nonnull final FutureJVppCore futureJvpp, + @Nonnull final NamingContext bdContext) { + super(futureJvpp); + this.bdContext = Preconditions.checkNotNull(bdContext, "bdContext should not be null"); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<ArpTerminationTableEntry> id, + @Nonnull final ArpTerminationTableEntry dataAfter, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("Creating ARP termination table entry: {} {}", id, dataAfter); + bdIpMacAddDel(id, dataAfter, writeContext, true); + LOG.debug("L2 ARP termination table entry created successfully: {} {}", id, dataAfter); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<ArpTerminationTableEntry> id, + @Nonnull final ArpTerminationTableEntry dataBefore, + @Nonnull final ArpTerminationTableEntry dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + throw new UnsupportedOperationException( + "ARP termination table entry update is not supported. It has to be deleted and then created."); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<ArpTerminationTableEntry> id, + @Nonnull final ArpTerminationTableEntry dataBefore, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("Deleting ARP termination table entry entry: {} {}", id, dataBefore); + bdIpMacAddDel(id, dataBefore, writeContext, false); + LOG.debug("ARP termination table entry deleted successfully: {} {}", id, dataBefore); + } + + private void bdIpMacAddDel(@Nonnull final InstanceIdentifier<ArpTerminationTableEntry> id, + @Nonnull final ArpTerminationTableEntry entry, + final WriteContext writeContext, boolean isAdd) throws WriteFailedException { + final String bdName = id.firstKeyOf(BridgeDomain.class).getName(); + final int bdId = bdContext.getIndex(bdName, writeContext.getMappingContext()); + + final BdIpMacAddDel request = createRequest(entry, bdId, isAdd); + LOG.debug("Sending l2FibAddDel request: {}", request); + final CompletionStage<BdIpMacAddDelReply> replyCompletionStage = + getFutureJVpp().bdIpMacAddDel(request); + + getReplyForWrite(replyCompletionStage.toCompletableFuture(), id); + } + + private BdIpMacAddDel createRequest(final ArpTerminationTableEntry entry, final int bdId, boolean isAdd) { + final BdIpMacAddDel request = new BdIpMacAddDel(); + request.bdId = bdId; + request.isAdd = booleanToByte(isAdd); + request.macAddress = parseMac(entry.getPhysAddress().getValue()); + + final IpAddress ipAddress = entry.getIpAddress(); + if (ipAddress.getIpv6Address() != null) { + // FIXME: HONEYCOMB-187 vpp does not support ipv6 in arp-termination table (based on analysis of l2_bd.c) + throw new UnsupportedOperationException("IPv6 address for ARP termination table is not supported yet"); + } + + request.ipAddress = ipv4AddressNoZoneToArray(new Ipv4AddressNoZone(ipAddress.getIpv4Address())); + return request; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vpp/BridgeDomainCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vpp/BridgeDomainCustomizer.java new file mode 100644 index 000000000..b4b86264b --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vpp/BridgeDomainCustomizer.java @@ -0,0 +1,137 @@ +/* + * 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.v3po.vpp; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Preconditions; +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.BridgeDomainAddDel; +import io.fd.vpp.jvpp.core.dto.BridgeDomainAddDelReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.GuardedBy; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.bridge.domains.BridgeDomain; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.bridge.domains.BridgeDomainKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BridgeDomainCustomizer + extends FutureJVppCustomizer + implements ListWriterCustomizer<BridgeDomain, BridgeDomainKey>, ByteDataTranslator, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(BridgeDomainCustomizer.class); + + private static final byte ADD_OR_UPDATE_BD = (byte) 1; + private final NamingContext bdContext; + @GuardedBy("this") + private int bridgeDomainIndexCounter = 1; + + public BridgeDomainCustomizer(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final NamingContext bdContext) { + super(futureJVppCore); + this.bdContext = Preconditions.checkNotNull(bdContext, "bdContext should not be null"); + } + + private BridgeDomainAddDelReply addOrUpdateBridgeDomain(@Nonnull final InstanceIdentifier<BridgeDomain> id, + final int bdId, @Nonnull final BridgeDomain bd) + throws WriteFailedException { + final BridgeDomainAddDelReply reply; + final BridgeDomainAddDel request = new BridgeDomainAddDel(); + request.bdId = bdId; + request.flood = booleanToByte(bd.isFlood()); + request.forward = booleanToByte(bd.isForward()); + request.learn = booleanToByte(bd.isLearn()); + request.uuFlood = booleanToByte(bd.isUnknownUnicastFlood()); + request.arpTerm = booleanToByte(bd.isArpTermination()); + request.isAdd = ADD_OR_UPDATE_BD; + + reply = getReplyForWrite(getFutureJVpp().bridgeDomainAddDel(request).toCompletableFuture(), id); + LOG.debug("Bridge domain {} (id={}) add/update successful", bd.getName(), bdId); + return reply; + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<BridgeDomain> id, + @Nonnull final BridgeDomain dataBefore, + @Nonnull final WriteContext ctx) + throws WriteFailedException { + LOG.debug("writeCurrentAttributes: id={}, current={}, ctx={}", id, dataBefore, ctx); + final String bdName = dataBefore.getName(); + + // Invoke 1. check index, 2. increase index 3. create ND 4. store mapping in a synchronized block to prevent + // race conditions in case of concurrent invocation + synchronized (this) { + int index; + if (bdContext.containsIndex(bdName, ctx.getMappingContext())) { + index = bdContext.getIndex(bdName, ctx.getMappingContext()); + } else { + // Critical section due to bridgeDomainIndexCounter read and write access + // TODO HONEYCOMB-199 move this "get next available index" into naming context or an adapter + // or a dedicated object + + // Use counter to assign bridge domain index, but still check naming context if it's not taken there + while (bdContext.containsName(bridgeDomainIndexCounter, ctx.getMappingContext())) { + bridgeDomainIndexCounter++; + } + index = bridgeDomainIndexCounter; + } + addOrUpdateBridgeDomain(id, index, dataBefore); + bdContext.addName(index, bdName, ctx.getMappingContext()); + } + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<BridgeDomain> id, + @Nonnull final BridgeDomain dataBefore, + @Nonnull final WriteContext ctx) + throws WriteFailedException { + LOG.debug("deleteCurrentAttributes: id={}, dataBefore={}, ctx={}", id, dataBefore, ctx); + final String bdName = id.firstKeyOf(BridgeDomain.class).getName(); + int bdId = bdContext.getIndex(bdName, ctx.getMappingContext()); + + final BridgeDomainAddDel request = new BridgeDomainAddDel(); + request.bdId = bdId; + + getReplyForWrite(getFutureJVpp().bridgeDomainAddDel(request).toCompletableFuture(), id); + LOG.debug("Bridge domain {} (id={}) deleted successfully", bdName, bdId); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<BridgeDomain> id, + @Nonnull final BridgeDomain dataBefore, @Nonnull final BridgeDomain dataAfter, + @Nonnull final WriteContext ctx) + throws WriteFailedException { + LOG.debug("updateCurrentAttributes: id={}, dataBefore={}, dataAfter={}, ctx={}", id, dataBefore, dataAfter, + ctx); + + final String bdName = checkNotNull(dataAfter.getName()); + checkArgument(bdName.equals(dataBefore.getName()), + "BridgeDomain name changed. It should be deleted and then created."); + + addOrUpdateBridgeDomain(id, bdContext.getIndex(bdName, ctx.getMappingContext()), dataAfter); + } + +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vpp/L2FibEntryCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vpp/L2FibEntryCustomizer.java new file mode 100644 index 000000000..6328555a3 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vpp/L2FibEntryCustomizer.java @@ -0,0 +1,129 @@ +/* + * 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.v3po.vpp; + +import com.google.common.base.Preconditions; +import com.google.common.primitives.Longs; +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.MacTranslator; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.L2FibAddDel; +import io.fd.vpp.jvpp.core.dto.L2FibAddDelReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.L2FibFilter; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.fib.attributes.l2.fib.table.L2FibEntry; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.fib.attributes.l2.fib.table.L2FibEntryKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.bridge.domains.BridgeDomain; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Writer Customizer responsible for L2 FIB create/delete operations.<br> Sends {@code l2_fib_add_del} message to + * VPP.<br> Equivalent of invoking {@code vppctl l2fib add/del} command. + */ +public class L2FibEntryCustomizer extends FutureJVppCustomizer + implements ListWriterCustomizer<L2FibEntry, L2FibEntryKey>, ByteDataTranslator, MacTranslator, + JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(L2FibEntryCustomizer.class); + + private final NamingContext bdContext; + private final NamingContext interfaceContext; + + public L2FibEntryCustomizer(@Nonnull final FutureJVppCore futureJVppCore, @Nonnull final NamingContext bdContext, + @Nonnull final NamingContext interfaceContext) { + super(futureJVppCore); + this.bdContext = Preconditions.checkNotNull(bdContext, "bdContext should not be null"); + this.interfaceContext = Preconditions.checkNotNull(interfaceContext, "interfaceContext should not be null"); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<L2FibEntry> id, + @Nonnull final L2FibEntry dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + + LOG.debug("Creating L2 FIB entry: {} {}", id, dataAfter); + l2FibAddDel(id, dataAfter, writeContext, true); + LOG.debug("L2 FIB entry created successfully: {} {}", id, dataAfter); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<L2FibEntry> id, + @Nonnull final L2FibEntry dataBefore, @Nonnull final L2FibEntry dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + throw new UnsupportedOperationException( + "L2 FIB entry update is not supported. It has to be deleted and then created."); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<L2FibEntry> id, + @Nonnull final L2FibEntry dataBefore, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + + LOG.debug("Deleting L2 FIB entry: {} {}", id, dataBefore); + l2FibAddDel(id, dataBefore, writeContext, false); + LOG.debug("L2 FIB entry deleted successfully: {} {}", id, dataBefore); + } + + private void l2FibAddDel(@Nonnull final InstanceIdentifier<L2FibEntry> id, @Nonnull final L2FibEntry entry, + final WriteContext writeContext, boolean isAdd) throws WriteFailedException { + final String bdName = id.firstKeyOf(BridgeDomain.class).getName(); + final int bdId = bdContext.getIndex(bdName, writeContext.getMappingContext()); + + int swIfIndex = -1; + final String swIfName = entry.getOutgoingInterface(); + if (swIfName != null) { + swIfIndex = interfaceContext.getIndex(swIfName, writeContext.getMappingContext()); + } + + final L2FibAddDel l2FibRequest = createL2FibRequest(entry, bdId, swIfIndex, isAdd); + LOG.debug("Sending l2FibAddDel request: {}", l2FibRequest); + final CompletionStage<L2FibAddDelReply> l2FibAddDelReplyCompletionStage = + getFutureJVpp().l2FibAddDel(l2FibRequest); + + getReplyForWrite(l2FibAddDelReplyCompletionStage.toCompletableFuture(), id); + } + + private L2FibAddDel createL2FibRequest(final L2FibEntry entry, final int bdId, final int swIfIndex, boolean isAdd) { + final L2FibAddDel request = new L2FibAddDel(); + request.mac = macToLong(entry.getPhysAddress().getValue()); + request.bdId = bdId; + request.swIfIndex = swIfIndex; + request.isAdd = booleanToByte(isAdd); + if (isAdd) { + request.staticMac = booleanToByte(entry.isStaticConfig()); + request.filterMac = booleanToByte(L2FibFilter.class == entry.getAction()); + } + return request; + } + + // mac address is string of the form: 11:22:33:44:55:66 + // but VPP expects long value in the format 11:22:33:44:55:66:XX:XX + private long macToLong(final String macAddress) { + final byte[] mac = parseMac(macAddress); + return Longs.fromBytes(mac[0], mac[1], mac[2], mac[3], + mac[4], mac[5], (byte) 0, (byte) 0); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/ClassifySessionReader.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/ClassifySessionReader.java new file mode 100644 index 000000000..daaa88c56 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/ClassifySessionReader.java @@ -0,0 +1,207 @@ +/* + * 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.v3po.vppclassifier; + +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 com.google.common.base.Optional; +import com.google.common.primitives.UnsignedInts; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.ListReaderCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.InterfaceDataTranslator; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.vpp.jvpp.core.dto.ClassifySessionDetails; +import io.fd.vpp.jvpp.core.dto.ClassifySessionDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.ClassifySessionDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.xml.bind.DatatypeConverter; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.HexString; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.OpaqueIndex; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.VppNode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.classify.table.base.attributes.ClassifySession; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.classify.table.base.attributes.ClassifySessionBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.classify.table.base.attributes.ClassifySessionKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.state.ClassifyTable; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.state.ClassifyTableBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.state.ClassifyTableKey; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Reader customizer responsible for classify session read.<br> to VPP.<br> Equivalent to invoking {@code vppctl show + * class table verbose} command. + */ +public class ClassifySessionReader extends FutureJVppCustomizer + implements ListReaderCustomizer<ClassifySession, ClassifySessionKey, ClassifySessionBuilder>, + InterfaceDataTranslator, VppNodeReader, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(ClassifySessionReader.class); + static final String CACHE_KEY = ClassifySessionReader.class.getName(); + + private final VppClassifierContextManager classifyTableContext; + + public ClassifySessionReader(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final VppClassifierContextManager classifyTableContext) { + super(futureJVppCore); + this.classifyTableContext = checkNotNull(classifyTableContext, "classifyTableContext should not be null"); + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> builder, + @Nonnull final List<ClassifySession> readData) { + ((ClassifyTableBuilder) builder).setClassifySession(readData); + } + + @Nonnull + @Override + public ClassifySessionBuilder getBuilder(@Nonnull final InstanceIdentifier<ClassifySession> id) { + return new ClassifySessionBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifySession> id, + @Nonnull final ClassifySessionBuilder builder, @Nonnull final ReadContext ctx) + throws ReadFailedException { + LOG.debug("Reading attributes for classify session: {}", id); + + final ClassifySessionKey key = id.firstKeyOf(ClassifySession.class); + checkArgument(key != null, "could not find ClassifySession key in {}", id); + + final ClassifySessionDetailsReplyDump classifySessionDump = dumpClassifySessions(id, ctx); + final byte[] match = DatatypeConverter.parseHexBinary(key.getMatch().getValue().replace(":", "")); + final Optional<ClassifySessionDetails> classifySession = + findClassifySessionDetailsByMatch(classifySessionDump, match); + + if (classifySession.isPresent()) { + final ClassifySessionDetails detail = classifySession.get(); + builder.setHitNext( + readVppNode(detail.tableId, detail.hitNextIndex, classifyTableContext, ctx.getMappingContext(), LOG) + .get()); + if (detail.opaqueIndex != ~0) { + // value is specified: + builder.setOpaqueIndex(readOpaqueIndex(detail.tableId, detail.opaqueIndex, ctx.getMappingContext())); + } + builder.setAdvance(detail.advance); + builder.setMatch(key.getMatch()); + + if (LOG.isTraceEnabled()) { + LOG.trace("Attributes for classify session {} successfully read: {}", id, builder.build()); + } + } + } + + private OpaqueIndex readOpaqueIndex(final int tableIndex, final int opaqueIndex, final MappingContext ctx) { + // We first try to map the value to a vpp node, if that fails, simply wrap the u32 value + // TODO: HONEYCOMB-118 the approach might fail if the opaqueIndex contains small value that collides + // with some of the adjacent nodes + + final Optional<VppNode> node = readVppNode(tableIndex, opaqueIndex, classifyTableContext, ctx, LOG); + if (node.isPresent()) { + return new OpaqueIndex(node.get()); + } else { + return new OpaqueIndex(UnsignedInts.toLong(opaqueIndex)); + } + } + + @Nullable + private ClassifySessionDetailsReplyDump dumpClassifySessions(@Nonnull final InstanceIdentifier<?> id, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + final ClassifyTableKey tableKey = id.firstKeyOf(ClassifyTable.class); + checkArgument(tableKey != null, "could not find ClassifyTable key in {}", id); + + final String cacheKey = CACHE_KEY + tableKey; + + ClassifySessionDetailsReplyDump classifySessionDump = + (ClassifySessionDetailsReplyDump) ctx.getModificationCache().get(cacheKey); + if (classifySessionDump != null) { + LOG.debug("Classify sessions is present in cache: {}", cacheKey); + return classifySessionDump; + } + + final String tableName = tableKey.getName(); + checkState(classifyTableContext.containsTable(tableName, ctx.getMappingContext()), + "Reading classify sessions for table {}, but table index could not be found in the classify table context", + tableName); + final int tableId = classifyTableContext.getTableIndex(tableName, ctx.getMappingContext()); + LOG.debug("Dumping classify sessions for classify table id={}", tableId); + + + final ClassifySessionDump dumpRequest = new ClassifySessionDump(); + dumpRequest.tableId = tableId; + final int timeOut = 30; // there can be many session with current ietf-acl implementation (could be probably + // removed after fixing HONEYCOMB-247) + classifySessionDump = + getReplyForRead(getFutureJVpp().classifySessionDump(dumpRequest).toCompletableFuture(), id, timeOut); + + if (classifySessionDump != null) { + // update the cache: + ctx.getModificationCache().put(cacheKey, classifySessionDump); + } + + return classifySessionDump; + } + + private static Optional<ClassifySessionDetails> findClassifySessionDetailsByMatch( + @Nullable final ClassifySessionDetailsReplyDump classifySessionDump, @Nonnull final byte[] match) { + if (classifySessionDump != null && classifySessionDump.classifySessionDetails != null) { + final List<ClassifySessionDetails> details = classifySessionDump.classifySessionDetails; + final List<ClassifySessionDetails> filteredSessions = details.stream() + .filter(singleDetail -> Arrays.equals(singleDetail.match, match)).collect(Collectors.toList()); + if (filteredSessions.isEmpty()) { + return Optional.absent(); + } else if (filteredSessions.size() == 1) { + return Optional.of(filteredSessions.get(0)); + } else { + throw new IllegalStateException(String.format( + "Found %d classify sessions witch given match. Single session expected.", + filteredSessions.size())); + } + } + return Optional.absent(); + } + + @Nonnull + @Override + public List<ClassifySessionKey> getAllIds(@Nonnull final InstanceIdentifier<ClassifySession> id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + LOG.debug("Reading list of keys for classify sessions: {}", id); + + final ClassifySessionDetailsReplyDump classifySessionDump = dumpClassifySessions(id, ctx); + if (classifySessionDump != null && classifySessionDump.classifySessionDetails != null) { + return classifySessionDump.classifySessionDetails.stream() + .map(detail -> new ClassifySessionKey(new HexString(printHexBinary(detail.match)))) + .collect(Collectors.toList()); + } else { + return Collections.emptyList(); + } + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/ClassifySessionWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/ClassifySessionWriter.java new file mode 100644 index 000000000..4e1a51265 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/ClassifySessionWriter.java @@ -0,0 +1,165 @@ +/* + * 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.v3po.vppclassifier; + +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 com.google.common.base.Optional; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.xml.bind.DatatypeConverter; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.OpaqueIndex; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.classify.table.base.attributes.ClassifySession; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.classify.table.base.attributes.ClassifySessionKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.ClassifyTable; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.ClassifyTableKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import io.fd.vpp.jvpp.VppBaseCallException; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSessionReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Writer customizer responsible for classify session create/delete.<br> Sends {@code classify_add_del_session} message + * to VPP.<br> Equivalent to invoking {@code vppctl classify table} command. + */ +public class ClassifySessionWriter extends VppNodeWriter + implements ListWriterCustomizer<ClassifySession, ClassifySessionKey>, ByteDataTranslator, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(ClassifySessionWriter.class); + private final VppClassifierContextManager classifyTableContext; + + public ClassifySessionWriter(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final VppClassifierContextManager classifyTableContext) { + super(futureJVppCore); + this.classifyTableContext = checkNotNull(classifyTableContext, "classifyTableContext should not be null"); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifySession> id, + @Nonnull final ClassifySession dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + LOG.debug("Creating classify session: iid={} dataAfter={}", id, dataAfter); + try { + classifyAddDelSession(true, id, dataAfter, writeContext); + LOG.debug("Successfully created classify session: iid={} dataAfter={}", id, dataAfter); + } catch (VppBaseCallException e) { + throw new WriteFailedException.CreateFailedException(id, dataAfter, e); + } + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifySession> id, + @Nonnull final ClassifySession dataBefore, + @Nonnull final ClassifySession dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + throw new UnsupportedOperationException("Classify session update is not supported"); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifySession> id, + @Nonnull final ClassifySession dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + LOG.debug("Removing classify session: iid={} dataBefore={}", id, dataBefore); + try { + classifyAddDelSession(false, id, dataBefore, writeContext); + LOG.debug("Successfully removed classify session: iid={} dataBefore={}", id, dataBefore); + } catch (VppBaseCallException e) { + throw new WriteFailedException.DeleteFailedException(id, e); + } + } + + private void classifyAddDelSession(final boolean isAdd, @Nonnull final InstanceIdentifier<ClassifySession> id, + @Nonnull final ClassifySession classifySession, + @Nonnull final WriteContext writeContext) + throws VppBaseCallException, WriteFailedException { + final ClassifyTableKey tableKey = id.firstKeyOf(ClassifyTable.class); + checkArgument(tableKey != null, "could not find classify table key in {}", id); + + final String tableName = tableKey.getName(); + checkState(classifyTableContext.containsTable(tableName, writeContext.getMappingContext()), + "Could not find classify table index for {} in the classify table context", tableName); + final int tableIndex = classifyTableContext.getTableIndex(tableName, writeContext.getMappingContext()); + + final ClassifyTable classifyTable = + getClassifyTable(writeContext, id.firstIdentifierOf(ClassifyTable.class), isAdd); + final int hitNextIndex = getNodeIndex(classifySession.getHitNext(), classifyTable, classifyTableContext, + writeContext.getMappingContext(), id); + final int opaqueIndex = + getOpaqueIndex(classifySession.getOpaqueIndex(), classifyTable, writeContext.getMappingContext(), id); + + final CompletionStage<ClassifyAddDelSessionReply> createClassifyTableReplyCompletionStage = getFutureJVpp() + .classifyAddDelSession( + getClassifyAddDelSessionRequest(isAdd, classifySession, tableIndex, hitNextIndex, opaqueIndex)); + + getReplyForWrite(createClassifyTableReplyCompletionStage.toCompletableFuture(), id); + } + + private ClassifyTable getClassifyTable(final WriteContext writeContext, + @Nonnull final InstanceIdentifier<ClassifyTable> id, + final boolean isAdd) { + final Optional<ClassifyTable> classifyTable; + if (isAdd) { + classifyTable = writeContext.readAfter(id); + } else { + classifyTable = writeContext.readBefore(id); + } + return classifyTable.get(); + } + + private ClassifyAddDelSession getClassifyAddDelSessionRequest(final boolean isAdd, + @Nonnull final ClassifySession classifySession, + final int tableIndex, + final int hitNextIndex, + final int opaqueIndex) { + ClassifyAddDelSession request = new ClassifyAddDelSession(); + request.isAdd = booleanToByte(isAdd); + request.tableIndex = tableIndex; + request.hitNextIndex = hitNextIndex; + request.opaqueIndex = opaqueIndex; + + // default 0: + request.advance = classifySession.getAdvance(); + + request.match = DatatypeConverter.parseHexBinary(classifySession.getMatch().getValue().replace(":", "")); + return request; + } + + private int getOpaqueIndex(@Nullable final OpaqueIndex opaqueIndex, final ClassifyTable classifyTable, + final MappingContext ctx, final InstanceIdentifier<ClassifySession> id) + throws VppBaseCallException, WriteFailedException { + if (opaqueIndex == null) { + return ~0; // value not specified + } + if (opaqueIndex.getUint32() != null) { + return opaqueIndex.getUint32().intValue(); + } else { + return getNodeIndex(opaqueIndex.getVppNode(), classifyTable, classifyTableContext, ctx, id); + } + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/ClassifyTableReader.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/ClassifyTableReader.java new file mode 100644 index 000000000..fba5fb694 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/ClassifyTableReader.java @@ -0,0 +1,171 @@ +/* + * 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.v3po.vppclassifier; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Optional; +import com.google.common.primitives.UnsignedInts; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingListReaderCustomizer; +import io.fd.hc2vpp.v3po.interfacesstate.InterfaceDataTranslator; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.MacTranslator; +import io.fd.vpp.jvpp.core.dto.ClassifyTableIds; +import io.fd.vpp.jvpp.core.dto.ClassifyTableIdsReply; +import io.fd.vpp.jvpp.core.dto.ClassifyTableInfo; +import io.fd.vpp.jvpp.core.dto.ClassifyTableInfoReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.HexString; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.VppClassifier; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.VppClassifierStateBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.VppNodeName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.state.ClassifyTable; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.state.ClassifyTableBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.state.ClassifyTableKey; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Reader customizer responsible for classify table read.<br> to VPP.<br> Equivalent to invoking {@code vppctl show + * class table} command. + */ +public class ClassifyTableReader extends FutureJVppCustomizer + implements InitializingListReaderCustomizer<ClassifyTable, ClassifyTableKey, ClassifyTableBuilder>, VppNodeReader, + MacTranslator, InterfaceDataTranslator, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(ClassifyTableReader.class); + private final VppClassifierContextManager classifyTableContext; + + public ClassifyTableReader(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final VppClassifierContextManager classifyTableContext) { + super(futureJVppCore); + this.classifyTableContext = checkNotNull(classifyTableContext, "classifyTableContext should not be null"); + } + + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> builder, + @Nonnull final List<ClassifyTable> readData) { + ((VppClassifierStateBuilder) builder).setClassifyTable(readData); + } + + @Nonnull + @Override + public ClassifyTableBuilder getBuilder(@Nonnull final InstanceIdentifier<ClassifyTable> id) { + return new ClassifyTableBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifyTable> id, + @Nonnull final ClassifyTableBuilder builder, @Nonnull final ReadContext ctx) + throws ReadFailedException { + LOG.debug("Reading attributes for classify table: {}", id); + + final ClassifyTableKey key = id.firstKeyOf(ClassifyTable.class); + checkArgument(key != null, "could not find ClassifyTable key in {}", id); + final ClassifyTableInfo request = new ClassifyTableInfo(); + + final String tableName = key.getName(); + if (!classifyTableContext.containsTable(tableName, ctx.getMappingContext())) { + LOG.debug("Could not find classify table {} in the naming context", tableName); + return; + } + request.tableId = classifyTableContext.getTableIndex(tableName, ctx.getMappingContext()); + + + final ClassifyTableInfoReply reply = + getReplyForRead(getFutureJVpp().classifyTableInfo(request).toCompletableFuture(), id); + + // mandatory values: + builder.setName(tableName); + builder.setKey(key); + builder.setNbuckets(UnsignedInts.toLong(reply.nbuckets)); + builder.setSkipNVectors(UnsignedInts.toLong(reply.skipNVectors)); + + // optional value read from context + final Optional<String> tableBaseNode = + classifyTableContext.getTableBaseNode(tableName, ctx.getMappingContext()); + if (tableBaseNode.isPresent()) { + builder.setClassifierNode(new VppNodeName(tableBaseNode.get())); + } + + builder.setMissNext( + readVppNode(reply.tableId, reply.missNextIndex, classifyTableContext, ctx.getMappingContext(), LOG) + .get()); + builder.setMask(new HexString(printHexBinary(reply.mask))); + builder.setActiveSessions(UnsignedInts.toLong(reply.activeSessions)); + + if (reply.nextTableIndex != ~0) { + // next table index is present: + builder.setNextTable(classifyTableContext.getTableName(reply.nextTableIndex, ctx.getMappingContext())); + } + + if (LOG.isTraceEnabled()) { + LOG.trace("Attributes for classify table {} successfully read: {}", id, builder.build()); + } + } + + @Nonnull + @Override + public List<ClassifyTableKey> getAllIds(@Nonnull final InstanceIdentifier<ClassifyTable> id, + @Nonnull final ReadContext context) throws ReadFailedException { + LOG.debug("Reading list of keys for classify tables: {}", id); + + final ClassifyTableIdsReply classifyTableIdsReply = + getReplyForRead(getFutureJVpp().classifyTableIds(new ClassifyTableIds()).toCompletableFuture(), + id); + if (classifyTableIdsReply.ids != null) { + return Arrays.stream(classifyTableIdsReply.ids).mapToObj(i -> { + final String tableName = classifyTableContext.getTableName(i, context.getMappingContext()); + LOG.trace("Classify table with name: {} and index: {} found in VPP", tableName, i); + return new ClassifyTableKey(tableName); + }).collect(Collectors.toList()); + } else { + return Collections.emptyList(); + } + } + + @Override + public Initialized<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.ClassifyTable> init( + @Nonnull final InstanceIdentifier<ClassifyTable> id, @Nonnull final ClassifyTable readValue, + @Nonnull final ReadContext ctx) { + return Initialized.create(getCfgId(id), + new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.ClassifyTableBuilder(readValue) + .setName(readValue.getName()) + .build()); + } + + static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.ClassifyTable> getCfgId( + final InstanceIdentifier<ClassifyTable> id) { + return InstanceIdentifier.create(VppClassifier.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.ClassifyTable.class, + new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.ClassifyTableKey(id.firstKeyOf(ClassifyTable.class).getName())); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/ClassifyTableWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/ClassifyTableWriter.java new file mode 100644 index 000000000..62b6a2142 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/ClassifyTableWriter.java @@ -0,0 +1,149 @@ +/* + * 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.v3po.vppclassifier; + +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 io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import javax.xml.bind.DatatypeConverter; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.ClassifyTable; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.ClassifyTableKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import io.fd.vpp.jvpp.VppBaseCallException; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Writer customizer responsible for classify table create/delete. <br> Sends {@code classify_add_del_table} message to + * VPP.<br> Equivalent to invoking {@code vppctl classify table} command. + */ +public class ClassifyTableWriter extends VppNodeWriter + implements ListWriterCustomizer<ClassifyTable, ClassifyTableKey>, ByteDataTranslator, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(ClassifyTableWriter.class); + private final VppClassifierContextManager classifyTableContext; + + public ClassifyTableWriter(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final VppClassifierContextManager classifyTableContext) { + super(futureJVppCore); + this.classifyTableContext = checkNotNull(classifyTableContext, "classifyTableContext should not be null"); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifyTable> id, + @Nonnull final ClassifyTable dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("Creating classify table: iid={} dataAfter={}", id, dataAfter); + try { + final int newTableIndex = + classifyAddDelTable(true, id, dataAfter, ~0 /* value not present */, + writeContext.getMappingContext()); + + // Add classify table name <-> vpp index mapping to the naming context: + classifyTableContext.addTable(newTableIndex, dataAfter.getName(), dataAfter.getClassifierNode(), + writeContext.getMappingContext()); + LOG.debug("Successfully created classify table(id={]): iid={} dataAfter={}", newTableIndex, id, dataAfter); + } catch (VppBaseCallException e) { + throw new WriteFailedException.CreateFailedException(id, dataAfter, e); + } + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifyTable> id, + @Nonnull final ClassifyTable dataBefore, @Nonnull final ClassifyTable dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + throw new UnsupportedOperationException("Classify table update is not supported"); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifyTable> id, + @Nonnull final ClassifyTable dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + LOG.debug("Removing classify table: iid={} dataBefore={}", id, dataBefore); + final String tableName = dataBefore.getName(); + checkState(classifyTableContext.containsTable(tableName, writeContext.getMappingContext()), + "Removing classify table {}, but index could not be found in the classify table context", tableName); + + final int tableIndex = classifyTableContext.getTableIndex(tableName, writeContext.getMappingContext()); + try { + classifyAddDelTable(false, id, dataBefore, tableIndex, writeContext.getMappingContext()); + + // Remove deleted interface from interface context: + classifyTableContext.removeTable(dataBefore.getName(), writeContext.getMappingContext()); + LOG.debug("Successfully removed classify table(id={]): iid={} dataAfter={}", tableIndex, id, dataBefore); + } catch (VppBaseCallException e) { + throw new WriteFailedException.DeleteFailedException(id, e); + } + } + + private int classifyAddDelTable(final boolean isAdd, @Nonnull final InstanceIdentifier<ClassifyTable> id, + @Nonnull final ClassifyTable table, final int tableId, final MappingContext ctx) + throws VppBaseCallException, WriteFailedException { + + final int missNextIndex = + getNodeIndex(table.getMissNext(), table, classifyTableContext, ctx, id); + + final CompletionStage<ClassifyAddDelTableReply> createClassifyTableReplyCompletionStage = + getFutureJVpp() + .classifyAddDelTable(getClassifyAddDelTableRequest(isAdd, tableId, table, missNextIndex, ctx)); + + final ClassifyAddDelTableReply reply = + getReplyForWrite(createClassifyTableReplyCompletionStage.toCompletableFuture(), id); + return reply.newTableIndex; + } + + private ClassifyAddDelTable getClassifyAddDelTableRequest(final boolean isAdd, final int tableIndex, + @Nonnull final ClassifyTable table, + final int missNextIndex, + @Nonnull final MappingContext ctx) { + final ClassifyAddDelTable request = new ClassifyAddDelTable(); + request.isAdd = booleanToByte(isAdd); + request.tableIndex = tableIndex; + + // mandatory, all u32 values are permitted: + request.nbuckets = table.getNbuckets().intValue(); + request.memorySize = table.getMemorySize().intValue(); + request.skipNVectors = table.getSkipNVectors().intValue(); + + // mandatory + request.missNextIndex = missNextIndex; + + final String nextTable = table.getNextTable(); + if (isAdd && nextTable != null) { + request.nextTableIndex = classifyTableContext.getTableIndex(nextTable, ctx); + } else { + request.nextTableIndex = ~0; // value not specified + } + request.mask = DatatypeConverter.parseHexBinary(table.getMask().getValue().replace(":", "")); + checkArgument(request.mask.length % 16 == 0, "Number of mask bytes must be multiple of 16."); + request.matchNVectors = request.mask.length / 16; + + return request; + } +}
\ No newline at end of file diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/VppClassifierContextManager.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/VppClassifierContextManager.java new file mode 100644 index 000000000..c06ef2700 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/VppClassifierContextManager.java @@ -0,0 +1,106 @@ +/* + * 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.v3po.vppclassifier; + +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.MappingContext; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.VppNodeName; + +/** + * Manages metadata for vpp-classifier + */ +public interface VppClassifierContextManager { + + /** + * Creates metadata for classify table. + * + * @param id classify table index + * @param name classify table name + * @param classifierNode name of VPP node the table is defined for + * @param ctx mapping context providing context data for current transaction + */ + void addTable(final int id, @Nonnull final String name, @Nullable final VppNodeName classifierNode, + @Nonnull final MappingContext ctx); + + /** + * Check whether metadata for given classify table metadata is present. + * + * @param name classify table name + * @param ctx mapping context providing context data for current transaction + * @return true if present, false otherwise + */ + boolean containsTable(@Nonnull String name, @Nonnull final MappingContext ctx); + + /** + * Returns classify table index associated with the given name. + * + * @param name classify table name + * @param ctx mapping context providing context data for current transaction + * @return integer index value matching supplied classify table name + * @throws IllegalArgumentException if classify table was not found + */ + int getTableIndex(@Nonnull final String name, @Nonnull final MappingContext ctx); + + /** + * Retrieves classify table name for given id. If not present, artificial name will be generated. + * + * @param id classify table index + * @param ctx mapping context providing context data for current transaction + * @return classify table name matching supplied index + */ + String getTableName(final int id, @Nonnull final MappingContext ctx); + + /** + * Returns name of the base vpp node associated with the classify table. + * + * @param name classify table name + * @param ctx mapping context providing context data for current transaction + * @return name of VPP node the table is defined for + */ + Optional<String> getTableBaseNode(final String name, @Nonnull final MappingContext ctx); + + /** + * Removes classify table metadata from current context. + * + * @param name classify table name + * @param ctx mapping context providing context data for current transaction + */ + void removeTable(@Nonnull final String name, @Nonnull final MappingContext ctx); + + /** + * Adds relative node index to node name mapping for given classify table. + * + * @param tableName classify table name + * @param nodeIndex index of a vpp node, relative to table's base node + * @param nodeName name of a vpp node + * @param ctx mapping context providing context data for current transaction + */ + void addNodeName(@Nonnull String tableName, final int nodeIndex, @Nonnull final String nodeName, + @Nonnull final MappingContext ctx); + + /** + * Retrieves node name associated with the given classify table and node index. + * + * @param tableIndex classify table index + * @param nodeIndex relative index of a vpp node + * @param ctx mapping context providing context data for current transaction + * @return name of vpp node + */ + Optional<String> getNodeName(final int tableIndex, final int nodeIndex, @Nonnull final MappingContext ctx); +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/VppClassifierContextManagerImpl.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/VppClassifierContextManagerImpl.java new file mode 100644 index 000000000..2255c78a7 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/VppClassifierContextManagerImpl.java @@ -0,0 +1,181 @@ +/* + * 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.v3po.vppclassifier; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.inject.Inject; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.util.read.BindingBrokerReader; +import java.util.List; +import java.util.stream.Collector; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.inject.Named; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.VppNodeName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev160909.VppClassifierContext; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev160909.VppClassifierContextBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev160909.vpp.classifier.context.ClassifyTableContext; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev160909.vpp.classifier.context.ClassifyTableContextBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev160909.vpp.classifier.context.ClassifyTableContextKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev160909.vpp.classifier.context.classify.table.context.NodeContext; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev160909.vpp.classifier.context.classify.table.context.NodeContextBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev160909.vpp.classifier.context.classify.table.context.NodeContextKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; + +/** + * Facade on top of {@link MappingContext} that manages {@link ClassifyTableContext}. + */ +public final class VppClassifierContextManagerImpl implements VppClassifierContextManager { + private static final Collector<ClassifyTableContext, ?, ClassifyTableContext> SINGLE_ITEM_COLLECTOR = + RWUtils.singleItemCollector(); + + @VisibleForTesting + static final InstanceIdentifier<VppClassifierContext> + VPP_CLASSIFIER_CONTEXT_IID = KeyedInstanceIdentifier.create(VppClassifierContext.class); + private final String artificialNamePrefix; + + /** + * Creates new VppClassifierContextManagerImpl. + * + * @param artificialNamePrefix artificial name to be used to generate names for classify tables without existing + * metadata + */ + public VppClassifierContextManagerImpl(@Nonnull final String artificialNamePrefix) { + this.artificialNamePrefix = + Preconditions.checkNotNull(artificialNamePrefix, "artificialNamePrefix should not be null"); + } + + private KeyedInstanceIdentifier<ClassifyTableContext, ClassifyTableContextKey> getMappingIid(final String name) { + return VPP_CLASSIFIER_CONTEXT_IID.child(ClassifyTableContext.class, new ClassifyTableContextKey(name)); + } + + @Override + public void addTable(final int id, @Nonnull final String name, @Nullable final VppNodeName classifierNode, + @Nonnull final MappingContext ctx) { + final KeyedInstanceIdentifier<ClassifyTableContext, ClassifyTableContextKey> mappingIid = getMappingIid(name); + final ClassifyTableContextBuilder tableCtx = new ClassifyTableContextBuilder().setIndex(id).setName(name); + if (classifierNode != null) { + tableCtx.setClassifierNodeName(classifierNode.getValue()); + } + ctx.put(mappingIid, tableCtx.build()); + } + + @Override + public boolean containsTable(@Nonnull final String name, @Nonnull final MappingContext ctx) { + final Optional<ClassifyTableContext> read = ctx.read(getMappingIid(name)); + return read.isPresent(); + } + + @Override + public int getTableIndex(@Nonnull final String name, @Nonnull final MappingContext ctx) { + final Optional<ClassifyTableContext> read = ctx.read(getMappingIid(name)); + checkArgument(read.isPresent(), "No mapping stored for name: %s", name); + return read.get().getIndex(); + } + + @Override + public String getTableName(final int id, @Nonnull final MappingContext ctx) { + if (!containsName(id, ctx)) { + final String artificialName = getArtificialName(id); + addTable(id, artificialName, null, ctx); + } + + final Optional<VppClassifierContext> read = ctx.read(VPP_CLASSIFIER_CONTEXT_IID); + checkState(read.isPresent(), "VppClassifierContext for index: %s is not present. But should be", id); + + return read.get().getClassifyTableContext().stream() + .filter(t -> t.getIndex().equals(id)) + .collect(SINGLE_ITEM_COLLECTOR).getName(); + } + + private boolean containsName(final int index, @Nonnull final MappingContext mappingContext) { + final Optional<VppClassifierContext> read = mappingContext.read(VPP_CLASSIFIER_CONTEXT_IID); + return read.isPresent() + ? read.get().getClassifyTableContext().stream().anyMatch(t -> t.getIndex().equals(index)) + : false; + } + + @Override + public Optional<String> getTableBaseNode(@Nonnull final String name, @Nonnull final MappingContext ctx) { + final Optional<ClassifyTableContext> read = ctx.read(getMappingIid(name)); + if (read.isPresent()) { + return Optional.fromNullable(read.get().getClassifierNodeName()); + } + return Optional.absent(); + } + + @Override + public void removeTable(@Nonnull final String name, @Nonnull final MappingContext ctx) { + ctx.delete(getMappingIid(name)); + } + + @Override + public void addNodeName(@Nonnull final String tableName, final int nodeIndex, + @Nonnull final String nodeName, + @Nonnull final MappingContext ctx) { + final KeyedInstanceIdentifier<NodeContext, NodeContextKey> iid = + getMappingIid(tableName).child(NodeContext.class, new NodeContextKey(nodeName)); + ctx.put(iid, new NodeContextBuilder().setName(nodeName).setIndex(nodeIndex).build()); + } + + @Override + public Optional<String> getNodeName(final int tableIndex, final int nodeIndex, @Nonnull final MappingContext ctx) { + if (!containsName(tableIndex, ctx)) { + return Optional.absent(); + } + final String tableName = getTableName(tableIndex, ctx); + final Optional<ClassifyTableContext> tableCtx = ctx.read(getMappingIid(tableName)); + final List<NodeContext> nodeContext = tableCtx.get().getNodeContext(); + if (nodeContext == null) { + return Optional.absent(); + } + return Optional.fromNullable(nodeContext.stream() + .filter(n -> n.getIndex().equals(nodeIndex)) + .findFirst() + .map(nodes -> nodes.getName()) + .orElse(null)); + } + + private String getArtificialName(final int index) { + return artificialNamePrefix + index; + } + + public static final class ContextsReaderFactory implements ReaderFactory { + + @Inject + @Named("honeycomb-context") + private DataBroker contextBindingBrokerDependency; + + @Override + public void init(final ModifiableReaderRegistryBuilder registry) { + registry.add(new BindingBrokerReader<>(VPP_CLASSIFIER_CONTEXT_IID, + contextBindingBrokerDependency, + LogicalDatastoreType.OPERATIONAL, VppClassifierContextBuilder.class)); + } + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/VppNodeReader.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/VppNodeReader.java new file mode 100644 index 000000000..c25a4572c --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/VppNodeReader.java @@ -0,0 +1,45 @@ +/* + * 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.v3po.vppclassifier; + +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.MappingContext; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.PacketHandlingAction; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.VppNode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.VppNodeName; +import org.slf4j.Logger; + +interface VppNodeReader { + + /** + * Converts vpp node index to YANG representation of vpp node. + * + * @param nodeIndex index of vpp node treated as signed integer. + * @return vpp node representation + */ + default Optional<VppNode> readVppNode(final int tableIndex, final int nodeIndex, + @Nonnull final VppClassifierContextManager vppClassifierContextManager, + @Nonnull final MappingContext ctx, @Nonnull final Logger log) { + final PacketHandlingAction action = PacketHandlingAction.forValue(nodeIndex); + if (action == null) { + return vppClassifierContextManager.getNodeName(tableIndex, nodeIndex, ctx) + .transform(nodeName -> new VppNode(new VppNodeName(nodeName))); + } + return Optional.of(new VppNode(action)); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/VppNodeWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/VppNodeWriter.java new file mode 100644 index 000000000..dd0b2d5ec --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppclassifier/VppNodeWriter.java @@ -0,0 +1,79 @@ +/* + * 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.v3po.vppclassifier; + +import static com.google.common.base.Preconditions.checkArgument; + +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.VppBaseCallException; +import io.fd.vpp.jvpp.core.dto.GetNextIndex; +import io.fd.vpp.jvpp.core.dto.GetNextIndexReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.VppNode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.ClassifyTable; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +abstract class VppNodeWriter extends FutureJVppCustomizer implements JvppReplyConsumer { + + protected VppNodeWriter(@Nonnull final FutureJVppCore futureJvpp) { + super(futureJvpp); + } + + protected int getNodeIndex(@Nonnull final VppNode node, @Nonnull final ClassifyTable classifyTable, + @Nonnull final VppClassifierContextManager vppClassifierContextManager, + @Nonnull final MappingContext ctx, @Nonnull final InstanceIdentifier<?> id) + throws VppBaseCallException, WriteFailedException { + if (node.getPacketHandlingAction() != null) { + return node.getPacketHandlingAction().getIntValue(); + } else { + return nodeNameToIndex(classifyTable, node.getVppNodeName().getValue(), vppClassifierContextManager, ctx, + id); + } + } + + private int nodeNameToIndex(@Nonnull final ClassifyTable classifyTable, @Nonnull final String nextNodeName, + @Nonnull final VppClassifierContextManager vppClassifierContextManager, + @Nonnull final MappingContext ctx, @Nonnull final InstanceIdentifier<?> id) + throws WriteFailedException { + checkArgument(classifyTable != null && classifyTable.getClassifierNode() != null, + "to use relative node names, table classifier node needs to be provided"); + final GetNextIndex request = new GetNextIndex(); + request.nodeName = classifyTable.getClassifierNode().getValue().getBytes(); + request.nextName = nextNodeName.getBytes(); + final CompletionStage<GetNextIndexReply> getNextIndexCompletionStage = + getFutureJVpp().getNextIndex(request); + + final GetNextIndexReply reply; + try { + reply = getReplyForRead(getNextIndexCompletionStage.toCompletableFuture(), id); + + // vpp does not provide relative node index to node name conversion (https://jira.fd.io/browse/VPP-219) + // as a workaround we need to add mapping to vpp-classfier-context + vppClassifierContextManager.addNodeName(classifyTable.getName(), reply.nextIndex, nextNodeName, ctx); + } catch (ReadFailedException e) { + throw new WriteFailedException(id, String.format("Failed to get node index for %s relative to %s", + nextNodeName, classifyTable.getClassifierNode()), e); + } + return reply.nextIndex; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppstate/BridgeDomainCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppstate/BridgeDomainCustomizer.java new file mode 100644 index 000000000..e6f3bbb49 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppstate/BridgeDomainCustomizer.java @@ -0,0 +1,181 @@ +/* + * 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.v3po.vppstate; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingListReaderCustomizer; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.dto.BridgeDomainDetails; +import io.fd.vpp.jvpp.core.dto.BridgeDomainDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.BridgeDomainDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.Vpp; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.BridgeDomains; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.state.BridgeDomainsBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.state.bridge.domains.BridgeDomain; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.state.bridge.domains.BridgeDomainBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.state.bridge.domains.BridgeDomainKey; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class BridgeDomainCustomizer extends FutureJVppCustomizer + implements InitializingListReaderCustomizer<BridgeDomain, BridgeDomainKey, BridgeDomainBuilder>, ByteDataTranslator { + + private static final Logger LOG = LoggerFactory.getLogger(BridgeDomainCustomizer.class); + private final NamingContext bdContext; + + public BridgeDomainCustomizer(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final NamingContext bdContext) { + super(futureJVppCore); + this.bdContext = Preconditions.checkNotNull(bdContext, "bdContext should not be null"); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<BridgeDomain> id, + @Nonnull final BridgeDomainBuilder builder, @Nonnull final ReadContext context) + throws ReadFailedException { + LOG.debug("vppstate.BridgeDomainCustomizer.readCurrentAttributes: id={}, builderbuilder={}, context={}", + id, builder, context); + + final BridgeDomainKey key = id.firstKeyOf(id.getTargetType()); + LOG.debug("vppstate.BridgeDomainCustomizer.readCurrentAttributes: key={}", key); + + final int bdId = bdContext.getIndex(key.getName(), context.getMappingContext()); + LOG.debug("vppstate.BridgeDomainCustomizer.readCurrentAttributes: bdId={}", bdId); + + BridgeDomainDetailsReplyDump reply; + BridgeDomainDetails bridgeDomainDetails; + final BridgeDomainDump request = new BridgeDomainDump(); + request.bdId = bdContext.getIndex(key.getName(), context.getMappingContext()); + try { + reply = getFutureJVpp().bridgeDomainDump(request).toCompletableFuture().get(); + bridgeDomainDetails = Iterables.getOnlyElement(reply.bridgeDomainDetails); + } catch (Exception e) { + LOG.debug("Unable to read bridge domain: {}", key.getName(), e); + return; + } + + logBridgeDomainDetails(bridgeDomainDetails); + + builder.setName(key.getName()); + builder.setArpTermination(byteToBoolean(bridgeDomainDetails.arpTerm)); + builder.setFlood(byteToBoolean(bridgeDomainDetails.flood)); + builder.setForward(byteToBoolean(bridgeDomainDetails.forward)); + builder.setLearn(byteToBoolean(bridgeDomainDetails.learn)); + builder.setUnknownUnicastFlood(byteToBoolean(bridgeDomainDetails.uuFlood)); + } + + private void logBridgeDomainDetails(final BridgeDomainDetails bridgeDomainDetails) { + LOG.debug("bridgeDomainDetails={}", bridgeDomainDetails); + if (bridgeDomainDetails != null) { + LOG.debug("bridgeDomainDetails.arpTerm={}", bridgeDomainDetails.arpTerm); + LOG.debug("bridgeDomainDetails.bdId={}", bridgeDomainDetails.bdId); + LOG.debug("bridgeDomainDetails.bviSwIfIndex={}", bridgeDomainDetails.bviSwIfIndex); + LOG.debug("bridgeDomainDetails.flood={}", bridgeDomainDetails.flood); + LOG.debug("bridgeDomainDetails.forward={}", bridgeDomainDetails.forward); + LOG.debug("bridgeDomainDetails.learn={}", bridgeDomainDetails.learn); + LOG.debug("bridgeDomainDetails.nSwIfs={}", bridgeDomainDetails.nSwIfs); + LOG.debug("bridgeDomainDetails.uuFlood={}", bridgeDomainDetails.uuFlood); + } + } + + @Nonnull + @Override + public BridgeDomainBuilder getBuilder(@Nonnull final InstanceIdentifier<BridgeDomain> id) { + return new BridgeDomainBuilder(); + } + + @Nonnull + @Override + public List<BridgeDomainKey> getAllIds(@Nonnull final InstanceIdentifier<BridgeDomain> id, + @Nonnull final ReadContext context) throws ReadFailedException { + final BridgeDomainDump request = new BridgeDomainDump(); + request.bdId = -1; // dump call + + BridgeDomainDetailsReplyDump reply; + try { + reply = getFutureJVpp().bridgeDomainDump(request).toCompletableFuture().get(); + } catch (Exception e) { + throw new ReadFailedException(id, e); + } + + if (reply == null || reply.bridgeDomainDetails == null) { + return Collections.emptyList(); + } + + final int bIdsLength = reply.bridgeDomainDetails.size(); + LOG.debug("vppstate.BridgeDomainCustomizer.getAllIds: bIds.length={}", bIdsLength); + if (bIdsLength == 0) { + // No bridge domains + return Collections.emptyList(); + } + + final List<BridgeDomainKey> allIds = new ArrayList<>(bIdsLength); + for (BridgeDomainDetails detail : reply.bridgeDomainDetails) { + logBridgeDomainDetails(detail); + + final String bName = bdContext.getName(detail.bdId, context.getMappingContext()); + LOG.debug("vppstate.BridgeDomainCustomizer.getAllIds: bName={}", bName); + allIds.add(new BridgeDomainKey(bName)); + } + + return allIds; + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> builder, + @Nonnull final List<BridgeDomain> readData) { + ((BridgeDomainsBuilder) builder).setBridgeDomain(readData); + } + + @Override + public Initialized<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.bridge.domains.BridgeDomain> init( + @Nonnull final InstanceIdentifier<BridgeDomain> id, + @Nonnull final BridgeDomain readValue, + @Nonnull final ReadContext ctx) { + return Initialized.create(getCfgId(id), + new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.bridge.domains.BridgeDomainBuilder() + .setName(readValue.getName()) + .setLearn(readValue.isLearn()) + .setUnknownUnicastFlood(readValue.isUnknownUnicastFlood()) + .setArpTermination(readValue.isArpTermination()) + .setFlood(readValue.isFlood()) + .setForward(readValue.isForward()) + .build()); + } + + static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.bridge.domains.BridgeDomain> getCfgId( + final InstanceIdentifier<BridgeDomain> id) { + return InstanceIdentifier.create(Vpp.class).child(BridgeDomains.class).child( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.bridge.domains.BridgeDomain.class, + new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.bridge.domains.BridgeDomainKey( + id.firstKeyOf(BridgeDomain.class).getName())); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppstate/L2FibEntryCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppstate/L2FibEntryCustomizer.java new file mode 100644 index 000000000..2605293c5 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppstate/L2FibEntryCustomizer.java @@ -0,0 +1,165 @@ +/* + * 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.v3po.vppstate; + +import com.google.common.base.Preconditions; +import com.google.common.primitives.Longs; +import io.fd.hc2vpp.v3po.interfacesstate.InterfaceDataTranslator; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.Initialized; +import io.fd.honeycomb.translate.spi.read.InitializingListReaderCustomizer; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.dto.L2FibTableDump; +import io.fd.vpp.jvpp.core.dto.L2FibTableEntry; +import io.fd.vpp.jvpp.core.dto.L2FibTableEntryReplyDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.L2FibFilter; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.L2FibForward; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.fib.attributes.L2FibTable; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.fib.attributes.L2FibTableBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.fib.attributes.l2.fib.table.L2FibEntry; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.fib.attributes.l2.fib.table.L2FibEntryBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.fib.attributes.l2.fib.table.L2FibEntryKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.state.bridge.domains.BridgeDomain; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.state.bridge.domains.BridgeDomainKey; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class L2FibEntryCustomizer extends FutureJVppCustomizer + implements InitializingListReaderCustomizer<L2FibEntry, L2FibEntryKey, L2FibEntryBuilder>, ByteDataTranslator, + InterfaceDataTranslator { + + private static final Logger LOG = LoggerFactory.getLogger(L2FibEntryCustomizer.class); + + private static final Collector<L2FibTableEntry, ?, L2FibTableEntry> SINGLE_ITEM_COLLECTOR = + RWUtils.singleItemCollector(); + + private final NamingContext bdContext; + private final NamingContext interfaceContext; + + public L2FibEntryCustomizer(@Nonnull final FutureJVppCore futureJVppCore, @Nonnull final NamingContext bdContext, + @Nonnull final NamingContext interfaceContext) { + super(futureJVppCore); + this.bdContext = Preconditions.checkNotNull(bdContext, "bdContext should not be null"); + this.interfaceContext = Preconditions.checkNotNull(interfaceContext, "interfaceContext should not be null"); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<L2FibEntry> id, + @Nonnull final L2FibEntryBuilder builder, @Nonnull final ReadContext ctx) + throws ReadFailedException { + + final L2FibEntryKey key = id.firstKeyOf(id.getTargetType()); + final BridgeDomainKey bridgeDomainKey = id.firstKeyOf(BridgeDomain.class); + final int bdId = bdContext.getIndex(bridgeDomainKey.getName(), ctx.getMappingContext()); + LOG.debug("Reading L2 FIB entry: key={}. bridgeDomainKey={}, bdId={}", key, bridgeDomainKey, bdId); + + try { + // TODO HONEYCOMB-186 use cached l2FibTable + final L2FibTableEntry entry = dumpL2Fibs(id, bdId).stream().filter(e -> key.getPhysAddress() + .equals(new PhysAddress(vppPhysAddrToYang(Longs.toByteArray(e.mac), 2)))) + .collect(SINGLE_ITEM_COLLECTOR); + + builder.setAction(byteToBoolean(entry.filterMac) + ? L2FibFilter.class + : L2FibForward.class); + builder.setBridgedVirtualInterface(byteToBoolean(entry.bviMac)); + + if (entry.swIfIndex != -1) { + builder.setOutgoingInterface(interfaceContext.getName(entry.swIfIndex, ctx.getMappingContext())); + } + builder.setStaticConfig(byteToBoolean(entry.staticMac)); + builder.setPhysAddress(key.getPhysAddress()); + builder.setKey(key); + } catch (Exception e) { + throw new ReadFailedException(id, e); + } + } + + @Nonnull + private List<L2FibTableEntry> dumpL2Fibs(final InstanceIdentifier<L2FibEntry> id, final int bdId) + throws ReadFailedException { + final L2FibTableDump l2FibRequest = new L2FibTableDump(); + l2FibRequest.bdId = bdId; + + final CompletableFuture<L2FibTableEntryReplyDump> l2FibTableDumpCompletableFuture = + getFutureJVpp().l2FibTableDump(l2FibRequest).toCompletableFuture(); + + final L2FibTableEntryReplyDump dump = getReplyForRead(l2FibTableDumpCompletableFuture, id); + + if (null == dump || null == dump.l2FibTableEntry) { + return Collections.emptyList(); + } else { + return dump.l2FibTableEntry; + } + } + + @Nonnull + @Override + public List<L2FibEntryKey> getAllIds(@Nonnull final InstanceIdentifier<L2FibEntry> id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + final BridgeDomainKey bridgeDomainKey = id.firstKeyOf(BridgeDomain.class); + final int bdId = bdContext.getIndex(bridgeDomainKey.getName(), ctx.getMappingContext()); + + LOG.debug("Reading L2 FIB for bridge domain {} (bdId={})", bridgeDomainKey, bdId); + return dumpL2Fibs(id, bdId).stream() + .map(entry -> new L2FibEntryKey( + new PhysAddress(vppPhysAddrToYang(Longs.toByteArray(entry.mac), 2)))) + .collect(Collectors.toList()); + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<L2FibEntry> readData) { + ((L2FibTableBuilder) builder).setL2FibEntry(readData); + } + + @Nonnull + @Override + public L2FibEntryBuilder getBuilder(@Nonnull final InstanceIdentifier<L2FibEntry> id) { + return new L2FibEntryBuilder(); + } + + @Override + public Initialized<L2FibEntry> init( + @Nonnull final InstanceIdentifier<L2FibEntry> id, + @Nonnull final L2FibEntry readValue, + @Nonnull final ReadContext ctx) { + return Initialized.create(getCfgId(id), + // Convert operational object to config. VPP does not support setting BVI (see v3po.yang) + new L2FibEntryBuilder(readValue).setBridgedVirtualInterface(null).build()); + } + + static InstanceIdentifier<L2FibEntry> getCfgId( + final @Nonnull InstanceIdentifier<L2FibEntry> id) { + return BridgeDomainCustomizer.getCfgId(RWUtils.cutId(id, BridgeDomain.class)).child( + L2FibTable.class).child(L2FibEntry.class, new L2FibEntryKey(id.firstKeyOf(L2FibEntry.class))); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppstate/VersionCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppstate/VersionCustomizer.java new file mode 100644 index 000000000..3f69c98df --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/vppstate/VersionCustomizer.java @@ -0,0 +1,82 @@ +/* + * 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.v3po.vppstate; + +import com.google.common.primitives.UnsignedInts; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.ReaderCustomizer; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.vpp.jvpp.core.dto.ShowVersion; +import io.fd.vpp.jvpp.core.dto.ShowVersionReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import io.fd.vpp.jvpp.dto.ControlPing; +import io.fd.vpp.jvpp.dto.ControlPingReply; +import io.fd.vpp.jvpp.dto.JVppReply; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppStateBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.state.Version; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.state.VersionBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public final class VersionCustomizer + extends FutureJVppCustomizer + implements ReaderCustomizer<Version, VersionBuilder>, ByteDataTranslator, JvppReplyConsumer { + + public VersionCustomizer(@Nonnull final FutureJVppCore futureJVppCore) { + super(futureJVppCore); + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final Version readValue) { + ((VppStateBuilder) parentBuilder).setVersion(readValue); + } + + @Nonnull + @Override + public VersionBuilder getBuilder(@Nonnull InstanceIdentifier<Version> id) { + return new VersionBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Version> id, @Nonnull final VersionBuilder builder, + @Nonnull final ReadContext context) throws ReadFailedException { + + // Execute with timeout + final CompletionStage<ShowVersionReply> showVersionFuture = getFutureJVpp().showVersion(new ShowVersion()); + final ShowVersionReply reply = getReplyForRead(showVersionFuture.toCompletableFuture(), id); + + builder.setBranch(toString(reply.version)); + builder.setName(toString(reply.program)); + builder.setBuildDate(toString(reply.buildDate)); + builder.setBuildDirectory(toString(reply.buildDirectory)); + builder.setPid(getPid(id)); + } + + private Long getPid(@Nonnull final InstanceIdentifier<Version> id) throws ReadFailedException { + final CompletionStage<JVppReply<ControlPing>> request = getFutureJVpp().send(new ControlPing()); + final ControlPingReply reply = (ControlPingReply)getReplyForRead(request.toCompletableFuture(), id); + return UnsignedInts.toLong(reply.vpePid); + } + + +} |