/// <summary> /// This method is called to clear the contents of an <see cref="SPBackupCatalog"/> to remove /// all backup sets it contains. This operation ensures that an spbrtoc.xml file remains with /// any remnants of restore and non-backup operations, but backup set references (and folders /// containing the sets) are deleted. /// <para>"Removal" of a backup set involves two different operations. The first operation is /// the actual updating of the table of contents file to reflect that the backup set is no /// longer present. The second part of the removal is the actual deletion of the backup /// subfolders housing the backup data being trimmed out.</para> /// </summary> /// <seealso cref="SPBackupCatalog"/> /// <seealso cref="SPCmdletRemoveSPBackupCatalog"/> /// <param name="workingCatalog">An <see cref="SPBackupCatalog"/> object that represents the /// backup set to be cleared.</param> /// <remarks>If an exception occurs, it is assumed that the calling method will trap it and /// handle it accordingly (including a possible re-throw).</remarks> public static void EmptyBackupCatalog(SPBackupCatalog workingCatalog) { // Use the XML for the backup catalog's TOC to grab all of the full backup SPHistoryObject // nodes and reverse sort them by directory number. XElement tocFile = workingCatalog.TableOfContents; String backupRoot = workingCatalog.CatalogPath; var fullHistoryObjects = tocFile.Elements().Where(ho => (ho.Element("SPBackupMethod").Value.Trim().ToUpper() == "FULL") && ho.Element("SPConfigurationOnly").Value.Trim().ToUpper() == "FALSE" && ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE").OrderByDescending(fho => Convert.ToInt32(fho.Element("SPDirectoryNumber").Value)); // We need each of the folders we'll be deleting as part of the operation. This has to be // dumped to a list because subsequent deferred evaluation (when deleting folders) wouldn't // work properly following PHASE 1 (below). var backupSetsToDelete = tocFile.Elements().Where(ho => (ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE")).Select(dho => new { ID = dho.Element("SPId").Value, Folder = Path.Combine(backupRoot, dho.Element("SPDirectoryName").Value) }).ToList(); // CLEANUP PHASE 1: clean-up the SPHistoryObject elements in the backup TOC file and save off the file tocFile.Elements().Where(ho => (ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE")).Remove(); workingCatalog.TableOfContents = tocFile; workingCatalog.SaveTableOfContents(); // CLEANUP PHASE 2: Iterate through the backup folders to delete and clear them out. foreach (var backupSet in backupSetsToDelete) { Directory.Delete(backupSet.Folder, true); } }
/// <summary> /// This method is called when the backup set pointers contained within <paramref name="workingCatalog"/> /// need to be validated and corrected to make them consistent with the current path held by /// the SPBackupCatalog. This type of correction becomes necessary when a backup catalog that /// was created at one path (for example, "\\BackupServer\Backups") is moved to another path /// (for example, "\\BackupServer\Restores") and referenced. /// <para>In this scenario, folder pointers that are internal to the backup catalog will still /// point to "\\BackupServer\Backups." Use of this method will correct those pointers to reference /// the path currently specified by the <paramref name="newCatalogPath"/>.</para> /// </summary> /// <seealso cref="SPBackupCatalog"/> /// <seealso cref="SPCmdletSetSPBackupCatalog"/> /// <param name="workingCatalog">An <see cref="SPBackupCatalog"/> object that represents the /// backup set to be parsed and path-corrected.</param> /// <param name="newCatalogPath">A <c>String</c> containing the new root path that should be used /// for modifying the backup directory paths in the <paramref name="workingCatalog"/>.</param> /// <remarks>If an exception occurs, it is assumed that the calling method will trap it and /// handle it accordingly (including a possible re-throw).</remarks> public static void UpdateBackupCatalogPaths(SPBackupCatalog workingCatalog, String newCatalogPath) { // We need the XML structure for the table of contents file. XElement tocFile = workingCatalog.TableOfContents; // Perform the modifications XElement workingToc = UpdateBackupCatalogPaths(tocFile, newCatalogPath); // Assign the updated TOC structure back to the catalog and save it off. workingCatalog.TableOfContents = workingToc; workingCatalog.SaveTableOfContents(); }
/// <summary> /// This method is called to trim the contents of an <see cref="SPBackupCatalog"/> to retain /// as many backups as possible that allow the backup catalog to remain below the retention /// size specified by the <paramref name="retainSize"/> parameter. Older full backups (and any /// differential backups that depend on those full backups) are removed from the backup set /// from oldest to newest, and the overall size of the backup set is checked with each removal. /// If the backup catalog is too big after a removal or set of removals, the process is repeated. /// <para>"Removal" of a backup set involves two different operations. The first operation is /// the actual updating of the table of contents file to reflect that the backup set is no /// longer present. The second part of the removal is the actual deletion of the backup /// subfolders housing the backup data being trimmed out. As indicated, this process is /// carried out recursively until the size constraints are met.</para> /// </summary> /// <seealso cref="SPBackupCatalog"/> /// <seealso cref="SPCmdletRemoveSPBackupCatalog"/> /// <param name="workingCatalog">An <see cref="SPBackupCatalog"/> object that represents the /// backup set to be trimmed.</param> /// <param name="retainSize">An <c>Int64</c> that contains the total maximum size of all backups /// to retain in the <paramref name="workingCatalog"/>.</param> /// <param name="ignoreErrors">TRUE if backup set errors should be ignored for purposes of /// determining what to delete, FALSE if backup sets with errors should not count against the /// number of backups retained.</param> /// <remarks>If an exception occurs, it is assumed that the calling method will trap it and /// handle it accordingly (including a possible re-throw).</remarks> public static void TrimBackupCatalogSize(SPBackupCatalog workingCatalog, Int64 retainSize, Boolean ignoreErrors) { // Use the XML for the backup catalog's TOC to grab all of the full backup SPHistoryObject // nodes and reverse sort them by directory number. var backupRoot = workingCatalog.CatalogPath; var tocFile = workingCatalog.TableOfContents; IOrderedEnumerable <XElement> fullHistoryObjects; // If we aren't ignoring errors, then we'll need to limit (potentially) the fullHistoryObjects // needed to just those backup sets that don't contain errors. if (ignoreErrors) { // All SPHistoryObjects are valid fullHistoryObjects = tocFile.Elements().Where(ho => (ho.Element("SPBackupMethod").Value.Trim().ToUpper() == "FULL") && ho.Element("SPConfigurationOnly").Value.Trim().ToUpper() == "FALSE" && ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE").OrderByDescending(fho => Convert.ToInt32(fho.Element("SPDirectoryNumber").Value)); } else { // Only SPHistoryObjects with a zero error count are valid fullHistoryObjects = tocFile.Elements().Where(ho => (ho.Element("SPBackupMethod").Value.Trim().ToUpper() == "FULL") && ho.Element("SPConfigurationOnly").Value.Trim().ToUpper() == "FALSE" && ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE" && Convert.ToInt32(ho.Element("SPErrorCount").Value) == 0).OrderByDescending(fho => Convert.ToInt32(fho.Element("SPDirectoryNumber").Value)); } // We need to enter into a repeating delete cycle until one of two conditions are met: // 1. We have only one full backup set (plus any number of differentials) left // 2. The backup catalog is equal to our below the retention size supplied while (fullHistoryObjects.Count() > 1 && workingCatalog.CatalogSize > retainSize) { // Get the ID of the next-to-the-last full backup set; i.e., the last full backup // set that will remain in the catalog after the delete. Everything below it in the // directory number sequence will be wiped. var lastDirNumber = Convert.ToInt32(fullHistoryObjects.ElementAt(fullHistoryObjects.Count() - 2).Element("SPDirectoryNumber").Value); // We need each of the folders we'll be deleting as part of the operation. This has to be // dumped to a list because subsequent deferred evaluation (when deleting folders) wouldn't // work properly following PHASE 1 (below). var backupSetsToDelete = tocFile.Elements().Where(ho => (ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE" && Convert.ToInt32(ho.Element("SPDirectoryNumber").Value) < lastDirNumber)).Select(dho => new { ID = dho.Element("SPId").Value, Folder = Path.Combine(backupRoot, dho.Element("SPDirectoryName").Value) }).ToList(); // CLEANUP PHASE 1: clean-up the SPHistoryObject elements in the backup TOC file and save off the file tocFile.Elements().Where(ho => (ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE" && Convert.ToInt32(ho.Element("SPDirectoryNumber").Value) < lastDirNumber)).Remove(); workingCatalog.TableOfContents = tocFile; workingCatalog.SaveTableOfContents(); // CLEANUP PHASE 2: Iterate through the backup folders to delete and clear them out. foreach (var backupSet in backupSetsToDelete) { Directory.Delete(backupSet.Folder, true); } // Refresh the working catalog and re-query for the needed objects. workingCatalog.Refresh(); if (ignoreErrors) { // All SPHistoryObjects are valid fullHistoryObjects = tocFile.Elements().Where(ho => (ho.Element("SPBackupMethod").Value.Trim().ToUpper() == "FULL") && ho.Element("SPConfigurationOnly").Value.Trim().ToUpper() == "FALSE" && ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE").OrderByDescending(fho => Convert.ToInt32(fho.Element("SPDirectoryNumber").Value)); } else { // Only SPHistoryObjects with a zero error count are valid fullHistoryObjects = tocFile.Elements().Where(ho => (ho.Element("SPBackupMethod").Value.Trim().ToUpper() == "FULL") && ho.Element("SPConfigurationOnly").Value.Trim().ToUpper() == "FALSE" && ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE" && Convert.ToInt32(ho.Element("SPErrorCount").Value) == 0).OrderByDescending(fho => Convert.ToInt32(fho.Element("SPDirectoryNumber").Value)); } } }
/// <summary> /// This method is called to trim the contents of an <see cref="SPBackupCatalog"/> to retain /// only a certain number of full backups. The method is responsible for ensuring that only /// the most recent number of full backups specified are retained. Older full backups (and any /// differential backups that depend on those full backups) are removed from the backup set. /// <para>"Removal" of a backup set involves two different operations. The first operation is /// the actual updating of the table of contents file to reflect that the backup set is no /// longer present. The second part of the removal is the actual deletion of the backup /// subfolders housing the backup data being trimmed out.</para> /// </summary> /// <seealso cref="SPBackupCatalog"/> /// <seealso cref="SPCmdletRemoveSPBackupCatalog"/> /// <param name="workingCatalog">An <see cref="SPBackupCatalog"/> object that represents the /// backup set to be trimmed.</param> /// <param name="retainCount">An <c>Int32</c> that contains the total number of full backups /// to retain in the <paramref name="workingCatalog"/>.</param> /// <param name="ignoreErrors">TRUE if backup set errors should be ignored for purposes of /// determining what to delete, FALSE if backup sets with errors should not count against the /// number of backups retained.</param> /// <remarks>If an exception occurs, it is assumed that the calling method will trap it and /// handle it accordingly (including a possible re-throw).</remarks> public static void TrimBackupCatalogCount(SPBackupCatalog workingCatalog, Int32 retainCount, Boolean ignoreErrors) { // Use the XML for the backup catalog's TOC to grab all of the full backup SPHistoryObject // nodes and reverse sort them by directory number. var tocFile = workingCatalog.TableOfContents; IOrderedEnumerable <XElement> fullHistoryObjects; // If we aren't ignoring errors, then we'll need to limit (potentially) the fullHistoryObjects // needed to just those backup sets that don't contain errors. if (ignoreErrors) { // All SPHistoryObjects are valid fullHistoryObjects = tocFile.Elements().Where(ho => (ho.Element("SPBackupMethod").Value.Trim().ToUpper() == "FULL") && ho.Element("SPConfigurationOnly").Value.Trim().ToUpper() == "FALSE" && ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE").OrderByDescending(fho => Convert.ToInt32(fho.Element("SPDirectoryNumber").Value)); } else { // Only SPHistoryObjects with a zero error count are valid fullHistoryObjects = tocFile.Elements().Where(ho => (ho.Element("SPBackupMethod").Value.Trim().ToUpper() == "FULL") && ho.Element("SPConfigurationOnly").Value.Trim().ToUpper() == "FALSE" && ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE" && Convert.ToInt32(ho.Element("SPErrorCount").Value) == 0).OrderByDescending(fho => Convert.ToInt32(fho.Element("SPDirectoryNumber").Value)); } // If we've got more full history objects than our count, then it's time to do some trimming. if (fullHistoryObjects.Count() > retainCount) { // We now have all of the SPHistoryObject nodes that correspond to full backups, and they // are sorted from most recent to oldest. We want to keep the most recent ones (number // specified by retainCount) and dump everything else. // // Get the directory number of the last full backup we want to keep; anything below that // will be deleted. We'll also get a quick reference to the backup root folder. Int32 lastDirNumber = Convert.ToInt32(fullHistoryObjects.ElementAt(retainCount - 1).Element("SPDirectoryNumber").Value); String backupRoot = workingCatalog.CatalogPath; // We need each of the folders we'll be deleting as part of the operation. This has to be // dumped to a list because subsequent deferred evaluation (when deleting folders) wouldn't // work properly following PHASE 1 (below). var backupSetsToDelete = tocFile.Elements().Where(ho => (ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE" && Convert.ToInt32(ho.Element("SPDirectoryNumber").Value) < lastDirNumber)).Select(dho => new { ID = dho.Element("SPId").Value, Folder = Path.Combine(backupRoot, dho.Element("SPDirectoryName").Value) }).ToList(); // CLEANUP PHASE 1: clean-up the SPHistoryObject elements in the backup TOC file and save off the file tocFile.Elements().Where(ho => (ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE" && Convert.ToInt32(ho.Element("SPDirectoryNumber").Value) < lastDirNumber)).Remove(); workingCatalog.TableOfContents = tocFile; workingCatalog.SaveTableOfContents(); // CLEANUP PHASE 2: Iterate through the backup folders to delete and clear them out. foreach (var backupSet in backupSetsToDelete) { Directory.Delete(backupSet.Folder, true); } } }