Skip to main content
  1. Posts/

Validators / Converters / Formatter / Helper in Python

·743 words·4 mins·
netdevops blog python validator helper converter
Maximilian Thoma
Author
Maximilian Thoma
network engineer
Table of Contents

If you are engaged in NetDevOps, you need to validate and convert data, and occasionally, you’ll require some assistance. Here is a small collection of tools that will be expanded periodically.

Validators
#

Check if IP is in CIDR
#

import ipaddress
 
def check_ip_in_cidr(ip, cidr):
    ip_address = ipaddress.IPv4Address(ip)
    network = ipaddress.IPv4Network(cidr, strict=False)
 
    return ip_address in network

Check valid CIDR
#

import re
 
def is_valid_cidr(cidr):
    # CIDR Format: IP/Prefix, e.g., 192.168.0.1/24
    cidr_regex = re.compile(r'^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$')
    if not cidr_regex.match(cidr):
        return False
 
    # Split the CIDR into IP and prefix
    ip, prefix = cidr.split('/')
 
    # Check if prefix is in the range 0-32
    if not (0 <= int(prefix) <= 32):
        return False
 
    # Check if each part of the IP is in the range 0-255
    ip_parts = ip.split('.')
    for part in ip_parts:
        if not (0 <= int(part) <= 255):
            return False
 
    return True

Validate correct hostname
#

import re
 
def is_valid_hostname(hostname):
    if len(hostname) > 255:
        return False
    if hostname[-1] == ".":
        hostname = hostname[:-1]  # strip exactly one dot from the right, if present
    allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
    return all(allowed.match(x) for x in hostname.split("."))

Check valid value ip or hostname
#

import socket
 
def check_valid_value(value):
    """ Validate if value is correct. """
    try:
        # CASE 1
        socket.inet_aton(value)
        return value
 
    except socket.error:
        if is_valid_hostname(value) is False:
            raise ValueError("IP or Hostname not valid.")
        else:
            return value

Converters
#

Hex to IP converter
#

import struct
import socket
 
def hex2ip(hex_ip):
    addr_long = int(hex_ip,16)
    hex(addr_long)
    hex_ip = socket.inet_ntoa(struct.pack(">L", addr_long))
    return hex_ip

IP range to CIDR converter
#

import ipaddress
 
def ip_range_to_cidr(start_ip, end_ip):
    try:
        start = ipaddress.ip_address(start_ip)
        end = ipaddress.ip_address(end_ip)
 
        if start.version != end.version:
            raise ValueError("start and end ip are not same ip version.")
 
        start_int = int(start)
        end_int = int(end)
 
        if start_int > end_int:
            raise ValueError("start ip must be smaller that end ip.")
 
        networks = list(ipaddress.summarize_address_range(start, end))
 
        for network in networks:
            if network.network_address == start and network.broadcast_address == end:
                return str(network)
 
        raise ValueError("no matching cidr.")
 
    except ValueError as e:
        return str(e)

IP to Long
#

import socket
import struct
 
def ip2long(ip):
    """
    Convert an IP string to long
    """
    packedIP = socket.inet_aton(ip)
    return struct.unpack("!L", packedIP)[0]

Long to IP
#

import socket
import struct
 
def long2ip(long):
    """
    Convert long to IP string
    """
    return socket.inet_ntoa(struct.pack('!L', long))

Formatters
#

MAC address formatter
#


def format_mac(mac: str) -> str:
    """ Convert any MAC format to uniform xx:xx:xx:xx:xx:xx format """
    mac = re.sub('[.:-]', '', mac).lower()  # remove delimiters and convert to lower case
    mac = ''.join(mac.split())  # remove whitespaces
    assert len(mac) == 12  # length should be now exactly 12 (eg. 008041aefd7e)
    assert mac.isalnum()  # should only contain letters and numbers
    # convert mac in canonical form (eg. 00:80:41:ae:fd:7e)
    mac = ":".join(["%s" % (mac[i:i+2]) for i in range(0, 12, 2)])
    return mac

Helpers
#

Get first ip of subnet
#

import ipaddress
 
def get_first_ip(cidr):
    return str(ipaddress.IPv4Network(cidr, strict=False)[0])

Get last ip of subnet
#

import ipaddress
 
def get_last_ip(cidr):
    return str(ipaddress.IPv4Network(cidr, strict=False)[-1])

Progressbar
#

def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = 'â–ˆ', printEnd = "\r"):
    """
    Call in a loop to create terminal progress bar
    @params:
        iteration   - Required  : current iteration (Int)
        total       - Required  : total iterations (Int)
        prefix      - Optional  : prefix string (Str)
        suffix      - Optional  : suffix string (Str)
        decimals    - Optional  : positive number of decimals in percent complete (Int)
        length      - Optional  : character length of bar (Int)
        fill        - Optional  : bar fill character (Str)
        printEnd    - Optional  : end character (e.g. "\r", "\r\n") (Str)
    """
    percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
    filledLength = int(length * iteration // total)
    bar = fill * filledLength + '-' * (length - filledLength)
    print(f'\r{prefix} |{bar}| {percent}% {suffix}', end = printEnd)
    # Print New Line on Complete
    if iteration == total:
        print()

Get from CIDR host adress CIDR network
#

import ipaddress
 
def get_cidr_network(ip_cidr):
    return str(ipaddress.ip_network(ip_cidr, strict=False))

Get from IP and netmask the CIDR netmask
#

import ipaddress
 
def calculate_cidr(ip, netmask):
    try:
        # Convert IP and netmask to ipaddress objects
        ip_obj = ipaddress.IPv4Address(ip)
        netmask_obj = ipaddress.IPv4Network(f"0.0.0.0/{netmask}").netmask
 
        # Calculate network address
        network_address = ipaddress.IPv4Network(f"{ip}/{netmask}", strict=False).network_address
 
        # Calculate prefix length
        prefix_length = ipaddress.IPv4Network(f"0.0.0.0/{netmask}").prefixlen
 
        # Return CIDR notation
        return f"{network_address}/{prefix_length}"
    except ValueError as e:
        return str(e)

Compress list of CIDR networks to reduce overhead
#

import sys
from netaddr import cidr_merge
 
 
def main(FILE):
    with open(FILE) as f:
        raw_lines = f.readlines()
    subnets = []
    for line in raw_lines:
        subnets.append(line.strip())
    subnets = cidr_merge(subnets)
    for subnet in subnets:
        print(subnet)
 
if __name__ == "__main__":
    main(sys.argv[1])

Related

Working with MongoDB & Python
·1357 words·7 mins
netdevops blog python mongodb
MongoDB is considered an easy-to-use database for several reasons, particularly when used in conjunction with PyMongo.
FLASK with LDAP Authentication against Active Directory and group authorization for specific pages
·457 words·3 mins
netdevops blog python flask ldap active_directory
This is an example of how to implement authentication for a FLASK website using Active Directory with LDAP.
Merge subnet lists
·80 words·1 min
netdevops blog python subnet cidr merge
This is a small python snippet to merge multiple subnet lists mixed with single ip addresses and CIDRs.