Network Asset Management – jak przy pomocy narzędzi skryptowych i bazy SQL-owej uprościć zarządzanie zasobami sieciowymi

rack_cablingWiele przedsiębiorstw nie posiada specjalistycznych, komercyjnych narzędzi wspomagających zarządzanie aktywami sieciowymi, czy też – szerzej rzecz ujmując – aktywami informatycznymi w ogóle. Przeważnie tego typu narzędzia są drogie i nie zawsze rachunek ekonomiczny przemawia za ich zakupem i implementacją.

Niniejszy artykuł poświęcony jest temu, jak przy odrobinie wiedzy oraz chęci, zbudować rozwiązanie o podobnej funkcjonalności, na dodatek zrobić to całkowicie za darmo, bazując na bezpłatnie dostępnych narzędziach. Przyglądając się zaprezentowanemu w nim prototypowemu rozwiązaniu, czytelnik będzie miał okazję poznać jeden ze sposobów, jak wykorzystać narzędzia skryptowe i wyrażenia regularne do ‚wydobywania’ z danych diagnostycznych, pozyskanych z urządzeń sieciowych, wartościowych informacji, a następnie zasilić nimi bazę SQL-ową i tym samym zyskać potężny, a jednocześnie prosty w użyciu interfejs do przetwarzania danych.

Załóżmy, że administrator sieci planuje zbudować bazę danych o wszystkich routerach i przełącznikach Cisco w swojej sieci. O każdym z tych urządzeń chciałby wiedzieć przykładowo: jaki to konkretnie model urządzenia, w oparciu o jaki system operacyjny działa (w jakiej konkretnie wersji występuje on na urządzeniu), jakie posiada interfejsy sieciowe i jakimi zasobami pamięci dysponuje (RAM, NVRAM i FLASH). Zaproponowana tutaj lista informacji jest oczywiście umowna i bynajmniej nie wyczerpuje wszystkich potencjalnych możliwości. Trzeba było jednak przyjąć pewne uproszczone założenia, żeby w dalszej kolejności mógł powstać nieskomplikowany prototyp narzędzia, ilustrującego działanie w praktyce tego, o czym mowa w artykule.

By wcielić w życie wspomniany plan, administrator winien zabrać się za pisanie skryptu (na przykład w języku Ruby) do pozyskania danych o zasobach sieciowych, a później wykorzystać dowolną bazę SQL-ową, by zasilić ją wyekstrahowanymi danymi. Ja na potrzeby artykułu wykorzystałem bezserwerową bazę SQLite, którą polecam do testów i do operowania na niewielkich rozmiarach baz. Polecam ją z uwagi na szereg zalet, z których najważniejsza jest prostota. Niewątpliwą zaletą SQLite jest ponadto możliwość błyskawicznej implementacji na dowolnej platformie (Windows, MacOS, Unix). Rzecz jasna, nic nie stoi na przeszkodzie, by skorzystać z innych, serwerowych systemów bazodanowych, takich choćby jak: Postgres, MySQL, Sybase, Oracle czy MS SQL Server.

Zaprezentowany poniżej skrypt czyta wszystkie pliki z umownie przyjętym rozszerzeniem nazwy „.conf”, znajdujące się w katalogu roboczym skryptu (też kwestia umownych założeń). Oczywiście, jest możliwe by zastosować inną konwencję nazewnictwa lub zmienić katalog roboczy, albo po prostu stosownie sparametryzować skrypt tak, by te dane można było podać jako parametry w linii poleceń, za każdym jego wywołaniem. Pliki „.conf” w naszym przykładzie noszą nazwy adekwatnie do nazw urządzeń, skąd pochodzą zawarte w nich dane. Zaś wspomniane dane to informacje wyprowadzane na standardowe wyjście poleceniem diagnostycznym: „show version” wykonanym na konsoli urządzenia. Przykładowo dane pochodzące z routera „core_router” znajdują się w pliku „core_router.conf” i mogą prezentować się następująco:

Cisco Internetwork Operating System Software
IOS (tm) 7000 Software (C7000-JS-M), Version 11.2(21), RELEASE SOFTWARE (fc1)
Copyright (c) 1986-1999 by cisco Systems, Inc.
Compiled Wed 15-Dec-99 23:44 by ccai
Image text-base: 0x00001000, data-base: 0x008F86E8

