Typed tree to retrieve configuration tree

This commit is contained in:
code liturgy 2022-11-14 18:49:29 +00:00
parent 96cb8df66f
commit 36b776d6d1
16 changed files with 333 additions and 208 deletions

View File

@ -1,4 +1,5 @@
using System.Text;
using System.Text.RegularExpressions;
namespace Sharp.Augeas.Test;
@ -29,11 +30,10 @@ public class AugeasTests
}
[Test]
public void NoExceptionThrownWhenPrintingVirtualhostTree()
public void GetTreeHasDirectivesAndArguments()
{
var virtualHostConfig = EXAMPLE_CONF_1;
_augeas.PrintVirtualHostTree(virtualHostConfig);
Assert.Pass();
var tree = _augeas.GetTree("VirtualHost", $"/files{EXAMPLE_CONF_1}/VirtualHost/*");
Assert.That(tree.Arguments.Count > 0 && tree.Directives.Count > 0);
}
[Test]
@ -75,6 +75,7 @@ public class AugeasTests
{
Assert.Fail("Unable to write changes to disk.");
}
var retrieveVal = _augeas.GetNode(nodePath);
Assert.That(retrieveVal == newValue);
}
@ -99,7 +100,6 @@ public class AugeasTests
}
[Test]
public void MatchCanReturnMultipleDirectives()
{
@ -108,13 +108,4 @@ public class AugeasTests
Assert.That(sites.Length > 0);
}
[Test]
public void GetTreeVirtualHostReturnsDictionaryWithKeys()
{
var path = EXAMPLE_CONF_1;
var tree = _augeas.GetVirtualHostTree(path);
Assert.That(tree.Count > 0);
}
}

View File

@ -1,4 +1,6 @@
using System.Runtime.InteropServices;using Sharp.Augeas.Test;
using System.Runtime.InteropServices;
using System.Text;
using Sharp.Augeas.Test;
using static Sharp.Augeas.AugeasExtern;
namespace Sharp.Augeas
@ -8,12 +10,13 @@ namespace Sharp.Augeas
/// </summary>
public sealed unsafe class Augeas
{
#region Flags
/// <summary>
///
/// </summary>
public static int NONE = 0;
public static int SAVE_BACKUP = (1 << 0);
public static int SAVE_NEWFILE = (1 << 1);
public static int TYPE_CHECK = (1 << 2);
@ -23,22 +26,23 @@ namespace Sharp.Augeas
public static int NO_MODL_AUTOLOAD = (1 << 6);
public static int AUG_ENABLE_SPAN = (1 << 7);
#endregion Flags
private readonly IntPtr _augeas;
private HashSet<string> _loadedFiles = new();
#region Constructor / Destructor
/// <summary>
/// Augeas Core Destructor
/// </summary>
~Augeas()
{
{
close_aug(_augeas);
}
/// <summary>
/// Augeas Core constructor
/// </summary>
@ -60,7 +64,7 @@ namespace Sharp.Augeas
public Augeas(string root)
{
var lensPath = Environment.GetEnvironmentVariable("AUG_LENS_PATH");
if (string.IsNullOrEmpty(lensPath))
{
throw new InvalidOperationException(
@ -68,21 +72,19 @@ namespace Sharp.Augeas
}
var augSettings = new AugSettings(root, lensPath);
_augeas = init_aug(augSettings, NO_STDINC | NO_LOAD);
if (_augeas == IntPtr.Zero)
{
throw new InvalidOperationException("Augeas is not a valid instance.");
}
}
#endregion
#region Augeas Internal Api
/// <summary>
/// Prints a preview of the desired segment in <see cref="matchPath"/>
/// </summary>
@ -114,7 +116,7 @@ namespace Sharp.Augeas
{
return true;
}
bool success = load_file(_augeas, configurationFilePath);
_loadedFiles.Add(configurationFilePath);
@ -145,9 +147,9 @@ namespace Sharp.Augeas
/// </summary>
/// <param name="matchPath">Configuration path.</param>
/// <returns>Dictionary with the Tree</returns>
private Dictionary<string,string> GetTree(string matchPath)
public Dictionary<string, string> GetDictionaryTree(string matchPath)
{
var result = new Dictionary<string, string>();
var result = new Dictionary<string, string>();
var raw = get_tree(_augeas, matchPath);
string sb = Marshal.PtrToStringAnsi(raw);
var lines = sb.Split(";ENDL;");
@ -159,6 +161,7 @@ namespace Sharp.Augeas
result.Add(pair[0].Remove(0, 3), pair[1]);
}
}
FreeString(raw);
return result;
}
@ -188,7 +191,7 @@ namespace Sharp.Augeas
public int InsertNode(string matchPath, string label, int before = 0)
{
return insert_node(_augeas, matchPath, label, before);
return insert_node(_augeas, matchPath, label, before);
}
@ -219,16 +222,16 @@ namespace Sharp.Augeas
FreeString(raw);
return sb;
}
#endregion
#endregion
/// <summary>
/// Matches of the path expression PATH in AUG.
/// </summary>
/// <param name="matchPath">Augeas path.</param>
/// <returns></returns>
public string[] Match (string matchPath)
public string[] Match(string matchPath)
{
sbyte** result = null;
var count = match(_augeas, matchPath, &result);
@ -248,42 +251,9 @@ namespace Sharp.Augeas
result[i] = Marshal.PtrToStringUTF8((IntPtr)list[i]);
Marshal.FreeCoTaskMem((IntPtr)list[i]);
}
return result;
}
/// <summary>
/// Given a <see cref="apacheSitePath"/>, prints the Virtual Host tree.
/// </summary>
/// <param name="apacheSitePath">Apache configuration file.</param>
public void PrintVirtualHostTree(string apacheSitePath)
{
LoadFile(apacheSitePath);
string virtualHostTree = $"/files{apacheSitePath}/VirtualHost/*";
PrintTree(virtualHostTree);
}
/// <summary>
/// Given a <see cref="apacheSitePath"/>, gets a virtual host tree.
/// </summary>
/// <param name="apacheSitePath">Apache configuration file.</param>
/// <returns></returns>
public Dictionary<string,string> GetVirtualHostTree(string apacheSitePath)
{
LoadFile(apacheSitePath);
string virtualHostTree = $"/files{apacheSitePath}/VirtualHost/*";
return GetTree(virtualHostTree);
}
void PrintVirtualHostProxyTree(string apacheSitePath)
{
LoadFile(apacheSitePath);
string virtualHostProxyMatchPath = $"/files{apacheSitePath}/VirtualHost/Proxy/*";
PrintTree(virtualHostProxyMatchPath);
}
}
}
}

