1 авг. 2012 г.

Строительство беспроводной сети своими руками. Или интернет в массы.

Не рассматривается прокидывание кабеля. Рассматривается только активное оборудование и настройка серверной части.

Дано:
Сервер с freebsd, точки доступа UniFi Ap (Ubiquiti Networks), машина с OS Windows.

Задача:
1. Построить беспроводную сеть с авторизацией через web-интерфейс
2. Обеспечить логирование доступа.

Решение.
1. Включаем точки доступа в уже существующую сеть. В эту же сеть выносим машину с OS windows.
2. Производим настройку точек доступа через программу, прилагаемую на диске. Описывать настройки точек доступа не вижу смысла, ибо там все и так ясно.
3. Настраиваем FreeBSD.

Для FreeBSD нам потребуется:
  1. Пересборка ядра;
  2. настроить pf;
  3. написать пару скриптов;
  4. apache22;
  5. php5;
  6. mysql-server;
  7. isc-dhcpd-server;
  8. sudo;
  9. mrtg
1. Собираем ядро со следующими параметрами:

device        pf
device        pflog

options        ALTQ
options        ALTQ_CBQ        # Class Bases Queueing
options        ALTQ_RED        # Random Early Detection
options        ALTQ_RIO        # RED In/Out
options        ALTQ_HFSC       # Hierarchical Packet Scheduler
options        ALTQ_CDNR       # Traffic conditioner
options        ALTQ_PRIQ       # Priority Queueing
options        ALTQ_NOPCC      # Required for SMP build


2. Настройка pf:

table <  rfcnets  >    {10.0.0.0/8, !192.168.1.0/24, 192.168.0.0/16, !172.16.0.0/16, 172.16.0.0/12 }
table <  me  >      { self, 172.16.0.1}

table <  permgroup  >

# Options: tune the behavior of pf, default values are given.
set timeout { interval 10, frag 30 }
set timeout { tcp.first 120, tcp.opening 30, tcp.established 86400 }
set timeout { tcp.closing 900, tcp.finwait 45, tcp.closed 5 }
set timeout { udp.first 60, udp.single 30, udp.multiple 60 }
set timeout { icmp.first 20, icmp.error 10 }
set timeout { other.first 60, other.single 30, other.multiple 60 }
set timeout { adaptive.start 0, adaptive.end 0 }
set limit { states 10000000, frags 50000 }
set loginterface none
#set optimization normal
set block-policy drop
set require-order yes
set fingerprints "/etc/pf.os"
#set state-policy if-bound

# Normalization: reassemble fragments and resolve or reduce traffic ambiguities.
scrub in all

# Поднимаем nat для всех, кто попал в таблицу permgroup, остальных редиректим на 10.10.0.2 при попытке перехода на любой внешний сайт, для авторизации.
nat on em0 from <  permgroup  > to any -  > em0
rdr on em1 proto tcp from !<  permgroup  > to any port 80 -  > 10.10.0.2


# Разрешаем loopback
pass quick on lo from any to any

# Описываем IN правила
#Запрещаем rfc сети на внешнем интерфейсе
block in quick on em0 from <  rfcnets  > to any

# Правила для сервисов (я открываю только то, что нужно и для внутренней сети)
# icmp
pass in quick inet proto icmp from any to 10.10.0.2 icmp-type echoreq keep state
# ssh
pass in quick proto tcp from any to 10.10.0.2 port 22 flags S/SA keep state
# http,https
pass in quick proto tcp from any to 10.10.0.2 port {443, 80} flags S/SA keep state
# dns
pass in quick proto {tcp, udp} from any to <  me  > port domain flags S/SA keep state

# Все остальное к серверу блокируем
block in quick from any to <  me  >

# Разрешаем доступ в инет для permgroup, при этом логируем доступ
pass in log quick from any to <  permgroup  >
pass in log quick from <  permgroup  > to any

# Остальное на вход блокируем
block in quick all

# Правила для OUT
# запрещаем rfc сети на внешнем интерфейсе.
block out quick on em0 from <  rfcnets  > to any

#Разрешаем все от самого сервера.
pass out quick from <  me  > to any keep state

# Разрешаем доступ в инет для permgroup, при этом логируем доступ
pass out log quick from any to <  permgroup  >
pass out log quick from <  permgroup  > to any

block out quick all



3. Скрипты для управления, администрирования и авторизации:

