// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 com.cloud.hypervisor.hyperv.test;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.naming.ConfigurationException;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.google.common.io.Files;
import com.google.gson.Gson;

import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;

import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.CreateStoragePoolCommand;
import com.cloud.agent.api.DeleteStoragePoolCommand;
import com.cloud.agent.api.GetHostStatsAnswer;
import com.cloud.agent.api.GetHostStatsCommand;
import com.cloud.agent.api.GetStorageStatsAnswer;
import com.cloud.agent.api.GetStorageStatsCommand;
import com.cloud.agent.api.GetVmStatsAnswer;
import com.cloud.agent.api.GetVmStatsCommand;
import com.cloud.agent.api.ModifyStoragePoolCommand;
import com.cloud.agent.api.StartAnswer;
import com.cloud.agent.api.StartCommand;
import com.cloud.agent.api.StartupCommand;
import com.cloud.agent.api.StartupRoutingCommand;
import com.cloud.agent.api.StartupStorageCommand;
import com.cloud.agent.api.StopAnswer;
import com.cloud.agent.api.StopCommand;
import com.cloud.agent.api.storage.CreateAnswer;
import com.cloud.agent.api.storage.CreateCommand;
import com.cloud.agent.api.storage.DestroyCommand;
import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer;
import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.hypervisor.hyperv.resource.HypervDirectConnectResource;
import com.cloud.network.Networks.RouterPrivateIpStrategy;
import com.cloud.serializer.GsonHelper;
import com.cloud.storage.Storage;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.utils.PropertiesUtil;
import com.cloud.utils.exception.CloudRuntimeException;

/**
 * Functional test suit for Hyper-V plugin.
 *
 * mvn clean build -DskipTests=false
 **/
public class HypervDirectConnectResourceTest {

    protected Logger logger = LogManager.getLogger(getClass());

    // TODO: make this a config parameter
    private static final String sampleLegitDiskImageURL = "http://s3-eu-west-1.amazonaws.com/cshv3eu/SmallDisk.vhdx";
    private static final Gson s_gson = GsonHelper.getGson();
    private static final HypervDirectConnectResource s_hypervresource = new HypervDirectConnectResource();

    private static String s_testLocalStoreUUID = "5fe2bad3-d785-394e-9949-89786b8a63d2";
    private static String s_testLocalStorePath = "." + File.separator + "var" + File.separator + "test" + File.separator + "storagepool";
    private static String s_testSecondaryStoreLocalPath = "." + File.separator + "var" + File.separator + "test" + File.separator + "secondary";

    // TODO: differentiate between NFS and HTTP template URLs.
    private static String s_testSampleTemplateUUID = "TestCopiedLocalTemplate.vhdx";
    private static String s_testSampleTemplateURL = s_testSampleTemplateUUID;

    // test volumes are both a minimal size vhdx. Changing the extension to .vhd
    // makes on corrupt.
    private static String s_testSampleVolumeWorkingUUID = "TestVolumeLegit.vhdx";
    private static String s_testSampleVolumeWorkingUUIDNoExt = "TestVolumeLegit";
    private static String s_testSampleVolumeCorruptUUID = "TestVolumeCorrupt.vhd";
    private static String s_testSampleVolumeTempUUID = "TestVolumeTemp.vhdx";
    private static String s_testSampleVolumeWorkingURIJSON;
    private static String s_testSampleVolumeCorruptURIJSON;
    private static String s_testSampleVolumeTempURIJSON;

    private static String s_testSampleTemplateURLJSON;
    private static String s_testLocalStorePathJSON;
    private static String s_agentExecutable;
    private static Process s_agentProc;
    private static String s_testPrimaryDataStoreHost;

    public HypervDirectConnectResourceTest() {
    }

