Помогите распилить (доработать) perl скрипт

Модератор: Модераторы разделов

Аватара пользователя
MozG1986
Сообщения: 103
ОС: Mageia5, Mageia6

Помогите распилить (доработать) perl скрипт

Сообщение MozG1986 »

Привожу листинг одного полезного perl-скрипта.
Его задача - реализовать аналог DDNS-updates для зон bind, хранящихся в ldap. Сам bind, насколько мне известно, такого пока не умеет.
Очень полезно для MDS (Mandriva DIrectory Service), если хочется резольвить имена узлов с динамически выданным ip адресом.

Оригинал скрипта взят отсюда: http://bind9-ldap.bayour.com/

В текущем варианте скрипт работоспособен, но хочется сделать его универсальным и опакетить.
Текущие его недостатки:

1) Редактирование переменных в самом скрипте, скрипт так и попортить недолго
Нужно вынести переменные в отдельный конфиг и поместить его например в /etc/dhcp2ldap.conf
Необъявленным в конфиге переменным присваивать значеня по умолчанию.
При отсутствии конфига завершать работу.
Нужно предусмотреть в качестве параметров коммандной строки указание своего пути для конфига.

2) В текущем варианте он тупо читает все записи в dhcpd.leases и оформляет их в ldap в forward и reverse зонах, при чем при обновлении записи он сначала удаляет ее, а потом заново записывает.
Это приводит к удалению и повторному добавлению записей об узлах, адреса которых закреплены в dhcpd по mac-адресу. В MDS к таким записям можно добавлять cname, которые потом пропадут при добавлении.
Нужно будет контролировать кем созданы записи и не трогать записи, созданные из веб-интерфейса MDS.

3) Этот недостаток выходит из второго. Читаются все записи в dhcpd.leases, в том числе и устаревшие, что приводит к тому, что зона в DNS заполняется ненужными устаревшими данными.
Веб-интерфейс MDS в содержимом зоны dhcp выводит только актуальные записи, я разберусь как он фильтрует их, а потом нужно будет реализовать такой же миханизм фильтрации в скрипте.
Наверняка недостатки 2 и 3 устранятся одной общей для них проверкой.

4) В изначальном варианте скрипт обслуживал подсеть с маской /16, я его подправил для работы с маской подсети /24
В идеале маску подсети нужно читать из конфига.

Сам скрипт (работоспособен с dns и dhcp - сервером, настроеным для MDS):
Spoiler

Код: Выделить всё

#!/usr/bin/perl
$CONFIGFILE="/etc/dhcp2ldap.conf";



####################################################################
#                   Edit These for Your Domain                     #
####################################################################

# dhcpd.leases file location
# Default:
# $LEASES = "/var/lib/dhcpd/dhcpd.leases"
$LEASES = "/var/lib/dhcpd/dhcpd.leases";

# DC Base
# Default:
# $BASE="dc=localhost,dc=localdomain";
$BASE="dc=localhost,dc=localdomain";

# Domain Name
# Default:
# $DOMAIN = "localhost.localdomain";
$DOMAIN = "localhost.localdomain";

# Netmask
# Valid values is "8", "16" or "24"
# Default:
# $NETMASK = "24"
$NETMASK = "24"

# Reverse zone Name
# Default:
# $REVERSE = "1.168.192.in-addr.arpa";
$REVERSE = "1.168.192.in-addr.arpa";

# Full forward zone base in LDAP
# Default:
# $FORWARD_BASE = ",ou=$DOMAIN,ou=$DOMAIN,ou=dns,$BASE";
$FORWARD_BASE = ",ou=$DOMAIN,ou=$DOMAIN,ou=dns,$BASE";

# Full reverse zone base in LDAP
# Default:
# $REVERSE_BASE = ",ou=$REVERSE,ou=$DOMAIN,ou=dns,$BASE";
$REVERSE_BASE = ",ou=$REVERSE,ou=$DOMAIN,ou=dns,$BASE";

# Login to bind to LDAP
# Default:
# $LOGIN="manager";
$LOGIN="manager";

