#@+leo
#@+node:0::@file plugins/mod_import_cisco_config.py
#@+body
from leoPlugins import *
from leoGlobals import *
import tkFileDialog


#@<< about this plugin >>
#@+node:1::<< about this plugin >>
#@+body
#@+at
#  This plugin adds a menu item under the File->Import menu to import
# Cisco configuration files.
# 
# The plugin will:
# 
# 1) create a new node, under the current node, where the configuration will be
# written. This node will typically have references to several sections (see below).
# 
# 2) create sections (child nodes) for the indented blocks present in the original
# config file. These child nodes will have sub-nodes grouping similar blocks (e.g.
# there will be an 'interface' child node, with as many sub-nodes as there are real
# interfaces in the configuration file).
# 
# 3) create sections for the custom keywords specified in the customBlocks[] 
# list in
# importCiscoConfig(). You can modify this list to specify different keywords. DO
# NOT put keywords that are followed by indented blocks (these are taken care 
# of by
# point 2 above). The negated form of the keywords (for example, if the 
# keyword is
# 'service', the negated form is 'no service') is also included in the sections.
# 
# 4) not display consecutive empty comment lines (lines with only a '!').
# 
# All created sections are alphabetically ordered.
# 
# Feedback on this plugin can be sent to Davide Salomoni (dsalomoni@yahoo.com).

#@-at
#@-body
#@-node:1::<< about this plugin >>


#@+others
#@+node:2::create_import_cisco_menu
#@+body
def create_import_cisco_menu(tag, keywords):
	if	(tag=="open2" or
		(tag=="start2" and not keywords.has_key('c')) or
		(tag=="command2" and keywords.get("label")=="new")):

		c = top()
		importMenu = c.frame.getMenu('import')
		newEntries = (
			("-", None, None),
			("Import C&isco Configuration", "Shift+Ctrl+I", importCiscoConfig))
		
		c.frame.createMenuEntries(importMenu, newEntries)
#@-body
#@-node:2::create_import_cisco_menu
#@+node:3::importCiscoConfig
#@+body
def importCiscoConfig(event=None):
	c = top(); current = c.currentVnode()
	if current == None: return
	
	#@<< open file >>
	#@+node:1::<< open file >>
	#@+body
	name = tkFileDialog.askopenfilename(
		title="Import Cisco Configuration File",
		filetypes=[("All files", "*")]
		)
	if name == "":	return
	
	v = current.insertAsNthChild(0)
	c.beginUpdate()
	v.setHeadString("cisco config: %s" % name)
	c.endUpdate()
	
	try:
		fh = open(name)
		es("importing: %s" % name)
		linelist = fh.read().splitlines()
		fh.close()
	except IOError,msg:
		es("error reading %s: %s" % (name, msg))
		return
	#@-body
	#@-node:1::<< open file >>


	# define which additional child nodes will be created
	# these keywords must NOT be followed by indented blocks
	customBlocks = ['aaa','ip as-path','ip prefix-list','ip route',
					'ip community-list','access-list','snmp-server','ntp',
					'boot','service','logging']
	out = []
	blocks = {}
	children = []
	lines = len(linelist)
	i = 0
	skipToNextLine = 0
	# create level-0 and level-1 children
	while i<(lines-1):
		for customLine in customBlocks:
			if (linelist[i].startswith(customLine) or
				linelist[i].startswith('no %s' % customLine)):
				
				#@<< process custom line >>
				#@+node:2::<< process custom line >>
				#@+body
				if not blocks.has_key(customLine):
					blocks[customLine] = []
					out.append(angleBrackets(customLine))
					# create first-level child
					child = v.insertAsNthChild(0)
					child.setHeadStringOrHeadline(angleBrackets(customLine))
					children.append(child)
				
				blocks[customLine].append(linelist[i])
				#@-body
				#@-node:2::<< process custom line >>

				skipToNextLine = 1
				break
		if skipToNextLine:
			skipToNextLine = 0
		else:
			if linelist[i+1].startswith(' '):
				
				#@<< process indented block >>
				#@+node:3::<< process indented block >>
				#@+body
				space = linelist[i].find(' ')
				if space == -1:
					space = len(linelist[i])
				key = linelist[i][:space]
				if not blocks.has_key(key):
					blocks[key] = []
					out.append(angleBrackets(key))
					# create first-level child
					child = v.insertAsNthChild(0)
					child.setHeadStringOrHeadline(angleBrackets(key))
					children.append(child)
				
				value = [linelist[i]]
				# loop through the indented lines
				i = i+1
				try:
					while linelist[i].startswith(' '):
						value.append(linelist[i])
						i = i+1
				except:
					# EOF
					pass
				i = i-1 # restore index
				# now add the value to the dictionary
				blocks[key].append(value)
				#@-body
				#@-node:3::<< process indented block >>

			else:
				out.append(linelist[i])
		i=i+1
	# process last line
	out.append(linelist[i])
	
	
	#@<< complete outline >>
	#@+node:4::<< complete outline >>
	#@+body
	# first print the level-0 text
	outClean = []
	prev = ''
	for line in out:
		if line=='!' and prev=='!':
			pass # skip repeated comment lines
		else:
			outClean.append(line)
		prev = line
	v.setBodyStringOrPane('\n'.join(outClean))
	
	# scan through the created outline and add children
	for child in children:
		# extract the key from the headline. Uhm... :)
		key = child.headString().split('<<'
			)[1].split('>>')[0].strip()
		if blocks.has_key(key):
			if type(blocks[key][0]) == type(''):
				# it's a string, no sub-children, so just print the text
				child.setBodyStringOrPane('\n'.join(blocks[key]))
			else:
				# it's a multi-level node
				for value in blocks[key]:
					# each value is a list containing the headline and then the text
					subchild = child.insertAsNthChild(0)
					subchild.setHeadStringOrHeadline(value[0])
					subchild.setBodyStringOrPane('\n'.join(value))
			child.sortChildren()
		else:
			# this should never happen
			es("Unknown key: %s" % key)
	v.sortChildren()
	#@-body
	#@-node:4::<< complete outline >>
#@-body
#@-node:3::importCiscoConfig
#@-others


if 0:
	registerHandler(("start2","open2","command2"), create_import_cisco_menu)
	
	import mod_import_cisco_config
	es("...import cisco config v1.2: "+ plugin_date(mod_import_cisco_config))

#@-body
#@-node:0::@file plugins/mod_import_cisco_config.py
#@-leo
