How Does a SysAdmin Can Apply a Python Skills To His Daily Work? Part2

System Administration & Programming Main Logo 2

How Does a SysAdmin Can Apply 

a Python Skills To His Daily Work? (Part2)

In the first part, we showed you how the script learned to collect hostname equipment by SNMP! 

In this part, we going to show you how to collect and process the remaining data necessary for the formation of zone files.

Let’s formulate the task, which must be solved: it is necessary to form domain names for routers and their interfaces. The script should come from a similar configuration (in this case Cisco one):

hostname R1.greenhouse
domain service.prov.com

interface FastEthernet0/1.24
description Line to Red House
ip address 10.24.1.1 255.255.255.254

We have to form two records, for the direct zone and for the reverse zone:

Fa0/1.24.Line_to_Red_House.R1.greenhouse.service.prov.com. IN A 10.24.1.1

1.1.24.10.in-addr.arpa. IN PTR Fa0/1.24.Line_to_Red_House.R1.greenhouse.service.prov.com.

There is a bad news: these are the wrong domain names. By the rules, you can only use numbers, letters and “-“. And the hyphen cannot stand first and last. To comply with the rules, the entry should look something like this:

Fa0-1-24.Line-to-Red-House.R1.greenhouse.service.prov.com. IN A 10.24.1.1

There are obvious problems with readability. And if the name of the interface does not start with non-zero digits, and even with a colon? After bringing this to the standard, understand what and wherein these figures and letters becomes quite difficult. Therefore, bind was configured with an option that allows you to work with nonstandard names. And the script, accordingly, makes the most readable names. Docking DNS networkers and global DNS is a bit of a separate conversation, but it’s easiest to allocate a separate domain for network equipment, for example, network.megaproduct.org.

To generate zone files, we need to collect router hostname values, interface names, description value on these interfaces and ip-addresses of these interfaces. The only exception is a record containing the ip-address belonging to the interface Loopback0. This entry will not contain the interface name and value of its description since this is the domain name of the router.

How to find the correct OID?

Let’s define with OIDs to get the names of interfaces, ip addresses and description from these interfaces. Our goal is universality, so we will use OID standard MIB files, which are implemented by all vendors. Typically, the easiest way to find the correct OID is not reading the documentation or google, but using snmpwalk or any MIB browser that you like best. Let see what might give us the concrete equipment, then choose the necessary one. By running snmpwalk, you get all the OIDs supported by the device with their values at the time of polling.

In order to get started with snmpwalk, you need to install the SNMP and SNMP-mibs-downloader package:

sudo apt-get install SNMP

sudo apt-get install SNMP-mibs-downloader

If it is necessary for the utilities to work with named OID in the configuration file /etc/SNMP/SNMP.conf, add the used mib or the keyword ALL.
Of course, using ALL is not recommended, the number of files is huge, searching all files in a row in many cases is clearly not a good idea. However, in the case of writing a program and searching for the desired MIB file, this is permissible. Do not forget to leave only the necessary things.

$ cat /etc/snmp/snmp.conf
# As the snmp packages come without MIB files due to license reasons, loading
# of MIBs is disabled by default. If you added the MIBs you can reenable
# loading them by commenting out the following line.
Mibs: SNMPv2-MIB: RFC1213-MIB: IF-MIB

#mibs: ALL
zw@note:~$

Check to see if snmp saw our MIB:

host$ snmptranslate 1.3.6.1.2.1.31.1.1.1.18
IF-MIB::ifAlias

host$ snmptranslate -On IF-MIB::ifAlias
.1.3.6.1.2.1.31.1.1.1.18

Now, making a request to the real host:

host$ snmpget -v 2c -c tilitili 10.239.192.2 1.3.6.1.2.1.31.1.1.1.18.1
IF-MIB::ifAlias.1 = STRING: Line_to_OUT

The last digit (1) that we added is ifindex. Ifndex is the interface number assigned by snmp to the hardware operating system agent.

Numbering is performed when the agent is initialized (when the OS boots). After each reboot, it will be different if you do not tell the router to remember the assigned ifindex (google for ifindex persist keywords).