# Password for $LOGIN
# Default:
# $PASSWORD = "secret";
$PASSWORD = "secret";

# LDAP hostname
# Default:
# $LDAP_HOST = 'localhost';
$LDAP_HOST = 'localhost';

# The description see below
# Default:
# $UPDATE_TIME = 30;    # In Seconds
$UPDATE_TIME = 30;    # In Seconds

# Set this to positive if you would like dhcp2ldap
# to periodically re-read the entire leases file
# even if the time stamp hasn't changed
#
# It will perform the auto-verify after
# ($AUTO_VERIFY * $UPDATE_TIME) seconds
#
# This is probably a good idea unless your parse times
# are incredibly high
$AUTO_VERIFY = 0;

####################################################################
#                   Do Not Edit Below This Line                    #
####################################################################

use IO::Socket;
use Net::LDAP;



$USER = "cn=$LOGIN,$BASE";

print "Initializing LDAP Connection...";
$ldap = Net::LDAP->new($LDAP_HOST) or die "$@";
$ldap->bind($USER, password => $PASSWORD) or die "$!\n";
print "Done\n";

$params = shift;

if($params eq "-d"){
        daemonize();
}
if($params eq "-h"){
        usage();
        exit;
}

while(1){

        if(changed($LEASES)){
                parse($LEASES);
                do_stuff();
        }

        sleep $UPDATE_TIME;
}


sub changed{
        my $file = shift;
        my $curstat = (stat($file))[9];

        if($AUTO_VERIFY){
                $check_count++;
        }

        if($oldstat != $curstat || (($check_count == $AUTO_VERIFY) && $AUTO_VERIFY)){
                $oldstat = $curstat;
                $check_count = 0;

                print "Timestamp change or AUTO_VERIFY triggered...\n";
                return 1;
        }
        else{
                return 0;
        }
}


sub daemonize{
        chdir '/';
        umask 0;
        open STDIN, '/dev/null';
        open STDOUT, '/dev/null';
        open STDERR, '/dev/null';

        if(fork()){
                exit;
        }
        else{
                setsid;
                return;
        }
}

sub parse{
        my $ip,$hostname;

        open IN, shift or die "Could not open leases file: $!\n";
        while($_ = <IN>){
                if(/^#/){
                        next;
                }
                chomp $_;
                if(/lease/){
                        /\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}/;
                        $ip = $&;
                        while(!(/\}/)){
                                $_ = <IN>;
                                if(/client-hostname/){
                                        /\".*\"/;
                                        $_ = $&;
                                        /\w+\S*\w+/;
                                        $_ = $&;
                                        $hostname = lc;
                                        $hosts{$hostname} = $ip;
                                }
                        }
                }
                $line = "";

        };
        close IN;
}


sub do_stuff{
        my $readd, $host, $ip, $hostname;
        while(($host,$ip) = each(%hosts)){
                $hostname = $host . "." . $DOMAIN . ".";
                $lookup = "";
                eval{$lookup = inet_ntoa((gethostbyname("$host.$DOMAIN"))[4]); };
                ($first,$second,$third,$fourth) = split(/\./,$ip);

                if($lookup eq $ip){
                        next;
                }

                elsif($lookup ne $ip){
                        print "Removing inaccurate records for $host...\n";
                        $result = $ldap->delete("relativeDomainName=$host" . $FORWARD_BASE);
                        $result->code && warn "failed to remove entry: ",  $result->error ;
                        $result = $ldap->delete("relativeDomainName=$fourth" . $REVERSE_BASE);
                        $result->code && warn "failed to remove entry: ", $result->error ;
                        $readd = 1;
                }

                if(!$lookup || $readd){
                        print "Adding entry for $host at $ip...\n";
                        $result = $ldap->add( "relativeDomainName=$host" . $FORWARD_BASE,
                                attr => [
                                        'relativeDomainName'=> $host,
                                        'objectClass'=> ['top','dNSZone'],
                                        'dNSTTL' => '7200',
                                        'zoneName' => $DOMAIN,
                                        'aRecord' => $ip
                                        ]
                                );
                        $result->code && warn "failed to add entry: ", $result->error ;

                        $result = $ldap->delete("relativeDomainName=$fourth" . $REVERSE_BASE);
                        $result->code && warn "failed to remove entry: ", $result->error ;

                        $result = $ldap->add( "relativeDomainName=$fourth" . $REVERSE_BASE,
                                attr => [
                                        'relativeDomainName'=> "$fourth",
                                        'objectClass'=> ['top','dNSZone'],
                                        'dNSTTL' => '7200',
                                        'zoneName' => $REVERSE,
                                        'pTRRecord' => $hostname
                                        ]
                                );
                        $result->code && warn "failed to add entry: ", $result->error ;
                        $readd = 0;
                }


        }
}