3.1. Управление сессиями (pf.sh):#!/bin/sh
/bin/echo `/usr/local/bin/sudo /sbin/pfctl -t permgroup -T $1 $2` >> /usr/home/admin/wifi/w.txt

Скрипт выполняет действия $1 для $2 (опц.) с таблицей permgroup, которая ранее была описана в файле pf.conf.
Пример:
  1. pf.sh add 10.10.0.1/32 - Добавит запись в таблицу permgroup ip-адрес 10.10.0.1/32;
  2. pf.sh delete 10.10.0.2/32 - Добавит запись в таблицу permgroup ip-адрес 10.10.0.2/3;2;
  3. pf.sh show - Выведет список адресов из таблицы permgroup.

3.2. Авторизация клиента:
3.2.1. index.html:
<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta name="MobileOptimized" content="240"/>
    <meta name="viewport" content="initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=yes, width=240" />
    <title></title>
  </head>

  <body bgcolor="#ffffff">
    <form action="valid.php" id="valid" method="post">
    <table>
      <tr>
        <td colspan="2" align="center">
          <img src="img/logo_150x40.gif" >
        </td>
      </tr>
      <tr>
        <td colspan="2" align="center">
          &nbsp;
        </td>
      </tr>
      <tr>
        <td>
          Логин
        </td>
        <td>
          <INPUT type="text" size="15" id="user" name="user" value="" placeholder="Логин">
        </td>
      </tr>
      <tr>
        <td>
          Пароль:
        </td>
        <td>
          <INPUT type="password" size="15" name="passwd" value="" placeholder="Пароль">
        </td>
      </tr>
      <tr>
        <td colspan="2" align="center">
          &nbsp;
        </td>
      </tr>
      <tr>
        <td colspan="2" align="center">
          <INPUT style = "width:100%; height:50; font-size: 25pt;" type="submit" value="Вход">
        </td>
      </tr>
    </table>
  </body> 
