Как стать автором
Обновить

Еще один вариант Инвентаризации

По мотивам этой статьи.

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

В один из суровых зимних дней, когда появлятся на открытом воздухе совсем не хочется, а тяга покодить усиливается, шеф попросил таблицу с конфигурациями наших компов. А так как я на досуге изучаю Python, с упором на 3 версию, шальная мысль «А не повелосипедить ли?» возникла практически мгновенно, и я поддался.

Краткий опрос гугла принес нерадостные вести — реализовать сбор конфигураций с компов в сети полностью на python'е мне пока не позубам. Покодить не удалось, но работу работать надо! С такими мыслями я стал перебирать в памяти готовые решения для моей задачи и вспомнил про cpu-z (офф. сайт) и про имеющийся в нем ghost mode.
Решено: велосипеду быть!

Задумка была проста — запускать по расписанию на каждом компе cpu-z в тихом режиме, из шары и генерировать отчеты, опять же в шару. Скриптом на python'е, запускаемым по расписанию или по требованию, собирать отчеты в единую табличку. От большой задачи задуманной в начале, осталась малая часть, но на безрыбье…

Первым делом были созданы две шары program и data, пользователи домена в первом каталоге имеют права на чтение, а во втором на запись. Дальше я скачал и распаковал в program сам cpu-z (версия не требующая установки) и набросал bat'ник в пару строк, поместив туда же.
@echo off
set PROGRAMFOLDER=\\samba\collector\program\cpuz\
set DATAFOLDER=\\samba\collector\data\
start "cpuz" "%PROGRAMFOLDER%cpuz.exe" -txt=%DATAFOLDER%%COMPUTERNAME%


Через групповые политики в планировщик заданий всем была добавлена задача запускать \\samba\collector\program\collect.bat при запуске ежедневно.

Отчеты со всех машин собираются, пора кодить.

На первые грабли я наступил практически сразу, pysmbc упорно отказывался работать в связке с третьим python'ом, хотя со вторым проявлял чудеса трудолюбия. Все мои попытки на разных машинах с разными вариантами установки заканчивались однотипной руганью в консоли


import smbc
Traceback (most recent call last):
File "", line 1, in ImportError: /usr/lib/python3.2/site-packages/smbc.cpython-32mu.so: undefined symbol: PyInt_Check


Гугл не помог, и я решил на время отложить разбор полетов с smbc и воспользоватся не совсем красивым вариантом с монтированием шары.

