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

       

Сценарии



Сценарии

Теперь, когда мы разобрались с базой данных, самое время написать сценарии для выполнения периодических или каждодневных действий, необходимых при системном администрировании. Эти сценарии построены на низкоуровневой библиотеке компонентов (Account.pm), которую мы создали, объединив в один файл все только что написанные подпрограммы. Такая подпрограмма позволяет убедиться, что все необходимые модули загружены:

sub InitAccountf
use XML: :Writer;

Srecord = { fields => [login, fullname,id,type,password]};
$addqueue = "addqueue"; tt имя файла очереди добавления
Sdelqueue = "delqueue"; ft имя файла очереди удаления
$maindata = "accountdb"; tt имя основной базы данных

ft учетных записей

if ($"0 eq "MSWin32"){
require Win32::Lanman;
require Win32::Perms;
require File::Path;

ft местоположение файлов учетных записей

Saccountdir = "\\\\server\\accountsystem\\";

ft списки рассылки

$maillists = "$accountdir\\maillists\\";

ft корневой каталог домашних каталогов

$homeNTdirs = "\\\\nomeserver\\home";

ft имя подпрограммы, добавляющей учетные записи

Saccountadd = "CreateNTAccount";



ft имя подпрограммы, удаляющей учетные записи

Saccountdel = "DeleteNTAccount": }

else {

require Expect;

и местоположение файлов учетных записей
$accountdir = "/usr/accountsystem/";
в списки рассылки

Smaillists = "Saccountdir/maillists/";
tt местоположение команды useradd
Suseraddex = ",/usr/sbin/useradd";
tt местоположение команды userdel
Suserdelex = "/usr/sbin/userdel";
tt местоположение команды passwd
Spasswdex = "/bin/passwd";
tt корневой каталог домашних каталогов
ShomeUnixdirs = "/home";
tt прототип домашнего каталога
$skeldir = "/home/skel";
ft командный интерпретатор по умолчанию
Sdefshell = "/bin/zsh";
ft имя подпрограммы, добавляющей учетные записи
Saccountadd = "CreateUnixAccount";
tt имя подпрограммы, удаляющей учетные записи
Saccountdel = "DeleteUnixAccount";
}
}

Рассмотрим сценарий, обрабатывающий очередь добавления:

use Account;
use XML;:Simple;

SlnitAccount;

считываем низкоуровневые подпрограммы

&ReadAddQueue;

tt считываем и анализируем очередь добавления

&ProcessAddQueue;

tt пытаемся создать все учетные записи

&DisposeAddQueue;

ft записываем учетную запись либо в основную

tt базу данных, либо обратно в очередь, если

tt возникли какие-то проблемы