sub usage{
        print <<EOF;

dhcp2ldapd v1.1: Dynamic DNS Updates for the Bind9 LDAP backend
Copyright 2005 Travis Groth <travis\@netfoo.org> under the GNU GPL

Usage:
        dhcp2ldapd [-d | -h]

-d runs dhcp2ldap in daemon mode
-h displays this help message
-c <path-to-configfile>
Please edit the config variables before running!

EOF
}


#############################################################################
#This program is free software; you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation; either version 2 of the License, or
#(at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#############################################################################


Описание сервиса для управления скриптом:
Spoiler

Код: Выделить всё

[Unit]
Description=dhcp2ldap
After=dhcpd.service
After=named.service
After=ldap.service

[Service]
EnvironmentFile=-/etc/dhcp2ldap.conf
PIDFile=/var/run/dhcp2ldap.pid
ExecStart=/usr/sbin/dhcp2ldap

[Install]
WantedBy=multi-user.target


Соответственно скрипт тогда должен лежать в /usr/sbin, а описание сервиса в /usr/lib/systemd/system
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: Помогите распилить (доработать) perl скрипт

Сообщение NickLion »

1) Скрипт не смотрел, но поставленную задачу можно решить как-то так (скрипт опускает комментарии #, а также убирает пробелы в начале и конце строк):

Код: Выделить всё

#!/usr/bin/perl -w

use Data::Dumper;

my %conf = (
# place default values here
    'VAR111' => 5,
    'special' => 'Tugrik',
    'ttt' => 'uuu',
    'someVarX' => '/usr/bin/qmake'
);

if (open(CONF, '<aa.conf')) {
    while (<CONF>) {
        next if $_ =~ /^\s*#/;
        next unless $_ =~ /^\s*(\w+)\s*=\s*(.+?)\s*$/;
        $conf{$1} = $2;
    }
}

print Dumper(\%conf);

Тогда, если aa.conf:

Код: Выделить всё

# some comment here
PARAM1 = Hello, World!
SUPER_VAR=192.168.0.0/24

# yet another comment
VAR111   =         5
VAR777= spaces after text


someVarX=/usr/bin/perl

То выхлоп будет:

Код: Выделить всё

$VAR1 = {
          'SUPER_VAR' => '192.168.0.0/24',
          'VAR111' => '5',
          'someVarX' => '/usr/bin/perl',
          'PARAM1' => 'Hello, World!',
          'VAR777' => 'spaces after text',
          'ttt' => 'uuu',
          'special' => 'Tugrik'
        };

В коде переменные использовать как: $conf{VAR111}
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 21504
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Помогите распилить (доработать) perl скрипт

Сообщение Bizdelnick »

А зачем велосипеды? Есть вагон модулей Config::*, есть YAML::XS, есть ещё куча подходящих парсеров. Выбрать оптимальный формат, заюзать модуль и загружать кофиг в хеш одной строкой.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
Аватара пользователя
MozG1986
Сообщения: 103
ОС: Mageia5, Mageia6

Re: Помогите распилить (доработать) perl скрипт

Сообщение MozG1986 »

Если в баше я еще более-менее могу что-то набросать, то perl вообще не знаю((( Изучать его ради правки одного скрипта - извините, нет. Но вот если добрые люди помогут его разодрать и допилить - готов опакетить его и выслать изменения разработчикам скрипта с указанием авторов, принявшимх участие в доработке.
Повторяю, сам скрипт исправно работает на моей машине, просто хочется сделать его лучше.
Спасибо сказали:
Аватара пользователя
MozG1986
Сообщения: 103
ОС: Mageia5, Mageia6

Re: Помогите распилить (доработать) perl скрипт

Сообщение MozG1986 »

Допилил))))
Протестируйте, кому не лень.