ROM: System Bootstrap, Version 11.2(3), SOFTWARE
ROM: 7000 Software (C7000-AJSV-M), Version 11.2(3), RELEASE SOFTWARE (fc2)

Router uptime is 1 hour, 38 minutes
System restarted by power-on at 15:19:36 MEST Tue Apr 25 2000
System image file is "c7000-js-mz_112-21.bin", booted via tftp from 172.17.240.250

cisco RP1 (68040) processor (revision C0) with 65536K bytes of memory.
Processor board ID 0025A50A
G.703/E1 software, Version 1.0.
SuperLAT software copyright 1990 by Meridian Technology Corp).
Bridging software.
X.25 software, Version 2.0, NET2, BFE and GOSIP compliant.
TN3270 Emulation software.
1 Switch Processor
1 EIP controller (6 Ethernet).
1 TRIP controller (4 Token Ring).
1 AIP controller (1 ATM).
6 Ethernet/IEEE 802.3 interface(s)
4 Token Ring/IEEE 802.5 interface(s)
1 ATM network interface(s)
128K bytes of non-volatile configuration memory.
4096K bytes of flash memory sized on embedded flash.

Configuration register is 0x2102

W trakcie czytania pliku, skrypt dokonuje jego analizy metodą „wiersz po wierszu” i szuka w czytanych wierszach dopasowań do warunków wyrażeń regularnych – narzędzia o potędze trudnej do przecenienia w rękach świadomego administratora. Celem tego typu analizy jest oczywiście „wyciągnięcie” z plików interesujących nas informacji, by w dalszej kolejności zapisać je w bazie aktywów sieciowych (Network Assets Database). Baza umożliwi później wygodne, szybkie, wydajne  i intuicyjne zapytania o aktywa.

Załóżmy, że dysponując już wcześniej przygotowaną bazą, chcemy uzyskać informacje o wszystkich routerach, które wyposażone są w więcej niż 32 MB pamięci FLAH i posiadają co najmniej jeden interfejs TokenRing. Wykonujemy w związku z tym następujące zapytanie do bazy:

SELECT * FROM Assets WHERE FLASH > 3200 AND INTERF LIKE '%Token%';

Zapytanie to możemy skierować do bazy wprost z poziomu linii poleceń interpretera komend systemu bazodanowego, jak w poniższym przykładzie: Uruchamiamy sqlite i wskazujemy interesującą nas bazę danych. Baza danych zawiera tabelę Assets ze zinwentaryzowanymi aktywami sieciowymi.

\home\janusz$ sqlite net_asset.db
SQLite version 3.7.14.1 2012-10-04 19:37:12
Enter ".help" for instructions
Enter SQL statements terminated with a ";"

sqlite> .table
Assets
sqlite> .mode line
sqlite> .schema
CREATE TABLE Assets(ID INTEGER PRIMARY KEY AUTOINCREMENT, Hostname TEXT, IOS TEXT, RAM NUMERIC, NVRAM NUMERIC, FLASH NUMERIC, INTERF TEXT);
sqlite> select * from Assets where FLASH > 3200 and INTERF like '%Token%';

ID = 2
Hostname = router_core
IOS = IOS (tm) 7000 Software (C7000-JS-M) Version 11.2(21) RELEASE SOFTWARE (fc1)
RAM = 65536
NVRAM = 128
FLASH = 4096
INTERF = 6 Ethernet/IEEE 802.3 interface(s); 4 Token Ring/IEEE 802.5 interface(s); 1 ATM network interface(s)

...
sqlite>

Proszę zauważyć, że polecenie „.schema” pokazuje strukturę tabeli Assets w postaci polecenia SQL-owego, przy pomocy którego tabela została wykreowana w bazie dennych.

Możemy też uczynić to fragmentem kodu w języku Ruby w taki na przykład sposób:

query = db.prepare " SELECT * FROM Assets WHERE FLASH > 3200 AND INTERF LIKE '%Token%'"
answer = query.execute
answer.each do |row|
   puts row.join "\s"
end

Kod skryptu prezentuje się następująco:

# Script for educational purposes, to ilustrate how to create useful tools for facilitating network assets management.
# Author: Janusz Nawrat - 2014