View File

@ -15,7 +15,7 @@
<TargetPath>root\%(RecursiveDir)\%(Filename)%(Extension)</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup >
<ItemGroup>
<Content Include="./Platform/Linux/libclAugeas.so">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

View File

@ -0,0 +1,25 @@
namespace Sharp.Augeas;
public class Argument : Node
{
public string Value;
public bool SetValue(string newValue)
{
Value = newValue;
return _augeas.SetNode(Path, newValue);
}
public Argument(Augeas augeas, string id, string path, string value) : base(augeas, path)
{
Id = id;
Path = path;
Value = value;
}
public override string ToString()
{
return Value;
}
}

View File

@ -0,0 +1,74 @@
using System.Text;
namespace Sharp.Augeas;
public class Directive : Node
{
public string Value;
public List<Argument> Arguments;
public bool HasArguments => Arguments.Count != 0;
/// <summary>
/// Sets a new value for the directive using string literals
/// The values are separated by spaces, the first value is the directive value,
/// the other values are values corresponding to the arguments.
/// </summary>
/// <param name="newValue"></param>
public void Set(string newValue)
{
var splitted = newValue.Split(" ");
if (splitted.Length > 0)
{
SetValue(splitted[0]);
}
for (int i = 1; i < splitted.Length; i++)
{
var argumentIndex = i - 1;
Arguments[argumentIndex].SetValue(splitted[i]);
}
}
/// <summary>
/// Set the value of this directive.
/// Note: This doesn't write any changes to disk.
/// </summary>
/// <param name="newValue">New value.</param>
/// <returns></returns>
public bool SetValue(string newValue)
{
Value = newValue;
return _augeas.SetNode(Path, newValue);
}
public Directive(Augeas augeas, string id, string path, string value) : base(augeas, path)
{
Value = value;
Id = id;
Path = path;
Arguments = new List<Argument>();
}
/// <summary>
/// Add a argument to this node.
/// </summary>
/// <param name="argument"></param>
public void AddArgument(Argument argument)
{
argument.Parent = this;
Arguments.Add(argument);
}
public override string ToString()
{
var sb = new StringBuilder($"{Value} ");
foreach (var arg in Arguments)
{
sb.Append($"{arg} ");
}
return sb.ToString().TrimEnd();
}
}