/usr/sbin/dhcp2ldap
Spoiler
#!/usr/bin/perl


use IO::Socket;
use Net::LDAP;
use Config::IniFiles;

$CONFIGFILE="/etc/dhcp2ldap.conf";
$printconf=0;
$index=0;
foreach (@ARGV) {
if($_ eq "-c"){
$CONFIGFILE = @ARGV[$index+1];
print "Using config File $CONFIGFILE \n";
}
if($_ eq "-p"){
$printconf=1;
}
if($_ eq "-d"){
daemonize();
}
if($_ eq "-h"){
usage();
exit;
}
$index++;
}

unless (-e "$CONFIGFILE"){
print "Config File not found \n";
exit;
}

# Read config
my $cfg = Config::IniFiles->new( -file => "$CONFIGFILE" );

# File dhcpd.leases location
if ( defined ($cfg->val( 'config', 'LEASES' ))){
$LEASES=$cfg->val( 'config', 'LEASES' );
} else {
$LEASES = "/var/lib/dhcpd/dhcpd.leases"};

# DC Base
if ( defined ($cfg->val( 'config', 'BASE' ))){
$BASE=$cfg->val( 'config', 'BASE' )
} else {
$BASE="dc=localhost,dc=localdomain"};

# Domain Name
if ( defined ($cfg->val( 'config', 'DOMAIN' ))){
$DOMAIN=$cfg->val( 'config', 'DOMAIN' );
} else {
$DOMAIN = "localhost.localdomain"};

# Reverse zone name
if ( defined ($cfg->val( 'config', 'REVERSE' ))){
$REVERSE=$cfg->val( 'config', 'REVERSE' );
} else {
$REVERSE = "0.168.192.in-addr.arpa"};

# Subnet Mask
if ( defined ($cfg->val( 'config', 'MASK' ))){
$MASK=$cfg->val( 'config', 'MASK' );
} else {
$MASK = "24"};

# Full forward zone name base in LDAP
if ( defined ($cfg->val( 'config', 'FORWARD_BASE' ))){
$FORWARD_BASE = $cfg->val( 'config', 'FORWARD_BASE' );
} else {
$FORWARD_BASE = "ou=$DOMAIN,ou=$DOMAIN,ou=dns,$BASE"};

# Full reverse zone name in LDAP
if ( defined ($cfg->val( 'config', 'REVERSE_BASE' ))){
$REVERSE_BASE=$cfg->val( 'config', 'REVERSE_BASE' );
} else {
$REVERSE_BASE = "ou=$REVERSE,ou=$DOMAIN,ou=dns,$BASE"};

# Login to bind to LDAP
if ( defined ($cfg->val( 'config', 'LOGIN' ))){
$LOGIN=$cfg->val( 'config', 'LOGIN' );
} else {
$LOGIN="manager"};

# Password for $LOGIN
if ( defined ($cfg->val( 'config', 'PASSWORD' ))){
$PASSWORD=$cfg->val( 'config', 'PASSWORD' );
} else {
$PASSWORD = "secret"};

# LDAP hostname
if ( defined ($cfg->val( 'config', 'LDAP_HOST' ))){
$LDAP_HOST=$cfg->val( 'config', 'LDAP_HOST' );
} else {
$LDAP_HOST = 'localhost'};

# The description see below
if ( defined ($cfg->val( 'config', 'UPDATE_TIME' ))){
$UPDATE_TIME=$cfg->val( 'config', 'UPDATE_TIME' );
} else {
$UPDATE_TIME = 30}; # In Seconds

