Skip to content

Commit 1dd604a

Browse files
committed
Merge branch 'release/0.6.1'
2 parents 7401995 + dc302c5 commit 1dd604a

File tree

6 files changed

+124
-4
lines changed

6 files changed

+124
-4
lines changed

.travis.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,13 @@ install: pip install -r requirements.txt
1313

1414
# command to run tests, e.g. python setup.py test
1515
script: python setup.py test
16+
17+
# deploy to pypi on tagged commits
18+
deploy:
19+
provider: pypi
20+
user: btorresgil
21+
password:
22+
secure: HDdWhCTXRqlWslYJ2yNEq4ObVlqjBgBMQb1ZiwkMFWc28uDR4L8jrtyEgiu1EqVEHFACSHM52ktd92Baufiv350qwpu2enljM2XA3hYTqYkM2dp/ItsKYZ/4QUkfnWUekebav1FkoPpoFMWkAeoDkQwVTKexJTqTwTnr1atTA7Q=
23+
on:
24+
tags: true
25+

HISTORY.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@
33
History
44
=======
55

6+
0.6.1
7+
-----
8+
9+
Released: 2018-03-27
10+
11+
Status: Alpha
12+
13+
- Added: visualize configuration tree in Jupyter Notebooks and graphviz
14+
- Fixed: small xpath generation issue
15+
- Fixed: uid is equal to id when id exists
16+
17+
618
0.6.0
719
-----
820

pandevice/__init__.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
__author__ = 'Palo Alto Networks'
2525
__email__ = '[email protected]'
26-
__version__ = '0.6.0'
26+
__version__ = '0.6.1'
2727

2828

2929
import logging
@@ -43,6 +43,9 @@ def emit(self, record):
4343
logging.NullHandler = NullHandler
4444

4545

46+
DOCUMENTATION_URL = 'http://pandevice.readthedocs.io/en/latest'
47+
48+
4649
def getlogger(name=__name__):
4750
import types
4851
logger_instance = logging.getLogger(name)
@@ -185,6 +188,29 @@ def stringToVersion(other):
185188
return other
186189

187190

191+
def tree_legend_dot():
192+
"""Create a graphviz dot string for a legend graph"""
193+
modules = ['firewall', 'policies', 'objects', 'network', 'device', 'panorama', 'ha']
194+
result = 'graph legend {' \
195+
'graph [fontsize=10, margin=0.001];' \
196+
'node [shape=box, fontsize=10, height=0.001, margin=0.1, ordering=out];'
197+
for module in modules:
198+
result += '{module} [style=filled fillcolor={color} URL="{url}' \
199+
'/module-{module}.html" target="_blank"];'.format(
200+
module=module,
201+
color=node_color(module),
202+
url=DOCUMENTATION_URL,
203+
)
204+
result += '}'
205+
return result
206+
207+
208+
def tree_legend():
209+
"""Display a legend for the colors of the tree method"""
210+
import graphviz
211+
return graphviz.Source(tree_legend_dot())
212+
213+
188214
# Convenience methods used internally by module
189215
# Do not use these methods outside the module
190216

@@ -303,3 +329,19 @@ def yesno(value):
303329
False: "no",
304330
}
305331
return convert[value]
332+
333+
334+
def node_color(module):
335+
nodecolor = {
336+
'device': 'lightpink',
337+
'firewall': 'lightblue',
338+
'ha': 'lavender',
339+
'network': 'lightcyan',
340+
'objects': 'lemonchiffon',
341+
'policies': 'lightsalmon',
342+
'panorama': 'palegreen2',
343+
}
344+
try:
345+
return nodecolor[module]
346+
except KeyError:
347+
return ''

pandevice/base.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,9 @@ def vsys(self, value):
152152
@property
153153
def uid(self):
154154
"""Returns the unique identifier of this object as a string."""
155-
if self.NAME is not None:
155+
if hasattr(self, 'id'):
156+
return self.id
157+
elif self.NAME is not None:
156158
return str(getattr(self, self.NAME))
157159
else:
158160
return None
@@ -1664,6 +1666,59 @@ def _perform_vsys_dict_import_delete(self, dev, vsys_dict):
16641666
# imports.
16651667
dev.xapi.delete(xpath, retry_on_peer=self.HA_SYNC)
16661668

1669+
def dot(self):
1670+
result = 'digraph configtree {graph [rankdir=LR, fontsize=10, margin=0.001];' \
1671+
'node [shape=box, fontsize=10, height=0.001, margin=0.1, ordering=out];'
1672+
result += self._dot(root_node=True)
1673+
result += '}'
1674+
return result
1675+
1676+
def _dot(self, root_node=False):
1677+
node = type(self).__name__
1678+
module = type(self).__module__.split('.')[-1]
1679+
result = '"{node_name}" [style=filled fillcolor={color} ' \
1680+
'URL="{url}' \
1681+
'/module-{module}.html#pandevice.{module}.{node}" ' \
1682+
'target="_blank"];'
1683+
result = result.format(node_name=node+' : '+self.uid,
1684+
node=node,
1685+
module=module,
1686+
url=pandevice.DOCUMENTATION_URL,
1687+
color=pandevice.node_color(module))
1688+
# Make recursive call to children
1689+
for child in self.children:
1690+
result += child._dot()
1691+
# Build relationship with parent
1692+
if not root_node and self.parent is not None:
1693+
if self.parent is not None:
1694+
result += '"{0}" -> "{1}";'.format(
1695+
type(self.parent).__name__+' : '+self.parent.uid,
1696+
type(self).__name__+' : '+self.uid,
1697+
)
1698+
return result
1699+
1700+
def tree(self):
1701+
"""Display a graph of the configuration tree
1702+
1703+
The tree includes this object and its children, recursively.
1704+
1705+
This method is only for use in Jupyder Notebooks
1706+
1707+
"""
1708+
import graphviz
1709+
return graphviz.Source(self.dot())
1710+
1711+
def fulltree(self):
1712+
"""Display a graph of the entire configuration tree
1713+
1714+
This method is only for use in Jupyder Notebooks
1715+
1716+
"""
1717+
if self.parent is not None:
1718+
return self.parent.fulltree()
1719+
return self.tree()
1720+
1721+
16671722

16681723
class VersioningSupport(object):
16691724
"""A class that supports getting version specific values of something.

pandevice/firewall.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,8 @@ def create_vsys(self):
281281
if self.vsys_name is not None:
282282
ET.SubElement(element, "display-name").text = self.vsys_name
283283
self.set_config_changed()
284-
self.xapi.set(self._root_xpath_vsys(None), ET.tostring(element), retry_on_peer=True)
284+
path = self._root_xpath_vsys(None).rsplit('/', 1)[0]
285+
self.xapi.set(path, ET.tostring(element, encoding='utf-8'), retry_on_peer=True)
285286

286287
def delete_vsys(self):
287288
"""Delete the vsys on the live device that this Firewall object represents"""

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
setup(
2525
name='pandevice',
26-
version='0.6.0',
26+
version='0.6.1',
2727
description='Framework for interacting with Palo Alto Networks devices via API',
2828
long_description='The Palo Alto Networks Device Framework is a way to interact with Palo Alto Networks devices (including Next-generation Firewalls and Panorama) using the device API that is object oriented and conceptually similar to interaction with the device via the GUI or CLI.',
2929
author='Palo Alto Networks',

0 commit comments

Comments
 (0)