require 'sqlite3'   # Please remember to install the appropriate gem: gem install sqlite3

# -------------------------------------------------------------
# Function for retrieving relevant data from files containing show version data collected from Cisco devices
# The applied convention is: One file contains data from one device and the file name is consistent with hostname 
# of the device.
# -------------------------------------------------------------

def get_data(filename)
   result = ""; interfaces = ""
   result << "#{File.basename(filename, ".*").chomp},"
   begin
      File.open(filename, "r") do |file|
      # Please notice some simple examples of regular expressions
      # used to retrieve data from files with diagnostic data
      while (line = file.gets)
         if line =~ /IOS/
            result << "#{line.gsub(',', '').chomp},"
         elsif line =~ /bytes of memory/
            if line.match '(\d{1,})K/(\d{1,})K'
               result << "#{$1.to_i + $2.to_i}"
               # puts "RAM: #{$1} and #{$2} make sum: #{$1.to_i + $2.to_i}"
            else
               result << "#{line.match '\d{1,}K'},"
            end
         elsif line =~ /non-volatile/ or line =~ /NVRAM/
            result << "#{line.match '\d{1,}K'},"
         elsif line =~ /flash memory/
            result << "#{line.match '\d{1,}K'},"
         elsif line =~ /interface/
            interfaces << line.chomp! << "; "
         end
      end
   end
   rescue => e
      puts "Error: #{e}"
   end
   interfaces.gsub!(/;\s*$/, ""); result << interfaces
   # puts "#{filename}: #{result}"
   return result
end

# -------------------------------------------------------------
# Function for supplying database with data prepared by the diagnostic data regexp filtering
# -------------------------------------------------------------

def insert_into_database(database)
   begin
      db = SQLite3::Database.open database

      # The Asset table is created in database if not exists
      db.execute "CREATE TABLE IF NOT EXISTS Assets(ID INTEGER PRIMARY KEY AUTOINCREMENT, Hostname TEXT, IOS TEXT, RAM NUMERIC, NVRAM NUMERIC, FLASH NUMERIC, INTERF TEXT)"

      # Files with diagnostic data are searched in current directory and then the list of their names 
      # was created and assigned to the config_file variable.
      # The diagnostic data file have by convention .conf filename extension and the filename is the same 
      # like hostname of the device to which they relate.

      config_file_list = Dir.glob("./**/*.conf")
      if config_file_list.length > 0
         puts "The following config files detected: "
         config_file_list.each do |file|
            puts file
         end
      else
         puts "Nothing to process... Quitting!"
         return
      end
      
      print "\nDo you really want to proceed with supplying assets to database? (Y/N) "
      begin
         answer = gets.chomp.upcase
         if answer == "N"
            puts "Breaking execution of the script..."
            return
         end
      end while answer != "Y"

      config_file_list.each do |config|
         # Each file is subject to filtering defined in get_data() function code
         values = get_data config

         # Data is parsed and assigned to the appropriate variables
         (host, ios, ram, nvram, flash, interf) = values.split(',')
      
         # Parsed data is subsequently loaded into the database
         db.execute "INSERT INTO Assets(Hostname, IOS, RAM, NVRAM, FLASH, INTERF) VALUES(\'#{host}\', \'#{ios}\', \'#{ram.to_i}\', \'#{nvram.to_i}\', '#{flash.to_i}\', \'#{interf}\')"

         puts "Config file: #{config} processed..."
      end

      stm = db.prepare "SELECT * FROM Assets"
      rs = stm.execute

      print "\nDo you want to display the database content? (Y/N) "
      begin
         answer = gets.chomp.upcase
         if answer =~ /Y/
            rs.each do |row|
               puts row.join "\s"
            end
         end
      end while answer =~ /[^Y|N]/
      
   rescue SQLite3::Exception => e
      puts "Exception occured: #{e}"
   end
end

insert_into_database("net_asset.db")