# Set this to positive if you would like dhcp2ldap
# to periodically re-read the entire leases file
# even if the time stamp hasn't changed
#
# It will perform the auto-verify after
# ($AUTO_VERIFY * $UPDATE_TIME) seconds
#
# This is probably a good idea unless your parse times
# are incredibly high
if ( defined ($cfg->val( 'config', 'AUTO_VERIFY' ))){
$AUTO_VERIFY=$cfg->val( 'config', 'AUTO_VERIFY' );
} else {
$AUTO_VERIFY = 0};

$USER = "cn=$LOGIN,$BASE";

if ($printconf){
print "LEASES = $LEASES \n";
print "BASE = $BASE \n";
print "DOMAIN = $DOMAIN \n";
print "REVERSE = $REVERSE \n";
print "MASK = $MASK \n";
print "FORWARD_BASE = $FORWARD_BASE \n";
print "REVERSE_BASE = $REVERSE_BASE \n";
print "LOGIN = $LOGIN \n";
print "PASSWORD = $PASSWORD \n";
print "LDAP_HOST = $LDAP_HOST \n";
print "UPDATE_TIME = $UPDATE_TIME \n";
print "AUTO_VERIFY = $AUTO_VERIFY \n";
exit;
}

print "Initializing LDAP Connection...";
$ldap = Net::LDAP->new($LDAP_HOST) or die "$@";
$ldap->bind($USER, password => $PASSWORD) or die "$!\n";
print "Done\n";


while(1){

if(changed($LEASES)){
parse($LEASES);
do_stuff();
}

sleep $UPDATE_TIME;
}




sub changed{
my $file = shift;
my $curstat = (stat($file))[9];

if($AUTO_VERIFY){
$check_count++;
}

if($oldstat != $curstat || (($check_count == $AUTO_VERIFY) && $AUTO_VERIFY)){
$oldstat = $curstat;
$check_count = 0;

print "Timestamp change or AUTO_VERIFY triggered...\n";
return 1;
}
else{
return 0;
}
}


sub daemonize{
chdir '/';
umask 0;
open STDIN, '/dev/null';
open STDOUT, '/dev/null';
open STDERR, '/dev/null';

if(fork()){
exit;
}
else{
setsid;
return;
}
}

sub parse{
my $ip,$hostname;

open IN, shift or die "Could not open leases file: $!\n";
while($_ = <IN>){
if(/^#/){
next;
}
chomp $_;
if(/lease/){
/\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}/;
$ip = $&;
while(!(/\}/)){
$_ = <IN>;
if(/client-hostname/){
/\".*\"/;
$_ = $&;
/\w+\S*\w+/;
$_ = $&;
$hostname = lc;
$hosts{$hostname} = $ip;
}
}
}
$line = "";

};
close IN;
}


sub do_stuff{
my $readd, $host, $ip, $hostname, $addr;
while(($host,$ip) = each(%hosts)){
$hostname = $host . "." . $DOMAIN . ".";
$lookup = "";
eval{$lookup = inet_ntoa((gethostbyname("$host.$DOMAIN"))[4]); };
($first,$second,$third,$fourth) = split(/\./,$ip);
if ($MASK eq "24") {
$addr=$fourth;
}
if ($MASK eq "16") {
$addr=$fourth . "." . $third;
}
if ($MASK eq "8") {
$addr=$fourth . "." . $third . "." . $second;
}
if($lookup eq $ip){
next;
}

elsif($lookup ne $ip){
print "Removing inaccurate records for $host...\n";
$result = $ldap->delete("relativeDomainName=$host" . "," . $FORWARD_BASE);
$result->code && warn "failed to remove entry: ", $result->error ;
$result = $ldap->delete("relativeDomainName=$addr" . "," . $REVERSE_BASE);
$result->code && warn "failed to remove entry: ", $result->error ;
$readd = 1;
}

if(!$lookup || $readd){
print "Adding entry for $host at $ip...\n";
$result = $ldap->add( "relativeDomainName=$host" . "," . $FORWARD_BASE,
attr => [
'relativeDomainName'=> $host,
'objectClass'=> ['top','dNSZone'],
'dNSTTL' => '7200',
'zoneName' => $DOMAIN,
'aRecord' => $ip
]
);
$result->code && warn "failed to add entry: ", $result->error ;

$result = $ldap->delete("relativeDomainName=$addr" . "," . $REVERSE_BASE);
$result->code && warn "failed to remove entry: ", $result->error ;

$result = $ldap->add( "relativeDomainName=$addr" . "," . $REVERSE_BASE,
attr => [
'relativeDomainName'=> "$addr",
'objectClass'=> ['top','dNSZone'],
'dNSTTL' => '7200',
'zoneName' => $REVERSE,
'pTRRecord' => $hostname
]
);
$result->code && warn "failed to add entry: ", $result->error ;
$readd = 0;
}


}
}