</html>
В принципе, стандартная форма авторизации за исключением того, что она адаптирована под мобильные платформы.
3.2.2. valid.php:
<?php
  session_start(); 
  error_reporting(E_ALL); 
  if (!isset($_POST["user"])) { 
    header("Location: "."index.html"); 
    exit(1); 

  require "config.php"; 
  require "func.php"; 
  
  date_default_timezone_set('Asia/Yekaterinburg'); 
  $pUser = ""; 
  $pPass = ""; 
  $pIp = ""; 
  $pId = ""; 
  $date = date("Y-m-d H:i:s"); 
  $day = date("Y-m-d 00:00:00"); 

  if (!preg_match ('/[^\w]/', $_POST["user"])) { 
    $pUser = $_POST["user"]; 
  else { 
    srv_error(1); 

  if (!preg_match ('/[^\w]/', $_POST["passwd"])) { 
    $pPass = $_POST["passwd"]; 
  else { 
    srv_error(1); 
  }
 
  $db1 = mysql_connect($db_host1, $db_user1, $db_pass1) or die(); 
  mysql_select_db($db_name1, $db1); 
  $result1 = mysql_query("SELECT * FROM abonent WHERE username ='$pUser' AND password='$pPass';", $db1); 

  $db2 = mysql_connect("127.0.0.1", "root", "") or die(); 
  mysql_select_db("wifi", $db2); 

  $row = mysql_fetch_array($result1, MYSQL_NUM); 
  if ($row === FALSE) { 
    accesslog($logfile, $_SERVER["REMOTE_ADDR"], $pUser, $pPass, 1); 
    srv_error(1); 
  else { 
    $result2 = mysql_query("SELECT ip FROM log WHERE user ='$pUser' AND dt >= '$day';", $db2); 
    while(($row = mysql_fetch_array($result2, MYSQL_NUM)) !== FALSE) { 
      shell_exec("/usr/home/admin/wifi/pf.sh" . " delete " . $row[0] . "/32"); 
    }
    shell_exec("/usr/home/admin/wifi/pf.sh" . " add " . $_SERVER["REMOTE_ADDR"] . "/32");
 
    mysql_query("INSERT INTO log (dt, ip, user, passwd, ok) VALUES ('$date', '{$_SERVER["REMOTE_ADDR"]}' ,'$pUser' , '', 1)", $db2) or die(); 
    header("Location: "."http://ya.ru"); 
?>
Скрипт берет данные о логине и пароле из db1, а логирование авторизации ведет в db2.
Скрипт открывает доступ в инет, в случае успешной авторизации. Если есть 2 сессии, то первая открытая сессия будет закрыта.
3.2.3. func.php: <?php function srv_error($value) {   if ($value == 1) {     $meta = "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n<meta http-equiv=\"refresh\" content=\"15; url=index.html\"><title>Ошибка доступа.</title>";     $msg = "<h1>Не знаю что не правильно вы ввели, но вы не авторизовались.</h1>";   }   if ($value == 2) {     $meta = "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n<meta http-equiv=\"refresh\" content=\"15; url=index.html\"><title>Ошибка сервера.</title>";     $msg = "<h1>Внутренняя ошибка сервера.</h1>";   }     echo "<html>     <head>"       . $meta .         "</head>         <body> "          . $msg .         "</body>       </html>";     unset($_SESSION["id"]);     exit(1); } function accesslog($file, $ip, $login, $passwd, $flag) {   $date = date("d.m.Y H:i:s");   if ($flag == 0) { $log = "[" . $date . "] from " . $ip . " " . $login . " " . $passwd . " GRANTED\n";   }   elseif ($flag == 1) {   $log = "[" . $date . "] from " . $ip . " " . $login . " " . $passwd . " DENIED!\n";   }

    $fp = fopen($file, 'a+');
    fwrite($fp, $log);
    fclose($fp); 
  }    
?>


srv_error - функция подставляет сообщение об ошибке (неверный логин/пароль или внутренняя ошибка). accesslog - функция, которая ведет запись в файл попытки авторизации, сохраняя логин и пароль. 4 и 5. Apache и php оставляем без изменений. Единственное, что надо сделать, так это установить php5-extentions пакет для того, чтобы php понимал как работать с СУБД. 6. MySQL-server, Установка mysql сервера проходит в штатном режиме, а вот БД создать придется.
CREATE DATABASE wifi;
CREATE TABLE `log` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `dt` datetime DEFAULT NULL,
  `ip` varchar(32) DEFAULT NULL,
  `user` varchar(255) DEFAULT NULL,
  `passwd` varchar(255) DEFAULT NULL,
  `ok` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=latin1;



Одна единственная таблица в БД.




7. dhcp

Настройка dhcp делается за 2 минуты. Я выдаю ip на 12 часов.

Конфиг:

option domain-name "example.org"; 
option domain-name-servers ns1.example.org, ns2.example.org; 
default-lease-time 43200; 
max-lease-time 43201; 
log-facility local7;

subnet 10.10.0.0 netmask 255.255.0.0 {
  range 10.10.228.1 10.10.228.254;
  option routers 10.10.0.2;
  option domain-name-servers 10.10.254.253;
}

Выделяем ip адреса для этого dhcp из диапазона 10.10.228.0/24, 10.10.0.2 - адрес этого же сервера, который еще и является шлюзом.

8. sudo
Сразу расставим все точки. Дабы не было путаницы. Политика безопасности в freebsd определяется группами, т.е. если пользователь принадлежит группе wheel, то пользователь может выполнить команду su, в остальных случаях sorry. В linux (в частности ubuntu/debian)  sudo используется для разграничения пользователей, которые могут выполнять ту самую команду su. Мы будем использовать sudo для того, чтобы выполнять действия, на которые нужны привилегия su, а именно приложение pfctl на право добавления/удаления записи в таблицу. Вот, собственно, конфиг sudoers:

Cmnd_Alias WIFI = /sbin/pfctl , /bin/sh
%www ALL = NOPASSWD : WIFI


Эти 2 строки добавляются в самый конец файла. Поясню, что создается алиас WIFI для списка программ pfctl и sh. Далее разрешаем использовать команды, прописанные в WIFI для группы www. Именно для группы. Почему для www? потому что скрипт valid.php выполняется от пользователя www.

9. mrtg
Чтобы хоть как-то мониторить количество открытых сессий на сервере можно набросать скрипт для mrtg, который будет отрисовывать график с подключениями.

#!/bin/sh

in=`/sbin/pfctl -t permgroup -T show | /usr/bin/wc -l`
out=`/sbin/pfctl -t permgroup -T show | /usr/bin/wc -l`

/bin/echo $in
/bin/echo $out
/bin/echo `/usr/bin/uptime | /usr/bin/cut -d, -f1`
/bin/echo authuser



В заключении рассмотрим настройку самого pflog. 
Настраивается он добавлением этих 2х строк в /etc/rc.conf:
pflog_enable="YES"
pflog_logfile="/var/log/pflog"


freeradius отладка.

Рассмотрим простую отладку для freeradius.
На radius-сервере даем команду:
freeradius —X —d /etc/freeradius
ключ X показывает на вход в режим отладки, а ключ d показывает место расположения файлов конфигурации (в примере указана директория для ubuntu. Кстати, если не указывать путь, то приложение запустится с конфигом из директории по умолчанию).
На клиенте:
radtest login password IP_SERVER 1812 community
Думаю, все понятно. 1812 — udp-порт, по умолчанию.

mpd5. Логгирование.

Сервис mpd5 после установки и запуска демона не ведет логи, как это делает, скажем, apache. В связи с этим появляется одна из проблем — анализа запуска и работы службы. Так как заставить mpd5 делать логи? В паутине написано много примеров, но я не нашел ни одного стоящего. И так, ничего лишнего.

Сначала создадим файл:
touch /var/log/mpd5.log

Теперь дадим на него права:
chmod 600 /var/log/mpd5.log

Теперь заставим syslogd писать логи от mpd5, для этого впишем всего 2 эти строки в /etc/syslog.conf:
!mpd
*.* /var/log/mpd5.log

После этого перезапустим сервис syslogd:
/etc/rc.d/syslogd restart

Теперь запустим mpd5:
/usr/local/etc/rc.d/mpd5 start

И проверяем логи:
cat /var/log/mpd5.log

Генератор паролей на python

От нечего делать написал генератор сложных паролей.


#!/usr/bin/python 
# -*- coding: utf-8 —*- 

import sys 
import random 

# Функция генерации пароля. 
def generator(x): 
 s = '' 
 if (x  > 0): 
  for i in range(1, x+1): 
   rnd = random.randint(33, 126) 
   s += chr(rnd) 
 else: 
  return 0 
 return s 


arg = sys.argv 
arg.pop(0) 
argc = len(arg) 
flags = ['-n', '-s'] 
if argc != 0: 
 for x in arg: 
  if x in flags: 
   ind = arg.index(x) 
   param = arg[ind+1] 
   if x == '-n': 
    print generator(int(param)) 
   elif x == '-s': 
    print ind, param 




Программка сырая, но пока что справляется со своим прямым назначением. Вот пример:
administrator@HP:~/prj/py/passgen$ ./gen.py —n 16
&FP@-&7x,eHuy~9q

python ASCII


Столкнулся с задачей получения ascii-кода символа и символа по ascii-коду.
Решение:

Получаем символ по коду:
chr(97)
Результатом будет строка 'a'.


Получаем код символа:
ord('a')
Результатом будет число 97.


Так же есть такая инструкция:
chr(ord('a') + 2)
Результатом будет строка 'c'.


Unicode:
Получаем символ:
unichr(97)

Результатом будет unicode-строка: u'a' 

mpd5 web консоль

Наткнулся на интересную вещь. В mpd5, помимо telnet консоли, есть своя вэб консоль и, как бы парадоксально не звучало, telnet консоль, доступная через web. Работает она несколько неудобно, т.к. проблемно получить команды консоли, и тем более надоедает вместо пробелов вбивать %20. Но тем не менее делается это так:


http://IP_NAS:WEB_PORT_MPD5/bincmd?help

Это выведет список доступных команд. А вот теперь отличие - в консоли (telnet) можно набрать такую команду:

show ?

И получим полный вывод справки о пункте консоли show. В вэб все обстоит несколько иначе. Сначала пишется help, а затем указывается то, что нас интересует:






http://IP_NAS:WEB_PORT_MPD5/bincmd?help%20show

Получим список команд доступных в пункте show. А если выполнить:
http://IP_NAS:WEB_PORT_MPD5/bincmd?help%20show%20session  

Получим информацию об использовании подменю session, находящемся в show. Аналогичным образом можно выполнить запрос на список сессий (список авторизовавшихся):
http://IP_NAS:WEB_PORT_MPD5/bincmd?show%20session  

И объем занятой памяти:
http://IP_NAS:WEB_PORT_MPD5/bincmd?show%20mem 

Простейший клиент и сервер на python (документация)

Сервер:

import SocketServer

class MyTCPHandler(SocketServer.BaseRequestHandler):
"""
The RequestHandler class for our server.

It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
"""

def handle(self):
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
print "%s wrote:" % self.client_address[0]
print self.data
# just send back the same data, but upper-cased
self.request.send(self.data.upper())

if __name__ == "__main__":
HOST, PORT = "localhost", 9999

# Create the server, binding to localhost on port 9999
server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)

# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()

Клиент:
import socket
import sys

HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])

# Create a socket (SOCK_STREAM means a TCP socket)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Connect to server and send data
sock.connect((HOST, PORT))
sock.send(data + "\n")

# Receive data from the server and shut down
received = sock.recv(1024)
sock.close()

print "Sent: %s" % data
print "Received: %s" % received

Соответственно расстарляем табуляцию.

Простейший вэб сервер на Python


python -m SimpleHTTPServer


Запустит простейший вэб-сервер на Python'е. Считаю полезным для быстрого доступа к собственной машине.
Внимание. Корнем сервера будет считаться pwd директория, в которой выполняется команда.

Python + MySQL. Работа с кириллицей.

Столкнулся с проблемой выбора кириллических записей из БД mysql. Проблема заключалась в том, что при выборе я получал вместо русских букв знаки вопроса. Вопрос решился чтением мануала. Решение получил такое:


#!/usr/bin/env python
# -*- coding: koi8-r -*-

Код программы с получением данных из MySQL:

import MySQLdb

local = MySQLdb.connect (host = "localhost", user = "root", passwd = "root_passwd", db = "mydb", charset = "koi8r", use_unicode = False)

local_cursor = local.cursor (MySQLdb.cursors.DictCursor)
local_cursor.execute ("SELECT * FROM my_table")

result_set_local = local_cursor.fetchall ()
for local_row in result_set_local:
  print str(local_row['russian'])

# -*- coding: koi8-r —*- - следует использовать для вывода результата в консоль.

Python. Curses


Поискав в инете инфу о curses в языке си и python — ничего толкового не нашел. Нашел только кривой мануал на офф сайте питона, да и тот тоже не особо ровный — прыжки с места на место. Решил как и всегда, написать свое объяснение curses «как для себя». Я попрежнему пишу на 2,6 версии.
Задача — вывести цветной текст в терминал.
Вот такое решение получаю.


#!/usr/local/bin/python

#Подключаем модуль curses, с которым, собственно, и работаем

import curses

#Подключаем модуль times, нужен только в конце скрипта для задержки на выход.
import time

#инициалищируем работу с экраном.
stdscr = curses.initscr()

# отключаем отображение курсора в терминале. см. описание ниже.
curses.curs_set(0)

#разрешаем работу с цветом.
curses.start_color()

#Объявляем, так называемую, палитру цветов.

#Палитра состоит из идентификационного номера, цвет текста, цвет фона

curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)

# просто описанная переменная, хотел проверить вывод значений в следующей процедуре.

a = 1

# процедура выводит строку (и только строку). Строка начнется в координате x=10 и y=10 знакомест. Текст будет окрашен в палитру 1.
stdscr.addstr(10,10, str(a), curses.color_pair(1))

#Обновляем то, что написали.

stdscr.refresh()

#Делаем задержку перед выходом из программы "чтобы посмотреть"
time.sleep(3)





#Возвращаем стандартное отображение курсора.
curses.curs_set(1)

Доступные цвета:

# black 0
# red 1
# green 2
# yellow 3
# blue 4
# magenta 5
# cyan 6
# white 7

хотя даже в доке сказано, что лучше пользовать их через переменные модуля curses — curses.COLOR_BLACK или curses.COLOR_WHITE.

curses.curs_set(значение) - позваляет управлять отображением курсора ввода с клавиатуры в терминале. Можно использовать следующие значения:
0 — скрыть курсор.
1 — стандартный курсор.
2 — непрозрачный курсор.
Следует отметить, что если не вернуть стандартный курсор по окончании программы, то могут возникнуть некоторые трудности при работе в терминале (не отображаемый курсор или непрозрачный курсор).

Netflow. Flow_capture и flow_fanout как это настраивается.

Нужно было организовать сервер статистики посещения интернет ресурсов своими абонентами. Настроил mpd5 на отдачу статистики, статисика уходит  на принимающий сервер, но дальше ничего не происходит. С толку сбил параметр для rc.conf — flow_fanout_remoteip. Этот параметр указывает на ip той машины, от которой нужно принимать статистику. В итоге конфиг выглядит так:


flow_capture_enable="YES"
flow_capture_localip="127.0.0.1"

#IP, на котором запущена flow_capture
flow_capture_remoteip="127.0.0.1"

#IP, от которого принимается flow-статистика. По умолчанию 0.0.0.0
flow_capture_port="9998"

#Порт на который принимается статистика
flow_capture_datadir="/usr/local/_dump/ng"

#Путь по которому будет сохраняться статистика
flow_capture_flags="-S60 -z9 -n95 -N0"
# Разные флаги (см. man)



flow_fanout_enable="YES"
#Включаем flow_fanout
flow_fanout_ip="10.10.254.1"
#Локальный ip для импорта
flow_fanout_remoteip="0.0.0.0"
#ip импортирующей машины. По умолчанию 0.0.0.0
flow_fanout_port="9996"
#Порт импортера
flow_fanout_export="127.0.0.1/127.0.0.1/9998 10.10.254.1/10.10.0.2/9997"

* ОС FreeBSD, файл rc.conf

Восстановление пароля 3Com 4200 series


Восстановление пароля на 3Com серия 4200 происходит путем ввода логина и пароля как recover.

В итоге получается такая картина:


Login: recover
Password:

*** Password Recovery Mode ***
The administrative password will be cleared if a hard reset operation is
carried out on the device within 30 seconds.

If a hard reset operation is not carried out during this period, the device
will return to the CLI login prompt

countdown = 0
Login:

Python + telnet

Столкнулся с проблемой организации telnet из python'а. Казалось бы, что проще — подключил библиотеку telnetlib и пользуйся? Однако не все так просто. В примерах, которые приведены на сайтах разработчиков и официальном сайте, при вводе логина/пароля и передаче команд используется символ \n (якобы для эмулирования нажатия клавиши enter), однако этот пример не работает у меня и нужно использовать "\r». Поясню почему. В документации говорится, что "\n» - символ перехода на новую строку с внутренним кодом 0x0A, а "\r» - символ возврата каретки с внутренним кодом 0x0D. Получается, что устройства, к которым я пытался подключиться отлавливают именно 0x0D, а 0x0A просто добавляли вконец написанной строки. В итоге код получился таким:


#!/usr/bin/env python
import sys
import telnetlib
import time


tn = telnetlib.Telnet("10.10.0.1", 23) #подключаемся к узлу 

tn.read_until("Router  >") # отлавливаем приглашение, которое заканчивается "Router  >"
tn.write("enable\r") # вводим команду (Обратить внимание на \r)

tn.read_until("Password:") #отлавливаем приглашение с вводом пароля
tn.write("my_pass\r") # вставляем пароль (Обратить внимание на \r)

tn.read_until("telnet@BigIron Router#") #отлавливаем приглашение, информирующее о входе в систему
tn.write("show chassis\r") # выполняем команду
s = tn.read_until(" C degrees") # считываем результат до определенного слова

print type(s) #навсякий случай узнаем что мы получили, а то мало ли

tn.close(); #закрываем сессию
print s # выводим полученный результат.

Python with SNMP

 Статья вновь из разряда "собрать самолет с нуля при потерянной инструкции". Кратко предыстория. Сначала пробовал PySNMP — не получилось. При запуске программы орет на пропущенные скобки, хотя все на месте.
Позже пробовал twistedsnmp — такая же проблема где-то пропущена закрывающаяся скобка и программа не запускается.
Отчаявшись — решил собрать библиотеку дла python'а из Net-snmp. При выполнении


python python.sh build

выдается более 20000 строк об ошибках и сообщение, что 'cc' имеет ошибки.

И тут, покапавшись в портах, нахожу программку с невзрачным названием 
yapsnmp (как позже оказалось это абривеатура). Установка прошла гладко. 
Но смутил пример автора для выполнения запроса:





yapsnmp.Session('router45-ny').walk('system')

Проблема была в следующем - т.к. у меня все мои девайсы используют 
разные версии snmp, то в примере автора не было указано ни версии 
протокола, ни комьюнити. Открыв доки - тоже не нашел ничего внятного, 
кроме того, что было в общих чертах указано что они могут быть. 
Попарился немного и о чудо!!! Программа заработала. Вот так все будет 
100% работать:

import yapsnmp
print yapsnmp.Session('10.10.1.1', version=2, community='public').walk('ifOperStatus')

А вот, собственно, вывод:





(('ifOperStatus.1', 'up'), ('ifOperStatus.2', 'up'), ('ifOperStatus.3', 'down'), ('ifOperStatus.4', 'down'), ('ifOperStatus.5', 'down'), ('ifOperStatus.6', 'down'), ('ifOperStatus.7', 'down'), ('ifOperStatus.8', 'down'), ('ifOperStatus.9', 'down'), ('ifOperStatus.10', 'down'), ('ifOperStatus.11', 'down'), ('ifOperStatus.12', 'down'), ('ifOperStatus.13', 'down'), ('ifOperStatus.14', 'down'), ('ifOperStatus.15', 'down'), ('ifOperStatus.16', 'down'), ('ifOperStatus.17', 'down'), ('ifOperStatus.18', 'down'), ('ifOperStatus.19', 'down'), ('ifOperStatus.20', 'down'), ('ifOperStatus.21', 'down'), ('ifOperStatus.22', 'down'), ('ifOperStatus.23', 'down'), ('ifOperStatus.24', 'down'), ('ifOperStatus.25', 'down'), ('ifOperStatus.26', 'down'), ('ifOperStatus.27', 'down'), ('ifOperStatus.28', 'down'), ('ifOperStatus.29', 'down'), ('ifOperStatus.30', 'down'), ('ifOperStatus.31', 'down'), ('ifOperStatus.32', 'down'), ('ifOperStatus.33', 'down'), ('ifOperStatus.34', 'down'), ('ifOperStatus.35', 'down'), ('ifOperStatus.36', 'down'), ('ifOperStatus.37', 'down'), ('ifOperStatus.38', 'down'), ('ifOperStatus.39', 'down'), ('ifOperStatus.40', 'down'), ('ifOperStatus.41', 'down'), ('ifOperStatus.42', 'down'), ('ifOperStatus.43', 'down'), ('ifOperStatus.44', 'down'), ('ifOperStatus.45', 'down'), ('ifOperStatus.46', 'down'), ('ifOperStatus.47', 'down'), ('ifOperStatus.48', 'down'), ('ifOperStatus.49', 'up'), ('ifOperStatus.50', 'down'), ('ifOperStatus.51', 'down'), ('ifOperStatus.52', 'up'))

Думаю, что кому-то пригодится.


UPD @2015.09.11
Разработка одного из проектов для ОС Ubuntu потребовала использование SNMP. Указанный ранее yapsnmp в репозитории не обнаружен. Пришлось использовать доступный python-netsnmp.
Работает следующим образом:

import netsnmp
ifOperStatus = '.1.3.6.1.2.1.2.2.1.8'
res = netsnmp.snmpwalk(ifOperStatus, Version=1, DestHost='***.***.***.***', Community='public')

Документация подразумевает немного другое использование
import netsnmp 
var = netsnmp.Varbind('sysDescr.0') 
res = netsnmp.snmpget(var, ...: Version = 1, ...: DestHost = 'localhost', ...: Community='public') 
print res ('Linux machine.pretendco.com 2.4.7-10lpc #1 Tue Nov 13 03:44:18 EST 2001 i686',)
Вся проблема в том, что в Ubuntu нужно дополнительно указывать соответствие MIB и OID. Следуя документации, получаю ошибку: 
error: walk: unknown object ID (sysDescr)None


Python + MySQL (записки админа)


 Поставили задачу записать в данные в MySQL средствами python. Проблема не была бы столь обширной, если бы не одно но(!) - манов по этой части, на момент написания этой статьи в моем предыдущем блоге (laa88.blog.ru), было не так уж и много, если не сказать, что не было совсем. Привожу пример кода + выборку данных из СУБД:


#!/usr/bin/env python

import MySQLdb



ldb_host = "localhost";
ldb_user = "root";
ldb_pass = "root_pass";
ldb_name = "lan";
local_connect = MySQLdb.connect (ldb_host, ldb_user, ldb_pass, ldb_name)

local_cursor = local_connect.cursor (MySQLdb.cursors.DictCursor)
local_cursor.execute ("SELECT * FROM abonents where id=433")
local_result = local_cursor.fetchall ()
for row in local_result:

       print "%s " % (row["switch_model"])
local_cursor.close ()

local_connect.close ()