In working with interfaces, everything is logical, take OID, add ifindex, make a query – get the description of the interface with this Ifindex.

Now, we need to request snmpwalk, which will request all available OIDs and their current values from the router:

host$ snmpwalk -v 2c -c tilitili 10.239.192.2

RFC1213-MIB::ifDescr.1 = STRING: «FastEthernet0/0»
RFC1213-MIB::ifDescr.2 = STRING: «FastEthernet0/1»
RFC1213-MIB::ifDescr.3 = STRING: «Null0»
RFC1213-MIB::ifDescr.36 = STRING: «Loopback0»
RFC1213-MIB::ipAdEntAddr.10.239.192.2 = IpAddress: 10.239.192.2
RFC1213-MIB::ipAdEntAddr.10.239.192.90 = IpAddress: 10.239.192.90
RFC1213-MIB::ipAdEntAddr.11.0.0.1 = IpAddress: 11.0.0.1
IF-MIB::ifName.1 = STRING: Fa0/0
IF-MIB::ifName.2 = STRING: Fa0/1
IF-MIB::ifName.3 = STRING: Nu0
IF-MIB::ifName.36 = STRING: Lo0
IF-MIB::ifAlias.1 = STRING: Line_to_OUT
IF-MIB::ifAlias.2 = STRING: Line_to_poligon
IF-MIB::ifAlias.3 = STRING:
IF-MIB::ifAlias.36 = STRING:

The snmpwalk output has something to read, it’s very verbose, so it’s better to filter it.

If you are paying attention, FastEthernet interfaces come with 1 ifindex and Loopback with 36. An interesting detail: ifDesc – gives the full names of interfaces, ifAlias – gives a description of interfaces, and ifName – produces abbreviated names of interfaces. Using the full names of interfaces was discarded at once, names due to the contents of the description and so will be long, and the full names of the interfaces will make them even longer.

If we need OIDs whose values we know, it’s easier to search them from the snmpwalk or MIB browser output using search or filtering. If we need an OID, for example, the current CPU utilization value, then it’s easier to search in google or in the documentation.

Let’s look for the necessary OIDs for the values of ip addresses that we know:

host$ snmpwalk -v 2c -c tilitili 10.239.192.2 | grep 10.239.192.2

RFC1213-MIB::ipAdEntAddr.10.239.192.2 = IpAddress: 10.239.192.2
RFC1213-MIB::ipAdEntIfIndex.10.239.192.2 = INTEGER: 36
RFC1213-MIB::ipAdEntNetMask.10.239.192.2 = IpAddress: 255.255.255.255
RFC1213-MIB::ipAdEntBcastAddr.10.239.192.2 = INTEGER: 1
RFC1213-MIB::ipAdEntReasmMaxSize.10.239.192.2 = INTEGER: 18024
RFC1213-MIB::ipRouteDest.10.239.192.2 = IpAddress: 10.239.192.2
RFC1213-MIB::ipRouteIfIndex.10.239.192.2 = INTEGER: 36

And from the output you can immediately see that there is no OID, adding to it ifindex, we would get ip interface addresses with this ifindex.

RFC1213-MIB :: ipAdEntAddr. + ip address – returns ip address.
RFC1213-MIB :: ipAdEntIfIndex. + ip address – returns ifindex

Thus, we first need to compile ip addresses and ifndex interfaces with these ip addresses, and then, ifndex, collect names and description interfaces.

Let’s move on to data collection: SNMPGETNEXT and Python

Previously used to collect hostname method snmpget to solve this problem is completely not suitable. This method needs an exact OID, but we do not. In such situations, another method is used – snmpgetnext. In snmp utilities, the snmpgetnext utility responds to this method.

It works like this: SNMPGETNEXT takes an OID and returns the next OID and its value.

host$ snmpgetnext -v 2c -c tilitili 10.239.192.2 1.3.6.1.2.1.4.20.1.1
RFC1213-MIB::ipAdEntAddr.10.239.192.2 = IpAddress: 10.239.192.2

