123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- /* Copyright (C) 2014 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
- *
- * You can redistribute this program and/or modify it under the terms of
- * the GNU Lesser Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- */
- using System;
- using System.Collections.Generic;
- using System.Text;
- namespace DiskAccessLibrary.LogicalDiskManager
- {
- public class VolumeManagerDatabaseHelper
- {
- public static void ConvertRaidToStripedVolume(DiskGroupDatabase database, Guid volumeGuid)
- {
- List<DatabaseRecord> records = new List<DatabaseRecord>();
- VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volumeGuid);
- volumeRecord = (VolumeRecord)volumeRecord.Clone();
- volumeRecord.VolumeTypeString = "gen";
- volumeRecord.ReadPolicy = ReadPolicyName.Select;
- volumeRecord.VolumeFlags = VolumeFlags.DefaultUnknown | VolumeFlags.Writeback;
- records.Add(volumeRecord);
- ComponentRecord componentRecord = database.FindComponentsByVolumeID(volumeRecord.VolumeId)[0];
- componentRecord = (ComponentRecord)componentRecord.Clone();
- componentRecord.ExtentLayout = ExtentLayoutName.Stripe;
- records.Add(componentRecord);
- database.UpdateDatabase(records);
- }
- public static void ConvertStripedVolumeToRaid(DiskGroupDatabase database, Guid volumeGuid)
- {
- List<DatabaseRecord> records = new List<DatabaseRecord>();
- VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volumeGuid);
- volumeRecord = (VolumeRecord)volumeRecord.Clone();
- volumeRecord.VolumeTypeString = "raid5";
- volumeRecord.ReadPolicy = ReadPolicyName.RAID;
- volumeRecord.VolumeFlags = VolumeFlags.DefaultUnknown | VolumeFlags.Writeback | VolumeFlags.Writecopy;
- records.Add(volumeRecord);
- ComponentRecord componentRecord = database.FindComponentsByVolumeID(volumeRecord.VolumeId)[0];
- componentRecord = (ComponentRecord)componentRecord.Clone();
- componentRecord.ExtentLayout = ExtentLayoutName.RAID5;
- records.Add(componentRecord);
- database.UpdateDatabase(records);
- }
- /// <summary>
- /// Update the database (add the new extent)
- /// </summary>
- /// <param name="volume">RAID-5 or striped volume</param>
- /// <returns>new extent ID</returns>
- public static ulong AddNewExtentToVolume(DiskGroupDatabase database, DynamicVolume volume, DiskExtent newExtent)
- {
- PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(newExtent.Disk);
- List<DatabaseRecord> records = new List<DatabaseRecord>();
- VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid);
- volumeRecord = (VolumeRecord)volumeRecord.Clone();
- volumeRecord.SizeLBA += (ulong)newExtent.TotalSectors;
- records.Add(volumeRecord);
- ComponentRecord componentRecord = database.FindComponentsByVolumeID(volumeRecord.VolumeId)[0];
- componentRecord = (ComponentRecord)componentRecord.Clone();
- componentRecord.NumberOfExtents++;
- componentRecord.NumberOfColumns++;
- records.Add(componentRecord);
- DiskRecord diskRecord = database.FindDiskByDiskGuid(privateHeader.DiskGuid);
- diskRecord = (DiskRecord)diskRecord.Clone();
- records.Add(diskRecord);
- ExtentRecord newExtentRecord = new ExtentRecord();
- newExtentRecord.Name = GetNextExtentName(database.ExtentRecords, diskRecord.Name);
- newExtentRecord.ComponentId = componentRecord.ComponentId;
- newExtentRecord.DiskId = diskRecord.DiskId;
- newExtentRecord.DiskOffsetLBA = (ulong)newExtent.FirstSector - privateHeader.PublicRegionStartLBA;
- newExtentRecord.SizeLBA = (ulong)newExtent.TotalSectors;
- newExtentRecord.HasColumnIndexFlag = true;
- newExtentRecord.ColumnIndex = (uint)volume.Columns.Count; // zero based
-
- records.Add(newExtentRecord);
- // we should update the disk records and extent records
- foreach (DynamicDiskExtent extent in volume.Extents)
- {
- ExtentRecord extentRecord = database.FindExtentByExtentID(extent.ExtentID);
- extentRecord = (ExtentRecord)extentRecord.Clone();
- records.Add(extentRecord);
- diskRecord = database.FindDiskByDiskID(extentRecord.DiskId);
- // there could be multiple extents on the same disk, make sure we only add each disk once
- if (!records.Contains(diskRecord))
- {
- diskRecord = (DiskRecord)diskRecord.Clone();
- records.Add(diskRecord);
- }
- }
- database.UpdateDatabase(records);
- return newExtentRecord.ExtentId;
- }
- /// <summary>
- /// Update the database to point to the new extent location (same or different disk)
- /// </summary>
- public static void UpdateExtentLocation(DiskGroupDatabase database, DynamicVolume volume, DynamicDiskExtent relocatedExtent)
- {
- PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(relocatedExtent.Disk);
- DiskRecord targetDiskRecord = database.FindDiskByDiskGuid(privateHeader.DiskGuid);
- VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid);
- List<DatabaseRecord> records = new List<DatabaseRecord>();
- ExtentRecord sourceExtentRecord = database.FindExtentByExtentID(relocatedExtent.ExtentID);
- ExtentRecord relocatedExtentRecord = (ExtentRecord)sourceExtentRecord.Clone();
- relocatedExtentRecord.DiskId = targetDiskRecord.DiskId;
- relocatedExtentRecord.DiskOffsetLBA = (ulong)relocatedExtent.FirstSector - privateHeader.PublicRegionStartLBA;
- records.Add(relocatedExtentRecord);
- // we should update the disk records
- foreach (DynamicDiskExtent extent in volume.Extents)
- {
- DiskRecord diskRecord = database.FindDiskByDiskID(relocatedExtentRecord.DiskId);
- // there could be multiple extents on the same disk, make sure we only add each disk once
- if (!records.Contains(diskRecord))
- {
- diskRecord = (DiskRecord)diskRecord.Clone();
- records.Add(diskRecord);
- }
- }
- // when moving to a new disk, we should update the new disk record as well
- if (!records.Contains(targetDiskRecord))
- {
- records.Add(targetDiskRecord.Clone());
- }
- database.UpdateDatabase(records);
- }
- public static void ExtendSimpleVolume(DiskGroupDatabase database, SimpleVolume volume, long additionalNumberOfSectors)
- {
- VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid);
- volumeRecord = (VolumeRecord)volumeRecord.Clone();
- volumeRecord.SizeLBA += (ulong)additionalNumberOfSectors;
- ExtentRecord extentRecord = database.FindExtentByExtentID(volume.DiskExtent.ExtentID);
- extentRecord = (ExtentRecord)extentRecord.Clone();
- extentRecord.SizeLBA += (ulong)additionalNumberOfSectors;
- DiskRecord diskRecord = database.FindDiskByDiskID(extentRecord.DiskId); // we should update the disk, see Database.cs
- diskRecord = (DiskRecord)diskRecord.Clone();
- List<DatabaseRecord> records = new List<DatabaseRecord>();
- records.Add(volumeRecord);
- records.Add(extentRecord);
- records.Add(diskRecord);
- database.UpdateDatabase(records);
- }
- public static void ExtendStripedVolume(DiskGroupDatabase database, StripedVolume volume, long additionalNumberOfExtentSectors)
- {
- if (additionalNumberOfExtentSectors % volume.SectorsPerStripe > 0)
- {
- throw new ArgumentException("Number of additional sectors must be multiple of stripes per sector");
- }
- List<DatabaseRecord> records = new List<DatabaseRecord>();
- VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid);
- volumeRecord = (VolumeRecord)volumeRecord.Clone();
- volumeRecord.SizeLBA += (ulong)(additionalNumberOfExtentSectors * volume.NumberOfColumns);
- records.Add(volumeRecord);
- // we only want to extend the last extent in each column
- foreach (DynamicColumn column in volume.Columns)
- {
- DynamicDiskExtent lastExtent = column.Extents[column.Extents.Count - 1];
- ExtentRecord extentRecord = database.FindExtentByExtentID(lastExtent.ExtentID);
- extentRecord = (ExtentRecord)extentRecord.Clone();
- extentRecord.SizeLBA += (ulong)additionalNumberOfExtentSectors;
- records.Add(extentRecord);
- DiskRecord diskRecord = database.FindDiskByDiskID(extentRecord.DiskId); // we should update the disk, see Database.cs
- diskRecord = (DiskRecord)diskRecord.Clone();
- records.Add(diskRecord);
- }
- database.UpdateDatabase(records);
- }
- public static void ExtendRAID5Volume(DiskGroupDatabase database, Raid5Volume volume, long additionalNumberOfExtentSectors)
- {
- if (additionalNumberOfExtentSectors % volume.SectorsPerStripe > 0)
- {
- throw new ArgumentException("Number of additional sectors must be multiple of stripes per sector");
- }
- List<DatabaseRecord> records = new List<DatabaseRecord>();
- VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid);
- volumeRecord = (VolumeRecord)volumeRecord.Clone();
- volumeRecord.SizeLBA += (ulong)(additionalNumberOfExtentSectors * (volume.NumberOfColumns - 1));
- records.Add(volumeRecord);
- foreach (DynamicColumn column in volume.Columns)
- {
- DynamicDiskExtent lastExtent = column.Extents[column.Extents.Count - 1];
- ExtentRecord extentRecord = database.FindExtentByExtentID(lastExtent.ExtentID);
- extentRecord = (ExtentRecord)extentRecord.Clone();
- extentRecord.SizeLBA += (ulong)additionalNumberOfExtentSectors;
- records.Add(extentRecord);
- DiskRecord diskRecord = database.FindDiskByDiskID(extentRecord.DiskId); // we should update the disk, see Database.cs
- diskRecord = (DiskRecord)diskRecord.Clone();
- records.Add(diskRecord);
- }
- database.UpdateDatabase(records);
- }
-
- public static ulong CreateSimpleVolume(DiskGroupDatabase database, DiskExtent extent)
- {
- List<DatabaseRecord> records = new List<DatabaseRecord>();
- VolumeRecord volumeRecord = new VolumeRecord();
- volumeRecord.Id = database.AllocateNewRecordID();
- volumeRecord.Name = GetNextSimpleVolumeName(database.VolumeRecords);
- volumeRecord.VolumeTypeString = "gen";
- volumeRecord.StateString = "ACTIVE";
- volumeRecord.ReadPolicy = ReadPolicyName.Select;
- volumeRecord.VolumeNumber = GetNextVolumeNumber(database.VolumeRecords);
- volumeRecord.VolumeFlags = VolumeFlags.Writeback | VolumeFlags.DefaultUnknown;
- volumeRecord.NumberOfComponents = 1;
- volumeRecord.SizeLBA = (ulong)(extent.TotalSectors);
- volumeRecord.PartitionType = PartitionType.RAW;
- volumeRecord.VolumeGuid = Guid.NewGuid();
- records.Add(volumeRecord);
- ComponentRecord componentRecord = new ComponentRecord();
- componentRecord.Id = database.AllocateNewRecordID();
- componentRecord.Name = volumeRecord.Name + "-01";
- componentRecord.StateString = "ACTIVE";
- componentRecord.ExtentLayout = ExtentLayoutName.Concatenated;
- componentRecord.NumberOfExtents = 1;
- componentRecord.VolumeId = volumeRecord.VolumeId;
- componentRecord.HasStripedExtentsFlag = false;
- componentRecord.NumberOfColumns = 0;
- records.Add(componentRecord);
- // we should update the disk record
- PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(extent.Disk);
- DiskRecord diskRecord = database.FindDiskByDiskGuid(privateHeader.DiskGuid);
- diskRecord = (DiskRecord)diskRecord.Clone();
- records.Add(diskRecord);
- ExtentRecord extentRecord = new ExtentRecord();
- extentRecord.Name = GetNextExtentName(database.ExtentRecords, diskRecord.Name);
- extentRecord.DiskOffsetLBA = (ulong)extent.FirstSector - privateHeader.PublicRegionStartLBA;
- extentRecord.SizeLBA = (ulong)extent.TotalSectors;
- extentRecord.ComponentId = componentRecord.ComponentId;
- extentRecord.DiskId = diskRecord.DiskId;
- extentRecord.HasColumnIndexFlag = false;
- records.Add(extentRecord);
- database.UpdateDatabase(records);
- return volumeRecord.VolumeId;
- }
- public static ulong CreateRAID5Volume(DiskGroupDatabase database, List<DiskExtent> extents, bool isDegraded)
- {
- int numberOfColumns;
- if (isDegraded)
- {
- numberOfColumns = extents.Count + 1;
- }
- else
- {
- numberOfColumns = extents.Count;
- }
- List<DatabaseRecord> records = new List<DatabaseRecord>();
-
- VolumeRecord volumeRecord = new VolumeRecord();
- volumeRecord.Id = database.AllocateNewRecordID();
- volumeRecord.Name = GetNextRAIDVolumeName(database.VolumeRecords);
- volumeRecord.VolumeTypeString = "raid5";
- volumeRecord.StateString = "ACTIVE";
- volumeRecord.ReadPolicy = ReadPolicyName.RAID;
- volumeRecord.VolumeNumber = GetNextVolumeNumber(database.VolumeRecords);
- volumeRecord.VolumeFlags = VolumeFlags.Writeback | VolumeFlags.Writecopy | VolumeFlags.DefaultUnknown;
- volumeRecord.NumberOfComponents = 1;
- volumeRecord.SizeLBA = (ulong)(extents[0].TotalSectors * (numberOfColumns - 1));
- volumeRecord.PartitionType = PartitionType.RAW;
- volumeRecord.VolumeGuid = Guid.NewGuid();
- records.Add(volumeRecord);
- ComponentRecord componentRecord = new ComponentRecord();
- componentRecord.Id = database.AllocateNewRecordID();
- componentRecord.Name = volumeRecord.Name + "-01";
- componentRecord.StateString = "ACTIVE";
- componentRecord.ExtentLayout = ExtentLayoutName.RAID5;
- componentRecord.NumberOfExtents = (uint)numberOfColumns;
- componentRecord.VolumeId = volumeRecord.VolumeId;
- componentRecord.HasStripedExtentsFlag = true;
- componentRecord.StripeSizeLBA = 128; // 64KB - the default
- componentRecord.NumberOfColumns = (uint)numberOfColumns;
- records.Add(componentRecord);
- for(int index = 0; index < extents.Count; index++)
- {
- DiskExtent extent = extents[index];
- // we should update the disk records
- PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(extent.Disk);
- DiskRecord diskRecord = database.FindDiskByDiskGuid(privateHeader.DiskGuid);
- diskRecord = (DiskRecord)diskRecord.Clone();
- records.Add(diskRecord);
- ExtentRecord extentRecord = new ExtentRecord();
- extentRecord.Name = GetNextExtentName(database.ExtentRecords, diskRecord.Name);
- extentRecord.DiskOffsetLBA = (ulong)extent.FirstSector - privateHeader.PublicRegionStartLBA;
- extentRecord.SizeLBA = (ulong)extent.TotalSectors;
- extentRecord.ComponentId = componentRecord.ComponentId;
- extentRecord.DiskId = diskRecord.DiskId;
-
- extentRecord.HasColumnIndexFlag = (index > 0);
- extentRecord.ColumnIndex = (uint)index; // zero based
- records.Add(extentRecord);
- }
- if (isDegraded)
- {
- // we have to make-up a disk
- // The DiskFlags and ExtentFlags are not necessary (they will be added later anyway when the disk group is reimported)
- DiskRecord diskRecord = new DiskRecord();
- diskRecord.Id = database.AllocateNewRecordID();
- diskRecord.Name = "Miss" + new Random().Next(100);
- diskRecord.DiskGuid = Guid.NewGuid();
- diskRecord.DiskFlags = DiskFlags.Detached;
- records.Add(diskRecord);
- ExtentRecord extentRecord = new ExtentRecord();
- extentRecord.Name = diskRecord.Name + "-01";
- extentRecord.ExtentFlags = ExtentFlags.Recover;
- extentRecord.SizeLBA = (ulong)extents[0].TotalSectors;
- extentRecord.ComponentId = componentRecord.ComponentId;
- extentRecord.DiskId = diskRecord.DiskId;
- extentRecord.HasColumnIndexFlag = true;
- extentRecord.ColumnIndex = (uint)extents.Count; // zero based
- records.Add(extentRecord);
- }
- database.UpdateDatabase(records);
- return volumeRecord.VolumeId;
- }
- private static string GetNextSimpleVolumeName(List<VolumeRecord> volumeRecords)
- {
- return GetNextVolumeName(volumeRecords, "Volume");
- }
- private static string GetNextSpannedVolumeName(List<VolumeRecord> volumeRecords)
- {
- return GetNextVolumeName(volumeRecords, "Volume");
- }
- private static string GetNextStripedVolumeName(List<VolumeRecord> volumeRecords)
- {
- return GetNextVolumeName(volumeRecords, "Stripe");
- }
- private static string GetNextRAIDVolumeName(List<VolumeRecord> volumeRecords)
- {
- return GetNextVolumeName(volumeRecords, "Raid");
- }
- private static string GetNextVolumeName(List<VolumeRecord> volumeRecords, string prefix)
- {
- int index = 1;
- while (true)
- {
- string name = prefix + index.ToString();
- bool isNameAvailable = true;
- foreach (VolumeRecord volumeRecord in volumeRecords)
- {
- if (volumeRecord.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
- {
- isNameAvailable = false;
- break;
- }
- }
- if (isNameAvailable)
- {
- return name;
- }
- index++;
- }
- }
- /// <param name="extentRecords">Could be all of the database records or just the relevant records</param>
- public static string GetNextExtentName(List<ExtentRecord> extentRecords, string diskName)
- {
- int index = 1;
- while (true)
- {
- string name = String.Format("{0}-{1}", diskName, index.ToString("00"));
- bool isNameAvailable = true;
- foreach (ExtentRecord extentRecord in extentRecords)
- {
- if (extentRecord.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
- {
- isNameAvailable = false;
- break;
- }
- }
- if (isNameAvailable)
- {
- return name;
- }
- index++;
- }
- }
- public static uint GetNextVolumeNumber(List<VolumeRecord> volumeRecords)
- {
- // volume number starts with 5 (probably because 1-4 are reserved for partitions)
- uint volumeNumber = 5;
- foreach (VolumeRecord record in volumeRecords)
- {
- if (record.VolumeNumber >= volumeNumber)
- {
- volumeNumber = record.VolumeNumber + 1;
- }
- }
- return volumeNumber;
- }
- }
- }
|