    /**
     * Prep test environment.
     **/
    @Before
    public final void setUp() throws ConfigurationException {
        // Obtain script locations from agent.properties
        final Map<String, Object> params = PropertiesUtil.toMap(loadProperties());
        // Used to create existing StoragePool in preparation for the
        // ModifyStoragePool
        params.put("local.storage.uuid", s_testLocalStoreUUID);

        // Make sure secondary store is available.
        File testSecondarStoreDir = new File(s_testSecondaryStoreLocalPath);
        if (!testSecondarStoreDir.exists()) {
            testSecondarStoreDir.mkdirs();
        }
        Assert.assertTrue("Need to be able to create the folder " + s_testSecondaryStoreLocalPath, testSecondarStoreDir.exists());
        try {
            params.put("local.secondary.storage.path", testSecondarStoreDir.getCanonicalPath());
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            Assert.fail("No canonical path for " + testSecondarStoreDir.getAbsolutePath());
        }

        // Clean up old test files in local storage folder:
        File testPoolDir = new File(s_testLocalStorePath);
        if (!testPoolDir.exists()) {
            testPoolDir.mkdirs();
        }
        Assert.assertTrue("To simulate local file system Storage Pool, " + " you need folder at " + testPoolDir.getPath(),
            testPoolDir.exists() && testPoolDir.isDirectory());
        try {
            s_testLocalStorePath = testPoolDir.getCanonicalPath();
        } catch (IOException e) {
            Assert.fail("No canonical path for " + testPoolDir.getAbsolutePath());
        }
        params.put("local.storage.path", s_testLocalStorePath);

        File testVolWorks = new File(s_testLocalStorePath + File.separator + s_testSampleVolumeWorkingUUID);
        if (!testVolWorks.exists()) {
            ReadableByteChannel rbc;
            try {
                URL sampleLegitDiskImageURL = new URL("http://s3-eu-west-1.amazonaws.com/cshv3eu/SmallDisk.vhdx");
                rbc = Channels.newChannel(sampleLegitDiskImageURL.openStream());
                FileOutputStream fos = new FileOutputStream(testVolWorks);
                fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        Assert.assertTrue("Need to put a sample working virtual disk at " + testVolWorks.getPath(), testVolWorks.exists());
        try {
            s_testSampleVolumeWorkingURIJSON = s_gson.toJson(testVolWorks.getCanonicalPath());
        } catch (IOException e) {
            Assert.fail("No canonical path for " + testPoolDir.getAbsolutePath());
        }

        FilenameFilter vhdsFilt = new FilenameFilter() {
            @Override
            public boolean accept(final File directory, final String fileName) {
                return fileName.endsWith(".vhdx") || fileName.endsWith(".vhd");
            }
        };
        for (File file : testPoolDir.listFiles(vhdsFilt)) {
            if (file.getName().equals(testVolWorks.getName())) {
                continue;
            }
            Assert.assertTrue("Should have deleted file " + file.getPath(), file.delete());
            logger.info("Cleaned up by delete file " + file.getPath());
        }

        s_testSampleVolumeTempURIJSON = createTestDiskImageFromExistingImage(testVolWorks, s_testLocalStorePath, s_testSampleVolumeTempUUID);
        logger.info("Created " + s_testSampleVolumeTempURIJSON);
        s_testSampleVolumeCorruptURIJSON = createTestDiskImageFromExistingImage(testVolWorks, s_testLocalStorePath, s_testSampleVolumeCorruptUUID);
        logger.info("Created " + s_testSampleVolumeCorruptURIJSON);
        createTestDiskImageFromExistingImage(testVolWorks, s_testLocalStorePath, s_testSampleTemplateUUID);
        s_testSampleTemplateURLJSON = s_testSampleTemplateUUID;
        logger.info("Created " + s_testSampleTemplateURLJSON + " in local storage.");

        // Create secondary storage template:
        createTestDiskImageFromExistingImage(testVolWorks, testSecondarStoreDir.getAbsolutePath(), "af39aa7f-2b12-37e1-86d3-e23f2f005101.vhdx");
        logger.info("Created " + "af39aa7f-2b12-37e1-86d3-e23f2f005101.vhdx" + " in secondary (NFS) storage.");

        s_testLocalStorePathJSON = s_gson.toJson(s_testLocalStorePath);

        String agentIp = (String)params.get("ipaddress");
        logger.info("Test using agent IP address " + agentIp);
        params.put("agentIp", agentIp);
        setTestJsonResult(params);
        s_hypervresource.configure("hypervresource", params);
        // Verify sample template is in place storage pool
        logger.info("setUp complete, sample StoragePool at " + s_testLocalStorePathJSON + " sample template at " + s_testSampleTemplateURLJSON);

        s_agentExecutable = (String)params.get("agent.executable");
        s_testPrimaryDataStoreHost = (String)params.get("ipaddress");
        agentCreation();
    }

    private String createTestDiskImageFromExistingImage(final File srcFile, final String dstPath, final String dstFileName) {
        String newFileURIJSON = null;
        File testVolTemp = new File(dstPath + File.separator + dstFileName);
        try {
            Files.copy(srcFile, testVolTemp);
        } catch (IOException e) {
            ; // NOP
        }
        Assert.assertTrue("Should be a temporary file created from the valid volume) at " + testVolTemp.getPath(), testVolTemp.exists());
        try {
            newFileURIJSON = s_gson.toJson(testVolTemp.getCanonicalPath());
        } catch (IOException e) {
            Assert.fail("No file at " + testVolTemp.getAbsolutePath());
        }
        return newFileURIJSON;
    }

    @Test
    public final void testGetVmStatsCommand() {
        // Sample GetVmStatsCommand
        List<String> vmNames = new ArrayList<String>();
        vmNames.add("i-2-11-VM");
        GetVmStatsCommand cmd = new GetVmStatsCommand(vmNames, "1", "localhost");

        s_hypervresource.executeRequest(cmd);
        GetVmStatsAnswer ans = (GetVmStatsAnswer)s_hypervresource.executeRequest(cmd);
        Assert.assertTrue(ans.getDetails(), ans.getResult());
    }

    public final void testStartupCommand() {
        StartupRoutingCommand defaultStartRoutCmd =
            new StartupRoutingCommand(0, 0, 0, 0, null, Hypervisor.HypervisorType.Hyperv, RouterPrivateIpStrategy.HostLocal);

        // Identity within the data centre is decided by CloudStack kernel,
        // and passed via ServerResource.configure()
        defaultStartRoutCmd.setDataCenter("1");
        defaultStartRoutCmd.setPod("1");
        defaultStartRoutCmd.setCluster("1");
        defaultStartRoutCmd.setGuid("1");
        defaultStartRoutCmd.setName("1");
        defaultStartRoutCmd.setPrivateIpAddress("1");
        defaultStartRoutCmd.setStorageIpAddress("1");
        defaultStartRoutCmd.setCpus(12);

        // TODO: does version need to be hard coded.
        defaultStartRoutCmd.setVersion("4.2.0");

        StartupCommand scmd = defaultStartRoutCmd;

        Command[] cmds = {scmd};
        String cmdsStr = s_gson.toJson(cmds);
        logger.debug("Commands[] toJson is " + cmdsStr);

        Command[] result = s_gson.fromJson(cmdsStr, Command[].class);
        logger.debug("Commands[] fromJson is " + s_gson.toJson(result));
        logger.debug("Commands[] first element has type" + result[0].toString());
    }

    // @Test
    public final void testJson() {
        StartupStorageCommand sscmd = null;
        com.cloud.agent.api.StoragePoolInfo pi = new com.cloud.agent.api.StoragePoolInfo("test123", "192.168.0.1", "c:\\", "c:\\", StoragePoolType.Filesystem, 100L, 50L);

        sscmd = new StartupStorageCommand();
        sscmd.setPoolInfo(pi);
        sscmd.setGuid(pi.getUuid());
        sscmd.setDataCenter("foo");
        sscmd.setResourceType(Storage.StorageResourceType.STORAGE_POOL);
        logger.debug("StartupStorageCommand fromJson is " + s_gson.toJson(sscmd));
    }

    @Test
    public final void testBadGetVmStatsCommand() {
        // Sample GetVmStatsCommand
        List<String> vmNames = new ArrayList<String>();
        vmNames.add("FakeVM");
        GetVmStatsCommand vmStatsCmd = new GetVmStatsCommand(vmNames, "1", "localhost");
        GetVmStatsAnswer ans = (GetVmStatsAnswer)s_hypervresource.executeRequest(vmStatsCmd);
        Assert.assertTrue(ans.getDetails(), ans.getResult());
    }

    @Test
    public final void testCreateStoragePoolCommand() {
        String folderName = "." + File.separator + "Dummy";
        StoragePoolVO pool = createTestStoragePoolVO(folderName);

        CreateStoragePoolCommand cmd = new CreateStoragePoolCommand(true, pool);
        logger.debug("TestCreateStoragePoolCommand sending " + s_gson.toJson(cmd));

        Answer ans = s_hypervresource.executeRequest(cmd);
        Assert.assertTrue(ans.getResult());
    }

    @Test
    public final void testModifyStoragePoolCommand() {
        // Create dummy folder
        String folderName = "." + File.separator + "Dummy";
        StoragePoolVO pool = createTestStoragePoolVO(folderName);

        ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, pool, folderName);
        Answer ans = s_hypervresource.executeRequest(cmd);
        Assert.assertTrue(ans.getResult());

        DeleteStoragePoolCommand delCmd = new DeleteStoragePoolCommand(pool, folderName);
        Answer ans2 = s_hypervresource.executeRequest(delCmd);
        Assert.assertTrue(ans2.getResult());
    }

    // Check
    @Test
    public final void testModifyStoragePoolCommand2() {
        // Should return existing pool
        // Create dummy folder
        String folderName = "." + File.separator + "Dummy";
        File folder = new File(folderName);
        if (!folder.exists()) {
            if (!folder.mkdir()) {
                Assert.assertTrue(false);
            }
        }

        // Use same spec for pool
        logger.info("Createing pool at : " + folderName);

        StoragePoolVO pool = new StoragePoolVO(StoragePoolType.Filesystem, "127.0.0.1", -1, folderName);
        pool.setUuid(s_testLocalStoreUUID);

        ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, pool, folderName);
        Answer ans = s_hypervresource.executeRequest(cmd);
        Assert.assertTrue(ans.getResult());

        DeleteStoragePoolCommand delCmd = new DeleteStoragePoolCommand(pool, folderName);
        Answer ans2 = s_hypervresource.executeRequest(delCmd);
        Assert.assertTrue(ans2.getResult());
    }

