[python]Detect changes in configuration of ASA

Three python scripts were written for doing specific tasks.

  • conn_asa.py – this script is responsible for generating two outputs hostname and md_now (md is short for message digest)
  • statechange.py – this script writes the hash generated from conn_asa.py into database.
  • compare_change.py – this script compares the hash stored in db with the current hash generated from conn_asa.py
  • conn_asa.py
    The script uses netmiko to send show run object network and show hostname.
    The result from show run object network is hashed with sha256, the digest string is passed into md_now variable.
    The result from show hostname returns the hostname\n, the split() method is used to remove the trailing newline if this is not removed the entire hostname with the newline will be stored in database causing the where clause executed by SQLAlchemy to become false causing SQLAlchemy to return no result.

    from netmiko import ConnectHandler
    import hashlib
    # testing dictionary, do not hardcode credential into code.
    asa = {
        'device_type': 'cisco_asa',
        'ip': '',
        'username': 'cisco',
        'password': 'cisco'
    connect_asa = ConnectHandler(**asa)
    results = connect_asa.send_command('show run object network')
    # split() has to be used to remove the newline \n return by ConnectHandler
    # if split() or rsplit() is not used the trailing \n will fail the where clause.
    hostname = connect_asa.send_command('show hostname').split()
    # create a sha256 object, sha256 has more reliability than sha1.
    sha = hashlib.sha256()
    # hash the result from show run object network,
    # has to convert into unicode byte before hashing
    # pass the hash string into md_now object.
    md_now = sha.hexdigest()


    from os.path import exists
    from sqlalchemy import TEXT, Integer, Table, create_engine, Column, MetaData
    from conn_asa import md_now, hostname
    db = create_engine('sqlite:///state.db')
    metadata = MetaData(db)
    if not exists('state.db'):
        network_object_state = Table('network_object_state', metadata,
                                 Column('state_id', Integer, primary_key=True),
                                 Column('hostname', TEXT, nullable=False),
                                 Column('message_digest', TEXT, nullable=False))
        network_object_state = Table('network_object_state', metadata, autoload=True)
    i = network_object_state.insert()
    i.execute({'hostname': hostname[0], # split() was used to return hostname, hence hostname becomes a list.
               'message_digest': md_now})


    from sqlalchemy import Table, MetaData, create_engine
    from conn_asa import md_now
    db = create_engine('sqlite:///state.db')
    metadata = MetaData(db)
    network_object_state = Table('network_object_state', metadata, autoload=True)
    md_then = network_object_state.select(network_object_state.c.hostname == 'ciscoasa').execute()
    # md_then cannot be returned without using fetchall or fetchone, md_then is just a SQL object.
    # fetchone returns one row from the database, each row is a tuple.
    # fetchall returns more than one row if the condition matches, the result will be a list of tuples.
    print("Network Objects unchanged" if md_then.fetchone()[2] == md_now else "Network Objects have changed")

    The code assumes the hostnames are unique for all devices, if there are same hostnames then the code will not work correctly, the fetchone returns the row that SQL first found with the SQL condition.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s