Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add rule hit count support #309

Closed
wants to merge 3 commits into from
Closed
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
2 changes: 1 addition & 1 deletion panos/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class PanObject(object):
CHILDMETHODS = ()
HA_SYNC = True
TEMPLATE_NATIVE = False
_UNKNOWN_PANOS_VERSION = (sys.maxsize, 0, 0)

def __init__(self, *args, **kwargs):
# Set the 'name' variable
Expand Down Expand Up @@ -2257,7 +2258,6 @@ class VersionedPanObject(PanObject):

"""

_UNKNOWN_PANOS_VERSION = (sys.maxsize, 0, 0)
_DEFAULT_NAME = None
_TEMPLATE_DEVICE_XPATH = "/config/devices/entry[@name='localhost.localdomain']"
_TEMPLATE_VSYS_XPATH = _TEMPLATE_DEVICE_XPATH + "/vsys/entry[@name='{vsys}']"
Expand Down
86 changes: 85 additions & 1 deletion panos/firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from decimal import Decimal

import panos.errors as err
from panos import device, getlogger, yesno
from panos import device, getlogger, isstring, yesno
from panos.base import ENTRY, PanDevice, Root
from panos.base import VarPath as Var

Expand Down Expand Up @@ -484,6 +484,90 @@ def organize_into_vsys(self, create_vsys_objects=True, refresh_vsys=True):
self.add(x)
break

def rule_hit_count(self, rules=None, style=None):
"""Retrieve the rule hit count.

PAN-OS 8.1+

Args:
rules (list): A list of rules. This can be a mix of `panos.policies`
instances or basic strings. If no rules are given, then the hit
count for all rules is retrieved.
style (str): The rule style to use (used if the style cannot automatically
be determined from the `rules` param). The style can be
"application-override", "authentication", "decryption", "dos",
"nat", "pbf", "qos", "sdwan", "security", or "tunnel-inspect". If
unspecified and no rules are specified in which the type can be
inferred, then this param defaults to "security".

Returns:
dict: A dict where the key is the rule name and the value is a dict of hit count information.

"""
if self.retrieve_panos_version() < (8, 1, 0):
raise err.PanDeviceError("Rule hit count is supported in PAN-OS 8.1+")

vals = []
val_types = set([])
for r in rules or []:
if hasattr(r, "HIT_COUNT_STYLE") and hasattr(r, "uid"):
val_types.add(r.HIT_COUNT_STYLE)
vals.append(r.uid)
elif not isstring(r):
raise ValueError("Unsupported type sent: {0}".format(r.__class__))
else:
vals.append(r)

if len(val_types) > 1:
raise ValueError("Multiple types encountered: {0}".format(val_types))

if val_types:
style = val_types.pop()

if style is None:
style = "security"

cmd = ET.Element("show")
sub = ET.SubElement(cmd, "rule-hit-count")
sub = ET.SubElement(sub, "vsys")
sub = ET.SubElement(sub, "vsys-name")
sub = ET.SubElement(sub, "entry", {"name": self.vsys or "vsys1"})
sub = ET.SubElement(sub, "rule-base")
sub = ET.SubElement(sub, "entry", {"name": style})
sub = ET.SubElement(sub, "rules")

if not vals:
ET.SubElement(sub, "all")
else:
sub = ET.SubElement(sub, "list")
for r in vals:
ET.SubElement(sub, "member").text = r

res = self.op(ET.tostring(cmd, encoding="utf-8"), cmd_xml=False)

str_fields = ("latest",)
int_fields = (
"hit-count",
"last-hit-timestamp",
"last-reset-timestamp",
"first-hit-timestamp",
"rule-creation-timestamp",
"rule-modification-timestamp",
)
ans = {}
for elm in res.findall(
"./result/rule-hit-count/vsys/entry/rule-base/entry/rules/entry"
):
name = elm.attrib["name"]
val = {"name": name}
for field in str_fields:
val[field] = elm.find("./{0}".format(field)).text
for field in int_fields:
val[field] = int(elm.find("./{0}".format(field)).text)
ans[name] = val

return ans


class FirewallState(object):
def __init__(self):
Expand Down
3 changes: 3 additions & 0 deletions panos/policies.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class SecurityRule(VersionedPanObject):
# TODO: Add QoS variables
SUFFIX = ENTRY
ROOT = Root.VSYS
HIT_COUNT_STYLE = "security"

def _setup(self):
# xpaths
Expand Down Expand Up @@ -283,6 +284,7 @@ class NatRule(VersionedPanObject):

SUFFIX = ENTRY
ROOT = Root.VSYS
HIT_COUNT_STYLE = "nat"

def _setup(self):
# xpaths
Expand Down Expand Up @@ -613,6 +615,7 @@ class PolicyBasedForwarding(VersionedPanObject):

SUFFIX = ENTRY
ROOT = Root.VSYS
HIT_COUNT_STYLE = "pbf"

def _setup(self):
# xpaths
Expand Down