CP-10797: Ensuring sufficient disk space for service pack installation: In the Install Update Wizard check if there is enough space to upload a hotfix

- Before starting the upload to the master hosts, check if there is enough disk space (check only performed for Cream or greater hosts)
- If enough space available the upload starts automatically; otherwise an error is displayed
- If we can free up enough disk space then we offer the option to Clean up. Otherwise we provide the user with the information on required and available space and the user will have to manually free up required space.

- Also Disable the Upload page for oem updates

Signed-off-by: Mihaela Stoica <mihaela.stoica@citrix.com>
This commit is contained in:
Mihaela Stoica 2015-02-05 17:01:14 +00:00
parent b09be18bcd
commit 48636e12d6
10 changed files with 832 additions and 102 deletions

View File

@ -98,6 +98,7 @@ namespace XenAdmin.Wizards.PatchingWizard
var existPatch = PatchingWizard_SelectPatchPage.SelectedExistingPatch;
DisablePage(PatchingWizard_PrecheckPage, updateType == UpdateType.NewOem);
DisablePage(PatchingWizard_UploadPage, updateType == UpdateType.NewOem);
PatchingWizard_SelectServers.SelectedUpdateType = updateType;
PatchingWizard_SelectServers.Patch = existPatch;

View File

@ -34,6 +34,7 @@
this.labelProgress = new System.Windows.Forms.Label();
this.progressBar1 = new System.Windows.Forms.ProgressBar();
this.flickerFreeListBox1 = new XenAdmin.Controls.FlickerFreeListBox();
this.diskSpaceErrorLinkLabel = new System.Windows.Forms.LinkLabel();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
@ -44,10 +45,12 @@
this.tableLayoutPanel1.Controls.Add(this.labelProgress, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.progressBar1, 0, 3);
this.tableLayoutPanel1.Controls.Add(this.flickerFreeListBox1, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.diskSpaceErrorLinkLabel, 1, 2);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
//
// label2
//
this.tableLayoutPanel1.SetColumnSpan(this.label2, 2);
resources.ApplyResources(this.label2, "label2");
this.label2.Name = "label2";
//
@ -58,19 +61,26 @@
//
// progressBar1
//
this.tableLayoutPanel1.SetColumnSpan(this.progressBar1, 2);
resources.ApplyResources(this.progressBar1, "progressBar1");
this.progressBar1.Name = "progressBar1";
//
// flickerFreeListBox1
//
this.tableLayoutPanel1.SetColumnSpan(this.flickerFreeListBox1, 2);
resources.ApplyResources(this.flickerFreeListBox1, "flickerFreeListBox1");
this.flickerFreeListBox1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
this.flickerFreeListBox1.FormattingEnabled = true;
this.flickerFreeListBox1.Items.AddRange(new object[] {
resources.GetString("flickerFreeListBox1.Items")});
this.flickerFreeListBox1.Name = "flickerFreeListBox1";
this.flickerFreeListBox1.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.flickerFreeListBox1_DrawItem);
//
// diskSpaceErrorLinkLabel
//
resources.ApplyResources(this.diskSpaceErrorLinkLabel, "diskSpaceErrorLinkLabel");
this.diskSpaceErrorLinkLabel.Name = "diskSpaceErrorLinkLabel";
this.diskSpaceErrorLinkLabel.TabStop = true;
this.diskSpaceErrorLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.diskspaceErrorLinkLabel_LinkClicked);
//
// PatchingWizard_UploadPage
//
resources.ApplyResources(this, "$this");
@ -78,7 +88,6 @@
this.Controls.Add(this.tableLayoutPanel1);
this.Name = "PatchingWizard_UploadPage";
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ResumeLayout(false);
}
@ -90,5 +99,6 @@
private System.Windows.Forms.Label labelProgress;
private System.Windows.Forms.ProgressBar progressBar1;
private Controls.FlickerFreeListBox flickerFreeListBox1;
private System.Windows.Forms.LinkLabel diskSpaceErrorLinkLabel;
}
}

View File

