Commit 4f086521 authored by bmortier's avatar bmortier

Merge branch '1.3-dev' into HEAD

Signed-off-by: bmortier's avatarBenoit Mortier <benoit.mortier@opensides.be>
parents c673a1b3 972c9077
### Description
<!-- Required -->
<!-- Description of the issue -->
### Source Argonaut Version
<!-- Required -->
<!-- Argonaut version where the code come from -->
### Destination Argonaut Version
<!-- Required -->
<!-- Argonaut version where the code sould go -->
### component with the new code
<!-- Required -->
<!-- Name of the component -->
### Reason of Backporting code
<!-- Required -->
### What defect does it correct
<!-- Required -->
<!-- Why do we backport this code -->
### Additional Information
<!-- optional -->
<!-- Any additional information, configuration or data that might be necessary to reproduce the issue. -->
### Description
<!-- Required -->
<!-- Description of the issue -->
### Distribution Name and Version
<!-- Required -->
<!-- Debian, Centos -->
### Argonaut Version
<!-- Required -->
### PERL version used
<!-- Required -->
### Origin of perl packages
<!-- Required -->
<!-- Distribution packages, Out of distribution -->
### Steps to Reproduce
<!-- Required -->
1. [First Step]
2. [Second Step]
3. [and so on...]
**Expected behavior:**
<!-- What you expect to happen-->
**Actual behavior:**
<!-- What actually happens -->
**Reproduces how often:**
<!-- What percentage of the time does it reproduce?-->
### Additional Information
<!-- optional -->
<!-- Any additional information, configuration or data that might be necessary to reproduce the issue. -->
### Requirements
* Filling out the template is required. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion.
* All new code requires tests to ensure against regressions
## Title of the Pull Requests
<!-- required -->
### Description of the Change
<!-- required -->
<!-- can be an aglomeration of commits bodies -->
### Benefits
<!-- optional -->
<!-- What benefits will be realized by the code change? -->
### Possible Drawbacks
<!-- optional -->
<!-- What are the possible side-effects or negative impacts of the code change? -->
### Applicable Issues
<!-- optional -->
<!-- Enter any applicable Issues here -->
\ No newline at end of file
## %"Argonaut 1.3" - 2019-04-11
### Added
#### argonaut
- argonaut#5680 adding contributing guide and new license
- argonaut#5686 Forbid deployments outside given time frames
- argonaut#5699 Support products directly on OPSI client as well
- argonaut#5703 Add help for argonaut-user-reminder
- argonaut#5704 Add support for getting logs from OPSI webservice
- argonaut#5705 Support ping for several targets
- argonaut#5711 Add OPSI action for productOnClient_getObjects
- argonaut#5712 Extend manager email possibilities in user-reminder
- argonaut#5720 Support inheriting profile only for OPSI client config
- argonaut#5721 Support inheriting localboots from group
- argonaut#5723 Add a log of emails sent by argonaut-user-reminder
- argonaut#5736 Add new variables in FAI integration script
- argonaut#5737 Add new options in argonaut-fuse PXE file generation
- argonaut#5746 add back the script to convert debconf/preseed file to ldif
- argonaut#5747 Add a global log system to argonaut-user-reminder
### Changed
#### argonaut
- argonaut#3613 argonaut fuse module opsi should check if the system is lock or not
- argonaut#5689 making the ip non mandatory in the opsi backend plugin when people don 't manage dns and dhcp with opsi
- argonaut#5701 split posix and ppolicy treatment
- argonaut#5707 Some OPSI actions are not possible on OPSI server
- argonaut#5735 argonaut-fuse should adapt command line options to FAI version
### Fixed
#### argonaut
- argonaut#2240 OPSI should update actions when a new one is called
- argonaut#5461 Error with Ldap2zone slave when using multiple domains with a common reverse zone
- argonaut#5709 OPSI.update_or_insert duplicates host instead of renaming
- argonaut#5719 Argonaut doens't receive error when paquet failing to installing
## %"Argonaut 1.2.3" - 2019-01-10
### Changed
......
......@@ -129,7 +129,7 @@
.\" ========================================================================
.\"
.IX Title "ARGONAUT-CLIENT 1"
.TH ARGONAUT-CLIENT 1 "2018-09-26" "Argonaut 1.2.3" "Argonaut Documentation"
.TH ARGONAUT-CLIENT 1 "2019-04-03" "Argonaut 1.3" "Argonaut Documentation"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
......
......@@ -92,6 +92,9 @@ BEGIN
)],
'net' => [qw(
&argonaut_get_mac
)],
'utils' => [qw(
&argonaut_check_time_frames
)],
'config' => [qw(
&argonaut_read_config
......@@ -682,71 +685,85 @@ sub argonaut_get_generic_settings {
$filter = "($filter)";
}
my @attrs = ('dn', 'ipHostNumber', 'macAddress', 'gotoMode', 'fdMode', 'argonautDeploymentTimeframe');
for my $param (values(%{$params})) {
if (ref $param eq ref []) {
push @attrs, $param->[0]
} else {
push @attrs, $param;
}
}
my $mesg = $ldap->search( # perform a search
base => $ldap_base,
filter => "(&(objectClass=$objectClass)$filter)"
);
base => $ldap_base,
filter => "(&(objectClass=$objectClass)$filter)",
attrs => \@attrs
);
my $settings = {
};
if(scalar($mesg->entries)==1) {
$settings->{'dn'} = ($mesg->entries)[0]->dn();
$settings->{'mac'} = ($mesg->entries)[0]->get_value("macAddress");
$settings->{'ip'} = ($mesg->entries)[0]->get_value("ipHostNumber");
if (($mesg->entries)[0]->exists('fdMode')) {
$settings->{'locked'} = ($mesg->entries)[0]->get_value("fdMode") eq 'locked';
} elsif (($mesg->entries)[0]->exists('gotoMode')) {
$settings->{'locked'} = ($mesg->entries)[0]->get_value("gotoMode") eq 'locked';
} else {
$settings->{'locked'} = 0;
my $foundOC = 0;
if (scalar($mesg->entries) > 1) {
die "Several computers matches $filter.$die_endl";
} elsif (scalar($mesg->entries) == 0) {
unless ($inheritance) {
die "This computer ($filter) is not configured in LDAP to run this module (missing service $objectClass).$die_endl";
}
$mesg = $ldap->search( # Get the system object
base => $ldap_base,
filter => $filter,
attrs => \@attrs
);
if (scalar($mesg->entries) > 1) {
die "Several computers matches $filter.$die_endl";
} elsif (scalar($mesg->entries) < 1) {
die "There is no computer matching $filter.$die_endl";
}
} else {
$foundOC = 1;
while (my ($key,$value) = each(%{$params})) {
if (ref $value eq ref []) {
$settings->{"$key"} = ($mesg->entries)[0]->get_value(@$value);
} elsif (($mesg->entries)[0]->get_value("$value")) {
$settings->{"$key"} = ($mesg->entries)[0]->get_value("$value");
} else {
if (($mesg->entries)[0]->get_value("$value")) {
$settings->{"$key"} = ($mesg->entries)[0]->get_value("$value");
} else {
$settings->{"$key"} = "";
}
$settings->{"$key"} = "";
}
}
return $settings;
} elsif(scalar($mesg->entries)==0) {
unless ($inheritance) {
die "This computer ($filter) is not configured in LDAP to run this module (missing service $objectClass).$die_endl";
}
$mesg = $ldap->search( # perform a search
base => $ldap_base,
filter => $filter,
attrs => [ 'dn', 'macAddress', 'gotoMode', 'fdMode' ]
);
if (scalar($mesg->entries)>1) {
die "Several computers matches $filter.$die_endl";
} elsif (scalar($mesg->entries)<1) {
die "There is no computer matching $filter.$die_endl";
}
$settings->{'dn'} = ($mesg->entries)[0]->dn();
$settings->{'mac'} = ($mesg->entries)[0]->get_value("macAddress");
$settings->{'ip'} = ($mesg->entries)[0]->get_value("ipHostNumber");
if (($mesg->entries)[0]->exists('fdMode')) {
$settings->{'locked'} = ($mesg->entries)[0]->get_value("fdMode") eq 'locked';
} elsif (($mesg->entries)[0]->exists('gotoMode')) {
$settings->{'locked'} = ($mesg->entries)[0]->get_value("gotoMode") eq 'locked';
} else {
$settings->{'locked'} = 0;
}
$settings->{'dn'} = ($mesg->entries)[0]->dn();
$settings->{'mac'} = ($mesg->entries)[0]->get_value("macAddress");
$settings->{'ip'} = ($mesg->entries)[0]->get_value("ipHostNumber");
if (($mesg->entries)[0]->exists('fdMode')) {
$settings->{'locked'} = ($mesg->entries)[0]->get_value("fdMode") eq 'locked';
} elsif (($mesg->entries)[0]->exists('gotoMode')) {
$settings->{'locked'} = ($mesg->entries)[0]->get_value("gotoMode") eq 'locked';
} else {
$settings->{'locked'} = 0;
}
$settings->{'timeframes'} = ($mesg->entries)[0]->get_value('argonautDeploymentTimeframe', asref => 1);
my $dn = ($mesg->entries)[0]->dn();
my $mesgGroup = $ldap->search( # Get the group object
base => $ldap_base,
filter => "(&(objectClass=$objectClass)(member=$dn))",
attrs => \@attrs
);
if (scalar($mesgGroup->entries) == 1) {
if (not defined $settings->{'timeframes'}) {
$settings->{'timeframes'} = ($mesgGroup->entries)[0]->get_value('argonautDeploymentTimeframe', asref => 1);
}
my $dn = ($mesg->entries)[0]->dn();
my $mesg = $ldap->search( # perform a search
base => $ldap_base,
filter => "(&(objectClass=$objectClass)(member=$dn))",
attrs => [values(%{$params})]
);
if(scalar($mesg->entries)==1) {
}
if (not $foundOC) {
if (scalar($mesgGroup->entries) == 1) {
while (my ($key,$value) = each(%{$params})) {
if (($mesg->entries)[0]->get_value("$value")) {
$settings->{"$key"} = ($mesg->entries)[0]->get_value("$value");
if (ref $value eq ref []) {
$settings->{"$key"} = ($mesgGroup->entries)[0]->get_value(@$value);
} elsif (($mesgGroup->entries)[0]->get_value("$value")) {
$settings->{"$key"} = ($mesgGroup->entries)[0]->get_value("$value");
} else {
$settings->{"$key"} = "";
}
......@@ -755,9 +772,26 @@ sub argonaut_get_generic_settings {
} else {
die "This computer ($filter) is not configured in LDAP to run this module (missing service $objectClass).$die_endl";
}
} else {
die "Several computers matches $filter.$die_endl";
}
if ($inheritance == 2) {
# Get group values on top of normal ones
if (scalar($mesgGroup->entries) == 1) {
$settings->{"group"} = {};
while (my ($key,$value) = each(%{$params})) {
if (ref $value eq ref []) {
$settings->{"group"}->{"$key"} = ($mesgGroup->entries)[0]->get_value(@$value);
} elsif (($mesgGroup->entries)[0]->get_value("$value")) {
$settings->{"group"}->{"$key"} = ($mesgGroup->entries)[0]->get_value("$value");
} else {
$settings->{"group"}->{"$key"} = "";
}
}
return $settings;
}
}
return $settings;
}
#------------------------------------------------------------------------------
......@@ -783,7 +817,8 @@ sub argonaut_get_server_settings {
'delete_finished_tasks' => "argonautDeleteFinished",
'fetch_packages' => "argonautFetchPackages",
'interface' => "argonautWakeOnLanInterface",
'logdir' => "argonautLogDir"
'logdir' => "argonautLogDir",
'timeout' => "argonautTimeout"
},
$config,$ip
);
......@@ -839,6 +874,42 @@ sub argonaut_get_fuse_settings {
);
}
#------------------------------------------------------------------------------
# check if we are in an authorized time frame - Returns true if we are
#
sub argonaut_check_time_frames {
my ($settings) = @_;
if (not defined $settings->{'timeframes'}) {
return 1;
}
foreach my $frame (@{$settings->{'timeframes'}}) {
if ($frame =~ m/(\d\d)(\d\d)-(\d\d)(\d\d)/) {
my ($sec,$min,$hour) = gmtime(time());
my $begin = ($1 * 60) + $2;
my $end = ($3 * 60) + $4;
my $now = ($hour * 60) + $min;
if ($begin > $end) {
# Frame over midnight
$end += 24 * 60;
if ($now < $begin) {
$now += 24 * 60;
}
}
if ($now < $begin) {
# Too soon
next;
} elsif ($now > $end) {
# Too late
next;
}
return 1;
} else {
die "Invalid value in time frames: $frame".$die_endl;
}
}
return 0;
}
#------------------------------------------------------------------------------
#
# $search_result = Net::LDAP::Serach
......
......@@ -129,7 +129,7 @@
.\" ========================================================================
.\"
.IX Title "ARGONAUT.CONF 1"
.TH ARGONAUT.CONF 1 "2018-09-26" "Argonaut 1.2.3" "Argonaut Documentation"
.TH ARGONAUT.CONF 1 "2019-04-03" "Argonaut 1.3" "Argonaut Documentation"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
......
#!/usr/bin/perl
=head1 INTRODUCTION
Script that looks up the specified Debconf or CDebconf database
and converts the specified questions/templates from Debconf format
"822" to LDIF.
=head1 REFERENCES
https://forge.fusiondirectory.org/projects/debconf/wiki/Debconf2ldif
=cut
use warnings;
use strict;
use Getopt::Long qw/GetOptions/;
use Debconf::Format::822 qw//;
use Net::LDAP::Entry qw//;
use Net::LDAP::LDIF qw//;
my $RAW = qr/(?i:^jpegPhoto|;binary)/;
my %options= (
diff => undef,
base => '',
templates_tree => 'ou=templates',
questions_tree => 'ou=questions',
flag => 'preseed',
templates_file => '/var/log/installer/cdebconf/templates.dat',
questions_file => '/var/log/installer/cdebconf/questions.dat',
keys_file => undef,
prefer_preseed => 0,
);
unless( GetOptions(
'h|help' => \&usage,
'diff!' => \$options{diff},
'flag|f=s' => \$options{flag},
'base|b=s' => \$options{base},
'templates-tree|templates|t=s' => \$options{templates_tree},
'questions-tree|questions|q=s' => \$options{questions_tree},
'templates-file=s' => \$options{templates_file},
'questions-file=s' => \$options{questions_file},
'keys-file|k=s' => \$options{keys_file},
'prefer-preseed|p!' => \$options{prefer_preseed},
'debconf|d' => sub {
$options{templates_file}= '/var/cache/debconf/templates.dat';
$options{questions_file}= '/var/cache/debconf/config.dat';
$options{flag}= '';
},
'cdebconf|c' => sub {
$options{templates_file}= '/var/log/installer/cdebconf/templates.dat';
$options{questions_file}= '/var/log/installer/cdebconf/questions.dat';
$options{flag}= 'preseed';
}
)) { usage("Can't parse options: $!\n")};
sub usage
{
(@_) && $_[0] ne "h" && print STDERR "\n@_\n\n";
print STDERR << "EOF";
usage: $0 [-h] [--diff | --nodiff] [-f flag] [-b base] [-t templates-tree] [-q questions-tree] [--templates-file file] [--questions-file file] [-k keys-file] [-p | --noprefer-preseed] [-d | -c]
-h : this (help) message
--diff : to see diff between templates and questions
-f : flag (default: preseed)
-b : LDAP base, usually ou=templatename,<ldap_base>
-t : Templates branch (default: ou=templates)
-q : Questions branch (default: ou=questions)
--templates-file : Templates file path (default: /var/log/installer/cdebconf/templates.dat)
--questions-file : Questions file path (default: /var/log/installer/cdebconf/questions.dat)
-k : Preseed keys file path (optional)
-p : Allow type/value specs from preseed file to have precedence over values from templates/questions files
-d : Overrides flag and templates/questions paths to use /var/cache/debconf/
-c : Overrides flag and templates/questions paths to use /var/log/installer/cdebconf
EOF
exit -1;
}
my( %open, %data, %add_domain);
# Create domain_part, basically domain name prefixed by ',' for easy append
# onto relative DNs.
$options{domain_part}= ','. $options{base} if length $options{base};
#
# Parse templates and questions DB
#
for my $section( qw/templates questions/) {
my $ckey= $section.'_file';
open ($open{$section}, q{<}, $options{$ckey}) or
warn "Can't rdopen '$options{$ckey}' ($!). Can proceed without it ".
"if -k FILE is specified.\n";
while( my ($name, $entry)= Debconf::Format::822::read( '', $open{$section})) {
$data{$section}{$name}= $entry
}
}
#
# Now place all items found to $lists{$section} and @all
#
my ( @all, %diff);
my %lists;
for my $section( qw/templates questions/) {
$lists{$section}= { map{ $_ => 1} ( keys %{ $data{$section}})};
push @all, keys %{ $data{$section}};
}
#
# If we only want to see diff between templates and questions
# (this is a consistency check feature), calculate/print the diff and exit
#
if( $options{diff}) {
for my $key( @all) {
my ( $exists, $place)= (1, undef);
for my $group( keys %lists){
#print "GROUP $group KEY $key\n";
if( $exists) {
unless( $exists= exists $lists{$group}{$key}) {
$place= $group;
}
}
}
if( not $exists) {
$diff{ $key}= $place
}
}
print "Missing items:\n";
while( my( $k, $v)= each %diff) {
$v= ucfirst( substr $v, 0, 1);
print "$v: $k\n";
}
print "\n";
exit 0
}
#
# List of template/question items we want to display. (Defaults to
# all if no --keys-file specified).
# In addition to basic functionality (one key per line), a preseed file
# can also be passed in and it'll be handled properly. This has
# actually become the primary way of invoking it.
#
my %filter;
# Should the domain be appended to DN? Default yes. Turn off with
# special "DOMAIN=0/1" in preseed file. (We'll leave that working but
# undocumented since it has no use in the current design)
my $with_domain= 1;
if( my $f= $options{keys_file}) { # Specify subset of all keys
open (my $kin, q{<}, $f) or die "Can't rdopen '$f' ($!)\n";
while( $_= <$kin>) {
if( /^#.*DOMAIN=(\d)/) { $with_domain= $1}
next if /^[#\s]/; # hash space tab
chomp;
my @in= split /\s/, $_, 4;
# If preseed file was given in
if( defined $in[1]) {
$filter{$in[1]}= {
owners => $in[0],
type => $in[2],
value => $in[3],
};
# Should we add domain to this DN?
$add_domain{$in[1]}= $with_domain;
# Else if it was one-per-line spec
} else {
$filter{$in[0]}= {}
}
}
close $kin or die "Can't rdclose '$f' ($!)\n";
} else { # All keys
$filter{$_}= {} for @all
}
#
# 822->LDIF conversion
#
# Initialize
my ( $writer, $ts, $qs)= ( undef, '', '');
open my $tsh, '>', \$ts;
open my $qsh, '>', \$qs;
# Perform the work
for my $key( sort keys %filter) {
my $t= $data{templates}{$key};
my $q= $data{questions}{$key};
#use Data::Dumper;
#print Dumper $q;
# If preseed file was passed in and --prefer-preseed specified, allow
# type/value specs from preseed file to have precedence over values from
# /var/log/installer/cdebconf/...
# (The important one here is primarily 'value' field, but we also pick up
# the type not to end up in a situation where we overwrite the value but
# miss to spot that the type has changed as well-- it shouldn't happen
# generally, but let's be on the cautious side).
$$t{fields}{type}= $filter{$key}{type} if
( $options{prefer_preseed} and defined $filter{$key}{type}) or
not defined $$t{fields}{type};
$$q{fields}{value}= $filter{$key}{value} if
( $options{prefer_preseed} and defined $filter{$key}{value}) or
not defined $$q{fields}{value};
# Here we know that $filter{$key}{owners} is a string and only one value
# because that comes from the preseed file.
# Note that owner is substituted only if no owner is found in the
# installer logs. The -p option has no effect on owner.
{ my $owner= $filter{$key}{owners} || 'd-i';
$$q{owners}= { $owner => 1} unless(
defined $$q{owners} and keys %{ $$q{owners}});
}
my $domain_part= $add_domain{$key} ? $options{domain_part} : '';
# Turn Debconf Template into LDAP form
my $te= new Net::LDAP::Entry;
$te->dn( "cn=$key,$options{templates_tree}$domain_part");
$te->add( objectClass => [ 'top', 'debConfDbEntry']);
$te->add( cn => $key);
$te->add( choices => [ $$t{fields}{choices}])
if defined $$t{fields}{choices};
$te->add( default => [ $$t{fields}{default}])
if defined $$t{fields}{default};
$te->add( description => [ $$t{fields}{description}])
if defined $$t{fields}{description};
$te->add( extendedDescription => [ $$t{fields}{extended_description}])
if defined $$t{fields}{extended_description};
$te->add( type => [ $$t{fields}{type}])
if defined $$t{fields}{type};
$writer= new Net::LDAP::LDIF( $tsh, 'w', change => 0, raw => $RAW);
if(!( $writer->write_entry( $te))) {
warn "Can't write_entry('$te->dn') to scalar\n";
}
$writer->done;
# Turn Debconf Question into LDAP form
my $qe= new Net::LDAP::Entry;
$qe->dn( "cn=$key,$options{questions_tree}$domain_part");
$qe->add( objectClass => [ 'top', 'debConfDbEntry']);
$qe->add( cn => $key);
$qe->add( flags => [ keys %{$$q{flags}}, $options{flag}])
if keys %{$$q{flags}} or $options{flag};
#print STDERR "1) $key ", keys %{$$q{owners}}, "\n";
#print STDERR "2) $key ", $filter{$key}{owners}, "\n";
$qe->add( owners => [ keys %{$$q{owners}}])
if keys %{$$q{owners}};