Updated Branches: refs/heads/master f25354dca -> a196fbc83
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/f9f196a4/plugins/hypervisors/hyperv/test/com/cloud/hypervisor/hyperv/test/HypervDirectConnectResourceTest.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/hyperv/test/com/cloud/hypervisor/hyperv/test/HypervDirectConnectResourceTest.java b/plugins/hypervisors/hyperv/test/com/cloud/hypervisor/hyperv/test/HypervDirectConnectResourceTest.java new file mode 100644 index 0000000..fddd23a --- /dev/null +++ b/plugins/hypervisors/hyperv/test/com/cloud/hypervisor/hyperv/test/HypervDirectConnectResourceTest.java @@ -0,0 +1,938 @@ +// 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.nio.file.Files; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +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.StartupRoutingCommand.VmState; +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 { + + private static final Logger s_logger = Logger + .getLogger(HypervDirectConnectResourceTest.class.getName()); + + // 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()); + s_logger.info("Cleaned up by delete file " + file.getPath()); + } + + s_testSampleVolumeTempURIJSON = + createTestDiskImageFromExistingImage(testVolWorks, + s_testLocalStorePath, s_testSampleVolumeTempUUID); + s_logger.info("Created " + s_testSampleVolumeTempURIJSON); + s_testSampleVolumeCorruptURIJSON = + createTestDiskImageFromExistingImage(testVolWorks, + s_testLocalStorePath, s_testSampleVolumeCorruptUUID); + s_logger.info("Created " + s_testSampleVolumeCorruptURIJSON); + createTestDiskImageFromExistingImage(testVolWorks, + s_testLocalStorePath, + s_testSampleTemplateUUID); + s_testSampleTemplateURLJSON = s_testSampleTemplateUUID; + s_logger.info("Created " + s_testSampleTemplateURLJSON + + " in local storage."); + + // Create secondary storage template: + createTestDiskImageFromExistingImage(testVolWorks, + testSecondarStoreDir.getAbsolutePath(), + "af39aa7f-2b12-37e1-86d3-e23f2f005101.vhdx"); + s_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"); + s_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 + s_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.toPath(), testVolTemp.toPath()); + } 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, + new HashMap<String, VmState>()); + + // 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); + s_logger.debug("Commands[] toJson is " + cmdsStr); + + Command[] result = s_gson.fromJson(cmdsStr, Command[].class); + s_logger.debug("Commands[] fromJson is " + s_gson.toJson(result)); + s_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); + s_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); + s_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 + s_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 + s_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"; + } + s_logger.debug("TestInitialize returned " + result); + s_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()) { + s_logger.error(ans.getDetails()); + } else { + s_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; + s_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); + s_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 = (Answer) 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}"; + + s_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) { + s_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) { + s_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 static 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."); + } + + s_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((String) params.get("zone")), + s_gson.toJson((String) params.get("pod")), + s_gson.toJson((String) params.get("cluster")), + s_gson.toJson((String) params.get("ipaddress")), + s_gson.toJson((String) params + .get("private.mac.address")), + s_gson.toJson((String) params.get( + "private.ip.netmask")), + s_gson.toJson((String) params.get("ipaddress")), + s_gson.toJson((String) params.get( + "private.ip.netmask")), + s_gson.toJson((String) params + .get("private.mac.address")), + s_gson.toJson((String) params.get( + "gateway.ip.address")), + s_gson.toJson((String) params.get("ipaddress")), + s_gson.toJson((String) params + .get("DefaultVirtualDiskFolder")), + s_gson.toJson((String) 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; + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/f9f196a4/plugins/pom.xml ---------------------------------------------------------------------- diff --git a/plugins/pom.xml b/plugins/pom.xml index e33ebd8..a707719 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -40,6 +40,7 @@ <module>event-bus/rabbitmq</module> <module>hypervisors/baremetal</module> <module>hypervisors/ucs</module> + <module>hypervisors/hyperv</module> <module>network-elements/elastic-loadbalancer</module> <module>network-elements/ovs</module> <module>network-elements/juniper-contrail</module> @@ -120,6 +121,17 @@ </modules> </profile> <profile> + <id>hyperV</id> + <activation> + <property> + <name>hyperV</name> + </property> + </activation> + <modules> + <module>hypervisors/hyperv</module> + </modules> + </profile> + <profile> <id>f5</id> <activation> <property> http://git-wip-us.apache.org/repos/asf/cloudstack/blob/f9f196a4/server/src/com/cloud/ha/HighAvailabilityManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/ha/HighAvailabilityManagerImpl.java b/server/src/com/cloud/ha/HighAvailabilityManagerImpl.java index 59337c0..d6feaf3 100755 --- a/server/src/com/cloud/ha/HighAvailabilityManagerImpl.java +++ b/server/src/com/cloud/ha/HighAvailabilityManagerImpl.java @@ -206,8 +206,8 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements HighAvai return; } - if(host.getHypervisorType() == HypervisorType.VMware) { - s_logger.info("Don't restart for VMs on host " + host.getId() + " as the host is VMware host"); + if(host.getHypervisorType() == HypervisorType.VMware || host.getHypervisorType() == HypervisorType.Hyperv) { + s_logger.info("Don't restart for VMs on host " + host.getId() + " as the host is VMware host or on Hyperv Host"); return; } @@ -302,6 +302,11 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements HighAvai return; } + if(vm.getHypervisorType() == HypervisorType.VMware || vm.getHypervisorType() == HypervisorType.Hyperv) { + s_logger.info("Skip HA for VMware VM or Hyperv VM" + vm.getInstanceName()); + return; + } + if (!investigate) { if (s_logger.isDebugEnabled()) { s_logger.debug("VM does not require investigation so I'm marking it as Stopped: " + vm.toString()); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/f9f196a4/server/src/com/cloud/network/guru/ControlNetworkGuru.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/network/guru/ControlNetworkGuru.java b/server/src/com/cloud/network/guru/ControlNetworkGuru.java index 1943e16..a5b7c88 100755 --- a/server/src/com/cloud/network/guru/ControlNetworkGuru.java +++ b/server/src/com/cloud/network/guru/ControlNetworkGuru.java @@ -133,7 +133,9 @@ public class ControlNetworkGuru extends PodBasedNetworkGuru implements NetworkGu InsufficientAddressCapacityException { assert nic.getTrafficType() == TrafficType.Control; - if (dest.getHost().getHypervisorType() == HypervisorType.VMware && isRouterVm(vm)) { + // we have to get management/private ip for the control nic for vmware/hyperv due ssh issues. + HypervisorType hType = dest.getHost().getHypervisorType(); + if ( ( (hType == HypervisorType.VMware) || (hType == HypervisorType.Hyperv) )&& isRouterVm(vm)) { if(dest.getDataCenter().getNetworkType() != NetworkType.Basic) { super.reserve(nic, config, vm, dest, context);