host$ snmpgetnext -v 2c -c tilitili 10.239.192.2 1.3.6.1.2.1.4.20.1.1.10.239.192.2
RFC1213-MIB::ipAdEntAddr.10.239.192.90 = IpAddress: 10.239.192.90

And so on. Note that we do not know the address of the router on the first request, so the first access goes just to the value of OID 1.3.6.1.2.1.4.20.1.1

Let’s try to automate these requests. Write the function and call it several times:


def snmp_getnextcmd (community, ip, port, OID):
# type class 'generator' errorIndication, errorStatus, errorIndex, result [3]
# next method to get the values in order, one by one with next ()
return (nextCmd (SnmpEngine (),
CommunityData (community),
UdpTransportTarget ((ip, port)),
ContextData (),
ObjectType (ObjectIdentity (OID))))

g = (snmp_getnextcmd (community_string, ip_address_host, port_snmp, OID_ipAdEntAddr))
print (g)
errorIndication, errorStatus, errorIndex, varBinds = next (g)
for name, val in varBinds:
print (name.prettyPrint (), '======', val.prettyPrint ())

errorIndication, errorStatus, errorIndex, varBinds = next (g)
for name, val in varBinds:
print (name.prettyPrint (), '======', val.prettyPrint ())

errorIndication, errorStatus, errorIndex, varBinds = next (g)
for name, val in varBinds:
print (name.prettyPrint (), '======', val.prettyPrint ())

errorIndication, errorStatus, errorIndex, varBinds = next (g)
for name, val in varBinds:
print (name.prettyPrint (), '======', val.prettyPrint ())

errorIndication, errorStatus, errorIndex, varBinds = next (g)
for name, val in varBinds:
print (name.prettyPrint (), '======', val.prettyPrint ())

Run, watch the result:

<generator object nextCmd at 0x7f960364f8e0>
SNMPv2-SMI::mib-2.4.20.1.1.10.239.192.2 ====== 10.239.192.2
SNMPv2-SMI::mib-2.4.20.1.1.10.239.192.90 ====== 10.239.192.90
SNMPv2-SMI::mib-2.4.20.1.1.11.0.0.1 ====== 11.0.0.1
SNMPv2-SMI::mib-2.4.20.1.2.10.239.192.2 ====== 36
SNMPv2-SMI::mib-2.4.20.1.2.10.239.192.90 ====== 1

The variable g is our generator. What does python tell us when it executes print (g)?
And then, using the built-in function next, the script iterates through the OID for the OID. The error values are added to errorIndication, errorStatus, errorIndex, and the result is added to the varBinds list. To get values from varBinds, use the for loop:


for name,val in varBinds:
print(name.prettyPrint(),' ====== ',val.prettyPrint())

And now we have a very simple sequence of actions:

  1. We get addresses for OID 1.3.6.1.2.1.4.20.1.1, sorting through the next, we write down the addresses on the list. For example, we will track the address 10.239.192.90.
  2. Get ifindex by OID 1.3.6.1.2.1.4.20.1.2, write to the list. For the address 10.239.192.90, the ifindex value of the interface is 1.
    indicating the value of ifindex. For the address 10.239.192.90, the final design will be 1.3.6.1.2.1.31.1.1.1.18.1.
  3. We collect interface names using OID 1.3.6.1.2.1.31.1.1.1.1 + .ifindex.

It remains to compile the first two items into the function with snmpgetnext, and we’ll compile the remaining two items with snmpget. The function itself will walk through the OID, sort through the values and stop when all the OIDs have run out. OID will be loaded in a list. List with OID is over – we finish the work. The result will be returned by a two-dimensional list.


