introducing loopback port for VPP 07/48307/6
authorMichal Cmarada <mcmarada@cisco.com>
Wed, 23 Nov 2016 15:34:33 +0000 (16:34 +0100)
committerMichal Cmarada <mcmarada@cisco.com>
Wed, 23 Nov 2016 15:34:33 +0000 (16:34 +0100)
adding support for loopback interface in vpp-renderer
adding support for loopback VPP router interface in neutron-vpp-mapper

Change-Id: Ibd91f8b109468abc16018f3a09d6bb76cd7ce018
Signed-off-by: Michal Cmarada <mcmarada@cisco.com>
neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronPortAware.java
neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/PortUtils.java
neutron-vpp-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/vpp/mapper/processors/PortHandler.java
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/commands/LoopbackCommand.java [new file with mode: 0644]
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/iface/InterfaceManager.java
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/policy/ForwardingManager.java
renderers/vpp/src/main/yang/vpp-renderer.yang
renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/commands/LoopbackCommandTest.java [new file with mode: 0644]
renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/policy/ForwardingManagerTest.java

index 4e8886357b217ab07c7c34dd47531dd374510898..454b4dea38eda59bd72c631c911b884c769b5325 100644 (file)
@@ -121,7 +121,7 @@ public class NeutronPortAware implements NeutronAware<Port> {
             UniqueId portId = new UniqueId(port.getUuid().getValue());
             addBaseEndpointMappings(addrEpKey, portId, rwTx);
 
-            // Add Qrouter port as Endpoint
+            // Add Qrouter and VPProuter port as Endpoint
             if (port.getAugmentation(PortBindingExtension.class) != null &&
                 PortUtils.DEVICE_VIF_TYPE.equals(port.getAugmentation(PortBindingExtension.class).getVifType())) {
                 LOG.trace("Port is QRouter port: {}", port.getUuid().getValue());
@@ -291,14 +291,14 @@ public class NeutronPortAware implements NeutronAware<Port> {
         Set<Port> portsInSameSubnet = PortUtils.findPortsBySubnet(subnetUuid, neutron.getPorts());
         for (Port portInSameSubnet : portsInSameSubnet) {
             if (PortUtils.isNormalPort(portInSameSubnet) || PortUtils.isDhcpPort(portInSameSubnet)
-                || PortUtils.isQrouterPort(portInSameSubnet)) {
+                || PortUtils.isQrouterOrVppRouterPort(portInSameSubnet)) {
                 // endpoints are created only from neutron normal port or DHCP port
                 Optional<FixedIps> firstFixedIps = PortUtils.resolveFirstFixedIps(portInSameSubnet);
                 if (firstFixedIps.isPresent()) {
                     // endpoint has only one network containment therefore only first IP is used
                     FixedIps ipWithSubnet = firstFixedIps.get();
                     List<EndpointGroupId> endpointGroupIds = new ArrayList<>();
-                    if (PortUtils.isDhcpPort(portInSameSubnet) || PortUtils.isQrouterPort(portInSameSubnet)) {
+                    if (PortUtils.isDhcpPort(portInSameSubnet) || PortUtils.isQrouterOrVppRouterPort(portInSameSubnet)) {
                         endpointGroupIds.add(NetworkService.EPG_ID);
                     } else if (PortUtils.isNormalPort(portInSameSubnet)) {
                         endpointGroupIds.add(NetworkClient.EPG_ID);
index 354d7b36eba08a74caaf66db785e6509a0f1a590..2e225876771f98de757c5a47ff9b2b75a4da8277 100644 (file)
@@ -93,10 +93,9 @@ public class PortUtils {
         return DEVICE_OWNER_DHCP.equals(port.getDeviceOwner());
     }
 
-    public static boolean isQrouterPort(Port port) {
+    public static boolean isQrouterOrVppRouterPort(Port port) {
         return DEVICE_OWNER_ROUTER_IFACE.equals(port.getDeviceOwner())
-            && port.getAugmentation(PortBindingExtension.class) != null
-            && DEVICE_VIF_TYPE.equals(port.getAugmentation(PortBindingExtension.class).getVifType());
+            && port.getAugmentation(PortBindingExtension.class) != null;
     }
 
     public static boolean isRouterInterfacePort(Port port) {
index decd0567f199415f7607752a22f303e21b9c451f..560d1556613de73e8ac23eb4ea396c5cea410e57 100644 (file)
@@ -18,6 +18,8 @@ import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;\r
 import org.opendaylight.groupbasedpolicy.neutron.vpp.mapper.SocketInfo;\r
 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;\r
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;\r
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId;\r
@@ -30,6 +32,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_render
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointBuilder;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.LoopbackCase;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.LoopbackCaseBuilder;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.TapCase;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.TapCaseBuilder;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.VhostUserCaseBuilder;\r
@@ -37,10 +41,14 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.P
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.RouterKey;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortKey;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.Subnets;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.SubnetKey;\r
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;\r
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;\r
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;\r
@@ -56,6 +64,8 @@ import org.slf4j.LoggerFactory;
 import com.google.common.annotations.VisibleForTesting;\r
 import com.google.common.base.Optional;\r
 \r
+import java.util.List;\r
+\r
 public class PortHandler implements TransactionChainListener {\r
 \r
     private static final Logger LOG = LoggerFactory.getLogger(MappingProvider.class);\r
@@ -189,15 +199,59 @@ public class PortHandler implements TransactionChainListener {
                     .setName(createQRouterPortName(port.getUuid()))\r
                     .build();\r
             vppEpBuilder.setInterfaceTypeChoice(tapCase);\r
+        } else if (isValidVppRouterPort(port)) {\r
+            vppEpBuilder.setInterfaceTypeChoice(getLoopbackCase(port));\r
         }\r
         return vppEpBuilder.build();\r
     }\r
 \r
+    private LoopbackCase getLoopbackCase(Port port) {\r
+        LoopbackCaseBuilder loopbackCase = new LoopbackCaseBuilder()\r
+            .setPhysAddress(new PhysAddress(port.getMacAddress().getValue()));\r
+        Optional<FixedIps> fixedIpsOptional = resolveFirstFixedIps(port);\r
+        if(fixedIpsOptional.isPresent() && fixedIpsOptional.get().getIpAddress() != null){\r
+            loopbackCase.setIpAddress(fixedIpsOptional.get().getIpAddress());\r
+            ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
+            Optional<Subnet> subnetOptional =\r
+                DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
+                    InstanceIdentifier.builder(Neutron.class)\r
+                        .child(Subnets.class)\r
+                        .child(Subnet.class, new SubnetKey(fixedIpsOptional.get().getSubnetId()))\r
+                        .build(), rTx);\r
+            if (subnetOptional.isPresent()) {\r
+                Ipv4Prefix ipv4Prefix = subnetOptional.get().getCidr().getIpv4Prefix();\r
+                loopbackCase.setIpPrefix(new IpPrefix(ipv4Prefix));\r
+            } else {\r
+                LOG.warn("IpPrefix for loopback port: {} was not set.", port);\r
+            }\r
+            if (loopbackCase.getIpAddress() != null && loopbackCase.getIpPrefix() != null) {\r
+                loopbackCase.setBvi(true);\r
+                LOG.trace("Creating loopback BVI interface: {} for VPP router port: {}.", loopbackCase, port);\r
+            }\r
+\r
+        } else {\r
+            LOG.warn("IpAddress for loopback port: {} was not set.", port);\r
+        }\r
+        return loopbackCase.build();\r
+    }\r
+\r
     /**\r
      * If Qrouter (L3 Agent) is in use, any of Openstack neutron routers is not going be mapped\r
      * to ODL neutron.\r
      */\r
     private boolean isValidQRouterPort(Port port) {\r
+        Optional<Router> optRouter = getRouterOptional(port);\r
+        return !optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER)\r
+                && port.getMacAddress() != null;\r
+    }\r
+\r
+    private boolean isValidVppRouterPort(Port port) {\r
+        Optional<Router> optRouter = getRouterOptional(port);\r
+        return optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER)\r
+            && port.getMacAddress() != null;\r
+    }\r
+\r
+    private Optional<Router> getRouterOptional(Port port) {\r
         ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
         InstanceIdentifier<Router> routerIid = InstanceIdentifier.builder(Neutron.class)\r
             .child(Routers.class)\r
@@ -205,8 +259,15 @@ public class PortHandler implements TransactionChainListener {
             .build();\r
         Optional<Router> optRouter = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, routerIid, rTx);\r
         rTx.close();\r
-        return !optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER)\r
-                && port.getMacAddress() != null;\r
+        return optRouter;\r
+    }\r
+\r
+    public static Optional<FixedIps> resolveFirstFixedIps(Port port) {\r
+        List<FixedIps> fixedIps = port.getFixedIps();\r
+        if (fixedIps != null && !fixedIps.isEmpty()) {\r
+            return Optional.of(fixedIps.get(0));\r
+        }\r
+        return Optional.absent();\r
     }\r
 \r
     private String createPortName(Uuid portUuid) {\r
@@ -277,6 +338,6 @@ public class PortHandler implements TransactionChainListener {
 \r
     @Override\r
     public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {\r
-        LOG.trace("Transaction chain was successfull. {}", chain);\r
+        LOG.trace("Transaction chain was successful. {}", chain);\r
     }\r
 }\r
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/commands/LoopbackCommand.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/commands/LoopbackCommand.java
new file mode 100644 (file)
index 0000000..2d8a6a4
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2016 Cisco Systems. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.groupbasedpolicy.renderer.vpp.commands;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.util.General.Operations;
+import org.opendaylight.groupbasedpolicy.util.NetUtils;
+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.IpPrefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;
+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.InterfaceBuilder;
+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.ip.rev140616.Interface1;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface1Builder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4Builder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.AddressBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.PrefixLengthBuilder;
+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.Loopback;
+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.VppInterfaceAugmentationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.L2Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.LoopbackBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.interconnection.BridgeBasedBuilder;
+
+import java.util.Collections;
+
+public class LoopbackCommand extends AbstractInterfaceCommand<LoopbackCommand> {
+
+    private PhysAddress physAddress;
+    private String bridgeDomain;
+    private boolean bvi;
+    private IpAddress ipAddress;
+    private IpPrefix ipPrefix;
+
+    private LoopbackCommand(LoopbackCommandBuilder builder) {
+        this.name = builder.getInterfaceName();
+        this.operation = builder.getOperation();
+        this.physAddress = builder.getPhysAddress();
+        this.enabled = builder.isEnabled();
+        this.description = builder.getDescription();
+        this.bridgeDomain = builder.getBridgeDomain();
+        this.bvi = builder.isBvi();
+        this.ipAddress = builder.getIpAddress();
+        this.ipPrefix = builder.getIpPrefix();
+    }
+
+    public static LoopbackCommandBuilder builder() {
+        return new LoopbackCommandBuilder();
+    }
+
+    public PhysAddress getPhysAddress() {
+        return physAddress;
+    }
+
+    public String getBridgeDomain() {
+        return bridgeDomain;
+    }
+
+    public boolean getBvi() {
+        return bvi;
+    }
+
+    public IpAddress getIpAddress() {
+        return ipAddress;
+    }
+
+    public IpPrefix getIpPrefix() {
+        return ipPrefix;
+    }
+
+    public short getPrefixLength() {
+        return (short) NetUtils.getMaskFromPrefix(this.ipPrefix.getIpv4Prefix().getValue());
+    }
+
+    @Override public InterfaceBuilder getInterfaceBuilder() {
+        InterfaceBuilder
+            interfaceBuilder =
+            new InterfaceBuilder().setKey(new InterfaceKey(name))
+                .setEnabled(enabled)
+                .setDescription(description)
+                .setType(Loopback.class)
+                .setName(name)
+                .setLinkUpDownTrapEnable(Interface.LinkUpDownTrapEnable.Enabled);
+
+        // Create the Loopback augmentation
+        VppInterfaceAugmentationBuilder
+            vppAugmentationBuilder =
+            new VppInterfaceAugmentationBuilder().setLoopback(new LoopbackBuilder().setMac(this.physAddress).build());
+
+        if (!Strings.isNullOrEmpty(bridgeDomain)) {
+            vppAugmentationBuilder.setL2(new L2Builder().setInterconnection(
+                new BridgeBasedBuilder().setBridgeDomain(bridgeDomain).setBridgedVirtualInterface(bvi).build())
+                .build());
+        }
+        Interface1Builder
+            interface1Builder =
+            new Interface1Builder().setIpv4(new Ipv4Builder().setAddress(Collections.singletonList(
+                new AddressBuilder().setIp(new Ipv4AddressNoZone(this.ipAddress.getIpv4Address()))
+                    .setSubnet(new PrefixLengthBuilder().setPrefixLength(this.getPrefixLength()).build())
+                    .build())).build());
+        interfaceBuilder.addAugmentation(Interface1.class, interface1Builder.build());
+        interfaceBuilder.addAugmentation(VppInterfaceAugmentation.class, vppAugmentationBuilder.build());
+        return interfaceBuilder;
+    }
+
+    @Override public String toString() {
+        return "LoopPortUserCommand [physAddress=" + physAddress + ", IpAddress=" + ipAddress + ", IpPrefix=" + ipPrefix
+            + ", bridgeDomain=" + bridgeDomain + ", operations=" + operation + ", name=" + name + ", description="
+            + description + ", enabled=" + enabled + ", bvi=" + bvi + "]";
+    }
+
+    public static class LoopbackCommandBuilder {
+
+        private String interfaceName;
+        private Operations operation;
+        private PhysAddress physAddress;
+        private String bridgeDomain;
+        private String description;
+        private boolean bvi = false;
+        private boolean enabled = true;
+        private IpAddress ipAddress;
+        private IpPrefix ipPrefix;
+
+        public String getInterfaceName() {
+            return interfaceName;
+        }
+
+        public LoopbackCommandBuilder setInterfaceName(String interfaceName) {
+            this.interfaceName = interfaceName;
+            return this;
+        }
+
+        public Operations getOperation() {
+            return operation;
+        }
+
+        public LoopbackCommandBuilder setOperation(Operations operation) {
+            this.operation = operation;
+            return this;
+        }
+
+        public PhysAddress getPhysAddress() {
+            return physAddress;
+        }
+
+        public LoopbackCommandBuilder setPhysAddress(PhysAddress physAddress) {
+            this.physAddress = physAddress;
+            return this;
+        }
+
+        public String getBridgeDomain() {
+            return bridgeDomain;
+        }
+
+        public LoopbackCommandBuilder setBridgeDomain(String bridgeDomain) {
+            this.bridgeDomain = bridgeDomain;
+            return this;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public LoopbackCommandBuilder setDescription(String description) {
+            this.description = description;
+            return this;
+        }
+
+        public boolean isEnabled() {
+            return enabled;
+        }
+
+        public LoopbackCommandBuilder setEnabled(boolean enabled) {
+            this.enabled = enabled;
+            return this;
+        }
+
+        public IpAddress getIpAddress() {
+            return ipAddress;
+        }
+
+        public LoopbackCommandBuilder setIpAddress(IpAddress ipAddress) {
+            this.ipAddress = ipAddress;
+            return this;
+        }
+
+        public IpPrefix getIpPrefix() {
+            return ipPrefix;
+        }
+
+        public LoopbackCommandBuilder setIpPrefix(IpPrefix ipPrefix) {
+            this.ipPrefix = ipPrefix;
+            return this;
+        }
+
+        public boolean isBvi() {
+            return bvi;
+        }
+
+        public LoopbackCommandBuilder setBvi(boolean bvi) {
+            this.bvi = bvi;
+            return this;
+        }
+
+        /**
+         * LoopPortCommand build method.
+         *
+         * @return LoopPortCommand
+         * @throws IllegalArgumentException if interfaceName or operation is null.
+         */
+        public LoopbackCommand build() {
+            Preconditions.checkArgument(this.interfaceName != null);
+            Preconditions.checkArgument(this.operation != null);
+
+            return new LoopbackCommand(this);
+        }
+    }
+}
index a053fabe53a616843b03aedeae28c0b7b21acb8d..d3222249886752288cd9a304c44a1dd4d3d99bf9 100644 (file)
@@ -25,6 +25,7 @@ import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.ConfigCommand;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.LoopbackCommand;
 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.TapPortCommand;
 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.VhostUserCommand;
 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.VhostUserCommand.VhostUserCommandBuilder;
@@ -40,6 +41,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpo
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint.InterfaceTypeChoice;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.LoopbackCase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.TapCase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.VhostUserCase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VhostUserRole;
@@ -91,11 +93,14 @@ public class InterfaceManager implements AutoCloseable {
 
     private ListenableFuture<Void> vppEndpointCreated(VppEndpoint vppEndpoint) {
         InterfaceTypeChoice interfaceTypeChoice = vppEndpoint.getInterfaceTypeChoice();
+        LOG.trace("Creating VPP endpoint {}, type of {}", vppEndpoint, interfaceTypeChoice);
         Optional<ConfigCommand> potentialIfaceCommand = Optional.absent();
         if (interfaceTypeChoice instanceof VhostUserCase) {
             potentialIfaceCommand = createInterfaceWithoutBdCommand(vppEndpoint, Operations.PUT);
         } else if (interfaceTypeChoice instanceof TapCase) {
             potentialIfaceCommand = createTapInterfaceWithoutBdCommand(vppEndpoint, Operations.PUT);
+        } else if (interfaceTypeChoice instanceof LoopbackCase){
+            potentialIfaceCommand = createLoopbackWithoutBdCommand(vppEndpoint, Operations.PUT);
         }
 
         if (!potentialIfaceCommand.isPresent()) {
@@ -117,6 +122,7 @@ public class InterfaceManager implements AutoCloseable {
                                                         VppEndpoint vppEndpoint, InstanceIdentifier<?> vppNodeIid) {
         final ReadWriteTransaction rwTx = vppDataBroker.newReadWriteTransaction();
         createIfaceWithoutBdCommand.execute(rwTx);
+        LOG.trace("Creating Interface on VPP: {}", createIfaceWithoutBdCommand);
         return Futures.transform(rwTx.submit(), new AsyncFunction<Void, Void>() {
 
             @Override
@@ -146,11 +152,14 @@ public class InterfaceManager implements AutoCloseable {
 
     private ListenableFuture<Void> vppEndpointDeleted(@Nonnull VppEndpoint vppEndpoint) {
         InterfaceTypeChoice interfaceTypeChoice = vppEndpoint.getInterfaceTypeChoice();
+        LOG.trace("Deleting VPP endpoint {}, type of {}", vppEndpoint, interfaceTypeChoice.toString());
         Optional<ConfigCommand> potentialIfaceCommand = Optional.absent();
         if (interfaceTypeChoice instanceof VhostUserCase) {
             potentialIfaceCommand = createInterfaceWithoutBdCommand(vppEndpoint, Operations.DELETE);
         } else if (interfaceTypeChoice instanceof TapCase) {
             potentialIfaceCommand = createTapInterfaceWithoutBdCommand(vppEndpoint, Operations.DELETE);
+        } else if (interfaceTypeChoice instanceof LoopbackCase){
+            potentialIfaceCommand = createLoopbackWithoutBdCommand(vppEndpoint, Operations.DELETE);
         }
 
         if (!potentialIfaceCommand.isPresent()) {
@@ -172,6 +181,7 @@ public class InterfaceManager implements AutoCloseable {
             DataBroker vppDataBroker, VppEndpoint vppEndpoint, InstanceIdentifier<?> vppNodeIid) {
         ReadWriteTransaction rwTx = vppDataBroker.newReadWriteTransaction();
         deleteIfaceWithoutBdCommand.execute(rwTx);
+        LOG.trace("Deleting Interface on VPP: {}", deleteIfaceWithoutBdCommand);
 
         return Futures.transform(rwTx.submit(), new AsyncFunction<Void, Void>() {
 
@@ -256,6 +266,29 @@ public class InterfaceManager implements AutoCloseable {
         return Optional.of(tapPortCommand);
     }
 
+    private static Optional<ConfigCommand> createLoopbackWithoutBdCommand(@Nonnull VppEndpoint vppEp,
+        @Nonnull Operations operation) {
+        if (!hasNodeAndInterface(vppEp)) {
+            LOG.debug("Interface command is not created for {}", vppEp);
+            return Optional.absent();
+        }
+        LoopbackCommand.LoopbackCommandBuilder builder = LoopbackCommand.builder();
+        LoopbackCase loopIface = (LoopbackCase) vppEp.getInterfaceTypeChoice();
+
+        builder.setPhysAddress(loopIface.getPhysAddress());
+        builder.setBvi(loopIface.isBvi());
+        builder.setIpAddress(loopIface.getIpAddress());
+        builder.setIpPrefix(loopIface.getIpPrefix());
+
+        LoopbackCommand loopbackCommand = builder
+            .setOperation(operation)
+            .setDescription(vppEp.getDescription())
+            .setInterfaceName(vppEp.getVppInterfaceName())
+            .build();
+
+        return Optional.of(loopbackCommand);
+    }
+
     /**
      * Adds bridge domain to an interface if the interface exist.<br>
      * It rewrites bridge domain in case it already exist.<br>
@@ -273,7 +306,7 @@ public class InterfaceManager implements AutoCloseable {
      * @return {@link ListenableFuture}
      */
     public synchronized @Nonnull ListenableFuture<Void> addBridgeDomainToInterface(@Nonnull String bridgeDomainName,
-            @Nonnull AddressEndpointWithLocation addrEpWithLoc) {
+            @Nonnull AddressEndpointWithLocation addrEpWithLoc, boolean enableBvi) {
         ExternalLocationCase epLoc = resolveAndValidateLocation(addrEpWithLoc);
         InstanceIdentifier<?> vppNodeIid = epLoc.getExternalNodeMountPoint();
         String interfacePath = epLoc.getExternalNodeConnector();
@@ -299,7 +332,7 @@ public class InterfaceManager implements AutoCloseable {
             @Override
             public ListenableFuture<Void> apply(Optional<Interface> optIface) throws Exception {
                 if (!optIface.isPresent()) {
-                    return Futures.immediateFailedFuture(new Exception("Iterface "
+                    return Futures.immediateFailedFuture(new Exception("Interface "
                             + interfaceIid.firstKeyOf(Interface.class) + " does not exist on node " + vppNodeIid));
                 }
 
@@ -321,7 +354,10 @@ public class InterfaceManager implements AutoCloseable {
                 final ReadWriteTransaction rwTxRead = mountpoint.newReadWriteTransaction();
                 Optional<L2> optL2 = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, l2Iid, rwTxRead);
                 L2Builder l2Builder = (optL2.isPresent()) ? new L2Builder(optL2.get()) : new L2Builder();
-                L2 l2 = l2Builder.setInterconnection(new BridgeBasedBuilder().setBridgeDomain(bridgeDomainName).build()).build();
+                L2 l2 = l2Builder.setInterconnection(new BridgeBasedBuilder()
+                    .setBridgeDomain(bridgeDomainName)
+                    .setBridgedVirtualInterface(enableBvi)
+                    .build()).build();
                 final ReadWriteTransaction rwTxPut = prepareTransactionAndPutData(mountpoint, l2, l2Iid);
                 LOG.debug("Adding bridge domain {} to interface {}", bridgeDomainName, interfacePath);
                 return Futures.transform(rwTxPut.submit(), new AsyncFunction<Void, Void>() {
index 1abae232cb47caccebe98fd20589fb99222e9058..5823d5bc4ef4b6aa8e74fdcce04517590cbd11c3 100644 (file)
@@ -20,7 +20,6 @@ import javax.annotation.Nullable;
 
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
-import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.groupbasedpolicy.renderer.vpp.api.BridgeDomainManager;
 import org.opendaylight.groupbasedpolicy.renderer.vpp.iface.InterfaceManager;
@@ -46,14 +45,12 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_render
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.VlanNetwork;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.GbpBridgeDomain;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.GbpBridgeDomainKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint.InterfaceTypeChoice;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.LoopbackCase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
-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.VxlanVni;
-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.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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.status.rev161005.BridgeDomainStatusAugmentation;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.status.rev161005.BridgeDomainStatusFields;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
@@ -179,7 +176,7 @@ public final class ForwardingManager {
             }
             String l2FloodDomain = optL2FloodDomain.get();
             try {
-                ifaceManager.addBridgeDomainToInterface(l2FloodDomain, rEp).get();
+                ifaceManager.addBridgeDomainToInterface(l2FloodDomain, rEp, isBviForEndpoint(rEp)).get();
                 LOG.debug("Interface added to bridge-domain {} for endpoint {}", l2FloodDomain, rEp);
             } catch (InterruptedException | ExecutionException e) {
                 // TODO add it to the status for renderer manager
@@ -192,6 +189,25 @@ public final class ForwardingManager {
         }
     }
 
+    private boolean isBviForEndpoint(AddressEndpointWithLocation rEp) {
+        VppEndpointKey vppEndpointKey =
+            new VppEndpointKey(rEp.getAddress(), rEp.getAddressType(), rEp.getContextId(), rEp.getContextType());
+        ReadOnlyTransaction rTx = dataBroker.newReadOnlyTransaction();
+        Optional<VppEndpoint> vppEndpointOptional =
+            DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.builder(Config.class).child(VppEndpoint.class, vppEndpointKey).build(), rTx);
+        if (vppEndpointOptional.isPresent()) {
+            InterfaceTypeChoice interfaceTypeChoice = vppEndpointOptional.get().getInterfaceTypeChoice();
+            if (interfaceTypeChoice instanceof LoopbackCase) {
+                LOG.trace("Vpp renderer endpoint {} IS a BVI interface.", rEp.getKey());
+                return ((LoopbackCase) interfaceTypeChoice).isBvi();
+            }
+        }
+        rTx.close();
+        LOG.trace("Vpp renderer endpoint {} IS NOT a BVI interface.", rEp.getKey());
+        return false;
+    }
+
     public void removeForwardingForEndpoint(RendererEndpointKey rEpKey, PolicyContext policyCtx) {
         AddressEndpointWithLocation rEp = policyCtx.getAddrEpByKey().get(KeyFactory.addressEndpointKey(rEpKey));
         ExternalLocationCase rEpLoc = resolveAndValidateLocation(rEp);
index 2c6ae2b5b586591c326cf1898d98da266c629135..25b3f835ef91c3d1b507f44416ae78643fb1a32d 100644 (file)
@@ -16,6 +16,7 @@ module vpp-renderer {
     import network-topology { prefix nt; revision-date 2013-10-21; }
     import opendaylight-l2-types { prefix l2-types; revision-date "2013-08-27"; }
     import ietf-yang-types { prefix yang-types; revision-date "2013-07-15"; }
+    import ietf-inet-types { prefix "inet-types"; }
 
     description
         "This module is a baseline for the group-based policy vpp renderer model.";
@@ -89,6 +90,24 @@ module vpp-renderer {
                         type yang-types:phys-address;
                     }
                 }
+                case loopback-case {
+                    leaf phys-address {
+                        description "MAC address of a loopback interface";
+                        type yang-types:phys-address;
+                    }
+                    leaf ip-address {
+                        description "Ip address of a loopback interface";
+                        type inet-types:ip-address;
+                    }
+                    leaf ip-prefix {
+                        description "Ip address prefix of a loopback interface";
+                        type inet-types:ip-prefix;
+                    }
+                    leaf bvi {
+                        description "Enable/disable BVI for loopback interface";
+                        type boolean;
+                    }
+                }
             }
         }
 
diff --git a/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/commands/LoopbackCommandTest.java b/renderers/vpp/src/test/java/org/opendaylight/groupbasedpolicy/renderer/vpp/commands/LoopbackCommandTest.java
new file mode 100644 (file)
index 0000000..1da16e3
--- /dev/null
@@ -0,0 +1,252 @@
+/*\r
+ * Copyright (c) 2016 Cisco Systems. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+\r
+package org.opendaylight.groupbasedpolicy.renderer.vpp.commands;\r
+\r
+import com.google.common.base.Optional;\r
+import org.junit.Assert;\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;\r
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;\r
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;\r
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;\r
+import org.opendaylight.groupbasedpolicy.renderer.vpp.VppRendererDataBrokerTest;\r
+import org.opendaylight.groupbasedpolicy.renderer.vpp.util.General;\r
+import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppIidFactory;\r
+import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;\r
+import org.opendaylight.groupbasedpolicy.util.NetUtils;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface1;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface1Builder;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4Builder;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.AddressBuilder;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.Subnet;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.PrefixLength;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.PrefixLengthBuilder;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.Loopback;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceAugmentation;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceAugmentationBuilder;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.L2Builder;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.LoopbackBuilder;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.interconnection.BridgeBasedBuilder;\r
+\r
+import java.util.Collections;\r
+import java.util.concurrent.ExecutionException;\r
+\r
+public class LoopbackCommandTest extends VppRendererDataBrokerTest {\r
+\r
+    private final static String DESCRIPTION = "used for testing";\r
+    private final static String INTERFACE_NAME = "testInterface";\r
+    private final static String BRIDGE_DOMAIN = "testBD";\r
+    private final static boolean IS_BVI = true;\r
+    private final static PhysAddress MAC_ADDRESS = new PhysAddress("00:11:22:33:44:55");\r
+    private final static IpAddress IP_ADDRESS = new IpAddress(new Ipv4Address("10.0.0.1"));\r
+    private final static IpPrefix IP_PREFIX = new IpPrefix(new Ipv4Prefix("10.0.0.1/24"));\r
+\r
+    private final static String UPD_DESCRIPTION = "updated description";\r
+    private final static PhysAddress UPD_MAC_ADDRESS = new PhysAddress("55:44:33:22:11:00");\r
+    private final static IpAddress UPD_IP_ADDRESS = new IpAddress(new Ipv4Address("20.0.0.1"));\r
+    private final static IpPrefix UPD_IP_PREFIX = new IpPrefix(new Ipv4Prefix("20.0.0.1/24"));\r
+\r
+    private final static VppInterfaceAugmentationBuilder vppAugmentationBuilder = new VppInterfaceAugmentationBuilder()\r
+        .setLoopback(new LoopbackBuilder().setMac(MAC_ADDRESS).build());\r
+\r
+    private final static VppInterfaceAugmentationBuilder vppAugmentationBuilderWithBD =\r
+            new VppInterfaceAugmentationBuilder(vppAugmentationBuilder.build())\r
+                .setL2(new L2Builder().setInterconnection(new BridgeBasedBuilder().setBridgeDomain(BRIDGE_DOMAIN)\r
+                    .setBridgedVirtualInterface(IS_BVI).setSplitHorizonGroup((short)0)\r
+                    .build()).build());\r
+\r
+    private final static InterfaceBuilder interfaceBuilder =\r
+            new InterfaceBuilder().setKey(new InterfaceKey(INTERFACE_NAME))\r
+                .setEnabled(true)\r
+                .setDescription(DESCRIPTION)\r
+                .setType(Loopback.class)\r
+                .setName(INTERFACE_NAME)\r
+                .setLinkUpDownTrapEnable(Interface.LinkUpDownTrapEnable.Enabled);\r
+\r
+    private final static Interface1Builder\r
+        interface1Builder =\r
+        new Interface1Builder().setIpv4(new Ipv4Builder().setAddress(Collections.singletonList(\r
+            new AddressBuilder()\r
+                .setIp(new Ipv4AddressNoZone(IP_ADDRESS.getIpv4Address()))\r
+                .setSubnet(new PrefixLengthBuilder().setPrefixLength((short) NetUtils.getMaskFromPrefix(IP_PREFIX.getIpv4Prefix().getValue())).build())\r
+                .build()))\r
+            .setEnabled(true)\r
+            .setForwarding(false)\r
+            .build());\r
+\r
+    private final static Interface BASIC_INTERFACE =\r
+            interfaceBuilder.addAugmentation(VppInterfaceAugmentation.class, vppAugmentationBuilder.build())\r
+                .addAugmentation(Interface1.class, interface1Builder.build()).build();\r
+\r
+    private final static Interface BASIC_INTERFACE_WITH_BD = interfaceBuilder\r
+        .addAugmentation(VppInterfaceAugmentation.class, vppAugmentationBuilderWithBD.build()).build();\r
+\r
+    private DataBroker dataBroker;\r
+\r
+    @Before\r
+    public void init() {\r
+        dataBroker = getDataBroker();\r
+    }\r
+\r
+    @Test\r
+    public void testAddLoopback() throws ExecutionException, InterruptedException {\r
+        ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction();\r
+        LoopbackCommand addCommand = LoopbackCommand.builder()\r
+            .setOperation(General.Operations.PUT)\r
+            .setInterfaceName(INTERFACE_NAME)\r
+            .setDescription(DESCRIPTION)\r
+            .setBvi(IS_BVI)\r
+            .setPhysAddress(MAC_ADDRESS)\r
+            .setIpPrefix(IP_PREFIX)\r
+            .setIpAddress(IP_ADDRESS)\r
+            .setEnabled(true)\r
+            .build();\r
+\r
+        Assert.assertEquals(IS_BVI, addCommand.getBvi());\r
+        Assert.assertEquals(MAC_ADDRESS, addCommand.getPhysAddress());\r
+        Assert.assertEquals(IP_ADDRESS, addCommand.getIpAddress());\r
+        Assert.assertEquals(IP_PREFIX, addCommand.getIpPrefix());\r
+\r
+        Optional<Interface> optional = executeCommand(rwTx, addCommand);\r
+\r
+        Assert.assertTrue(optional.isPresent());\r
+        Assert.assertEquals(BASIC_INTERFACE, optional.get());\r
+\r
+    }\r
+\r
+    @Test\r
+    public void testAddLoopback_WithBridgeDomain() throws ExecutionException, InterruptedException {\r
+        ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction();\r
+        LoopbackCommand addCommand = LoopbackCommand.builder()\r
+            .setOperation(General.Operations.PUT)\r
+            .setInterfaceName(INTERFACE_NAME)\r
+            .setDescription(DESCRIPTION)\r
+            .setBvi(IS_BVI)\r
+            .setPhysAddress(MAC_ADDRESS)\r
+            .setIpPrefix(IP_PREFIX)\r
+            .setIpAddress(IP_ADDRESS)\r
+            .setBridgeDomain(BRIDGE_DOMAIN)\r
+            .setEnabled(true)\r
+            .build();\r
+\r
+        Assert.assertEquals(BRIDGE_DOMAIN, addCommand.getBridgeDomain());\r
+\r
+        Optional<Interface> optional = executeCommand(rwTx, addCommand);\r
+\r
+        Assert.assertTrue(optional.isPresent());\r
+        Assert.assertEquals(BASIC_INTERFACE_WITH_BD, optional.get());\r
+\r
+    }\r
+\r
+    private Optional<Interface> executeCommand(ReadWriteTransaction rwTx, LoopbackCommand addCommand)\r
+            throws ExecutionException, InterruptedException {\r
+        addCommand.execute(rwTx);\r
+\r
+        rwTx.submit().get();\r
+\r
+        ReadOnlyTransaction rTx = dataBroker.newReadOnlyTransaction();\r
+\r
+        return DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
+                VppIidFactory.getInterfaceIID(new InterfaceKey(INTERFACE_NAME)), rTx);\r
+    }\r
+\r
+    @Test\r
+    public void testDeleteLoopbackPort() throws ExecutionException, InterruptedException {\r
+        ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction();\r
+\r
+        rwTx.put(LogicalDatastoreType.CONFIGURATION, VppIidFactory.getInterfaceIID(BASIC_INTERFACE.getKey()),\r
+                BASIC_INTERFACE, true);\r
+        rwTx.submit().get();\r
+\r
+        Optional<Interface> optional = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
+                VppIidFactory.getInterfaceIID(BASIC_INTERFACE.getKey()), dataBroker.newReadOnlyTransaction());\r
+\r
+        Assert.assertTrue(optional.isPresent());\r
+\r
+        LoopbackCommand deleteCommand = LoopbackCommand.builder()\r
+            .setOperation(General.Operations.DELETE)\r
+            .setInterfaceName(INTERFACE_NAME)\r
+            .build();\r
+\r
+        ReadWriteTransaction rwTxDel = dataBroker.newReadWriteTransaction();\r
+        deleteCommand.execute(rwTxDel);\r
+        rwTxDel.submit();\r
+\r
+        Optional<Interface> optionalDeleted = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
+                VppIidFactory.getInterfaceIID(new InterfaceKey(deleteCommand.getName())),\r
+                dataBroker.newReadOnlyTransaction());\r
+\r
+        Assert.assertFalse(optionalDeleted.isPresent());\r
+    }\r
+\r
+    @Test\r
+    public void testUpdateLoopbackPort() throws ExecutionException, InterruptedException {\r
+        ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction();\r
+\r
+        rwTx.put(LogicalDatastoreType.CONFIGURATION, VppIidFactory.getInterfaceIID(BASIC_INTERFACE.getKey()),\r
+                BASIC_INTERFACE, true);\r
+        rwTx.submit().get();\r
+\r
+        Optional<Interface> optional = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
+                VppIidFactory.getInterfaceIID(BASIC_INTERFACE.getKey()), dataBroker.newReadOnlyTransaction());\r
+\r
+        Assert.assertTrue(optional.isPresent());\r
+\r
+        LoopbackCommand updateCommand = LoopbackCommand.builder()\r
+            .setOperation(General.Operations.MERGE)\r
+            .setInterfaceName(INTERFACE_NAME)\r
+            .setDescription(UPD_DESCRIPTION)\r
+            .setPhysAddress(UPD_MAC_ADDRESS)\r
+            .setIpPrefix(UPD_IP_PREFIX)\r
+            .setIpAddress(UPD_IP_ADDRESS)\r
+            .setEnabled(false)\r
+            .build();\r
+\r
+        ReadWriteTransaction rwTxUpd = dataBroker.newReadWriteTransaction();\r
+        updateCommand.execute(rwTxUpd);\r
+        rwTxUpd.submit().get();\r
+\r
+        Optional<Interface> optionalUpdated = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
+                VppIidFactory.getInterfaceIID(new InterfaceKey(updateCommand.getName())),\r
+                dataBroker.newReadOnlyTransaction());\r
+\r
+        Assert.assertTrue(optionalUpdated.isPresent());\r
+        Interface updatedInterface = optionalUpdated.get();\r
+\r
+        Assert.assertEquals(UPD_DESCRIPTION, updatedInterface.getDescription());\r
+        Assert.assertFalse(updatedInterface.isEnabled());\r
+        VppInterfaceAugmentation augmentation = updatedInterface.getAugmentation(VppInterfaceAugmentation.class);\r
+        Assert.assertEquals(INTERFACE_NAME, updatedInterface.getName());\r
+        Assert.assertEquals(UPD_MAC_ADDRESS, augmentation.getLoopback().getMac());\r
+        Interface1 interface1 = updatedInterface.getAugmentation(Interface1.class);\r
+\r
+        // merge operation will add new ip address to list so index is 1 for new ip\r
+        String ip = interface1.getIpv4().getAddress().get(1).getIp().getValue();\r
+        Subnet subnet = interface1.getIpv4().getAddress().get(1).getSubnet();\r
+        String prefix = "";\r
+\r
+        if ( subnet instanceof PrefixLength){\r
+            prefix = ((PrefixLength) subnet).getPrefixLength().toString();\r
+        }\r
+        IpPrefix ipPrefix = new IpPrefix(new Ipv4Prefix(ip + "/" + prefix));\r
+        IpAddress ipAddress = new IpAddress( new Ipv4Address(ip));\r
+        Assert.assertEquals(UPD_IP_PREFIX, ipPrefix);\r
+        Assert.assertEquals(UPD_IP_ADDRESS, ipAddress);\r
+    }\r
+}\r
index fb9da42f9ff34f3fd65cba5ec95bd546623e2cb4..801bbfbbed95ec316f6d71590d3e966f8ebe5753 100644 (file)
@@ -52,6 +52,7 @@ public class ForwardingManagerTest extends CustomDataBrokerTest {
     private static final String BD_1 = "bd1";
     private static final NodeId NODE_1 = new NodeId("node1");
     private static final VlanId VLAN_1 = new VlanId(1);
+    private static final boolean IS_BVI = false;
     @Mock
     private InterfaceManager ifaceManager;
     @Mock
@@ -146,12 +147,12 @@ public class ForwardingManagerTest extends CustomDataBrokerTest {
         AddressEndpointWithLocation firstAddrEpWithLoc =
                 policyCtx.getAddrEpByKey().get(KeyFactory.addressEndpointKey(firstRendererEp.getKey()));
         Mockito.when(ifaceManager.addBridgeDomainToInterface(Mockito.eq(DtoFactory.L2FD_CTX.getValue()),
-                Mockito.eq(firstAddrEpWithLoc)))
+                Mockito.eq(firstAddrEpWithLoc), Mockito.eq(IS_BVI)))
             .thenReturn(Futures.immediateFuture(null));
 
         fwdManager.createForwardingForEndpoint(firstRendererEp.getKey(), policyCtx);
         Mockito.verify(ifaceManager).addBridgeDomainToInterface(Matchers.eq(DtoFactory.L2FD_CTX.getValue()),
-                Matchers.eq(firstAddrEpWithLoc));
+                Matchers.eq(firstAddrEpWithLoc), Mockito.eq(IS_BVI));
     }
 
     @Test