@ -28,6 +28,7 @@ from ansible.module_utils.network import to_list
DEFAULT_COMMENT_TOKENS = [ ' # ' , ' ! ' ]
class ConfigLine ( object ) :
def __init__ ( self , text ) :
@ -106,6 +107,19 @@ def parse(lines, indent, comment_tokens=None):
return config
def dumps ( objects , output = ' block ' ) :
if output == ' block ' :
items = [ c . raw for c in objects ]
elif output == ' commands ' :
items = [ c . text for c in objects ]
elif output == ' lines ' :
items = list ( )
for obj in objects :
line = list ( )
line . extend ( [ p . text for p in obj . parents ] )
line . append ( obj . text )
items . append ( ' ' . join ( line ) )
return ' \n ' . join ( items )
class NetworkConfig ( object ) :
@ -113,6 +127,10 @@ class NetworkConfig(object):
self . indent = indent or 1
self . _config = list ( )
self . _device_os = device_os
self . _syntax = ' block ' # block, lines, junos
if self . _device_os == ' junos ' :
self . _syntax = ' junos '
if contents :
self . load ( contents )
@ -121,21 +139,10 @@ class NetworkConfig(object):
def items ( self ) :
return self . _config
@property
def lines ( self ) :
lines = list ( )
for item , next_item in get_next ( self . items ) :
if next_item is None :
lines . append ( item . line )
elif not next_item . line . startswith ( item . line ) :
lines . append ( item . line )
return lines
def __str__ ( self ) :
if self . _device_os == ' junos ' :
lines = self . to_lines ( self . expand ( self . items ) )
return ' \n ' . join ( lines )
return self . to_block ( self . expand ( self . items ) )
return dumps ( self . expand_line ( self . items ) , ' lines ' )
return dumps ( self . expand_line ( self . items ) )
def load ( self , contents ) :
self . _config = parse ( contents , indent = self . indent )
@ -152,6 +159,21 @@ class NetworkConfig(object):
if parents == path [ : - 1 ] :
return item
def get_object ( self , path ) :
for item in self . items :
if item . text == path [ - 1 ] :
parents = [ p . text for p in item . parents ]
if parents == path [ : - 1 ] :
return item
def get_section_objects ( self , path ) :
if not isinstance ( path , list ) :
path = [ path ]
obj = self . get_object ( path )
if not obj :
raise ValueError ( ' path does not exist in config ' )
return self . expand_section ( obj )
def search ( self , regexp , path = None ) :
regex = re . compile ( r ' ^ %s ' % regexp , re . M )
@ -177,7 +199,7 @@ class NetworkConfig(object):
regexp = r ' %s ' % regexp
return re . findall ( regexp , str ( self ) )
def expand ( self , objs ) :
def expand _line ( self , objs ) :
visited = set ( )
expanded = list ( )
for o in objs :
@ -189,35 +211,6 @@ class NetworkConfig(object):
visited . add ( o )
return expanded
def to_lines ( self , objects ) :
lines = list ( )
for obj in objects :
line = list ( )
line . extend ( [ p . text for p in obj . parents ] )
line . append ( obj . text )
lines . append ( ' ' . join ( line ) )
return lines
def to_block ( self , section ) :
return ' \n ' . join ( [ item . raw for item in section ] )
def get_section ( self , path ) :
try :
section = self . get_section_objects ( path )
if self . _device_os == ' junos ' :
return self . to_lines ( section )
return self . to_block ( section )
except ValueError :
return list ( )
def get_section_objects ( self , path ) :
if not isinstance ( path , list ) :
path = [ path ]
obj = self . get_object ( path )
if not obj :
raise ValueError ( ' path does not exist in config ' )
return self . expand_section ( obj )
def expand_section ( self , configobj , S = None ) :
if S is None :
S = list ( )
@ -228,74 +221,73 @@ class NetworkConfig(object):
self . expand_section ( child , S )
return S
def get_object ( self , path ) :
for item in self . items :
if item . text == path [ - 1 ] :
parents = [ p . text for p in item . parents ]
if parents == path [ : - 1 ] :
return item
def expand_block ( self , objects , visited = None ) :
items = list ( )
def get_children ( self , path ) :
obj = self . get_object ( path )
if obj :
return obj . children
if not visited :
visited = set ( )
def difference ( self , other , path = None , match = ' line ' , replace = ' line ' ) :
updates = list ( )
for o in objects :
items . append ( o )
visited . add ( o )
for child in o . children :
items . extend ( self . expand_block ( [ child ] , visited ) )
config = self . items
if path :
config = self . get_children ( path ) or list ( )
return items
if match == ' line ' :
for item in config :
def diff_line ( self , other ) :
diff = list ( )
for item in self . items :
if item not in other . items :
updates . append ( item )
elif match == ' strict ' :
if path :
current = other . get_children ( path ) or list ( )
else :
current = other . items
diff . append ( item )
return diff
for index , item in enumerate ( config ) :
def diff_strict ( self , other ) :
diff = list ( )
for index , item in enumerate ( self . items ) :
try :
if item != current [ index ] :
updates . append ( item )
if item != other . items [ index ] :
diff . append ( item )
except IndexError :
updates . append ( item )
diff . append ( item )
return diff
elif match == ' exact ' :
if path :
current = other . get_children ( path ) or list ( )
else :
current = other . items
if len ( current ) != len ( config ) :
updates . extend ( config )
def diff_exact ( self , other ) :
diff = list ( )
if len ( other . items ) != len ( self . items ) :
diff . extend ( self . items )
else :
for ours , theirs in itertools . izip ( config , current ) :
for ours , theirs in itertools . izip ( self . items , other . items ) :
if ours != theirs :
updates . extend ( config )
diff . extend ( self . items )
break
return diff
def difference ( self , other , match = ' line ' , replace = ' line ' ) :
try :
func = getattr ( self , ' diff_ %s ' % match )
updates = func ( other )
except AttributeError :
raise TypeError ( ' invalid value for match keyword ' )
if self . _device_os == ' junos ' :
return updates
changes = list ( )
for update in updates :
if replace == ' block ' :
if update . parents :
changes . append ( update . parents [ - 1 ] )
for child in update . parents [ - 1 ] . children :
changes . append ( child )
parents = list ( )
for u in updates :
if u . parents is None :
if u not in parents :
parents . append ( u )
else :
changes . append ( update )
else :
changes . append ( update )
updates = self . expand ( changes )
for p in u . parents :
if p not in parents :
parents . append ( p )
return self . expand_block ( parents )
return [ item . text for item in updates ]
return self . expand_line ( updates )
def replace ( self , patterns , repl , parents = None , add_if_missing = False ,
ignore_whitespace = True ) :