28
Sharp.Augeas/Tree/Node.cs Normal file
View File

@ -0,0 +1,28 @@
namespace Sharp.Augeas;
public class Node
{
public string Id;
public Node Parent;
public string Path;
protected readonly Augeas _augeas;
public Node(Augeas augeas, string path)
{
_augeas = augeas;
Parent = null;
Path = path;
}
public Node(Augeas augeas, Node parent, string path)
{
_augeas = augeas;
Parent = parent;
Path = path;
}
public bool Save()
{
return _augeas.Save();
}
}

View File

@ -0,0 +1,56 @@
using System.Text;
namespace Sharp.Augeas
{
public class SuperNode : Node
{
public List<Directive> Directives;
public List<Argument> Arguments;
public List<SuperNode> SuperNodes;
public SuperNode(Augeas augeas, string path, string id) : base(augeas, path)
{
Path = path;
Id = id;
Directives = new List<Directive>();
Arguments = new List<Argument>();
SuperNodes = new List<SuperNode>();
}
public Directive GetDirective(string value)
{
return Directives.FirstOrDefault(d => d.Value == value, null);
}
public void AddArgument(Argument argument)
{
argument.Parent = this;
Arguments.Add(argument);
}
public void AddDirective(Directive directive)
{
directive.Parent = this;
Directives.Add(directive);
}
public void AddSuperNode(SuperNode superNode)
{
superNode.Parent = this;
SuperNodes.Add(superNode);
}
public override string ToString()
{
var sb = new StringBuilder($"{Id} ");
foreach (var arg in Arguments)
{
sb.Append($"{arg} ");
}
return sb.ToString().TrimEnd();;
}
}
}

View File

@ -0,0 +1,119 @@
using System.Text;
namespace Sharp.Augeas;
public static class TreeExtensions
{
/// <summary>
/// Builds a typed tree with the <see cref="augeasPath"/>
/// </summary>
/// <param name="augeas"></param>
/// <param name="superNodeName">Super node name.</param>
/// <param name="augeasPath">Augeas path.</param>
/// <returns></returns>
public static SuperNode GetTree(this Augeas augeas, string superNodeName, string augeasPath)
{
var basePath = augeasPath.Replace("*", "");
var tree = augeas.GetDictionaryTree(augeasPath);
Dictionary<string, Directive> addedDirectives = new Dictionary<string, Directive>();
Dictionary<string, SuperNode> addedSuperNodes = new Dictionary<string, SuperNode>();
addedSuperNodes.Add(superNodeName, new SuperNode(augeas, augeasPath, superNodeName));
foreach (var treeKey in tree.Keys)
{
var hierarchy = treeKey.Split('/');
var firstElement = hierarchy[0];
// Parent
if (hierarchy.Length <= 1)
{
var rFullPath = $"{basePath}{firstElement}";
if (firstElement.StartsWith("directive"))
{
Directive directive = new Directive(augeas, firstElement, rFullPath, tree[firstElement]);
directive.Parent = addedSuperNodes[superNodeName];
addedSuperNodes[superNodeName].AddDirective(directive);
addedDirectives.Add(treeKey, directive);
continue;
}
// If is argument
if (firstElement.StartsWith("arg"))
{
Argument argument = new Argument(augeas, firstElement, rFullPath, tree[firstElement]);
argument.Parent = addedSuperNodes[superNodeName];
addedSuperNodes[superNodeName].AddArgument(argument);
continue;
}
if (IsSuperNodeCandidate(firstElement) && !addedSuperNodes.ContainsKey(firstElement))
{
var superNode = new SuperNode(augeas, rFullPath, firstElement);
superNode.Parent = addedSuperNodes[superNodeName];
addedSuperNodes.Add(firstElement, superNode);
}
continue;
}
// handle children
for (var i = 1; i < hierarchy.Length; i++)
{
var childKey = hierarchy[i];
var parentPathBuilder = new StringBuilder();
for (int j = 0; j < i; j++) parentPathBuilder.Append($"{hierarchy[j]}/");
var parentPath = parentPathBuilder.ToString();
parentPath = parentPath.Trim('/');
var fullPath = $"{parentPath}/{childKey}";
var rFullPath = $"{basePath}{fullPath}";
var childValue = tree[fullPath];
if (IsSuperNodeCandidate(firstElement) && !addedSuperNodes.ContainsKey(firstElement))
{
var superNode = new SuperNode(augeas, rFullPath, firstElement);
superNode.Parent = addedSuperNodes[superNodeName];
addedSuperNodes.Add(firstElement, superNode);
}
if (childKey.StartsWith("directive"))
{
Directive directive = new Directive(augeas, childKey, rFullPath, childValue);
if (addedDirectives.ContainsKey(fullPath)) continue;
addedDirectives.Add(fullPath, directive);
if (addedSuperNodes.ContainsKey(parentPath))
addedSuperNodes[parentPath].AddDirective(directive);
continue;
}
if (childKey.StartsWith("arg"))
{
Argument argument = new Argument(augeas, childKey, rFullPath, childValue);
if (addedDirectives.ContainsKey(parentPath))
{
addedDirectives[parentPath].AddArgument(argument);
continue;
}
if (addedSuperNodes.ContainsKey(parentPath))
addedSuperNodes[parentPath].AddArgument(argument);
}
}
}
var superNodes = addedSuperNodes.Keys;
foreach (var key in superNodes)
{
if (key != superNodeName)
{
addedSuperNodes[superNodeName].AddSuperNode(addedSuperNodes[key]);
}
}
return addedSuperNodes[superNodeName];
}
private static bool IsSuperNodeCandidate(string key)
{
return !key.StartsWith("arg") && !key.StartsWith("directive");
}
}