    public final StoragePoolVO createTestStoragePoolVO(final String folderName) {
        File folder = new File(folderName);
        if (!folder.exists()) {
            if (!folder.mkdir()) {
                Assert.assertTrue(false);
            }
        }

        // Use same spec for pool
        logger.info("Createing pool at : " + folderName);

        StoragePoolVO pool = new StoragePoolVO(StoragePoolType.Filesystem, "127.0.0.1", -1, folderName);
        return pool;
    }

    @Test
    public final void testInitialize() {
        StartupCommand[] startCmds = s_hypervresource.initialize();
        Command[] cmds = new Command[] {startCmds[0], startCmds[1]};
        String result = s_gson.toJson(cmds);
        if (result == null) {
            result = "NULL";
        }
        logger.debug("TestInitialize returned " + result);
        logger.debug("TestInitialize expected " + _setTestJsonResultStr);
        Assert.assertTrue("StartupCommand[] not what we expected", _setTestJsonResultStr.equals(result));
        return;
    }

    @Test
    public final void testPrimaryStorageDownloadCommandHTTP() {
        PrimaryStorageDownloadCommand cmd = samplePrimaryDownloadCommand();
        cmd.setUrl("http://s3-eu-west-1.amazonaws.com/cshv3eu/SmallDisk.vhdx");
        corePrimaryStorageDownloadCommandTestCycle(cmd);
    }

