Рендеринг нескольких CSV-файлов с помощью Perl с использованием одного дескриптора открытия файла?

У меня есть этот код, где я хочу обрабатывать несколько файлов csv (в настоящее время это всего один файл) и использовать perl для отображения его формата перед его отправкой в ​​​​linux-box и заменяет исходное содержимое файла с помощью соединения ssh. Вот код

#!/usr/bin/perl -w
use strict;
# this is a csv which will contains IP addresses of one specific category for e.g malware.
my $seculert_qradar_list = "$seculert_dir/seculert.csv";

#ssh connection information 
my $qradar_console = '10.10.1.22';
my $qradar_ssh_key = "$seculert_dir/qr-id_dsa";
my $qradar_ssh_knownhosts = "$seculert_dir/known_hosts";

#################################################################################

# NOTE: this is the "OUT" file.
# 1 - Name
# 2 - Sub-Name
# 3 - IP Address
# 4 - is colour, deprecated
# 5 - database length, deprecated
# 6 - asset weight, deprecated
# 7 - an ID for the 'record' each unique name pair (first 2 columns) gets an ID
#################################################################################
my $source = 'BAD-IP-Addresses-LABEL';
my $type_description = 'honeypots-for-examnple';

# Based upon the format described above I want to render the csv as written in print OUT statement. This format is important, because the endsystem process the file (remotenet.conf) based upon the provided layout.
open(FP, ">>$seculert_qradar_list");
for my $line (<FP>) {
    my ($hostname, $ip, $something1, $something2) = split(/,/, $line);
    print OUT "$source $type_description $ip #FF0000 0 90  29\n";
}
close(FP);

# Here I just want the contents of modified csv to be written over remotenet.conf. This file is then processed through auto-deploy script by the system. The results get populated on front-end webserver.    
print "Sending to QRadar...\n";
# SSH To QRadar's Console and push out file + trigger update
`scp -i $qradar_ssh_key -o UserKnownHostsFile=$qradar_ssh_knownhosts -o StrictHostKeyChecking=no root\@$qradar_console:/store/configservices/staging/globalconfig/remotenet.conf .`;
`sed -i -e '/^SECULERT/d' remotenet.conf`;
`cat $seculert_qradar_list >> remotenet.conf`;
`scp -i $qradar_ssh_key -o UserKnownHostsFile=$qradar_ssh_knownhosts -o StrictHostKeyChecking=no remotenet.conf root\@$qradar_console:/store/configservices/staging/globalconfig/remotenet.conf`;

print "Cleaning up...\n";
# Remove our SECULERT list and the newly pushed out qradar conf
unlink($seculert_qradar_list); unlink ('remotenet.conf');

print "Deploying in QRadar...(takes time to complete)\n";
# QRadar magic
`ssh -i $qradar_ssh_key -o UserKnownHostsFile=$qradar_ssh_knownhosts -o StrictHostKeyChecking=no root\@$qradar_console /opt/qradar/upgrade/util/setup/upgrades/do_deploy.pl`;
print "Complete!\n\n";

Что мне интересно узнать и, возможно, получить помощь от программиста на Perl, используя один дескриптор файла, я могу открыть несколько файлов, например, в моем случае у меня есть что-то вроде этого

  1. вирус.csv
  2. бот.csv
  3. вредоносный файл.csv

Нужно ли мне повторно копировать код цикла for для каждого файла csv с другим дескриптором? Целевой файл remotenet.conf остается прежним.

После правильного рендеринга, например, для одного CSV-файла, remotenet.conf на веб-интерфейсе будет выглядеть примерно так:

Virus
    10.10.2.1
     .......

Bot
    10.10.4.1
     ......

Было бы здорово, если бы несколько изменений происходили за один раз, с одним автоматическим развертыванием (см. код в конце). Я надеюсь, что смогу понять проблему. Пожалуйста, дайте мне знать, если требуются дополнительные разъяснения.

Благодарность

ЧЕГО Я ХОЧУ СДЕЛАТЬ

Мне нужен динамический код, в котором в одной папке хранится несколько csv, готовых к рендерингу. Последовательность описывается как: -

  1. читать каждый CSV-файл из папки.
  2. преобразует его в приемлемый формат.
  3. Измените значения двух переменных на основе имени файла csv. Например,

Для имени файла malware.csv

мой источник $ = 'ПЛОХИЕ IP-адреса-LABEL'; мой $type_description = 'приманки для примера';

Для bot.csv

