技术分享
Hyper-v虚拟机备份与还原实现
2019-08-15
上篇介绍备份和还原的整体流程,本骗具体介绍实现过程。
第一步 hyper-v虚拟机信息的获取
fd端获取所有hyper-v信息,同过dir给fd发送一个命令,fd端接收到命令字处理请求,调用一个system(“quryvm.exe”),在hyper-v端生成一个 hyper-v.conf文件,里面目前存放每一个hyper-v的三个主要参数,hyper-v虚拟机名,hyper-v虚拟机所在host名,hyper-v虚拟机硬盘文件路径;
若下图,hyper-v.conf 文件
big HS22-3 C:/Users/Public/Documents/Hyper-V/Virtual Hard Disks/big.vhdx 418 SZY C:/Users/Public/Documents/Hyper-V/Virtual Hard Disks/418.vhdx
对应的hyper-v管理界面如下图所示;
虚拟机big 在host为HS22-3的hyper-v服务器上 路径为 HS22-3的C:/Users/Public/Documents/Hyper-V/Virtual Hard Disks/big.vhdx

虚拟机418在host为SZY的hyper-v服务器上,路径为SZY的C:/Users/Public/Documents/Hyper-V/Virtual Hard Disks/418.vhdx

所有的信息都是通过quryvm.exe这个程序获得,下面请看quryvm.exe的源码
using System;
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.IO;
using System.Text;
using System.Management;
using System.Text.RegularExpressions;
namespace queryvm
{
#region Return Value of RequestStateChange Method of the Msvm_ComputerSystem Class
//Return Value of RequestStateChange Method of the Msvm_ComputerSystem Class
//This method returns one of the following values.
//Completed with No Error (0)
//DMTF Reserved (7–4095)
//Method Parameters Checked - Transition Started (4096)
//Failed (32768)
//Access Denied (32769)
//Not Supported (32770)
//Status is unknown (32771)
//Timeout (32772)
//Invalid parameter (32773)
//System is in use (32774)
//Invalid state for this operation (32775)
//Incorrect data type (32776)
//System is not available (32777)
//Out of memory (32778)
#endregion
public class VMManagement
{
private static string script = File.ReadAllText(@"C:\hyperv.ps1");
private static void GetHyperPath(string vmname, string hhost)
{
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.AddScript(script);
ps.Invoke();
ps.AddCommand("gethyperpath").AddParameters(
new Dictionary<string, string>(){
{"name", vmname},
{"pcname", hhost}
}
);
foreach (PSObject result in ps.Invoke())
{
Console.WriteLine(result);
}
}
}
private static void Sethost(string hhost)
{
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.AddScript(script);
ps.Invoke();
ps.AddCommand("sethost").AddParameters(
new Dictionary<string, string>(){
{"hhost", hhost}
}
);
foreach (PSObject result in ps.Invoke())
{
Console.WriteLine(result);
}
}
}
private static void Clean(string clean)
{
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.AddScript(script);
ps.Invoke();
ps.AddCommand("Clean").AddParameters(
new Dictionary<string, string>(){
{"clean", clean}
}
);
foreach (PSObject result in ps.Invoke())
{
Console.WriteLine(result);
}
}
}
public static string ReadLineAddress()
{
String textFileName = @"C:\WINDOWS\system32\drivers\etc\hosts";
TextReader tr = new StreamReader(textFileName);
StringBuilder strtmp = new StringBuilder();
string findstr = "";
int i = 0;
while (tr.Peek() > 0)
{
i++;
findstr = tr.ReadLine();
if (findstr.Contains("#"))
{
continue;
}
if (string.IsNullOrEmpty(findstr))
{
continue;
}else{
string temp = findstr;
string[] words = Regex.Split(findstr, "\\s+", RegexOptions.IgnoreCase);
strtmp.Append(words[1]);
strtmp.Append("+");
}
}
tr.Close();
return strtmp.ToString();
}
public static int Main()
{
//Console.WriteLine("myxp");
//return 0;
try
{
//获取系统信息
System.OperatingSystem osInfo = System.Environment.OSVersion;
int versionMajor = osInfo.Version.Major;
int versionMinor = osInfo.Version.Minor;
string hostName = System.Net.Dns.GetHostName();
string hhosts = hostName + "+" +ReadLineAddress();
Console.WriteLine(hostName);
string clean = "clean";
Clean(clean);
string vmNameSpace = @"\\{0}\root\virtualization\v2";
string[] test = hhosts.Split('+');
foreach (var ss in test)
{
if (string.IsNullOrEmpty(ss))
{
continue;
}
Console.WriteLine(ss);
ManagementScope manScope = new ManagementScope(string.Format(vmNameSpace, ss), null);
manScope.Connect();
//ObjectQuery queryObj = new ObjectQuery("SELECT * FROM Msvm_ComputerSystem where ProcessID >= 0");
ObjectQuery queryObj = new ObjectQuery("SELECT * FROM Msvm_ComputerSystem where ElementName!=__SERVER");
ManagementObjectSearcher vmSearcher = new ManagementObjectSearcher(manScope, queryObj);
ManagementObjectCollection vmCollection = vmSearcher.Get();
// loop through the machines
foreach (ManagementObject vm in vmCollection)
{
string vmname = vm["ElementName"].ToString();
Console.WriteLine("xuniji");
GetHyperPath(vmname,ss);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return -1;
}
return 0;
}
}
}代码说明 先声明下小编是个C程序员,以前没有接触过任何C#程序
代码就比较简单了 这是通过c调system 函数调用queryvm.exe 而在 queryvm中调用了ps1脚本,其中cs代码中gethyperpath对应ps1中有相应函数,通过这个函数获取hyperv虚拟机系统,其他函数类似。获取所有虚拟机信息的思路 大致是这样的,通过一个循环,解析hosts文件中的hyperv服务器名,获取一个host然后获取 这个服务器上所有的虚拟机;本脚本只针对2012,(客户环境) 如果是03,08,16的话只需要修改相应ps1中的命令即可;
代码中hyper-v.psl如下
function gethyperpath
{
param([string]$name,[string]$pcname)
$name | Out-file -append -Encoding utf8 C:\hyper-v.conf
$pcname | Out-file -append -Encoding utf8 C:\hyper-v.conf
$b= Get-VM -VMName $name -ComputerName $pcname | Select-Object VMID
$c= Get-VHD -VMID $b.VMID -ComputerName $pcname | Select-Object "Path"
$c.Path.replace("\", "/") | Out-file -append -Encoding utf8 C:\hyper-v.conf
return $name
}
function Clean
{
param([string]$clean)
Clear-Content C:\hyper-v.conf
return $clean
}
function cleanname
{
param([string]$cleanname)
Clear-Content C:\name.conf
return $cleanname
}
function getmemory
{
param([string]$vname,[string]$hostname)
$a=Get-VMMemory -VMName $vname -ComputerName $hostname
return $a.Startup
}
function getcpunum
{
param([string]$vname,[string]$hostname)
$b=Get-VMProcessor -VMName $vname -ComputerName $hostname
return $b.Count
}
function getnet
{
param([string]$vname,[string]$hostname)
$e=Get-VMNetworkAdapter $vname -ComputerName $hostname
if([String]::IsNullOrEmpty($e.SwitchName))
{
$temp="null";
return $temp
}
return $e.SwitchName
}
function getpath
{
param([string]$vname,[string]$hostname)
$b= Get-VM -VMName $vname -ComputerName $hostname | Select-Object VMID
$c= Get-VHD -VMID $b.VMID -ComputerName $hostname | Select-Object "Path"
return $c.Path
}
function getvmtype
{
param([string]$vname,[string]$hostname)
$d= Get-VM -VMName $vname -ComputerName $hostname
return $d.Generation
}
function createvm
{ param([string]$vname,[string]$vmtype,[string]$hostname,[string]$memory,[string]$cpunum,[string]$net,[string]$path)
$vname
$vmtype
$hostname
$memory
$cpunum
$net
$path
New-VM $vname -ComputerName $hostname -Generation $vmtype -MemoryStartupBytes $memory -VHDPath $path -SwitchName $net
Set-VMProcessor -VMName $vname -ComputerName $hostname -Count $cpunum
return 0
}
function createvmtwo
{ param([string]$vname,[string]$vmtype,[string]$hostname,[string]$memory,[string]$cpunum,[string]$path)
$vname
$vmtype
$hostname
$memory
$cpunum
$path.Trim()
$svname= Get-VMSwitch -ComputerName $hostname
if($svname.Name.Count -gt 1)
{
$net=$svname.Name[0];
} else {
$net=$svname.Name
}
$net
New-VM $vname -ComputerName $hostname -Generation $vmtype -MemoryStartupBytes $memory -VHDPath $path -SwitchName $net
Set-VMProcessor -VMName $vname -ComputerName $hostname -Count $cpunum
return 0
}在获取完虚拟机名 host名,vhd路径后,fd端读取hyper-v.conf文件 ,通过一系列流程,回显给客户端页面,然后客户端勾选需要的虚拟机,勾选确定后,通过流程处理 ,将hyperv虚拟机名称host名称存入数据库,以备还原的时候用;

第二步 备份vhd文件
第一步中选择备份的虚拟机时,可以将备份的vhdx路径记录下,然后同过备份软件 直接将vhd文件备份到指定的sd,涉及公司具体代码不做过多说明,以上代码纯个人开发的,转载请注明出处。
- 标签:
-
容灾备份