2017-01-16 20:59:50 +01:00
|
|
|
|
/* Copyright (c) Citrix Systems, Inc.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Redistribution and use in source and binary forms,
|
|
|
|
|
* with or without modification, are permitted provided
|
|
|
|
|
* that the following conditions are met:
|
|
|
|
|
*
|
|
|
|
|
* * Redistributions of source code must retain the above
|
|
|
|
|
* copyright notice, this list of conditions and the
|
|
|
|
|
* following disclaimer.
|
|
|
|
|
* * Redistributions in binary form must reproduce the above
|
|
|
|
|
* copyright notice, this list of conditions and the
|
|
|
|
|
* following disclaimer in the documentation and/or other
|
|
|
|
|
* materials provided with the distribution.
|
|
|
|
|
*
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
|
|
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
|
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
|
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
|
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2018-08-09 11:54:32 +02:00
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Drawing;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
using System.IO;
|
2018-09-14 16:55:05 +02:00
|
|
|
|
using System.Net;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
using System.Xml;
|
|
|
|
|
using System.Xml.XPath;
|
|
|
|
|
using DiscUtils;
|
|
|
|
|
using DiscUtils.Wim;
|
|
|
|
|
using XenAdmin.Controls;
|
2017-11-17 02:04:45 +01:00
|
|
|
|
using XenCenterLib;
|
2018-08-09 11:54:32 +02:00
|
|
|
|
using XenCenterLib.Compression;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
using XenAdmin.Dialogs;
|
|
|
|
|
using XenAdmin.Controls.Common;
|
|
|
|
|
using XenCenterLib.Archive;
|
|
|
|
|
using XenOvf;
|
|
|
|
|
using XenOvf.Definitions.VMC;
|
|
|
|
|
using XenOvf.Utilities;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace XenAdmin.Wizards.ImportWizard
|
|
|
|
|
{
|
|
|
|
|
internal partial class ImportSourcePage : XenTabPage
|
|
|
|
|
{
|
|
|
|
|
#region Private fields
|
|
|
|
|
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
2018-08-09 11:54:32 +02:00
|
|
|
|
private readonly string[] m_supportedImageTypes = { ".vhd", ".vmdk" };//CA-61385: remove ".vdi", ".wim" support for Boston
|
|
|
|
|
private readonly string[] m_supportedApplianceTypes = { ".ovf", ".ova", ".ova.gz" };
|
2019-08-21 09:05:06 +02:00
|
|
|
|
private readonly string[] m_supportedXvaTypes = {".xva", ".xva.gz", "ova.xml"};
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Stores the last valid selected appliance
|
|
|
|
|
/// </summary>
|
|
|
|
|
private string m_lastValidAppliance;
|
|
|
|
|
private bool m_buttonNextEnabled;
|
2018-08-09 11:54:32 +02:00
|
|
|
|
|
|
|
|
|
private string _unzipFileIn;
|
|
|
|
|
private string _unzipFileOut;
|
2018-09-14 16:55:05 +02:00
|
|
|
|
|
|
|
|
|
private Uri _uri;
|
|
|
|
|
private string _downloadFolder;
|
|
|
|
|
private WebClient _webClient;
|
|
|
|
|
private Queue<ApplianceFile> _filesToDownload;
|
|
|
|
|
|
|
|
|
|
private bool longProcessInProgress;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
public ImportSourcePage()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
m_ctrlError.HideError();
|
|
|
|
|
m_tlpEncryption.Visible = false;
|
2018-08-09 11:54:32 +02:00
|
|
|
|
m_tlpError.Visible = false;
|
|
|
|
|
progressBar1.Visible = false;
|
|
|
|
|
labelProgress.Visible = false;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region Base class (XenTabPage) overrides
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the page's title (headline)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public override string PageTitle { get { return Messages.IMPORT_SOURCE_PAGE_TITLE; } }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the page's label in the (left hand side) wizard progress panel
|
|
|
|
|
/// </summary>
|
|
|
|
|
public override string Text { get { return Messages.IMPORT_SOURCE_PAGE_TEXT; } }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the value by which the help files section for this page is identified
|
|
|
|
|
/// </summary>
|
|
|
|
|
public override string HelpID { get { return "Source"; } }
|
|
|
|
|
|
|
|
|
|
protected override bool ImplementsIsDirty()
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-09 00:55:50 +01:00
|
|
|
|
protected override void PageLeaveCore(PageLoadedDirection direction, ref bool cancel)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
if (direction == PageLoadedDirection.Forward && IsDirty)
|
|
|
|
|
{
|
2018-09-14 16:55:05 +02:00
|
|
|
|
if (IsUri() && !Download())
|
|
|
|
|
{
|
|
|
|
|
cancel = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
if (!PerformCheck(CheckIsSupportedType, CheckPathExists))
|
|
|
|
|
{
|
|
|
|
|
cancel = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-09 11:54:32 +02:00
|
|
|
|
string extension = Path.GetExtension(FilePath).ToLower();
|
|
|
|
|
|
2019-11-25 17:07:58 +01:00
|
|
|
|
if (extension == ".gz" && (!PerformCheck(CheckSourceIsWritable) || !Uncompress()))
|
2018-08-09 11:54:32 +02:00
|
|
|
|
{
|
2019-11-25 17:07:58 +01:00
|
|
|
|
cancel = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (extension == ".ova" && !PerformCheck(CheckSourceIsWritable))
|
|
|
|
|
{
|
|
|
|
|
cancel = true;
|
|
|
|
|
return;
|
2018-08-09 11:54:32 +02:00
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2018-08-09 11:54:32 +02:00
|
|
|
|
CheckDelegate check = null;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
switch (TypeOfImport)
|
|
|
|
|
{
|
|
|
|
|
case ImportWizard.ImportType.Xva:
|
2018-08-09 11:54:32 +02:00
|
|
|
|
check = GetDiskCapacityXva;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
break;
|
|
|
|
|
case ImportWizard.ImportType.Ovf:
|
2018-08-09 11:54:32 +02:00
|
|
|
|
check = LoadAppliance;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
break;
|
|
|
|
|
case ImportWizard.ImportType.Vhd:
|
2018-08-09 11:54:32 +02:00
|
|
|
|
check = GetDiskCapacityImage;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-09 11:54:32 +02:00
|
|
|
|
if (!PerformCheck(check))
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
cancel = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-25 17:07:58 +01:00
|
|
|
|
if (TypeOfImport == ImportWizard.ImportType.Ovf && SelectedOvfPackage.HasEncryption())
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
cancel = true;
|
|
|
|
|
m_tlpEncryption.Visible = true;
|
|
|
|
|
m_buttonNextEnabled = false;
|
|
|
|
|
OnPageUpdated();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-20 13:33:26 +01:00
|
|
|
|
public override void PageCancelled(ref bool cancel)
|
2018-08-09 11:54:32 +02:00
|
|
|
|
{
|
2018-09-14 16:55:05 +02:00
|
|
|
|
if (_webClient != null && _webClient.IsBusy)
|
|
|
|
|
_webClient.CancelAsync();
|
|
|
|
|
|
2018-08-09 11:54:32 +02:00
|
|
|
|
if (_unzipWorker.IsBusy)
|
|
|
|
|
_unzipWorker.CancelAsync();
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
public override void PopulatePage()
|
|
|
|
|
{
|
|
|
|
|
lblIntro.Text = OvfModeOnly ? Messages.IMPORT_SOURCE_PAGE_INTRO_OVF_ONLY : Messages.IMPORT_SOURCE_PAGE_INTRO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override bool EnableNext()
|
|
|
|
|
{
|
|
|
|
|
return m_buttonNextEnabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Accessors
|
|
|
|
|
|
|
|
|
|
public bool OvfModeOnly { private get; set; }
|
|
|
|
|
|
|
|
|
|
public ImportWizard.ImportType TypeOfImport { get; private set; }
|
|
|
|
|
|
2019-11-25 17:07:58 +01:00
|
|
|
|
public string FilePath => m_textBoxFile.Text.Trim();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2019-11-25 17:07:58 +01:00
|
|
|
|
public Package SelectedOvfPackage { get; private set; }
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
public ulong ImageLength { get; private set; }
|
|
|
|
|
|
|
|
|
|
public bool IsWIM { get; private set; }
|
|
|
|
|
|
2016-02-24 16:33:18 +01:00
|
|
|
|
public bool IsXvaVersion1 { get; private set; }
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
public ulong DiskCapacity { get; private set; }
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
public void SetFileName(string fileName)
|
|
|
|
|
{
|
|
|
|
|
m_textBoxFile.Text = fileName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region Private methods
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-11-25 17:07:58 +01:00
|
|
|
|
/// Performs certain checks on the page's input data and shows/hides an error accordingly
|
2013-06-24 13:41:48 +02:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="checks">The checks to perform</param>
|
|
|
|
|
private bool PerformCheck(params CheckDelegate[] checks)
|
|
|
|
|
{
|
|
|
|
|
m_buttonNextEnabled = m_ctrlError.PerformCheck(checks);
|
|
|
|
|
OnPageUpdated();
|
|
|
|
|
return m_buttonNextEnabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool GetDiskCapacityXva(out string error)
|
|
|
|
|
{
|
|
|
|
|
error = string.Empty;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2017-06-29 17:19:39 +02:00
|
|
|
|
FileInfo info = new FileInfo(FilePath);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
ImageLength = info.Length > 0 ? (ulong)info.Length : 0;
|
|
|
|
|
|
2016-02-24 16:33:18 +01:00
|
|
|
|
DiskCapacity = IsXvaVersion1
|
|
|
|
|
? GetTotalSizeFromXmlGeneva() //Geneva style
|
|
|
|
|
: GetTotalSizeFromXmlXva(GetXmlStringFromTarXVA()); //xva style
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
|
|
|
|
DiskCapacity = ImageLength;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-24 16:33:18 +01:00
|
|
|
|
private ulong GetTotalSizeFromXmlGeneva()
|
|
|
|
|
{
|
|
|
|
|
ulong totalSize = 0;
|
|
|
|
|
XmlDocument xmlMetadata = new XmlDocument();
|
2017-06-29 17:19:39 +02:00
|
|
|
|
xmlMetadata.Load(FilePath);
|
2016-02-24 16:33:18 +01:00
|
|
|
|
XPathNavigator nav = xmlMetadata.CreateNavigator();
|
|
|
|
|
XPathNodeIterator nodeIterator = nav.Select(".//vdi");
|
|
|
|
|
|
|
|
|
|
while (nodeIterator.MoveNext())
|
|
|
|
|
totalSize += UInt64.Parse(nodeIterator.Current.GetAttribute("size", ""));
|
|
|
|
|
|
|
|
|
|
return totalSize;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
private string GetXmlStringFromTarXVA()
|
|
|
|
|
{
|
2017-06-29 17:19:39 +02:00
|
|
|
|
using (Stream stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read))
|
2019-07-25 11:17:34 +02:00
|
|
|
|
{
|
|
|
|
|
using (var iterator = ArchiveFactory.Reader(ArchiveFactory.Type.Tar, stream))
|
|
|
|
|
if (iterator.HasNext())
|
|
|
|
|
{
|
|
|
|
|
Stream ofs = new MemoryStream();
|
|
|
|
|
iterator.ExtractCurrentFile(ofs);
|
|
|
|
|
return new StreamReader(ofs).ReadToEnd();
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2019-07-25 11:17:34 +02:00
|
|
|
|
return string.Empty;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
private ulong GetTotalSizeFromXmlXva(string xmlString)
|
|
|
|
|
{
|
|
|
|
|
ulong totalSize = 0;
|
|
|
|
|
XmlDocument xmlMetadata = new XmlDocument();
|
|
|
|
|
xmlMetadata.LoadXml(xmlString);
|
|
|
|
|
XPathNavigator nav = xmlMetadata.CreateNavigator();
|
|
|
|
|
XPathNodeIterator nodeIterator = nav.Select(".//name[. = \"virtual_size\"]");
|
|
|
|
|
|
|
|
|
|
while (nodeIterator.MoveNext())
|
|
|
|
|
{
|
|
|
|
|
XPathNavigator vdiNavigator = nodeIterator.Current;
|
|
|
|
|
|
|
|
|
|
if (vdiNavigator.MoveToNext())
|
|
|
|
|
totalSize += UInt64.Parse(vdiNavigator.Value);
|
|
|
|
|
}
|
|
|
|
|
return totalSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool GetDiskCapacityImage(out string error)
|
|
|
|
|
{
|
|
|
|
|
error = string.Empty;
|
2017-06-29 17:19:39 +02:00
|
|
|
|
string filename = FilePath;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
FileInfo info = new FileInfo(filename);
|
|
|
|
|
ImageLength = info.Length > 0 ? (ulong)info.Length : 0;
|
|
|
|
|
|
|
|
|
|
if (IsWIM)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
using (FileStream wimstream = new FileStream(filename, FileMode.Open, FileAccess.Read))
|
|
|
|
|
{
|
|
|
|
|
WimFile wimDisk = new WimFile(wimstream);
|
|
|
|
|
string manifest = wimDisk.Manifest;
|
2019-11-25 17:07:58 +01:00
|
|
|
|
Wim_Manifest wimManifest = Tools.Deserialize<Wim_Manifest>(manifest);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
DiskCapacity = wimManifest.Image[wimDisk.BootImage].TotalBytes; //image data size
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
|
|
|
|
error = Messages.IMAGE_SELECTION_PAGE_ERROR_CORRUPT_FILE;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
using (VirtualDisk vd = VirtualDisk.OpenDisk(filename, FileAccess.Read))
|
|
|
|
|
{
|
|
|
|
|
DiskCapacity = (ulong)vd.Capacity;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (IOException ioe)
|
|
|
|
|
{
|
|
|
|
|
error = ioe.Message.Contains("Invalid VMDK descriptor file")
|
|
|
|
|
? Messages.IMAGE_SELECTION_PAGE_ERROR_INVALID_VMDK_DESCRIPTOR
|
|
|
|
|
: Messages.IMAGE_SELECTION_PAGE_ERROR_INVALID_FILE_TYPE;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
|
|
|
|
error = Messages.IMAGE_SELECTION_PAGE_ERROR_CORRUPT_FILE;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool LoadAppliance(out string error)
|
|
|
|
|
{
|
|
|
|
|
error = string.Empty;
|
|
|
|
|
|
2017-06-29 17:19:39 +02:00
|
|
|
|
if (m_lastValidAppliance == FilePath)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
return true;
|
|
|
|
|
|
2019-11-25 17:07:58 +01:00
|
|
|
|
SelectedOvfPackage = Package.Create(FilePath);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2019-11-25 17:07:58 +01:00
|
|
|
|
if (!OVF.Validate(SelectedOvfPackage, out List<string> warnings))
|
|
|
|
|
{
|
|
|
|
|
error = warnings[warnings.Count - 1];
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2019-11-25 17:07:58 +01:00
|
|
|
|
if (warnings != null && warnings.Count > 0 && !Properties.Settings.Default.IgnoreOvfValidationWarnings)
|
|
|
|
|
{
|
|
|
|
|
using (var dlg = new OvfValidationDialog(warnings))
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2019-11-25 17:07:58 +01:00
|
|
|
|
if (dlg.ShowDialog(this) == DialogResult.Cancel)
|
|
|
|
|
return false;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2019-11-25 17:07:58 +01:00
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2019-11-25 17:07:58 +01:00
|
|
|
|
m_lastValidAppliance = FilePath;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
private bool IsUri()
|
|
|
|
|
{
|
|
|
|
|
Regex regex = new Regex(Messages.URI_REGEX, RegexOptions.IgnoreCase);
|
2017-06-29 17:19:39 +02:00
|
|
|
|
return regex.Match(FilePath).Success;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Check on the fly
|
|
|
|
|
/// </summary>
|
|
|
|
|
private bool CheckPathValid(out string error)
|
|
|
|
|
{
|
|
|
|
|
error = string.Empty;
|
|
|
|
|
|
2017-06-29 17:19:39 +02:00
|
|
|
|
if (String.IsNullOrEmpty(FilePath))
|
2013-06-24 13:41:48 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
2018-09-14 16:55:05 +02:00
|
|
|
|
if (IsUri())
|
|
|
|
|
return CheckDownloadFromUri(out error);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2017-06-29 17:19:39 +02:00
|
|
|
|
if (!PathValidator.IsPathValid(FilePath))
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
error = Messages.IMPORT_SELECT_APPLIANCE_PAGE_ERROR_INVALID_PATH;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Check when we leave the page
|
|
|
|
|
/// </summary>
|
|
|
|
|
private bool CheckIsSupportedType(out string error)
|
|
|
|
|
{
|
|
|
|
|
error = string.Empty;
|
|
|
|
|
IsWIM = false;
|
|
|
|
|
|
2017-06-29 17:19:39 +02:00
|
|
|
|
string filepath = FilePath;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
foreach (var ext in m_supportedXvaTypes)
|
|
|
|
|
{
|
|
|
|
|
if (filepath.ToLower().EndsWith(ext))
|
|
|
|
|
{
|
|
|
|
|
if (OvfModeOnly)
|
|
|
|
|
{
|
|
|
|
|
error = Messages.IMPORT_SOURCE_PAGE_ERROR_OVF_ONLY;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-24 16:33:18 +01:00
|
|
|
|
if (ext == "ova.xml")
|
|
|
|
|
IsXvaVersion1 = true;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
TypeOfImport = ImportWizard.ImportType.Xva;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var ext in m_supportedApplianceTypes)
|
|
|
|
|
{
|
|
|
|
|
if (filepath.ToLower().EndsWith(ext))
|
|
|
|
|
{
|
|
|
|
|
TypeOfImport = ImportWizard.ImportType.Ovf;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var ext in m_supportedImageTypes)
|
|
|
|
|
{
|
|
|
|
|
if (filepath.ToLower().EndsWith(ext))
|
|
|
|
|
{
|
|
|
|
|
if (OvfModeOnly)
|
|
|
|
|
{
|
|
|
|
|
error = Messages.IMPORT_SOURCE_PAGE_ERROR_OVF_ONLY;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ext == ".wim")
|
|
|
|
|
IsWIM = true;
|
|
|
|
|
|
|
|
|
|
TypeOfImport = ImportWizard.ImportType.Vhd;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
error = Messages.ERROR_UNSUPPORTED_FILE_TYPE;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Check when we leave the page
|
|
|
|
|
/// </summary>
|
|
|
|
|
private bool CheckPathExists(out string error)
|
|
|
|
|
{
|
|
|
|
|
error = string.Empty;
|
|
|
|
|
|
2017-06-29 17:19:39 +02:00
|
|
|
|
if (File.Exists(FilePath))
|
2013-06-24 13:41:48 +02:00
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
error = Messages.IMPORT_SELECT_APPLIANCE_PAGE_ERROR_NONE_EXIST_PATH;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Check the source folder is writable
|
|
|
|
|
/// </summary>
|
|
|
|
|
private bool CheckSourceIsWritable(out string error)
|
|
|
|
|
{
|
|
|
|
|
error = string.Empty;
|
|
|
|
|
|
2017-06-29 17:19:39 +02:00
|
|
|
|
var directory = Path.GetDirectoryName(FilePath);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
var touchFile = Path.Combine(directory, "_");
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
//attempt writing in the destination directory
|
|
|
|
|
using (FileStream fs = File.OpenWrite(touchFile))
|
|
|
|
|
{
|
|
|
|
|
//it works
|
|
|
|
|
}
|
|
|
|
|
File.Delete(touchFile);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (UnauthorizedAccessException)
|
|
|
|
|
{
|
|
|
|
|
error = Messages.IMPORT_SELECT_APPLIANCE_PAGE_ERROR_SOURCE_IS_READONLY;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-14 16:55:05 +02:00
|
|
|
|
private bool CheckDownloadFromUri(out string error)
|
|
|
|
|
{
|
|
|
|
|
error = string.Empty;
|
2016-07-22 09:31:58 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
2018-09-14 16:55:05 +02:00
|
|
|
|
_uri = new Uri(FilePath);
|
|
|
|
|
return true;
|
2016-07-22 09:31:58 +02:00
|
|
|
|
}
|
|
|
|
|
catch (ArgumentNullException)
|
|
|
|
|
{
|
|
|
|
|
error = Messages.IMPORT_SELECT_APPLIANCE_PAGE_ERROR_INVALID_URI;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
catch (UriFormatException)
|
|
|
|
|
{
|
|
|
|
|
error = Messages.IMPORT_SELECT_APPLIANCE_PAGE_ERROR_INVALID_URI;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-09-14 16:55:05 +02:00
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2018-08-09 11:54:32 +02:00
|
|
|
|
#endregion
|
|
|
|
|
|
2018-09-14 16:55:05 +02:00
|
|
|
|
#region Download and Uncompression
|
|
|
|
|
|
|
|
|
|
private bool Download()
|
|
|
|
|
{
|
2019-08-30 19:58:10 +02:00
|
|
|
|
using (var dlog = new FolderBrowserDialog
|
|
|
|
|
{
|
|
|
|
|
Description = Messages.FOLDER_BROWSER_DOWNLOAD_APPLIANCE,
|
|
|
|
|
SelectedPath = Win32.GetKnownFolderPath(Win32.KnownFolders.Downloads)
|
|
|
|
|
})
|
2018-09-14 16:55:05 +02:00
|
|
|
|
{
|
|
|
|
|
if (dlog.ShowDialog() == DialogResult.OK)
|
|
|
|
|
{
|
|
|
|
|
if (_webClient == null)
|
|
|
|
|
{
|
|
|
|
|
_webClient = new WebClient { Proxy = XenAdminConfigManager.Provider.GetProxyFromSettings(null, false) };
|
|
|
|
|
_webClient.DownloadFileCompleted += webclient_DownloadFileCompleted;
|
|
|
|
|
_webClient.DownloadProgressChanged += webclient_DownloadProgressChanged;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_downloadFolder = dlog.SelectedPath;
|
|
|
|
|
var downloadedPath = Path.Combine(_downloadFolder, Path.GetFileName(_uri.AbsolutePath));
|
|
|
|
|
if (string.IsNullOrEmpty(Path.GetExtension(downloadedPath))) //CA-41747
|
|
|
|
|
downloadedPath += ".ovf";
|
|
|
|
|
|
|
|
|
|
var file = new ApplianceFile(_uri, downloadedPath);
|
|
|
|
|
_filesToDownload = new Queue<ApplianceFile>();
|
|
|
|
|
_filesToDownload.Enqueue(file);
|
|
|
|
|
|
|
|
|
|
LongProcessWrapper(() => _webClient.DownloadFileAsync(_uri, downloadedPath, file),
|
|
|
|
|
string.Format(Messages.IMPORT_WIZARD_DOWNLOADING, Path.GetFileName(downloadedPath).Ellipsise(50)));
|
|
|
|
|
return m_textBoxFile.Text == downloadedPath;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-09 11:54:32 +02:00
|
|
|
|
|
|
|
|
|
private bool Uncompress()
|
|
|
|
|
{
|
|
|
|
|
_unzipFileIn = FilePath;
|
|
|
|
|
if (string.IsNullOrEmpty(_unzipFileIn))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
_unzipFileOut = Path.Combine(Path.GetDirectoryName(_unzipFileIn), Path.GetFileNameWithoutExtension(_unzipFileIn));
|
|
|
|
|
|
|
|
|
|
var msg = string.Format(Messages.UNCOMPRESS_APPLIANCE_DESCRIPTION,
|
|
|
|
|
Path.GetFileName(_unzipFileOut), Path.GetFileName(_unzipFileIn));
|
|
|
|
|
|
|
|
|
|
using (var dlog = new ThreeButtonDialog(new ThreeButtonDialog.Details(SystemIcons.Exclamation, msg, Messages.XENCENTER),
|
|
|
|
|
new ThreeButtonDialog.TBDButton(Messages.YES, DialogResult.Yes),
|
|
|
|
|
new ThreeButtonDialog.TBDButton(Messages.NO, DialogResult.No)))
|
|
|
|
|
{
|
|
|
|
|
if (dlog.ShowDialog(this) == DialogResult.Yes)
|
2018-09-14 16:55:05 +02:00
|
|
|
|
{
|
|
|
|
|
LongProcessWrapper(() => _unzipWorker.RunWorkerAsync(),
|
|
|
|
|
string.Format(Messages.IMPORT_WIZARD_UNCOMPRESSING, Path.GetFileName(_unzipFileIn).Ellipsise(50)));
|
|
|
|
|
return m_textBoxFile.Text == _unzipFileOut;
|
|
|
|
|
}
|
2018-08-09 11:54:32 +02:00
|
|
|
|
|
2018-09-14 16:55:05 +02:00
|
|
|
|
return false;
|
2018-08-09 11:54:32 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-14 16:55:05 +02:00
|
|
|
|
private void LongProcessWrapper(Action process, string processMessage)
|
2018-08-09 11:54:32 +02:00
|
|
|
|
{
|
|
|
|
|
m_textBoxFile.Enabled = false;
|
|
|
|
|
m_buttonBrowse.Enabled = false;
|
|
|
|
|
m_buttonNextEnabled = false;
|
|
|
|
|
OnPageUpdated();
|
|
|
|
|
|
|
|
|
|
m_tlpError.Visible = false;
|
2018-09-14 16:55:05 +02:00
|
|
|
|
labelProgress.Text = processMessage;
|
2018-08-09 11:54:32 +02:00
|
|
|
|
labelProgress.Visible = true;
|
|
|
|
|
progressBar1.Visible = true;
|
|
|
|
|
progressBar1.Value = 0;
|
2018-09-14 16:55:05 +02:00
|
|
|
|
|
|
|
|
|
longProcessInProgress = true;
|
|
|
|
|
process.Invoke();
|
|
|
|
|
while (longProcessInProgress)
|
2018-08-09 11:54:32 +02:00
|
|
|
|
Application.DoEvents();
|
|
|
|
|
|
|
|
|
|
m_textBoxFile.Enabled = true;
|
|
|
|
|
m_buttonBrowse.Enabled = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Event handlers
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2018-09-14 16:55:05 +02:00
|
|
|
|
private void m_buttonBrowse_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
using (FileDialog ofd = new OpenFileDialog
|
|
|
|
|
{
|
|
|
|
|
CheckFileExists = true,
|
|
|
|
|
CheckPathExists = true,
|
|
|
|
|
DereferenceLinks = true,
|
|
|
|
|
Filter = OvfModeOnly ? Messages.IMPORT_SOURCE_PAGE_FILETYPES_OVF_ONLY : Messages.IMPORT_SOURCE_PAGE_FILETYPES,
|
|
|
|
|
RestoreDirectory = true,
|
|
|
|
|
Multiselect = false,
|
|
|
|
|
})
|
|
|
|
|
{
|
|
|
|
|
if (ofd.ShowDialog() == DialogResult.OK)
|
|
|
|
|
m_textBoxFile.Text = ofd.FileName;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
private void m_textBoxImage_TextChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
m_tlpEncryption.Visible = false;
|
2018-09-14 16:55:05 +02:00
|
|
|
|
m_tlpError.Visible = false;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
PerformCheck(CheckPathValid);
|
|
|
|
|
IsDirty = true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-09 11:54:32 +02:00
|
|
|
|
|
|
|
|
|
private void _unzipWorker_DoWork(object sender, DoWorkEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
FileInfo info = new FileInfo(_unzipFileIn);
|
|
|
|
|
long length = info.Length;
|
|
|
|
|
|
|
|
|
|
using (Stream inStream = File.Open(_unzipFileIn, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
|
|
|
|
|
using (Stream outStream = File.Open(_unzipFileOut, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
using (CompressionStream bzis = CompressionFactory.Reader(CompressionFactory.Type.Gz, inStream))
|
|
|
|
|
{
|
|
|
|
|
byte[] buffer = new byte[4 * 1024];
|
|
|
|
|
|
|
|
|
|
int bytesRead;
|
|
|
|
|
while ((bytesRead = bzis.Read(buffer, 0, buffer.Length)) > 0)
|
|
|
|
|
{
|
|
|
|
|
outStream.Write(buffer, 0, bytesRead);
|
|
|
|
|
|
|
|
|
|
int percentage = (int)Math.Floor((double)bzis.Position * 100 / length);
|
|
|
|
|
|
|
|
|
|
if (percentage < 0)
|
|
|
|
|
_unzipWorker.ReportProgress(0);
|
|
|
|
|
else if (percentage > 100)
|
|
|
|
|
_unzipWorker.ReportProgress(100);
|
|
|
|
|
else
|
|
|
|
|
_unzipWorker.ReportProgress(percentage);
|
|
|
|
|
|
|
|
|
|
if (_unzipWorker.CancellationPending)
|
|
|
|
|
{
|
|
|
|
|
e.Cancel = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e.Result = _unzipFileOut;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
log.Error(string.Format("Error while uncompressing {0} to {1}", _unzipFileIn, _unzipFileOut), ex);
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
outStream.Flush();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void _unzipWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
progressBar1.Value = e.ProgressPercentage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void _unzipWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
progressBar1.Visible = false;
|
|
|
|
|
labelProgress.Visible = false;
|
|
|
|
|
|
2018-09-14 16:55:05 +02:00
|
|
|
|
if (e.Cancelled || e.Error != null)
|
2018-08-09 11:54:32 +02:00
|
|
|
|
{
|
2018-09-14 16:55:05 +02:00
|
|
|
|
if (e.Error != null)
|
2018-08-09 11:54:32 +02:00
|
|
|
|
{
|
2018-09-14 16:55:05 +02:00
|
|
|
|
_labelError.Text = string.Format(Messages.IMPORT_WIZARD_FAILED_UNCOMPRESS,
|
|
|
|
|
Path.GetFileName(_unzipFileIn).Ellipsise(50));
|
|
|
|
|
m_tlpError.Visible = true;
|
2018-08-09 11:54:32 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
File.Delete(_unzipFileOut);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
log.Error(string.Format("Failed to delete uncompressed file {0}.", _unzipFileOut), ex);
|
|
|
|
|
}
|
2018-09-14 16:55:05 +02:00
|
|
|
|
|
|
|
|
|
longProcessInProgress = false;
|
2018-08-09 11:54:32 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var uncompressedFile = e.Result as string;
|
|
|
|
|
progressBar1.Value = 100;
|
|
|
|
|
m_textBoxFile.Text = uncompressedFile;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
File.Delete(_unzipFileIn);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
log.Error(string.Format("Failed to delete compressed file {0}.", _unzipFileIn), ex);
|
|
|
|
|
}
|
2018-09-14 16:55:05 +02:00
|
|
|
|
longProcessInProgress = false;
|
2018-08-09 11:54:32 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-09-14 16:55:05 +02:00
|
|
|
|
|
|
|
|
|
private void webclient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
Program.Invoke(this, () => progressBar1.Value = e.ProgressPercentage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void webclient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
Program.Invoke(this, () =>
|
|
|
|
|
{
|
|
|
|
|
var appfile = (ApplianceFile)e.UserState;
|
|
|
|
|
|
|
|
|
|
if (e.Cancelled)
|
|
|
|
|
{
|
|
|
|
|
longProcessInProgress = false;
|
|
|
|
|
}
|
|
|
|
|
else if (e.Error != null) // failure
|
|
|
|
|
{
|
|
|
|
|
progressBar1.Visible = false;
|
|
|
|
|
labelProgress.Visible = false;
|
|
|
|
|
_labelError.Text = string.Format(Messages.IMPORT_WIZARD_FAILED_DOWNLOAD,
|
|
|
|
|
Path.GetFileName(appfile.LocalPath));
|
|
|
|
|
m_tlpError.Visible = true;
|
|
|
|
|
log.Error(string.Format("Failed to download file {0}.", appfile), e.Error);
|
|
|
|
|
longProcessInProgress = false;
|
|
|
|
|
}
|
|
|
|
|
else // success
|
|
|
|
|
{
|
|
|
|
|
if (_filesToDownload.Peek() == appfile)
|
|
|
|
|
_filesToDownload.Dequeue();
|
|
|
|
|
|
|
|
|
|
if (appfile.LocalPath.ToLower().EndsWith(".ovf"))
|
|
|
|
|
{
|
2019-11-25 17:07:58 +01:00
|
|
|
|
var envType = Tools.DeserializeOvfXml(Tools.LoadFile(appfile.LocalPath));
|
2018-09-14 16:55:05 +02:00
|
|
|
|
|
|
|
|
|
if (envType != null)
|
|
|
|
|
{
|
|
|
|
|
int index = _uri.OriginalString.LastIndexOf('/') + 1;
|
|
|
|
|
var remoteDir = _uri.OriginalString.Substring(0, index);
|
|
|
|
|
|
|
|
|
|
foreach (var file in envType.References.File)
|
|
|
|
|
{
|
|
|
|
|
var remoteUri = new Uri(remoteDir + file.href);
|
|
|
|
|
var localPath = Path.Combine(_downloadFolder, file.href);
|
|
|
|
|
_filesToDownload.Enqueue(new ApplianceFile(remoteUri, localPath));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_filesToDownload.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
progressBar1.Value = 100;
|
|
|
|
|
progressBar1.Visible = false;
|
|
|
|
|
labelProgress.Visible = false;
|
|
|
|
|
m_textBoxFile.Text = Path.Combine(_downloadFolder, Path.GetFileName(_uri.AbsolutePath));
|
|
|
|
|
longProcessInProgress = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
progressBar1.Value = 0;
|
|
|
|
|
var file = _filesToDownload.Peek();
|
|
|
|
|
labelProgress.Text = string.Format(Messages.IMPORT_WIZARD_DOWNLOADING,
|
|
|
|
|
Path.GetFileName(file.LocalPath).Ellipsise(50));
|
|
|
|
|
_webClient.DownloadFileAsync(file.RemoteUri, file.LocalPath, file);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
private class ApplianceFile
|
|
|
|
|
{
|
|
|
|
|
public ApplianceFile(Uri remoteUri, string localPath)
|
|
|
|
|
{
|
|
|
|
|
RemoteUri = remoteUri;
|
|
|
|
|
LocalPath = localPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Uri RemoteUri { get; private set; }
|
|
|
|
|
|
|
|
|
|
public string LocalPath { get; private set; }
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|