- augeas
- augeas-libs
- augeas-devel (ie augeas headers)
git clone https://github.com/georgehansper/augsuggest.c.git
cd augsuggest.c
make
This program is an Augeas client (https://augeas.net)
It is strongly recommended to be using Augeas 1.13.0 or later (see Idempotency below).
augsuggest
uses the Augeas API to create a script which can be run directly from augtool.
The resulting script is intended to
- be able to recreate the original file
- make no changes if applied to the original file
- use path-expressions in preference to position-numbers
The output script differs from the style of output generated by the augtool print
command, in that
the print
command output always uses an absolute position (number) for repeated nodes or sequential nodes
eg
augtool> print /files/etc/hosts
/files/etc/hosts
/files/etc/hosts/#comment[1] = "default entries"
/files/etc/hosts/1
/files/etc/hosts/1/ipaddr = "127.0.0.1"
/files/etc/hosts/1/canonical = "localhost"
/files/etc/hosts/1/alias[1] = "localhost.localdomain"
/files/etc/hosts/1/alias[2] = "localhost4"
...
augsuggest
tries hard to avoid numbered positions, and will generate a path-expression in place of a number in most cases (see Limitations below).
eg
set /files/etc/hosts/seq::*[ipaddr='127.0.0.1']/ipaddr '127.0.0.1'
set /files/etc/hosts/seq::*[ipaddr='127.0.0.1']/canonical 'localhost'
set /files/etc/hosts/seq::*[ipaddr='127.0.0.1']/alias[.='localhost.localdomain'] 'localhost.localdomain'
set /files/etc/hosts/seq::*[ipaddr='127.0.0.1']/alias[.='localhost4'] 'localhost4'
...
The path-expression is based on the values set for each position (number), such that associated paths are kept together
In practical terms, this means that if we apply the above augsuggest
output to any /etc/hosts
file:
a) If there is an existing /etc/hosts
entry like the following one, it will leave it unchanged:
127.0.0.1 localhost localhost.localdomain localhost4
b) If there is no entry for 127.0.0.1
in /etc/hosts
, it will create a new entry, appended to the end of the file
c) If there is an existing entry for 127.0.0.1
in /etc/hosts
which is different to the above, it will be modified
using the values from augsuggest
output, regardless of whether or not 127.0.0.1
is the first entry in the file
A normal use for this would be to idempotently add an entry to /etc/hosts
,
or if an existing entry is found, idempotently modify it to the values given
The nature of the augsuggest
paths are that they are position-independent, so that it doesn't require 127.0.0.1
to be
the first entry in /etc/hosts
- it can be any line in the file
The term "idempotent" is a mathematical term, referring to a function or operation which when subsequently re-applied to the value of the previous result, leaves the result unchanged. eg. multiplying a number by zero or one, or taking the zero-th power of a number.
augsuggest
aims to create an augtool script that is "idempotent", in that once it has been applied to a file, re-applying
the same script will make no futher changes.
Augeas versions prior to 1.13.0 lack support for the seq::*
element in the path-expression, making it difficult to write idempotent augtool scripts
As such, augsuggest
should be used with Augeas 1.13.0 or later.
The option --noseq
will alter the output of augsuggest
to use *
in place of seq::*
The resulting script will still be idempotent, and will be compatible with earlier versions of Augeas
However, it will not be able to create the paths which are of the form .../1
or .../2
etc
Existing paths will still have their values modified to those in the script
For example, given a path like this (from an augtool
command match
or print
):
/files/etc/hosts/1/ipaddr = "127.0.0.1"
The corresponding output from augsuggest --noseq ...
would include
set /files/etc/hosts/*[ipaddr='127.0.0.1']/ipaddr '127.0.0.1'
This statement will not create a new entry for 127.0.0.1
if none exists.
Augeas is already quite good at being idempotent, but writing path-expressions can be difficult for newcomers, and tedious for experienced users.
The output of augsuggest
is intended to automate the generation of Augeas path-expressions as follows:
a) Copy the target file to another location, eg.
cp /etc/hosts /var/tmp/hosts.new
b) Edit the new file as desired, eg add
192.0.2.3 defaultdns
c) Run augsuggest
as follows
augsuggest --target=/etc/hosts /var/tmp/hosts.new
d) Identify the path-expressions associated with the changes
set /files/etc/hosts/seq::*[ipaddr='192.0.2.3']/ipaddr '192.0.2.3'
set /files/etc/hosts/seq::*[ipaddr='192.0.2.3']/canonical 'defaultdns'
e) Copy the above directly into an augtool
script or use them in your chosen augeas client
augsuggest
can also produce paths based on regular expressions, as follows:
augsuggest --target=/etc/hosts --regexp /var/tmp/hosts.new
set /files/etc/hosts/seq::*[ipaddr=~regexp('192\\.0\\.2\\.3')]/ipaddr '192.0.2.3'
set /files/etc/hosts/seq::*[ipaddr=~regexp('192\\.0\\.2\\.3')]/canonical 'defaultdns'
The output is based entirely on set operations. The set operation can only:
a) change an existing value in-situ b) append a new value after the last position in the group
This means that when an entry is re-created, it may not be in the same position as originally intended.
ie if the entry for 192.0.2.3
does not already exist, it will be created as the last entry in /etc/hosts
Often, such out-of-sequence entries will not matter to the resulting configuration file. If it does matter, further manual editing of the augtool script will be required.
augsuggest
tries hard to find a path-expression which is unique to a position, and independent of the numerical position
However, it is not always successful.
If it fails to find a unique expression, it will produce a partial expression followed by a position-number. eg.
For an /etc/hosts
section:
#------
192.0.2.3 defaultdns
#------
The output would be:
set /files/etc/hosts/#comment[.='--------'][1] '--------'
set /files/etc/hosts/seq::*[ipaddr='192.0.2.3']/ipaddr '192.0.2.3'
set /files/etc/hosts/seq::*[ipaddr='192.0.2.3']/canonical 'defaultdns'
set /files/etc/hosts/#comment[.='--------'][2] '--------'
Notice how #comment
paths have [1]
and [2]
appended respectively to the [expr]
augsuggest
could not find a position-independent path-expression to describe these comments, because the same comment appears twice in the file.
The resulting output is still idempotent, but the path for the #comment
is no longer "independent of position"
Copyright (C) 2022 George Hansper [email protected]