Moving SuperPutty config to Linux Pac Manager

I have transitioning from a primary Windows desktop environment to a Linux desktop, and found that I wanted to get my saved connections from superputty, which my co-workers are using to a Linux tool. I found a tool called PAC Manager which does what I needed.

The main thing I wanted was a tree hierarchy what the routers are split by location. SupperPutty uses an XML based config file and PAC Manager uses YAML files. The main problem was finding a way to discover and map out the tree structure, because SupperPutty list the hierarchy like a file listing with a separator of “/”.

I was able to use a Python to extract the XML elements, and create a list of directory tree objects and element objects. In PAC’s YaML you need to list each folder and element separately, you also need to show the parent child relationships in both the folder and they element. I have attached the current version of my Python code below. I may later develop this further into a library that abstracts and can import and export both formats fully, but the current code meets my current need.

#!/usr/bin/python
import uuid
import xml.etree.ElementTree as ET

def branchListImport(devices):
    temp = []
    branches = {}
    for y in devices:
        x = y['SessionId'].split('/')[:-1]
        if "/".join(y['SessionId'].split('/')[:-1]) not in branches:
            if x[0] not in branches:
                branches.update({str(x[0]) : {'name' : str(x[0]), 'description' : str(x[0]), 'uuidNumber' : uuid.uuid4(), 'parent' : "__PAC__EXPORTED__"}})
            if len(x) > 1:
                for y in range(1,len(x)):
                  if "/".join(x[:(y+1)]) not in branches:
                      branches.update({"/".join(x[:(y+1)]) : {'name': "/".join(x[:(y+1)]), 'description' : str(x[y]), 'uuidNumber' : uuid.uuid4(), 'parent' : str(branches["/".join(x[:y])]['uuidNumber'])}})
    for x in sorted(branches.items()):
        temp.append(branchPoint(**x[1]))
    for x in temp:
        for y in temp:
            if str(x.uuid) == str(y.parent):
                x.addChild(y.uuid)
    return temp


def deviceListImport(devices, branchList):
    temp = []
    for x in devices:
        temp.append(device(description=x['SessionName'], parentName="/".join(x['SessionId'].split('/')[:-1]), ip=x['Host'], port=x['Port'], method=x['Proto'], username=x['Username'] ))
    for x in temp:
        for y in branchList:
            if x.parentName == y.name:
                x.parentUuid = str(y.uuid)
                y.addChild(str(x.uuid))
                break
    return temp


class device(object):
    def __init__(self, description="", parentName="Unknown", parentUuid=False, uuidNumber=False, ip="", port="", method="", username="", password=False):
        self.description = description
        self.parentName = parentName
        self.parentUuid = parentUuid
        if uuidNumber == False:
            self.uuid = uuid.uuid4()
        else:
            if isinstance(uuidNumber, uuid.UUID):
                self.uuid = uuidNumber
            elif isinstance(uuidNumber, str):
                self.uuid = uuid.UUID(uuidNumber)
        self.ip = ip
        self.port = port
        if method.upper() == "SSH":
            self.method = "SSH"
        elif method.upper() == "TELNET":
            self.method = "Telnet"
        else:
            self.method = method.upper()
        self.username = username
        self.password = password
    def __hash__(self):
        return hash(self.description, self.parentName, self.parentUuid, self.uuid, self.ip, self.port, self.method, self.username, self.password)
    def __str__(self):
        return str(self.uuid)
    def __repr__(self):
        return 'pac_template.device(description="{}", parentName={}, parentUuid="{}", uuidNumber="{}", ip="{}", port="{}", method="{}", username="{}", password="{}"'.format(self.description, self.parentName, self.parentUuid, self.uuid, self.ip, self.port, self.method, self.username, self.password)
    @property
    def ymlString(self):
        if self.password == False:
            password = "<<ASK_PASS>>"
        else:
            password = self.password
        return elementTemplate.format(uuid=self.uuid, ip=self.ip, desc=self.description, parent=self.parentUuid, port=self.port, method=self.method, username=self.username, password=password)


class branchPoint(object):
    def __init__(self, description="", name="", parent="__PAC__EXPORTED__", children=False,uuidNumber=False):
        self.description = description
        self.name = name
        self.parent = parent
        self.children = children
        if uuidNumber == False:
            self.uuid = uuid.uuid4()
        else:
            if isinstance(uuidNumber, uuid.UUID):
                self.uuid = uuidNumber
            elif isinstance(uuidNumber, str):
                self.uuid = uuid.UUID(uuidNumber)
    def __hash__(self):
        return hash(self.uuid, self.name, self.description, self.parent, self.children)
    def __str__(self):
        return str(self.uuid)
    def __repr__(self):
        return 'pac_template.branchPoint(description="{}", name={}, parent="{}", children={}, uuidNumber="{}"'.format(self.description, self.name, self.parent, self.children, self.uuid)
    def addChild(self, child):
        if self.children == False:
            self.children = []
        self.children.append(str(child))
    @property
    def ymlString(self):
        temp = "{}:\n  _is_group: 1\n  _protected: 0\n  children:\n".format(str(self.uuid))
        if self.children != False:
            for x in self.children:
                temp += "    {}: 1\n".format(x)
        temp += "  cluster: []\n  description: Connection group '{0}'\n  name: {0}\n  parent: {1}\n  screenshots: ~\n  variables: []".format(self.description, self.parent)
        return temp

