public void LoadConfigFile(string configFile) { Log.Trace(@"Loading config file {0}", configFile); ConfigErrors.Clear(); ConfigWarnings.Clear(); ConfigPath = configFile; if (Read()) { DoValidation(); } else { ConfigErrors.Add($"Failed to read the configuration file {configFile}"); } }
/// <summary> /// Reads the config file /// </summary> /// <returns>an empty string if no exception occurs</returns> public bool Read() { Log.Trace(@"ConfigFileHelper.Read() ..."); try { if (!ConfigFileExists) { return(false); } bool isConfigRead = true; ParityFile1 = string.Empty; ParityFile2 = string.Empty; ZParityFile = string.Empty; ParityFile3 = string.Empty; ParityFile4 = string.Empty; ParityFile5 = string.Empty; ParityFile6 = string.Empty; ConfigErrors.Clear(); ConfigWarnings.Clear(); ContentFiles.Clear(); SnapShotSources.Clear(); ExcludePatterns.Clear(); IncludePatterns.Clear(); BlockSizeKB = Constants.DefaultBlockSize; AutoSaveGB = Constants.DefaultAutoSave; foreach (string line in File.ReadLines(ConfigPath)) { string lineStart = line.Trim(); if (string.IsNullOrWhiteSpace(lineStart) || lineStart.StartsWith(@"#")) { continue; } // Not a comment so process the line // split the line by the first space encountered string[] configItem = lineStart.Split(new[] { ' ' }, 2); Log.Trace(@"configItem [{0}]", string.Join(" ", configItem)); string configItemName = configItem[0] ?? string.Empty; Log.Trace(@"configItemName [{0}]", configItemName); string configItemValue = (configItem.Length > 1) ? configItem[1] : string.Empty; Log.Trace(@"configItemValue [{0}]", configItemValue); // ignore the line if it is not an a recognized setting if (!validConfigNames.Contains(configItemName)) { continue; } Log.Trace(@"configItemName found in validConfigNames"); switch (configItemName) { case @"parity": ParityFile1 = configItemValue; break; case @"q-parity": Log.Warn(@"'q-parity' entry in config file will be changed to '2-parity' when config is saved"); ParityFile2 = configItemValue; break; case @"2-parity": // handle legacy 'q-parity' entry by giving it priority over any '2-parity' entry; saving config will rename 'q-parity' to '2-parity' and leave the path unchanged if (string.IsNullOrEmpty(ParityFile2)) { ParityFile2 = configItemValue; } break; case @"z-parity": // #WARNING! Your CPU doesn't have a fast implementation for triple parity. // #WARNING! It's recommended to switch to 'z-parity' instead than '3-parity'. ZParityFile = configItemValue; ParityFile3 = string.Empty; break; case @"3-parity": if (string.IsNullOrWhiteSpace(ZParityFile)) { ParityFile3 = configItemValue; } break; case @"4-parity": ParityFile4 = configItemValue; break; case @"5-parity": ParityFile5 = configItemValue; break; case @"6-parity": ParityFile6 = configItemValue; break; case @"content": ContentFiles.Add(configItemValue); break; case @"disk": case @"data": // Handle older configs { // get the data name, d1,d2,d3 etc string diskName = configItemValue.Split(' ')[0]; // get the path int diskSplitIndex = configItemValue.IndexOf(' '); string diskPath = configItemValue.Substring(diskSplitIndex + 1); // special handling of data sources since order preservation is extremely important if (!string.IsNullOrEmpty(diskName) && !string.IsNullOrEmpty(diskPath)) { SnapShotSources.Add(new SnapShotSource { Name = diskName, DirSource = diskPath }); } } break; case @"exclude": ExcludePatterns.Add(configItemValue); break; case @"include": IncludePatterns.Add(configItemValue); break; case @"block_size": BlockSizeKB = uint.Parse(configItemValue); if (BlockSizeKB < Constants.MinBlockSize || BlockSizeKB > Constants.MaxBlockSize) { isConfigRead = false; } break; case @"autosave": AutoSaveGB = uint.Parse(configItemValue); // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (AutoSaveGB < Constants.MinAutoSave || AutoSaveGB > Constants.MaxAutoSave) { isConfigRead = false; } break; case @"nohidden": Nohidden = true; break; } } return(isConfigRead); } catch (Exception ex) { Log.Error(ex); return(false); } }
private void DoValidation() { if (string.IsNullOrEmpty(ConfigPath)) { ConfigErrors.Add(@"Config validation failed. There is no config file set."); Log.Error(@"Config validation failed. There is no config file set."); return; } Log.Debug("validating config file {0}", ConfigPath); // RULE: at least one data source must be specified if (!SnapShotSources.Any()) { ConfigErrors.Add("At least one data source must be specified. Check the value for \"d1\" \r\n"); } // RULE: the first parity location must not be empty if (string.IsNullOrEmpty(ParityFile1)) { ConfigErrors.Add("The first parity location must not be empty. Check the value for \"parity\""); } // RULE: number of content files must be at least the (number of parity files + 1) if (ContentFiles.Count < ParityPaths.Count + 1) { ConfigErrors.Add($"The number of content files must be at least one greater than the number of parity files. There should be at least {ParityPaths.Count + 1} content files."); } // RULE: check that devices are not repeated if (!IsRulePassDevicesMustNotRepeat(DataSourcePaths)) { ConfigErrors.Add("Devices for Source and Parity must be unique. Check the values for data and parity"); } // RULE: data paths must be accessible foreach (SnapShotSource source in DataSourcePaths) { // test if path exists if (!Directory.Exists(source.DirSource)) { ConfigErrors.Add($"Source is inaccessible or does not exist: {source.DirSource}"); } } // RULE: parity devices should be greater or equal to data devices ByteSize largestSourceDevice = new ByteSize(StorageUtil.GetDriveSizes(DataSourcePaths).Max()); ByteSize smallestParityDevice = new ByteSize(StorageUtil.GetDriveSizes(ParityPaths).Min()); if (largestSourceDevice > smallestParityDevice) { ConfigWarnings.Add($@"One or more data devices [{largestSourceDevice}] are larger than the smallest parity device [{smallestParityDevice}]. All parity devices should be equal or greater in size than all data devices."); } // RULE: blockSize valid value if (BlockSizeKB < 1 || BlockSizeKB > 16384) { ConfigErrors.Add(@"The blockSize value is invalid and must be between 1 and 16384"); } // RULE: autoSave valid value if (AutoSaveGB > Constants.MaxAutoSave) { ConfigErrors.Add($"The autoSave value is invalid and must be between {Constants.MinAutoSave} and {Constants.MaxAutoSave}"); } if (!IsValid) { Log.Error(@"The configuration file is not valid. See errors below:"); foreach (string error in ConfigErrors) { Log.Error(@" - {0}", error); } } }