/*
 * Copyright (c) 2016 Red Hat, Inc. and others.  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.netvirt.utils.netvirt.it.utils;

import org.junit.Assert;
import org.opendaylight.netvirt.openstack.netvirt.translator.NeutronSecurityGroup;
import org.opendaylight.ovsdb.utils.ovsdb.it.utils.DockerOvs;
import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;

import java.io.IOException;

/**
 * This class adds the ability to issue pings from one port to another to its base class, NeutronNetItUtil.
 * Please see NetvirtIT#testNeutronNet for an example of how this class is used
 * N.B. At this point this class only supports DockerOvs runs where the docker-compose file runs a single
 * docker container with OVS. A little work will need to be invested to extend this functionality to support
 * configuring "ping-ability" on multiple docker containers.
 */
public class PingableNeutronNetItUtil extends NeutronNetItUtil {

    private static final int DEFAULT_WAIT = 5000;
    private DockerOvs dockerOvs;

    public PingableNeutronNetItUtil(DockerOvs dockerOvs, SouthboundUtils southboundUtils, String tenantId) {
        super(southboundUtils, tenantId);
        this.dockerOvs = dockerOvs;
    }

    public PingableNeutronNetItUtil(DockerOvs dockerOvs, SouthboundUtils southboundUtils, String tenantId,
                                                        String segId, String macPfx, String ipPfx, String cidr) {
        super(southboundUtils, tenantId, segId, macPfx, ipPfx, cidr);
        this.dockerOvs = dockerOvs;
    }

    /**
     * Create a port on the network. The deviceOwner will be set to "compute:None".
     * @param bridge bridge where the port will be created on OVS
     * @param portName name for this port
     * @param owner deviceOwner, e.g., "network:dhcp"
     * @param secGroups Optional NeutronSecurityGroup objects see NeutronUtils.createNeutronSecurityGroup()
     * @throws InterruptedException if we're interrupted while waiting for objects to be created
     */
    public void createPort(Node bridge, String portName, String owner, NeutronSecurityGroup... secGroups)
            throws InterruptedException, IOException {

        PortInfo portInfo = buildPortInfo(portName);

        dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "tuntap", "add", portInfo.name, "mode", "tap");
        dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "link", "set", "dev", portInfo.name, "address", portInfo.mac);

        doCreatePort(bridge, portInfo, owner, "tap", secGroups);
    }

    /**
     * This method must be run on a port before calling ping() or pingIp()
     * @param portName The name of the port used when it was created using createPort()
     * @throws IOException if an IO error occurs with one of the spawned procs
     * @throws InterruptedException because we sleep
     */
    public void preparePortForPing(String portName) throws IOException, InterruptedException {
        String nsName = "ns-" + portName;

        PortInfo portInfo = portInfoByName.get(portName);
        Assert.assertNotNull(portInfo);
        dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "netns", "add", nsName);
        dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "link", "set", portName, "netns", nsName);
        dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "netns", "exec", nsName, "ip", "addr",
                                                                        "add", "dev", portName, portInfo.ip + "/24");
        dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "netns", "exec", nsName, "ip", "link",
                                                                        "set", "dev", portName, "up");
        dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "netns", "exec", nsName, "ip", "route",
                                                                        "add", "default", "via", portInfo.ip);
    }

    /**
     * Ping from one port to the other
     * @param fromPort name of the port to ping from. This is the name you used for createPort.
     * @param toPort name of the port to ping to. This is the name you used for createPort.
     * @throws IOException if an IO error occurs with one of the spawned procs
     * @throws InterruptedException because we sleep
     */
    public void ping(String fromPort, String toPort) throws IOException, InterruptedException {
        PortInfo portInfo = portInfoByName.get(toPort);
        Assert.assertNotNull(portInfo);
        pingIp(fromPort, portInfo.ip);
    }

    /**
     * Ping from one port to an IP address
     * @param fromPort name of the port to ping from. This is the name you used for createPort.
     * @param ip The IP address to ping
     * @throws IOException if an IO error occurs with one of the spawned procs
     * @throws InterruptedException because we sleep
     */
    public void pingIp(String fromPort, String ip) throws IOException, InterruptedException {
        String fromNs = "ns-" + fromPort;
        dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "netns", "exec", fromNs, "ping", "-c", "4", ip);
    }
}

@@ -2523,16 +2523,159 @@
 eption %7B
+%0A        if (dockerOvs.usingExternalDocker()) %7B%0A            super.createPort(bridge, portName, owner, secGroups);%0A            return;%0A        %7D
 %0A%0A      
@@ -3416,32 +3416,110 @@
 ptedException %7B%0A
+        if (dockerOvs.usingExternalDocker()) %7B%0A            return;%0A        %7D%0A%0A
         String n
@@ -4911,32 +4911,110 @@
 ptedException %7B%0A
+        if (dockerOvs.usingExternalDocker()) %7B%0A            return;%0A        %7D%0A%0A
         PortInfo
@@ -5458,32 +5458,32 @@
 e sleep%0A     */%0A
-
     public void 
@@ -5552,32 +5552,110 @@
 ptedException %7B%0A
+        if (dockerOvs.usingExternalDocker()) %7B%0A            return;%0A        %7D%0A%0A
         String f

Fix IT failure when not running DockerOvs docker