| // 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; |
| } |
| } |