View File

@ -1,6 +0,0 @@
namespace Sharp.Augeas;
public static class ApacheConfigExtensions
{
}

View File

@ -1,20 +0,0 @@
namespace Sharp.Augeas;
public class Argument<T> : Node where T: Node
{
public new T Parent;
public string Value;
public Argument(T parent, string value)
{
Parent = parent;
Children = null;
Value = value;
}
public Argument()
{
}
}

View File

@ -1,7 +0,0 @@
namespace Sharp.Augeas;
public class Directive : Node
{
public string Value;
public List<Argument> Arguments;
}

View File

@ -1,50 +0,0 @@
namespace Sharp.Augeas;
public class Node
{
public Node Parent;
public List<Node> Children;
public Node()
{
Parent = null;
Children = new List<Node>();
}
public Node(Node parent)
{
Parent = parent;
Children = new List<Node>();
}
public T[] GetChildren<T>()
{
return Children
.OfType<T>()
.ToArray();
}
public Node(Node parent, List<Node> children)
{
Parent = parent;
Children = children;
}
public void AddChild(Node node)
{
Children.Add(node);
}
public void RemoveChild(Node child)
{
if (Children.Contains(child))
{
Children.Remove(child);
}
}
public void RemoveAllChildren()
{
Children = new List<Node>();
}
}

View File

@ -1,8 +0,0 @@
namespace Sharp.Augeas;
public class Proxy : Node
{
public new VirtualHost Parent;
public List<Directive<Proxy>> Directives;
public List<Argument<Proxy>> Arguments;
}

View File

@ -1,9 +0,0 @@
namespace Sharp.Augeas
{
public class VirtualHost : Node
{
public List<Directive<Proxy>> Directives;
public List<Argument<Proxy>> Arguments;
}
}

View File

@ -1,38 +0,0 @@
namespace Sharp.Augeas;
public static class VirtualHostTreeGenerator
{
public static VirtualHost Generate(Dictionary<string,string> dic)
{
var keys = dic.Keys;
var virtualHost = new VirtualHost();
foreach (var key in keys)
{
}
return virtualHost;
}
private static bool IsProxy(string key)
{
return key.Contains("f/VirtualHost/Proxy/");
}
private static bool IsVirtualHostArg(string key)
{
return key.Contains("f/VirtualHost/arg;");
}
private static bool IsProxyDirective(string key)
{
return key.Contains("f/VirtualHost/Proxy/directive");
}
private static bool IsVirtualHostDirective(string key)
{
return key.Contains("f/VirtualHost/directive");
}
private static bool IsDirectiveArg(string key)
{
return key.Contains("f/VirtualHost/directive");
}
}