VolumeManagerDatabaseHelper.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. /* Copyright (C) 2014 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
  2. *
  3. * You can redistribute this program and/or modify it under the terms of
  4. * the GNU Lesser Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Text;
  10. namespace DiskAccessLibrary.LogicalDiskManager
  11. {
  12. public class VolumeManagerDatabaseHelper
  13. {
  14. public static void ConvertRaidToStripedVolume(DiskGroupDatabase database, Guid volumeGuid)
  15. {
  16. List<DatabaseRecord> records = new List<DatabaseRecord>();
  17. VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volumeGuid);
  18. volumeRecord = (VolumeRecord)volumeRecord.Clone();
  19. volumeRecord.VolumeTypeString = "gen";
  20. volumeRecord.ReadPolicy = ReadPolicyName.Select;
  21. volumeRecord.VolumeFlags = VolumeFlags.DefaultUnknown | VolumeFlags.Writeback;
  22. records.Add(volumeRecord);
  23. ComponentRecord componentRecord = database.FindComponentsByVolumeID(volumeRecord.VolumeId)[0];
  24. componentRecord = (ComponentRecord)componentRecord.Clone();
  25. componentRecord.ExtentLayout = ExtentLayoutName.Stripe;
  26. records.Add(componentRecord);
  27. database.UpdateDatabase(records);
  28. }
  29. public static void ConvertStripedVolumeToRaid(DiskGroupDatabase database, Guid volumeGuid)
  30. {
  31. List<DatabaseRecord> records = new List<DatabaseRecord>();
  32. VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volumeGuid);
  33. volumeRecord = (VolumeRecord)volumeRecord.Clone();
  34. volumeRecord.VolumeTypeString = "raid5";
  35. volumeRecord.ReadPolicy = ReadPolicyName.RAID;
  36. volumeRecord.VolumeFlags = VolumeFlags.DefaultUnknown | VolumeFlags.Writeback | VolumeFlags.Writecopy;
  37. records.Add(volumeRecord);
  38. ComponentRecord componentRecord = database.FindComponentsByVolumeID(volumeRecord.VolumeId)[0];
  39. componentRecord = (ComponentRecord)componentRecord.Clone();
  40. componentRecord.ExtentLayout = ExtentLayoutName.RAID5;
  41. records.Add(componentRecord);
  42. database.UpdateDatabase(records);
  43. }
  44. /// <summary>
  45. /// Update the database (add the new extent)
  46. /// </summary>
  47. /// <param name="volume">RAID-5 or striped volume</param>
  48. /// <returns>new extent ID</returns>
  49. public static ulong AddNewExtentToVolume(DiskGroupDatabase database, DynamicVolume volume, DiskExtent newExtent)
  50. {
  51. PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(newExtent.Disk);
  52. List<DatabaseRecord> records = new List<DatabaseRecord>();
  53. VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid);
  54. volumeRecord = (VolumeRecord)volumeRecord.Clone();
  55. volumeRecord.SizeLBA += (ulong)newExtent.TotalSectors;
  56. records.Add(volumeRecord);
  57. ComponentRecord componentRecord = database.FindComponentsByVolumeID(volumeRecord.VolumeId)[0];
  58. componentRecord = (ComponentRecord)componentRecord.Clone();
  59. componentRecord.NumberOfExtents++;
  60. componentRecord.NumberOfColumns++;
  61. records.Add(componentRecord);
  62. DiskRecord diskRecord = database.FindDiskByDiskGuid(privateHeader.DiskGuid);
  63. diskRecord = (DiskRecord)diskRecord.Clone();
  64. records.Add(diskRecord);
  65. ExtentRecord newExtentRecord = new ExtentRecord();
  66. newExtentRecord.Name = GetNextExtentName(database.ExtentRecords, diskRecord.Name);
  67. newExtentRecord.ComponentId = componentRecord.ComponentId;
  68. newExtentRecord.DiskId = diskRecord.DiskId;
  69. newExtentRecord.DiskOffsetLBA = (ulong)newExtent.FirstSector - privateHeader.PublicRegionStartLBA;
  70. newExtentRecord.SizeLBA = (ulong)newExtent.TotalSectors;
  71. newExtentRecord.HasColumnIndexFlag = true;
  72. newExtentRecord.ColumnIndex = (uint)volume.Columns.Count; // zero based
  73. records.Add(newExtentRecord);
  74. // we should update the disk records and extent records
  75. foreach (DynamicDiskExtent extent in volume.Extents)
  76. {
  77. ExtentRecord extentRecord = database.FindExtentByExtentID(extent.ExtentID);
  78. extentRecord = (ExtentRecord)extentRecord.Clone();
  79. records.Add(extentRecord);
  80. diskRecord = database.FindDiskByDiskID(extentRecord.DiskId);
  81. // there could be multiple extents on the same disk, make sure we only add each disk once
  82. if (!records.Contains(diskRecord))
  83. {
  84. diskRecord = (DiskRecord)diskRecord.Clone();
  85. records.Add(diskRecord);
  86. }
  87. }
  88. database.UpdateDatabase(records);
  89. return newExtentRecord.ExtentId;
  90. }
  91. /// <summary>
  92. /// Update the database to point to the new extent location (same or different disk)
  93. /// </summary>
  94. public static void UpdateExtentLocation(DiskGroupDatabase database, DynamicVolume volume, DynamicDiskExtent relocatedExtent)
  95. {
  96. PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(relocatedExtent.Disk);
  97. DiskRecord targetDiskRecord = database.FindDiskByDiskGuid(privateHeader.DiskGuid);
  98. VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid);
  99. List<DatabaseRecord> records = new List<DatabaseRecord>();
  100. ExtentRecord sourceExtentRecord = database.FindExtentByExtentID(relocatedExtent.ExtentID);
  101. ExtentRecord relocatedExtentRecord = (ExtentRecord)sourceExtentRecord.Clone();
  102. relocatedExtentRecord.DiskId = targetDiskRecord.DiskId;
  103. relocatedExtentRecord.DiskOffsetLBA = (ulong)relocatedExtent.FirstSector - privateHeader.PublicRegionStartLBA;
  104. records.Add(relocatedExtentRecord);
  105. // we should update the disk records
  106. foreach (DynamicDiskExtent extent in volume.Extents)
  107. {
  108. DiskRecord diskRecord = database.FindDiskByDiskID(relocatedExtentRecord.DiskId);
  109. // there could be multiple extents on the same disk, make sure we only add each disk once
  110. if (!records.Contains(diskRecord))
  111. {
  112. diskRecord = (DiskRecord)diskRecord.Clone();
  113. records.Add(diskRecord);
  114. }
  115. }
  116. // when moving to a new disk, we should update the new disk record as well
  117. if (!records.Contains(targetDiskRecord))
  118. {
  119. records.Add(targetDiskRecord.Clone());
  120. }
  121. database.UpdateDatabase(records);
  122. }
  123. public static void ExtendSimpleVolume(DiskGroupDatabase database, SimpleVolume volume, long additionalNumberOfSectors)
  124. {
  125. VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid);
  126. volumeRecord = (VolumeRecord)volumeRecord.Clone();
  127. volumeRecord.SizeLBA += (ulong)additionalNumberOfSectors;
  128. ExtentRecord extentRecord = database.FindExtentByExtentID(volume.DiskExtent.ExtentID);
  129. extentRecord = (ExtentRecord)extentRecord.Clone();
  130. extentRecord.SizeLBA += (ulong)additionalNumberOfSectors;
  131. DiskRecord diskRecord = database.FindDiskByDiskID(extentRecord.DiskId); // we should update the disk, see Database.cs
  132. diskRecord = (DiskRecord)diskRecord.Clone();
  133. List<DatabaseRecord> records = new List<DatabaseRecord>();
  134. records.Add(volumeRecord);
  135. records.Add(extentRecord);
  136. records.Add(diskRecord);
  137. database.UpdateDatabase(records);
  138. }
  139. public static void ExtendStripedVolume(DiskGroupDatabase database, StripedVolume volume, long additionalNumberOfExtentSectors)
  140. {
  141. if (additionalNumberOfExtentSectors % volume.SectorsPerStripe > 0)
  142. {
  143. throw new ArgumentException("Number of additional sectors must be multiple of stripes per sector");
  144. }
  145. List<DatabaseRecord> records = new List<DatabaseRecord>();
  146. VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid);
  147. volumeRecord = (VolumeRecord)volumeRecord.Clone();
  148. volumeRecord.SizeLBA += (ulong)(additionalNumberOfExtentSectors * volume.NumberOfColumns);
  149. records.Add(volumeRecord);
  150. // we only want to extend the last extent in each column
  151. foreach (DynamicColumn column in volume.Columns)
  152. {
  153. DynamicDiskExtent lastExtent = column.Extents[column.Extents.Count - 1];
  154. ExtentRecord extentRecord = database.FindExtentByExtentID(lastExtent.ExtentID);
  155. extentRecord = (ExtentRecord)extentRecord.Clone();
  156. extentRecord.SizeLBA += (ulong)additionalNumberOfExtentSectors;
  157. records.Add(extentRecord);
  158. DiskRecord diskRecord = database.FindDiskByDiskID(extentRecord.DiskId); // we should update the disk, see Database.cs
  159. diskRecord = (DiskRecord)diskRecord.Clone();
  160. records.Add(diskRecord);
  161. }
  162. database.UpdateDatabase(records);
  163. }
  164. public static void ExtendRAID5Volume(DiskGroupDatabase database, Raid5Volume volume, long additionalNumberOfExtentSectors)
  165. {
  166. if (additionalNumberOfExtentSectors % volume.SectorsPerStripe > 0)
  167. {
  168. throw new ArgumentException("Number of additional sectors must be multiple of stripes per sector");
  169. }
  170. List<DatabaseRecord> records = new List<DatabaseRecord>();
  171. VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid);
  172. volumeRecord = (VolumeRecord)volumeRecord.Clone();
  173. volumeRecord.SizeLBA += (ulong)(additionalNumberOfExtentSectors * (volume.NumberOfColumns - 1));
  174. records.Add(volumeRecord);
  175. foreach (DynamicColumn column in volume.Columns)
  176. {
  177. DynamicDiskExtent lastExtent = column.Extents[column.Extents.Count - 1];
  178. ExtentRecord extentRecord = database.FindExtentByExtentID(lastExtent.ExtentID);
  179. extentRecord = (ExtentRecord)extentRecord.Clone();
  180. extentRecord.SizeLBA += (ulong)additionalNumberOfExtentSectors;
  181. records.Add(extentRecord);
  182. DiskRecord diskRecord = database.FindDiskByDiskID(extentRecord.DiskId); // we should update the disk, see Database.cs
  183. diskRecord = (DiskRecord)diskRecord.Clone();
  184. records.Add(diskRecord);
  185. }
  186. database.UpdateDatabase(records);
  187. }
  188. public static ulong CreateSimpleVolume(DiskGroupDatabase database, DiskExtent extent)
  189. {
  190. List<DatabaseRecord> records = new List<DatabaseRecord>();
  191. VolumeRecord volumeRecord = new VolumeRecord();
  192. volumeRecord.Id = database.AllocateNewRecordID();
  193. volumeRecord.Name = GetNextSimpleVolumeName(database.VolumeRecords);
  194. volumeRecord.VolumeTypeString = "gen";
  195. volumeRecord.StateString = "ACTIVE";
  196. volumeRecord.ReadPolicy = ReadPolicyName.Select;
  197. volumeRecord.VolumeNumber = GetNextVolumeNumber(database.VolumeRecords);
  198. volumeRecord.VolumeFlags = VolumeFlags.Writeback | VolumeFlags.DefaultUnknown;
  199. volumeRecord.NumberOfComponents = 1;
  200. volumeRecord.SizeLBA = (ulong)(extent.TotalSectors);
  201. volumeRecord.PartitionType = PartitionType.RAW;
  202. volumeRecord.VolumeGuid = Guid.NewGuid();
  203. records.Add(volumeRecord);
  204. ComponentRecord componentRecord = new ComponentRecord();
  205. componentRecord.Id = database.AllocateNewRecordID();
  206. componentRecord.Name = volumeRecord.Name + "-01";
  207. componentRecord.StateString = "ACTIVE";
  208. componentRecord.ExtentLayout = ExtentLayoutName.Concatenated;
  209. componentRecord.NumberOfExtents = 1;
  210. componentRecord.VolumeId = volumeRecord.VolumeId;
  211. componentRecord.HasStripedExtentsFlag = false;
  212. componentRecord.NumberOfColumns = 0;
  213. records.Add(componentRecord);
  214. // we should update the disk record
  215. PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(extent.Disk);
  216. DiskRecord diskRecord = database.FindDiskByDiskGuid(privateHeader.DiskGuid);
  217. diskRecord = (DiskRecord)diskRecord.Clone();
  218. records.Add(diskRecord);
  219. ExtentRecord extentRecord = new ExtentRecord();
  220. extentRecord.Name = GetNextExtentName(database.ExtentRecords, diskRecord.Name);
  221. extentRecord.DiskOffsetLBA = (ulong)extent.FirstSector - privateHeader.PublicRegionStartLBA;
  222. extentRecord.SizeLBA = (ulong)extent.TotalSectors;
  223. extentRecord.ComponentId = componentRecord.ComponentId;
  224. extentRecord.DiskId = diskRecord.DiskId;
  225. extentRecord.HasColumnIndexFlag = false;
  226. records.Add(extentRecord);
  227. database.UpdateDatabase(records);
  228. return volumeRecord.VolumeId;
  229. }
  230. public static ulong CreateRAID5Volume(DiskGroupDatabase database, List<DiskExtent> extents, bool isDegraded)
  231. {
  232. int numberOfColumns;
  233. if (isDegraded)
  234. {
  235. numberOfColumns = extents.Count + 1;
  236. }
  237. else
  238. {
  239. numberOfColumns = extents.Count;
  240. }
  241. List<DatabaseRecord> records = new List<DatabaseRecord>();
  242. VolumeRecord volumeRecord = new VolumeRecord();
  243. volumeRecord.Id = database.AllocateNewRecordID();
  244. volumeRecord.Name = GetNextRAIDVolumeName(database.VolumeRecords);
  245. volumeRecord.VolumeTypeString = "raid5";
  246. volumeRecord.StateString = "ACTIVE";
  247. volumeRecord.ReadPolicy = ReadPolicyName.RAID;
  248. volumeRecord.VolumeNumber = GetNextVolumeNumber(database.VolumeRecords);
  249. volumeRecord.VolumeFlags = VolumeFlags.Writeback | VolumeFlags.Writecopy | VolumeFlags.DefaultUnknown;
  250. volumeRecord.NumberOfComponents = 1;
  251. volumeRecord.SizeLBA = (ulong)(extents[0].TotalSectors * (numberOfColumns - 1));
  252. volumeRecord.PartitionType = PartitionType.RAW;
  253. volumeRecord.VolumeGuid = Guid.NewGuid();
  254. records.Add(volumeRecord);
  255. ComponentRecord componentRecord = new ComponentRecord();
  256. componentRecord.Id = database.AllocateNewRecordID();
  257. componentRecord.Name = volumeRecord.Name + "-01";
  258. componentRecord.StateString = "ACTIVE";
  259. componentRecord.ExtentLayout = ExtentLayoutName.RAID5;
  260. componentRecord.NumberOfExtents = (uint)numberOfColumns;
  261. componentRecord.VolumeId = volumeRecord.VolumeId;
  262. componentRecord.HasStripedExtentsFlag = true;
  263. componentRecord.StripeSizeLBA = 128; // 64KB - the default
  264. componentRecord.NumberOfColumns = (uint)numberOfColumns;
  265. records.Add(componentRecord);
  266. for(int index = 0; index < extents.Count; index++)
  267. {
  268. DiskExtent extent = extents[index];
  269. // we should update the disk records
  270. PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(extent.Disk);
  271. DiskRecord diskRecord = database.FindDiskByDiskGuid(privateHeader.DiskGuid);
  272. diskRecord = (DiskRecord)diskRecord.Clone();
  273. records.Add(diskRecord);
  274. ExtentRecord extentRecord = new ExtentRecord();
  275. extentRecord.Name = GetNextExtentName(database.ExtentRecords, diskRecord.Name);
  276. extentRecord.DiskOffsetLBA = (ulong)extent.FirstSector - privateHeader.PublicRegionStartLBA;
  277. extentRecord.SizeLBA = (ulong)extent.TotalSectors;
  278. extentRecord.ComponentId = componentRecord.ComponentId;
  279. extentRecord.DiskId = diskRecord.DiskId;
  280. extentRecord.HasColumnIndexFlag = (index > 0);
  281. extentRecord.ColumnIndex = (uint)index; // zero based
  282. records.Add(extentRecord);
  283. }
  284. if (isDegraded)
  285. {
  286. // we have to make-up a disk
  287. // The DiskFlags and ExtentFlags are not necessary (they will be added later anyway when the disk group is reimported)
  288. DiskRecord diskRecord = new DiskRecord();
  289. diskRecord.Id = database.AllocateNewRecordID();
  290. diskRecord.Name = "Miss" + new Random().Next(100);
  291. diskRecord.DiskGuid = Guid.NewGuid();
  292. diskRecord.DiskFlags = DiskFlags.Detached;
  293. records.Add(diskRecord);
  294. ExtentRecord extentRecord = new ExtentRecord();
  295. extentRecord.Name = diskRecord.Name + "-01";
  296. extentRecord.ExtentFlags = ExtentFlags.Recover;
  297. extentRecord.SizeLBA = (ulong)extents[0].TotalSectors;
  298. extentRecord.ComponentId = componentRecord.ComponentId;
  299. extentRecord.DiskId = diskRecord.DiskId;
  300. extentRecord.HasColumnIndexFlag = true;
  301. extentRecord.ColumnIndex = (uint)extents.Count; // zero based
  302. records.Add(extentRecord);
  303. }
  304. database.UpdateDatabase(records);
  305. return volumeRecord.VolumeId;
  306. }
  307. private static string GetNextSimpleVolumeName(List<VolumeRecord> volumeRecords)
  308. {
  309. return GetNextVolumeName(volumeRecords, "Volume");
  310. }
  311. private static string GetNextSpannedVolumeName(List<VolumeRecord> volumeRecords)
  312. {
  313. return GetNextVolumeName(volumeRecords, "Volume");
  314. }
  315. private static string GetNextStripedVolumeName(List<VolumeRecord> volumeRecords)
  316. {
  317. return GetNextVolumeName(volumeRecords, "Stripe");
  318. }
  319. private static string GetNextRAIDVolumeName(List<VolumeRecord> volumeRecords)
  320. {
  321. return GetNextVolumeName(volumeRecords, "Raid");
  322. }
  323. private static string GetNextVolumeName(List<VolumeRecord> volumeRecords, string prefix)
  324. {
  325. int index = 1;
  326. while (true)
  327. {
  328. string name = prefix + index.ToString();
  329. bool isNameAvailable = true;
  330. foreach (VolumeRecord volumeRecord in volumeRecords)
  331. {
  332. if (volumeRecord.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
  333. {
  334. isNameAvailable = false;
  335. break;
  336. }
  337. }
  338. if (isNameAvailable)
  339. {
  340. return name;
  341. }
  342. index++;
  343. }
  344. }
  345. /// <param name="extentRecords">Could be all of the database records or just the relevant records</param>
  346. public static string GetNextExtentName(List<ExtentRecord> extentRecords, string diskName)
  347. {
  348. int index = 1;
  349. while (true)
  350. {
  351. string name = String.Format("{0}-{1}", diskName, index.ToString("00"));
  352. bool isNameAvailable = true;
  353. foreach (ExtentRecord extentRecord in extentRecords)
  354. {
  355. if (extentRecord.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
  356. {
  357. isNameAvailable = false;
  358. break;
  359. }
  360. }
  361. if (isNameAvailable)
  362. {
  363. return name;
  364. }
  365. index++;
  366. }
  367. }
  368. public static uint GetNextVolumeNumber(List<VolumeRecord> volumeRecords)
  369. {
  370. // volume number starts with 5 (probably because 1-4 are reserved for partitions)
  371. uint volumeNumber = 5;
  372. foreach (VolumeRecord record in volumeRecords)
  373. {
  374. if (record.VolumeNumber >= volumeNumber)
  375. {
  376. volumeNumber = record.VolumeNumber + 1;
  377. }
  378. }
  379. return volumeNumber;
  380. }
  381. }
  382. }