SQLite use example#
In section 15 there was an example of reviewing the output of command “show ip dhcp snooping binding”. In the output we received information about parameters of connected devices (interface, IP, MAC, VLAN).
In this version you can only see all devices connected to switch. If you want to find out others based on one of the parameters, it’s not convenient in this way.
For example, if you want to get information based on IP address about to which interface the host is connected, which MAC address it has and in which VLAN it is, then script is not very simple and more importantly, not convenient.
Let’s write information obtained from the output “sh ip dhcp snooping binding” to SQLite. This will allow do queries based on any parameter and get missing ones. For this example, it is sufficient to create a single table where information will be stored.
Table is defined in a separate dhcp_snooping_schema.sql file:
create table if not exists dhcp (
mac text not NULL primary key,
ip text,
vlan text,
interface text
);
For all fields the data type is “text”.
MAC address is the primary key of our table which is logical because MAC address must be unique.
Additionally, by using expression create table if not exists
-
SQLite will only create a table if it does not exist.
Now you have to create a database file, connect to database and create a table (create_sqlite_ver1.py file):
import sqlite3
conn = sqlite3.connect('dhcp_snooping.db')
print('Creating schema...')
with open('dhcp_snooping_schema.sql', 'r') as f:
schema = f.read()
conn.executescript(schema)
print("Done")
conn.close()
Comments to file:
during execution of
conn = sqlite3.connect('dhcp_snooping.db')
:file dhcp_snooping.db is created if it does not exist
Connection object is created
table is created in database (if it does not exist) based on commands specified in dhcp_snooping_schema.sql file:
dhcp_snooping_schema.sql file opens
schema = f.read()
- whole file is read in one stringconn.executescript(schema)
- executescript() method allows SQL to execute commands that are written in the file
Execution of script:
$ python create_sqlite_ver1.py
Creating schema...
Done
The result should be a database file and a dhcp table.
You can check that table has been created with sqlite3 utility which allows you to execute queries directly in command line.
List of tables created is shown as follows:
$ sqlite3 dhcp_snooping.db "SELECT name FROM sqlite_master WHERE type='table'"
dhcp
Now it is necessary to write information from the output of “sh ip dhcp snooping binding” command to the table (dhcp_snooping.txt file):
MacAddress IpAddress Lease(sec) Type VLAN Interface
------------------ --------------- ---------- ------------- ---- --------------------
00:09:BB:3D:D6:58 10.1.10.2 86250 dhcp-snooping 10 FastEthernet0/1
00:04:A3:3E:5B:69 10.1.5.2 63951 dhcp-snooping 5 FastEthernet0/10
00:05:B3:7E:9B:60 10.1.5.4 63253 dhcp-snooping 5 FastEthernet0/9
00:09:BC:3F:A6:50 10.1.10.6 76260 dhcp-snooping 10 FastEthernet0/3
Total number of bindings: 4
In the second version of the script, the output in dhcp_snooping.txt file is processed with regular expressions and then entries are added to database (create_sqlite_ver2.py file):
import sqlite3
import re
regex = re.compile('(\S+) +(\S+) +\d+ +\S+ +(\d+) +(\S+)')
result = []
with open('dhcp_snooping.txt') as data:
for line in data:
match = regex.search(line)
if match:
result.append(match.groups())
conn = sqlite3.connect('dhcp_snooping.db')
print('Creating schema...')
with open('dhcp_snooping_schema.sql', 'r') as f:
schema = f.read()
conn.executescript(schema)
print('Done')
print('Inserting DHCP Snooping data')
for row in result:
try:
with conn:
query = '''insert into dhcp (mac, ip, vlan, interface)
values (?, ?, ?, ?)'''
conn.execute(query, row)
except sqlite3.IntegrityError as e:
print('Error occured: ', e)
conn.close()
Note
For now, you should delete database file every time because script tries to create it every time you start.
Comments to the script:
in regular expression that processes the output of “sh ip dhcp snooping binding”, numbered groups are used instead of named groups as it was in example of section `https://pyneng.readthedocs.io/en/latest/book/14_regex/4a_group_example.html>`__
groups were created only for those elements we are interested in
result - a list that stores the result of processing the command output
but now there is no dictionaries but tuples with results
this is necessary to enable them to be immediately written to database
Scroll elements in received list of tuples
This script uses another version of database entry
query
string containes a query. But instead of values, question marks are given. This query type allows dynamicly substite field values.then
execute
method is passed a query string androw
tuple where values are
Execute the script:
$ python create_sqlite_ver2.py
Creating schema...
Done
Inserting DHCP Snooping data
Let’s check if data has been written:
$ sqlite3 dhcp_snooping.db "select * from dhcp"
-- Loading resources from /home/vagrant/.sqliterc
mac ip vlan interface
----------------- ---------- ---------- ---------------
00:09:BB:3D:D6:58 10.1.10.2 10 FastEthernet0/1
00:04:A3:3E:5B:69 10.1.5.2 5 FastEthernet0/1
00:05:B3:7E:9B:60 10.1.5.4 5 FastEthernet0/9
00:09:BC:3F:A6:50 10.1.10.6 10 FastEthernet0/3
Now let’s try to ask by a certain parameter:
$ sqlite3 dhcp_snooping.db "select * from dhcp where ip = '10.1.5.2'"
-- Loading resources from /home/vagrant/.sqliterc
mac ip vlan interface
----------------- ---------- ---------- ----------------
00:04:A3:3E:5B:69 10.1.5.2 5 FastEthernet0/10
That is, it is now possible to get others parameters based on one parameter.
Let’s modify the script to make it check for the presence of dhcp_snooping.db. If you have a database file you don’t need to create a table, we believe it has already been created.
File create_sqlite_ver3.py:
import os
import sqlite3
import re
data_filename = 'dhcp_snooping.txt'
db_filename = 'dhcp_snooping.db'
schema_filename = 'dhcp_snooping_schema.sql'
regex = re.compile('(\S+) +(\S+) +\d+ +\S+ +(\d+) +(\S+)')
result = []
with open('dhcp_snooping.txt') as data:
for line in data:
match = regex.search(line)
if match:
result.append(match.groups())
db_exists = os.path.exists(db_filename)
conn = sqlite3.connect(db_filename)
if not db_exists:
print('Creating schema...')
with open(schema_filename, 'r') as f:
schema = f.read()
conn.executescript(schema)
print('Done')
else:
print('Database exists, assume dhcp table does, too.')
print('Inserting DHCP Snooping data')
for row in result:
try:
with conn:
query = '''insert into dhcp (mac, ip, vlan, interface)
values (?, ?, ?, ?)'''
conn.execute(query, row)
except sqlite3.IntegrityError as e:
print('Error occured: ', e)
conn.close()
Now there is a verification of the presence of database file and dhcp_snooping.db file will only be created if it does not exist. Data is also written only if dhcp_snooping.db file is not created.
Note
Separating the process of creating a table and completing it with the data is specified in tasks to the section.
If no file (delete it first):
$ rm dhcp_snooping.db
$ python create_sqlite_ver3.py
Creating schema...
Done
Inserting DHCP Snooping data
Let’s check. In case the file already exists but the data is not written:
$ rm dhcp_snooping.db
$ python create_sqlite_ver1.py
Creating schema...
Done
$ python create_sqlite_ver3.py
Database exists, assume dhcp table does, too.
Inserting DHCP Snooping data
If both DB and data are exist:
$ python create_sqlite_ver3.py
Database exists, assume dhcp table does, too.
Inserting DHCP Snooping data
Error occurred: UNIQUE constraint failed: dhcp.mac
Error occurred: UNIQUE constraint failed: dhcp.mac
Error occurred: UNIQUE constraint failed: dhcp.mac
Error occurred: UNIQUE constraint failed: dhcp.mac
Now we make a separate script that sends queries to database and displays results. It should:
expect parameters from user:
parameter name
parameter value
provide normal output on request
File get_data_ver1.py:
import sqlite3
import sys
db_filename = 'dhcp_snooping.db'
key, value = sys.argv[1:]
keys = ['mac', 'ip', 'vlan', 'interface']
keys.remove(key)
conn = sqlite3.connect(db_filename)
#Allows to further access data in columns by column name
conn.row_factory = sqlite3.Row
print('\nDetailed information for host(s) with', key, value)
print('-' * 40)
query = 'select * from dhcp where {} = ?'.format(key)
result = conn.execute(query, (value, ))
for row in result:
for k in keys:
print('{:12}: {}'.format(k, row[k]))
print('-' * 40)
Comments to the script:
key, value are read from arguments that passed to script
selected key is removed from keys list. Thus, only parameters that you want to display are left in the list
connecting to DB
conn.row_factory = sqlite3.Row
- allows further access data in column based on column names
Select rows from database where key is equal to specified value
in SQL the values can be set by a question mark but you cannot give a column name. Therefore, the column name is substituted by row formatting and the value by SQL tool.
Pay attention to
(value,)
- tuple with one element is passed
The resulting information is displayed to standard output stream:
iterate over the results obtained and show only those fields that are in keys list
Let’s check the script.
Show host parameters with IP 10.1.10.2:
$ python get_data_ver1.py ip 10.1.10.2
Detailed information for host(s) with ip 10.1.10.2
----------------------------------------
mac : 00:09:BB:3D:D6:58
vlan : 10
interface : FastEthernet0/1
----------------------------------------
Show hosts in VLAN 10:
$ python get_data_ver1.py vlan 10
Detailed information for host(s) with vlan 10
----------------------------------------
mac : 00:09:BB:3D:D6:58
ip : 10.1.10.2
interface : FastEthernet0/1
----------------------------------------
mac : 00:07:BC:3F:A6:50
ip : 10.1.10.6
interface : FastEthernet0/3
----------------------------------------
The second version of the script to get data with minor improvements:
Instead of rows formatting, a dictionary that contains queries corresponding to each key is used.
Checking the key that was selected
Method
keys
is used to get all columns that match the query
File get_data_ver2.py:
import sqlite3
import sys
db_filename = 'dhcp_snooping.db'
query_dict = {
'vlan': 'select mac, ip, interface from dhcp where vlan = ?',
'mac': 'select vlan, ip, interface from dhcp where mac = ?',
'ip': 'select vlan, mac, interface from dhcp where ip = ?',
'interface': 'select vlan, mac, ip from dhcp where interface = ?'
}
key, value = sys.argv[1:]
keys = query_dict.keys()
if not key in keys:
print('Enter key from {}'.format(', '.join(keys)))
else:
conn = sqlite3.connect(db_filename)
conn.row_factory = sqlite3.Row
print('\nDetailed information for host(s) with', key, value)
print('-' * 40)
query = query_dict[key]
result = conn.execute(query, (value, ))
for row in result:
for row_name in row.keys():
print('{:12}: {}'.format(row_name, row[row_name]))
print('-' * 40)
There are several drawbacks to this script:
does not check number of arguments that are passed to the script
It would be good to collect information from different switches. To do this, you should add a field that indicates on which switch the entry was found
In addition, a lot of work needs to be done in the script that creates database and writes the data.
All improvements will be done in tasks of this section.