    private void corePrimaryStorageDownloadCommandTestCycle(final PrimaryStorageDownloadCommand cmd) {
        PrimaryStorageDownloadAnswer ans = (PrimaryStorageDownloadAnswer)s_hypervresource.executeRequest(cmd);
        if (!ans.getResult()) {
            logger.error(ans.getDetails());
        } else {
            logger.debug(ans.getDetails());
        }

        Assert.assertTrue(ans.getDetails(), ans.getResult());
        // Test that returned URL works.
        // CreateCommand createCmd = CreateCommandSample();
        // CreateCommand testCreateCmd = new
        // CreateCommand(createCmd.getDiskCharacteristics(),
        // ans.getInstallPath(), createCmd.getPool());
        // CreateAnswer ans2 =
        // (CreateAnswer)s_hypervresource.executeRequest(testCreateCmd);
        // Assert.assertTrue(ans2.getDetails(), ans2.getResult());
    }

    private PrimaryStorageDownloadCommand samplePrimaryDownloadCommand() {
        String cmdJson =
            "{\"localPath\":" + s_testLocalStorePathJSON + ",\"poolUuid\":\"" + s_testLocalStoreUUID + "\",\"poolId\":201," + "\"secondaryStorageUrl\":" +
                "\"nfs://10.70.176.36/mnt/cshv3/secondarystorage\"," + "\"primaryStorageUrl\":" + "\"nfs://10.70.176.29E:\\\\Disks\\\\Disks\"," + "\"url\":" +
                "\"nfs://10.70.176.36/mnt/cshv3/secondarystorage/template/tmpl//2/204//af39aa7f-2b12-37e1-86d3-e23f2f005101.vhdx\"," +
                "\"format\":\"VHDX\",\"accountId\":2," + "\"name\":" + "\"204-2-5a1db1ac-932b-3e7e-a0e8-5684c72cb862\"" + ",\"contextMap\":{},\"wait\":10800}";
        PrimaryStorageDownloadCommand cmd = s_gson.fromJson(cmdJson, PrimaryStorageDownloadCommand.class);
        return cmd;
    }