Nieunikonione w trakcie pisania tego rodzaju narzędzi skryptowych jest przeprowadzenie testów na danych diagnostycznych pochodzących z różnych urządzeń, ponieważ poszczególne ich modele mogą wyróżniać się detalami sposobu wyprowadzania i rodzajem wyprowadzanych danych – tych danych, które później trzeba poddać przetwarzaniu przy pomocy wyrażeń regularnych. Przykładowo: pewne modele routerów pokazują dostępną pamięć RAM w taki sposób [pula A]/[pula B], co w skrypcie musi znaleźć odzwierciedlenie, po pierwsze, w odpowiednio skonstruowanym wyrażeniu regularnym, by wyekstrahować obie wielkości, a po drugie, w przeprowadzeniu arytmetyki ich wymaganego sumowania. Ufam jednak, że zabawa w dostrajanie skryptu będzie dla czytelników, jeśli nie czystą przyjemnością, to co najmniej czynnością nieuciążliwą.

Wywołanie skryptu wygląda tak:

\home\janusz$ ruby show_version.rb
The following config files detected:
./router_access.conf
./router_core.conf
./router_distribution.conf

Do you really want to proceed with supplying assets to database? (Y/N) y
Config file: ./router_access.conf processed...
Config file: ./router_core.conf processed...
Config file: ./router_distribution.conf processed...

Do you want to display the database content? (Y/N) y
1 router_access IOS (tm) C2950 Software (C2950-I6Q4L2-M) Version 12.1(22)EA4 RELEASE SOFTWARE(fc1) 21039 1024 65535 24 FastEthernet/IEEE 802.3 interface(s)
2 router_core IOS (tm) 7000 Software (C7000-JS-M) Version 11.2(21) RELEASE SOFTWARE (fc1) 65536 128 4096 6 Ethernet/IEEE 802.3 interface(s); 4 Token Ring/IEEE 802.5 interface(s); 1 ATM network interface(s)
3 router_distribution IOS (tm) 7000 Software (C7000-JS-M) Version 12.2(21) RELEASE SOFTWARE (fc1) 1024 128 4096 6 Ethernet/IEEE 802.3 interface(s)

\home\janusz$

Pokazany skrypt jest oczywiście zaledwie zaczynem pomysłu na Network Asset Management. W dalszej kolejności można go udoskonalić albo rozbudować o całkiem nowe funkcjonalności. Pomysły na takie zmiany, które przychodzą mi w tej chwili do głowy, to:

  • Rozbudowa struktury bazy danych i przechowywanie w oddzielnych tabelach – dodatkowo – innych danych, typu choćby konfiguracja urządzenia (show running-config / show startup-config), adresacja interfejsów (show ip interfeces brief), szczegółowe informacje o sprzęcie (na przykład show controllers) czy najbardziej nas interesujące informacje pochodzące z diagnostyki dostarczanej przez kompleksowe polecenie show tech-support.
  • Opracowanie graficznego interfejsu użytkownika do prezentowania danych, na przykład z użyciem biblioteki GTK lub TK.
  • Opracowanie automatycznego mechanizmu pobierania danych z urządzeń – przy pomocy SNMP lub terminalowo (na przykład przez SSH, bo Telnet nie jest bezpiecznym protokołem, chyba że zastosujemy go w tunelu szyfrowanym: VPN lub STUNEL).
  • Ochrona informacji zapisywanych w bazie mechanizmami kryptograficznymi – szyfrowanie danych w bazie, wyliczanie kryptograficznych sum kontrolnych danych, w celu zapewnienia mechanizmów kontroli ich integralności.

Zachęcam Was do eksperymentowania ze skryptowym oprogramowaniem tematu Network Assest Management oraz do dalszego rozwoju przykładowego skryptu.

Informacje Janusz Nawrat
Just ordinary man who likes thinking...

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

TOMASZ WEŁNA

artysta grafik | wykładowca

PRACOWNIA OKO

Szkoła Rysunku Malarstwa i Grafiki DR TOMASZA WEŁNY | KRAKÓW | Plac Matejki 10 | tel 691 81 75 74

Piękno neurobiologii

Blog Jerzego Vetulaniego

Teoria muzyki, zasady muzyki, podstawy muzyki

Teoria muzyki, zasady muzyki, podstawy muzyki - czyli to co każdy amator muzyki wiedzieć powinien :)

Personal Development & Inspirations

Przemyślenia i refleksje, którymi warto się podzielić (blog by Janusz Nawrat)

Business IT Cooperation Platform

Biznes i IT - dwa światy, które muszą współdziałać

%d bloggers like this: