mpenning / ciscoconfparse

Parse, Audit, Query, Build, and Modify Arista / Cisco / Juniper / Palo Alto / F5 configurations.
http://www.pennington.net/py/ciscoconfparse/
GNU General Public License v3.0
794 stars 220 forks source link

delete recursive - fails to update parent #99

Closed nkaliape closed 6 years ago

nkaliape commented 6 years ago

When a child is chosen for delete, it needs to update the parent. If NOT - while deleting the parent in future - it tries to look for "missing"(dead) children.

mpenning commented 6 years ago

I cannot replicate this problem...

>>> from ciscoconfparse import CiscoConfParse
>>> config = """!
... parent
...  child 1
...  child 2
...  child 3"""
>>> parse = CiscoConfParse(config.splitlines())
>>> target = parse.find_objects_w_parents("parent", "child 2")[0]
>>> target.delete()
>>> parse.commit()
>>> print "\n".join(parse.ioscfg)
!
parent
 child 1
 child 3
>>> parse.find_objects_w_parents("parent", "child 2")
[]
>>>

Please call commit() after deleting an object.

http://www.pennington.net/py/ciscoconfparse/api_CiscoConfParse.html?highlight=commit#ciscoconfparse.CiscoConfParse.commit

nkaliape commented 6 years ago

Hi mike, Goal: Take the whole config and break it down to pieces. Issue hit: index error

from copy import deepcopy from random import randint def pick_n_del(confobj): parse = confobj.objs cur_len = len(parse) my_choice = randint(0, cur_len) my_choice -= 1 disp_str = "\n PICK-n-DEL current choice {num} of total {tot} \n".format(num=my_choice, tot=cur_len) print(disp_str) my_ioscfg = parse[my_choice] my_str = None if not my_ioscfg.is_comment: my_str = my_ioscfg.geneology_text print("\n") print(my_ioscfg.geneology) print("\n") print (my_str) print("\n") print ("children...", my_ioscfg.all_children) my_ioscfg.delete()

confobj.commit()

return my_str

def check_stale(confobj): print(" check for Staleness ") ioscfg_obj_list = confobj.objs cur_len = len(ioscfg_obj_list) for ioscfg_obj in ioscfg_obj_list: my_line_num = str(ioscfg_obj.linenum) my_children = ioscfg_obj.all_children

remove stale child

    print("linenum:" + my_line_num + "of " + str(cur_len) + " ==>" + str(ioscfg_obj.geneology) + "\n" + str(ioscfg_obj.all_children))
    for each_child in my_children:
        if each_child.linenum > cur_len:
            print('BANG!!!!')
            print("linenum:", my_line_num , " cur_len", cur_len, "xxx",    my_children)
            import pdb; pdb.set_trace()
            return False
return True

from ciscoconfparse import CiscoConfParse myconf = CiscoConfParse('my.cfg') from pprint import pprint my_cfg_list = [] for each_obj in myconf.find_objects('^!$'): each_obj.delete()

myconf.commit()

while len(myconf.objs): print ("\n") print ('\n BEFORE PICK-DEL') print(myconf) if not check_stale(myconf): import pdb; pdb.set_trace() prev_copy = deepcopy(myconf) ret = pick_n_del(myconf) print ('\n AFTER PICK-DEL')

print(myconf)

#pprint(myconf.objs)
if ret:
    my_cfg_list.append(ret)

print('DONE...') import pdb; pdb.set_trace() print(my_cfg_list) import re for each_cfg in my_cfg_list: if len(each_cfg) == 1 and re.match("^interface ", each_cfg[0]): my_cfg_list.remove(each_cfg) print(my_cfg_list)

without commit():

with commit():

Logs with commit: 0 ! 7 ! OOOPS!!! 11 ! OOOPS!!! 15 ! OOOPS!!! 20 ! OOOPS!!! 24 ! Traceback (most recent call last): File "my.py", line 60, in each_obj.delete() File "/home/nkaliape/ncc/local/lib/python2.7/site-packages/ciscoconfparse/ccp_abc.py", line 235, in delete if self.confobj._list[self.linenum].text==text:

Logs without commit: (added check stale)

AFTER PICK-DEL BEFORE PICK-DEL *** check for Staleness *** linenum:0of 13 ==>[] [, , , , , ] linenum:1of 13 ==>[] [, ] linenum:2of 13 ==>[, ] [] linenum:3of 13 ==>[, ] [] linenum:4of 13 ==>[] [, ] linenum:5of 13 ==>[, ] [] linenum:6of 13 ==>[] [, , ] BANG!!!! ('linenum:', '6', ' cur_len', 13, 'xxx', [, , ]) > /home/nkaliape/my.py(39)check_stale() -> return False (Pdb) Here only 0-13 index but stale 14 index is seen as child of 6. The root cause found: Whenever we delete a child recursively, we do NOT update / remove the child from parent. With this fix - there is no need of consistency check. Also when we have consistency check - when we consistency fails - we just skip deleting - what is the action or reporting we do? - there is no else code for consistency check failure.
mpenning commented 6 years ago

I made design decisions about CiscoConfParse; these decisions require calling commit() after deleting objects. I will not change this design decision.

nkaliape commented 6 years ago

Hi Mike,

Here my point is that l I'm hitting the issue even with commit(). I'm hitting index error - if last line has "!".

!

interface Serial1/2

encapsulation hdlc

ip address 1.1.1.9 255.255.255.252

!

class-map GOLD

match access-group 102

class-map SILVER

match protocol tcp

!

from ciscoconfparse import CiscoConfParse

myconf = CiscoConfParse('my.cfg')

from pprint import pprint

my_cfg_list = []

for each_obj in myconf.find_objects('^!$'):

each_obj.delete()

myconf.commit()

Traceback (most recent call last):

File "my.py", line 47, in

each_obj.delete()

File "/home/nkaliape/ncc/local/lib/python2.7/site-packages/ciscoconfparse/ccp_abc.py", line 235, in delete

if self.confobj._list[self.linenum].text==text:

IndexError: list index out of range

Thanks, Narayan

On Fri, Mar 2, 2018 at 6:18 AM, Mike Pennington notifications@github.com wrote:

I made design decisions about CiscoConfParse; these decisions require calling commit() after deleting objects. I will not change this design decision.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/mpenning/ciscoconfparse/issues/99#issuecomment-369932463, or mute the thread https://github.com/notifications/unsubscribe-auth/AV5inbSqsuTtsuse3N6tWmmuUOIvUgysks5taVSpgaJpZM4SZQhw .

-- -Narayan

mpenning commented 6 years ago

This example is absurd:

for each_obj in myconf.find_objects('^!$'):
    each_obj.delete()
    myconf.commit()

If this throws an error, I really don't see a point in fixing that. Please find a broken use-case that doesn't rely on deleting empty comment objects from a configuration.

BTW, there is a way to support deletion of "empty" comments; I just don't see the point of rewriting things to support a corner case like this.