2013-06-24 13:41:48 +02:00
|
|
|
|
/* 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.ComponentModel;
|
2016-02-06 08:18:07 +01:00
|
|
|
|
using System.Diagnostics;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
using System.Drawing;
|
2016-02-06 08:18:07 +01:00
|
|
|
|
using System.IO;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
using System.Linq;
|
2016-02-06 08:18:07 +01:00
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Text;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
using System.Windows.Forms;
|
2016-02-06 08:18:07 +01:00
|
|
|
|
using XenAdmin.Commands;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
using XenAdmin.Controls;
|
|
|
|
|
using XenAdmin.Core;
|
2016-02-06 08:18:07 +01:00
|
|
|
|
using XenAdmin.CustomFields;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
using XenAdmin.Dialogs;
|
2016-02-06 08:18:07 +01:00
|
|
|
|
using XenAdmin.Model;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
using XenAdmin.Network;
|
2016-02-06 08:18:07 +01:00
|
|
|
|
using XenAdmin.SettingsPanels;
|
|
|
|
|
using XenAPI;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
namespace XenAdmin.TabPages
|
|
|
|
|
{
|
|
|
|
|
public partial class GeneralTabPage : BaseTabPage
|
|
|
|
|
{
|
|
|
|
|
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
|
|
|
|
|
|
private static readonly Dictionary<Type, List<PDSection>> _expandedSections = new Dictionary<Type, List<PDSection>>();
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Set when we need to do a rebuild, but we are not visible, to queue up a rebuild.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private bool refreshNeeded = false;
|
|
|
|
|
|
|
|
|
|
private LicenseStatus licenseStatus;
|
|
|
|
|
|
|
|
|
|
private List<PDSection> sections;
|
|
|
|
|
|
|
|
|
|
public LicenseManagerLauncher LicenseLauncher { private get; set; }
|
|
|
|
|
|
|
|
|
|
public GeneralTabPage()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
|
|
|
|
|
VM_guest_metrics_CollectionChangedWithInvoke =
|
|
|
|
|
Program.ProgramInvokeHandler(VM_guest_metrics_CollectionChanged);
|
|
|
|
|
OtherConfigAndTagsWatcher.TagsChanged += new EventHandler(OtherConfigAndTagsWatcher_TagsChanged);
|
|
|
|
|
sections = new List<PDSection>();
|
|
|
|
|
foreach (Control control in panel2.Controls)
|
|
|
|
|
{
|
|
|
|
|
Panel p = control as Panel;
|
|
|
|
|
if (p == null)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
foreach (Control c in p.Controls)
|
|
|
|
|
{
|
|
|
|
|
PDSection s = c as PDSection;
|
|
|
|
|
if (s == null)
|
|
|
|
|
continue;
|
|
|
|
|
sections.Add(s);
|
|
|
|
|
s.MaximumSize = new Size(900, 9999999);
|
|
|
|
|
s.fixFirstColumnWidth(150);
|
|
|
|
|
s.contentChangedSelection += s_contentChangedSelection;
|
|
|
|
|
s.contentReceivedFocus += s_contentReceivedFocus;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void licenseStatus_ItemUpdated(object sender, EventArgs e)
|
|
|
|
|
{
|
2015-11-23 15:35:20 +01:00
|
|
|
|
if (pdSectionLicense == null || licenseStatus == null)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
GeneralTabLicenseStatusStringifier ss = new GeneralTabLicenseStatusStringifier(licenseStatus);
|
2015-11-23 15:35:20 +01:00
|
|
|
|
Program.Invoke(Program.MainWindow, () =>
|
|
|
|
|
{
|
2015-11-25 13:32:04 +01:00
|
|
|
|
pdSectionLicense.UpdateEntryValueWithKey(
|
|
|
|
|
FriendlyName("host.license_params-expiry"),
|
|
|
|
|
ss.ExpiryDate,
|
|
|
|
|
ss.ShowExpiryDate);
|
2015-11-23 15:35:20 +01:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Program.Invoke(Program.MainWindow, () =>
|
|
|
|
|
{
|
2015-11-25 13:32:04 +01:00
|
|
|
|
pdSectionLicense.UpdateEntryValueWithKey(
|
|
|
|
|
Messages.LICENSE_STATUS,
|
|
|
|
|
ss.ExpiryStatus,
|
|
|
|
|
true);
|
2015-11-23 15:35:20 +01:00
|
|
|
|
});
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void s_contentReceivedFocus(PDSection s)
|
|
|
|
|
{
|
|
|
|
|
scrollToSelectionIfNeeded(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void s_contentChangedSelection(PDSection s)
|
|
|
|
|
{
|
|
|
|
|
scrollToSelectionIfNeeded(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void scrollToSelectionIfNeeded(PDSection s)
|
|
|
|
|
{
|
|
|
|
|
if (s.HasNoSelection())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Rectangle selectedRowBounds = s.SelectedRowBounds;
|
|
|
|
|
|
|
|
|
|
// translate to the coordinates of the pdsection container panel (the one added for padding purposes)
|
|
|
|
|
selectedRowBounds.Offset(s.Parent.Location);
|
|
|
|
|
|
|
|
|
|
// Top edge visible?
|
|
|
|
|
if (panel2.ClientRectangle.Height - selectedRowBounds.Top > 0 && selectedRowBounds.Top > 0)
|
|
|
|
|
{
|
|
|
|
|
// Bottom edge visible?
|
|
|
|
|
if (panel2.ClientRectangle.Height - selectedRowBounds.Bottom > 0 && selectedRowBounds.Bottom > 0)
|
|
|
|
|
{
|
|
|
|
|
// The entire selected row is in view, no need to move
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
panel2.ForceScrollTo(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void EditButton_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
2014-01-22 13:39:32 +01:00
|
|
|
|
new PropertiesCommand(Program.MainWindow, xenObject).Execute();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IXenObject xenObject;
|
|
|
|
|
public IXenObject XenObject
|
|
|
|
|
{
|
|
|
|
|
set
|
|
|
|
|
{
|
2014-09-25 18:49:55 +02:00
|
|
|
|
if (value == null)
|
|
|
|
|
return;
|
|
|
|
|
|
2016-03-11 15:17:58 +01:00
|
|
|
|
RegisterLicenseStatusUpdater(value);
|
2015-11-23 15:35:20 +01:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (xenObject != value)
|
|
|
|
|
{
|
|
|
|
|
UnregisterHandlers();
|
|
|
|
|
|
2015-10-27 19:11:53 +01:00
|
|
|
|
xenObject = value;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
RegisterHandlers();
|
|
|
|
|
BuildList();
|
|
|
|
|
List<PDSection> listPDSections = null;
|
|
|
|
|
if (_expandedSections.TryGetValue(xenObject.GetType(), out listPDSections))
|
|
|
|
|
ResetExpandState(listPDSections);
|
|
|
|
|
else
|
|
|
|
|
ResetExpandState();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
BuildList();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-25 13:32:04 +01:00
|
|
|
|
private void RegisterLicenseStatusUpdater(IXenObject xenObject)
|
2015-11-23 15:35:20 +01:00
|
|
|
|
{
|
|
|
|
|
if (licenseStatus != null)
|
|
|
|
|
licenseStatus.ItemUpdated -= licenseStatus_ItemUpdated;
|
2015-11-25 13:32:04 +01:00
|
|
|
|
|
|
|
|
|
if (xenObject is Host || xenObject is Pool)
|
|
|
|
|
{
|
|
|
|
|
licenseStatus = new LicenseStatus(xenObject);
|
|
|
|
|
licenseStatus.ItemUpdated += licenseStatus_ItemUpdated;
|
|
|
|
|
licenseStatus.BeginUpdate();
|
|
|
|
|
}
|
2015-11-23 15:35:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
void s_ExpandedEventHandler(PDSection pdSection)
|
|
|
|
|
{
|
|
|
|
|
if (pdSection != null)
|
|
|
|
|
{
|
|
|
|
|
//Add to the expandedSections
|
|
|
|
|
List<PDSection> listSections;
|
|
|
|
|
if (_expandedSections.TryGetValue(xenObject.GetType(), out listSections))
|
|
|
|
|
{
|
|
|
|
|
if (!listSections.Contains(pdSection) && pdSection.IsExpanded)
|
|
|
|
|
listSections.Add(pdSection);
|
|
|
|
|
else if (!pdSection.IsExpanded)
|
|
|
|
|
listSections.Remove(pdSection);
|
|
|
|
|
}
|
|
|
|
|
else if (pdSection.IsExpanded)
|
|
|
|
|
{
|
|
|
|
|
List<PDSection> list = new List<PDSection>();
|
|
|
|
|
list.Add(pdSection);
|
|
|
|
|
_expandedSections.Add(xenObject.GetType(), list);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
SetStatesOfExpandingLinks();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ResetExpandState()
|
|
|
|
|
{
|
|
|
|
|
panel2.SuspendLayout();
|
|
|
|
|
foreach (PDSection s in sections)
|
|
|
|
|
{
|
|
|
|
|
s.Contract();
|
|
|
|
|
}
|
|
|
|
|
pdSectionGeneral.Expand();
|
|
|
|
|
panel2.ResumeLayout();
|
|
|
|
|
}
|
|
|
|
|
private void ResetExpandState(List<PDSection> expandedSections)
|
|
|
|
|
{
|
|
|
|
|
panel2.SuspendLayout();
|
|
|
|
|
foreach (PDSection s in sections)
|
|
|
|
|
{
|
|
|
|
|
if (expandedSections.Contains(s))
|
|
|
|
|
s.Expand();
|
|
|
|
|
else
|
|
|
|
|
s.Contract();
|
|
|
|
|
}
|
|
|
|
|
pdSectionGeneral.Expand();
|
|
|
|
|
panel2.ResumeLayout();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UnregisterHandlers()
|
|
|
|
|
{
|
|
|
|
|
if (xenObject != null)
|
|
|
|
|
xenObject.PropertyChanged -= PropertyChanged;
|
|
|
|
|
|
|
|
|
|
if (xenObject is Host)
|
|
|
|
|
{
|
|
|
|
|
Host host = xenObject as Host;
|
|
|
|
|
|
|
|
|
|
Host_metrics metric = xenObject.Connection.Resolve<Host_metrics>(host.metrics);
|
|
|
|
|
if (metric != null)
|
|
|
|
|
metric.PropertyChanged -= PropertyChanged;
|
|
|
|
|
}
|
|
|
|
|
else if (xenObject is VM)
|
|
|
|
|
{
|
|
|
|
|
VM vm = xenObject as VM;
|
|
|
|
|
|
|
|
|
|
VM_metrics metric = vm.Connection.Resolve(vm.metrics);
|
|
|
|
|
if (metric != null)
|
|
|
|
|
metric.PropertyChanged -= PropertyChanged;
|
|
|
|
|
|
|
|
|
|
VM_guest_metrics guestmetric = xenObject.Connection.Resolve(vm.guest_metrics);
|
|
|
|
|
if (guestmetric != null)
|
|
|
|
|
guestmetric.PropertyChanged -= PropertyChanged;
|
|
|
|
|
|
|
|
|
|
vm.Connection.Cache.DeregisterCollectionChanged<VM_guest_metrics>(VM_guest_metrics_CollectionChangedWithInvoke);
|
|
|
|
|
}
|
|
|
|
|
else if (xenObject is SR)
|
|
|
|
|
{
|
|
|
|
|
SR sr = xenObject as SR;
|
|
|
|
|
|
|
|
|
|
foreach (PBD pbd in sr.Connection.ResolveAll(sr.PBDs))
|
|
|
|
|
{
|
|
|
|
|
pbd.PropertyChanged -= PropertyChanged;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (xenObject is Pool)
|
|
|
|
|
{
|
|
|
|
|
xenObject.Connection.Cache.DeregisterBatchCollectionChanged<Pool_patch>(Pool_patch_BatchCollectionChanged);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VM_guest_metrics_CollectionChanged(object sender, CollectionChangeEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (!this.Visible)
|
|
|
|
|
return;
|
|
|
|
|
// Required to refresh the panel when the vm boots so we show the correct pv driver state and version
|
|
|
|
|
// Note this does NOT get called every 2s, just when the vm power state changes (hopefully!)
|
|
|
|
|
BuildList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private readonly CollectionChangeEventHandler VM_guest_metrics_CollectionChangedWithInvoke;
|
|
|
|
|
private void RegisterHandlers()
|
|
|
|
|
{
|
|
|
|
|
if (xenObject != null)
|
|
|
|
|
xenObject.PropertyChanged += new PropertyChangedEventHandler(PropertyChanged);
|
|
|
|
|
|
|
|
|
|
if (xenObject is Host)
|
|
|
|
|
{
|
|
|
|
|
Host host = xenObject as Host;
|
|
|
|
|
Host_metrics metric = xenObject.Connection.Resolve(host.metrics);
|
|
|
|
|
if (metric != null)
|
|
|
|
|
metric.PropertyChanged += new PropertyChangedEventHandler(PropertyChanged);
|
|
|
|
|
}
|
|
|
|
|
else if (xenObject is VM)
|
|
|
|
|
{
|
|
|
|
|
VM vm = xenObject as VM;
|
|
|
|
|
|
|
|
|
|
VM_metrics metric = vm.Connection.Resolve(vm.metrics);
|
|
|
|
|
if (metric != null)
|
|
|
|
|
metric.PropertyChanged += new PropertyChangedEventHandler(PropertyChanged);
|
|
|
|
|
|
|
|
|
|
VM_guest_metrics guestmetric = xenObject.Connection.Resolve(vm.guest_metrics);
|
|
|
|
|
if (guestmetric != null)
|
|
|
|
|
guestmetric.PropertyChanged += new PropertyChangedEventHandler(PropertyChanged);
|
|
|
|
|
|
|
|
|
|
xenObject.Connection.Cache.RegisterCollectionChanged<VM_guest_metrics>(VM_guest_metrics_CollectionChangedWithInvoke);
|
|
|
|
|
}
|
|
|
|
|
else if (xenObject is Pool)
|
|
|
|
|
{
|
|
|
|
|
xenObject.Connection.Cache.RegisterBatchCollectionChanged<Pool_patch>(Pool_patch_BatchCollectionChanged);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Pool_patch_BatchCollectionChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
Program.BeginInvoke(this, BuildList);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OtherConfigAndTagsWatcher_TagsChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
BuildList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We queue up a rebuild if we are not shown but the contents becomes out of date, this just fires off the rebuild
|
|
|
|
|
protected override void OnVisibleChanged(EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (Visible && refreshNeeded)
|
|
|
|
|
{
|
|
|
|
|
BuildList();
|
|
|
|
|
refreshNeeded = false;
|
|
|
|
|
}
|
|
|
|
|
base.OnVisibleChanged(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void PropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (e.PropertyName == "state" ||
|
|
|
|
|
e.PropertyName == "last_updated")
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:17:02 +02:00
|
|
|
|
Program.Invoke(this, delegate
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
if (e.PropertyName == "PBDs")
|
|
|
|
|
{
|
|
|
|
|
SR sr = xenObject as SR;
|
|
|
|
|
if (sr == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
foreach (PBD pbd in xenObject.Connection.ResolveAll(sr.PBDs))
|
|
|
|
|
{
|
|
|
|
|
pbd.PropertyChanged -= PropertyChanged;
|
|
|
|
|
pbd.PropertyChanged += PropertyChanged;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BuildList();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Atm we are rebuilding on almost any property changed event.
|
|
|
|
|
// As long as we are just clearing and readding the rows in the PDSections this seems to be super quick.
|
|
|
|
|
// If it gets slower we should update specific boxes for specific property changes.
|
2015-11-23 15:35:20 +01:00
|
|
|
|
if (licenseStatus != null && licenseStatus.Updated)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
licenseStatus.BeginUpdate();
|
|
|
|
|
BuildList();
|
|
|
|
|
EnableDisableEdit();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void EnableDisableEdit()
|
|
|
|
|
{
|
|
|
|
|
buttonProperties.Enabled = xenObject != null && !xenObject.Locked && xenObject.Connection != null && xenObject.Connection.IsConnected;
|
CA-162989: Container Management GUI use-ability/homogeneity fixes
Implemented changes as follows (copied from ticket):
"I'd suggest the following use-ability/homogeneity fixes for the new container management tabs, if they are quick and easy:
Combine "Docker Version" and "Docker Information" on the VM-General-tab into "Container Management - Docker Status" with the following fields only:
API version
Version
Git Commit
Driver
Index Server Address
Execution Driver
IPv4 Forwarding
In the "Processes" tab, change the name from "Docker Processes" to "Container Processes"
In the "Details" tab, change the name from "Docker Detail" to "Container Details"
In the "Details" tab, drop the top level element "docker_inspect" (XML requires a single root-node, afaik the Windows form treenode does not), or alternatively open the root node by default and rename it to "Inspect Result"
In the "Details" tab, add the "Details"-headline in black on white - just like on the "Processes"-tab
Also, on the container's General tab, show Properties button disabled, instead on hiding it (to be consistent to other cases, e.g. disconnected servers)
"
Signed-off-by: Gabor Apati-Nagy <gabor.apati-nagy@citrix.com>
2015-03-05 20:10:54 +01:00
|
|
|
|
|
2015-03-09 21:02:07 +01:00
|
|
|
|
//keeping it separate
|
CA-162989: Container Management GUI use-ability/homogeneity fixes
Implemented changes as follows (copied from ticket):
"I'd suggest the following use-ability/homogeneity fixes for the new container management tabs, if they are quick and easy:
Combine "Docker Version" and "Docker Information" on the VM-General-tab into "Container Management - Docker Status" with the following fields only:
API version
Version
Git Commit
Driver
Index Server Address
Execution Driver
IPv4 Forwarding
In the "Processes" tab, change the name from "Docker Processes" to "Container Processes"
In the "Details" tab, change the name from "Docker Detail" to "Container Details"
In the "Details" tab, drop the top level element "docker_inspect" (XML requires a single root-node, afaik the Windows form treenode does not), or alternatively open the root node by default and rename it to "Inspect Result"
In the "Details" tab, add the "Details"-headline in black on white - just like on the "Processes"-tab
Also, on the container's General tab, show Properties button disabled, instead on hiding it (to be consistent to other cases, e.g. disconnected servers)
"
Signed-off-by: Gabor Apati-Nagy <gabor.apati-nagy@citrix.com>
2015-03-05 20:10:54 +01:00
|
|
|
|
if (xenObject is DockerContainer)
|
2016-02-03 11:12:05 +01:00
|
|
|
|
{
|
CA-162989: Container Management GUI use-ability/homogeneity fixes
Implemented changes as follows (copied from ticket):
"I'd suggest the following use-ability/homogeneity fixes for the new container management tabs, if they are quick and easy:
Combine "Docker Version" and "Docker Information" on the VM-General-tab into "Container Management - Docker Status" with the following fields only:
API version
Version
Git Commit
Driver
Index Server Address
Execution Driver
IPv4 Forwarding
In the "Processes" tab, change the name from "Docker Processes" to "Container Processes"
In the "Details" tab, change the name from "Docker Detail" to "Container Details"
In the "Details" tab, drop the top level element "docker_inspect" (XML requires a single root-node, afaik the Windows form treenode does not), or alternatively open the root node by default and rename it to "Inspect Result"
In the "Details" tab, add the "Details"-headline in black on white - just like on the "Processes"-tab
Also, on the container's General tab, show Properties button disabled, instead on hiding it (to be consistent to other cases, e.g. disconnected servers)
"
Signed-off-by: Gabor Apati-Nagy <gabor.apati-nagy@citrix.com>
2015-03-05 20:10:54 +01:00
|
|
|
|
buttonProperties.Enabled = false;
|
2016-02-03 11:12:05 +01:00
|
|
|
|
|
|
|
|
|
DockerContainer container = (DockerContainer)xenObject;
|
|
|
|
|
buttonViewConsole.Visible = true;
|
|
|
|
|
buttonViewLog.Visible = true;
|
|
|
|
|
|
|
|
|
|
// Grey out the buttons if the Container management VM is Windows.
|
|
|
|
|
// For Linux VM, enable the buttons only when the docker is running.
|
|
|
|
|
if (container.Parent.IsWindows)
|
|
|
|
|
{
|
|
|
|
|
buttonViewConsole.Enabled = false;
|
|
|
|
|
buttonViewLog.Enabled = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
buttonViewConsole.Enabled = buttonViewLog.Enabled = container.power_state == vm_power_state.Running;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
buttonViewConsole.Visible = false;
|
|
|
|
|
buttonViewLog.Visible = false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void BuildList()
|
|
|
|
|
{
|
|
|
|
|
//Program.AssertOnEventThread();
|
|
|
|
|
|
|
|
|
|
if (!this.Visible)
|
|
|
|
|
{
|
|
|
|
|
refreshNeeded = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (xenObject == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (xenObject is Host && !xenObject.Connection.IsConnected)
|
|
|
|
|
base.Text = Messages.CONNECTION_GENERAL_TAB_TITLE;
|
|
|
|
|
else if (xenObject is Host)
|
|
|
|
|
{
|
|
|
|
|
base.Text = Messages.HOST_GENERAL_TAB_TITLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else if (xenObject is VM)
|
|
|
|
|
{
|
|
|
|
|
VM vm = (VM)xenObject;
|
|
|
|
|
if (vm.is_a_snapshot)
|
|
|
|
|
base.Text = Messages.SNAPSHOT_GENERAL_TAB_TITLE;
|
|
|
|
|
else if (vm.is_a_template)
|
|
|
|
|
base.Text = Messages.TEMPLATE_GENERAL_TAB_TITLE;
|
|
|
|
|
else
|
|
|
|
|
base.Text = Messages.VM_GENERAL_TAB_TITLE;
|
|
|
|
|
}
|
|
|
|
|
else if (xenObject is SR)
|
|
|
|
|
base.Text = Messages.SR_GENERAL_TAB_TITLE;
|
|
|
|
|
else if (xenObject is Pool)
|
|
|
|
|
base.Text = Messages.POOL_GENERAL_TAB_TITLE;
|
2015-02-10 04:03:53 +01:00
|
|
|
|
else if (xenObject is DockerContainer)
|
|
|
|
|
base.Text = Messages.CONTAINER_GENERAL_TAB_TITLE;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
panel2.SuspendLayout();
|
|
|
|
|
// Clear all the data from the sections (visible and non visible)
|
|
|
|
|
foreach (PDSection s in sections)
|
|
|
|
|
{
|
|
|
|
|
s.PauseLayout();
|
|
|
|
|
s.ClearData();
|
|
|
|
|
}
|
|
|
|
|
// Generate the content of each box, each method performs a cast and only populates if XenObject is the relevant type
|
|
|
|
|
|
|
|
|
|
if (xenObject is Host && (xenObject.Connection == null || !xenObject.Connection.IsConnected))
|
|
|
|
|
{
|
|
|
|
|
generateDisconnectedHostBox();
|
|
|
|
|
}
|
2014-12-05 15:16:58 +01:00
|
|
|
|
else if (xenObject is DockerContainer)
|
|
|
|
|
{
|
|
|
|
|
generateDockerContainerGeneralBox();
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
generateGeneralBox();
|
|
|
|
|
generateCustomFieldsBox();
|
|
|
|
|
generateInterfaceBox();
|
|
|
|
|
generateMemoryBox();
|
|
|
|
|
generateVersionBox();
|
|
|
|
|
generateLicenseBox();
|
|
|
|
|
generateCPUBox();
|
|
|
|
|
generateHostPatchesBox();
|
|
|
|
|
generateBootBox();
|
|
|
|
|
generateHABox();
|
|
|
|
|
generateStatusBox();
|
|
|
|
|
generateMultipathBox();
|
|
|
|
|
generatePoolPatchesBox();
|
|
|
|
|
generateMultipathBootBox();
|
2014-07-14 16:17:47 +02:00
|
|
|
|
generateVCPUsBox();
|
2015-02-15 03:52:12 +01:00
|
|
|
|
generateDockerInfoBox();
|
2015-02-19 15:09:08 +01:00
|
|
|
|
generateReadCachingBox();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// hide all the sections which haven't been populated, those that have make sure are visible
|
|
|
|
|
foreach (PDSection s in sections)
|
|
|
|
|
{
|
|
|
|
|
if (s.IsEmpty())
|
|
|
|
|
{
|
|
|
|
|
s.Parent.Visible = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
s.Parent.Visible = true;
|
|
|
|
|
if (s.ContainsFocus)
|
|
|
|
|
s.RestoreSelection();
|
|
|
|
|
}
|
|
|
|
|
s.StartLayout();
|
|
|
|
|
}
|
|
|
|
|
panel2.ResumeLayout();
|
|
|
|
|
EnableDisableEdit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateInterfaceBox()
|
|
|
|
|
{
|
|
|
|
|
Host Host = xenObject as Host;
|
|
|
|
|
Pool Pool = xenObject as Pool;
|
|
|
|
|
if (Host != null)
|
|
|
|
|
{
|
|
|
|
|
fillInterfacesForHost(Host, false);
|
|
|
|
|
}
|
|
|
|
|
else if (Pool != null)
|
|
|
|
|
{
|
|
|
|
|
// Here we tell fillInterfacesForHost to prefix each entry with the hosts name label, so we know which entry belongs to which host
|
|
|
|
|
// and also to better preserve uniqueness for keys in the PDSection
|
|
|
|
|
|
|
|
|
|
foreach (Host h in Pool.Connection.Cache.Hosts)
|
|
|
|
|
fillInterfacesForHost(h, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void fillInterfacesForHost(Host Host, bool includeHostSuffix)
|
|
|
|
|
{
|
|
|
|
|
PDSection s = pdSectionManagementInterfaces;
|
|
|
|
|
|
2013-08-22 17:48:16 +02:00
|
|
|
|
ToolStripMenuItem editValue = new ToolStripMenuItem(Messages.EDIT) { Image = Properties.Resources.edit_16 };
|
|
|
|
|
editValue.Click += delegate
|
|
|
|
|
{
|
|
|
|
|
NetworkingProperties p = new NetworkingProperties(Host, null);
|
|
|
|
|
p.ShowDialog(Program.MainWindow);
|
|
|
|
|
};
|
2013-12-13 11:51:08 +01:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (!string.IsNullOrEmpty(Host.hostname))
|
|
|
|
|
{
|
|
|
|
|
if (!includeHostSuffix)
|
2013-12-22 18:33:13 +01:00
|
|
|
|
s.AddEntry(FriendlyName("host.hostname"), Host.hostname, editValue);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
else
|
|
|
|
|
s.AddEntry(
|
|
|
|
|
string.Format(Messages.PROPERTY_ON_OBJECT, FriendlyName("host.hostname"), Helpers.GetName(Host)),
|
|
|
|
|
Host.hostname,
|
2013-12-22 18:33:13 +01:00
|
|
|
|
editValue);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
foreach (PIF pif in xenObject.Connection.ResolveAll<PIF>(Host.PIFs))
|
|
|
|
|
{
|
|
|
|
|
if (pif.management)
|
|
|
|
|
{
|
|
|
|
|
if (!includeHostSuffix)
|
2013-12-22 18:33:13 +01:00
|
|
|
|
s.AddEntry(Messages.MANAGEMENT_INTERFACE, pif.FriendlyIPAddress, editValue);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
else
|
|
|
|
|
s.AddEntry(
|
|
|
|
|
string.Format(Messages.PROPERTY_ON_OBJECT, Messages.MANAGEMENT_INTERFACE, Helpers.GetName(Host)),
|
|
|
|
|
pif.FriendlyIPAddress,
|
2013-12-22 18:33:13 +01:00
|
|
|
|
editValue);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (PIF pif in xenObject.Connection.ResolveAll<PIF>(Host.PIFs))
|
|
|
|
|
{
|
|
|
|
|
if (pif.IsSecondaryManagementInterface(Properties.Settings.Default.ShowHiddenVMs))
|
|
|
|
|
{
|
|
|
|
|
if (!includeHostSuffix)
|
2013-12-22 18:33:13 +01:00
|
|
|
|
s.AddEntry(pif.ManagementPurpose.Ellipsise(30), pif.FriendlyIPAddress, editValue);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
else
|
|
|
|
|
s.AddEntry(
|
|
|
|
|
string.Format(Messages.PROPERTY_ON_OBJECT, pif.ManagementPurpose.Ellipsise(30), Helpers.GetName(Host)),
|
|
|
|
|
pif.FriendlyIPAddress,
|
2013-12-22 18:33:13 +01:00
|
|
|
|
editValue);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateCustomFieldsBox()
|
|
|
|
|
{
|
|
|
|
|
List<CustomField> customFields = CustomFieldsManager.CustomFieldValues(xenObject);
|
|
|
|
|
if (customFields.Count <= 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
PDSection s = pdSectionCustomFields;
|
|
|
|
|
|
|
|
|
|
foreach (CustomField customField in customFields)
|
|
|
|
|
{
|
2013-08-22 17:48:16 +02:00
|
|
|
|
ToolStripMenuItem editValue = new ToolStripMenuItem(Messages.EDIT){Image= Properties.Resources.edit_16};
|
|
|
|
|
editValue.Click += delegate
|
|
|
|
|
{
|
2013-12-22 18:17:04 +01:00
|
|
|
|
using (PropertiesDialog dialog = new PropertiesDialog(xenObject))
|
|
|
|
|
{
|
|
|
|
|
dialog.SelectCustomFieldsEditPage();
|
|
|
|
|
dialog.ShowDialog();
|
|
|
|
|
}
|
2013-08-22 17:48:16 +02:00
|
|
|
|
};
|
2013-12-13 11:51:08 +01:00
|
|
|
|
|
|
|
|
|
var menuItems = new[] { editValue };
|
2013-06-24 13:41:48 +02:00
|
|
|
|
CustomFieldWrapper cfWrapper = new CustomFieldWrapper(xenObject, customField.Definition);
|
|
|
|
|
|
|
|
|
|
s.AddEntry(customField.Definition.Name.Ellipsise(30), cfWrapper.ToString(), menuItems, customField.Definition.Name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generatePoolPatchesBox()
|
|
|
|
|
{
|
|
|
|
|
Pool pool = xenObject as Pool;
|
|
|
|
|
if (pool == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
PDSection s = pdSectionUpdates;
|
|
|
|
|
|
|
|
|
|
List<KeyValuePair<String, String>> messages = CheckPoolUpdate(pool);
|
|
|
|
|
if (messages.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
foreach (KeyValuePair<String, String> kvp in messages)
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(kvp.Key, kvp.Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Host master = Helpers.GetMaster(xenObject.Connection);
|
|
|
|
|
if (master == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var poolAppPatches = poolAppliedPatches();
|
|
|
|
|
if (!string.IsNullOrEmpty(poolAppPatches))
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(FriendlyName("Pool_patch.fully_applied"), poolAppPatches);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CommandToolStripMenuItem applypatch = new CommandToolStripMenuItem(
|
2014-01-22 13:39:32 +01:00
|
|
|
|
new InstallNewUpdateCommand(Program.MainWindow), true);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2013-12-13 11:51:08 +01:00
|
|
|
|
var menuItems = new[] { applypatch };
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
var poolPartPatches = poolPartialPatches();
|
|
|
|
|
if (!string.IsNullOrEmpty(poolPartPatches))
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(FriendlyName("Pool_patch.partially_applied"), poolPartPatches, menuItems, Color.Red);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var poolNotAppPatches = poolNotAppliedPatches();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateHostPatchesBox()
|
|
|
|
|
{
|
|
|
|
|
Host host = xenObject as Host;
|
|
|
|
|
if (host == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
PDSection s = pdSectionUpdates;
|
|
|
|
|
|
|
|
|
|
List<KeyValuePair<String, String>> messages = CheckServerUpdates(host);
|
|
|
|
|
if (messages.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
foreach (KeyValuePair<String, String> kvp in messages)
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(kvp.Key, kvp.Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (hostAppliedPatches(host) != "")
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(FriendlyName("Pool_patch.applied"), hostAppliedPatches(host));
|
|
|
|
|
}
|
2015-01-28 14:17:58 +01:00
|
|
|
|
|
|
|
|
|
// add supplemental packs
|
|
|
|
|
var suppPacks = hostInstalledSuppPacks(host);
|
|
|
|
|
if (!string.IsNullOrEmpty(suppPacks))
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(FriendlyName("Supplemental_packs.installed"), suppPacks);
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateHABox()
|
|
|
|
|
{
|
|
|
|
|
VM vm = xenObject as VM;
|
|
|
|
|
if (vm == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Pool pool = Helpers.GetPoolOfOne(xenObject.Connection);
|
|
|
|
|
if (pool == null || !pool.ha_enabled)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
PDSection s = pdSectionHighAvailability;
|
|
|
|
|
|
2013-12-22 18:33:13 +01:00
|
|
|
|
s.AddEntry(FriendlyName("VM.ha_restart_priority"), Helpers.RestartPriorityI18n(vm.HARestartPriority),
|
2014-01-22 13:39:32 +01:00
|
|
|
|
new PropertiesToolStripMenuItem(new VmEditHaCommand(Program.MainWindow, xenObject)));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void generateStatusBox()
|
|
|
|
|
{
|
|
|
|
|
SR sr = xenObject as SR;
|
|
|
|
|
if (sr == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
PDSection s = pdSectionStatus;
|
|
|
|
|
|
2015-11-30 12:53:54 +01:00
|
|
|
|
bool broken = sr.IsBroken() || !sr.MultipathAOK;
|
2013-07-19 17:15:37 +02:00
|
|
|
|
bool detached = !sr.HasPBDs;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2013-08-22 17:48:16 +02:00
|
|
|
|
ToolStripMenuItem repair = new ToolStripMenuItem
|
|
|
|
|
{
|
2015-11-30 12:53:54 +01:00
|
|
|
|
Text = Messages.GENERAL_SR_CONTEXT_REPAIR,
|
2013-08-22 17:48:16 +02:00
|
|
|
|
Image = Properties.Resources._000_StorageBroken_h32bit_16
|
|
|
|
|
};
|
|
|
|
|
repair.Click += delegate
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2015-11-30 12:53:54 +01:00
|
|
|
|
Program.MainWindow.ShowPerConnectionWizard(xenObject.Connection, new RepairSRDialog(sr));
|
2013-08-22 17:48:16 +02:00
|
|
|
|
};
|
2013-12-13 11:51:08 +01:00
|
|
|
|
|
|
|
|
|
var menuItems = new[] { repair };
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
if (broken && !detached)
|
|
|
|
|
s.AddEntry(FriendlyName("SR.state"), sr.StatusString, menuItems);
|
|
|
|
|
else
|
|
|
|
|
s.AddEntry(FriendlyName("SR.state"), sr.StatusString);
|
|
|
|
|
|
|
|
|
|
foreach (Host host in xenObject.Connection.Cache.Hosts)
|
|
|
|
|
{
|
|
|
|
|
PBD pbdToSR = null;
|
|
|
|
|
foreach (PBD pbd in xenObject.Connection.ResolveAll(host.PBDs))
|
|
|
|
|
{
|
|
|
|
|
if (pbd.SR.opaque_ref != xenObject.opaque_ref)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
pbdToSR = pbd;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (pbdToSR == null)
|
|
|
|
|
{
|
|
|
|
|
if (!sr.shared)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (!detached)
|
|
|
|
|
s.AddEntry(" " + Helpers.GetName(host).Ellipsise(30),
|
|
|
|
|
Messages.REPAIR_SR_DIALOG_CONNECTION_MISSING, menuItems, Color.Red);
|
|
|
|
|
else
|
|
|
|
|
s.AddEntry(" " + Helpers.GetName(host).Ellipsise(30),
|
|
|
|
|
Messages.REPAIR_SR_DIALOG_CONNECTION_MISSING, Color.Red);
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pbdToSR.PropertyChanged -= new PropertyChangedEventHandler(PropertyChanged);
|
|
|
|
|
pbdToSR.PropertyChanged += new PropertyChangedEventHandler(PropertyChanged);
|
|
|
|
|
|
|
|
|
|
if (!pbdToSR.currently_attached)
|
|
|
|
|
{
|
|
|
|
|
if (!detached)
|
|
|
|
|
s.AddEntry(Helpers.GetName(host).Ellipsise(30), pbdToSR.StatusString, menuItems, Color.Red);
|
|
|
|
|
else
|
|
|
|
|
s.AddEntry(Helpers.GetName(host).Ellipsise(30), pbdToSR.StatusString, Color.Red);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(Helpers.GetName(host).Ellipsise(30), pbdToSR.StatusString);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateMultipathBox()
|
|
|
|
|
{
|
|
|
|
|
SR sr = xenObject as SR;
|
|
|
|
|
if (sr == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
PDSection s = pdSectionMultipathing;
|
|
|
|
|
|
|
|
|
|
if (!sr.MultipathCapable)
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(Messages.MULTIPATH_CAPABLE, Messages.NO);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sr.LunPerVDI)
|
|
|
|
|
{
|
|
|
|
|
Dictionary<VM, Dictionary<VDI, String>>
|
|
|
|
|
pathStatus = sr.GetMultiPathStatusLunPerVDI();
|
|
|
|
|
|
|
|
|
|
foreach (Host host in xenObject.Connection.Cache.Hosts)
|
|
|
|
|
{
|
|
|
|
|
PBD pbd = sr.GetPBDFor(host);
|
|
|
|
|
if (pbd == null || !pbd.MultipathActive)
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(host.Name, Messages.MULTIPATH_NOT_ACTIVE);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.AddEntry(host.Name, Messages.MULTIPATH_ACTIVE);
|
|
|
|
|
foreach (KeyValuePair<VM, Dictionary<VDI, String>> kvp in pathStatus)
|
|
|
|
|
{
|
|
|
|
|
VM vm = kvp.Key;
|
|
|
|
|
if (vm.resident_on == null ||
|
|
|
|
|
vm.resident_on.opaque_ref != host.opaque_ref)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
bool renderOnOneLine = false;
|
|
|
|
|
int lastMax = -1;
|
|
|
|
|
int lastCurrent = -1;
|
|
|
|
|
|
|
|
|
|
foreach (KeyValuePair<VDI, String> kvp2 in kvp.Value)
|
|
|
|
|
{
|
|
|
|
|
int current;
|
|
|
|
|
int max;
|
|
|
|
|
if (!PBD.ParsePathCounts(kvp2.Value, out current, out max))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (!renderOnOneLine)
|
|
|
|
|
{
|
|
|
|
|
lastMax = max;
|
|
|
|
|
lastCurrent = current;
|
|
|
|
|
renderOnOneLine = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lastMax == max && lastCurrent == current)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
renderOnOneLine = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (renderOnOneLine)
|
|
|
|
|
{
|
|
|
|
|
AddMultipathLine(s, String.Format(" {0}", vm.Name),
|
|
|
|
|
lastCurrent, lastMax, pbd.ISCSISessions);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(String.Format(" {0}", vm.Name), "");
|
|
|
|
|
|
|
|
|
|
foreach (KeyValuePair<VDI, String> kvp2 in kvp.Value)
|
|
|
|
|
{
|
|
|
|
|
int current;
|
|
|
|
|
int max;
|
|
|
|
|
if (!PBD.ParsePathCounts(kvp2.Value, out current, out max))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
AddMultipathLine(s, String.Format(" {0}", kvp2.Key.Name),
|
|
|
|
|
current, max, pbd.ISCSISessions);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Dictionary<PBD, String> pathStatus = sr.GetMultiPathStatusLunPerSR();
|
|
|
|
|
|
|
|
|
|
foreach (Host host in xenObject.Connection.Cache.Hosts)
|
|
|
|
|
{
|
|
|
|
|
PBD pbd = sr.GetPBDFor(host);
|
|
|
|
|
if (pbd == null || !pathStatus.ContainsKey(pbd))
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(host.Name,
|
|
|
|
|
pbd != null && pbd.MultipathActive ?
|
|
|
|
|
Messages.MULTIPATH_ACTIVE :
|
|
|
|
|
Messages.MULTIPATH_NOT_ACTIVE);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String status = pathStatus[pbd];
|
|
|
|
|
|
|
|
|
|
int current;
|
|
|
|
|
int max;
|
|
|
|
|
PBD.ParsePathCounts(status, out current, out max); //Guaranteed to work if PBD is in pathStatus
|
|
|
|
|
|
|
|
|
|
AddMultipathLine(s, host.Name, current, max, pbd.ISCSISessions);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void AddMultipathLine(PDSection s, String title, int current, int max, int iscsiSessions)
|
|
|
|
|
{
|
|
|
|
|
bool bad = current < max || (iscsiSessions != -1 && max < iscsiSessions);
|
|
|
|
|
string row = string.Format(Messages.MULTIPATH_STATUS, current, max);
|
|
|
|
|
if (iscsiSessions != -1)
|
|
|
|
|
row += string.Format(Messages.MULTIPATH_STATUS_ISCSI_SESSIONS, iscsiSessions);
|
|
|
|
|
|
|
|
|
|
if (bad)
|
|
|
|
|
s.AddEntry(title, row, Color.Red);
|
|
|
|
|
else
|
|
|
|
|
s.AddEntry(title, row);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateMultipathBootBox()
|
|
|
|
|
{
|
|
|
|
|
Host host = xenObject as Host;
|
|
|
|
|
if (host == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
int current, max;
|
|
|
|
|
if (!host.GetBootPathCounts(out current, out max))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
PDSection s = pdSectionMultipathBoot;
|
|
|
|
|
string text = string.Format(Messages.MULTIPATH_STATUS, current, max);
|
|
|
|
|
bool bad = current < max;
|
|
|
|
|
if (bad)
|
|
|
|
|
s.AddEntry(Messages.STATUS, text, Color.Red);
|
|
|
|
|
else
|
|
|
|
|
s.AddEntry(Messages.STATUS, text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateBootBox()
|
|
|
|
|
{
|
|
|
|
|
VM vm = xenObject as VM;
|
|
|
|
|
if (vm == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
PDSection s = pdSectionBootOptions;
|
|
|
|
|
|
|
|
|
|
if (vm.IsHVM)
|
|
|
|
|
{
|
2013-12-13 11:51:08 +01:00
|
|
|
|
s.AddEntry(FriendlyName("VM.BootOrder"), HVMBootOrder(vm),
|
2014-01-22 13:39:32 +01:00
|
|
|
|
new PropertiesToolStripMenuItem(new VmEditStartupOptionsCommand(Program.MainWindow, vm)));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-12-13 11:51:08 +01:00
|
|
|
|
s.AddEntry(FriendlyName("VM.PV_args"), vm.PV_args,
|
2014-01-22 13:39:32 +01:00
|
|
|
|
new PropertiesToolStripMenuItem(new VmEditStartupOptionsCommand(Program.MainWindow, vm)));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateLicenseBox()
|
|
|
|
|
{
|
|
|
|
|
Host host = xenObject as Host;
|
|
|
|
|
if (host == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
PDSection s = pdSectionLicense;
|
|
|
|
|
|
|
|
|
|
if (host.license_params == null || host.IsXCP)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Dictionary<string, string> info = new Dictionary<string, string>(host.license_params);
|
|
|
|
|
|
|
|
|
|
// This field is now supressed as it has no meaning under the current license scheme, and was never
|
|
|
|
|
// enforced anyway.
|
|
|
|
|
info.Remove("sockets");
|
|
|
|
|
|
2016-01-21 13:31:33 +01:00
|
|
|
|
// Remove "expiry" field for "basic" license
|
|
|
|
|
if (!string.IsNullOrEmpty(host.edition) && host.edition == "basic")
|
|
|
|
|
info.Remove("expiry");
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (info.ContainsKey("expiry"))
|
|
|
|
|
{
|
2013-08-22 17:48:16 +02:00
|
|
|
|
ToolStripMenuItem editItem = new ToolStripMenuItem(Messages.LAUNCH_LICENSE_MANAGER);
|
|
|
|
|
editItem.Click += delegate
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2013-08-22 17:48:16 +02:00
|
|
|
|
if (LicenseLauncher != null)
|
|
|
|
|
{
|
|
|
|
|
LicenseLauncher.Parent = Program.MainWindow;
|
|
|
|
|
LicenseLauncher.LaunchIfRequired(false, ConnectionsManager.XenConnections);
|
|
|
|
|
}
|
|
|
|
|
};
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
GeneralTabLicenseStatusStringifier ss = new GeneralTabLicenseStatusStringifier(licenseStatus);
|
2013-12-22 18:33:13 +01:00
|
|
|
|
s.AddEntry(Messages.LICENSE_STATUS, ss.ExpiryStatus, editItem);
|
2014-10-29 15:51:42 +01:00
|
|
|
|
s.AddEntry(FriendlyName("host.license_params-expiry"), ss.ExpiryDate, editItem, ss.ShowExpiryDate);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
info.Remove("expiry");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(host.edition))
|
|
|
|
|
{
|
2014-11-14 18:29:01 +01:00
|
|
|
|
s.AddEntry(FriendlyName("host.edition"), Helpers.GetFriendlyLicenseName(host));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (info.ContainsKey("sku_type"))
|
|
|
|
|
{
|
|
|
|
|
info.Remove("sku_type");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (info.ContainsKey("sku_type"))
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(FriendlyName("host.license_params-sku_type"), Helpers.GetFriendlyLicenseName(host));
|
|
|
|
|
info.Remove("sku_type");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(Helpers.ClearwaterOrGreater(host))
|
|
|
|
|
s.AddEntry(Messages.NUMBER_OF_SOCKETS, host.CpuSockets.ToString());
|
|
|
|
|
|
|
|
|
|
if (host.license_server.ContainsKey("address"))
|
|
|
|
|
{
|
2014-11-14 18:29:01 +01:00
|
|
|
|
var licenseServerAddress = host.license_server["address"].Trim();
|
|
|
|
|
if (licenseServerAddress == "" || licenseServerAddress.ToLower() == "localhost")
|
2014-10-31 13:16:45 +01:00
|
|
|
|
s.AddEntry(FriendlyName(String.Format("host.license_server-address")), host.license_server["address"]);
|
|
|
|
|
else
|
|
|
|
|
{
|
2014-10-31 16:06:35 +01:00
|
|
|
|
var openUrl = new ToolStripMenuItem(Messages.LICENSE_SERVER_WEB_CONSOLE_GOTO);
|
|
|
|
|
openUrl.Click += (sender, args) => Program.OpenURL(string.Format(Messages.LICENSE_SERVER_WEB_CONSOLE_FORMAT, licenseServerAddress, Host.LicenseServerWebConsolePort));
|
2014-10-31 13:16:45 +01:00
|
|
|
|
s.AddEntryLink(FriendlyName(String.Format("host.license_server-address")),
|
|
|
|
|
host.license_server["address"],
|
|
|
|
|
new[] {openUrl},
|
|
|
|
|
openUrl.PerformClick);
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
if (host.license_server.ContainsKey("port"))
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(FriendlyName(String.Format("host.license_server-port")), host.license_server["port"]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (string key in new string[] { "productcode", "serialnumber" })
|
|
|
|
|
{
|
|
|
|
|
if (info.ContainsKey(key))
|
|
|
|
|
{
|
|
|
|
|
string row_name = string.Format("host.license_params-{0}", key);
|
|
|
|
|
string k = key;
|
|
|
|
|
if (host.license_params[k] != string.Empty)
|
|
|
|
|
s.AddEntry(FriendlyName(row_name), host.license_params[k]);
|
|
|
|
|
info.Remove(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string restrictions = Helpers.GetHostRestrictions(host);
|
|
|
|
|
if (restrictions != "")
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(Messages.RESTRICTIONS, restrictions);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateVersionBox()
|
|
|
|
|
{
|
|
|
|
|
Host host = xenObject as Host;
|
|
|
|
|
|
|
|
|
|
if (host == null || host.software_version == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
bool isXCP = host.IsXCP;
|
|
|
|
|
if (host.software_version.ContainsKey("date"))
|
|
|
|
|
pdSectionVersion.AddEntry(isXCP ? Messages.SOFTWARE_VERSION_XCP_DATE : Messages.SOFTWARE_VERSION_DATE, host.software_version["date"]);
|
|
|
|
|
if (host.software_version.ContainsKey("build_number"))
|
|
|
|
|
pdSectionVersion.AddEntry(isXCP ? Messages.SOFTWARE_VERSION_XCP_BUILD_NUMBER : Messages.SOFTWARE_VERSION_BUILD_NUMBER, host.software_version["build_number"]);
|
|
|
|
|
if (isXCP && host.software_version.ContainsKey("platform_version"))
|
|
|
|
|
pdSectionVersion.AddEntry(Messages.SOFTWARE_VERSION_XCP_PLATFORM_VERSION, host.software_version["platform_version"]);
|
|
|
|
|
if (!isXCP && host.software_version.ContainsKey("product_version"))
|
|
|
|
|
pdSectionVersion.AddEntry(Messages.SOFTWARE_VERSION_PRODUCT_VERSION, host.ProductVersionText);
|
2015-12-07 19:03:20 +01:00
|
|
|
|
if (host.software_version.ContainsKey("dbv"))
|
|
|
|
|
pdSectionVersion.AddEntry("DBV", host.software_version["dbv"]);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateCPUBox()
|
|
|
|
|
{
|
|
|
|
|
Host host = xenObject as Host;
|
|
|
|
|
if (host == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
PDSection s = pdSectionCPU;
|
|
|
|
|
|
|
|
|
|
SortedDictionary<long, Host_cpu> d = new SortedDictionary<long, Host_cpu>();
|
|
|
|
|
foreach (Host_cpu cpu in xenObject.Connection.ResolveAll(host.host_CPUs))
|
|
|
|
|
{
|
|
|
|
|
d.Add(cpu.number, cpu);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool cpusIdentical = CPUsIdentical(d.Values);
|
|
|
|
|
|
|
|
|
|
foreach (Host_cpu cpu in d.Values)
|
|
|
|
|
{
|
|
|
|
|
String label = String.Format(Messages.GENERAL_DETAILS_CPU_NUMBER,
|
|
|
|
|
cpusIdentical && d.Values.Count > 1 ? String.Format("0 - {0}", d.Values.Count - 1)
|
|
|
|
|
: cpu.number.ToString());
|
|
|
|
|
|
|
|
|
|
s.AddEntry(label, Helpers.GetCPUProperties(cpu));
|
|
|
|
|
if (cpusIdentical)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-14 16:17:47 +02:00
|
|
|
|
private void generateVCPUsBox()
|
|
|
|
|
{
|
|
|
|
|
VM vm = xenObject as VM;
|
|
|
|
|
if (vm == null)
|
|
|
|
|
return;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2014-07-14 16:17:47 +02:00
|
|
|
|
PDSection s = pdSectionVCPUs;
|
|
|
|
|
|
|
|
|
|
s.AddEntry(FriendlyName("VM.VCPUs"), vm.VCPUs_at_startup.ToString());
|
2014-07-16 15:55:22 +02:00
|
|
|
|
if (vm.VCPUs_at_startup != vm.VCPUs_max)
|
|
|
|
|
s.AddEntry(FriendlyName("VM.MaxVCPUs"), vm.VCPUs_max.ToString());
|
2014-07-14 16:17:47 +02:00
|
|
|
|
s.AddEntry(FriendlyName("VM.Topology"), vm.Topology);
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
private void generateDisconnectedHostBox()
|
|
|
|
|
{
|
|
|
|
|
IXenConnection conn = xenObject.Connection;
|
|
|
|
|
|
|
|
|
|
PDSection s = pdSectionGeneral;
|
|
|
|
|
|
|
|
|
|
string name = Helpers.GetName(xenObject);
|
|
|
|
|
s.AddEntry(FriendlyName("host.name_label"), name);
|
|
|
|
|
if (conn != null && conn.Hostname != name)
|
|
|
|
|
s.AddEntry(FriendlyName("host.address"), conn.Hostname);
|
|
|
|
|
|
|
|
|
|
if (conn != null && conn.PoolMembers.Count > 1)
|
|
|
|
|
s.AddEntry(FriendlyName("host.pool_members"), string.Join(", ", conn.PoolMembers.ToArray()));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateGeneralBox()
|
|
|
|
|
{
|
|
|
|
|
PDSection s = pdSectionGeneral;
|
|
|
|
|
|
|
|
|
|
s.AddEntry(FriendlyName("host.name_label"), Helpers.GetName(xenObject),
|
2014-01-22 13:39:32 +01:00
|
|
|
|
new PropertiesToolStripMenuItem(new PropertiesCommand(Program.MainWindow, xenObject)));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2015-10-27 19:11:53 +01:00
|
|
|
|
VM vm = xenObject as VM;
|
|
|
|
|
if (vm == null || vm.DescriptionType != VM.VmDescriptionType.None)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2015-10-27 19:11:53 +01:00
|
|
|
|
s.AddEntry(FriendlyName("host.name_description"), xenObject.Description,
|
|
|
|
|
new PropertiesToolStripMenuItem(new DescriptionPropertiesCommand(Program.MainWindow, xenObject)));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-27 19:11:53 +01:00
|
|
|
|
GenTagRow(s);
|
|
|
|
|
GenFolderRow(s);
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (xenObject is Host)
|
|
|
|
|
{
|
|
|
|
|
Host host = xenObject as Host;
|
|
|
|
|
|
|
|
|
|
if (Helpers.GetPool(xenObject.Connection) != null)
|
|
|
|
|
s.AddEntry(Messages.POOL_MASTER, host.IsMaster() ? Messages.YES : Messages.NO);
|
|
|
|
|
|
|
|
|
|
if (!host.IsLive)
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(FriendlyName("host.enabled"), Messages.HOST_NOT_LIVE, Color.Red);
|
|
|
|
|
}
|
|
|
|
|
else if (!host.enabled)
|
|
|
|
|
{
|
2013-08-22 17:48:16 +02:00
|
|
|
|
var item = new ToolStripMenuItem(Messages.EXIT_MAINTENANCE_MODE);
|
|
|
|
|
item.Click += delegate
|
|
|
|
|
{
|
2014-01-22 13:39:32 +01:00
|
|
|
|
new HostMaintenanceModeCommand(Program.MainWindow, host,
|
2013-08-22 17:48:16 +02:00
|
|
|
|
HostMaintenanceModeCommandParameter.Exit).Execute();
|
|
|
|
|
};
|
2013-06-24 13:41:48 +02:00
|
|
|
|
s.AddEntry(FriendlyName("host.enabled"),
|
|
|
|
|
host.MaintenanceMode ? Messages.HOST_IN_MAINTENANCE_MODE : Messages.DISABLED,
|
2013-12-13 11:51:08 +01:00
|
|
|
|
new[] { item },
|
2013-06-24 13:41:48 +02:00
|
|
|
|
Color.Red);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-08-22 17:48:16 +02:00
|
|
|
|
var item = new ToolStripMenuItem(Messages.ENTER_MAINTENANCE_MODE);
|
|
|
|
|
item.Click += delegate
|
|
|
|
|
{
|
2014-01-22 13:39:32 +01:00
|
|
|
|
new HostMaintenanceModeCommand(Program.MainWindow, host,
|
2013-08-22 17:48:16 +02:00
|
|
|
|
HostMaintenanceModeCommandParameter.Enter).Execute();
|
|
|
|
|
};
|
2013-12-22 18:33:13 +01:00
|
|
|
|
s.AddEntry(FriendlyName("host.enabled"), Messages.YES, item);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.AddEntry(FriendlyName("host.iscsi_iqn"), host.iscsi_iqn,
|
2014-01-22 13:39:32 +01:00
|
|
|
|
new PropertiesToolStripMenuItem(new IqnPropertiesCommand(Program.MainWindow, xenObject)));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
s.AddEntry(FriendlyName("host.log_destination"), host.SysLogDestination ?? Messages.HOST_LOG_DESTINATION_LOCAL,
|
2014-01-22 13:39:32 +01:00
|
|
|
|
new PropertiesToolStripMenuItem(new HostEditLogDestinationCommand(Program.MainWindow, xenObject)));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
PrettyTimeSpan uptime = host.Uptime;
|
|
|
|
|
PrettyTimeSpan agentUptime = host.AgentUptime;
|
|
|
|
|
s.AddEntry(FriendlyName("host.uptime"), uptime == null ? "" : host.Uptime.ToString());
|
|
|
|
|
s.AddEntry(FriendlyName("host.agentUptime"), agentUptime == null ? "" : host.AgentUptime.ToString());
|
|
|
|
|
|
2015-10-26 17:01:55 +01:00
|
|
|
|
if (host.external_auth_type == Auth.AUTH_TYPE_AD)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
s.AddEntry(FriendlyName("host.external_auth_service_name"), host.external_auth_service_name);
|
|
|
|
|
}
|
|
|
|
|
else if (xenObject is VM)
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(FriendlyName("VM.OSName"), vm.GetOSName());
|
|
|
|
|
|
2015-06-09 13:34:13 +02:00
|
|
|
|
s.AddEntry(FriendlyName("VM.OperatingMode"), vm.IsHVM ? Messages.VM_OPERATING_MODE_HVM : Messages.VM_OPERATING_MODE_PV);
|
|
|
|
|
|
2015-10-26 17:01:55 +01:00
|
|
|
|
if (!vm.DefaultTemplate)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
s.AddEntry(Messages.BIOS_STRINGS_COPIED, vm.BiosStringsCopied ? Messages.YES : Messages.NO);
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-26 17:01:55 +01:00
|
|
|
|
if (vm.Connection != null)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
var appl = vm.Connection.Resolve(vm.appliance);
|
|
|
|
|
if (appl != null)
|
|
|
|
|
{
|
2013-08-22 17:48:16 +02:00
|
|
|
|
var applProperties = new ToolStripMenuItem(Messages.VM_APPLIANCE_PROPERTIES);
|
|
|
|
|
applProperties.Click +=
|
|
|
|
|
(sender, e) =>
|
|
|
|
|
{
|
|
|
|
|
using (PropertiesDialog propertiesDialog = new PropertiesDialog(appl))
|
|
|
|
|
propertiesDialog.ShowDialog(this);
|
|
|
|
|
};
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2013-12-13 11:51:08 +01:00
|
|
|
|
s.AddEntryLink(Messages.VM_APPLIANCE, appl.Name, new[] { applProperties },
|
2013-06-24 13:41:48 +02:00
|
|
|
|
() =>
|
|
|
|
|
{
|
|
|
|
|
using (PropertiesDialog propertiesDialog = new PropertiesDialog(appl))
|
|
|
|
|
propertiesDialog.ShowDialog(this);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (vm.is_a_snapshot)
|
|
|
|
|
{
|
|
|
|
|
VM snapshotOf = vm.Connection.Resolve(vm.snapshot_of);
|
|
|
|
|
s.AddEntry(Messages.SNAPSHOT_OF, snapshotOf == null ? string.Empty : snapshotOf.Name);
|
|
|
|
|
s.AddEntry(Messages.CREATION_TIME, HelpersGUI.DateTimeToString(vm.snapshot_time.ToLocalTime() + vm.Connection.ServerTimeOffset, Messages.DATEFORMAT_DMY_HMS, true));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!vm.is_a_template)
|
|
|
|
|
{
|
2015-08-13 18:41:15 +02:00
|
|
|
|
GenerateVirtualisationStatusForGeneralBox(s, vm);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
if (vm.RunningTime != null)
|
|
|
|
|
s.AddEntry(FriendlyName("VM.uptime"), vm.RunningTime.ToString());
|
|
|
|
|
|
|
|
|
|
if (vm.IsP2V)
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(FriendlyName("VM.P2V_SourceMachine"), vm.P2V_SourceMachine);
|
|
|
|
|
s.AddEntry(FriendlyName("VM.P2V_ImportDate"), HelpersGUI.DateTimeToString(vm.P2V_ImportDate.ToLocalTime(), Messages.DATEFORMAT_DMY_HMS, true));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Dont show if WLB is enabled.
|
|
|
|
|
if (VMCanChooseHomeServer(vm))
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(FriendlyName("VM.affinity"), vm.AffinityServerString,
|
2014-01-22 13:39:32 +01:00
|
|
|
|
new PropertiesToolStripMenuItem(new VmEditHomeServerCommand(Program.MainWindow, xenObject)));}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (xenObject is XenObject<SR>)
|
|
|
|
|
{
|
|
|
|
|
SR sr = xenObject as SR;
|
|
|
|
|
s.AddEntry(Messages.TYPE, sr.FriendlyTypeName);
|
|
|
|
|
|
|
|
|
|
if (sr.content_type != SR.Content_Type_ISO && sr.GetSRType(false) != SR.SRTypes.udev)
|
2015-07-20 21:40:28 +02:00
|
|
|
|
{
|
2013-06-24 13:41:48 +02:00
|
|
|
|
s.AddEntry(FriendlyName("SR.size"), sr.SizeString);
|
2015-08-26 18:54:03 +02:00
|
|
|
|
|
2016-02-17 14:14:42 +01:00
|
|
|
|
/* DISABLED THIN PROVISIONING
|
2015-10-13 17:24:51 +02:00
|
|
|
|
if (sr.type == "lvmohba" || sr.type == "lvmoiscsi")
|
2015-08-26 18:54:03 +02:00
|
|
|
|
{
|
2015-10-13 17:24:51 +02:00
|
|
|
|
// add entries related to thin lvhd SRs
|
|
|
|
|
IEnumerable<CommandToolStripMenuItem> menuItems = null;
|
|
|
|
|
if (!sr.IsThinProvisioned)
|
|
|
|
|
{
|
|
|
|
|
menuItems = new[] { new CommandToolStripMenuItem(new ConvertToThinSRCommand(Program.MainWindow, new List<SelectedItem>() { new SelectedItem(xenObject) }), true) };
|
|
|
|
|
}
|
|
|
|
|
s.AddEntry(FriendlyName("SR.provisioning"), sr.IsThinProvisioned
|
|
|
|
|
? string.Format(Messages.SR_THIN_PROVISIONING_COMMITTED, sr.PercentageCommitted)
|
|
|
|
|
: Messages.SR_THICK_PROVISIONING, menuItems);
|
|
|
|
|
|
|
|
|
|
if(sr.IsThinProvisioned && sr.sm_config.ContainsKey("initial_allocation") && sr.sm_config.ContainsKey("allocation_quantum"))
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(FriendlyName("SR.disk-space-allocations"),
|
|
|
|
|
Helpers.GetAllocationProperties(sr.sm_config["initial_allocation"], sr.sm_config["allocation_quantum"]));
|
|
|
|
|
}
|
2016-02-17 12:42:19 +01:00
|
|
|
|
}
|
|
|
|
|
*/
|
2015-07-20 21:40:28 +02:00
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
if (sr.GetScsiID() != null)
|
|
|
|
|
s.AddEntry(FriendlyName("SR.scsiid"), sr.GetScsiID() ?? Messages.UNKNOWN);
|
|
|
|
|
|
|
|
|
|
// if in folder-view or if looking at SR on storagelink then display
|
|
|
|
|
// location here
|
|
|
|
|
if (Program.MainWindow.SelectionManager.Selection.HostAncestor == null && Program.MainWindow.SelectionManager.Selection.PoolAncestor == null)
|
|
|
|
|
{
|
|
|
|
|
IXenObject belongsTo = Helpers.GetPool(sr.Connection);
|
|
|
|
|
|
|
|
|
|
if (belongsTo != null)
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(Messages.POOL, Helpers.GetName(belongsTo));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
belongsTo = Helpers.GetMaster(sr.Connection);
|
|
|
|
|
|
|
|
|
|
if (belongsTo != null)
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(Messages.SERVER, Helpers.GetName(belongsTo));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (xenObject is XenObject<Pool>)
|
|
|
|
|
{
|
|
|
|
|
Pool p = xenObject as Pool;
|
|
|
|
|
if (p != null)
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(Messages.POOL_LICENSE, p.LicenseString);
|
|
|
|
|
if (Helpers.ClearwaterOrGreater(p.Connection))
|
|
|
|
|
s.AddEntry(Messages.NUMBER_OF_SOCKETS, p.CpuSockets.ToString());
|
2013-12-13 18:35:40 +01:00
|
|
|
|
|
2013-12-19 13:41:07 +01:00
|
|
|
|
var master = p.Connection.Resolve(p.master);
|
2013-12-20 12:00:53 +01:00
|
|
|
|
if (master != null)
|
2013-12-19 13:41:07 +01:00
|
|
|
|
{
|
2013-12-20 12:00:53 +01:00
|
|
|
|
if (p.IsPoolFullyUpgraded)
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(Messages.SOFTWARE_VERSION_PRODUCT_VERSION, master.ProductVersionText);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2014-01-22 13:39:32 +01:00
|
|
|
|
var cmd = new RollingUpgradeCommand(Program.MainWindow);
|
2013-12-20 12:00:53 +01:00
|
|
|
|
var runRpuWizard = new ToolStripMenuItem(Messages.ROLLING_POOL_UPGRADE_ELLIPSIS,
|
|
|
|
|
null,
|
|
|
|
|
(sender, args) => cmd.Execute());
|
|
|
|
|
|
|
|
|
|
s.AddEntryLink(Messages.SOFTWARE_VERSION_PRODUCT_VERSION,
|
|
|
|
|
string.Format(Messages.POOL_VERSIONS_LINK_TEXT, master.ProductVersionText),
|
|
|
|
|
new[] { runRpuWizard },
|
|
|
|
|
cmd);
|
|
|
|
|
}
|
2013-12-13 18:35:40 +01:00
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.AddEntry(FriendlyName("host.uuid"), GetUUID(xenObject));
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-13 18:41:15 +02:00
|
|
|
|
private static void GenerateVirtualisationStatusForGeneralBox(PDSection s, VM vm)
|
|
|
|
|
{
|
2015-12-15 15:32:11 +01:00
|
|
|
|
if (vm != null && vm.Connection != null)
|
2015-08-13 18:41:15 +02:00
|
|
|
|
{
|
|
|
|
|
//For Dundee or higher Windows VMs
|
|
|
|
|
if (Helpers.DundeeOrGreater(vm.Connection) && vm.IsWindows)
|
|
|
|
|
{
|
|
|
|
|
var gm = vm.Connection.Resolve(vm.guest_metrics);
|
|
|
|
|
|
2016-04-08 18:10:08 +02:00
|
|
|
|
bool isIoOptimized = gm != null && gm.PV_drivers_detected;
|
2016-01-13 14:26:37 +01:00
|
|
|
|
bool isManagementAgentInstalled = vm.GetVirtualisationStatus.HasFlag(VM.VirtualisationStatus.MANAGEMENT_INSTALLED);
|
2015-08-14 15:25:49 +02:00
|
|
|
|
bool canInstallIoDriversAndManagementAgent = InstallToolsCommand.CanExecute(vm) && !isIoOptimized;
|
|
|
|
|
bool canInstallManagementAgentOnly = InstallToolsCommand.CanExecute(vm) && isIoOptimized && !isManagementAgentInstalled;
|
2015-08-13 18:41:15 +02:00
|
|
|
|
//canInstallIoDriversOnly is missing - management agent communicates with XS using the I/O drivers
|
|
|
|
|
|
2015-12-15 15:32:11 +01:00
|
|
|
|
var sb = new StringBuilder();
|
2015-08-13 18:41:15 +02:00
|
|
|
|
|
2015-12-15 15:32:11 +01:00
|
|
|
|
if (vm.power_state == vm_power_state.Running)
|
|
|
|
|
{
|
|
|
|
|
if (vm.virtualisation_status.HasFlag(XenAPI.VM.VirtualisationStatus.UNKNOWN))
|
2015-08-13 18:41:15 +02:00
|
|
|
|
{
|
2015-12-15 15:32:11 +01:00
|
|
|
|
s.AddEntry(FriendlyName("VM.VirtualizationState"), vm.VirtualisationStatusString);
|
2015-08-13 18:41:15 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2015-12-15 15:32:11 +01:00
|
|
|
|
//Row 1 : I/O Drivers
|
|
|
|
|
if (isIoOptimized)
|
|
|
|
|
{
|
|
|
|
|
sb.Append(Messages.VIRTUALIZATION_STATE_VM_IO_OPTIMIZED);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sb.Append(Messages.VIRTUALIZATION_STATE_VM_IO_NOT_OPTIMIZED);
|
|
|
|
|
}
|
2015-08-13 18:41:15 +02:00
|
|
|
|
|
|
|
|
|
sb.Append(Environment.NewLine);
|
2015-12-15 15:32:11 +01:00
|
|
|
|
|
|
|
|
|
//Row 2: Management Agent
|
|
|
|
|
if (isManagementAgentInstalled)
|
|
|
|
|
{
|
|
|
|
|
sb.Append(Messages.VIRTUALIZATION_STATE_VM_MANAGEMENT_AGENT_INSTALLED);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sb.Append(Messages.VIRTUALIZATION_STATE_VM_MANAGEMENT_AGENT_NOT_INSTALLED);
|
|
|
|
|
}
|
2015-08-13 18:41:15 +02:00
|
|
|
|
sb.Append(Environment.NewLine);
|
|
|
|
|
}
|
2015-12-15 15:32:11 +01:00
|
|
|
|
}
|
2015-08-13 18:41:15 +02:00
|
|
|
|
|
2015-12-15 15:32:11 +01:00
|
|
|
|
//Row 3 : vendor device - Windows Update
|
|
|
|
|
sb.Append(vm.has_vendor_device ? Messages.VIRTUALIZATION_STATE_VM_RECEIVING_UPDATES : Messages.VIRTUALIZATION_STATE_VM_NOT_RECEIVING_UPDATES);
|
2015-08-13 18:41:15 +02:00
|
|
|
|
|
2015-12-15 15:32:11 +01:00
|
|
|
|
// displaying Row1 - Row3
|
|
|
|
|
s.AddEntry(FriendlyName("VM.VirtualizationState"), sb.ToString());
|
2015-08-13 18:41:15 +02:00
|
|
|
|
|
2015-12-15 15:32:11 +01:00
|
|
|
|
if (vm.power_state == vm_power_state.Running)
|
|
|
|
|
{
|
2015-08-13 18:41:15 +02:00
|
|
|
|
//Row 4: Install Tools
|
|
|
|
|
string installMessage = string.Empty;
|
|
|
|
|
if (canInstallIoDriversAndManagementAgent)
|
|
|
|
|
{
|
|
|
|
|
installMessage = Messages.VIRTUALIZATION_STATE_VM_INSTALL_IO_DRIVERS_AND_MANAGEMENT_AGENT;
|
|
|
|
|
}
|
|
|
|
|
else if (canInstallManagementAgentOnly)
|
|
|
|
|
{
|
|
|
|
|
installMessage = Messages.VIRTUALIZATION_STATE_VM_INSTALL_MANAGEMENT_AGENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(installMessage))
|
|
|
|
|
{
|
2015-09-22 18:19:03 +02:00
|
|
|
|
var installtools = new ToolStripMenuItem(installMessage);
|
2015-08-13 18:41:15 +02:00
|
|
|
|
installtools.Click += delegate
|
|
|
|
|
{
|
|
|
|
|
new InstallToolsCommand(Program.MainWindow, vm).Execute();
|
|
|
|
|
};
|
|
|
|
|
s.AddEntryLink(string.Empty, installMessage,
|
|
|
|
|
new[] { installtools },
|
|
|
|
|
new InstallToolsCommand(Program.MainWindow, vm));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-12-15 15:32:11 +01:00
|
|
|
|
|
2015-08-14 13:11:02 +02:00
|
|
|
|
//for everything else (All VMs on pre-Dundee hosts & All non-Windows VMs on any host)
|
2015-12-15 15:32:11 +01:00
|
|
|
|
else if (vm.power_state == vm_power_state.Running)
|
2015-08-13 18:41:15 +02:00
|
|
|
|
{
|
2015-09-18 16:48:59 +02:00
|
|
|
|
if (vm.virtualisation_status == 0 || vm.virtualisation_status.HasFlag(XenAPI.VM.VirtualisationStatus.PV_DRIVERS_OUT_OF_DATE))
|
2015-08-13 18:41:15 +02:00
|
|
|
|
{
|
|
|
|
|
if (InstallToolsCommand.CanExecute(vm))
|
|
|
|
|
{
|
|
|
|
|
var installtools = new ToolStripMenuItem(Messages.INSTALL_XENSERVER_TOOLS_DOTS);
|
|
|
|
|
installtools.Click += delegate
|
|
|
|
|
{
|
|
|
|
|
new InstallToolsCommand(Program.MainWindow, vm).Execute();
|
|
|
|
|
};
|
|
|
|
|
s.AddEntryLink(FriendlyName("VM.VirtualizationState"), vm.VirtualisationStatusString,
|
|
|
|
|
new[] { installtools },
|
|
|
|
|
new InstallToolsCommand(Program.MainWindow, vm));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(FriendlyName("VM.VirtualizationState"), vm.VirtualisationStatusString, Color.Red);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(FriendlyName("VM.VirtualizationState"), vm.VirtualisationStatusString);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-05 15:16:58 +01:00
|
|
|
|
private void generateDockerContainerGeneralBox()
|
|
|
|
|
{
|
|
|
|
|
if (xenObject is DockerContainer)
|
|
|
|
|
{
|
|
|
|
|
PDSection s = pdSectionGeneral;
|
|
|
|
|
DockerContainer dockerContainer = (DockerContainer)xenObject;
|
2015-02-10 04:03:53 +01:00
|
|
|
|
s.AddEntry(Messages.NAME, dockerContainer.Name.Length != 0 ? dockerContainer.Name : Messages.NONE);
|
|
|
|
|
s.AddEntry(Messages.STATUS, dockerContainer.status.Length != 0 ? dockerContainer.status : Messages.NONE);
|
2015-02-13 10:25:13 +01:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
DateTime created = Util.FromUnixTime(double.Parse(dockerContainer.created)).ToLocalTime();
|
|
|
|
|
s.AddEntry(Messages.CONTAINER_CREATED, HelpersGUI.DateTimeToString(created, Messages.DATEFORMAT_DMY_HMS, true));
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(Messages.CONTAINER_CREATED, dockerContainer.created.Length != 0 ? dockerContainer.created : Messages.NONE);
|
|
|
|
|
}
|
2015-02-10 04:03:53 +01:00
|
|
|
|
s.AddEntry(Messages.CONTAINER_IMAGE, dockerContainer.image.Length != 0 ? dockerContainer.image : Messages.NONE);
|
|
|
|
|
s.AddEntry(Messages.CONTAINER, dockerContainer.container.Length != 0 ? dockerContainer.container : Messages.NONE);
|
|
|
|
|
s.AddEntry(Messages.CONTAINER_COMMAND, dockerContainer.command.Length != 0 ? dockerContainer.command : Messages.NONE);
|
2015-03-06 12:43:47 +01:00
|
|
|
|
var ports = dockerContainer.PortList.Select(p => p.Description);
|
2015-03-09 16:43:49 +01:00
|
|
|
|
if (ports.Count() > 0)
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(Messages.CONTAINER_PORTS, string.Join(Environment.NewLine, ports));
|
|
|
|
|
}
|
2015-02-10 04:03:53 +01:00
|
|
|
|
s.AddEntry(Messages.UUID, dockerContainer.uuid.Length != 0 ? dockerContainer.uuid : Messages.NONE);
|
2014-12-05 15:16:58 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-19 15:09:08 +01:00
|
|
|
|
private void generateReadCachingBox()
|
|
|
|
|
{
|
|
|
|
|
VM vm = xenObject as VM;
|
|
|
|
|
if (vm == null || !vm.IsRunning || !Helpers.CreamOrGreater(vm.Connection))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
PDSection s = pdSectionReadCaching;
|
|
|
|
|
|
|
|
|
|
if (vm.ReadCachingEnabled)
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(FriendlyName("VM.read_caching_status"), Messages.VM_READ_CACHING_ENABLED);
|
|
|
|
|
var vdiList = vm.ReadCachingVDIs.Select(vdi => vdi.NameWithLocation).ToArray();
|
|
|
|
|
s.AddEntry(FriendlyName("VM.read_caching_disks"), string.Join("\n", vdiList));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
s.AddEntry(FriendlyName("VM.read_caching_status"), Messages.VM_READ_CACHING_DISABLED);
|
2015-03-16 20:54:55 +01:00
|
|
|
|
var reason = vm.ReadCachingDisabledReason;
|
|
|
|
|
if (reason != null)
|
|
|
|
|
s.AddEntry(FriendlyName("VM.read_caching_reason"), reason);
|
2015-02-19 15:09:08 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
private static bool VMCanChooseHomeServer(VM vm)
|
|
|
|
|
{
|
|
|
|
|
if (vm != null && !vm.is_a_template)
|
|
|
|
|
{
|
|
|
|
|
String ChangeHomeReason = vm.IsOnSharedStorage();
|
|
|
|
|
|
|
|
|
|
return !Helpers.WlbEnabledAndConfigured(vm.Connection) &&
|
|
|
|
|
(String.IsNullOrEmpty(ChangeHomeReason) || vm.HasNoDisksAndNoLocalCD);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void GenTagRow(PDSection s)
|
|
|
|
|
{
|
|
|
|
|
string[] tags = Tags.GetTags(xenObject);
|
2013-12-13 11:51:08 +01:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (tags != null && tags.Length > 0)
|
|
|
|
|
{
|
2013-08-22 17:48:16 +02:00
|
|
|
|
ToolStripMenuItem goToTag = new ToolStripMenuItem(Messages.VIEW_TAG_MENU_OPTION);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
foreach (string tag in tags)
|
|
|
|
|
{
|
2013-08-22 17:48:16 +02:00
|
|
|
|
var item = new ToolStripMenuItem(tag.Ellipsise(30));
|
|
|
|
|
item.Click += delegate { Program.MainWindow.SearchForTag(tag); };
|
|
|
|
|
goToTag.DropDownItems.Add(item);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2013-12-13 11:51:08 +01:00
|
|
|
|
|
2014-01-22 13:39:32 +01:00
|
|
|
|
s.AddEntry(Messages.TAGS, TagsString(), new[] { goToTag, new PropertiesToolStripMenuItem(new PropertiesCommand(Program.MainWindow, xenObject)) });
|
2013-06-24 13:41:48 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2013-12-13 11:51:08 +01:00
|
|
|
|
|
2014-01-22 13:39:32 +01:00
|
|
|
|
s.AddEntry(Messages.TAGS, Messages.NONE, new PropertiesToolStripMenuItem(new PropertiesCommand(Program.MainWindow, xenObject)));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string TagsString()
|
|
|
|
|
{
|
|
|
|
|
string[] tags = Tags.GetTags(xenObject);
|
|
|
|
|
if (tags == null || tags.Length == 0)
|
|
|
|
|
return Messages.NONE;
|
|
|
|
|
|
2013-12-13 11:51:08 +01:00
|
|
|
|
return string.Join(", ", tags.OrderBy(s => s).ToArray());
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void GenFolderRow(PDSection s)
|
|
|
|
|
{
|
|
|
|
|
List<ToolStripMenuItem> menuItems = new List<ToolStripMenuItem>();
|
|
|
|
|
if (xenObject.Path != "")
|
|
|
|
|
{
|
2013-08-22 17:48:16 +02:00
|
|
|
|
var item = new ToolStripMenuItem(Messages.VIEW_FOLDER_MENU_OPTION);
|
|
|
|
|
item.Click += delegate { Program.MainWindow.SearchForFolder(xenObject.Path); };
|
|
|
|
|
menuItems.Add(item);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2014-01-22 13:39:32 +01:00
|
|
|
|
menuItems.Add(new PropertiesToolStripMenuItem(new PropertiesCommand(Program.MainWindow, xenObject)));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
s.AddEntry(
|
|
|
|
|
Messages.FOLDER,
|
|
|
|
|
new FolderListItem(xenObject.Path, FolderListItem.AllowSearch.None, false),
|
|
|
|
|
menuItems
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateMemoryBox()
|
|
|
|
|
{
|
|
|
|
|
Host host = xenObject as Host;
|
|
|
|
|
if (host == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
PDSection s = pdSectionMemory;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
s.AddEntry(FriendlyName("host.ServerMemory"), host.HostMemoryString);
|
|
|
|
|
s.AddEntry(FriendlyName("host.VMMemory"), host.ResidentVMMemoryUsageString);
|
|
|
|
|
s.AddEntry(FriendlyName("host.XenMemory"), host.XenMemoryString);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-15 03:52:12 +01:00
|
|
|
|
private void addStringEntry(PDSection s, string key, string value)
|
|
|
|
|
{
|
2015-02-18 10:50:55 +01:00
|
|
|
|
s.AddEntry(key, string.IsNullOrEmpty(value) ? Messages.NONE : value);
|
2015-02-15 03:52:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateDockerInfoBox()
|
|
|
|
|
{
|
|
|
|
|
VM vm = xenObject as VM;
|
|
|
|
|
if (vm == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
VM_Docker_Info info = vm.DockerInfo;
|
|
|
|
|
if (info == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
VM_Docker_Version version = vm.DockerVersion;
|
|
|
|
|
if (version == null)
|
|
|
|
|
return;
|
|
|
|
|
|
CA-162989: Container Management GUI use-ability/homogeneity fixes
Implemented changes as follows (copied from ticket):
"I'd suggest the following use-ability/homogeneity fixes for the new container management tabs, if they are quick and easy:
Combine "Docker Version" and "Docker Information" on the VM-General-tab into "Container Management - Docker Status" with the following fields only:
API version
Version
Git Commit
Driver
Index Server Address
Execution Driver
IPv4 Forwarding
In the "Processes" tab, change the name from "Docker Processes" to "Container Processes"
In the "Details" tab, change the name from "Docker Detail" to "Container Details"
In the "Details" tab, drop the top level element "docker_inspect" (XML requires a single root-node, afaik the Windows form treenode does not), or alternatively open the root node by default and rename it to "Inspect Result"
In the "Details" tab, add the "Details"-headline in black on white - just like on the "Processes"-tab
Also, on the container's General tab, show Properties button disabled, instead on hiding it (to be consistent to other cases, e.g. disconnected servers)
"
Signed-off-by: Gabor Apati-Nagy <gabor.apati-nagy@citrix.com>
2015-03-05 20:10:54 +01:00
|
|
|
|
PDSection s = pdSectionDockerInfo;
|
|
|
|
|
|
2015-02-15 03:52:12 +01:00
|
|
|
|
addStringEntry(s, Messages.DOCKER_INFO_API_VERSION, version.ApiVersion);
|
|
|
|
|
addStringEntry(s, Messages.DOCKER_INFO_VERSION, version.Version);
|
|
|
|
|
addStringEntry(s, Messages.DOCKER_INFO_GIT_COMMIT, version.GitCommit);
|
CA-162989: Container Management GUI use-ability/homogeneity fixes
Implemented changes as follows (copied from ticket):
"I'd suggest the following use-ability/homogeneity fixes for the new container management tabs, if they are quick and easy:
Combine "Docker Version" and "Docker Information" on the VM-General-tab into "Container Management - Docker Status" with the following fields only:
API version
Version
Git Commit
Driver
Index Server Address
Execution Driver
IPv4 Forwarding
In the "Processes" tab, change the name from "Docker Processes" to "Container Processes"
In the "Details" tab, change the name from "Docker Detail" to "Container Details"
In the "Details" tab, drop the top level element "docker_inspect" (XML requires a single root-node, afaik the Windows form treenode does not), or alternatively open the root node by default and rename it to "Inspect Result"
In the "Details" tab, add the "Details"-headline in black on white - just like on the "Processes"-tab
Also, on the container's General tab, show Properties button disabled, instead on hiding it (to be consistent to other cases, e.g. disconnected servers)
"
Signed-off-by: Gabor Apati-Nagy <gabor.apati-nagy@citrix.com>
2015-03-05 20:10:54 +01:00
|
|
|
|
addStringEntry(s, Messages.DOCKER_INFO_DRIVER, info.Driver);
|
|
|
|
|
addStringEntry(s, Messages.DOCKER_INFO_INDEX_SERVER_ADDRESS, info.IndexServerAddress);
|
|
|
|
|
addStringEntry(s, Messages.DOCKER_INFO_EXECUTION_DRIVER, info.ExecutionDriver);
|
|
|
|
|
addStringEntry(s, Messages.DOCKER_INFO_IPV4_FORWARDING, info.IPv4Forwarding);
|
2015-02-15 03:52:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
private bool CPUsIdentical(IEnumerable<Host_cpu> cpus)
|
|
|
|
|
{
|
|
|
|
|
String cpuText = null;
|
|
|
|
|
foreach (Host_cpu cpu in cpus)
|
|
|
|
|
{
|
|
|
|
|
if (cpuText == null)
|
|
|
|
|
{
|
|
|
|
|
cpuText = Helpers.GetCPUProperties(cpu);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (Helpers.GetCPUProperties(cpu) != cpuText)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string hostAppliedPatches(Host host)
|
|
|
|
|
{
|
|
|
|
|
List<string> result = new List<string>();
|
|
|
|
|
|
|
|
|
|
foreach (Pool_patch patch in host.AppliedPatches())
|
|
|
|
|
result.Add(patch.Name);
|
|
|
|
|
|
|
|
|
|
result.Sort(StringUtility.NaturalCompare);
|
|
|
|
|
|
|
|
|
|
return string.Join("\n", result.ToArray());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string hostUnappliedPatches(Host host)
|
|
|
|
|
{
|
|
|
|
|
List<string> result = new List<string>();
|
|
|
|
|
|
|
|
|
|
foreach (Pool_patch patch in Pool_patch.GetAllThatApply(host, ConnectionsManager.XenConnectionsCopy))
|
|
|
|
|
{
|
|
|
|
|
if (!patch.AppliedTo(ConnectionsManager.XenConnectionsCopy).Contains(new XenRef<Host>(xenObject.opaque_ref)))
|
|
|
|
|
result.Add(patch.Name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.Sort(StringUtility.NaturalCompare);
|
|
|
|
|
return string.Join("\n", result.ToArray());
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-28 14:17:58 +01:00
|
|
|
|
private string hostInstalledSuppPacks(Host host)
|
|
|
|
|
{
|
|
|
|
|
var result = host.SuppPacks.Select(suppPack => suppPack.LongDescription).ToList();
|
|
|
|
|
result.Sort(StringUtility.NaturalCompare);
|
|
|
|
|
return string.Join("\n", result.ToArray());
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
#region VM delegates
|
|
|
|
|
|
|
|
|
|
private static string HVMBootOrder(VM vm)
|
|
|
|
|
{
|
|
|
|
|
var order = vm.BootOrder.ToUpper().Union(new[] { 'D', 'C', 'N' });
|
|
|
|
|
return string.Join("\n", order.Select(c => new BootDevice(c).ToString()).ToArray());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Pool delegates
|
|
|
|
|
|
|
|
|
|
private string poolAppliedPatches()
|
|
|
|
|
{
|
|
|
|
|
return poolPatchString(patch => patch.host_patches.Count == xenObject.Connection.Cache.HostCount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string poolPartialPatches()
|
|
|
|
|
{
|
|
|
|
|
return poolPatchString(patch => patch.host_patches.Count > 0 &&
|
|
|
|
|
patch.host_patches.Count != xenObject.Connection.Cache.HostCount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string poolNotAppliedPatches()
|
|
|
|
|
{
|
|
|
|
|
return poolPatchString(patch => patch.host_patches.Count == 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string poolPatchString(Predicate<Pool_patch> predicate)
|
|
|
|
|
{
|
|
|
|
|
Pool_patch[] patches = xenObject.Connection.Cache.Pool_patches;
|
|
|
|
|
|
|
|
|
|
List<String> output = new List<String>();
|
|
|
|
|
|
|
|
|
|
foreach (Pool_patch patch in patches)
|
|
|
|
|
if (predicate(patch))
|
|
|
|
|
output.Add(patch.name_label);
|
|
|
|
|
|
|
|
|
|
output.Sort(StringUtility.NaturalCompare);
|
|
|
|
|
|
|
|
|
|
return String.Join(",", output.ToArray());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks for reboot warnings on all hosts in the pool and returns them as a list
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="pool"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private List<KeyValuePair<String, String>> CheckPoolUpdate(Pool pool)
|
|
|
|
|
{
|
|
|
|
|
List<KeyValuePair<String, String>> warnings = new List<KeyValuePair<string, string>>();
|
|
|
|
|
foreach (Host host in xenObject.Connection.Cache.Hosts)
|
|
|
|
|
{
|
|
|
|
|
warnings.AddRange(CheckServerUpdates(host));
|
|
|
|
|
}
|
|
|
|
|
return warnings;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks the server has been restarted after any patches that require a restart were applied and returns a list of reboot warnings
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="host"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private List<KeyValuePair<String, String>> CheckServerUpdates(Host host)
|
|
|
|
|
{
|
|
|
|
|
List<Pool_patch> patches = host.AppliedPatches();
|
|
|
|
|
List<KeyValuePair<String, String>> warnings = new List<KeyValuePair<String, String>>();
|
|
|
|
|
double bootTime = host.BootTime;
|
|
|
|
|
double agentStart = host.AgentStartTime;
|
|
|
|
|
|
|
|
|
|
if (bootTime == 0.0 || agentStart == 0.0)
|
|
|
|
|
return warnings;
|
|
|
|
|
|
|
|
|
|
foreach (Pool_patch patch in patches)
|
|
|
|
|
{
|
|
|
|
|
double applyTime = Util.ToUnixTime(patch.AppliedOn(host));
|
|
|
|
|
|
|
|
|
|
if (patch.after_apply_guidance.Contains(after_apply_guidance.restartHost)
|
|
|
|
|
&& applyTime > bootTime)
|
|
|
|
|
{
|
|
|
|
|
//TODO: Could we come up with a better key string than foopatch on blahhost? Also needs i18
|
|
|
|
|
warnings.Add(new KeyValuePair<String, String>(
|
|
|
|
|
String.Format("{0} on {1}", patch.Name, host.Name),
|
|
|
|
|
String.Format(Messages.GENERAL_PANEL_UPDATE_WARNING, host.Name, patch.Name)));
|
|
|
|
|
}
|
|
|
|
|
else if (patch.after_apply_guidance.Contains(after_apply_guidance.restartXAPI)
|
|
|
|
|
&& applyTime > agentStart)
|
|
|
|
|
{
|
|
|
|
|
// Actually, it only needs xapi restart, but we have no UI to do that.
|
|
|
|
|
warnings.Add(new KeyValuePair<String, String>(
|
|
|
|
|
String.Format("{0} on {1}", patch.Name, host.Name),
|
|
|
|
|
String.Format(Messages.GENERAL_PANEL_UPDATE_WARNING, host.Name, patch.Name)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return warnings;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string GetUUID(IXenObject o)
|
|
|
|
|
{
|
|
|
|
|
return o.Get("uuid") as String;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string FriendlyName(string propertyName)
|
|
|
|
|
{
|
|
|
|
|
return Core.PropertyManager.GetFriendlyName(string.Format("Label-{0}", propertyName)) ?? propertyName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void linkLabelExpand_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
foreach (PDSection s in sections)
|
|
|
|
|
{
|
|
|
|
|
if (!s.Parent.Visible)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
s.DisableFocusEvent = true;
|
|
|
|
|
s.Expand();
|
|
|
|
|
s.DisableFocusEvent = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
linkLabelCollapse.Focus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void linkLabelCollapse_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
foreach (PDSection s in sections)
|
|
|
|
|
{
|
|
|
|
|
if (!s.Parent.Visible)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
s.DisableFocusEvent = true;
|
|
|
|
|
s.Contract();
|
|
|
|
|
s.DisableFocusEvent = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
linkLabelExpand.Focus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SetStatesOfExpandingLinks()
|
|
|
|
|
{
|
2013-12-13 11:51:08 +01:00
|
|
|
|
var sectionsVisible = sections.Where(section => section.Parent.Visible);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
bool anyExpanded = sectionsVisible.Any(s => s.IsExpanded);
|
|
|
|
|
bool anyCollapsed = sectionsVisible.Any(s => !s.IsExpanded);
|
|
|
|
|
linkLabelExpand.Enabled = anyCollapsed;
|
|
|
|
|
linkLabelCollapse.Enabled = anyExpanded;
|
|
|
|
|
}
|
2016-02-03 11:12:05 +01:00
|
|
|
|
|
|
|
|
|
private void buttonViewConsole_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
2016-02-06 08:18:07 +01:00
|
|
|
|
if (xenObject is DockerContainer)
|
|
|
|
|
{
|
|
|
|
|
DockerContainer dockerContainer = (DockerContainer)xenObject;
|
|
|
|
|
string vmIp = dockerContainer.Parent.IPAddressForSSH;
|
|
|
|
|
//Set command 'docker attach' to attach to the container.
|
|
|
|
|
string dockerCmd = "env docker attach --sig-proxy=false " + dockerContainer.uuid;
|
|
|
|
|
startPutty(dockerCmd, vmIp);
|
|
|
|
|
}
|
2016-02-03 11:12:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void buttonViewLog_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
2016-02-06 08:18:07 +01:00
|
|
|
|
if (xenObject is DockerContainer)
|
|
|
|
|
{
|
|
|
|
|
DockerContainer dockerContainer = (DockerContainer)xenObject;
|
|
|
|
|
string vmIp = dockerContainer.Parent.IPAddressForSSH;
|
|
|
|
|
//Set command 'docker logs' to retrieve the logs of the container.
|
|
|
|
|
string dockerCmd = "env docker logs --tail=50 --follow --timestamps " + dockerContainer.uuid;
|
|
|
|
|
startPutty(dockerCmd, vmIp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void startPutty(string command, string ipAddr)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
//Write docker command to a temp file.
|
2016-02-15 09:47:49 +01:00
|
|
|
|
string cmdFile = Path.Combine(Path.GetTempPath(), "ContainerManagementCommand.txt");
|
2016-02-06 08:18:07 +01:00
|
|
|
|
File.WriteAllText(cmdFile, command);
|
2016-02-03 11:12:05 +01:00
|
|
|
|
|
2016-02-06 08:18:07 +01:00
|
|
|
|
//Invoke Putty, SSH to VM and execute docker command.
|
|
|
|
|
var puttyPath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "putty.exe");
|
2016-02-15 09:47:49 +01:00
|
|
|
|
string args = "-m " + cmdFile + " -t " + ipAddr;
|
|
|
|
|
|
|
|
|
|
//Specify the key for SSH connection.
|
|
|
|
|
var keyFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "ContainerManagement.ppk");
|
|
|
|
|
if (File.Exists(keyFile))
|
|
|
|
|
{
|
|
|
|
|
args = args + " -i " + keyFile;
|
|
|
|
|
}
|
|
|
|
|
var startInfo = new ProcessStartInfo(puttyPath, args);
|
|
|
|
|
|
2016-02-06 08:18:07 +01:00
|
|
|
|
Process.Start(startInfo);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
log.Error("Error starting PuTTY.", ex);
|
|
|
|
|
new ThreeButtonDialog(new ThreeButtonDialog.Details(SystemIcons.Error, Messages.ERROR_PUTTY_LAUNCHING, Messages.XENCENTER)).ShowDialog(Parent);
|
|
|
|
|
}
|
2016-02-03 11:12:05 +01:00
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|