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()
Brian, Thanks for sharing this. I’m in exactly the same position here.. exporting from Putty/SuperPutty to PAC Manager. Silly question, but since I am a complete noob when it comes to Python.. running your script, will it prompt for the source file or do you specify it as a cli switch?
It imports the file named “Sessions.xml” without any arguments. I didn’t add the logic to grab arguments since I just sort of whipped it together. It prints the output to the console.
Brian,
Would you mind if your script above was included in the Asbru-cm github utils directory?
I found your script very useful and I think it would benefit other Asbru users.
https://github.com/asbru-cm/asbru-cm/issues/39
Regards
Eoin
Yes you can as long as you attribute the code to this blog
Thanks Brian