Perl для системного администрирования

Создание нескольких конфигурационных файлов



Создание нескольких конфигурационных файлов

После того как написан верный заголовок для конфигурационных файлов, осталось решить еще одну проблему. Правильно настроенный DNS-c-зрвер поддерживает как прямое преобразование (имен в IP-адреса), так и обратное преобразование (IP-адресов в имена) для каждого домена (или зоны), который он обслуживает. Для этого надо иметь два конфигурационных файла на каждую зону. Самый лучший способ их синхронизировать - создавать файлы в одно и то же время.

Рассмотрим в данной главе последний пример генерирования файлов, поэтому соберем воедино все, что обсуждали раньше. Приведенный сценарий использует для создания конфигурационных файлов зоны DNS простой файл базы данных.

Чтобы не усложнять сценарий, я сделал ряд предположений относительно данных, самые важные из которых касаются топологии сети и пространства имен. Я считаю, что сеть состоит из одной подсети класса С с одной зоной DNS. В результате, необходимо создать один файл для прямого преобразования имен и один для обратного. Добавить код для работы с несколькими подсетями и зонами (т. е. создать отдельные файлы для каждой) будет несложно.

Вот, вкратце, что мы делаем:

  1. Считываем файл базы данных в хэш хэшей, проверяя при этом данные.
  2. Генерируем заголовок.
  3. Записываем данные в файл для прямого преобразования (из имен в IP-адреса) и помещаем его под контроль RCS.
  4. Записываем данные в файл для обратного преобразования (из IP-адресов в имена) и помещаем его под контроль RCS.

Вот как выглядит пример и получаемые в результате файлы:

use Res;

Sdatafile = "./database";



база данных узлов

Soutputfile = "zone.$$";

временный файл для вывода

$target = "zone.db";

получаемый файл

$revtarget = "rev.db";

получаемый файл для обратного преобразования

$defzone = ".oog.org";

# создаваемая по умолчанию зона

Srecordsep = "-=-\n";

получаем текущую дату в формате YYYYMMDD

@localtime = localtime;

$today = sprintf("%04d%02d%02d",$localtime[5]+1900,

$localtime[4]+l,

$localtime[3]);

имя пользователя, как в NT/2000, так и

Unix $user = ($"0 eq "MSWin32")? $ENV{USERNAME} :

(getpwuid($<))[6]." (".(getpwuid($<))[0].")"; $/ = Srecordsep;

считываем файл базы данных

open(DATA, Sdatafile) or die "Ошибка! Невозможно открыть datafile;$!\n";

while (<DATA>) {

chomp; # удаляем разделитель записей

разбиваем на key!,value"! @record = split /:\s*|\n/m;

$record ={}; # создаем ссылку на пустой хэш

%{$record} = @record;

# заполняем его значениями из ©record

в ищем ошибки в именах узлов

if ($record->{name} =" /["-.a-zA-ZO-9]/) {

warn "!!!! ",$record->{name} .

встретились недопустимые в именах узлов символы, пропускаем.. Дп";

next; }

# ищем ошибки в псевдонимах

if ($record->{aliases} =" /["-.a-zA-ZO-9\s]/) {

warn "!!!! " . $record->{name} .

встретились недопустимые в псевдонимах символы, пропускаем.. .\п";

next; }

# ищем пропущенные адреса unless ($record->{address}) {

warn "!!!! " . $record->{name} .

нет IP-адреса, пропускаем.. Дп"; next; }

# ищем повторяющиеся адреса

if (defined $addrs{$record->{address}}) {

warn "!!!! Повторение IP-адреса:" . $record->{name}.

" & " . $addrs{$record->{address}} . ", пропускаем. . Дп"; next;

>

else {

$addrs{$record->{address}} = $record->{name};

}

$entries{$record->{name}} = Srecord; # добавляем это в хэш хэшей

}

close(DATA);

Sheader = &GenerateHeader;