def snmp_getnextcmd_next (community, ip, port, OID, file):
# method handles the class generator from def snmp_getnext
# OID is an OID list in the form list_OID = [OID_ipAdEntAddr, OID_ipAdEntIfIndex, OID_ipAdEntNetMask], where variable string values
# in the form '1.2.3.4'
# return a two-dimensional list with values, by the number of OIDs
list_result = [] # to generate first-level lists
list_result2 = [] # summary list
g = (snmp_getnextcmd (community, ip, port, OID [0])) # start with the first OID
varBinds = 0
flag = True
for oid in list_OID:
if varBinds! = 0:
for name, val in varBinds:
list_result2.append (list_result)
list_result = []
list_result.append (val.prettyPrint ())
i = 0
while i <= 0: # on the list
errorIndication, errorStatus, errorIndex, varBinds = next (g)
if errors (errorIndication, errorStatus, errorIndex, ip_address_host, file):
if str (varBinds) .find (oid)! = -1:
i = 0
for name, val in varBinds:
list_result.append (val.prettyPrint ())
else:
i = i + 1
flag = False
else:
file.write (datetime.strftime (datetime.now (), "% Y.% m.% d% H:% M:% S") + ':' + 'Error snmp_getnextcmd_next ip =' + ip + 'OID = '+ OID [0] +' \ n ')
print ('Error snmp_getnextcmd_next', False)
i = i + 1
flag = False
list_result2.append (list_result)
return list_result2

We get a list containing ip addresses, ifindex, masks. The masks remained after the experiments, a heavy legacy of the past. To proceed to the next steps to collect interface names, description, you need to filter the received data. For example, equipment Huawei, Cisco will give out addresses from the network 127.0.0.0/8, to form for the domain names is meaningless. In the current version of the script, there is filtering over the permissible range of IPv4, everything that does not fall into the range is deleted. Obviously, for your networks, the filtering rules will be different.

Now, we collect interface names, description with snmpget and proceed to create zone files. To form the reverse zone file, you need to change the order of the octets, for which you can use reverse_pointer from the ipaddress library or write something of your own.

print(ipaddress.ip_address(‘194.4.5.1’).reverse_pointer)

1.5.4.194.in-addr.arpa

Our script calls reverse zone files based on the addresses it receives from the router, it creates a dictionary in which the key consists of the first two octets of the ip address. Then from these keys file names are formed. To do this, you still need to parse the address in octets. We apply a script which, having received a list of router addresses, should give out the correct zone files.