@ -5,6 +5,7 @@ using System.Windows.Forms;
using XenAdmin.Actions;
using XenAdmin.Controls;
using XenAdmin.Core;
using XenAdmin.Dialogs;
using XenAPI;
namespace XenAdmin.Wizards.PatchingWizard
@ -49,12 +50,15 @@ namespace XenAdmin.Wizards.PatchingWizard
if (SelectedUpdateType == UpdateType.Existing)
_patch = SelectedExistingPatch;
if (direction == PageLoadedDirection.Forward)
StartUploading();
{
PrepareUploadActions();
TryUploading();
}
}
public override void PageCancelled()
{
foreach (var action in uploadActions.Where(action => !action.IsCompleted))
foreach (var action in uploadActions.Values.Where(action => action != null && !action.IsCompleted))
{
CancelAction(action);
}
@ -62,101 +66,181 @@ namespace XenAdmin.Wizards.PatchingWizard
public override bool EnableNext()
{
return uploadActions.Count == 0 || uploadActions.All(action => action.Succeeded);
return uploadActions.Values.All(action => action == null || action.Succeeded);
}
public override bool EnablePrevious()
{
return uploadActions.Count == 0 || uploadActions.All(action => action.IsCompleted);
return !canUpload || uploadActions.Values.All(action => action == null || action.IsCompleted);
}
private List<AsyncAction> uploadActions = new List<AsyncAction>();
private Dictionary<Host, AsyncAction> uploadActions = new Dictionary<Host, AsyncAction>();
private void StartUploading()
private static bool PatchExistsOnPool(Pool_patch patch, Host poolMaster)
{
var poolPatches = new List<Pool_patch>(poolMaster.Connection.Cache.Pool_patches);
return (poolPatches.Exists(p => p.uuid == patch.uuid));
}
private void PrepareUploadActions()
{
OnPageUpdated();
SuppPackVdis.Clear();
uploadActions.Clear();
//Upload the patches to the masters if it is necessary
List<Host> masters = SelectedMasters;
switch (SelectedUpdateType)
foreach (Host selectedServer in masters)
{
case UpdateType.NewRetail:
foreach (Host selectedServer in masters)
{
Host master = Helpers.GetMaster(selectedServer.Connection);
UploadPatchAction action = new UploadPatchAction(master.Connection, SelectedNewPatch, true);
action.Changed += singleAction_Changed;
action.Completed += singleAction_Completed;
uploadActions.Add(action);
}
break;
case UpdateType.Existing:
foreach (Host selectedServer in masters)
{
List<Pool_patch> poolPatches = new List<Pool_patch>(selectedServer.Connection.Cache.Pool_patches);
if (poolPatches.Find(patch => patch.uuid == SelectedExistingPatch.uuid) == null)
AsyncAction action = null;
switch (SelectedUpdateType)
{
case UpdateType.NewRetail:
action = new UploadPatchAction(selectedServer.Connection, SelectedNewPatch, true);
break;
case UpdateType.Existing:
if (!PatchExistsOnPool(SelectedExistingPatch, selectedServer))
{
//Download patch from server Upload in the selected server
var action = new CopyPatchFromHostToOther(SelectedExistingPatch.Connection,
selectedServer, SelectedExistingPatch);
action.Changed += singleAction_Changed;
action.Completed += singleAction_Completed;
uploadActions.Add(action);
action = new CopyPatchFromHostToOther(SelectedExistingPatch.Connection, selectedServer,
SelectedExistingPatch);
}
}
break;
case UpdateType.NewSuppPack:
SuppPackVdis.Clear();
foreach (Host selectedServer in masters)
{
UploadSupplementalPackAction action = new UploadSupplementalPackAction(
selectedServer.Connection,
SelectedServers.Where(s => s.Connection == selectedServer.Connection).ToList(),
break;
case UpdateType.NewSuppPack:
action = new UploadSupplementalPackAction(
selectedServer.Connection,
SelectedServers.Where(s => s.Connection == selectedServer.Connection).ToList(),
SelectedNewPatch,
true);
action.Changed += singleAction_Changed;
action.Completed += singleAction_Completed;
uploadActions.Add(action);
}
break;
break;
}
if (action != null)
{
action.Changed += singleAction_Changed;
action.Completed += singleAction_Completed;
}
uploadActions.Add(selectedServer, action);
}
if (uploadActions.Count > 0)
flickerFreeListBox1.Items.Clear();
foreach (KeyValuePair<Host, AsyncAction> uploadAction in uploadActions)
{
flickerFreeListBox1.Items.Clear();
labelProgress.Text = "";
progressBar1.Value = 0;
flickerFreeListBox1.Items.AddRange(uploadActions.ToArray());
flickerFreeListBox1.Items.Add(uploadAction);
}
flickerFreeListBox1.Refresh();
OnPageUpdated();
RunMultipleActions(Messages.UPLOAD_PATCH_TITLE, Messages.UPLOAD_PATCH_DESCRIPTION, Messages.UPLOAD_PATCH_END_DESCRIPTION, uploadActions);
}
private void RunMultipleActions(string title, string startDescription, string endDescription,
List<AsyncAction> subActions)
private bool canUpload = true;
private DiskSpaceRequirements diskSpaceRequirements;
private void TryUploading()
{
if (subActions.Count > 0)
// reset progress bar and action progress description
UpdateActionProgress(null);
// Check if we can upload the patches to the masters if it is necessary.
// This check is only available for Cream or greater hosts.
// If we can upload (i.e. there is enough disk space) then start the upload.
// Otherwise display error.
canUpload = true;
diskSpaceRequirements = null;
var diskSpaceActions = new List<AsyncAction>();
foreach (Host master in SelectedMasters.Where(master => Helpers.CreamOrGreater(master.Connection)))
{
using (var multipleAction = new MultipleAction(Connection, title, startDescription,
endDescription, subActions, true, true, true))
AsyncAction action = null;
switch (SelectedUpdateType)
{
multipleAction.Completed += multipleAction_Completed;
multipleAction.RunAsync();
case UpdateType.NewRetail:
action = new CheckDiskSpaceForPatchUploadAction(master, SelectedNewPatch, true);
break;
case UpdateType.Existing:
if (SelectedExistingPatch != null && !PatchExistsOnPool(SelectedExistingPatch, master))
action = new CheckDiskSpaceForPatchUploadAction(master, SelectedExistingPatch, true);
break;
}
if (action != null)
{
action.Changed += delegate
{
Program.Invoke(Program.MainWindow, () => UpdateActionProgress(action));
};
diskSpaceActions.Add(action);
}
}
if (diskSpaceActions.Count == 0)
{
StartUploading();
return;
}
using (var multipleAction = new MultipleAction(Connection, "", "", "", diskSpaceActions, true, true, true))
{
multipleAction.Completed += delegate
{
Program.Invoke(Program.MainWindow, () =>
{
if (multipleAction.Exception is NotEnoughSpaceException)
{
canUpload = false;
diskSpaceRequirements = (multipleAction.Exception as NotEnoughSpaceException).DiskSpaceRequirements;
}
UpdateButtons();
OnPageUpdated();
if (canUpload)
StartUploading();
});
};
multipleAction.RunAsync();
}
}
private void StartUploading()
{
// reset progress bar and action progress description
UpdateActionProgress(null);
// start the upload
var actions = uploadActions.Values.Where(a => a != null).ToList();
if (actions.Count == 0)
return;
using (var multipleAction = new MultipleAction(Connection, Messages.UPLOAD_PATCH_TITLE, Messages.UPLOAD_PATCH_DESCRIPTION, Messages.UPLOAD_PATCH_END_DESCRIPTION, actions, true, true, true))
{
multipleAction.Completed += multipleAction_Completed;
multipleAction.RunAsync();
}
}
private void UpdateButtons()
{
if (!canUpload && diskSpaceRequirements != null)
{
diskSpaceErrorLinkLabel.Visible = true;
diskSpaceErrorLinkLabel.Text = diskSpaceRequirements.CanCleanup ? Messages.PATCHINGWIZARD_CLEANUP : Messages.PATCHINGWIZARD_MORE_INFO;
}
else
diskSpaceErrorLinkLabel.Visible = false;
}
private void UpdateActionProgress(AsyncAction action)
{
if (action == null) // reset progress
{
progressBar1.Value = 0;
labelProgress.Text = "";
labelProgress.ForeColor = Color.Black;
}
else if (action.StartedRunning) // update progress and description for started actions
{
progressBar1.Value = action.PercentComplete;
labelProgress.Text = GetActionDescription(action);
labelProgress.ForeColor = !action.IsCompleted || action.Succeeded ? Color.Black : Color.Red;
}
}
@ -185,8 +269,7 @@ namespace XenAdmin.Wizards.PatchingWizard
Program.Invoke(this, () =>
{
progressBar1.Value = action.PercentComplete;
labelProgress.Text = GetActionDescription(action);
UpdateActionProgress(action);
flickerFreeListBox1.Refresh();
OnPageUpdated();
});
@ -244,37 +327,72 @@ namespace XenAdmin.Wizards.PatchingWizard
{
if (e.Index < 0)
return;
AsyncAction action = flickerFreeListBox1.Items[e.Index] as AsyncAction;
if (action == null)
{
Drawing.DrawText(e.Graphics, Messages.UPLOAD_PATCH_ALREADY_UPLOADED, flickerFreeListBox1.Font, new Rectangle(e.Bounds.Left, e.Bounds.Top, e.Bounds.Width, e.Bounds.Height), Color.Green, flickerFreeListBox1.BackColor);
return;
}
Host host = action.Host;
var hostAndAction = (KeyValuePair<Host, AsyncAction>)flickerFreeListBox1.Items[e.Index];
Host host = hostAndAction.Key;
if (host == null)
return;
AsyncAction action = hostAndAction.Value;
using (SolidBrush backBrush = new SolidBrush(flickerFreeListBox1.BackColor))
{
e.Graphics.FillRectangle(backBrush, e.Bounds);
}
var pool = Helpers.GetPool(host.Connection);
e.Graphics.DrawImage(pool != null ? Images.GetImage16For(pool) : Images.GetImage16For(host),
e.Bounds.Left, e.Bounds.Top);
var poolOrHost = Helpers.GetPool(host.Connection) ?? (IXenObject)host;
string text = GetActionDescription(action);
string text = action == null ? Messages.UPLOAD_PATCH_ALREADY_UPLOADED : GetActionDescription(action);
int width = Drawing.MeasureText(text, flickerFreeListBox1.Font).Width;
Color textColor = GetTextColor(action);
Drawing.DrawText(e.Graphics, pool != null ? pool.Name : host.Name, flickerFreeListBox1.Font, new Rectangle(e.Bounds.Left + Properties.Resources._000_Server_h32bit_16.Width, e.Bounds.Top, e.Bounds.Right - (width + Properties.Resources._000_Server_h32bit_16.Width), e.Bounds.Height), flickerFreeListBox1.ForeColor, TextFormatFlags.Left | TextFormatFlags.EndEllipsis);
e.Graphics.DrawImage(Images.GetImage16For(poolOrHost), e.Bounds.Left, e.Bounds.Top);
Drawing.DrawText(e.Graphics, poolOrHost.Name, flickerFreeListBox1.Font, new Rectangle(e.Bounds.Left + Properties.Resources._000_Server_h32bit_16.Width, e.Bounds.Top, e.Bounds.Right - (width + Properties.Resources._000_Server_h32bit_16.Width), e.Bounds.Height), flickerFreeListBox1.ForeColor, TextFormatFlags.Left | TextFormatFlags.EndEllipsis);
Drawing.DrawText(e.Graphics, text, flickerFreeListBox1.Font, new Rectangle(e.Bounds.Right - width, e.Bounds.Top, width, e.Bounds.Height), textColor, flickerFreeListBox1.BackColor);
}
private Color GetTextColor(AsyncAction action)
{
Color textColor;
if (!action.StartedRunning) // not started yet
if (action == null || !action.StartedRunning) // not started yet
textColor = flickerFreeListBox1.ForeColor;
else if (!action.IsCompleted) // in progress
textColor = Color.Blue;
else textColor = action.Succeeded ? Color.Green : Color.Red; // completed
return textColor;
}
Drawing.DrawText(e.Graphics, text, flickerFreeListBox1.Font, new Rectangle(e.Bounds.Right - width, e.Bounds.Top, width, e.Bounds.Height), textColor, flickerFreeListBox1.BackColor);
private void diskspaceErrorLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
if (diskSpaceRequirements == null)
return;
if (diskSpaceRequirements.CanCleanup)
{
ThreeButtonDialog d = new ThreeButtonDialog(
new ThreeButtonDialog.Details(SystemIcons.Warning, diskSpaceRequirements.GetSpaceRequirementsMessage()),
new ThreeButtonDialog.TBDButton(Messages.OK, DialogResult.OK),
new ThreeButtonDialog.TBDButton(Messages.CANCEL, DialogResult.Cancel));
if (d.ShowDialog(this) == DialogResult.OK)
{
// do the cleanup and retry uploading
CleanupDiskSpaceAction action = new CleanupDiskSpaceAction(diskSpaceRequirements.Host, null, true);
action.Completed += delegate
{
if (action.Succeeded)
{
Program.Invoke(Program.MainWindow, TryUploading);
}
};
action.RunAsync();
}
}
else
{
new ThreeButtonDialog(
new ThreeButtonDialog.Details(SystemIcons.Warning, diskSpaceRequirements.GetSpaceRequirementsMessage()))
.ShowDialog(this);
}
}
}
}