    public final CreateCommand createCommandSample() {
        String sample =
            "{\"volId\":17,\"pool\":{\"id\":201,\"uuid\":\"" + s_testLocalStoreUUID + "\",\"host\":\"10.70.176.29\"" + ",\"path\":" + s_testLocalStorePathJSON +
                ",\"port\":0,\"type\":\"Filesystem\"}," + "\"diskCharacteristics\":{\"size\":0," + "\"tags\":[],\"type\":\"ROOT\",\"name\":\"ROOT-15\"," +
                "\"useLocalStorage\":true,\"recreatable\":true," + "\"diskOfferingId\":11," + "\"volumeId\":17,\"hyperType\":\"Hyperv\"}," + "\"templateUrl\":" +
                s_testSampleTemplateURLJSON + ",\"wait\":0}";
        CreateCommand cmd = s_gson.fromJson(sample, CreateCommand.class);
        return cmd;
    }

    @Test
    public final void testCreateCommand() {
        String sample =
            "{\"volId\":10,\"pool\":{\"id\":201,\"uuid\":\"" + s_testLocalStoreUUID + "\",\"host\":\"10.70.176.29\"" + ",\"path\":" + s_testLocalStorePathJSON +
                ",\"port\":0,\"type\":\"Filesystem\"}," + "\"diskCharacteristics\":{\"size\":0," + "\"tags\":[],\"type\":\"ROOT\",\"name\":\"ROOT-9\"," +
                "\"useLocalStorage\":true,\"recreatable\":true," + "\"diskOfferingId\":11," + "\"volumeId\":10,\"hyperType\":\"Hyperv\"}," + "\"templateUrl\":" +
                s_testSampleTemplateURLJSON + ",\"contextMap\":{},\"wait\":0}";

        File destDir = new File(s_testLocalStorePath);
        Assert.assertTrue(destDir.isDirectory());
        File testSampleTemplateURLFile = new File(s_testLocalStorePath + File.separator + s_gson.fromJson(s_testSampleTemplateURLJSON, String.class));
        Assert.assertTrue("The template that create should make" + " volumes from is missing from path " + testSampleTemplateURLFile.getPath(),
            testSampleTemplateURLFile.exists());

        int fileCount = destDir.listFiles().length;
        logger.debug(" test local store has " + fileCount + "files");
        // Test requires there to be a template at the tempalteUrl, which is its
        // location in the local file system.
        CreateCommand cmd = s_gson.fromJson(sample, CreateCommand.class);
        CreateAnswer ans = (CreateAnswer)s_hypervresource.executeRequest(cmd);
        Assert.assertTrue(ans.getDetails(), ans.getResult());
        Assert.assertTrue("CreateCommand should add a file to the folder", fileCount + 1 == destDir.listFiles().length);
        File newFile = new File(ans.getVolume().getPath());
        Assert.assertTrue("The new file should have a size greater than zero", newFile.length() > 0);
        newFile.delete();
    }

    @Test
    public final void testStartCommandCorruptDiskImage() {
        String sampleStart =
            "{\"vm\":{\"id\":16,\"name\":\"i-3-17-VM\"," + "\"type\":\"User\",\"cpus\":1,\"speed\":500," + "\"minRam\":536870912,\"maxRam\":536870912,"
                + "\"arch\":\"x86_64\"," + "\"os\":\"CentOS 6.0 (64-bit)\"," + "\"bootArgs\":\"\",\"rebootOnCrash\":false," + "\"enableHA\":false,"
                + "\"limitCpuUse\":false," + "\"vncPassword\":\"31f82f29aff646eb\"," + "\"params\":{}," + "\"uuid\":\"8b030b6a-0243-440a-8cc5-45d08815ca11\""
                + ",\"disks\":[" + "{\"id\":18,\"name\":\"" +
                s_testSampleVolumeCorruptUUID +
                "\"," +
                "\"mountPoint\":" +
                s_testSampleVolumeCorruptURIJSON +
                "," +
                "\"path\":" +
                s_testSampleVolumeCorruptURIJSON +
                ",\"size\":0," +
                "\"type\":\"ROOT\"," +
                "\"storagePoolType\":\"Filesystem\"," +
                "\"storagePoolUuid\":\"" +
                s_testLocalStoreUUID +
                "\"" +
                ",\"deviceId\":0}," +
                "{\"id\":16,\"name\":\"Hyper-V Sample2\",\"size\":0," +
                "\"type\":\"ISO\",\"storagePoolType\":\"ISO\"," +
                "\"deviceId\":3}]," +
                "\"nics\":[" +
                "{\"deviceId\":0,\"networkRateMbps\":100," +
                "\"defaultNic\":true," +
                "\"uuid\":\"99cb4813-23af-428c-a87a-2d1899be4f4b\"," +
                "\"ip\":\"10.1.1.67\",\"netmask\":\"255.255.255.0\"," +
                "\"gateway\":\"10.1.1.1\"," +
                "\"mac\":\"02:00:51:2c:00:0e\",\"dns1\":\"4.4.4.4\"," +
                "\"broadcastType\":\"Vlan\",\"type\":\"Guest\"," +
                "\"broadcastUri\":\"vlan://261\"," +
                "\"isolationUri\":\"vlan://261\"," +
                "\"isSecurityGroupEnabled\":false}" + "]},\"contextMap\":{},\"wait\":0}";

        StartCommand cmd = s_gson.fromJson(sampleStart, StartCommand.class);
        StartAnswer ans = (StartAnswer)s_hypervresource.executeRequest(cmd);
        Assert.assertFalse(ans.getDetails(), ans.getResult());
    }

