技术分享
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,涉及公司具体代码不做过多说明,以上代码纯个人开发的,转载请注明出处。

- 标签:
-
容灾备份