Skip to content

Instantly share code, notes, and snippets.

@cidrblock
Created October 21, 2018 17:19
Show Gist options
  • Save cidrblock/198ea7b516779fc71836aeb9cc5ab294 to your computer and use it in GitHub Desktop.
Save cidrblock/198ea7b516779fc71836aeb9cc5ab294 to your computer and use it in GitHub Desktop.
Fleet configuration management OO prototyping
""" fleet prototype
"""
import os
import multiprocessing.pool
from netmiko.ssh_autodetect import SSHDetect
from netmiko.ssh_dispatcher import ConnectHandler
class Configuration(): #pylint: disable=R0903
""" A class for a configuration
"""
def __init__(self, current_raw):
self.current_raw = RawConfiguration(current_raw)
self.current_sliced = SlicedConfiguration(self.current_raw)
self.desired_sliced = SlicedConfiguration(self.current_raw)
@property
def changed(self):
""" Has the config changed
"""
return str(self.desired_sliced) != str(self.current_sliced)
def upsert(self, configuration_line):
""" Insert or update the desired configuration with a new
configuration_line
Args:
current_raw (ConfigurationLine): The new line
"""
match = [(idx, ds) for idx, ds in enumerate(self.desired_sliced.lines)
if ds.line == configuration_line.line]
if match:
self.desired_sliced.lines[match[0][0]] = configuration_line
result = "updated"
else:
self.desired_sliced.lines.append(configuration_line)
result = "added"
return result
class ConfigurationLine():
""" A class for a configuraiton line
"""
def __init__(self, line):
self.line = line
self.children = []
@property
def indent(self):
""" Return the indent of the current line
"""
return len(self.line) - len(self.line.lstrip())
def __repr__(self):
output = [self.line]
output.extend([str(child) for child in self.children])
return "\n".join(output)
class Fleet():
""" A fleet class
"""
def __init__(self):
self.devices = []
self.credentials = (os.environ['SSH_USERNAME'], os.environ['SSH_PASSWORD'])
def add(self, names):
""" Add devices to the fleet
Args:
names (list): A list of devices to add
"""
for name in names:
self.devices.append(ManagedDevice(name=name))
def all(self, opersys, action, config):
""" Perform acctions on all devices
Args:
opersys (string): The os scope
action (string): The action to take
config (string): The new desired config line
"""
for device in self.devices:
if device.device_type == opersys:
getattr(device.configuration, action)(config)
@property
def changed(self):
""" Determine which devices have changed
"""
return [d.name for d in self.devices if d.configuration.changed]
def gather_facts(self):
""" Gather facts on all managed devices
"""
with multiprocessing.pool.ThreadPool(5) as pool:
results = [pool.apply_async(device.gather_facts,
[self.credentials])
for device in self.devices]
for async_result in results:
async_result.get()
class ManagedDevice(): #pylint: disable=R0903
""" A managed device class
"""
def __init__(self, name):
self.name = name
self.configuration = None
self.remote_device = None
self.device_type = None
def gather_facts(self, credentials):
""" Get the os and running config from a device
"""
remote_device = {'device_type': 'autodetect',
'host': self.name,
'username': credentials[0],
'password': credentials[1]}
guesser = SSHDetect(**remote_device)
best_match = guesser.autodetect()
self.device_type = best_match.split("_")[1]
remote_device['device_type'] = best_match
self.remote_device = remote_device
net_connect = ConnectHandler(**self.remote_device)
output = net_connect.send_command("show run")
self.configuration = Configuration(output)
class RawConfiguration(): #pylint: disable=R0903
""" A class for a raw configuration
"""
def __init__(self, text):
self.text = text
self.lines = self.text.splitlines()
def __repr__(self):
return self.text
class SlicedConfiguration(): #pylint: disable=R0903
""" A class for a sliced configuration
"""
def __init__(self, raw):
sliced = []
for line in raw.lines:
line = ConfigurationLine(line)
if line.indent == 0:
sliced.append(line)
else:
sliced[-1].children.append(line)
self.lines = sliced
def __repr__(self):
output = [str(l) for l in self.lines]
return "\n".join(output)
def main():
""" start here
"""
fleet = Fleet()
fleet.add(["router1", "router2", "router3"])
fleet.gather_facts()
acl = ConfigurationLine("ip access-list foo")
acl.children.append(" 10 permit ip 8.8.7.0/24 8.8.8.0/24")
acl.children.append(" 20 permit ip any 8.8.8.8/32")
fleet.all(opersys="nxos", action="upsert", config=acl)
print(fleet.changed)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment