Сквозь порты на оборудовании к пользовательским машинам

    Доброе время суток, Хабражители.

    Данный пост повествует Вам о том, как с помощью PowerShell мы опять смогли немного облегчить нам жизнь и автоматизировать поиск оборудования и портов, на которых сидят компьютеры пользователей. Это необходимо в тот момент, когда надо пробросить vlan`ы (ну или просто для информации).
    "

    Предыстория

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

    Используя Putty, я попробовал проделать все это вручную, все получилось и я начал думать как все это автоматизировать. Да не просто автоматизировать, а сделать это с помощью PowerShell. Почему именно PoSh? В то время я на него сильно запал (хотя я и сейчас с него не слезаю), можно было сделать это в чем-нибудь другом, но мне очень сильно хотелось сделать это через PoSh.

    Так как подключаться к оборудованию требовалось через Telnet и SSH (в основном, через Telnet, так как на тот момент SSH был не везде, но об этом позже) я провел немало времени в интернете, что бы узнать как PowerShell может работать с этими протоколами.
    Тогда я обратился к 2 механизмам подключения:
    1. использование plink.exe (из Putty)
    2. Netcmdlets фирмы /n software

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

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

    Vader, rise!

    Самым важным из произошедшего было то, что обновили прошивки у коммутаторов, и теперь стало возможно подключаться к ним по SSH. Что же, отлично, значит забиваем на telnet.
    За этим последовало еще 1 изменение: раз telnet больше не нужен, мы можем не использовать plink.exe (из Putty) и вся слава достается NetCmdlets.
    Пообщавшись не мало с техподдержкой этой фирмы, наконец я разобрался в устройстве этих орудий труда, и начал работу.

    Что мне было нужно:
    1. Проверять правильность ввода IP адреса компьютера
    2. Проверять наличие компьютера в сети
    3. Подключаться к каждому оборудованию последовательно, начиная с самого главного, и выяснять на каких портах висит наше устройство
    4. Показать все это пользователю, что бы он (т.е. я) остался доволен

    NetCmdlets

    Так как я хочу сказать спасибо людям, которые их сделали, небольшое отступление и пару слов об этих командлетах:
    Почитать про них и скачать их можно здесь. Скачать можно как триальную версию (на 30 дней) так и полную (за 100$ на один компьютер). Я пользовался триальной версией, т.к. после окончании срока ее можно просто переустановить.
    Недавно на ресурсе powershellmagazine.com раздавались эти командлеты бесплатно, я успел ухватить. Так что будьте на чеку! Но вернемся к нашим баранам.
    Выбрав набор этих cmdletов, я приступил к их изучению. Этот набор достаточно большой, так что я не буду описывать все, что там есть. остановлюсь на 2:
    • Сonnect-SSh
    • Invoke-SSh

    Можно обойтись только Invoke-SSh, но тогда у вас не будет постоянной сессии с устройством (т.е., скажем, выполнить команды в «conf t» на устройстве уже будет нельзя). По этому командлетом Сonnect-SSh мы создаем подключение к устройству, а командлетом Invoke-SSh выполняем команды. Все достаточно просто.
    Важным моментом является то, что эти командлеты могут работать вместе с командлетом Get-Credential, в которую Вы можете записать учетные данные на подключение к оборудованию (примеры увидите в скрипте), т.е. учетные данные не хранятся в открытом виде в скрипте (как это у меня было с использованием plink.exe). Я не параноик, но перестраховаться люблю.

    Парсинг вывода и регулярные выражения

    Меня ожидал очень романтичный парсинг вывода оболочки Cisco iOS, т.к командлет Invoke-SSH показывает вывод в столбце Text, т.е. передав в переменную вывод, мы получаем большой текст.
    Здесь нам приходят на помощь регулярные выражения. Но кто парсил большие тексты, знает, что достать нужный фрагмент оттуда довольно не просто, но, благодаря PowerShell, мы с блеском вышли из этой ситуации.
    В данный момент читай PowerShell in Action 2-nd Edition. Тому, кто изучает PoSh, я настоятельно рекомендую эту книгу. И в ней я прочел о такой вещи, как named regexp. Суть в том, что при PowerShell содержит переменную $Mathces, в которую заносятся все совпадения при использовании регулярных выражений:

    PS [13] > $Matches
    PS [14] > $a="ass 123 saa"
    PS [15] > $a -match "\d+"
    True
    PS [16] > $Matches
    
    Name                           Value                                           
    ----                           -----                                           
    0                              123 
    

    Но это еще не все! Вся плюшка в том, что если мы добавим
    ? в наше выражение, мы получим следующее:
    PS [17] > $a -match "(?<Numbers>\d+)" True PS [18] > $Matches Name Value ---- ----- Numbers 123 0 123 PS [19] > $Matches.Numbers 123

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

    Сам скрипт

    Собственно, сам скрипт.
    ## Функция вывода информации о конечном оборудовании
    Function LastSwitch
        {
        "IP адрес свича: {0}, порт {1}" -f $IpSwitch,$Port
        If ($CiscoPhone) {"Компьютер подключен через Cisco IP Phone"}
        Read-Host "Press Enter for continue..."
        break   
        }
    ##Проверяем правильность ввода
    For (;;)
        {
        $ip=Read-host "Enter ip"
        If ($ip -match "(\d{1,3}\.){3}\d{1,3}") {break}
        else {Write-Warning "Invalid IP address! Try again..."}
        }
    ##Проверяем на наличие компьютера в сети
    if ((test-connection $ip -quiet) -ne "True") 
        {
        Write-Warning "Компьютер не в сети!"
        Read-Host "Press Enter to continue..."
        break
        }
    $cred = get-credential Admin ##Учетная запись для подключения к устройствам
    $IpSwitch = "10.138.30.1" ##Родоначальник наших коммутаторов
    $MAC = $null
    $CiscoPhone = $false
    For (;;)
    {
    $conn = Connect-ssh -Server $IpSwitch -Credential $cred -ShellPrompt "#" -Force
    Invoke-SSH -Connection $conn -Command "terminal length 0" | out-null
    Invoke-SSH -Connection $conn -Command "ping $ip" | out-null
    If (!$MAC)
        {
        ## Получаем МАС адрес устройства
        ((Invoke-SSH -Connection $conn -Command "sh arp | i $ip ").text | 
            Where-Object {$_ -match "\w"}) -match "(?<MAC>\w{4}\.\w{4}\.\w{4})" | out-null
        $MAC = $Matches.MAC
        }
    ## Находим порт, на котором висит оборудование
    (((Invoke-SSH -Connection $conn -Command "sh mac address-table address $MAC").Text | 
        where-object {$_ -match $mac}) | 
        Select-Object -First 1) -match "(?<port>((\D{2}\d{1,3})/|Po)(\d{1,3})(/\d{1,3})?)" | out-null
    $port = $Matches.Port
    ## Проверяем количество оборудования подключенного к порту
    $portInfo = (Invoke-SSH -Connection $conn -Command "show mac address-table interface $port").Text | 
        where-object {$_ -match $port}
    If (($portInfo | measure).Count -eq 1) {LastSwitch}
    ## Информация о порте
    $DetailPortInfo = (Invoke-SSH -Connection $conn -Command "sh cdp neighbors $port detail" -Force).Text
    ## Если в инфомрации о порту есть запись "Cisco IP phone", значит компьютер подключен чезез IP телефон.
    If ($DetailPortInfo -match "Cisco IP phone") {$CiscoPhone = $true; LastSwitch}
    (($DetailPortInfo | where-object {$_ -match "IP address: (\d{1,3}\.){3}\d{1,3}"}) | 
        Select-Object -First 1) -match "(?<ip>(\d{1,3}\.){3}\d{1,3})" | out-null
    "IP адрес свича: {0}, порт {1}" -f $IpSwitch,$Port
    $IpSwitch = $Matches.IP
    Disconnect-SSH $conn
    }
    

    Небольшие комментарии:
    • Учетные данные мы получаем с помощью команды Get-Credential. Кому лень вводить каждый раз учетные данные, можно сделать автоматически, поместив зашифрованный пароль в текстовый файл. Подробнее об этом можно прочитать тут
    • На наших коммутаторах стоит ограничение на вывод информации за 1 раз (т.е. листать может с помощью пробела (аналогия из PowerShell: get-help | more)). Нас это не устраивает, т.к. Invoke-SSH не умеет листать, и в итоге мы будем получать ошибку. Для решения данной проблемы, мы отключаем это на время сессии с помощью команды terminal length 0
    • ((\D{2}\d{1,3})/|Po)(\d{1,3})(/\d{1,3})?- регулярное выражение, призванное нами для нахождения портов. Оно ловит такие порты как: Fa5, Gi0/2, Te2/0/8, Po255 и т.п. (если вдруг что то забыл, пишите, я подправлю)
    • Некоторые компьютеры у нас могут быть подключены через Cisco IP phone, по этому пришлось добавить еще пару строк, что бы это выяснять
    • Так как нам не нужен вывод лишней информации (от Invoke-SSH), этот вывод мы дружно отправляем в никуда (out-null)
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 8

      0
      Да, жестокое решение, но может работать :)

      Если DHCP не на винде, то можно задействовать option 82 — сервер в discover получит позывные коммутатора и номер порта, в который воткнут клиент — собственно, всё. Просто и элегантно, информация всегда актуальна.
      Ходят слухи, что в Windows Server 2012 добавят полную поддержку option 82.
        0
        Интересный хак. Сам когда-то пользовался www.netdisco.org/ для тех же целей.
          0
          Ээээ. А повершелл не может в дотнетные либы? Есть же нормальный способ подключаться по SSH.
            0
            Ну, если честно, я не пойму чем это отличается от приведенных мною командлетов? Сама библиотека не позволит PoSh`у подключаться по SSH, все равно нужны командлеты которые ее используют. Если не прав, поправьте.
            На счет нормального способа, читал что в PowerShell v3 обещали добавить командлеты для работы по SSH. Будем надеяться.
            0
            А по протоколу SNMP эту задачу нельзя было решить? Например используя это: (правда, для ком. пользования платная) xaegr.wordpress.com/2008/03/11/netcmdlets/
              0
              ой, простите меня, невнимательного. Вы упоминали их в начале статьи.
                0
                Командлеты Connect-SSH & Invoke-SSH из этого же модуля :)
                Когда только писал этот скрипт (год назад) я пробовал через SNMP, но к сожалению я завяз там при поиске информации о MAC адресах и т.п., в итоге я отказался от этой затеи.
                  0
                  К сожалению, не нашел страницу, которая попадалась мне на глаза про порты и snmp.
                  Вот похожий пример на Хабре, надеюсь будет вам интересен. habrahabr.ru/post/128439/

            Only users with full accounts can post comments. Log in, please.