(Как уже было упомянуто выше, я использовал python 3 и поэтому не мог запускать скрипт на машине с samb'ой, centos 5.7 в репах python3 отсутствует, а собирать или ставить из дополнительных желания не было. Писал на рабочей машине Fedora 16)

Дальше сюрпризов не возникло. Первый рабочий вариант появился в течении получаса:

import os
import subprocess

mount_comm = 'mount -t cifs //samba/collector/ /mnt/media -o user=DOMAIN/user,password=pass'
umount_comm = 'umount /mnt/media'

mount_res = subprocess.Popen(mount_comm, shell=True, stdout=subprocess.PIPE,
                            stdin=subprocess.PIPE, stderr=subprocess.PIPE).communicate()

report_file = 'report.html'

glb_result_dict = {}
dir_path = '/mnt/media/data/'
for item in os.listdir(dir_path):

    if item[-4:] == '.txt':

        source_file = open(dir_path+item, encoding='cp1251')
        source_list = source_file.readlines()

        def str_parser(str, sep='\t\t\t'):
            return str.rstrip().split(sep)[1]

        result_dict = {}
        mem_dev_cnt = 0
        
        for n, i in enumerate(source_list):
            if 'Processors Information' in i[0:22]:
                result_dict['prc_core_num'] = str_parser(source_list[n+4], '\t\t')
                result_dict['prc_name'] = str_parser(source_list[n+6])
                result_dict['prc_spec'] = str_parser(source_list[n+8], '\t\t')
                result_dict['prc_pack'] = str_parser(source_list[n+9], ')\t')
            if 'Chipset' in i[0:7]:
                result_dict['chp_n_bridge'] = str_parser(source_list[n+3])
                result_dict['chp_s_bridge'] = str_parser(source_list[n+4])
                result_dict['mem_type'] = str_parser(source_list[n+5])
                result_dict['mem_size'] = str_parser(source_list[n+6])
            if 'DMI Baseboard' in i[0:13]:
                result_dict['mbd_vendor'] = str_parser(source_list[n+1])
                result_dict['mbd_model'] = str_parser(source_list[n+2])
            if 'DMI Physical Memory Array' in i[0:25]:
                result_dict['mem_max_cap'] = str_parser(source_list[n+4], '\t\t')
                result_dict['mem_max_dev'] = str_parser(source_list[n+5], '\t\t')
            if 'DMI Memory Device' in i[0:18]:
                if (source_list[n+6][1:5]) == 'size':
                    mem_dev_cnt = mem_dev_cnt + 1
            if 'Software' in i[0:8]:
                result_dict['os'] = str_parser(source_list[n+3])
            result_dict['mem_dev'] = mem_dev_cnt
            result_dict['cmp_name'] = item[0:-4]
    
        glb_result_dict[item[0:-4]] = result_dict

tmpl_top = '''
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
    <style type="text/css">
        TD { font-size: 8pt; }
    </style>
<body>
    <div>
        <table border="1" cellpadding="4" cellspacing="0">
            <tr>
                <td>Имя комп.</td>
                <td>Проц. модель</td>
                <td>Проц. специф.</td>
                <td>Проц. сокет</td>
                <td>Кол-во ядер</td>
                <td>Мат. модель</td>
                <td>Мат. производитель</td>
                <td>Юж. мост</td>
                <td>Сев. мост</td>
                <td>Память объем</td>
                <td>Память макс. объем</td>
                <td>Память слотов зан.</td>
                <td>Память всего слотов</td>
                <td>Память тип</td>
                <td>ОС</td>
            </tr>'''

tmpl_bottom = '''
        </table>
    </div>
</body>
</html>
'''

for i in sorted(glb_result_dict.keys()):
    tmpl_top = tmpl_top + '''
            <tr>
                <td>{cmp}</td>
                <td>{dct[prc_name]}</td>
                <td>{dct[prc_spec]}</td>
                <td>{dct[prc_pack]}</td>
                <td>{dct[prc_core_num]}</td>
                <td>{dct[mbd_model]}</td>
                <td>{dct[mbd_vendor]}</td>
                <td>{dct[chp_s_bridge]}</td>
                <td>{dct[chp_n_bridge]}</td>
                <td>{dct[mem_size]}</td>
                <td>{dct[mem_max_cap]}</td>
                <td>{dct[mem_dev]}</td>
                <td>{dct[mem_max_dev]}</td>
                <td>{dct[mem_type]}</td>
                <td>{dct[os]}</td>
            </tr>'''.format(cmp = i, dct=glb_result_dict[i])

tmpl = tmpl_top + tmpl_bottom

open('/mnt/media/'+report_file, 'w').write(tmpl)

umount_res = subprocess.Popen(umount_comm, shell=True, stdout=subprocess.PIPE,
                            stdin=subprocess.PIPE, stderr=subprocess.PIPE).communicate()



Совсем сыро, на скорую руку, но работает.

Мой список замечаний:
Логин с паролем в открытом виде это плохо, монтрование шары тоже (требуется запуск от рута) — с smbc нужно все-таки разобраться.
Обрабатывать исключения при возможных ошибках чтения записи файлов и монтирования.
HTML шаблон выкинуть в файл.
Функция в теле цикла без необходимости плохо (Как она вообще туда попала?).

На выходе получается вот такая табличка:


Изначально все задумывалось только в целях попрактиковаться, на один раз так сказать. Но этот велик так и продолжает служить автору, хотя далеко не идеален, многие вещи надо бы доделать например сбор данных с линуховых машин… Доработкой скрипта пока не занимался ввиду отсутствия времени, но в планах это есть.

Думаю не ошибусь если скажу, что существующих решений с нужным функционалом достаточно и они лучше, быстрее, функциональнее.

Профит:
Час приятного и полезного времяпрепровождения, кусочек опыта в копилку, шеф получил данные в нужном виде и в срок.

Надеюсь эта заметка окажется кому нибудь полезной, интересной или натолкнет на правильные мысли.

Буду рад любым замечаниям по коду и по статье в целом.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.