tt считываем очередь добавления в структуру данных $queue
sub ReadAddQueue{

open(ADD,Saccountdir.Saddqueue) or

die "Unable to open ".Saccountdir.$addqueue.":$!\n";
read(ADD, Squeuecontents, -s ADD);
close(ADD);
Squeue = XMLin("<queue>".Squeuecontents."</queue>",

keyattr => ["login"]);

ft обходим в цикле структуру данных, пытаясь создать учетную
запись для каждого запроса (т. е. для каждого ключа)
sub ProcessAddQueue{

foreach my Slogin (keys %{$queue->{account}})

{

sub InitAccountf
use XML: :Writer;

Srecord = { fields => [login, fullname,id,type,password]};
$addqueue = "addqueue"; tt имя файла очереди добавления
Sdelqueue = "delqueue"; ft имя файла очереди удаления
$maindata = "accountdb"; tt имя основной базы данных

ft учетных записей

if ($"0 eq "MSWin32"){
require Win32::Lanman;
require Win32::Perms;
require File::Path;

ft местоположение файлов учетных записей

Saccountdir = "\\\\server\\accountsystem\\";

ft списки рассылки

$maillists = "$accountdir\\maillists\\";

ft корневой каталог домашних каталогов

$homeNTdirs = "\\\\nomeserver\\home";

ft имя подпрограммы, добавляющей учетные записи

Saccountadd = "CreateNTAccount";

ft имя подпрограммы, удаляющей учетные записи

Saccountdel = "DeleteNTAccount": }

else {

require Expect;

и местоположение файлов учетных записей
$accountdir = "/usr/accountsystem/";
в списки рассылки

Smaillists = "Saccountdir/maillists/";
tt местоположение команды useradd
Suseraddex = ",/usr/sbin/useradd";
tt местоположение команды userdel
Suserdelex = "/usr/sbin/userdel";
tt местоположение команды passwd
Spasswdex = "/bin/passwd";
tt корневой каталог домашних каталогов
ShomeUnixdirs = "/home";
tt прототип домашнего каталога
$skeldir = "/home/skel";
ft командный интерпретатор по умолчанию
Sdefshell = "/bin/zsh";
ft имя подпрограммы, добавляющей учетные записи
Saccountadd = "CreateUnixAccount";
tt имя подпрограммы, удаляющей учетные записи
Saccountdel = "DeleteUnixAccount";
}
}

Рассмотрим сценарий, обрабатывающий очередь добавления:

use Account;
use XML;:Simple;

SlnitAccount;

считываем низкоуровневые подпрограммы

&ReadAddQueue;

считываем и анализируем очередь добавления

&ProcessAddQueue;

пытаемся создать все учетные записи

&DisposeAddQueue;

записываем учетную запись либо в основную

tt базу данных, либо обратно в очередь, если

tt возникли какие-то проблемы

tt считываем очередь добавления в структуру данных $queue
sub ReadAddQueue{

open(ADD,Saccountdir.Saddqueue) or

die "Unable to open ".Saccountdir.$addqueue.":$!\n";
read(ADD, Squeuecontents, -s ADD);
close(ADD);
Squeue = XMLin("<queue>".Squeuecontents."</queue>",

keyattr => ["login"]);
ft обходим в цикле структуру данных, пытаясь создать учетную
запись для каждого запроса (т. е. для каждого ключа)
sub ProcessAddQueue{

foreach my Slogin (keys %{$queue->{account}}){

Sresult = &$accountadd($login,

$queue->{account}->{$login});
if (!$result){

$queue->{account}->{$login}{status} = "created";
}
else {

$queue->{account}->{$login}{status} =

"error:$result";
}
}
}

ft теперь снова обходим структуру данных. Каждую учетную запись
# со статусом "created," добавляем в основную базу данных. Все
и остальные записываем обратно в файл очереди, перезаписывая
tt все его содержимое.
sub DisposeAddQueue{

foreach my Slogin (keys %{$queue->{account}}){

if ($queue->{account}->{$login}{status} eq "created")!
$queue->{account}->{$login}{login} = Slogin;
$queue->{account}->{$login}{creation_date} = time;
&AppendAccountXML($accountdir.$maindata,

$queue->{account}->{$login});
delete $queue->{account}->{$login};
next;
}
}

# To, что осталось сейчас в Squeue, - это учетные записи,

# которые невозможно создать

# перезаписываем файл очереди
open(ADD,">".$accountdir.$addqueue) or

die "Unable to open ".$accountdir.$addqueue.":$!\n";
П если есть учетные записи, которые не были созданы,

# записываем их

if (scalar keys %{$queue->{account}}){

print ADD XMLout(&TransformForWrite($queue),

rootname => undef);
}

close(ADD);
}

Сценарий, обрабатывающий очередь удаления, очень похож:

use Account;
use XML::Simple;

SlnitAccount;

# считываем низкоуровневые подпрограммы

&ReadDelOueue;

# считываем и анализируем очередь удаления

&ProcessDelQueue;

пытаемся удалить все учетные записи

&DisposeDelQueue;

удаляем учетную запись либо из основной

базы данных, либо записываем ее обратно в
в очередь, если возникли какие-то проблемы

и считываем очередь удаления в структуру данных $queue
sub ReadDelQueue{

open(DEL,Saccountdir.Sdelqueue) or

die "Unable to open ${accountdir}${delqueue}:$!\n";
read(OEL, Squeuecontents, -s DEL);
close(DEL);
Squeue = XMLin("<queue>".$queuecontents."</queue>",

keyattr => ["login"]);
}

# обходим в цикле структуру данных, пытаясь удалить учетную

# запись при каждом запросе (т, е. для каждого ключа)
sub ProcessDelQueue{

foreach my Slogin (keys %{$queue->{account}}){
Sresult = &$accountdel($login,

$queue->{account}->{$login});
if (!$result){

Squeue->{account}->{$login}{status} = "deleted";
}
else {

$queue->{account}->{$login}{status} =
"error:$result";
}
>
}

# считываем основную базу данных и затем вновь обходим в цикле
структуру Squeue. Для каждой учетной записи со статусом

"deleted," изменяем информацию в основной базе данных. Затем
записываем в базу данных. Все, что нельзя удалить, помещаем

# обратно в файл очереди удаления. Файл перезаписывается,
sub DisposeDelQueue{

&ReadMainDatabase;

foreach my Slogin (keys %{$queue->{account}}){

if ($queue->{account}->{Slogin}{status} eq "deleted"){
unless (exists $maindb->{account}->{$login}){
warn "Could not find Slogin in $maindata\n";
next;
}

$maindb->{account}->{$login}{status} = "deleted";
$maindb->{account}->{$login}{deletion_date} = time;
delete $queue->{account}->{$login};
next;
}
&WriteMainDatabase;

# все, что сейчас осталось в Sqjjeue, - это учетные записи,

# которые нельзя удалить

open(DEL,">".$accountdir.$delqueue) or die "Unable to open ".

$accountdir.$delqueue.":$!\n";

if (scalar keys %{$queue->{account}}){

print DEL XMLout(&TransformForWrite($queue), rootname => undef);

}

close(DEL); }

sub ReadMainDatabase{

open(MAIN,$accountdir.$maindata) or

die "Unable to open ".$accountdir.$maindata.":$!\n";

read (MAIN, $dbcontents, -s MAIN);

close(MAIN); $maindb = XMLin("<maindb>".Sdbcontents. "</maindb>",

keyattr => ["login"]); }

sub WriteMainDatabase{

# замечание: было бы «гораздо безопаснее* записывать данные

# сначала во временный файл и только если они были записаны

# успешно, записывать их окончательно open(MAIN,">".

$accountdir.Smaindata) or

die "Unable to open ".$accountdir.$maindata.":$!\n";

print MAIN XMLout(&TransformForWrite($maindb),

rootname => undef); close(MAIN); }

Можно написать еще множество сценариев. Например, мы могли бы применять сценарии, осуществляющие экспорт данных и проверку согласованности. В частности, совпадает ли домашний каталог пользователя с типом учетной записи из основной базы данных? Входит ли пользователь в нужную группу? Нам не хватит места, чтобы рассмотреть весь спектр таких программ, поэтому завершим этот раздел небольшим примером экспортирования данных. Речь уже шла о том, что хотелось бы завести отдельные списки рассылки для пользователей различного типа. В следующем примере из основной базы данных считываются данные и создается набор файлов, содержащих имена пользователей (по одному файлу для каждого типа пользователей):

use Account; И только чтобы найти файлы use XML::Simple:

&InitAccount;

SReadMainDatabase:

&WriteFiles:

open(MAIN,Saccountdir.Smaindata) or

die "Unauie to open ".Saccountdir.$maindata "-$'\r";

read (MAIN, Sdbcontents. -s MAIN);

ciose(MAIN): Smaindb = XMLin("<maindb>

".Sdbcontents." /maincm>",

keyattr -> [""]):

}

обходим в цикле списки, собираем списки учетных записей

определенного типа и сохраняем им в хэше списков. Затем

записываем содержимое каждого ключа в отдельный файл.

sub WriteFiles {

foreach my Saccount (@{$niaindb->{account}}){

next if $account->{status}

eq "deleted"; push(@{$types{$account->{type}}},

$account->{login}); }

foreach $type (keys %types){

open(OUT,">".Smalllists.Stype) or die "Unable to write to

".Saccountdir.$maillists.$type.": $!\n";

print OUT ]0in("\n",sort @{$types{$type}})."\n"; close(OUT);

}

}

Если посмотреть в каталог списков рассылки, то можно увидеть:

> dir

faculty staff

Каждый из этих файлов содержит соответствующий список учетных записей пользователей.



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