Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions panos/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,21 +210,26 @@ def uid(self):
return ""

def add(self, child):
"""Add a child node to this node
"""Add a child node to this node.

Args:
child (PanObject): Node to add as a child

Returns:
PanObject: Child node

Raises:
ValueError: If the child is this node itself

"""
if child is self:
raise ValueError("An object cannot be added as its own child")
child.parent = self
self.children.append(child)
return child

def insert(self, index, child):
"""Insert a child node at a specific index
"""Insert a child node at a specific index.

This is useful for ordering or reordering security policy rules

Expand All @@ -235,18 +240,29 @@ def insert(self, index, child):
Returns:
PanObject: Child node

Raises:
ValueError: If the child is this node itself

"""
if child is self:
raise ValueError("An object cannot be added as its own child")
child.parent = self
self.children.insert(index, child)
return child

def extend(self, children):
"""Add a list of child nodes to this node
"""Add a list of child nodes to this node.

Args:
children (list): List of PanObject instances

Raises:
ValueError: If the children include this node itself

"""
children = list(children)
if any(child is self for child in children):
raise ValueError("An object cannot be added as its own child")
for child in children:
child.parent = self
self.children.extend(children)
Expand Down
27 changes: 27 additions & 0 deletions tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import uuid
import xml.etree.ElementTree as ET

import pytest
import pan.xapi
import panos.base as Base
import panos.errors as Err
Expand Down Expand Up @@ -78,6 +79,32 @@ def test_property_uid(self):

self.assertEqual(expected, ret_val)

def test_add_self_leaves_tree_unchanged(self):
"""Reject self-parenting without modifying the tree."""
with pytest.raises(ValueError):
self.obj.add(self.obj)
assert self.obj.parent is None
assert self.obj.children == []

def test_insert_self_leaves_tree_unchanged(self):
"""Reject self-insertion without modifying the tree."""
with pytest.raises(ValueError):
self.obj.insert(0, self.obj)
assert self.obj.parent is None
assert self.obj.children == []

def test_extend_self_leaves_all_parents_unchanged(self):
"""Reject self-extension before changing any parent links."""
existing = self.obj.add(Base.PanObject("existing"))
other_parent = Base.PanObject("other")
child = other_parent.add(Base.PanObject("child"))
with pytest.raises(ValueError):
self.obj.extend([child, self.obj])
assert self.obj.parent is None
assert self.obj.children == [existing]
assert child.parent is other_parent
assert other_parent.children == [child]

def test_add_without_children(self):
CHILD_NAME = "child"
child = Base.PanObject(CHILD_NAME)
Expand Down