создаем файл прямого преобразования open(OUTPUT,"> Soutputfile") or

die "Ошибка! Невозможно записать в $outputfile:$!\n": print OUTPUT $header;

foreach my Sentry (sort byaddress keys %entries) { print OUTPUT

"; Владелец -- ",$entries{$_}->{owner},"

(", $entries{$entry}->{department},"):

Sentries{$entry}->{building},"/", Sentries{$entry}->{room), "\n";

tt выводим запись А

printf OUTPUT "%-20s\tIN A %s\n",

Sentries{Sentry}->{name},Sentries{Sentry>->{address};

it выводим записи CNAMES (псевдонимы)

if (defined $entries{$entry}->{aliases}){

foreach my Salias (split(p ',$entries{$entry}->{aliases}))

{

printf OUTPUT "%-20s\tIN CNAME %s\n",Salias,

Sentries{Sentry}->{name}: > }

print OUTPUT "\n"; }

close(OUTPUT);

Rcs->bindir('/usr/local/bin'); my Srcsobj = Rcs->new;

$rcsobj->file($target);

$rcsobj->co('-!');

rename($outputfile,Starget) or

die "Ошибка! Невозможно переименовать

Soutputfile в Starget:$!\n": $rcsobj->ci("-u","-m"."

Преобразовано пользователем Suser в ".scalar(localtime));

ft создаем файл обратного преобразования open(OUTPUT,"> Soutputfile") or

die "Ошибка! Невозможно записать в $outputfile:$!\n"; print OUTPUT Sheader;

foreach my Sentry (sort byaddress keys %entries) { print OUTPUT

"; Владелец-- ",$entries{$entry}->{owner}," (",

Sentries{Sentry}->{department},"): ",

$entries{$entry}->{building},"/",

Sentries{Sentry}->{room},"\n";

printf OUTPUT "%-3d\tIN PTR %s$defzone.\n\n",

(split/\./,$entries{$entry}->{address})[3],

$entnes{$entry}->{name};

clOse(OUTPUT);

$rcsobj->file($revtarget);

$rcsob]->co( '-1');

предполагаем, что целевой файл по крайней

# мере один раз извлекался из репозитория rename($outputfile,Srevtarget) or

die "Ошибка! Невозможно переименовать

Soutputfile в Srevtarget;$!\n";

$rcsobj->ci("-u","-m"."Преобразовано пользователем

$user в ".scalar(localtime));

sub GenerateHeader{ my(Sheader);

if (open(OLDZONE,$target)){ while (<OLDZONE>) {

next unless (/(\d{8}).«serial/); $oldserial = $1; last; }

close(OLDZONE); } else {

Soldserial = "000000"; }

$olddate = substr($oldserial,0,6);

$count = (Solddate == $today) ? substr($oldserial,6,2)+1 : 0;

Sserial = sprintf("%6d%02d",$today,Scount);

$header .= "; файл зоны dns - СОЗДАН $0\п";

Sheader .= "; HE РЕДАКТИРУЙТЕ ВРУЧНУЮ!\n;\n";

Sheader .= "; Преобразован пользователем Suser в ".scalar(localtime)."\n;\n":

П подсчитываем число узлов в каждом отделе foreach Sentry (keys %entries){

$depts{$entries{$entry)->{department}}++; } foreach $dept (keys %depts) {

Sheader .= "; в отделе $dept $depts{$dept} машин.\n";

X

Sheader ,= "; общее число машин: ".scalar(keys %entries)."\nff\n\n";

Sheader .= «"EOH";

@ IN SOA dns.oog.org. hostmaster.oog.org. (

$serial : serial 10800 ; refresh 3600 : retry 604800 ; expire 43200) ; TTL

@ IN NS dns.dog.org

ЕОН

return $header; }

sub byaddress {

@a = split(/\./,$entries{$a}->{address});

@b = split(/\./,$entries{$b}->{address});

($a[0]<=>$b[0]) ||

($a[1]<=>$b[1]) ||

($a[2]<=>$b[2]) ||

($a[3]<=>$b[3]); }

Вот какой файл получается для прямого преобразования (zone.db):

файл зоны dns - СОЗДАН createdns НЕ РЕДАКТИРУЙТЕ ВРУЧНУЮ!

Преобразован пользователем David N. Blank-Edelman (dnb); в Fri May 29 15:46:46 1998

в отделе design 1 машин. в отделе software 1 машин, в отделе IT 2 машин, общее число машин: 4

@ IN SOA dns.oog.org. hostmaster.oog.org. (

1998052900 ; serial 10800 ; refresh 3600 ; retry 604800 ; expire 43200) ; TTL

@ IN NS dns.oog.org.

; Владелец -- Cindy Coltrane (marketing): west/143 bendir IN A 192.168.1.3

ben IN CNAME bendir

bendoodles IN CNAME bendir

; Владелец -- David Davis (software): main/909 shimmer IN A 192.168.1.11

shim IN CNAME shimmer

shimmy IN CNAME shimmer

shimmydoodles IN CNAME shimmer

; Владелец -- Ellen Monk (design): main/1116 Sulawesi IN A 192.168.1.12

sula IN CNAME Sulawesi

su-lee IN CNAME Sulawesi

; Владелец -- Alex Rollins (IT): main/1101 sender IN A 192.168.1.55

sandy IN CNAME sander

micky IN CNAME sander

mickydoo IN CNAME sander

А вот как выглядит файл для обратного преобразования (rev.db):

файл зоны dns - СОЗДАН createdns НЕ РЕДАКТИРУЙТЕ ВРУЧНУЮ!

Преобразован пользователем David N. Blank-Edelman (dnb); в Fri May 29 15:46:46 1998

в отделе design 1 машин, в отделе software 1 машин, в отделе IT 2 машин, общее число машин: 4

@ IN SOA dns.oog.org. hostmaster.oog.org. (

1998052900 ; serial 10800 ; refresh 3600 ; retry 604800 ; expire 43200) ; TTL

@ IN NS dns.oog.org.

; Владелец -- Cindy Coltrane (marketing): west/143 3 IN PTR bendir.oog.org.

; Владелец -- David Davis (software): main/909

11 IN PTR shimmer.oog.org.

; Владелец -- Ellen Monk (design): main/1116

12 IN PTR sulawesi.oog.org.

; Владелец -- Alex Rollins (IT): main/1101 55 IN PTR sander.oog.org.

Этот метод создания файлов открывает перед нами много возможностей. До сих пор мы генерировали файлы, используя содержимое одного текстового файла базы данных. Запись из базы данных считывалась и записывалась в файл, возможно, подвергаясь при этом форматированию. Таким образом, в создаваемые файлы попадали только записи из базы данных.

Иногда бывает полезно, чтобы сценарий добавлял в процессе преобразования свои предопределенные данные. Например, в случае с конфигурационными файлами DNS можно улучшить сценарий преобразования так, чтобы он добавлял записи MX (Mail eXchange), указывающие на центральный почтовый сервер, для каждого узла из базы данных. Простое изменение нескольких строк кода с таких:

К выводим запись А

printf OUTPUT "%-20s\tIN A %s\rT, Sentries{Sentry}->{name},Sentries{Sentry}->{address}:

на следующие:

# выводим запись А

printf OUTPUT "%-20s\tIN A %s\n",

$entries{Sentry}->{name},Sentries{Sentry}->{address};

и выводим запись MX

print OUTPUT " IN MX 10 $mailserver\n";

приведет к тому, что почта, посылаемая на любой из узлов домена, будет направляться на машину $mailserver. Если эта машина настроена так, что может обрабатывать почту для всего домена, то мы задействовали очень важный компонент инфраструктуры (централизованную обработку почты), добавив всего лишь одну строчку кода на Perl.



Содержание раздела