Merge pull request #315 from MihaelaStoica/CAR-1711

CP-10797: Ensuring sufficient disk space for service pack installation: ...
This commit is contained in:
Gabor Apati-Nagy 2015-02-06 13:51:36 +00:00
commit 2d89776d49
10 changed files with 832 additions and 100 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,185 @@ 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, () => UpdateActionDescription(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)
{
UpdateActionDescription(action);
progressBar1.Value = action == null ? 0 : action.PercentComplete;
}
private void UpdateActionDescription(AsyncAction action)
{
if (action == null) // reset action description
{
labelProgress.Text = "";
labelProgress.ForeColor = SystemColors.ControlText;
}
else if (action.StartedRunning) // update description for started actions
{
labelProgress.Text = GetActionDescription(action);
labelProgress.ForeColor = !action.IsCompleted || action.Succeeded ? SystemColors.ControlText : Color.Red;
}
}
@ -185,8 +273,7 @@ namespace XenAdmin.Wizards.PatchingWizard
Program.Invoke(this, () =>
{
progressBar1.Value = action.PercentComplete;
labelProgress.Text = GetActionDescription(action);
UpdateActionProgress(action);
flickerFreeListBox1.Refresh();
OnPageUpdated();
});
@ -242,39 +329,72 @@ namespace XenAdmin.Wizards.PatchingWizard
private void flickerFreeListBox1_DrawItem(object sender, DrawItemEventArgs e)
{
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;
if (host == null)
if (e.Index < 0 || !(flickerFreeListBox1.Items[e.Index] is KeyValuePair<Host, AsyncAction>))
return;
var hostAndAction = (KeyValuePair<Host, AsyncAction>)flickerFreeListBox1.Items[e.Index];
var host = hostAndAction.Key;
var 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 ArgumentNullException("host");
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 ArgumentNullException("host");
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 ArgumentNullException("host");
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>
@ -23379,6 +23442,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>
@ -24133,6 +24241,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>
@ -24196,6 +24313,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>
@ -31834,7 +31960,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>
@ -8137,6 +8158,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>
@ -8357,6 +8393,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>
@ -8378,6 +8417,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>
@ -11027,7 +11069,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>

View File

@ -84,6 +84,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" />