View File

@ -119,7 +119,7 @@
</resheader>
<assembly alias="mscorlib" name="mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="tableLayoutPanel1.ColumnCount" type="System.Int32, mscorlib">
<value>1</value>
<value>2</value>
</data>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="label2.Dock" type="System.Windows.Forms.DockStyle, System.Windows.Forms">
@ -157,9 +157,6 @@ Please wait for this operation to complete, then click Next to continue with the
<data name="&gt;&gt;label2.ZOrder" xml:space="preserve">
<value>0</value>
</data>
<data name="labelProgress.AutoSize" type="System.Boolean, mscorlib">
<value>True</value>
</data>
<data name="labelProgress.Dock" type="System.Windows.Forms.DockStyle, System.Windows.Forms">
<value>Fill</value>
</data>
@ -167,7 +164,7 @@ Please wait for this operation to complete, then click Next to continue with the
<value>NoControl</value>
</data>
<data name="labelProgress.Location" type="System.Drawing.Point, System.Drawing">
<value>3, 357</value>
<value>3, 354</value>
</data>
<data name="labelProgress.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
<value>3, 3, 3, 3</value>
@ -176,7 +173,7 @@ Please wait for this operation to complete, then click Next to continue with the
<value>1, 1, 1, 1</value>
</data>
<data name="labelProgress.Size" type="System.Drawing.Size, System.Drawing">
<value>694, 15</value>
<value>618, 18</value>
</data>
<data name="labelProgress.TabIndex" type="System.Int32, mscorlib">
<value>0</value>
@ -232,14 +229,11 @@ Please wait for this operation to complete, then click Next to continue with the
<data name="flickerFreeListBox1.ItemHeight" type="System.Int32, mscorlib">
<value>18</value>
</data>
<data name="flickerFreeListBox1.Items" xml:space="preserve">
<value>Nothing to upload</value>
</data>
<data name="flickerFreeListBox1.Location" type="System.Drawing.Point, System.Drawing">
<value>3, 40</value>
</data>
<data name="flickerFreeListBox1.Size" type="System.Drawing.Size, System.Drawing">
<value>694, 311</value>
<value>694, 308</value>
</data>
<data name="flickerFreeListBox1.TabIndex" type="System.Int32, mscorlib">
<value>2</value>
@ -256,6 +250,42 @@ Please wait for this operation to complete, then click Next to continue with the
<data name="&gt;&gt;flickerFreeListBox1.ZOrder" xml:space="preserve">
<value>3</value>
</data>
<data name="diskSpaceErrorLinkLabel.Dock" type="System.Windows.Forms.DockStyle, System.Windows.Forms">
<value>Fill</value>
</data>
<data name="diskSpaceErrorLinkLabel.Location" type="System.Drawing.Point, System.Drawing">
<value>627, 354</value>
</data>
<data name="diskSpaceErrorLinkLabel.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
<value>3, 3, 3, 3</value>
</data>
<data name="diskSpaceErrorLinkLabel.Padding" type="System.Windows.Forms.Padding, System.Windows.Forms">
<value>1, 1, 1, 1</value>
</data>
<data name="diskSpaceErrorLinkLabel.Size" type="System.Drawing.Size, System.Drawing">
<value>70, 18</value>
</data>
<data name="diskSpaceErrorLinkLabel.TabIndex" type="System.Int32, mscorlib">
<value>2</value>
</data>
<data name="diskSpaceErrorLinkLabel.Text" xml:space="preserve">
<value>&amp;More info...</value>
</data>
<data name="diskSpaceErrorLinkLabel.Visible" type="System.Boolean, mscorlib">
<value>False</value>
</data>
<data name="&gt;&gt;diskSpaceErrorLinkLabel.Name" xml:space="preserve">
<value>diskSpaceErrorLinkLabel</value>
</data>
<data name="&gt;&gt;diskSpaceErrorLinkLabel.Type" xml:space="preserve">
<value>System.Windows.Forms.LinkLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="&gt;&gt;diskSpaceErrorLinkLabel.Parent" xml:space="preserve">
<value>tableLayoutPanel1</value>
</data>
<data name="&gt;&gt;diskSpaceErrorLinkLabel.ZOrder" xml:space="preserve">
<value>4</value>
</data>
<data name="tableLayoutPanel1.Dock" type="System.Windows.Forms.DockStyle, System.Windows.Forms">
<value>Fill</value>
</data>
@ -287,7 +317,7 @@ Please wait for this operation to complete, then click Next to continue with the
<value>0</value>
</data>
<data name="tableLayoutPanel1.LayoutSettings" type="System.Windows.Forms.TableLayoutSettings, System.Windows.Forms">
<value>&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;TableLayoutSettings&gt;&lt;Controls&gt;&lt;Control Name="label2" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /&gt;&lt;Control Name="labelProgress" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /&gt;&lt;Control Name="progressBar1" Row="3" RowSpan="1" Column="0" ColumnSpan="1" /&gt;&lt;Control Name="flickerFreeListBox1" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /&gt;&lt;/Controls&gt;&lt;Columns Styles="Percent,100" /&gt;&lt;Rows Styles="AutoSize,0,Percent,100,AutoSize,0,AutoSize,0" /&gt;&lt;/TableLayoutSettings&gt;</value>
<value>&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;TableLayoutSettings&gt;&lt;Controls&gt;&lt;Control Name="label2" Row="0" RowSpan="1" Column="0" ColumnSpan="2" /&gt;&lt;Control Name="labelProgress" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /&gt;&lt;Control Name="progressBar1" Row="3" RowSpan="1" Column="0" ColumnSpan="2" /&gt;&lt;Control Name="flickerFreeListBox1" Row="1" RowSpan="1" Column="0" ColumnSpan="2" /&gt;&lt;Control Name="diskSpaceErrorLinkLabel" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /&gt;&lt;/Controls&gt;&lt;Columns Styles="Percent,100,AutoSize,0" /&gt;&lt;Rows Styles="AutoSize,0,Percent,100,AutoSize,0,AutoSize,0" /&gt;&lt;/TableLayoutSettings&gt;</value>
</data>
<metadata name="$this.Localizable" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>

