Files

163 lines
6.7 KiB
Python

# Config Generator
# Written by Hex Ripley
import re
import ipaddress
#### Regex Templates ####
# Valid IP/CIDR notation pattern
ip_cidr_pattern = re.compile(r'^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[?[a-fA-F0-9:\.]+\]?)\/(\d{1,2})$') # Regex identifies valid ip/cidr input
# use with ValueError | if ip_cidr_pattern.match(ip_cidr):
# when building | ...
# interpreters and | else:
# evaluators | raise ValueError("Invalid IP/CIDR notation")
# Valid JUNOS interface pattern E.G. ae0 or xe-0/1/2
interface_pattern = re.compile(r'^(ae[0-9]{1,2}|[gx]e-[0-9]\/[0-9]\/[0-9])$')
#### Input interpreters ####
def get_cidr(ip_cidr): # Grab the CIDR notation off a user's input
if ip_cidr_pattern.match(ip_cidr):
network = ipaddress.ip_network(ip_cidr, strict=False)
prefix_length = network.prefixlen
return prefix_length
else:
raise ValueError("Invalid ip/cidr notation on get_cidr call")
def get_network_block(ip_cidr): # If the user gives an address that is not the network address, find the network address.
if ip_cidr_pattern.match(ip_cidr):
network = ipaddress.ip_network(ip_cidr, strict=False)
network_address = network.network_address
prefix_length = network.prefixlen
network_block = f"{network_address}/{prefix_length}"
return network_block
else:
raise ValueError("Invalid ip/cidr notation on get_network_block call")
def get_next_ip_in_subnet(ip_cidr): # Return the next IP within the subnet
if ip_cidr_pattern.match(ip_cidr):
network = ipaddress.ip_network(ip_cidr, strict=False)
ip = ipaddress.ip_address(ip_cidr.split('/')[0])
if ip == network.broadcast_address:
raise ValueError("IP address is the broadcast address; no next IP in the subnet.")
next_ip = ip + 1
if next_ip in network: # Make sure next IP is within subnet
return f"{next_ip}/{network.prefixlen}"
else:
raise ValueError("Next IP address is outside the subnet.")
#### Evaluator Engine ####
def template_engine(config_data_template): # Does the work!
data_strings = {}
template_strings = config_data_template["template_output"]
evaluation_functions = config_data_template["evaluation_functions"]
interpreter_functions = config_data_template["interpreter_functions"]
for evaluator in evaluation_functions.items():
template_string = evaluator[0]
prompt_and_evaluator = evaluator[1]
evaluated_input = get_valid_input(prompt_and_evaluator[0],prompt_and_evaluator[1])
data_strings[template_string] = evaluated_input
for interpreter in interpreter_functions.items():
template_string = interpreter[0]
interpreter_and_key = interpreter[1]
interpreted_value = interpreter_and_key[0](data_strings[interpreter_and_key[1]])
data_strings[template_string] = interpreted_value
adjusted_strings = replace_target_words(template_strings,data_strings)
print("\n ---- \n")
for string in adjusted_strings:
print(string)
print("\n")
def replace_target_words(strings, replacements): # Function to replace words in a string
def replace_in_string(s):
# Find all target words in braces (e.g., {word})
return re.sub(r'\{([a-zA-Z0-9_]+)\}', lambda match: replacements.get(match.group(1), match.group(1)), s)
# Apply replacement to all strings
return [replace_in_string(s) for s in strings]
def get_valid_input(prompt, eval_func): # Input validater, does not proceed until validation function is satisfied.
while True:
user_input = input(prompt+" > ")
try:
# Attempt to evaluate the input using the provided function
result = eval_func(user_input)
return result
except Exception as e:
# Print error message and prompt again
print(f"Invalid input: {e}. Please try again.")
#### Evaluator Functions ####
def eval_interface(interface): # Check if interface is valid.
if interface_pattern.match(interface):
return interface
else:
raise ValueError("Invalid Interface: Correct examples ae0, ge-1/2/4")
def eval_unit_number(unit_number): # Check if unit number is valid for what we expect to be configured.
try:
if unit_number is not None and 0 < int(unit_number) < 30000:
return unit_number
else:
return ValueError()
except:
raise ValueError("Invalid Unit Number: Enter a unit greater than 0 and less than 30,000")
def eval_access_block(access_block):
if ip_cidr_pattern.match(access_block):
return access_block
else:
raise ValueError("Invalid ip/cidr notation")
def eval_transport_block(transport_block):
if ip_cidr_pattern.match(transport_block):
if get_cidr(transport_block) == 64:
return transport_block
else:
raise ValueError("Transport Block CIDR must be 64")
else:
raise ValueError("Invalid ip/cidr notation")
#### Data Template ####
#### Data Templates are a dict with three keys:
## template_output ##
#template_output is a list of lines of strings containing lines of config with desired variables in braces.
## evaluation_functions ##
#evaluation_functions is a dict with the keys being the target variable and the values being a list containing the input for the get_valid_input function, which takes the first value as a prompt to show to the user and the second value as an evaluator function to run on the user's input.
## interpreter_functions ##
#interpreter_functions is a dict with keys being the target variable and the values being a list containing the desired interpreter function and the target value for the interpreter function. The target value for the interpreter function can be the key of a previous evaluation function or previous interpreter_function.
config_ipv6_junos = {
"template_output" : [
"set interfaces {interface} unit {unit_number} family inet6 address {access_first}",
"set routing-options rib inet6.0 static route {transport_block} next-hop {access_second}",
"set routing-options rib inet6.0 static route {transport_block} tag 101",
"set policy-options prefix-list bgp-advertise-v6 {access_block}"
] ,
"evaluation_functions" : {
"interface" : ["What is the user's interface?",eval_interface],
"unit_number" : ["What is the user's unit number?",eval_unit_number],
"access_block" : ["What is the IPv6 Access Block?",eval_access_block],
"transport_block" : ["What is the IPv6 Transport Block?",eval_transport_block]
} ,
"interpreter_functions" : {
"access_first" : [get_next_ip_in_subnet,"access_block"],
"access_second" : [get_next_ip_in_subnet,"access_first"]
} }
### Run this example
template_engine(config_ipv6_junos)