diff --git a/XenAdmin/Controls/PDSection.cs b/XenAdmin/Controls/PDSection.cs index 3d1ad0709..4909dc3c3 100644 --- a/XenAdmin/Controls/PDSection.cs +++ b/XenAdmin/Controls/PDSection.cs @@ -394,6 +394,13 @@ namespace XenAdmin.Controls AddRow(CreateKeyCell(key), valueCell, null, contextMenuItems); } + internal void AddEntryWithNoteLink(string key, string value, string note, Action action, params ToolStripMenuItem[] contextMenuItems) + { + var valueCell = new DataGridViewTextBoxCell { Value = value }; + var noteCell = new DataGridViewLinkCell { Value = note, Tag = action }; + AddRow(CreateKeyCell(key), valueCell, noteCell, contextMenuItems); + } + internal void AddEntryWithNoteLink(string key, string value, string note, Action action, Color fontColor, params ToolStripMenuItem[] contextMenuItems) { var valueCell = new DataGridViewTextBoxCell { Value = value }; diff --git a/XenAdmin/Controls/PvsCacheStorageRow.cs b/XenAdmin/Controls/PvsCacheStorageRow.cs index c2ef95664..c450e215c 100644 --- a/XenAdmin/Controls/PvsCacheStorageRow.cs +++ b/XenAdmin/Controls/PvsCacheStorageRow.cs @@ -71,7 +71,7 @@ namespace XenAdmin.Controls comboBoxCacheSr.Items.Clear(); // add the "Not configured" item first - var notConfiguredItem = new SrComboBoxItem(null, Messages.PVS_CACHE_NOT_CONFIGURED); + var notConfiguredItem = new SrComboBoxItem(null, Messages.NOT_CONFIGURED); comboBoxCacheSr.Items.Add(notConfiguredItem); // add Memory SR; if no memory SR found, add a placeholder (we will create the memory SR in ConfigurePvsCacheAction) diff --git a/XenAdmin/Dialogs/PvsPages/PvsCacheConfigurationPage.cs b/XenAdmin/Dialogs/PvsPages/PvsCacheConfigurationPage.cs index bb2ad19c3..3f8767233 100644 --- a/XenAdmin/Dialogs/PvsPages/PvsCacheConfigurationPage.cs +++ b/XenAdmin/Dialogs/PvsPages/PvsCacheConfigurationPage.cs @@ -109,7 +109,7 @@ namespace XenAdmin.Dialogs var configuredRows = rows.Where(r => r.CacheSr != null).ToList(); if (configuredRows.Count == 0) - return Messages.PVS_CACHE_NOT_CONFIGURED; + return Messages.NOT_CONFIGURED; return configuredRows.Any(row => row.CacheSr.GetSRType(false) != SR.SRTypes.tmpfs) ? Messages.PVS_CACHE_MEMORY_AND_DISK diff --git a/XenAdmin/TabPages/GeneralTabPage.cs b/XenAdmin/TabPages/GeneralTabPage.cs index 27b78d271..95f2e6372 100644 --- a/XenAdmin/TabPages/GeneralTabPage.cs +++ b/XenAdmin/TabPages/GeneralTabPage.cs @@ -35,11 +35,13 @@ using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; +using XenAdmin.Actions; using XenAdmin.Commands; using XenAdmin.Controls; using XenAdmin.Core; using XenAdmin.CustomFields; using XenAdmin.Dialogs; +using XenAdmin.Dialogs.ServerUpdates; using XenAdmin.Model; using XenAdmin.Network; using XenAdmin.SettingsPanels; @@ -372,9 +374,8 @@ namespace XenAdmin.TabPages base.Text = Messages.CONNECTION_GENERAL_TAB_TITLE; else if (xenObject is Host) base.Text = Messages.HOST_GENERAL_TAB_TITLE; - else if (xenObject is VM) + else if (xenObject is VM vm) { - VM vm = (VM)xenObject; if (vm.is_a_snapshot) base.Text = Messages.SNAPSHOT_GENERAL_TAB_TITLE; else if (vm.is_a_template) @@ -417,12 +418,12 @@ namespace XenAdmin.TabPages GenerateVersionBox(); GenerateLicenseBox(); GenerateCPUBox(); - GenerateHostPatchesBox(); + GenerateHostUpdatesBox(); GenerateBootBox(); GenerateHABox(); GenerateStatusBox(); GenerateMultipathBox(); - GeneratePoolPatchesBox(); + GeneratePoolUpdatesBox(); GenerateMultipathBootBox(); GenerateVCPUsBox(); GenerateDockerInfoBox(); @@ -543,24 +544,18 @@ namespace XenAdmin.TabPages } } - private void GeneratePoolPatchesBox() + private void GeneratePoolUpdatesBox() { if (!(xenObject is Pool pool)) return; - PDSection s = pdSectionUpdates; - var messages = CheckPoolUpdate(pool); if (messages.Count > 0) { foreach (var kvp in messages) - s.AddEntry(kvp.Key, kvp.Value); + pdSectionUpdates.AddEntry(kvp.Key, kvp.Value); } - Host coordinator = Helpers.GetCoordinator(xenObject.Connection); - if (coordinator == null) - return; - var fullyApplied = new List(); var partAppliedError = new List(); var partApplied = new List(); @@ -568,7 +563,11 @@ namespace XenAdmin.TabPages var cache = xenObject.Connection.Cache; var allHostCount = xenObject.Connection.Cache.HostCount; - if (Helpers.ElyOrGreater(xenObject.Connection)) + if (Helpers.CloudOrGreater(pool.Connection)) + { + GenerateCdnUpdatesBox(pool); + } + else if (Helpers.ElyOrGreater(xenObject.Connection)) { foreach (var u in cache.Pool_updates) { @@ -605,76 +604,127 @@ namespace XenAdmin.TabPages if (fullyApplied.Count > 0) { fullyApplied.Sort(StringUtility.NaturalCompare); - s.AddEntry(FriendlyName("Pool_patch.fully_applied"), string.Join(Environment.NewLine, fullyApplied)); + pdSectionUpdates.AddEntry(FriendlyName("Pool_patch.fully_applied"), string.Join(Environment.NewLine, fullyApplied)); } if (partApplied.Count > 0) { var menuItems = new ToolStripMenuItem[] {new CommandToolStripMenuItem(new InstallNewUpdateCommand(Program.MainWindow), true)}; partApplied.Sort(StringUtility.NaturalCompare); - s.AddEntry(FriendlyName("Pool_patch.partially_applied"), string.Join(Environment.NewLine, partApplied), menuItems); + pdSectionUpdates.AddEntry(FriendlyName("Pool_patch.partially_applied"), string.Join(Environment.NewLine, partApplied), menuItems); } if (partAppliedError.Count > 0) { var menuItems = new ToolStripMenuItem[] {new CommandToolStripMenuItem(new InstallNewUpdateCommand(Program.MainWindow), true)}; partAppliedError.Sort(StringUtility.NaturalCompare); - s.AddEntry(string.Format(Messages.STRING_SPACE_STRING, + pdSectionUpdates.AddEntry(string.Format(Messages.STRING_SPACE_STRING, FriendlyName("Pool_patch.partially_applied"), Messages.UPDATES_GENERAL_TAB_ENFORCE_HOMOGENEITY), string.Join(Environment.NewLine, partAppliedError), Color.Red, menuItems); } } - private void GenerateHostPatchesBox() + private void GenerateCdnUpdatesBox(Pool pool) { - Host host = xenObject as Host; - if (host == null) - return; + var repoNames = (from repoRef in pool.repositories + let repo = pool.Connection.Resolve(repoRef) + where repo != null + let found = RepoDescriptor.AllRepos.FirstOrDefault(rd => rd.MatchesRepository(repo)) + where found != null + select found.FriendlyName).ToList(); - PDSection s = pdSectionUpdates; - List> messages; - - bool elyOrGreater = Helpers.ElyOrGreater(host); - - if (elyOrGreater) - { - // As of Ely we use host.updates_requiring_reboot to generate the list of reboot required messages - messages = CheckHostUpdatesRequiringReboot(host); - } - else - { - // For older versions no change to how messages are generated - messages = CheckServerUpdates(host); - } - - if (messages.Count > 0) - { - foreach (KeyValuePair kvp in messages) + pdSectionUpdates.AddEntryWithNoteLink(Messages.UPDATES_GENERAL_TAB_REPO, + repoNames.Count == 0 ? Messages.NOT_CONFIGURED : string.Join("\n", repoNames), + Messages.UPDATES_GENERAL_TAB_CONFIG, + () => { - s.AddEntry(kvp.Key, kvp.Value); + using (var dialog = new ConfigUpdatesDialog()) + dialog.ShowDialog(this); + }); + + string lastSyncTime = Messages.INDETERMINABLE; + + if (Helpers.XapiEqualOrGreater_23_18_0(pool.Connection)) + { + lastSyncTime = Messages.NEVER; + + if (pool.last_update_sync > Util.GetUnixMinDateTime()) + { + lastSyncTime = HelpersGUI.DateTimeToString(pool.last_update_sync.ToLocalTime(), Messages.DATEFORMAT_DMY_HMS, true); } } - var appliedPatchesList = Helpers.HostAppliedPatchesList(host); - var appliedPatches = string.Join(Environment.NewLine, appliedPatchesList.ToArray()); + pdSectionUpdates.AddEntryWithNoteLink(Messages.UPDATES_GENERAL_TAB_LAST_SYNCED, + lastSyncTime, Messages.UPDATES_GENERAL_TAB_SYNC_NOW, + () => + { + var syncAction = new SyncWithCdnAction(pool); + syncAction.Completed += a => Updates.CheckForCdnUpdates(a.Connection); + syncAction.RunAsync(); + }); + } + + private void GenerateHostUpdatesBox() + { + if (!(xenObject is Host host)) + return; + + bool elyOrGreater = Helpers.ElyOrGreater(host); + + // As of Ely we use host.updates_requiring_reboot to generate the list of reboot required messages + // For older versions no change to how messages are generated + + var messages = elyOrGreater ? CheckHostUpdatesRequiringReboot(host) : CheckServerUpdates(host); + + foreach (var kvp in messages) + pdSectionUpdates.AddEntry(kvp.Key, kvp.Value); + + var appliedPatches = string.Join(Environment.NewLine, Helpers.HostAppliedPatchesList(host)); + if (!string.IsNullOrEmpty(appliedPatches)) - { - s.AddEntry(FriendlyName("Pool_patch.applied"), appliedPatches); - } + pdSectionUpdates.AddEntry(FriendlyName("Pool_patch.applied"), appliedPatches); var recommendedPatches = RecommendedPatchesForHost(host); + if (!string.IsNullOrEmpty(recommendedPatches)) - { - s.AddEntry(FriendlyName("Pool_patch.required-updates"), recommendedPatches); - } + pdSectionUpdates.AddEntry(FriendlyName("Pool_patch.required-updates"), recommendedPatches); if (!elyOrGreater) { - // add supplemental packs var suppPacks = hostInstalledSuppPacks(host); + if (!string.IsNullOrEmpty(suppPacks)) + pdSectionUpdates.AddEntry(FriendlyName("Supplemental_packs.installed"), suppPacks); + } + + if (Helpers.CloudOrGreater(host)) + { + var pool = Helpers.GetPool(host.Connection); + if (pool == null) //standalone host { - s.AddEntry(FriendlyName("Supplemental_packs.installed"), suppPacks); + pool = Helpers.GetPoolOfOne(host.Connection); + GenerateCdnUpdatesBox(pool); + } + + if (Helpers.XapiEqualOrGreater_22_20_0(host)) + { + var unixMinDateTime = Util.GetUnixMinDateTime(); + var softwareVersionDate = unixMinDateTime; + + if (host.software_version.ContainsKey("date")) + { + if (!Util.TryParseIso8601DateTime(host.software_version["date"], out softwareVersionDate)) + Util.TryParseNonIso8601DateTime(host.software_version["date"], out softwareVersionDate); + } + + string lastUpdateTime = Messages.NEVER; + + if (host.last_software_update > softwareVersionDate && host.last_software_update > unixMinDateTime) + { + lastUpdateTime = HelpersGUI.DateTimeToString(host.last_software_update.ToLocalTime(), Messages.DATEFORMAT_DMY_HMS, true); + } + + pdSectionUpdates.AddEntry(Messages.SOFTWARE_VERSION_LAST_UPDATED, lastUpdateTime); } } } @@ -1080,19 +1130,16 @@ namespace XenAdmin.TabPages private void GenerateVersionBox() { - Host host = xenObject as Host; - - if (host == null || host.software_version == null) + if (!(xenObject is Host host) || host.software_version == null) return; - var softwareVersionDate = DateTime.MinValue; var unixMinDateTime = Util.GetUnixMinDateTime(); if (host.software_version.ContainsKey("date")) { string buildDate = host.software_version["date"]; - if (Util.TryParseIso8601DateTime(host.software_version["date"], out softwareVersionDate) && softwareVersionDate > unixMinDateTime) + if (Util.TryParseIso8601DateTime(host.software_version["date"], out var softwareVersionDate) && softwareVersionDate > unixMinDateTime) buildDate = HelpersGUI.DateTimeToString(softwareVersionDate.ToLocalTime(), Messages.DATEFORMAT_DMY_HMS, true); else if (Util.TryParseNonIso8601DateTime(host.software_version["date"], out softwareVersionDate) && softwareVersionDate > unixMinDateTime) buildDate = HelpersGUI.DateTimeToString(softwareVersionDate.ToLocalTime(), Messages.DATEFORMAT_DMY, true); @@ -1116,11 +1163,6 @@ namespace XenAdmin.TabPages if (host.software_version.ContainsKey("dbv")) pdSectionVersion.AddEntry("DBV", host.software_version["dbv"]); - - if (Helpers.CloudOrGreater(host) && Helpers.XapiEqualOrGreater_22_20_0(host) && - host.last_software_update > softwareVersionDate && host.last_software_update > unixMinDateTime) - pdSectionVersion.AddEntry(Messages.SOFTWARE_VERSION_LAST_UPDATED, - HelpersGUI.DateTimeToString(host.last_software_update.ToLocalTime(), Messages.DATEFORMAT_DMY_HMS, true)); } private void GenerateCPUBox() diff --git a/XenModel/Messages.Designer.cs b/XenModel/Messages.Designer.cs index ab2b7dc86..0bfb5e1c1 100755 --- a/XenModel/Messages.Designer.cs +++ b/XenModel/Messages.Designer.cs @@ -21503,6 +21503,15 @@ namespace XenAdmin { } } + /// + /// Looks up a localized string similar to Indeterminable. + /// + public static string INDETERMINABLE { + get { + return ResourceManager.GetString("INDETERMINABLE", resourceCulture); + } + } + /// /// Looks up a localized string similar to Disk snapshots are not currently available for this VM. /// @@ -28382,6 +28391,15 @@ namespace XenAdmin { } } + /// + /// Looks up a localized string similar to Not configured. + /// + public static string NOT_CONFIGURED { + get { + return ResourceManager.GetString("NOT_CONFIGURED", resourceCulture); + } + } + /// /// Looks up a localized string similar to not contained in. /// @@ -31749,15 +31767,6 @@ namespace XenAdmin { } } - /// - /// Looks up a localized string similar to Not configured. - /// - public static string PVS_CACHE_NOT_CONFIGURED { - get { - return ResourceManager.GetString("PVS_CACHE_NOT_CONFIGURED", resourceCulture); - } - } - /// /// Looks up a localized string similar to This PVS cache storage cannot be changed because it is in use.. /// @@ -37410,6 +37419,15 @@ namespace XenAdmin { } } + /// + /// Looks up a localized string similar to Configure updates.... + /// + public static string UPDATES_GENERAL_TAB_CONFIG { + get { + return ResourceManager.GetString("UPDATES_GENERAL_TAB_CONFIG", resourceCulture); + } + } + /// /// Looks up a localized string similar to (full application required). /// @@ -37419,6 +37437,33 @@ namespace XenAdmin { } } + /// + /// Looks up a localized string similar to Last synchronized. + /// + public static string UPDATES_GENERAL_TAB_LAST_SYNCED { + get { + return ResourceManager.GetString("UPDATES_GENERAL_TAB_LAST_SYNCED", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Update channel. + /// + public static string UPDATES_GENERAL_TAB_REPO { + get { + return ResourceManager.GetString("UPDATES_GENERAL_TAB_REPO", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Synchronize Now. + /// + public static string UPDATES_GENERAL_TAB_SYNC_NOW { + get { + return ResourceManager.GetString("UPDATES_GENERAL_TAB_SYNC_NOW", resourceCulture); + } + } + /// /// Looks up a localized string similar to Automatically check for {0} updates. /// diff --git a/XenModel/Messages.resx b/XenModel/Messages.resx index 51c5ee931..e01bf00d3 100755 --- a/XenModel/Messages.resx +++ b/XenModel/Messages.resx @@ -7491,6 +7491,9 @@ This might result in failure to migrate VMs to this server during the RPU or to Incorrect old password + + Indeterminable + Disk snapshots are not currently available for this VM @@ -9858,6 +9861,9 @@ When you configure an NFS storage repository, you simply provide the host name o not attached to + + Not configured + not contained in @@ -10998,9 +11004,6 @@ Click Previous if you need to go back and specify a different network location o MemorySR - - Not configured - This PVS cache storage cannot be changed because it is in use. @@ -12924,9 +12927,21 @@ Note that if RBAC is enabled, only updates which you have privileges to dismiss Download {0} + + Configure updates... + (full application required) + + Last synchronized + + + Update channel + + + Synchronize Now + Automatically check for {0} updates