// 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. | |
using System; | |
using CloudStack.Plugin.WmiWrappers.ROOT.VIRTUALIZATION; | |
using System.Management; | |
using Newtonsoft.Json.Linq; | |
using Newtonsoft.Json; | |
using System.IO; | |
using log4net; | |
using HypervResource; | |
using CloudStack.Plugin.AgentShell; | |
using System.Collections.Generic; | |
using System.Xml; | |
using Xunit; | |
namespace ServerResource.Tests | |
{ | |
public class HypervResourceControllerTest | |
{ | |
protected static string testCifsUrl = AgentSettings.Default.testCifsUrl; | |
protected static string testCifsPath = AgentSettings.Default.testCifsPath; | |
protected static String testPrimaryDataStoreHost = HypervResourceController.config.StorageIpAddress; | |
protected static String testS3TemplateName = AgentSettings.Default.testS3TemplateName; | |
protected static String testCifsTemplateName = AgentSettings.Default.testS3TemplateName; | |
protected static String testSystemVMTemplateName = AgentSettings.Default.testSystemVMTemplateName; | |
protected static String testSystemVMTemplateNameNoExt = AgentSettings.Default.testSystemVMTemplateNameNoExt; | |
protected static String testLocalStoreUUID = "5fe2bad3-d785-394e-9949-89786b8a63d2"; | |
protected static String testLocalStorePath = Path.Combine(AgentSettings.Default.hyperv_plugin_root, "var", "test", "storagepool"); | |
protected static String testSecondaryStoreLocalPath = Path.Combine(AgentSettings.Default.hyperv_plugin_root, "var", "test", "secondary"); | |
// TODO: differentiate between NFS and HTTP template URLs. | |
protected static String testSampleTemplateUUID = "TestCopiedLocalTemplate.vhdx"; | |
protected static String testSampleTemplateURL = testSampleTemplateUUID; | |
// test volumes are both a minimal size vhdx. Changing the extension to .vhd makes on corrupt. | |
protected static String testSampleVolumeWorkingUUID = "TestVolumeLegit.vhdx"; | |
protected static String testSampleVolumeCorruptUUID = "TestVolumeCorrupt.vhd"; | |
protected static String testSampleVolumeTempUUID = "TestVolumeTemp.vhdx"; | |
protected static String testSampleVolumeTempUUIDNoExt = "TestVolumeTemp"; | |
protected static String testSampleVolumeWorkingURIJSON; | |
protected static String testSampleVolumeCorruptURIJSON; | |
protected static String testSampleVolumeTempURIJSON; | |
protected static String testSampleTemplateURLJSON; | |
protected static String testLocalStorePathJSON; | |
protected static WmiCallsV2 wmiCallsV2 = new WmiCallsV2(); | |
private static ILog s_logger = LogManager.GetLogger(typeof(HypervResourceControllerTest)); | |
/// <summary> | |
/// Test WmiCalls to which incoming HTTP POST requests are dispatched. | |
/// | |
/// TODO: revise beyond first approximation | |
/// First approximation is a quick port of the existing Java tests for Hyper-V server resource. | |
/// A second approximation would use the AgentShell settings files directly. | |
/// A third approximation would look to invoke ServerResource methods via an HTTP request | |
/// </summary> | |
public HypervResourceControllerTest() | |
{ | |
AgentService.ConfigServerResource(); | |
HypervResourceController.config.PrivateMacAddress = AgentSettings.Default.private_mac_address; | |
HypervResourceController.config.PrivateNetmask = AgentSettings.Default.private_ip_netmask; | |
HypervResourceController.config.StorageIpAddress = HypervResourceController.config.PrivateIpAddress; | |
HypervResourceController.config.StorageMacAddress = HypervResourceController.config.PrivateMacAddress; | |
HypervResourceController.config.StorageNetmask = HypervResourceController.config.PrivateNetmask; | |
// Used to create existing StoragePool in preparation for the ModifyStoragePool | |
testLocalStoreUUID = AgentSettings.Default.local_storage_uuid.ToString(); | |
// Make sure secondary store is available. | |
string fullPath = Path.GetFullPath(testSecondaryStoreLocalPath); | |
s_logger.Info("Test secondary storage in " + fullPath); | |
DirectoryInfo testSecondarStoreDir = new DirectoryInfo(fullPath); | |
if (!testSecondarStoreDir.Exists) | |
{ | |
try | |
{ | |
testSecondarStoreDir.Create(); | |
} | |
catch (System.IO.IOException ex) | |
{ | |
throw new NotImplementedException("Need to be able to create the folder " + testSecondarStoreDir.FullName + " failed due to " + ex.Message); | |
} | |
} | |
// Convert to secondary storage string to canonical path | |
testSecondaryStoreLocalPath = testSecondarStoreDir.FullName; | |
AgentSettings.Default.local_secondary_storage_path = testSecondaryStoreLocalPath; | |
// Make sure local primary storage is available | |
DirectoryInfo testPoolDir = new DirectoryInfo(testLocalStorePath); | |
Assert.True(testPoolDir.Exists, "To simulate local file system Storage Pool, you need folder at " + testPoolDir.FullName); | |
// Convert to local primary storage string to canonical path | |
testLocalStorePath = testPoolDir.FullName; | |
AgentSettings.Default.local_storage_path = testLocalStorePath; | |
// Clean up old test files in local storage folder | |
FileInfo testVolWorks = new FileInfo(Path.Combine(testLocalStorePath, testSampleVolumeWorkingUUID)); | |
Assert.True(testVolWorks.Exists, "Create a working virtual disk at " + testVolWorks.FullName); | |
// Delete all temporary files in local folder save the testVolWorks | |
foreach (var file in testPoolDir.GetFiles()) | |
{ | |
if (file.FullName == testVolWorks.FullName) | |
{ | |
continue; | |
} | |
file.Delete(); | |
file.Refresh(); | |
Assert.False(file.Exists, "removed file from previous test called " + file.FullName); | |
} | |
// Recreate starting point files for test, and record JSON encoded paths for each ... | |
testSampleVolumeTempURIJSON = CreateTestDiskImageFromExistingImage(testVolWorks, testLocalStorePath, testSampleVolumeTempUUID); | |
s_logger.Info("Created " + testSampleVolumeTempURIJSON); | |
testSampleVolumeCorruptURIJSON = CreateTestDiskImageFromExistingImage(testVolWorks, testLocalStorePath, testSampleVolumeCorruptUUID); | |
s_logger.Info("Created " + testSampleVolumeCorruptURIJSON); | |
CreateTestDiskImageFromExistingImage(testVolWorks, testLocalStorePath, testSampleTemplateUUID); | |
testSampleTemplateURLJSON = JsonConvert.SerializeObject(testSampleTemplateUUID); | |
s_logger.Info("Created " + testSampleTemplateURLJSON + " in local storage."); | |
// ... including a secondary storage template: | |
CreateTestDiskImageFromExistingImage(testVolWorks, testSecondarStoreDir.FullName, "af39aa7f-2b12-37e1-86d3-e23f2f005101.vhdx"); | |
s_logger.Info("Created " + "af39aa7f-2b12-37e1-86d3-e23f2f005101.vhdx" + " in secondary (NFS) storage."); | |
// Capture other JSON encoded paths | |
testSampleVolumeWorkingURIJSON = Newtonsoft.Json.JsonConvert.SerializeObject(testVolWorks.FullName); | |
testLocalStorePathJSON = JsonConvert.SerializeObject(testLocalStorePath); | |
// TODO: may need to initialise the server resource in future. | |
// s_hypervresource.initialize(); | |
// Verify sample template is in place storage pool | |
s_logger.Info("setUp complete, sample StoragePool at " + testLocalStorePathJSON | |
+ " sample template at " + testSampleTemplateURLJSON); | |
} | |
private String CreateTestDiskImageFromExistingImage(FileInfo srcFile, | |
String dstPath, | |
String dstFileName) | |
{ | |
var newFullname = Path.Combine(dstPath, dstFileName); | |
var newFileInfo = new FileInfo(newFullname); | |
if (!newFileInfo.Exists) | |
{ | |
newFileInfo = srcFile.CopyTo(newFullname); | |
} | |
newFileInfo.Refresh(); | |
Assert.True(newFileInfo.Exists, "Attempted to create " + newFullname + " from " + newFileInfo.FullName); | |
return JsonConvert.SerializeObject(newFileInfo.FullName); | |
} | |
[Fact(Skip="these are functional tests")] | |
public void TestPrimaryStorageDownloadCommandHTTP() | |
{ | |
string downloadURI = "https://s3-eu-west-1.amazonaws.com/cshv3eu/SmallDisk.vhdx"; | |
corePrimaryStorageDownloadCommandTestCycle(downloadURI); | |
} | |
private void corePrimaryStorageDownloadCommandTestCycle(string downloadURI) | |
{ | |
// Arrange | |
HypervResourceController rsrcServer = new HypervResourceController(); | |
dynamic jsonPSDCmd = JsonConvert.DeserializeObject(samplePrimaryDownloadCommand()); | |
jsonPSDCmd.url = downloadURI; | |
// Act | |
dynamic jsonResult = rsrcServer.PrimaryStorageDownloadCommand(jsonPSDCmd); | |
// Assert | |
JObject ansAsProperty = jsonResult[0]; | |
dynamic ans = ansAsProperty.GetValue(CloudStackTypes.PrimaryStorageDownloadAnswer); | |
Assert.True((bool)ans.result, "PrimaryStorageDownloadCommand did not succeed " + ans.details); | |
// Test that URL of downloaded template works for file creation. | |
dynamic jsonCreateCmd = JsonConvert.DeserializeObject(CreateCommandSample()); | |
jsonCreateCmd.templateUrl = ans.installPath; | |
dynamic jsonAns2 = rsrcServer.CreateCommand(jsonCreateCmd); | |
JObject ansAsProperty2 = jsonAns2[0]; | |
dynamic ans2 = ansAsProperty2.GetValue(CloudStackTypes.CreateAnswer); | |
Assert.True((bool)ans2.result, (string)ans2.details); | |
FileInfo newFile = new FileInfo((string)ans2.volume.path); | |
Assert.True(newFile.Length > 0, "The new file should have a size greater than zero"); | |
newFile.Delete(); | |
} | |
private string samplePrimaryDownloadCommand() | |
{ | |
String cmdJson = "{\"localPath\":" + testLocalStorePathJSON + | |
",\"poolUuid\":\"" + testLocalStoreUUID + "\",\"poolId\":201," + | |
"\"secondaryStorageUrl\":\"nfs://10.70.176.36/mnt/cshv3/secondarystorage\"," + | |
"\"primaryStorageUrl\":\"nfs://" + HypervResourceController.config.StorageIpAddress + "E:\\\\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}"; | |
return cmdJson; | |
} | |
public string CreateCommandSample() | |
{ | |
String sample = "{\"volId\":17,\"pool\":{\"id\":201,\"uuid\":\"" + testLocalStoreUUID + "\",\"host\":\"" + HypervResourceController.config.StorageIpAddress + "\"" + | |
",\"path\":" + 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\":" + testSampleTemplateURLJSON + ",\"wait\":0}"; | |
return sample; | |
} | |
[Fact(Skip="these are functional tests")] | |
public void TestDestroyCommand() | |
{ | |
// Arrange | |
String sampleVolume = getSampleVolumeObjectTO(); | |
String destroyCmd = //"{\"volume\":" + getSampleVolumeObjectTO() + "}"; | |
"{\"volume\":{\"name\":\"" + testSampleVolumeTempUUIDNoExt | |
+ "\",\"storagePoolType\":\"Filesystem\"," | |
+ "\"mountPoint\":" | |
+ testLocalStorePathJSON | |
+ ",\"path\":" + testSampleVolumeTempURIJSON | |
+ ",\"storagePoolUuid\":\"" + testLocalStoreUUID | |
+ "\"," | |
+ "\"type\":\"ROOT\",\"id\":9,\"size\":0}}"; | |
HypervResourceController rsrcServer = new HypervResourceController(); | |
dynamic jsonDestroyCmd = JsonConvert.DeserializeObject(destroyCmd); | |
// Act | |
dynamic destroyAns = rsrcServer.DestroyCommand(jsonDestroyCmd); | |
// Assert | |
JObject ansAsProperty2 = destroyAns[0]; | |
dynamic ans = ansAsProperty2.GetValue(CloudStackTypes.Answer); | |
String path = jsonDestroyCmd.volume.path; | |
Assert.True((bool)ans.result, "DestroyCommand did not succeed " + ans.details); | |
Assert.True(!File.Exists(path), "Failed to delete file " + path); | |
} | |
[Fact(Skip="these are functional tests")] | |
public void TestCreateCommand() | |
{ | |
// TODO: Need sample to update the test. | |
// Arrange | |
String createCmd = "{\"volId\":10,\"pool\":{\"id\":201,\"uuid\":\"" + testLocalStoreUUID + "\",\"host\":\"" + HypervResourceController.config.StorageIpAddress + "\"" + | |
",\"path\":" + 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\":" + testSampleTemplateURLJSON + ",\"contextMap\":{},\"wait\":0}"; | |
dynamic jsonCreateCmd = JsonConvert.DeserializeObject(createCmd); | |
HypervResourceController rsrcServer = new HypervResourceController(); | |
Assert.True(Directory.Exists(testLocalStorePath)); | |
string filePath = Path.Combine(testLocalStorePath, (string)JsonConvert.DeserializeObject(testSampleTemplateURLJSON)); | |
Assert.True(File.Exists(filePath), "The template we make volumes from is missing from path " + filePath); | |
int fileCount = Directory.GetFiles(testLocalStorePath).Length; | |
s_logger.Debug(" test local store has " + fileCount + "files"); | |
// Act | |
// Test requires there to be a template at the tempalteUrl, which is its location in the local file system. | |
dynamic jsonResult = rsrcServer.CreateCommand(jsonCreateCmd); | |
JObject ansAsProperty2 = jsonResult[0]; | |
dynamic ans = ansAsProperty2.GetValue(CloudStackTypes.CreateAnswer); | |
Assert.NotNull(ans); | |
Assert.True((bool)ans.result, "Failed to CreateCommand due to " + (string)ans.result); | |
Assert.Equal(Directory.GetFiles(testLocalStorePath).Length, fileCount + 1); | |
FileInfo newFile = new FileInfo((string)ans.volume.path); | |
Assert.True(newFile.Length > 0, "The new file should have a size greater than zero"); | |
newFile.Delete(); | |
} | |
/// <summary> | |
/// Possible additional tests: place an ISO in the drive | |
/// </summary> | |
[Fact(Skip="these are functional tests")] | |
public void TestStartStopCommand() | |
{ | |
string vmName = TestStartCommand(); | |
TestStopCommand(vmName); | |
} | |
public static String getSamplePrimaryDataStoreInfo() | |
{ | |
String samplePrimaryDataStoreInfo = | |
"{\"org.apache.cloudstack.storage.to.PrimaryDataStoreTO\":" + | |
"{\"uuid\":\"" + testLocalStoreUUID + "\"," + | |
"\"id\":201," + | |
"\"host\":\"" + testPrimaryDataStoreHost + "\"," + | |
"\"type\":\"Filesystem\"," + // Not used in PrimaryDataStoreTO | |
"\"poolType\":\"Filesystem\"," + // Not used in PrimaryDataStoreTO | |
"\"path\":" + 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\":\"" + testSampleVolumeTempUUIDNoExt + "\"," + | |
"\"size\":52428800," + | |
"\"volumeId\":10," + | |
// "\"vmName\":\"i-3-5-VM\"," + // TODO: do we have to fill in the vmName? | |
"\"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; | |
} | |
[Fact(Skip="these are functional tests")] | |
public void TestCopyCommandFromCifs() | |
{ | |
// Arrange | |
string sampleCopyCommandForTemplateDownload = | |
#region string_literal | |
// org.apache.cloudstack.storage.command.CopyCommand | |
"{\"srcTO\":" + | |
"{\"org.apache.cloudstack.storage.to.TemplateObjectTO\":" + | |
"{\"path\":\"" + testCifsPath + "\"," + | |
"\"origUrl\":\"http://10.147.28.7/templates/5d67394c-4efd-4b62-966b-51aa53b35277.vhd.bz2\"," + | |
"\"uuid\":\"7e4ca941-cb1b-4113-ab9e-043960d0fb10\"," + | |
"\"id\":206," + | |
"\"format\":\"VHDX\"," + | |
"\"accountId\":2," + | |
"\"checksum\":\"4b31e2846cc67fc10ea7281986519a54\"," + | |
"\"hvm\":true," + | |
"\"displayText\":\"OS031\"," + | |
"\"imageDataStore\":" + | |
"{\"com.cloud.agent.api.to.NfsTO\":" + | |
"{\"_url\":\"" + testCifsUrl + "\"," + // Unique item here | |
"\"_role\":\"Image\"}" + | |
"}," + // end of imageDataStore | |
"\"hypervisorType\":\"Hyperv\"," + | |
"\"name\":\"" + testS3TemplateName + "\"}" + | |
"}," + // end of srcTO | |
"\"destTO\":" + | |
"{\"org.apache.cloudstack.storage.to.TemplateObjectTO\":" + | |
"{" + | |
"\"origUrl\":\"http://10.147.28.7/templates/5d67394c-4efd-4b62-966b-51aa53b35277.vhd.bz2\"," + | |
"\"uuid\":\"7e4ca941-cb1b-4113-ab9e-043960d0fb10\"," + | |
"\"id\":206," + | |
"\"format\":\"VHDX\"," + | |
"\"accountId\":2," + | |
"\"checksum\":\"4b31e2846cc67fc10ea7281986519a54\"," + | |
"\"hvm\":true," + | |
"\"displayText\":\"Test of CIFS Download\"," + | |
"\"imageDataStore\":" + getSamplePrimaryDataStoreInfo() + "," + // end of imageDataStore | |
"\"name\":\"" + testS3TemplateName + "\"," + | |
"\"hypervisorType\":\"Hyperv\"}" + | |
"}," +// end of destTO | |
"\"wait\":10800}"; // end of CopyCommand | |
#endregion | |
HypervResourceController rsrcServer; | |
dynamic jsonDownloadCopyCmd; | |
string dwnldDest; | |
dynamic jsonCloneCopyCmd; | |
string newVolName; | |
CopyCommandTestSetupCifs(null, sampleCopyCommandForTemplateDownload, out rsrcServer, out jsonDownloadCopyCmd, out dwnldDest, out jsonCloneCopyCmd, out newVolName); | |
// Act & Assert | |
DownloadTemplateToPrimaryStorage(rsrcServer, jsonDownloadCopyCmd, dwnldDest); | |
// Repeat to verify ability to detect existing file. | |
DownloadTemplateToPrimaryStorage(rsrcServer, jsonDownloadCopyCmd, dwnldDest); | |
File.Delete(dwnldDest); | |
} | |
[Fact(Skip="these are functional tests")] | |
public void TestCopyCommand() | |
{ | |
// Arrange | |
string sampleCopyCommandToCreateVolumeFromTemplate = | |
#region string_literal | |
// org.apache.cloudstack.storage.command.CopyCommand | |
"{\"srcTO\":" + | |
"{\"org.apache.cloudstack.storage.to.TemplateObjectTO\":" + | |
"{" + | |
"\"origUrl\":\"http://people.apache.org/~bhaisaab/vms/ttylinux_pv.vhd\"," + | |
"\"uuid\":\"9873f1c0-bdcc-11e2-8baa-ea85dab5fcd0\"," + | |
"\"id\":5," + | |
"\"format\":\"VHDX\"," + | |
"\"accountId\":1," + | |
"\"checksum\":\"4b31e2846cc67fc10ea7281986519a54\"," + | |
"\"hvm\":false," + | |
"\"displayText\":\"tiny Linux\"," + | |
"\"imageDataStore\":" + getSamplePrimaryDataStoreInfo() + "," + | |
"\"name\":\"" + testS3TemplateName + "\"}" + | |
"}," + // end of srcTO | |
"\"destTO\":" + | |
"{\"org.apache.cloudstack.storage.to.VolumeObjectTO\":" + | |
"{\"uuid\":\"19ae8e67-cb2c-4ab4-901e-e0b864272b59\"," + | |
"\"volumeType\":\"ROOT\"," + | |
"\"dataStore\":" + getSamplePrimaryDataStoreInfo() + "," + | |
"\"name\":\"ROOT-5\"," + | |
"\"size\":52428800," + | |
"\"volumeId\":10," + | |
"\"vmName\":\"i-3-5-VM\"," + | |
"\"accountId\":3," + | |
"\"id\":10 }" + | |
"}," + // end of destTO | |
"\"wait\":0}"; // end of Copy Command | |
#endregion | |
//"name":"ROOT-8","size":140616708,"volumeId":8,"vmName":"s-8-VM","accountId":1,"id":8}},"contextMap":{},"wait":0} | |
string sampleCopyCommandForTemplateDownload = | |
#region string_literal | |
// org.apache.cloudstack.storage.command.CopyCommand | |
"{\"srcTO\":" + | |
"{\"org.apache.cloudstack.storage.to.TemplateObjectTO\":" + | |
"{\"path\":\"" + testS3TemplateName + ".vhdx" + "\"," + | |
"\"origUrl\":\"http://10.147.28.7/templates/5d67394c-4efd-4b62-966b-51aa53b35277.vhd.bz2\"," + | |
"\"uuid\":\"7e4ca941-cb1b-4113-ab9e-043960d0fb10\"," + | |
"\"id\":206," + | |
"\"format\":\"VHDX\"," + | |
"\"accountId\":2," + | |
"\"checksum\":\"4b31e2846cc67fc10ea7281986519a54\"," + | |
"\"hvm\":true," + | |
"\"displayText\":\"OS031\"," + | |
"\"imageDataStore\":" + | |
"{\"com.cloud.agent.api.to.S3TO\":" + | |
"{\"id\":1," + | |
"\"uuid\":\"95a64c8f-2128-4502-b5b4-0d7aa77406d2\"," + | |
"\"accessKey\":\"" + AgentSettings.Default.testS3AccessKey + "\"," + | |
"\"secretKey\":\"" + AgentSettings.Default.testS3SecretKey + "\"," + | |
"\"endPoint\":\"" + AgentSettings.Default.testS3Endpoint + "\"," + | |
"\"bucketName\":\"" + AgentSettings.Default.testS3Bucket + "\"," + | |
"\"httpsFlag\":false," + | |
"\"created\":\"May 19, 2013 4:17:25 PM\"}" + | |
"}," + // end of imageDataStore | |
"\"name\":\"" + testS3TemplateName + "\"}" + | |
"}," + // end of srcTO | |
"\"destTO\":" + | |
"{\"org.apache.cloudstack.storage.to.TemplateObjectTO\":" + | |
"{" + | |
"\"origUrl\":\"http://10.147.28.7/templates/5d67394c-4efd-4b62-966b-51aa53b35277.vhd.bz2\"," + | |
"\"uuid\":\"7e4ca941-cb1b-4113-ab9e-043960d0fb10\"," + | |
"\"id\":206," + | |
"\"format\":\"VHDX\"," + | |
"\"accountId\":2," + | |
"\"checksum\":\"4b31e2846cc67fc10ea7281986519a54\"," + | |
"\"hvm\":true," + | |
"\"displayText\":\"OS031\"," + | |
"\"imageDataStore\":" + getSamplePrimaryDataStoreInfo() + "," + // end of imageDataStore | |
"\"name\":\"" + testS3TemplateName + "\"}" + | |
"}," +// end of destTO | |
"\"wait\":10800}"; // end of CopyCommand | |
#endregion | |
HypervResourceController rsrcServer; | |
dynamic jsonDownloadCopyCmd; | |
string dwnldDest; | |
dynamic jsonCloneCopyCmd; | |
string newVolName; | |
CopyCommandTestSetup(sampleCopyCommandToCreateVolumeFromTemplate, sampleCopyCommandForTemplateDownload, out rsrcServer, out jsonDownloadCopyCmd, out dwnldDest, out jsonCloneCopyCmd, out newVolName); | |
// Act & Assert | |
DownloadTemplateToPrimaryStorage(rsrcServer, jsonDownloadCopyCmd, dwnldDest); | |
CreateVolumeFromTemplate(rsrcServer, jsonCloneCopyCmd, newVolName); | |
// Repeat to verify ability to detect existing file. | |
DownloadTemplateToPrimaryStorage(rsrcServer, jsonDownloadCopyCmd, dwnldDest); | |
File.Delete(dwnldDest); | |
File.Delete(newVolName); | |
} | |
private static void CreateVolumeFromTemplate(HypervResourceController rsrcServer, dynamic jsonCloneCopyCmd, string newVolName) | |
{ | |
dynamic copyResult = rsrcServer.CopyCommand(jsonCloneCopyCmd); | |
// Assert | |
Assert.NotNull(copyResult[0][CloudStackTypes.CopyCmdAnswer]); | |
Assert.True((bool)copyResult[0][CloudStackTypes.CopyCmdAnswer].result, "CopyCommand did not succeed " + copyResult[0][CloudStackTypes.CopyCmdAnswer].details); | |
Assert.True(File.Exists(newVolName), "CopyCommand failed to generate " + newVolName); | |
} | |
private static void DownloadTemplateToPrimaryStorage(HypervResourceController rsrcServer, dynamic jsonDownloadCopyCmd, string dwnldDest) | |
{ | |
dynamic dwnldResult = rsrcServer.CopyCommand(jsonDownloadCopyCmd); | |
// Assert | |
Assert.NotNull(dwnldResult[0][CloudStackTypes.CopyCmdAnswer]); | |
Assert.True((bool)dwnldResult[0][CloudStackTypes.CopyCmdAnswer].result, "CopyCommand did not succeed " + dwnldResult[0][CloudStackTypes.CopyCmdAnswer].details); | |
Assert.True(File.Exists(dwnldDest), "CopyCommand failed to generate " + dwnldDest); | |
} | |
[Fact(Skip="these are functional tests")] | |
public void TestCopyCommandBz2Img() | |
{ | |
// Arrange | |
string sampleCopyCommandToCreateVolumeFromTemplate = | |
#region string_literal | |
// org.apache.cloudstack.storage.command.CopyCommand | |
"{\"srcTO\":" + | |
"{\"org.apache.cloudstack.storage.to.TemplateObjectTO\":" + | |
"{" + | |
"\"origUrl\":\"http://people.apache.org/~bhaisaab/vms/ttylinux_pv.vhd\"," + | |
"\"uuid\":\"9873f1c0-bdcc-11e2-8baa-ea85dab5fcd0\"," + | |
"\"id\":5," + | |
"\"format\":\"VHD\"," + | |
"\"accountId\":1," + | |
"\"checksum\":\"f613f38c96bf039f2e5cbf92fa8ad4f8\"," + | |
"\"hvm\":false," + | |
"\"displayText\":\"tiny Linux\"," + | |
"\"imageDataStore\":" + getSamplePrimaryDataStoreInfo() + "," + | |
"\"name\":\"" + testSystemVMTemplateNameNoExt + "\"}" + | |
"}," + // end of srcTO | |
"\"destTO\":" + | |
"{\"org.apache.cloudstack.storage.to.VolumeObjectTO\":" + | |
"{\"uuid\":\"19ae8e67-cb2c-4ab4-901e-e0b864272b59\"," + | |
"\"volumeType\":\"ROOT\"," + | |
"\"dataStore\":" + getSamplePrimaryDataStoreInfo() + "," + | |
"\"name\":\"ROOT-5\"," + | |
"\"size\":52428800," + | |
"\"volumeId\":10," + | |
"\"vmName\":\"i-3-5-VM\"," + | |
"\"accountId\":1," + | |
"\"id\":10}" + | |
"}," + // end of destTO | |
"\"wait\":0}"; // end of Copy Command | |
#endregion | |
string sampleCopyCommandForTemplateDownload = | |
#region string_literal | |
// org.apache.cloudstack.storage.command.CopyCommand | |
"{\"srcTO\":" + | |
"{\"org.apache.cloudstack.storage.to.TemplateObjectTO\":" + | |
"{\"path\":\"" + testSystemVMTemplateName + "\"," + | |
"\"origUrl\":\"http://10.147.28.7/templates/5d67394c-4efd-4b62-966b-51aa53b35277.vhd.bz2\"," + | |
"\"uuid\":\"7e4ca941-cb1b-4113-ab9e-043960d0fb10\"," + | |
"\"id\":206," + | |
"\"format\":\"VHD\"," + | |
"\"accountId\":1," + | |
"\"checksum\": \"f613f38c96bf039f2e5cbf92fa8ad4f8\"," + | |
"\"hvm\":true," + | |
"\"displayText\":\"OS031\"," + | |
"\"imageDataStore\":" + | |
"{\"com.cloud.agent.api.to.S3TO\":" + | |
"{\"id\":1," + | |
"\"uuid\":\"95a64c8f-2128-4502-b5b4-0d7aa77406d2\"," + | |
"\"accessKey\":\"" + AgentSettings.Default.testS3AccessKey + "\"," + | |
"\"secretKey\":\"" + AgentSettings.Default.testS3SecretKey + "\"," + | |
"\"endPoint\":\"" + AgentSettings.Default.testS3Endpoint + "\"," + | |
"\"bucketName\":\"" + AgentSettings.Default.testS3Bucket + "\"," + | |
"\"httpsFlag\":false," + | |
"\"created\":\"May 19, 2013 4:17:25 PM\"}" + | |
"}," + // end of imageDataStore | |
"\"name\":\"" + testSystemVMTemplateNameNoExt + "\"}" + | |
"}," + // end of srcTO | |
"\"destTO\":" + | |
"{\"org.apache.cloudstack.storage.to.TemplateObjectTO\":" + | |
"{" + | |
"\"origUrl\":\"http://10.147.28.7/templates/5d67394c-4efd-4b62-966b-51aa53b35277.vhd.bz2\"," + | |
"\"uuid\":\"7e4ca941-cb1b-4113-ab9e-043960d0fb10\"," + | |
"\"id\":206," + | |
"\"format\":\"VHD\"," + | |
"\"accountId\":1," + | |
"\"checksum\": \"f613f38c96bf039f2e5cbf92fa8ad4f8\"," + | |
"\"hvm\":true," + | |
"\"displayText\":\"OS031\"," + | |
"\"imageDataStore\":" + getSamplePrimaryDataStoreInfo() + "," + // end of imageDataStore | |
"\"name\":\"" + testSystemVMTemplateNameNoExt + "\"}" + | |
"}," +// end of destTO | |
"\"wait\":10800}"; // end of CopyCommand | |
#endregion | |
HypervResourceController rsrcServer; | |
dynamic jsonDownloadCopyCmd; | |
string dwnldDest; | |
dynamic jsonCloneCopyCmd; | |
string newVolName; | |
CopyCommandTestSetup(sampleCopyCommandToCreateVolumeFromTemplate, sampleCopyCommandForTemplateDownload, out rsrcServer, out jsonDownloadCopyCmd, out dwnldDest, out jsonCloneCopyCmd, out newVolName); | |
// Act & Assert | |
DownloadTemplateToPrimaryStorage(rsrcServer, jsonDownloadCopyCmd, dwnldDest); | |
CreateVolumeFromTemplate(rsrcServer, jsonCloneCopyCmd, newVolName); | |
File.Delete(dwnldDest); | |
File.Delete(newVolName); | |
} | |
private static void CopyCommandTestSetup(string sampleCopyCommandToCreateVolumeFromTemplate, string sampleCopyCommandForTemplateDownload, out HypervResourceController rsrcServer, out dynamic jsonDownloadCopyCmd, out string dwnldDest, out dynamic jsonCloneCopyCmd, out string newVolName) | |
{ | |
rsrcServer = new HypervResourceController(); | |
jsonDownloadCopyCmd = JsonConvert.DeserializeObject(sampleCopyCommandForTemplateDownload); | |
TemplateObjectTO dwnldTemplate = TemplateObjectTO.ParseJson(jsonDownloadCopyCmd.destTO); | |
dwnldDest = dwnldTemplate.FullFileName; | |
jsonCloneCopyCmd = JsonConvert.DeserializeObject(sampleCopyCommandToCreateVolumeFromTemplate); | |
VolumeObjectTO newVol = VolumeObjectTO.ParseJson(jsonCloneCopyCmd.destTO); | |
newVol.format = dwnldTemplate.format; | |
newVolName = dwnldTemplate.FullFileName; | |
if (File.Exists(dwnldDest)) | |
{ | |
File.Delete(dwnldDest); | |
} | |
if (File.Exists(newVolName)) | |
{ | |
File.Delete(newVolName); | |
} | |
} | |
private static void CopyCommandTestSetupCifs(string sampleCopyCommandToCreateVolumeFromTemplate, string sampleCopyCommandForTemplateDownload, out HypervResourceController rsrcServer, out dynamic jsonDownloadCopyCmd, out string dwnldDest, out dynamic jsonCloneCopyCmd, out string newVolName) | |
{ | |
rsrcServer = new HypervResourceController(); | |
jsonDownloadCopyCmd = JsonConvert.DeserializeObject(sampleCopyCommandForTemplateDownload); | |
TemplateObjectTO dwnldTemplate = TemplateObjectTO.ParseJson(jsonDownloadCopyCmd.destTO); | |
dwnldDest = dwnldTemplate.FullFileName; | |
if (File.Exists(dwnldDest)) | |
{ | |
File.Delete(dwnldDest); | |
} | |
newVolName = null; | |
jsonCloneCopyCmd = null; | |
} | |
[Fact(Skip="these are functional tests")] | |
public void TestModifyStoragePoolCommand() | |
{ | |
// Create dummy folder | |
String folderName = Path.Combine(".", "Dummy"); | |
if (!Directory.Exists(folderName)) | |
{ | |
Directory.CreateDirectory(folderName); | |
} | |
var pool = new | |
{ // From java class StorageFilerTO | |
type = Enum.GetName(typeof(StoragePoolType), StoragePoolType.Filesystem), | |
host = "127.0.0.1", | |
port = -1, | |
path = folderName, | |
uuid = Guid.NewGuid().ToString(), | |
userInfo = string.Empty // Used in future to hold credential | |
}; | |
var cmd = new | |
{ | |
add = true, | |
pool = pool, | |
localPath = folderName | |
}; | |
JToken tok = JToken.FromObject(cmd); | |
HypervResourceController controller = new HypervResourceController(); | |
// Act | |
dynamic jsonResult = controller.ModifyStoragePoolCommand(tok); | |
// Assert | |
dynamic ans = jsonResult[0][CloudStackTypes.ModifyStoragePoolAnswer]; | |
Assert.True((bool)ans.result, (string)ans.details); // always succeeds | |
// Clean up | |
var cmd2 = new | |
{ | |
pool = pool, | |
localPath = folderName | |
}; | |
JToken tok2 = JToken.FromObject(cmd); | |
// Act | |
dynamic jsonResult2 = controller.DeleteStoragePoolCommand(tok2); | |
// Assert | |
dynamic ans2 = jsonResult2[0][CloudStackTypes.Answer]; | |
Assert.True((bool)ans2.result, (string)ans2.details); // always succeeds | |
} | |
[Fact(Skip="these are functional tests")] | |
public void CreateStoragePoolCommand() | |
{ | |
var cmd = new { localPath = "NULL" }; | |
JToken tok = JToken.FromObject(cmd); | |
HypervResourceController controller = new HypervResourceController(); | |
// Act | |
dynamic jsonResult = controller.CreateStoragePoolCommand(tok); | |
// Assert | |
dynamic ans = jsonResult[0][CloudStackTypes.Answer]; | |
Assert.True((bool)ans.result, (string)ans.details); // always succeeds | |
} | |
[Fact(Skip="these are functional tests")] | |
public void MaintainCommand() | |
{ | |
// Omit HostEnvironment object, as this is a series of settings currently not used. | |
var cmd = new { }; | |
JToken tok = JToken.FromObject(cmd); | |
HypervResourceController controller = new HypervResourceController(); | |
// Act | |
dynamic jsonResult = controller.MaintainCommand(tok); | |
// Assert | |
dynamic ans = jsonResult[0][CloudStackTypes.MaintainAnswer]; | |
Assert.True((bool)ans.result, (string)ans.details); // always succeeds | |
} | |
[Fact(Skip="these are functional tests")] | |
public void SetupCommand() | |
{ | |
// Omit HostEnvironment object, as this is a series of settings currently not used. | |
var cmd = new { multipath = false, needSetup = true }; | |
JToken tok = JToken.FromObject(cmd); | |
HypervResourceController controller = new HypervResourceController(); | |
// Act | |
dynamic jsonResult = controller.SetupCommand(tok); | |
// Assert | |
dynamic ans = jsonResult[0][CloudStackTypes.SetupAnswer]; | |
Assert.True((bool)ans.result, (string)ans.details); // always succeeds | |
} | |
[Fact(Skip="these are functional tests")] | |
public void TestPassingUserdataToVm() | |
{ | |
// Sample data | |
String key = "cloudstack-vm-userdata"; | |
String value = "username=root;password=1pass@word1"; | |
// Find the VM | |
List<String> vmNames = wmiCallsV2.GetVmElementNames(); | |
// Get associated WMI object | |
var vm = wmiCallsV2.GetComputerSystem(AgentSettings.Default.testKvpVmName); | |
// Get existing KVP | |
var vmSettings = wmiCallsV2.GetVmSettings(vm); | |
var kvpInfo = wmiCallsV2.GetKvpSettings(vmSettings); | |
// HostExchangesItems are embedded objects in the sense that the object value is stored and not a reference to the object. | |
string[] kvpProps = kvpInfo.HostExchangeItems; | |
// If the value already exists, delete it | |
foreach (var item in kvpProps) | |
{ | |
String wmiObjectXml = item; | |
String existingKey; | |
String existingValue; | |
ParseKVP(wmiObjectXml, out existingKey, out existingValue); | |
if (existingKey == key) | |
{ | |
wmiCallsV2.DeleteHostKvpItem(vm, existingKey); | |
break; | |
} | |
} | |
// Add new user data | |
wmiCallsV2.AddUserData(vm, value); | |
// Verify key added to subsystem | |
kvpInfo = wmiCallsV2.GetKvpSettings(vmSettings); | |
// HostExchangesItems are embedded objects in the sense that the object value is stored and not a reference to the object. | |
kvpProps = kvpInfo.HostExchangeItems; | |
// If the value already exists, delete it | |
bool userDataInPlace = false; | |
foreach (var item in kvpProps) | |
{ | |
String wmiObjectXml = item; | |
String existingKey; | |
String existingValue; | |
ParseKVP(wmiObjectXml, out existingKey, out existingValue); | |
if (existingKey == key && existingValue == value) | |
{ | |
// wmiCallsV2.DeleteHostKvpItem(vm, existingKey); | |
userDataInPlace = true; | |
break; | |
} | |
} | |
Assert.True(userDataInPlace, "User data key / value did no save properly"); | |
} | |
private static void ParseKVP(String wmiObjectXml, out String existingKey, out String existingValue) | |
{ | |
// Reference: http://blogs.msdn.com/b/virtual_pc_guy/archive/2008/12/05/enumerating-parent-kvp-data.aspx | |
// Create XML parser | |
var xmlDoc = new XmlDocument(); | |
// Load WMI object | |
xmlDoc.LoadXml(wmiObjectXml); | |
// Use xpath to get name and value | |
var namePropXpath = "/INSTANCE/PROPERTY[@NAME='Name']/VALUE"; | |
var nameNode = xmlDoc.SelectSingleNode(namePropXpath); | |
existingKey = nameNode.InnerText; | |
var dataPropXpath = "/INSTANCE/PROPERTY[@NAME='Data']/VALUE"; | |
var dataNode = xmlDoc.SelectSingleNode(dataPropXpath); | |
existingValue = dataNode.InnerText; | |
} | |
[Fact(Skip="these are functional tests")] | |
public void GetVmStatsCommandFail() | |
{ | |
// Use WMI to find existing VMs | |
List<String> vmNames = new List<String>(); | |
vmNames.Add("FakeVM"); | |
var cmd = new | |
{ | |
hostGuid = "FAKEguid", | |
hostName = AgentSettings.Default.host, | |
vmNames = vmNames | |
}; | |
JToken tok = JToken.FromObject(cmd); | |
HypervResourceController controller = new HypervResourceController(); | |
// Act | |
dynamic jsonResult = controller.GetVmStatsCommand(tok); | |
// Assert | |
dynamic ans = jsonResult[0][CloudStackTypes.GetVmStatsAnswer]; | |
Assert.True((bool)ans.result, (string)ans.details); // always succeeds, fake VM means no answer for the named VM | |
} | |
[Fact(Skip="these are functional tests")] | |
public void GetVmStatsCommand() | |
{ | |
// Use WMI to find existing VMs | |
List<String> vmNames = wmiCallsV2.GetVmElementNames(); | |
var cmd = new | |
{ | |
hostGuid = "FAKEguid", | |
hostName = AgentSettings.Default.host, | |
vmNames = vmNames | |
}; | |
JToken tok = JToken.FromObject(cmd); | |
HypervResourceController controller = new HypervResourceController(); | |
// Act | |
dynamic jsonResult = controller.GetVmStatsCommand(tok); | |
// Assert | |
dynamic ans = jsonResult[0][CloudStackTypes.GetVmStatsAnswer]; | |
Assert.True((bool)ans.result, (string)ans.details); | |
} | |
[Fact(Skip="these are functional tests")] | |
public void GetStorageStatsCommand() | |
{ | |
// TODO: Update sample data to unsure it is using correct info. | |
String sample = String.Format( | |
#region string_literal | |
"{{\"" + | |
"id\":{0}," + | |
"\"localPath\":{1}," + | |
"\"pooltype\":\"Filesystem\"," + | |
"\"contextMap\":{{}}," + | |
"\"wait\":0}}", | |
JsonConvert.SerializeObject(AgentSettings.Default.testLocalStoreUUID), | |
JsonConvert.SerializeObject(AgentSettings.Default.testLocalStorePath) | |
); | |
#endregion | |
var cmd = JsonConvert.DeserializeObject(sample); | |
JToken tok = JToken.FromObject(cmd); | |
HypervResourceController controller = new HypervResourceController(); | |
// Act | |
dynamic jsonResult = controller.GetStorageStatsCommand(tok); | |
// Assert | |
dynamic ans = jsonResult[0][CloudStackTypes.GetStorageStatsAnswer]; | |
Assert.True((bool)ans.result, (string)ans.details); | |
Assert.True((long)ans.used <= (long)ans.capacity); // TODO: verify that capacity is indeed capacity and not used. | |
} | |
// TODO: can we speed up this command? The logic takes over a second. | |
[Fact(Skip="these are functional tests")] | |
public void GetHostStatsCommand() | |
{ | |
// Arrange | |
long hostIdVal = 5; | |
HypervResourceController controller = new HypervResourceController(); | |
string sample = string.Format( | |
#region string_literal | |
"{{" + | |
"\"hostGuid\":\"B4AE5970-FCBF-4780-9F8A-2D2E04FECC34-HypervResource\"," + | |
"\"hostName\":\"CC-SVR11\"," + | |
"\"hostId\":{0}," + | |
"\"contextMap\":{{}}," + | |
"\"wait\":0}}", | |
JsonConvert.SerializeObject(hostIdVal)); | |
#endregion | |
var cmd = JsonConvert.DeserializeObject(sample); | |
JToken tok = JToken.FromObject(cmd); | |
// Act | |
dynamic jsonResult = controller.GetHostStatsCommand(tok); | |
// Assert | |
dynamic ans = jsonResult[0][CloudStackTypes.GetHostStatsAnswer]; | |
Assert.True((bool)ans.result); | |
Assert.True(hostIdVal == (long)ans.hostStats.hostId); | |
Assert.True(0.0 < (double)ans.hostStats.totalMemoryKBs); | |
Assert.True(0.0 < (double)ans.hostStats.freeMemoryKBs); | |
Assert.True(0.0 <= (double)ans.hostStats.networkReadKBs); | |
Assert.True(0.0 <= (double)ans.hostStats.networkWriteKBs); | |
Assert.True(0.0 <= (double)ans.hostStats.cpuUtilization); | |
Assert.True(100.0 >= (double)ans.hostStats.cpuUtilization); | |
Assert.True("host".Equals((string)ans.hostStats.entityType)); | |
Assert.True(String.IsNullOrEmpty((string)ans.details)); | |
} | |
[Fact(Skip="these are functional tests")] | |
public void GetHostStatsCommandFail() | |
{ | |
// Arrange | |
HypervResourceController controller = new HypervResourceController(); | |
var cmd = new { GetHostStatsCommand = new { hostId = "badvalueType" } }; | |
JToken tokFail = JToken.FromObject(cmd); | |
// Act | |
dynamic jsonResult = controller.GetHostStatsCommand(tokFail); | |
// Assert | |
dynamic ans = jsonResult[0][CloudStackTypes.GetHostStatsAnswer]; | |
Assert.False((bool)ans.result); | |
Assert.Null((string)ans.hostStats); | |
Assert.NotNull(ans.details); | |
} | |
[Fact(Skip="these are functional tests")] | |
public void TestStartupCommand() | |
{ | |
// Arrange | |
HypervResourceController controller = new HypervResourceController(); | |
String sampleStartupRoutingCommand = | |
#region string_literal | |
"[{\"" + CloudStackTypes.StartupRoutingCommand + "\":{" + | |
"\"cpus\":0," + | |
"\"speed\":0," + | |
"\"memory\":0," + | |
"\"dom0MinMemory\":0," + | |
"\"poolSync\":false," + | |
"\"vms\":{}," + | |
"\"hypervisorType\":\"Hyperv\"," + | |
"\"hostDetails\":{" + | |
"\"com.cloud.network.Networks.RouterPrivateIpStrategy\":\"HostLocal\"" + | |
"}," + | |
"\"type\":\"Routing\"," + | |
"\"dataCenter\":\"1\"," + | |
"\"pod\":\"1\"," + | |
"\"cluster\":\"1\"," + | |
"\"guid\":\"16f85622-4508-415e-b13a-49a39bb14e4d\"," + | |
"\"name\":\"localhost\"," + | |
"\"version\":\"4.2.0\"," + | |
"\"privateIpAddress\":\"1\"," + | |
"\"storageIpAddress\":\"1\"," + | |
"\"contextMap\":{}," + | |
"\"wait\":0}}]"; | |
#endregion | |
uint cores; | |
uint mhz; | |
wmiCallsV2.GetProcessorResources(out cores, out mhz); | |
ulong memory_mb; | |
ulong freememory; | |
wmiCallsV2.GetMemoryResources(out memory_mb, out freememory); | |
memory_mb *= 1024; | |
long capacityBytes; | |
long availableBytes; | |
HypervResourceController.GetCapacityForLocalPath(wmiCallsV2.GetDefaultVirtualDiskFolder(), | |
out capacityBytes, out availableBytes); | |
var DefaultVirtualDiskFolder = JsonConvert.SerializeObject(wmiCallsV2.GetDefaultVirtualDiskFolder()); | |
string expected = | |
#region string_literal | |
"[{\"" + CloudStackTypes.StartupRoutingCommand + "\":{" + | |
"\"cpus\":" + cores + "," + | |
"\"speed\":" + mhz + "," + | |
"\"memory\":" + memory_mb + "," + | |
"\"dom0MinMemory\":" + (AgentSettings.Default.dom0MinMemory * 1024 * 1024) + "," + | |
"\"poolSync\":false," + | |
"\"vms\":{}," + | |
"\"hypervisorType\":\"Hyperv\"," + | |
"\"hostDetails\":{" + | |
"\"com.cloud.network.Networks.RouterPrivateIpStrategy\":\"HostLocal\"" + | |
"}," + | |
"\"type\":\"Routing\"," + | |
"\"dataCenter\":\"1\"," + | |
"\"pod\":\"1\"," + | |
"\"cluster\":\"1\"," + | |
"\"guid\":\"16f85622-4508-415e-b13a-49a39bb14e4d\"," + | |
"\"name\":\"localhost\"," + | |
"\"version\":\"4.2.0\"," + | |
"\"privateIpAddress\":\"" + AgentSettings.Default.private_ip_address + "\"," + | |
"\"storageIpAddress\":\"" + AgentSettings.Default.private_ip_address + "\"," + | |
"\"contextMap\":{}," + | |
"\"wait\":0," + | |
"\"privateNetmask\":\"" + AgentSettings.Default.private_ip_netmask + "\"," + | |
"\"privateMacAddress\":\"" + AgentSettings.Default.private_mac_address + "\"," + | |
"\"storageNetmask\":\"" + AgentSettings.Default.private_ip_netmask + "\"," + | |
"\"storageMacAddress\":\"" + AgentSettings.Default.private_mac_address + "\"," + | |
"\"gatewayIpAddress\":\"" + AgentSettings.Default.gateway_ip_address + "\"" + | |
",\"caps\":\"hvm\"" + | |
"}}," + | |
"{\"com.cloud.agent.api.StartupStorageCommand\":{" + | |
"\"poolInfo\":{" + | |
"\"uuid\":\"16f85622-4508-415e-b13a-49a39bb14e4d\"," + | |
"\"host\":\"" + AgentSettings.Default.private_ip_address + "\"," + | |
"\"localPath\":" + DefaultVirtualDiskFolder + "," + | |
"\"hostPath\":" + DefaultVirtualDiskFolder + "," + | |
"\"poolType\":\"Filesystem\"," + | |
"\"capacityBytes\":" + capacityBytes + "," + | |
"\"availableBytes\":" + availableBytes + "," + | |
"\"details\":null" + | |
"}," + | |
"\"guid\":\"16f85622-4508-415e-b13a-49a39bb14e4d\"," + | |
"\"dataCenter\":\"1\"," + | |
"\"resourceType\":\"STORAGE_POOL\"" + | |
"}}]"; | |
#endregion | |
dynamic jsonArray = JsonConvert.DeserializeObject(sampleStartupRoutingCommand); | |
// Act | |
dynamic jsonResult = controller.StartupCommand(jsonArray); | |
// Assert | |
string actual = JsonConvert.SerializeObject(jsonResult); | |
Assert.Equal(expected, actual); | |
} | |
private static string TestStartCommand() | |
{ | |
// Arrange | |
HypervResourceController rsrcServer = new HypervResourceController(); | |
String sample = getSampleStartCommand(); | |
dynamic jsonStartCmd = JsonConvert.DeserializeObject(sample); | |
// Act | |
dynamic startAns = rsrcServer.StartCommand(jsonStartCmd); | |
// Assert | |
Assert.NotNull(startAns[0][CloudStackTypes.StartAnswer]); | |
Assert.True((bool)startAns[0][CloudStackTypes.StartAnswer].result, "StartCommand did not succeed " + startAns[0][CloudStackTypes.StartAnswer].details); | |
string vmCmdName = jsonStartCmd.vm.name.Value; | |
var vm = wmiCallsV2.GetComputerSystem(vmCmdName); | |
var vmSettings = wmiCallsV2.GetVmSettings(vm); | |
var memSettings = wmiCallsV2.GetMemSettings(vmSettings); | |
var procSettings = wmiCallsV2.GetProcSettings(vmSettings); | |
dynamic jsonObj = JsonConvert.DeserializeObject(sample); | |
var vmInfo = jsonObj.vm; | |
string vmName = vmInfo.name; | |
var nicInfo = vmInfo.nics; | |
int vcpus = vmInfo.cpus; | |
int memSize = vmInfo.maxRam / 1048576; | |
Assert.True((long)memSettings.VirtualQuantity == memSize); | |
Assert.True((long)memSettings.Reservation == memSize); | |
Assert.True((long)memSettings.Limit == memSize); | |
Assert.True((int)procSettings.VirtualQuantity == vcpus); | |
Assert.True((int)procSettings.Reservation == vcpus); | |
Assert.True((int)procSettings.Limit == 100000); | |
// examine NIC for correctness | |
var nicSettingsViaVm = wmiCallsV2.GetEthernetPortSettings(vm); | |
Assert.True(nicSettingsViaVm.Length > 0, "Should be at least one ethernet port on VM"); | |
string expectedMac = (string)jsonStartCmd.vm.nics[0].mac; | |
string strippedExpectedMac = expectedMac.Replace(":", string.Empty); | |
Assert.Equal(nicSettingsViaVm[0].Address.ToLower(), strippedExpectedMac.ToLower()); | |
// Assert switchport has correct VLAN | |
var ethernetConnections = wmiCallsV2.GetEthernetConnections(vm); | |
var vlanSettings = wmiCallsV2.GetVlanSettings(ethernetConnections[0]); | |
string isolationUri = (string)jsonStartCmd.vm.nics[0].isolationUri; | |
string vlan = isolationUri.Replace("vlan://", string.Empty); | |
Assert.Equal(vlanSettings.AccessVlanId.ToString(), vlan); | |
return vmName; | |
} | |
private static void TestStopCommand(string vmName) | |
{ | |
// Arrange | |
HypervResourceController rsrcServer = new HypervResourceController(); | |
String sampleStop = "{\"isProxy\":false,\"vmName\":\"i-2-17-VM\",\"contextMap\":{},\"wait\":0}"; | |
dynamic jsonStopCmd = JsonConvert.DeserializeObject(sampleStop); | |
// Act | |
dynamic stopAns = rsrcServer.StopCommand(jsonStopCmd); | |
// Assert VM is gone! | |
Assert.NotNull(stopAns[0][CloudStackTypes.StopAnswer]); | |
Assert.True((bool)stopAns[0][CloudStackTypes.StopAnswer].result, "StopCommand did not succeed " + stopAns[0][CloudStackTypes.StopAnswer].details); | |
var finalVm = wmiCallsV2.GetComputerSystem(vmName); | |
Assert.True(wmiCallsV2.GetComputerSystem(vmName) == null); | |
} | |
} | |
} |