sub usage{
print <<EOF;

dhcp2ldapd v1.1: Dynamic DNS Updates for the Bind9 LDAP backend
Copyright 2005 Travis Groth <travis\@netfoo.org> under the GNU GPL

Usage:
dhcp2ldapd [-d | -h]

-d runs dhcp2ldap in daemon mode
-h displays this help message
-c <path> use custom config file
-p read config and exit
Please edit the config variables before running!

EOF
}


#############################################################################
#This program is free software; you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation; either version 2 of the License, or
#(at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#############################################################################


/etc/dhcp2ldap.conf
Spoiler
[config]
# File dhcpd.leases location
# Default:
# $LEASES = "/var/lib/dhcpd/dhcpd.leases"
LEASES = /var/lib/dhcpd/dhcpd.leases

# DC Base
# Default:
# $BASE="dc=localhost,dc=localdomain";
BASE = dc=localhost,dc=localdomain

# Domain Name
# Default:
# $DOMAIN = "localhost.localdomain";
DOMAIN = localhost.localdomain

# Reverse zone name
# Default:
# $REVERSE = "1.168.192.in-addr.arpa";
REVERSE = 0.168.192.in-addr.arpa

# NetMask. Use 8, 16 or 24
MASK = 24

# Full forward zone name base in LDAP
# Default:
# $FORWARD_BASE = ",ou=$DOMAIN,ou=$DOMAIN,ou=dns,$BASE";
# FORWARD_BASE = ou=localhost.localdomain,ou=localhost.localdomain,ou=dns,dc=localhost,dc=localdo
main

# Full reverse zone name in LDAP
# Default:
# $REVERSE_BASE = ",ou=$REVERSE,ou=$DOMAIN,ou=dns,$BASE";
# REVERSE_BASE = ou=10.168.192.in-addr.arpa,ou=localhost.localdomain,ou=dns,dc=localhost,dc=localdomain

# Login to bind to LDAP
# Default:
# $LOGIN="manager";
LOGIN = admin

# Password for $LOGIN
# Default:
# $PASSWORD = "secret";
PASSWORD = password

# LDAP hostname
# Default:
# $LDAP_HOST = 'localhost';
LDAP_HOST = localhost

# The description see below
# Default:
# $UPDATE_TIME = 30; # In Seconds
UPDATE_TIME = 30

# Set this to positive if you would like dhcp2ldap
# to periodically re-read the entire leases file
# even if the time stamp hasn't changed
#
# It will perform the auto-verify after
# ($AUTO_VERIFY * $UPDATE_TIME) seconds
#
# This is probably a good idea unless your parse times
# are incredibly high
AUTO_VERIFY = 0



/usr/lib/systemd/system/dhcp2ldap.service
Spoiler
[Unit]
Description=dhcp2ldap
After=dhcpd.service
After=named.service
After=ldap.service

[Service]
EnvironmentFile=-/etc/dhcp2ldap.conf
PIDFile=/var/run/dhcp2ldap.pid
ExecStart=/usr/sbin/dhcp2ldap

[Install]
WantedBy=multi-user.target
Спасибо сказали: