our
$VERSION
=
'1.16.0'
;
my
$has_config_augeas
=
can_load(
modules
=> {
'Config::Augeas'
=>
undef
} ) ? 1 : 0;
@EXPORT
=
qw(augeas)
;
sub
augeas {
my
(
$action
,
@options
) =
@_
;
my
$ret
;
my
$is_ssh
= Rex::is_ssh();
my
$aug
;
my
$use_augtool
=
!
$has_config_augeas
|| Rex::Config->get_local_augeas_backend eq
'augtool'
;
if
( !
$is_ssh
&& !
$use_augtool
) {
Rex::Logger::debug(
"Creating Config::Augeas Object"
);
$aug
= Config::Augeas->new;
my
$commands_to_prepend
= Rex::Config->get_augeas_commands_prepend();
$aug
->srun(
join
qq(\n)
, @{
$commands_to_prepend
} );
}
my
$on_change
;
my
$changed
;
if
(
$action
eq
"modify"
) {
my
$config_option
= {
@options
};
$on_change
=
delete
$config_option
->{on_change}
if
ref
$config_option
->{on_change} eq
'CODE'
;
if
(
$is_ssh
||
$use_augtool
) {
my
@commands
;
for
my
$key
(
keys
%{
$config_option
} ) {
Rex::Logger::debug(
"modifying $key -> "
.
$config_option
->{
$key
} );
push
@commands
,
qq(set $key "$config_option->{$key}"\n)
;
}
my
$result
= _run_augtool(
@commands
);
$ret
=
$result
->{
return
};
$changed
=
$result
->{changed};
}
else
{
for
my
$key
(
keys
%{
$config_option
} ) {
Rex::Logger::debug(
"modifying $key -> "
.
$config_option
->{
$key
} );
$aug
->set(
$key
,
$config_option
->{
$key
} );
}
$ret
=
$aug
->save;
Rex::Logger::debug(
"Augeas set status: $ret"
);
$changed
= 1
if
$ret
&&
$aug
->get(
'/augeas/events/saved'
);
}
}
elsif
(
$action
eq
"remove"
) {
if
(
$options
[-2]
&&
$options
[-2] eq
'on_change'
&&
ref
$options
[-1] eq
'CODE'
)
{
$on_change
=
pop
@options
;
pop
@options
;
}
my
@commands
;
for
my
$aug_key
(
@options
) {
Rex::Logger::debug(
"deleting $aug_key"
);
if
(
$is_ssh
||
$use_augtool
) {
push
@commands
,
"rm $aug_key\n"
;
}
else
{
my
$_r
=
$aug
->remove(
$aug_key
);
Rex::Logger::debug(
"Augeas delete status: $_r"
);
}
}
if
(
$is_ssh
||
$use_augtool
) {
my
$result
= _run_augtool(
@commands
);
$ret
=
$result
->{
return
};
$changed
=
$result
->{changed};
}
else
{
$ret
=
$aug
->save;
$changed
= 1
if
$ret
&&
$aug
->get(
'/augeas/events/saved'
);
}
}
elsif
(
$action
eq
"insert"
) {
my
$file
=
shift
@options
;
my
$opts
= {
@options
};
my
$label
=
$opts
->{
"label"
};
delete
$opts
->{
"label"
};
if
(
$options
[-2]
&&
$options
[-2] eq
'on_change'
&&
ref
$options
[-1] eq
'CODE'
)
{
$on_change
=
pop
@options
;
pop
@options
;
}
if
(
$is_ssh
||
$use_augtool
) {
my
$position
= (
exists
$opts
->{
"before"
} ?
"before"
:
"after"
);
unless
(
exists
$opts
->{
$position
} ) {
Rex::Logger::info(
"Error inserting key. You have to specify before or after."
);
return
0;
}
my
@commands
= (
"ins $label $position $file$opts->{$position}\n"
);
delete
$opts
->{
$position
};
for
(
my
$i
= 0 ;
$i
<
@options
;
$i
+= 2 ) {
my
$key
=
$options
[
$i
];
my
$val
=
$options
[
$i
+ 1 ];
next
if
(
$key
eq
"after"
or
$key
eq
"before"
or
$key
eq
"label"
);
my
$_key
=
"$file/$label/$key"
;
Rex::Logger::debug(
"Setting $_key => $val"
);
push
@commands
,
qq(set $_key "$val"\n)
;
}
my
$result
= _run_augtool(
@commands
);
$ret
=
$result
->{
return
};
$changed
=
$result
->{changed};
}
else
{
if
(
exists
$opts
->{
"before"
} ) {
$aug
->insert(
$label
,
before
=>
"$file"
.
$opts
->{
"before"
} );
delete
$opts
->{
"before"
};
}
elsif
(
exists
$opts
->{
"after"
} ) {
my
$t
=
$aug
->insert(
$label
,
after
=>
"$file"
.
$opts
->{
"after"
} );
delete
$opts
->{
"after"
};
}
else
{
Rex::Logger::info(
"Error inserting key. You have to specify before or after."
);
return
0;
}
for
(
my
$i
= 0 ;
$i
<
@options
;
$i
+= 2 ) {
my
$key
=
$options
[
$i
];
my
$val
=
$options
[
$i
+ 1 ];
next
if
(
$key
eq
"after"
or
$key
eq
"before"
or
$key
eq
"label"
);
my
$_key
=
"$file/$label/$key"
;
Rex::Logger::debug(
"Setting $_key => $val"
);
$aug
->set(
$_key
,
$val
);
}
$ret
=
$aug
->save();
$changed
= 1
if
$ret
&&
$aug
->get(
'/augeas/events/saved'
);
}
}
elsif
(
$action
eq
"dump"
) {
my
$file
=
shift
@options
;
my
$aug_key
=
$file
;
if
(
$is_ssh
||
$use_augtool
) {
my
$output
= _run_augtool(
"print $aug_key"
);
say
$output
->{
'return'
};
}
else
{
$aug
->
print
(
$aug_key
);
}
$ret
= 0;
}
elsif
(
$action
eq
"exists"
) {
my
$file
=
shift
@options
;
my
$aug_key
=
$file
;
my
$val
=
$options
[0] ||
""
;
if
(
$is_ssh
||
$use_augtool
) {
my
@paths
;
my
$result
= _run_augtool(
"match $aug_key"
);
for
my
$line
(
split
"\n"
,
$result
->{
return
} ) {
$line
=~ s/\s=[^=]+$// or
next
;
push
@paths
,
$line
;
}
if
(
$val
) {
for
my
$k
(
@paths
) {
my
@ret
;
my
$result
= _run_augtool(
"get $k"
);
for
my
$line
(
split
"\n"
,
$result
->{
return
} ) {
$line
=~ s/^[^=]+=\s//;
push
@ret
,
$line
;
}
if
(
$ret
[0] eq
$val
) {
return
$k
;
}
}
}
else
{
return
@paths
;
}
$ret
=
undef
;
}
else
{
my
@paths
=
$aug
->match(
$aug_key
);
if
(
$val
) {
for
my
$k
(
@paths
) {
if
(
$aug
->get(
$k
) eq
$val
) {
return
$k
;
}
}
}
else
{
return
@paths
;
}
$ret
=
undef
;
}
}
elsif
(
$action
eq
"get"
) {
my
$file
=
shift
@options
;
if
(
$is_ssh
||
$use_augtool
) {
my
@lines
;
my
$result
= _run_augtool(
"get $file"
);
for
my
$line
(
split
"\n"
,
$result
->{
return
} ) {
$line
=~ s/^[^=]+=\s//;
push
@lines
,
$line
;
}
return
$lines
[0];
}
else
{
return
$aug
->get(
$file
);
}
}
else
{
Rex::Logger::info(
"Unknown augeas action."
);
}
if
(
$on_change
&&
$changed
) {
Rex::Logger::debug(
"Calling on_change hook of augeas"
);
$on_change
->();
}
Rex::Logger::debug(
"Augeas Returned: $ret"
)
if
$ret
;
return
$ret
;
}
sub
_run_augtool {
my
(
@commands
) =
@_
;
die
"augtool is not installed or not executable in the path"
unless
can_run
"augtool"
;
my
$rnd_file
= get_tmp_file;
my
$fh
= Rex::Interface::File->create;
my
$commands_to_prepend
= Rex::Config->get_augeas_commands_prepend();
unshift
@commands
, @{
$commands_to_prepend
};
$fh
->
open
(
">"
,
$rnd_file
);
$fh
->
write
(
$_
.
qq(\n)
)
foreach
(
@commands
);
$fh
->
close
;
my
(
$return
,
$error
) = i_run
"augtool --file $rnd_file --autosave"
,
sub
{
@_
},
fail_ok
=> 1;
my
$ret
= $? == 0 ? 1 : 0;
if
(
$ret
) {
Rex::Logger::debug(
"Augeas command return value: $ret"
);
Rex::Logger::debug(
"Augeas result: $return"
);
}
else
{
Rex::Logger::info(
"Augeas command failed: $error"
,
'warn'
);
}
my
$changed
=
"$return"
=~ /Saved/ ? 1 : 0;
unlink
$rnd_file
;
{
result
=>
$ret
,
return
=>
$return
||
$error
,
changed
=>
$changed
,
};
}
1;