Pull to refresh

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

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

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

В один из суровых зимних дней, когда появлятся на открытом воздухе совсем не хочется, а тяга покодить усиливается, шеф попросил таблицу с конфигурациями наших компов. А так как я на досуге изучаю 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 шаблон выкинуть в файл.
Функция в теле цикла без необходимости плохо (Как она вообще туда попала?).

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


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

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

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

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

Буду рад любым замечаниям по коду и по статье в целом.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.