    @Test
    public final void testStartStopCommand() {
        String sample = getSampleStartCommand();
        StartAnswer sans = simpleVmStart(sample);
        Assert.assertTrue(sans.getDetails(), sans.getResult());
        StopAnswer stopAns = simpleVmStop();
        Assert.assertTrue(stopAns.getDetails(), stopAns.getResult());
    }

    @Test
    public final void testStartStartCommand() {
        String sample = getSampleStartCommand();

        StartAnswer sans = simpleVmStart(sample);
        Assert.assertTrue(sans.getDetails(), sans.getResult());
        simpleVmStart(sample);

        StopAnswer ans = simpleVmStop();
        Assert.assertTrue(ans.getDetails(), ans.getResult());
    }

    private StopAnswer simpleVmStop() {
        String sampleStop = "{\"isProxy\":false,\"vmName\":\"i-2-17-VM\"," + "\"contextMap\":{},\"wait\":0}";
        StopCommand cmd = s_gson.fromJson(sampleStop, StopCommand.class);
        StopAnswer ans = (StopAnswer)s_hypervresource.executeRequest(cmd);
        return ans;
    }

    private StartAnswer simpleVmStart(final String sample) {
        StartCommand cmd = s_gson.fromJson(sample, StartCommand.class);
        logger.info("StartCommand sample " + s_gson.toJson(cmd));
        StartAnswer ans = (StartAnswer)s_hypervresource.executeRequest(cmd);
        return ans;
    }

    @Test
    public final void testDestroyCommand() {
        // TODO: how does the command vary when we are only deleting a vm versus
        // deleting a volume?

//        String sample2 = "{\"volume\":" + getSampleVolumeObjectTO() + "}";

        String sample2 =
            "{\"volume\":{\"name\":\"" + s_testSampleVolumeWorkingUUIDNoExt + "\",\"storagePoolType\":\"Filesystem\"," + "\"mountPoint\":" + s_testLocalStorePathJSON +
                ",\"path\":" + s_testSampleVolumeTempURIJSON + ",\"storagePoolUuid\":\"" + s_testLocalStoreUUID + "\"," + "\"type\":\"ROOT\",\"id\":9,\"size\":0}}";
        DestroyCommand cmd = s_gson.fromJson(sample2, DestroyCommand.class);
        Answer ans = s_hypervresource.executeRequest(cmd);
        Assert.assertTrue(ans.getDetails(), ans.getResult());
    }

    @Test
    public final void testGetStorageStatsCommand() {
        // TODO: Update sample data to unsure it is using correct info.
        String sample =
            "{\"id\":\"" + s_testLocalStoreUUID + "\",\"localPath\":" + s_testLocalStorePathJSON + "," + "\"pooltype\":\"Filesystem\"," + "\"contextMap\":{},\"wait\":0}";

        logger.info("Sample JSON: " + sample);

        GetStorageStatsCommand cmd = s_gson.fromJson(sample, GetStorageStatsCommand.class);
        s_hypervresource.executeRequest(cmd);
        GetStorageStatsAnswer ans = (GetStorageStatsAnswer)s_hypervresource.executeRequest(cmd);
        Assert.assertTrue(ans.getDetails(), ans.getResult());
        Assert.assertTrue(ans.getByteUsed() != ans.getCapacityBytes());
    }

    @After
    public final void agentTerminate() {
        // Write carriage return line feed to script's stdin
        OutputStream scriptInput = s_agentProc.getOutputStream();
        OutputStreamWriter siw = new OutputStreamWriter(scriptInput);
        try {
            BufferedWriter writer = new BufferedWriter(siw);
            writer.write("\r\n");
            writer.flush();
            writer.close();
        } catch (IOException ex) {
            logger.debug("Error closing agent at " + s_agentExecutable + " message " + ex.getMessage());
        }
    }