View File

@ -0,0 +1,154 @@
/* Copyright (c) Citrix Systems Inc.
* 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;
using System.IO;
using XenAPI;
namespace XenAdmin.Actions
{
public class CheckDiskSpaceForPatchUploadAction : PureAsyncAction
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly string fileName;
private readonly long fileSize;
/// <summary>
/// This constructor is used to check disk space for uploading a single patch
/// </summary>
public CheckDiskSpaceForPatchUploadAction(Host host, Pool_patch patch, bool suppressHistory)
: this(host, patch.Name, patch.size, suppressHistory)
{ }
/// <summary>
/// This constructor is used to check disk space for uploading a single update file
/// </summary>
public CheckDiskSpaceForPatchUploadAction(Host host, string path, bool suppressHistory)
: this(host, FileName(path), FileSize(path), suppressHistory)
{ }
/// <summary>
/// This constructor is used to check disk space for uploading a file of given size
/// </summary>
public CheckDiskSpaceForPatchUploadAction(Host host, string fileName, long size, bool suppressHistory)
: base(host.Connection, Messages.ACTION_CHECK_DISK_SPACE_TITLE, "", suppressHistory)
{
if (host == null)
throw new NullReferenceException();
Host = host;
this.fileName = fileName;
fileSize = size;
}
private static long FileSize(string path)
{
FileInfo fileInfo = new FileInfo(path);
return fileInfo.Length;
}
private static string FileName(string path)
{
FileInfo fileInfo = new FileInfo(path);
return fileInfo.Name;
}
protected override void Run()
{
SafeToExit = false;
Description = String.Format(Messages.ACTION_CHECK_DISK_SPACE_DESCRIPTION, Host.Name);
if (!IsEnoughDiskSpace())
{
DiskSpaceRequirements diskSpaceRequirements = null;
var getDiskSpaceRequirementsAction = new GetDiskSpaceRequirementsAction(Host, fileName, fileSize, true);
try
{
getDiskSpaceRequirementsAction.RunExternal(Session);
diskSpaceRequirements = getDiskSpaceRequirementsAction.DiskSpaceRequirements;
}
catch (Failure failure)
{
log.WarnFormat("Getting disk space requirements on {0} failed with: {1}", Host.Name, failure.Message);
}
Exception = new NotEnoughSpaceException(Host.Name, fileName, diskSpaceRequirements);
}
}
private bool IsEnoughDiskSpace()
{
string result;
try
{
result = Host.call_plugin(Session, Host.opaque_ref, "disk-space", "check_patch_upload",
new Dictionary<string, string> { { "size", fileSize.ToString() } });
}
catch (Failure failure)
{
log.WarnFormat("Plugin call disk-space.check_patch_upload({0}) on {1} failed with {2}", fileSize, Host.Name,
failure.Message);
return true;
}
return result.ToLower() == "true";
}
}
public class NotEnoughSpaceException : Exception
{
private readonly string host;
private readonly string fileName;
public DiskSpaceRequirements DiskSpaceRequirements { get; private set; }
public NotEnoughSpaceException(string host, string fileName)
{
this.host = host;
this.fileName = fileName;
DiskSpaceRequirements = null;
}
public NotEnoughSpaceException(string host, string fileName, DiskSpaceRequirements diskSpaceRequirements)
{
this.host = host;
this.fileName = fileName;
DiskSpaceRequirements = diskSpaceRequirements;
}
public override string Message
{
get { return String.Format(Messages.NOT_ENOUGH_SPACE_MESSAGE, host, fileName); }
}
}
}

View File

@ -0,0 +1,72 @@
/* Copyright (c) Citrix Systems Inc.
* 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;
using XenAPI;
namespace XenAdmin.Actions
{
public class CleanupDiskSpaceAction : PureAsyncAction
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly Pool_patch excludedPatch;
public CleanupDiskSpaceAction(Host host, Pool_patch excludedPatch, bool suppressHistory)
: base(host.Connection, Messages.ACTION_CLEANUP_DISK_SPACE_TITLE, "", suppressHistory)
{
if (host == null)
throw new NullReferenceException();
Host = host;
this.excludedPatch = excludedPatch;
}
protected override void Run()
{
Description = String.Format(Messages.ACTION_CLEANUP_DISK_SPACE_DESCRIPTION, Host.Name);
try
{
var args = new Dictionary<string, string> {{"exclude", excludedPatch.uuid}};
Result = Host.call_plugin(Session, Host.opaque_ref, "disk-space", "cleanup_disk_space", args);
if (Result.ToLower() == "true")
Description = String.Format(Messages.ACTION_CLEANUP_DISK_SPACE_SUCCESS, Host.Name);
}
catch (Failure failure)
{
log.WarnFormat("Plugin call disk-space.cleanup_disk_space() on {0} failed with {1}", Host.Name,
failure.Message);
throw;
}
}
}
}

View File

@ -0,0 +1,174 @@
/* Copyright (c) Citrix Systems Inc.
* 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;
using System.IO;
using System.Text;
using XenAPI;
namespace XenAdmin.Actions
{
public class GetDiskSpaceRequirementsAction : PureAsyncAction
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly string updateName;
private readonly long updateSize;
private readonly Pool_patch currentPatch;
public DiskSpaceRequirements DiskSpaceRequirements { get; private set; }
/// <summary>
/// This constructor is used to calculate the disk space requirements for installing or uploading a single patch
/// </summary>
public GetDiskSpaceRequirementsAction(Host host, Pool_patch patch, bool suppressHistory)
: this(host, patch.Name, patch.size, suppressHistory)
{
currentPatch = patch;
}
/// <summary>
/// This constructor is used to calculate the disk space requirements for uploading a single update file
/// </summary>
public GetDiskSpaceRequirementsAction(Host host, string path, bool suppressHistory)
: this(host, FileName(path), FileSize(path), suppressHistory)
{ }
/// <summary>
/// This constructor is used to check disk space for installing or uploading an update of given size
/// </summary>
public GetDiskSpaceRequirementsAction(Host host, string updateName, long size, bool suppressHistory)
: base(host.Connection, Messages.ACTION_GET_DISK_SPACE_REQUIREMENTS_TITLE, suppressHistory)
{
if (host == null)
throw new NullReferenceException();
Host = host;
this.updateName = updateName;
updateSize = size;
}
private static long FileSize(string path)
{
FileInfo fileInfo = new FileInfo(path);
return fileInfo.Length;
}
private static string FileName(string path)
{
FileInfo fileInfo = new FileInfo(path);
return fileInfo.Name;
}
protected override void Run()
{
Description = String.Format(Messages.ACTION_GET_DISK_SPACE_REQUIREMENTS_DESCRIPTION, Host.Name);
long requiredDiskSpace = updateSize;
string result;
// get available disk space
long availableDiskSpace = 0;
try
{
result = Host.call_plugin(Session, Host.opaque_ref, "disk-space", "get_avail_host_disk_space", new Dictionary<string, string>());
availableDiskSpace = Convert.ToInt64(result);
}
catch (Failure failure)
{
log.WarnFormat("Plugin call disk-space.get_avail_host_disk_space on {0} failed with {1}", Host.Name, failure.Message);
}
// get reclaimable disk space (excluding current patch)
long reclaimableDiskSpace = 0;
try
{
var args = new Dictionary<string, string>();
if (currentPatch != null)
args.Add("exclude", currentPatch.uuid);
result = Host.call_plugin(Session, Host.opaque_ref, "disk-space", "get_reclaimable_disk_space", args);
reclaimableDiskSpace = Convert.ToInt64(result);
}
catch (Failure failure)
{
log.WarnFormat("Plugin call disk-space.get_reclaimable_disk_space on {0} failed with {1}", Host.Name, failure.Message);
}
DiskSpaceRequirements = new DiskSpaceRequirements(Host, updateName, requiredDiskSpace, availableDiskSpace, reclaimableDiskSpace);
log.WarnFormat("Cleanup message: \r\n{0}", DiskSpaceRequirements.GetSpaceRequirementsMessage());
}
}
public class DiskSpaceRequirements
{
public readonly Host Host;
public readonly string UpdateName;
public readonly long RequiredDiskSpace;
public readonly long AvailableDiskSpace;
public readonly long ReclaimableDiskSpace;
public DiskSpaceRequirements(Host host, string updateName, long requiredDiskSpace, long availableDiskSpace, long reclaimableDiskSpace)
{
Host = host;
UpdateName = updateName;
RequiredDiskSpace = requiredDiskSpace;
AvailableDiskSpace = availableDiskSpace;
ReclaimableDiskSpace = reclaimableDiskSpace;
}
public bool CanCleanup
{
get { return RequiredDiskSpace < ReclaimableDiskSpace; }
}
public string GetSpaceRequirementsMessage()
{
StringBuilder sbMessage = new StringBuilder();
sbMessage.AppendFormat(Messages.NOT_ENOUGH_SPACE_MESSAGE, Host.Name, UpdateName);
sbMessage.AppendLine();
sbMessage.AppendLine();
sbMessage.AppendFormat(Messages.NOT_ENOUGH_SPACE_MESSAGE_REQUIRED_SPACE, Util.DiskSizeString(RequiredDiskSpace));
sbMessage.AppendLine();
sbMessage.AppendFormat(Messages.NOT_ENOUGH_SPACE_MESSAGE_AVAILABLE_SPACE, Util.DiskSizeString(AvailableDiskSpace));
sbMessage.AppendLine();
sbMessage.AppendLine();
if (CanCleanup)
sbMessage.AppendFormat(Messages.NOT_ENOUGH_SPACE_MESSAGE_CLEANUP, Util.DiskSizeString(ReclaimableDiskSpace));
else
sbMessage.AppendLine(Messages.NOT_ENOUGH_SPACE_MESSAGE_NOCLEANUP);
return sbMessage.ToString();
}
}
}

View File

@ -348,6 +348,51 @@ namespace XenAdmin {
}
}
/// <summary>
/// Looks up a localized string similar to Checking disk space on {0}.
/// </summary>
public static string ACTION_CHECK_DISK_SPACE_DESCRIPTION {
get {
return ResourceManager.GetString("ACTION_CHECK_DISK_SPACE_DESCRIPTION", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Check disk space.
/// </summary>
public static string ACTION_CHECK_DISK_SPACE_TITLE {
get {
return ResourceManager.GetString("ACTION_CHECK_DISK_SPACE_TITLE", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cleaning up disk space on {0}.
/// </summary>
public static string ACTION_CLEANUP_DISK_SPACE_DESCRIPTION {
get {
return ResourceManager.GetString("ACTION_CLEANUP_DISK_SPACE_DESCRIPTION", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Successfully cleaned up disk space on {0}.
/// </summary>
public static string ACTION_CLEANUP_DISK_SPACE_SUCCESS {
get {
return ResourceManager.GetString("ACTION_CLEANUP_DISK_SPACE_SUCCESS", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Clean up disk space.
/// </summary>
public static string ACTION_CLEANUP_DISK_SPACE_TITLE {
get {
return ResourceManager.GetString("ACTION_CLEANUP_DISK_SPACE_TITLE", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Creating {0} ....
/// </summary>
@ -897,6 +942,24 @@ namespace XenAdmin {
}
}
/// <summary>
/// Looks up a localized string similar to Getting space requirements on {0}.
/// </summary>
public static string ACTION_GET_DISK_SPACE_REQUIREMENTS_DESCRIPTION {
get {
return ResourceManager.GetString("ACTION_GET_DISK_SPACE_REQUIREMENTS_DESCRIPTION", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Get space requirements.
/// </summary>
public static string ACTION_GET_DISK_SPACE_REQUIREMENTS_TITLE {
get {
return ResourceManager.GetString("ACTION_GET_DISK_SPACE_REQUIREMENTS_TITLE", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Action completed. {0} VDIs found..
/// </summary>
@ -23352,6 +23415,51 @@ namespace XenAdmin {
}
}
/// <summary>
/// Looks up a localized string similar to There is not enough space on &apos;{0}&apos; to upload update &apos;{1}&apos;..
/// </summary>
public static string NOT_ENOUGH_SPACE_MESSAGE {
get {
return ResourceManager.GetString("NOT_ENOUGH_SPACE_MESSAGE", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Space available: {0}.
/// </summary>
public static string NOT_ENOUGH_SPACE_MESSAGE_AVAILABLE_SPACE {
get {
return ResourceManager.GetString("NOT_ENOUGH_SPACE_MESSAGE_AVAILABLE_SPACE", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to XenCenter can free up {0} by removing residual update files. Do you wish to proceed with the cleanup?.
/// </summary>
public static string NOT_ENOUGH_SPACE_MESSAGE_CLEANUP {
get {
return ResourceManager.GetString("NOT_ENOUGH_SPACE_MESSAGE_CLEANUP", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You need to manually free up more space and try again..
/// </summary>
public static string NOT_ENOUGH_SPACE_MESSAGE_NOCLEANUP {
get {
return ResourceManager.GetString("NOT_ENOUGH_SPACE_MESSAGE_NOCLEANUP", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Space required: {0}.
/// </summary>
public static string NOT_ENOUGH_SPACE_MESSAGE_REQUIRED_SPACE {
get {
return ResourceManager.GetString("NOT_ENOUGH_SPACE_MESSAGE_REQUIRED_SPACE", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Not in any folder.
/// </summary>
@ -24106,6 +24214,15 @@ namespace XenAdmin {
}
}
/// <summary>
/// Looks up a localized string similar to &amp;Clean up....
/// </summary>
public static string PATCHINGWIZARD_CLEANUP {
get {
return ResourceManager.GetString("PATCHINGWIZARD_CLEANUP", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No action required.
/// </summary>
@ -24169,6 +24286,15 @@ namespace XenAdmin {
}
}
/// <summary>
/// Looks up a localized string similar to &amp;More info....
/// </summary>
public static string PATCHINGWIZARD_MORE_INFO {
get {
return ResourceManager.GetString("PATCHINGWIZARD_MORE_INFO", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Resolve pool problems.
/// </summary>
@ -31807,7 +31933,7 @@ namespace XenAdmin {
}
/// <summary>
/// Looks up a localized string similar to Already uploaded to selected server(s).
/// Looks up a localized string similar to Already uploaded.
/// </summary>
public static string UPLOAD_PATCH_ALREADY_UPLOADED {
get {

View File

@ -213,6 +213,21 @@
<data name="ACTION_CHANGING_SHADOW_MULTIPLIER_FOR" xml:space="preserve">
<value>Changing shadow multiplier for '{0}'...</value>
</data>
<data name="ACTION_CHECK_DISK_SPACE_DESCRIPTION" xml:space="preserve">
<value>Checking disk space on {0}</value>
</data>
<data name="ACTION_CHECK_DISK_SPACE_TITLE" xml:space="preserve">
<value>Check disk space</value>
</data>
<data name="ACTION_CLEANUP_DISK_SPACE_DESCRIPTION" xml:space="preserve">
<value>Cleaning up disk space on {0}</value>
</data>
<data name="ACTION_CLEANUP_DISK_SPACE_SUCCESS" xml:space="preserve">
<value>Successfully cleaned up disk space on {0}</value>
</data>
<data name="ACTION_CLEANUP_DISK_SPACE_TITLE" xml:space="preserve">
<value>Clean up disk space</value>
</data>
<data name="ACTION_CREATE_BOND_DESCRIPTION" xml:space="preserve">
<value>Creating {0} ...</value>
</data>
@ -399,6 +414,12 @@
<data name="ACTION_GET_DATASOURCES" xml:space="preserve">
<value>Fetch data sources</value>
</data>
<data name="ACTION_GET_DISK_SPACE_REQUIREMENTS_DESCRIPTION" xml:space="preserve">
<value>Getting space requirements on {0}</value>
</data>
<data name="ACTION_GET_DISK_SPACE_REQUIREMENTS_TITLE" xml:space="preserve">
<value>Get space requirements</value>
</data>
<data name="ACTION_GET_METADATA_VDIS_DONE" xml:space="preserve">
<value>Action completed. {0} VDIs found.</value>
</data>
@ -8128,6 +8149,21 @@ It is strongly recommended that you Cancel and apply the latest version of the p
<data name="NOT_CONTAINS" xml:space="preserve">
<value>does not contain</value>
</data>
<data name="NOT_ENOUGH_SPACE_MESSAGE" xml:space="preserve">
<value>There is not enough space on '{0}' to upload update '{1}'.</value>
</data>
<data name="NOT_ENOUGH_SPACE_MESSAGE_AVAILABLE_SPACE" xml:space="preserve">
<value>Space available: {0}</value>
</data>
<data name="NOT_ENOUGH_SPACE_MESSAGE_CLEANUP" xml:space="preserve">
<value>XenCenter can free up {0} by removing residual update files. Do you wish to proceed with the cleanup?</value>
</data>
<data name="NOT_ENOUGH_SPACE_MESSAGE_NOCLEANUP" xml:space="preserve">
<value>You need to manually free up more space and try again.</value>
</data>
<data name="NOT_ENOUGH_SPACE_MESSAGE_REQUIRED_SPACE" xml:space="preserve">
<value>Space required: {0}</value>
</data>
<data name="NOT_IN_A_FOLDER" xml:space="preserve">
<value>Not in any folder</value>
</data>
@ -8348,6 +8384,9 @@ It is strongly recommended that you Cancel and apply the latest version of the p
<data name="PASTE" xml:space="preserve">
<value>Paste</value>
</data>
<data name="PATCHINGWIZARD_CLEANUP" xml:space="preserve">
<value>&amp;Clean up...</value>
</data>
<data name="PATCHINGWIZARD_MODEPAGE_NOACTION" xml:space="preserve">
<value>No action required</value>
</data>
@ -8369,6 +8408,9 @@ It is strongly recommended that you Cancel and apply the latest version of the p
<data name="PATCHINGWIZARD_MODEPAGE_UNKNOWNACTION" xml:space="preserve">
<value>Unknown</value>
</data>
<data name="PATCHINGWIZARD_MORE_INFO" xml:space="preserve">
<value>&amp;More info...</value>
</data>
<data name="PATCHINGWIZARD_PATCHINGPAGE_POOL_RESOLVE" xml:space="preserve">
<value>Resolve pool problems</value>
</data>
@ -11018,7 +11060,7 @@ Do you want to continue?</value>
<value>Uploading to server '{0}'...</value>
</data>
<data name="UPLOAD_PATCH_ALREADY_UPLOADED" xml:space="preserve">
<value>Already uploaded to selected server(s)</value>
<value>Already uploaded</value>
</data>
<data name="UPLOAD_PATCH_DESCRIPTION" xml:space="preserve">
<value>Uploading...</value>
@ -11027,8 +11069,8 @@ Do you want to continue?</value>
<value>Update successfully uploaded to selected server(s)</value>
</data>
<data name="UPLOAD_PATCH_TITLE" xml:space="preserve">
<value>Upload</value>
</data>
<value>Upload</value>
</data>
<data name="UPSELL_BLURB_ALERTS" xml:space="preserve">
<value>Upgrade your XenServer license to enable Alerting and Reporting capabilities. Email based performance and error alerting will proactively notify administrators of error conditions or performance problems before they affect critical services.
@ -11223,7 +11265,7 @@ To learn more about the XenServer Dynamic Workload Balancing feature or to start
</data>
<data name="VDI_ON_SR_TITLE" xml:space="preserve">
<value>{0} on '{1}' {2}</value>
</data>
</data>
<data name="VENDOR_NAME" xml:space="preserve">
<value>Vendor</value>
</data>
@ -12351,5 +12393,5 @@ You will need to navigate to the Console on each of the selected VMs to complete
</data>
<data name="YOU_ARE_HERE" xml:space="preserve">
<value>You are here</value>
</data>
</root>
</data>
</root>

View File

@ -83,6 +83,9 @@
<Compile Include="Actions\Pool\CreatePoolAction.cs" />
<Compile Include="Actions\Pool\PoolAction.cs" />
<Compile Include="Actions\Pool\SetGpuPlacementPolicyAction.cs" />
<Compile Include="Actions\Pool_Patch\CheckDiskSpaceForPatchUploadAction.cs" />
<Compile Include="Actions\Pool_Patch\CleanupDiskSpaceAction.cs" />
<Compile Include="Actions\Pool_Patch\GetDiskSpaceRequirementsAction.cs" />
<Compile Include="Actions\SupplementalPack\InstallSupplementalPackAction.cs" />
<Compile Include="Actions\SupplementalPack\UploadSupplementalPackAction.cs" />
<Compile Include="Actions\Pool_Patch\ApplyPatchAction.cs" />