elementTemplate = """{uuid}:
  KPX title regexp: '.*{desc}.*'
  _is_group: 0
  _protected: 0
  auth fallback: 1
  auth type: userpass
  autoreconnect: ''
  autossh: ''
  children: {{}}
  cluster: []
  description: "Connection with '{desc}'"
  embed: 0
  expect: []
  favourite: 0
  infer from KPX where: 3
  infer user pass from KPX: ''
  ip: {ip}
  local after: []
  local before: []
  local connected: []
  mac: ''
  macros: []
  method: {method}
  name: '{desc}'
  options: ' -X'
  parent: {parent}
  pass: '{password}'
  passphrase: ''
  passphrase user: ''
  port: {port}
  prepend command: ''
  proxy ip: ''
  proxy pass: ''
  proxy port: 8080
  proxy user: ''
  public key: /home/braaen
  quote command: ''
  remove control chars: ''
  save session logs: ''
  screenshots: ~
  search pass on KPX: 0
  send slow: 0
  send string active: ''
  send string every: 60
  send string intro: 1
  send string txt: ''
  session log pattern: <UUID>_<NAME>_<DATE_Y><DATE_M><DATE_D>_<TIME_H><TIME_M><TIME_S>.txt
  session logs amount: 10
  session logs folder: ~/.config/pac/session_logs
  startup launch: ''
  startup script: ''
  startup script name: sample1.pl
  terminal options:
    audible bell: ''
    back color: '#000000000000'
    bold color: '#cc62cc62cc62'
    bold color like text: 1
    command prompt: '[#%\$>]|\:\/\s*$'
    cursor shape: block
    disable ALT key bindings: ''
    disable CTRL key bindings: ''
    disable SHIFT key bindings: ''
    open in tab: 1
    password prompt: "([p|P]ass|[p|P]ass[w|W]or[d|t]|ontrase.a|Enter passphrase for key '.+'):\\s*$"
    tab back color: '#000000000000'
    terminal backspace: auto
    terminal character encoding: UTF-8
    terminal emulation: xterm
    terminal font: Monospace 9
    terminal scrollback lines: 5000
    terminal select words: \.:_\/-A-Za-z0-9
    terminal transparency: 0
    terminal window hsize: 800
    terminal window vsize: 600
    text color: '#cc62cc62cc62'
    timeout command: 40
    timeout connect: 40
    use personal settings: ''
    use tab back color: ''
    username prompt: '([l|L]ogin|[u|u]suario|[u|U]ser-?[n|N]ame|[u|U]ser):\s*$'
    visible bell: ''
  title: '{desc}'
  use prepend command: ''
  use proxy: 0
  use sudo: ''
  user: {username}
  variables: []"""

def main():
    temp = []
    tree = ET.parse('Sessions.xml')
    root = tree.getroot()
    devices = []
    for child in root:
        if child.tag == 'SessionData':
            devices.append(child.attrib)
    branchList = branchListImport(devices)
    deviceList = deviceListImport(devices, branchList)

    temp.append("---\n__PAC__EXPORTED__:\n  children:")
    temp += ["    {}: 1".format(str(x.uuid)) for x in branchList if '__PAC__EXPORTED__' == x.parent]
    temp += [x.ymlString for x in branchList]
    temp += [x.ymlString for x in deviceList]

    print "\n".join(temp)


if __name__ == "__main__":
  main()

Better way of formatting Mac Addresses in Python

Following up on my Formating mac addresses using python post I have a better way of using Mac addresses.  I have been using the netaddr library for ip and mac address manipulations.  To convert a binary mac address to an EUI object you can use the following code

import netaddr
from netaddr import EUI

def convertMac(octet):
"""
This Function converts a binary mac address to a hexadecimal string representation
"""
return EUI(netaddr.strategy.eui48.packed_to_int(octet))

with the EUI object you have multiple options to format with.

>>> from netaddr import EUI
>>> import netaddr
>>> mac = EUI('01-23-45-67-89-0A')
>>> print mac
01-23-45-67-89-0A
>>>
>>> mac.dialect = netaddr.mac_cisco
>>> print mac
0123.4567.890a
>>>
>>> mac.dialect = netaddr.mac_unix
>>> print mac
1:23:45:67:89:a
>>>
>>> mac.dialect = netaddr.mac_bare
>>> print mac
01234567890A
>>>
>>> mac.dialect = netaddr.mac_unix_expanded
>>> print mac
01:23:45:67:89:0a
>>> mac.dialect = netaddr.mac_eui48
>>> print mac
01-23-45-67-89-0A

Mercurial for Tracking Cisco Configs

As I have been working with different routers I have squired a small repository of configuration files that I use for examples, backups, and ways of remembering the way the network was set up before a change. At the start I maintained each config as a separate file, usually containing the date (i.e. router1-071012.cfg). While that worked for a while it got cumbersome when I wanted to track more than one legacy version of a config file. Continue reading “Mercurial for Tracking Cisco Configs”