мой $source = 'бот-сеть'; мой $type_description = 'топ-10';

В конце копирует отформатированный файл вместе с содержимым в remotenet.conf через ssh.


person user3066819    schedule 21.12.2013    source источник
comment
Ваш код немного запутан, и мне трудно его понять: (1) Вы открыли FP для добавления, но пытаетесь прочитать из него. (2) Где открывается OUT? (3) Не могли бы вы уточнить, какая часть должна повторяться для каждого файла CSV?   -  person amon    schedule 21.12.2013
comment
Я пересмотрел код с github github.com/harvard. -itsecurity/qradar-seculert-push/blob/master/ исходный автор определил FP таким образом.   -  person user3066819    schedule 21.12.2013


Ответы (1)


Вы не можете читать из файла, открытого в режиме добавления >>. Кажется, вы только что переименовали дескриптор файла OUT в FP, но этого недостаточно.

  • Исходный код включал chdir $seculert_dir — это имеет смысл, потому что теперь нам не нужно постоянно добавлять префикс каталога к именам файлов (это эквивалентно команде cd в оболочке).
  • Исходный код точно не использовал лучшие практики. Я обернул команды scp и ssh версиями, которые выполняют минимальную проверку ошибок.
  • Также нет необходимости вызывать внешние инструменты для того, что мы можем делать с помощью Perl.

Вот мой обновленный код, который может считывать данные из упрощенных файлов CSV:


Во-первых, мы запускаем преамп. use warnings предпочтительнее переключателя -w.

#!/usr/bin/perl
use strict;
use warnings;

my $seculert_dir = "!FIXME!";
chdir $seculert_dir;  # go into that dir, so that we don't have to use prefixes

my @csv_files = ("seculert.csv"); # this is a csv which will contains IP addresses
                                  # of one specific category for e.g malware.
                                  # We only use the 2nd column

#ssh connection information 
my $qradar_console = '10.10.1.22';
my $qradar_ssh_key = "qr-id_dsa";
my $qradar_ssh_knownhosts = "known_hosts";

Это была наша конфигурация. Затем мы получаем конфиг с сервера с помощью обернутой команды SCP:

# fetch the remotenet.conf from QRadar
print STDERR "Fetching configuration from QRadar...\n";
scp("root\@$qradar_console:/store/configservices/staging/globalconfig/remotenet.conf" => '.');

Теперь мы открываем файл, в который мы помещаем нашу измененную конфигурацию.

# write the changed conf here before uploading:
open my $new_conf, ">", "remotenet.conf.changed" or die qq(Can't open "remotenet.conf.changed" for writing: $!);

Теперь открываем старую конфигурацию, копируем ее, но пропускаем строки, начинающиеся с SECULERT.

# copy old conf over, delete lines starting with "SECULERT"
open my $old_conf, "<", "remotenet.conf" or die qq(Can't open "remotenet.conf" for reading: $!);
while (<$old_conf>) {
    print {$new_conf} $_ unless /^SECULERT/;
}
close $old_conf;

Обратите внимание, как я использовал «лексические дескрипторы файлов» (open my $fh, ...). Это позволяет избежать некоторых проблем и более современно, чем использование голых слов.

Далее мы просматриваем файлы CSV. Мы открываем каждый из них, затем извлекаем 2-й столбец и печатаем его вместе с остальным материалом в измененный файл конфигурации.

# append the data from the CSVs
for my $csv_file (@csv_files) {
    my $source = 'BAD-IP-Addresses-LABEL';
    my $type_description = 'honeypots-for-examnple';

    open my $csv, "<", $csv_file or die qq(Can't open "$csv_file" for reading: $!);

    while (my $line = <$csv>) {
        my (undef, $ip) = split /,/, $line; # we're only interested in the 2nd column

        # Based upon the format described below I want to render the csv
        # as written in print OUT statement. This format is important, because the
        # endsystem process the file (remotenet.conf) based upon the provided layout.
        #
        # Columns in the output:
        # 1 - Name
        # 2 - Sub-Name
        # 3 - IP Address
        # 4 - is colour, deprecated
        # 5 - database length, deprecated
        # 6 - asset weight, deprecated
        # 7 - an ID for the 'record' each unique name pair (first 2 columns) gets an ID
        print {$new_conf} "$source $type_description $ip #FF0000 0 90  29\n";
    }
}

Теперь у нас есть вся необходимая информация в новом файле конфигурации, и мы можем загрузить ее на сервер:

close $new_conf;

# copy the changed remotenet.conf back to QRadar
scp('remotenet.conf.changed' => "root\@$qradar_console:/store/configservices/staging/globalconfig/remotenet.conf");

# Remove our SECULERT list and the newly pushed out qradar conf
print STDERR "Cleaning up...\n";
unlink $_ or warn qq(Can't remove "$_": $!) for 'remotenet.conf', 'remotenet.conf.changed';

Далее запускаем скрипт развертывания:

# QRadar magic -- run deploy script
print STDERR "Deploying in QRadar...(takes time to complete)\n";
ssh("root\@$qradar_console", '/opt/qradar/upgrade/util/setup/upgrades/do_deploy.pl');

print STDERR "Complete!\n\n";

Вот обертки для scp и ssh. Это подпрограммы (функции, процедуры или методы на других языках). Аргументы находятся в массиве @_, из которого мы распаковываем их в переменные с лучшими именами. Команда system принимает имя команды и список аргументов. Поскольку это позволяет обойти оболочку, нам не нужно думать о побегах оболочки. system возвращает ноль в случае успеха, поэтому мы используем его для проверки на наличие ошибок.

# Wrappers for SSH and SCP commands
sub scp {
    my ($from, $to) = @_;
    my $success = 0 == system 'scp',
        '-i' => $qradar_ssh_key,
        '-o' => "UserKnownHostsFile=$qradar_ssh_knownhosts",
        '-o' => "StrictHostKeyChecking=no",
        $from => $to;
    return $success if defined wantarray;                # return failure when somebody checks for it
    die qq(Can't scp "$from" to "$to") if not $success;  # die when failure, and nobody checks.
}

sub ssh {
    my ($host, $command) = @_;
    my $success = 0 == system 'ssh',
        '-i' => $qradar_ssh_key,
        '-o' => "UserKnownHostsFile=$qradar_ssh_knownhosts",
        '-o' => "StrictHostKeyChecking=no",
        $host, $command;
    return $success if defined wantarray;                           # return failure when somebody checks for it
    die qq(Can't ssh into "$host" for '$command') if not $success;  # die when failure, and nobody checks.
}

Этот код все еще можно улучшить, например. используя правильный парсер CSV, такой как Text::CSV, используя лучшие привязки SSH, чем просто перенос программ командной строки, автоматическую проверку ошибок с помощью autodie и лучшую обработку временных файлов.

Кажется, вам нужны разные значения $source для разных файлов. Для этого мы должны использовать более сложную структуру данных, чем @csv_files — я бы использовал хэш массивов, например.

my %csv_files = (
  'malware.csv' => ['BAD-IP-Addresses-LABEL', 'honeypots-for-examnple'],
  'bot.csv'     => ['bot-net', 'top-10'],
);

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

for my $csv_file (keys %csv_files) {
  my ($source, $type_description) = @{ $csv_files{$csv_file} };

  ...
}

Выражение $csv_files{$csv_file} обращается к записи с именем $csv_file в хеше $csv_files. Эта запись содержит ссылку на массив в качестве значения. @{…}, который преобразует ссылку на массив в массив, который мы можем распаковать с помощью назначения списка my ($foo, $bar) = @array.

person amon    schedule 21.12.2013
comment
Спасибо, я пересмотрел код с комментариями и новыми обновлениями. Пожалуйста, посмотрите. Я новичок в Perl, я старался изо всех сил ответить на ваши вопросы. - person user3066819; 21.12.2013
comment
У меня нет слов, чтобы отплатить вам за уровень работы, которую вы проделали с кодом. Вы волшебник; Перл волшебник :). Я не могу дождаться понедельника, так как хочу запустить ваш код на рабочем сервере. Я сообщу вам о результатах. С помощью одного этого кода вы многому научили меня о perl; спасибо за все амон! - person user3066819; 22.12.2013
comment
Это для обновления; на самом деле передайте мою благодарность за то, что я протестировал ваш скрипт на рабочем сервере; и это работает как шарм :). Единственная незначительная корректировка была связана с print {$new_conf} "$source $type_description $ip #FF0000 0 90 29\n"; тем, что он будет делать: он поставит разрыв строки после IP-адреса, а остальная часть текста до 29 будет помещена в отдельную строку, за которой следует еще один разрыв строки; Я думаю, что csv считает последнее значение csv концом строки. Это приводит к тому, что конечная система не отображает remotnet.conf. Вместо этого я определил формат в файле csv. - person user3066819; 23.12.2013