mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
351 lines
18 KiB
C#
351 lines
18 KiB
C#
// 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.V2;
|
|
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 NSubstitute;
|
|
using System.Web.Http;
|
|
using Xunit;
|
|
|
|
namespace ServerResource.Tests
|
|
{
|
|
public class HypervResourceController1Test
|
|
{
|
|
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 IWmiCallsV2 wmiCallsV2;
|
|
|
|
|
|
private static ILog s_logger = LogManager.GetLogger(typeof(HypervResourceController1Test));
|
|
|
|
/// <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 HypervResourceController1Test()
|
|
{
|
|
wmiCallsV2 = Substitute.For<IWmiCallsV2>();
|
|
//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);
|
|
|
|
testSampleTemplateURLJSON = JsonConvert.SerializeObject(testSampleTemplateUUID);
|
|
s_logger.Info("Created " + testSampleTemplateURLJSON + " in local 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]
|
|
public void TestCreateCommand()
|
|
{
|
|
DirectoryInfo localStorePath = new DirectoryInfo(testLocalStorePath);
|
|
if (!localStorePath.Exists)
|
|
{
|
|
try
|
|
{
|
|
localStorePath.Create();
|
|
}
|
|
catch (System.IO.IOException ex)
|
|
{
|
|
throw new NotImplementedException("Need to be able to create the folder " + localStorePath.FullName + " failed due to " + ex.Message);
|
|
}
|
|
}
|
|
|
|
FileInfo sampleTemplateFile = new FileInfo(Path.Combine(testLocalStorePath, testSampleTemplateUUID));
|
|
if (!sampleTemplateFile.Exists)
|
|
{
|
|
//Create a file to write to.
|
|
using (StreamWriter sw = sampleTemplateFile.CreateText())
|
|
{
|
|
sw.WriteLine("This is fake template file for test");
|
|
}
|
|
}
|
|
var counter = 0;
|
|
wmiCallsV2.When(x => x.CreateDynamicVirtualHardDisk(Arg.Any<ulong>(), Arg.Any<String>())).Do(x => counter++);
|
|
// 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();
|
|
HypervResourceController.wmiCallsV2 = wmiCallsV2;
|
|
|
|
Assert.True(Directory.Exists(testLocalStorePath), testLocalStorePath + " does not exist ");
|
|
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);
|
|
s_logger.Debug("CreateDynamicVirtualHardDisk method is called " + counter + " times");
|
|
|
|
//Assert.Equal(counter, 1);
|
|
|
|
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();
|
|
sampleTemplateFile.Delete();
|
|
}
|
|
|
|
[Fact]
|
|
public void TestDestroyCommand()
|
|
{
|
|
testSampleVolumeTempURIJSON = "\"storagepool\"";
|
|
// Arrange
|
|
String destroyCmd = //"{\"volume\":" + getSampleVolumeObjectTO() + "}";
|
|
"{\"volume\":{\"name\":\"" + testSampleVolumeTempUUIDNoExt
|
|
+ "\",\"storagePoolType\":\"Filesystem\","
|
|
+ "\"mountPoint\":"
|
|
+ testLocalStorePathJSON
|
|
+ ",\"path\":" + testSampleVolumeTempURIJSON
|
|
+ ",\"storagePoolUuid\":\"" + testLocalStoreUUID
|
|
+ "\","
|
|
+ "\"type\":\"ROOT\",\"id\":9,\"size\":0}}";
|
|
|
|
ImageManagementService imgmgr = new ImageManagementService();
|
|
wmiCallsV2.GetImageManagementService().Returns(imgmgr);
|
|
|
|
HypervResourceController rsrcServer = new HypervResourceController();
|
|
HypervResourceController.wmiCallsV2 = wmiCallsV2;
|
|
|
|
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]
|
|
public void TestStartCommand()
|
|
{
|
|
ComputerSystem system = new ComputerSystem();
|
|
wmiCallsV2.DeployVirtualMachine(Arg.Any<Object>(), Arg.Any<string>()).Returns(system);
|
|
|
|
// Arrange
|
|
HypervResourceController rsrcServer = new HypervResourceController();
|
|
HypervResourceController.wmiCallsV2 = wmiCallsV2;
|
|
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);
|
|
|
|
Assert.Null((string)startAns[0][CloudStackTypes.StartAnswer].details);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestStopCommand()
|
|
{
|
|
//string vmName = "Test VM";
|
|
var counter = 0;
|
|
wmiCallsV2.When(x => x.DestroyVm(Arg.Any<Object>())).Do(x => counter++);
|
|
|
|
// Arrange
|
|
HypervResourceController rsrcServer = new HypervResourceController();
|
|
HypervResourceController.wmiCallsV2 = wmiCallsV2;
|
|
|
|
String sampleStop = "{\"isProxy\":false,\"vmName\":\"i-2-17-VM\",\"contextMap\":{},\"checkBeforeCleanup\":false,\"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);
|
|
|
|
Assert.Null((string)stopAns[0][CloudStackTypes.StopAnswer].details);
|
|
Assert.Equal<int>(counter, 1);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|