    private void agentCreation() {
        // Launch script
        try {
            List<String> exeArgs = new ArrayList<String>();

            exeArgs.add(s_agentExecutable);
            exeArgs.add("--console");

            // when we launch from the shell, we need to use Cygwin's path to
            // the exe
            ProcessBuilder builder = new ProcessBuilder(exeArgs);
            builder.redirectErrorStream(true);
            s_agentProc = builder.start();
            Thread.sleep(4000);
        } catch (Exception ex) {
            logger.debug("Error calling starting aget at " + s_agentExecutable + " message " + ex.getMessage());
        }
    }

    @Test
    public final void testGetHostStatsCommand() {
        // Arrange
        long hostIdVal = 123;
        GetHostStatsCommand cmd = new GetHostStatsCommand("1", "testHost", hostIdVal);

        // Act
        GetHostStatsAnswer ans = (GetHostStatsAnswer)s_hypervresource.executeRequest(cmd);

        // Assert
        Assert.assertTrue(ans.getResult());
        Assert.assertTrue(0.0 < ans.getTotalMemoryKBs());
        Assert.assertTrue(0.0 < ans.getFreeMemoryKBs());
        Assert.assertTrue(0.0 <= ans.getNetworkReadKBs());
        Assert.assertTrue(0.0 <= ans.getNetworkWriteKBs());
        Assert.assertTrue(0.0 <= ans.getCpuUtilization());
        Assert.assertTrue(100 >= ans.getCpuUtilization());
        Assert.assertTrue(ans.getEntityType().equals("host"));
        Assert.assertTrue(ans.getDetails() == null);
    }

    public Properties loadProperties() throws ConfigurationException {
        Properties properties = new Properties();
        final File file = PropertiesUtil.findConfigFile("agent.properties");
        if (file == null) {
            throw new ConfigurationException("Unable to find agent.properties.");
        }

        logger.info("agent.properties found at " + file.getAbsolutePath());

        try {
            properties.load(new FileInputStream(file));
        } catch (final FileNotFoundException ex) {
            throw new CloudRuntimeException("Cannot find the file: " + file.getAbsolutePath(), ex);
        } catch (final IOException ex) {
            throw new CloudRuntimeException("IOException in reading " + file.getAbsolutePath(), ex);
        }
        return properties;
    }

    private String _setTestJsonResultStr = null;

    public final void setTestJsonResult(final Map<String, Object> params) {
        File dir = new File((String)params.get("DefaultVirtualDiskFolder"));
        long usableCapacity = dir.getUsableSpace() - 4294967296L;
        long totalSpace = dir.getTotalSpace() - 4294967296L;

        // TODO: add test to verify capacity statistics change
        _setTestJsonResultStr =
            String.format("[{\"com.cloud.agent.api.StartupRoutingCommand\":{" + "\"cpus\":%s," + "\"speed\":%s," + "\"memory\":%s," + "\"dom0MinMemory\":%s,"
                + "\"poolSync\":false," + "\"vms\":{}," + "\"caps\":\"hvm\"," + "\"hypervisorType\":\"Hyperv\"," + "\"hostDetails\":{"
                + "\"com.cloud.network.Networks.RouterPrivateIpStrategy\":" + "\"HostLocal\"" + "}," + "\"type\":\"Routing\"," + "\"dataCenter\":%s," + "\"pod\":%s,"
                + "\"cluster\":%s," + "\"guid\":\"16f85622-4508-415e-b13a-49a39bb14e4d\"," + "\"name\":\"hypervresource\"," + "\"version\":\"4.2.0\","
                + "\"privateIpAddress\":%s," + "\"privateMacAddress\":%s," + "\"privateNetmask\":%s," + "\"storageIpAddress\":%s," + "\"storageNetmask\":%s,"
                + "\"storageMacAddress\":%s," + "\"gatewayIpAddress\":%s," + "\"contextMap\":{}," + "\"wait\":0" + "}},"
                + "{\"com.cloud.agent.api.StartupStorageCommand\":{" + "\"totalSize\":0," + "\"poolInfo\":{" + "\"uuid\":\"16f85622-4508-415e-b13a-49a39bb14e4d\","
                + "\"host\":%s," + "\"localPath\":%s," + "\"hostPath\":%s," + "\"poolType\":\"Filesystem\"," + "\"capacityBytes\":%s," + "\"availableBytes\":%s" + "},"
                + "\"resourceType\":\"STORAGE_POOL\"," + "\"hostDetails\":{}," + "\"type\":\"Storage\"," + "\"dataCenter\":\"1\"," + "\"guid\":"
                + "\"16f85622-4508-415e-b13a-49a39bb14e4d\"," + "\"contextMap\":{}," + "\"wait\":0" + "}}]", params.get("TestCoreCount"), params.get("TestCoreMhz"),
                params.get("TestMemoryMb"), params.get("TestDom0MinMemoryMb"), s_gson.toJson(params.get("zone")), s_gson.toJson(params.get("pod")),
                s_gson.toJson(params.get("cluster")), s_gson.toJson(params.get("ipaddress")), s_gson.toJson(params.get("private.mac.address")),
                s_gson.toJson(params.get("private.ip.netmask")), s_gson.toJson(params.get("ipaddress")), s_gson.toJson(params.get("private.ip.netmask")),
                s_gson.toJson(params.get("private.mac.address")), s_gson.toJson(params.get("gateway.ip.address")), s_gson.toJson(params.get("ipaddress")),
                s_gson.toJson(params.get("DefaultVirtualDiskFolder")), s_gson.toJson(params.get("DefaultVirtualDiskFolder")), s_gson.toJson(totalSpace),
                s_gson.toJson(usableCapacity));
    }