The whole script in one
# the script takes the address either from the database or from the file. # the script makes the first request for snmp sysname, the error handler is triggered, if there were errors, the address is skipped # If the sysname collection was successful, addresses are requested, ifindex, masks (no masks are required, this is left since the query was processed), and so on # If the host is cut off between requests – later will throw a script with a traceback, because the testresult will be porridge # on desc requests and name interface – the handler is running # in bind must be the option check-names master ignore # the zone file header is not generated by the script, it should be in a separate file. The default is named.soa. The script only sets $ INCLUDE #import section from pysnmp.hlapi import * from ipaddress import * from datetime import datetime import mysql.connector from mysql.connector import errorcode # to work with mysql it is necessary to put mysql.connector for python3 # By default, the data from mysql, how to switch to the file, see below. file format – one line one ip address in decimal form by octets with a dot separator # var section #snmp community_string = ‘community’ # community string port_snmp = 161 OID_ipAdEntAddr = ‘1.3.6.1.2.1.4.20.1.1’ # From SNMPv2-MIB ip addresses OID_ifNumber = ‘1.3.6.1.2.1.2.1.0’ # From RFC1213-MIB number of interfaces ifindex OID_sysName = ‘1.3.6.1.2.1.1.5.0’ # From SNMPv2-MIB hostname / sysname OID_ipAdEntIfIndex = ‘1.3.6.1.2.1.4.20.1.2’ # From SNMPv2-MIB ifindex interface OID_ipAdEntNetMask = ‘1.3.6.1.2.1.4.20.1.3’ # From SNMPv2-MIB OID_ifAlias = ‘1.3.6.1.2.1.31.1.1.1.18’ # Desc the interface. to get to OID, add ifindex OID_ifName = ‘1.3.6.1.2.1.31.1.1.1.1’ # the name of the interface to the OID should be added ifindex list_OID = [OID_ipAdEntAddr, OID_ipAdEntIfIndex, OID_ipAdEntNetMask] #log filename_log = ‘zone_gen.log’ # for the log file log_level = ‘debug’ # (‘normal’), (‘debug’), (‘min’) # bind zone files file_direct_zone = ‘spd.esrr.rzd.hosts’ # the file name of the direct zone domain = ‘domain.name.org’ #domainname named_soa = ‘$ INCLUDE /var/bind/named.soa’ direct_zone = [] # list for a direct zone reverse_zone = {} # dictionary for reverse zones name_server_record = ‘dns.server.ru. IN A 10.111.33.21 \ n ‘ ip_segment_ds = [’10 .0.0.0 / 8 ‘] # to remove addresses that do not fall within the range #var section for mysql username_db = ‘username’ # login for mysql password_db = ‘password’ # password for mysql db = ‘name_database’ # database name query_from_db = ‘select ip from devices where type = 2 or type = 3;’ # sql query example; host = ‘192.11.33.123’ # address of the database # ip from file filename_of_ip = ‘ip.txt’ # filename with IP addresses select_source_ip = ‘file’ # A flag from where to get data from a file (‘file’) or confstractor (‘mysql’) #section section def snmp_getnextcmd (community, ip, port, OID):     # type class ‘generator’ errorIndication, errorStatus, errorIndex, result [3]     # next method to get the values in order, one after another using next ()     return (nextCmd (SnmpEngine (),                     CommunityData (community),                     UdpTransportTarget ((ip, port)),                     ContextData (),                     ObjectType (ObjectIdentity (OID)))) def snmp_getcmd (community, ip, port, OID):     # type class ‘generator’ errorIndication, errorStatus, errorIndex, result [3] – list     # method get get the result of access to the device by SNMP with the specified OID     return (getCmd (SnmpEngine (),                    CommunityData (community),                    UdpTransportTarget ((ip, port)),                    ContextData (),                    ObjectType (ObjectIdentity (OID)))) def snmp_get_next (community, ip, port, OID, file):     # method handles the class generator from def snmp_get     # process errors, output the type class ‘pysnmp.smi.rfc1902.ObjectType’ with OID and value     # we get one scalar value     errorIndication, errorStatus, errorIndex, varBinds = next (snmp_getcmd (community, ip, port, OID))     # there must be an error handler here     x = []     if errors (errorIndication, errorStatus, errorIndex, ip, file):          for name, val in varBinds:             return (val.prettyPrint (), True)     else:         file.write (datetime.strftime (datetime.now (), “% Y.% m.% d% H:% M:% S”) + ‘: Error snmp_get_next ip =’ + ip + ‘OID =’ + OID + ‘\ n’)         return (‘Error’, False) def errors (errorIndication, errorStatus, errorIndex, ip, file):     # error handling In case of errors, we return False and write to the file (unrealized)     if errorIndication:         print (errorIndication, ‘ip address’, ip)         file.write (datetime.strftime (datetime.now (), “% Y.% m.% d% H:% M:% S”) + ‘:’ + str (errorIndication) + ‘= ip address =’ + ip + ‘\ n’)         return False     elif errorStatus:         print (datetime.strftime (datetime.now (), “% Y.% m.% d% H:% M:% S”) + ‘:’ + ‘% s at% s’% (errorStatus.prettyPrint () ,                             errorIndex and varBinds [int (errorIndex) – 1] [0] or ‘?’))         file.write (datetime.strftime (datetime.now (), “% Y.% m.% d% H:% M:% S”) + ‘:’ + ‘% s at% s’% (errorStatus.prettyPrint (),                             errorIndex and varBinds [int (errorIndex) – 1] [0] or ‘?’ + ‘\ n’))         return False     else:         return True def snmp_getnextcmd_next (community, ip, port, OID, file):     # method handles the class generator from def snmp_getnext     # OID is an OID list in the form list_OID = [OID_ipAdEntAddr, OID_ipAdEntIfIndex, OID_ipAdEntNetMask], where variable string values     # in the form ‘1.2.3.4’     # return a two-dimensional list with values, by the number of OIDs     list_result = [] # to generate first-level lists     list_result2 = [] # summary list     g = (snmp_getnextcmd (community, ip, port, OID [0])) # start with the first OID     varBinds = 0     flag = True     for oid in list_OID:         if varBinds! = 0:             for name, val in varBinds:                 list_result2.append (list_result)                 list_result = []                 list_result.append (val.prettyPrint ())         i = 0         while i <= 0: # on the list             errorIndication, errorStatus, errorIndex, varBinds = next (g)             if errors (errorIndication, errorStatus, errorIndex, ip_address_host, file):                 if str (varBinds) .find (oid)! = -1:                     i = 0                     for name, val in varBinds:                         list_result.append (val.prettyPrint ())                 else:                     i = i + 1                     flag = False             else:                 file.write (datetime.strftime (datetime.now (), “% Y.% m.% d% H:% M:% S”) + ‘:’ + ‘Error snmp_getnextcmd_next ip =’ + ip + ‘OID = ‘+ OID [0] +’ \ n ‘)                 print (‘Error snmp_getnextcmd_next’, False)                 i = i + 1                 flag = False     list_result2.append (list_result)     return list_result2 def check_ip (ip): # check the ip address     try:         ip_address (ip)     except ValueError:         return False     else:         return True def check_ip2 (ip): # check the ip address     try:         ip_interface (ip)     except ValueError:         return False     else:         return True def get_from_mysql (host, user, password, db_name, query, fd):     flag = True     try:         cnx = mysql.connector.connect (user = user, password = password, host = host, database = db_name)         if cnx.is_connected ():             print (‘Connected to MySQL database’)             fd.write (datetime.strftime (datetime.now (), “% Y.% m.% d% H:% M:% S”) + ‘:’ + ‘Connected to MySQL database’ + host + ‘\ n ‘)             cursor1 = cnx.cursor ()             cursor1.execute (query)             query_result = []             i = 0             list = []             for var in cursor1:                 query_result.append (str (var) [1: -2])             for var in query_result:                 # check that what the constructor gave is ip address and not garbage                 if var.isdigit ():                    if check_ip (int (var)):                       list.append (str (ip_address (int (var))))                        # query_result.append (str (ip_address (int (str (var) [1: -2]))))     except mysql.connector.Error as err:         if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:             print (“Something is wrong with your user name or password”)             fd.write (datetime.strftime (datetime.now (), “% Y.% m.% d% H:% M:% S”) + ‘:’ + “Something is wrong with your user name or password” + ‘\ n’)             flag = False         elif err.errno == errorcode.ER_BAD_DB_ERROR:             print (“Database does not exist”)             fd.write (datetime.strftime (datetime.now (), “% Y.% m.% d% H:% M:% S”) + ‘:’ + “Database does not exist” + ‘\ n’)             flag = False         else:             print (err)             fd (err)             flag = False     cursor1.close ()     cnx.close ()     return (list, flag) def get_from_file (file, filelog): # selects ip addresses from the file. one line – one address in decimal form     fd = open (file, ‘r’)     list_ip = []     for line in fd:        line = line.rstrip (‘\ n’)        if check_ip (line):            list_ip.append (line)        else:             filed.write (datetime.strftime (datetime.now (), “% Y.% m.% d% H:% M:% S”) + ‘: Error The garbage at the ip address source’ + line)     fd.close ()     return list_ip # code section # open the log file filed = open (filename_log, ‘w’) # write down the current time filed.write (datetime.strftime (datetime.now (), “% Y.% m.% d% H:% M:% S”) + ‘\ n’) # get the list of addresses from the database if select_source_ip == ‘file’:     ip_from_confstractor = get_from_file (filename_of_ip, filed) else:     if select_source_ip == ‘mysql’:         ip_from_confstractor, flag_mysql = get_from_mysql (host, username_db, password_db, db, query_from_db, filed) if log_level == ‘debug’:     filed.write (datetime.strftime (datetime.now (), “% Y.% m.% d% H:% M:% S”) + ‘:’ + ‘ip address from’ + select_source_ip + ‘\ n’ )     for var in ip_from_confstractor:         filed.write (var + ‘\ n’) # get the total amount of ifindex was not necessary #number_ifindex = (snmp_get_next (community_string, ip_address_host, port_snmp, OID_ifNumber)) for ip_address_host in ip_from_confstractor:     # get sysname hostname + domainname, error flag     sysname, flag_snmp_get = (snmp_get_next (community_string, ip_address_host, port_snmp, OID_sysName, filed))     if flag_snmp_get:         # It’s OK, the host responded to snmp         if sysname == ‘No Such Object currently exists at this OID’:             # and the community is invalid. it is necessary to skip the host, otherwise we catch traceback. And you just can not catch that problem in the community, so you should always ask for the hostname, which gives all the devices             print (‘ERROR community’, sysname, ”, ip_address_host)             filed.write (datetime.strftime (datetime.now (),                                           “% Y.% m.% D% H:% M:% S”) + ‘:’ + ‘ERROR community sysname =’ + sysname + ‘ip =’ + ip_address_host + ‘\ n’)         else:             if log_level == ‘debug’:                 filed.write (datetime.strftime (datetime.now (), “% Y.% m.% d% H:% M:% S”) + ‘:’ + ‘sysname’ + sysname + ‘type’ + str (type (sysname)) + ‘len’ + str (len (sysname)) + ‘ip’ + ip_address_host + ‘\ n’)             if len (sysname) <3:                 sysname = ‘None_sysname’                 if log_level == ‘debug’ or log_level == ‘normal’:                    filed.write (datetime.strftime (datetime.now (), “% Y.% m.% d% H:% M:% S”) + ‘:’ + ‘Error sysname is shorter than 3 characters =’ + sysname + ‘ ip = ‘+ ip_address_host +’ \ n ‘)             if sysname.find (domain) == -1:                 # something gave up a hostname without a domain, for example, Huawei or Catos                 sysname = sysname + ‘.’ + domain # print (“check domain host did not give domain”, sysname, “”, ip_address_host, “\ n”)                 if log_level == ‘debug’ or log_level == ‘normal’:                     filed.write (“check domain host did not give domain:” + sysname + “” + ip_address_host + “” + “\ n”)             else:                 # this is the condition by the level of writing to the log                 pass     # get addresses, ifindex, masks we will load into a two-dimensional list     # testresult [0] = addresses     # testresult [1] = ifindex     # testresult [2] = mask             testresult = snmp_getnextcmd_next (community_string, ip_address_host, port_snmp, list_OID, filed)             i = 0             for var in testresult [0]: # remove addresses not falling into ip_segment_ds                 flag = ‘False’                 for net in ip_segment_ds:                    if (IPv4Address (var) in IPv4Network (net)):                       flag = ‘True’                 if flag == ‘True’:                     pass                 else:                     testresult [0] .pop (i)                     testresult [1] .pop (i)                 i = i + 1     # knowing ifindex, filling desc interfaces, adding a separate list, changing the spaces and hyphens for underscores     # testresult [3] = desc             desc = []             i = 0             for var in testresult [1]:                 x, flag_snmp_get = snmp_get_next (community_string, ip_address_host, port_snmp, OID_ifAlias + ‘.’ + var, filed)                 if flag_snmp_get:                     x = x.replace (‘”‘, ”) .replace (”, ‘_’). replace (‘(‘, ”) .replace (‘)’, ”) .replace (                         ‘replace’, ”). replace (‘[‘, ”). replace (“‘”, “”). replace (‘ -_ ‘,’ ‘). replace (‘_-‘, ”)                     desc.append (x)                     i = i + 1                 else:                     i = i + 1                     testresult [1] .pop (i) # if the host does not respond to the desc request on ifindex, do not ask again, and remove ifindex from the list                 # because the data for it could not be compiled. accordingly the name of the interface for it will not be queried.                     testresult [0] .pop (i) # address is also removed, otherwise there will be confusion when collecting zone files             testresult.append (desc) # add a third     # If you know ifindex, collect the names of the interfaces             desc = []             for var in testresult [1]:                 x, flag_snmp_get = snmp_get_next (community_string, ip_address_host, port_snmp, OID_ifName + ‘.’ + var, filed)                 if flag_snmp_get:                     x = x.replace (‘Loopback’, ‘Lo’). replace (‘loopback0’, ‘Lo0’). replace (”, ‘_’)                     desc.append (x)                     i = i + 1                 else:                     i = i + 1                     testresult [1] .pop (i) # If the host does not respond to the interface name request with ifindex, do not ask again, and remove ifindex from the list             # because the data for it could not be compiled.                     testresult [0] .pop (i) # address is also removed, otherwise there will be confusion when collecting zone files or tracebrake we get                     testresult [3] .pop (i) # desc remove, and then there will be confusion when collecting zone files or tracebrake we get             testresult.append (desc) # add a fourth i = 0             for var in testresult [3]:                 if var == ‘No_Such_Object_currently_exists_at_this_OID’: # for APC and poor Zyxel, do not support OID desc                     testresult [3] [i] = ”                     testresult [4] [i] = ‘Lo0’                 i = i + 1             if log_level == ‘debug’:                 for var in testresult:                     filed.write (str (var) + ‘\ n’)     # generate data for zone files             i = 0             for var in testresult [0]:                 rev = var.split (‘.’)                 revs = rev [:]                 revs.reverse ()                 revsstr = ‘.’. join (revs)     # fill the dictionary of return zones                 if (rev [0] + ‘.’ + rev [1]) in reverse_zone:                     if testresult [4] [i] == ‘Lo0’:                         reverse_zone [rev [0] + ‘.’ + rev [1]]. append (revsstr + ‘.in-addr.arpa. IN PTR’ + sysname + ‘.’)                     else:                         reverse_zone [rev [0] + ‘.’ + rev [1]]. append (revsstr + ‘.in-addr.arpa. IN PTR’ + testresult [4] [i] + ‘.’ + testresult [3] [i] + ‘.’ + sysname + ‘ . ‘)                 else:                     reverse_zone [rev [0] + ‘.’ + rev [1]] = []                     if testresult [4] [i] == ‘Lo0’:                         reverse_zone [rev [0] + ‘.’ + rev [1]]. append (revsstr + ‘.in-addr.arpa. IN PTR’ + sysname + ‘.’)                     else:                         reverse_zone [rev [0] + ‘.’ + rev [1]]. append (                             revsstr + ‘.in-addr.arpa. IN PTR ‘+ testresult [4] [i] +’. ‘ + testresult [3] [                                 i] + ‘.’ + sysname + ‘.’)     # fill in the list of the direct zone                 if testresult [4] [i] == ‘Lo0’:                     direct_zone.append (sysname + ‘.’ + ‘IN A’ + var)                 elif testresult [3] [i] == ”:                     direct_zone.append (testresult [4] [i] + ‘.’ + sysname + ‘.’ + ‘IN A’ + var)                 else:                     direct_zone.append (testresult [4] [i] + ‘.’ + testresult [3] [i] + ‘.’ + sysname + ‘.’ + ‘IN A’ + var)                 i = i + 1     else:         print (‘Error host did not respond when requesting ip’, ip_address_host)         filed.write (datetime.strftime (datetime.now (), “% Y.% m.% d% H:% M:% S”) + ‘:’ + ‘Error host did not respond when requesting snmp, ip host’ + ip_address_host + ” + str (flag_snmp_get) + ‘\ n’) # write all collected files f = open (file_direct_zone, ‘w’) # file of the direct zone f.write (named_soa + ‘\ n’ + ‘\ n’) f.write (name_server_record) for i in direct_zone:     i = i.replace (‘..’, ‘.’)     f.write (i + ‘\ n’) f.close () file_list_reverse = list (reverse_zone.keys ()) print (file_list_reverse) if log_level == ‘debug’:     for i in file_list_reverse:         filed.write (i + ‘\ n’) for i in list (reverse_zone.keys ()): # write reverse zone files     f = open (i + ‘. rev’, ‘w’)     f.write (named_soa + ‘\ n’ + ‘\ n’)     for y in reverse_zone [i]:         y = y.replace (‘..’, ‘.’)         f.write (y + ‘\ n’)     f.close () # write in the log time filed.write (‘\ n’ + datetime.strftime (datetime.now (), “% Y.% m.% d% H:% M:% S”) + ‘\ n’) # close the log file filed.close ()