    public static String getSamplePrimaryDataStoreInfo() {
        String samplePrimaryDataStoreInfo =
            "{\"org.apache.cloudstack.storage.to.PrimaryDataStoreTO\":" + "{\"uuid\":\"" + s_testLocalStoreUUID + "\"," + "\"id\":201," + "\"host\":\"" +
                s_testPrimaryDataStoreHost + "\"," + "\"type\":\"Filesystem\"," // Not used in
                                                                                // PrimaryDataStoreTO
                + "\"poolType\":\"Filesystem\"," // Not used in
                // PrimaryDataStoreTO
                + "\"path\":" + s_testLocalStorePathJSON + "," + "\"port\":0}" + "}";
        return samplePrimaryDataStoreInfo;
    }

    public static String getSampleVolumeObjectTO() {
        String sampleVolumeObjectTO =
            "{\"org.apache.cloudstack.storage.to.VolumeObjectTO\":" + "{\"uuid\":\"19ae8e67-cb2c-4ab4-901e-e0b864272b59\"," + "\"volumeType\":\"ROOT\"," +
                "\"format\":\"VHDX\"," + "\"dataStore\":" + getSamplePrimaryDataStoreInfo() + "," + "\"name\":\"" + s_testSampleVolumeWorkingUUIDNoExt + "\"," +
                "\"size\":52428800," + "\"volumeId\":10,"
                // "\"vmName\":\"i-3-5-VM\"," + // TODO: required?
                + "\"accountId\":3,\"id\":10}" + "}"; // end of destTO
        return sampleVolumeObjectTO;
    }

    public static String getSampleStartCommand() {
        String sample =
            "{\"vm\":{\"id\":17,\"name\":\"i-2-17-VM\"," + "\"type\":\"User\",\"cpus\":1,\"speed\":500," + "\"minRam\":536870912,\"maxRam\":536870912," +
                "\"arch\":\"x86_64\"," + "\"os\":\"CentOS 6.0 (64-bit)\"," + "\"bootArgs\":\"\",\"rebootOnCrash\":false," + "\"enableHA\":false,\"limitCpuUse\":false," +
                "\"vncPassword\":\"31f82f29aff646eb\"," + "\"params\":{}," + "\"uuid\":\"8b030b6a-0243-440a-8cc5-45d08815ca11\"" + ",\"disks\":[" + "{\"data\":" +
                getSampleVolumeObjectTO() + ",\"diskSeq\":0,\"type\":\"ROOT\"}," + "{\"diskSeq\":1,\"type\":\"ISO\"}" + "]," + "\"nics\":[" +
                "{\"deviceId\":0,\"networkRateMbps\":100," + "\"defaultNic\":true," + "\"uuid\":\"99cb4813-23af-428c-a87a-2d1899be4f4b\"," + "\"ip\":\"10.1.1.67\"," +
                "\"netmask\":\"255.255.255.0\"," + "\"gateway\":\"10.1.1.1\"," + "\"mac\":\"02:00:51:2c:00:0e\",\"dns1\":\"4.4.4.4\"," +
                "\"broadcastType\":\"Vlan\",\"type\":\"Guest\"," + "\"broadcastUri\":\"vlan://261\"," + "\"isolationUri\":\"vlan://261\"," +
                "\"isSecurityGroupEnabled\":false}" + "]},\"contextMap\":{},\"wait\":0}";
        return sample;
    }
}
