pan-os-python 1.12.4__tar.gz → 1.12.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/PKG-INFO +1 -1
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/panos/__init__.py +24 -3
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/panos/base.py +55 -24
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/panos/firewall.py +7 -9
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/panos/network.py +7 -2
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/panos/panorama.py +20 -17
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/panos/predefined.py +3 -2
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/panos/userid.py +13 -7
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/pyproject.toml +1 -1
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/LICENSE +0 -0
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/README.md +0 -0
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/panos/device.py +0 -0
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/panos/errors.py +0 -0
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/panos/ha.py +0 -0
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/panos/objects.py +0 -0
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/panos/plugins.py +0 -0
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/panos/policies.py +0 -0
- {pan_os_python-1.12.4 → pan_os_python-1.12.6}/panos/updater.py +0 -0
|
@@ -26,13 +26,13 @@ Documentation available at https://pan-os-python.readthedocs.io
|
|
|
26
26
|
|
|
27
27
|
__author__ = "Palo Alto Networks"
|
|
28
28
|
__email__ = "devrel@paloaltonetworks.com"
|
|
29
|
-
__version__ = "1.12.
|
|
29
|
+
__version__ = "1.12.6"
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
import logging
|
|
33
|
+
import re
|
|
33
34
|
import sys
|
|
34
35
|
import xml.etree.ElementTree as ET
|
|
35
|
-
from distutils.version import LooseVersion # Used by PanOSVersion class
|
|
36
36
|
|
|
37
37
|
# Warn if running on end-of-life python
|
|
38
38
|
if sys.version_info < (3, 6):
|
|
@@ -127,7 +127,28 @@ pan.DEBUG2 = pan.DEBUG1 - 1
|
|
|
127
127
|
pan.DEBUG3 = pan.DEBUG2 - 1
|
|
128
128
|
|
|
129
129
|
|
|
130
|
-
class
|
|
130
|
+
class _LooseVersion:
|
|
131
|
+
"""Minimal LooseVersion replacement; distutils was removed in Python 3.12."""
|
|
132
|
+
|
|
133
|
+
_component_re = re.compile(r"(\d+|[a-z]+|\.)")
|
|
134
|
+
|
|
135
|
+
def __init__(self, vstring=None):
|
|
136
|
+
if vstring:
|
|
137
|
+
self.parse(vstring)
|
|
138
|
+
|
|
139
|
+
def parse(self, vstring):
|
|
140
|
+
self.vstring = vstring
|
|
141
|
+
parts = [x for x in self._component_re.split(vstring) if x and x != "."]
|
|
142
|
+
self.version = [int(x) if x.isdigit() else x for x in parts]
|
|
143
|
+
|
|
144
|
+
def __str__(self):
|
|
145
|
+
return self.vstring
|
|
146
|
+
|
|
147
|
+
def __repr__(self):
|
|
148
|
+
return "LooseVersion ('%s')" % str(self)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class PanOSVersion(_LooseVersion):
|
|
131
152
|
"""LooseVersion with convenience properties to access version components"""
|
|
132
153
|
|
|
133
154
|
@property
|
|
@@ -29,6 +29,7 @@ import sys
|
|
|
29
29
|
import time
|
|
30
30
|
import xml.dom.minidom as minidom
|
|
31
31
|
import xml.etree.ElementTree as ET
|
|
32
|
+
from xml.sax.saxutils import escape as xml_escape
|
|
32
33
|
|
|
33
34
|
import pan.commit
|
|
34
35
|
import pan.xapi
|
|
@@ -36,6 +37,31 @@ from pan.config import PanConfig
|
|
|
36
37
|
|
|
37
38
|
import panos
|
|
38
39
|
import panos.errors as err
|
|
40
|
+
|
|
41
|
+
# Defined before sub-module imports below: panos.userid imports _xpath_safe
|
|
42
|
+
# from this module, so the symbol must exist by the time `from panos import
|
|
43
|
+
# userid` triggers userid's module body.
|
|
44
|
+
SELF = "/%s"
|
|
45
|
+
ENTRY = "/entry[@name=%s]"
|
|
46
|
+
MEMBER = "/member[text()=%s]"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _xpath_safe(val):
|
|
50
|
+
"""Return val as an XPath 1.0 string literal, safe to inject into a predicate.
|
|
51
|
+
|
|
52
|
+
XPath 1.0 has no escape syntax for quotes inside string literals, so a value
|
|
53
|
+
containing quotes must be wrapped in the opposite quote, or split into a
|
|
54
|
+
concat() expression when both quote types are present.
|
|
55
|
+
"""
|
|
56
|
+
val = "" if val is None else str(val)
|
|
57
|
+
if "'" not in val:
|
|
58
|
+
return "'" + val + "'"
|
|
59
|
+
if '"' not in val:
|
|
60
|
+
return '"' + val + '"'
|
|
61
|
+
parts = val.split("'")
|
|
62
|
+
return "concat('" + "', \"'\", '".join(parts) + "')"
|
|
63
|
+
|
|
64
|
+
|
|
39
65
|
from panos import (
|
|
40
66
|
chunk_instances_for_delete_similar,
|
|
41
67
|
isstring,
|
|
@@ -48,9 +74,6 @@ from panos import (
|
|
|
48
74
|
logger = panos.getlogger(__name__)
|
|
49
75
|
|
|
50
76
|
Root = panos.enum("DEVICE", "VSYS", "MGTCONFIG", "PANORAMA", "PANORAMA_VSYS")
|
|
51
|
-
SELF = "/%s"
|
|
52
|
-
ENTRY = "/entry[@name='%s']"
|
|
53
|
-
MEMBER = "/member[text()='%s']"
|
|
54
77
|
|
|
55
78
|
|
|
56
79
|
# PanObject type
|
|
@@ -340,7 +363,7 @@ class PanObject(object):
|
|
|
340
363
|
# xpath was asked for.
|
|
341
364
|
addon = p.XPATH
|
|
342
365
|
if p.SUFFIX is not None:
|
|
343
|
-
addon += p.SUFFIX % (p.uid,)
|
|
366
|
+
addon += p.SUFFIX % (_xpath_safe(p.uid),)
|
|
344
367
|
path.insert(0, addon)
|
|
345
368
|
if p.__class__.__name__ == "Firewall" and p.parent is not None:
|
|
346
369
|
if p.parent.__class__.__name__ == "DeviceGroup":
|
|
@@ -412,7 +435,7 @@ class PanObject(object):
|
|
|
412
435
|
xpath = "/config/shared"
|
|
413
436
|
else:
|
|
414
437
|
xpath = "/config/devices/entry[@name='localhost.localdomain']"
|
|
415
|
-
xpath += "/{0}/entry[@name=
|
|
438
|
+
xpath += "/{0}/entry[@name={1}]".format(label, _xpath_safe(vsys or "vsys1"))
|
|
416
439
|
|
|
417
440
|
return xpath
|
|
418
441
|
|
|
@@ -476,7 +499,7 @@ class PanObject(object):
|
|
|
476
499
|
regex,
|
|
477
500
|
matchedvar.path
|
|
478
501
|
+ "/"
|
|
479
|
-
+ "entry[@name
|
|
502
|
+
+ "entry[@name=%s]" % _xpath_safe(entry_value[0]),
|
|
480
503
|
section,
|
|
481
504
|
)
|
|
482
505
|
entryvar = matchedvar
|
|
@@ -860,7 +883,9 @@ class PanObject(object):
|
|
|
860
883
|
entry_value = panos.string_or_list(getattr(self, matchedvar.variable))
|
|
861
884
|
varpath = re.sub(
|
|
862
885
|
regex,
|
|
863
|
-
matchedvar.path
|
|
886
|
+
matchedvar.path
|
|
887
|
+
+ "/"
|
|
888
|
+
+ "entry[@name=%s]" % _xpath_safe(entry_value[0]),
|
|
864
889
|
varpath,
|
|
865
890
|
)
|
|
866
891
|
else:
|
|
@@ -1448,7 +1473,9 @@ class PanObject(object):
|
|
|
1448
1473
|
replacement = replacement[0]
|
|
1449
1474
|
path = re.sub(
|
|
1450
1475
|
regex,
|
|
1451
|
-
matchedvar.path
|
|
1476
|
+
matchedvar.path
|
|
1477
|
+
+ "/"
|
|
1478
|
+
+ "entry[@name=%s]" % _xpath_safe(replacement),
|
|
1452
1479
|
path,
|
|
1453
1480
|
)
|
|
1454
1481
|
else:
|
|
@@ -1983,10 +2010,10 @@ class PanObject(object):
|
|
|
1983
2010
|
prefix = ""
|
|
1984
2011
|
xpath = self.xpath_nosuffix()
|
|
1985
2012
|
if self.SUFFIX == ENTRY:
|
|
1986
|
-
joiner = "@name=
|
|
2013
|
+
joiner = "@name={0}"
|
|
1987
2014
|
prefix = "entry"
|
|
1988
2015
|
elif self.SUFFIX == MEMBER:
|
|
1989
|
-
joiner = "text()=
|
|
2016
|
+
joiner = "text()={0}"
|
|
1990
2017
|
prefix = "member"
|
|
1991
2018
|
|
|
1992
2019
|
# After some testing, PAN-OS seems to be able to handle a DELETE API call
|
|
@@ -1999,7 +2026,7 @@ class PanObject(object):
|
|
|
1999
2026
|
"{0}/{1}[{2}]".format(
|
|
2000
2027
|
xpath,
|
|
2001
2028
|
prefix,
|
|
2002
|
-
" or ".join(joiner.format(x.uid) for x in chunk),
|
|
2029
|
+
" or ".join(joiner.format(_xpath_safe(x.uid)) for x in chunk),
|
|
2003
2030
|
),
|
|
2004
2031
|
retry_on_peer=self.HA_SYNC,
|
|
2005
2032
|
)
|
|
@@ -2036,7 +2063,9 @@ class PanObject(object):
|
|
|
2036
2063
|
"""Iterates over a vsys_dict, deleting the import for all instances."""
|
|
2037
2064
|
for vsys_spec in vsys_dict.values():
|
|
2038
2065
|
for objs in vsys_spec.values():
|
|
2039
|
-
members = " or ".join(
|
|
2066
|
+
members = " or ".join(
|
|
2067
|
+
"text()={0}".format(_xpath_safe(x.uid)) for x in objs
|
|
2068
|
+
)
|
|
2040
2069
|
xpath = "{0}/member[{1}]".format(objs[0].xpath_import_base(), members)
|
|
2041
2070
|
# API complains if you try to do this in one delete statement,
|
|
2042
2071
|
# so do one delete per vsys per path, just like when we set the
|
|
@@ -2431,7 +2460,7 @@ class VersionedPanObject(PanObject):
|
|
|
2431
2460
|
|
|
2432
2461
|
_DEFAULT_NAME = None
|
|
2433
2462
|
_TEMPLATE_DEVICE_XPATH = "/config/devices/entry[@name='localhost.localdomain']"
|
|
2434
|
-
_TEMPLATE_VSYS_XPATH = _TEMPLATE_DEVICE_XPATH + "/vsys/entry[@name=
|
|
2463
|
+
_TEMPLATE_VSYS_XPATH = _TEMPLATE_DEVICE_XPATH + "/vsys/entry[@name={vsys}]"
|
|
2435
2464
|
_TEMPLATE_MGTCONFIG_XPATH = "/config/mgt-config"
|
|
2436
2465
|
|
|
2437
2466
|
def __init__(self, *args, **kwargs):
|
|
@@ -2638,7 +2667,7 @@ class VersionedPanObject(PanObject):
|
|
|
2638
2667
|
if ap.startswith("entry "):
|
|
2639
2668
|
junk, var_to_use = ap.split()
|
|
2640
2669
|
sol_value = panos.string_or_list(settings[var_to_use])[0]
|
|
2641
|
-
finder = "entry[@name=
|
|
2670
|
+
finder = "entry[@name={0}]".format(_xpath_safe(sol_value))
|
|
2642
2671
|
tag = "entry"
|
|
2643
2672
|
attribs["name"] = sol_value
|
|
2644
2673
|
elif ap == "entry[@name='localhost.localdomain']":
|
|
@@ -2725,8 +2754,8 @@ class VersionedPanObject(PanObject):
|
|
|
2725
2754
|
p = None
|
|
2726
2755
|
if token.startswith("entry "):
|
|
2727
2756
|
junk, var_to_use = token.split()
|
|
2728
|
-
p = "entry[name=
|
|
2729
|
-
*(x for x in self._value_as_list(settings[var_to_use]))
|
|
2757
|
+
p = "entry[name={0}]".format(
|
|
2758
|
+
*(_xpath_safe(x) for x in self._value_as_list(settings[var_to_use]))
|
|
2730
2759
|
)
|
|
2731
2760
|
else:
|
|
2732
2761
|
p = None
|
|
@@ -2835,7 +2864,7 @@ class VersionedPanObject(PanObject):
|
|
|
2835
2864
|
"""Returns the version specific xpath of this object."""
|
|
2836
2865
|
panos_version = self.retrieve_panos_version()
|
|
2837
2866
|
val = self._xpaths._get_versioned_value(panos_version, self.parent)
|
|
2838
|
-
return val.format(vsys=self.vsys or "vsys1")
|
|
2867
|
+
return val.format(vsys=_xpath_safe(self.vsys or "vsys1"))
|
|
2839
2868
|
|
|
2840
2869
|
|
|
2841
2870
|
class VersionedParamPath(VersioningSupport):
|
|
@@ -3198,7 +3227,7 @@ class ParamPath(object):
|
|
|
3198
3227
|
return
|
|
3199
3228
|
settings[entry_var] = ans.attrib["name"]
|
|
3200
3229
|
sol_val = panos.string_or_list(settings[entry_var])[0]
|
|
3201
|
-
path_str = "entry[@name=
|
|
3230
|
+
path_str = "entry[@name={0}]".format(_xpath_safe(sol_val))
|
|
3202
3231
|
else:
|
|
3203
3232
|
# Standard path part
|
|
3204
3233
|
try:
|
|
@@ -3386,7 +3415,7 @@ class VsysOperations(VersionedPanObject):
|
|
|
3386
3415
|
|
|
3387
3416
|
if vsys != "shared" and vsys is not None and self.XPATH_IMPORT is not None:
|
|
3388
3417
|
xpath = self.xpath_import_base(vsys)
|
|
3389
|
-
element = "<member>{0}</member>".format(self.uid)
|
|
3418
|
+
element = "<member>{0}</member>".format(xml_escape(self.uid))
|
|
3390
3419
|
device = self.nearest_pandevice()
|
|
3391
3420
|
device.active().xapi.set(xpath, element, retry_on_peer=True)
|
|
3392
3421
|
|
|
@@ -3420,8 +3449,8 @@ class VsysOperations(VersionedPanObject):
|
|
|
3420
3449
|
p = p.parent
|
|
3421
3450
|
|
|
3422
3451
|
if vsys != "shared" and vsys is not None and self.XPATH_IMPORT is not None:
|
|
3423
|
-
xpath = "{0}/member[text()=
|
|
3424
|
-
self.xpath_import_base(vsys), self.uid
|
|
3452
|
+
xpath = "{0}/member[text()={1}]".format(
|
|
3453
|
+
self.xpath_import_base(vsys), _xpath_safe(self.uid)
|
|
3425
3454
|
)
|
|
3426
3455
|
device = self.nearest_pandevice()
|
|
3427
3456
|
device.active().xapi.delete(xpath, retry_on_peer=True)
|
|
@@ -3830,9 +3859,11 @@ class PanDevice(PanObject):
|
|
|
3830
3859
|
def method(self, *args, **kwargs):
|
|
3831
3860
|
retry_on_peer = kwargs.pop(
|
|
3832
3861
|
"retry_on_peer",
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3862
|
+
(
|
|
3863
|
+
True
|
|
3864
|
+
if super_method_name not in ("keygen", "op", "ad_hoc", "export")
|
|
3865
|
+
else False
|
|
3866
|
+
),
|
|
3836
3867
|
)
|
|
3837
3868
|
apply_on_peer = kwargs.pop("apply_on_peer", False)
|
|
3838
3869
|
ha_peer = self.pan_device.ha_peer
|
|
@@ -27,6 +27,7 @@ import panos.errors as err
|
|
|
27
27
|
from panos import device, getlogger, yesno
|
|
28
28
|
from panos.base import ENTRY, PanDevice, Root
|
|
29
29
|
from panos.base import VarPath as Var
|
|
30
|
+
from panos.base import _xpath_safe
|
|
30
31
|
|
|
31
32
|
logger = getlogger(__name__)
|
|
32
33
|
|
|
@@ -333,7 +334,7 @@ class Firewall(PanDevice):
|
|
|
333
334
|
devices_xpath = self.devicegroup().xpath() + self.XPATH
|
|
334
335
|
devices_xml = panorama.xapi.get(devices_xpath)
|
|
335
336
|
dg_vsys = devices_xml.findall(
|
|
336
|
-
"result/devices/entry[@name
|
|
337
|
+
"result/devices/entry[@name=%s]/vsys/entry" % _xpath_safe(self.serial)
|
|
337
338
|
)
|
|
338
339
|
if dg_vsys:
|
|
339
340
|
if len(dg_vsys) == 1:
|
|
@@ -344,7 +345,7 @@ class Firewall(PanDevice):
|
|
|
344
345
|
# It's not the only vsys, just delete the vsys
|
|
345
346
|
panorama.set_config_changed()
|
|
346
347
|
panorama.xapi.delete(
|
|
347
|
-
self.xpath() + "/vsys/entry[@name
|
|
348
|
+
self.xpath() + "/vsys/entry[@name=%s]" % _xpath_safe(self.vsys)
|
|
348
349
|
)
|
|
349
350
|
else:
|
|
350
351
|
# This is a firewall under a panorama
|
|
@@ -392,7 +393,7 @@ class Firewall(PanDevice):
|
|
|
392
393
|
)
|
|
393
394
|
# Add system settings to firewall instances
|
|
394
395
|
for fw in firewall_instances:
|
|
395
|
-
entry = xml.find("entry[@name
|
|
396
|
+
entry = xml.find("entry[@name=%s]" % _xpath_safe(fw.serial))
|
|
396
397
|
system = fw.find_or_create(None, device.SystemSettings)
|
|
397
398
|
system.hostname = entry.findtext("hostname")
|
|
398
399
|
system.ip_address = entry.findtext("ip-address")
|
|
@@ -591,7 +592,6 @@ class FirewallCommit(object):
|
|
|
591
592
|
self.exclude_device_and_network,
|
|
592
593
|
self.exclude_shared_objects,
|
|
593
594
|
self.exclude_policy_and_objects,
|
|
594
|
-
self.force,
|
|
595
595
|
]
|
|
596
596
|
|
|
597
597
|
return any(x for x in pp_list)
|
|
@@ -622,11 +622,9 @@ class FirewallCommit(object):
|
|
|
622
622
|
ET.SubElement(partial, "shared-object").text = "excluded"
|
|
623
623
|
if self.exclude_policy_and_objects:
|
|
624
624
|
ET.SubElement(partial, "policy-and-objects").text = "excluded"
|
|
625
|
+
fe.append(partial)
|
|
625
626
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
fe.append(partial)
|
|
629
|
-
else:
|
|
630
|
-
root.append(partial)
|
|
627
|
+
if self.force:
|
|
628
|
+
fe = ET.SubElement(root, "force")
|
|
631
629
|
|
|
632
630
|
return root
|
|
@@ -26,7 +26,12 @@ import panos.errors as err
|
|
|
26
26
|
from panos import device, getlogger, string_or_list
|
|
27
27
|
from panos.base import ENTRY, MEMBER, PanObject, Root
|
|
28
28
|
from panos.base import VarPath as Var
|
|
29
|
-
from panos.base import
|
|
29
|
+
from panos.base import (
|
|
30
|
+
VersionedPanObject,
|
|
31
|
+
VersionedParamPath,
|
|
32
|
+
VsysOperations,
|
|
33
|
+
_xpath_safe,
|
|
34
|
+
)
|
|
30
35
|
|
|
31
36
|
logger = getlogger(__name__)
|
|
32
37
|
|
|
@@ -741,7 +746,7 @@ class Subinterface(Interface):
|
|
|
741
746
|
if self._BASE_INTERFACE_NAME in path:
|
|
742
747
|
base = self.uid.split(".")[0]
|
|
743
748
|
path = path.replace(
|
|
744
|
-
self._BASE_INTERFACE_NAME, "entry[@name=
|
|
749
|
+
self._BASE_INTERFACE_NAME, "entry[@name={0}]".format(_xpath_safe(base))
|
|
745
750
|
)
|
|
746
751
|
|
|
747
752
|
return path
|
|
@@ -28,7 +28,7 @@ import panos.errors as err
|
|
|
28
28
|
from panos import base, firewall, getlogger, policies, yesno
|
|
29
29
|
from panos.base import ENTRY, MEMBER, OpState, PanObject, Root
|
|
30
30
|
from panos.base import VarPath as Var
|
|
31
|
-
from panos.base import VersionedPanObject, VersionedParamPath
|
|
31
|
+
from panos.base import VersionedPanObject, VersionedParamPath, _xpath_safe
|
|
32
32
|
|
|
33
33
|
logger = getlogger(__name__)
|
|
34
34
|
|
|
@@ -643,7 +643,7 @@ class Panorama(base.PanDevice):
|
|
|
643
643
|
serial = str(device)
|
|
644
644
|
if serial is None:
|
|
645
645
|
continue
|
|
646
|
-
entry = devices_xml.find("entry[@name
|
|
646
|
+
entry = devices_xml.find("entry[@name=%s]" % _xpath_safe(serial))
|
|
647
647
|
if entry is None:
|
|
648
648
|
if only_connected:
|
|
649
649
|
raise err.PanNotConnectedOnPanorama(
|
|
@@ -661,7 +661,10 @@ class Panorama(base.PanDevice):
|
|
|
661
661
|
except AttributeError:
|
|
662
662
|
continue
|
|
663
663
|
# Create entry if needed
|
|
664
|
-
if
|
|
664
|
+
if (
|
|
665
|
+
filtered_devices_xml.find("entry[@name=%s]" % _xpath_safe(serial))
|
|
666
|
+
is None
|
|
667
|
+
):
|
|
665
668
|
entry_copy = deepcopy(entry)
|
|
666
669
|
# If looking for specific vsys, erase all vsys in filtered entry
|
|
667
670
|
if vsys != "shared" and vsys is not None:
|
|
@@ -670,7 +673,7 @@ class Panorama(base.PanDevice):
|
|
|
670
673
|
filtered_devices_xml.append(entry_copy)
|
|
671
674
|
# Get specific vsys
|
|
672
675
|
if vsys != "shared" and vsys is not None:
|
|
673
|
-
vsys_entry = entry.find("vsys/entry[@name
|
|
676
|
+
vsys_entry = entry.find("vsys/entry[@name=%s]" % _xpath_safe(vsys))
|
|
674
677
|
if vsys_entry is None:
|
|
675
678
|
raise err.PanNotAttachedOnPanorama(
|
|
676
679
|
"Can't find device with serial %s and"
|
|
@@ -678,7 +681,7 @@ class Panorama(base.PanDevice):
|
|
|
678
681
|
% (serial, vsys, self.id)
|
|
679
682
|
)
|
|
680
683
|
vsys_section = filtered_devices_xml.find(
|
|
681
|
-
"entry[@name
|
|
684
|
+
"entry[@name=%s]/vsys" % _xpath_safe(serial)
|
|
682
685
|
)
|
|
683
686
|
vsys_section.append(vsys_entry)
|
|
684
687
|
devices_xml = filtered_devices_xml
|
|
@@ -733,7 +736,8 @@ class Panorama(base.PanDevice):
|
|
|
733
736
|
continue
|
|
734
737
|
for fw_entry in dg_entry.find("devices"):
|
|
735
738
|
fw_entry_op = devicegroup_opxml.find(
|
|
736
|
-
"entry/devices/entry[@name
|
|
739
|
+
"entry/devices/entry[@name=%s]"
|
|
740
|
+
% _xpath_safe(fw_entry.get("name"))
|
|
737
741
|
)
|
|
738
742
|
if fw_entry_op is not None:
|
|
739
743
|
panos.xml_combine(fw_entry, fw_entry_op)
|
|
@@ -748,7 +752,7 @@ class Panorama(base.PanDevice):
|
|
|
748
752
|
dg_serials = [
|
|
749
753
|
entry.get("name")
|
|
750
754
|
for entry in devicegroup_configxml.findall(
|
|
751
|
-
"entry[@name
|
|
755
|
+
"entry[@name=%s]/devices/entry" % _xpath_safe(dg.name)
|
|
752
756
|
)
|
|
753
757
|
]
|
|
754
758
|
# Find firewall with each serial
|
|
@@ -759,13 +763,14 @@ class Panorama(base.PanDevice):
|
|
|
759
763
|
all_dg_vsys = [
|
|
760
764
|
entry.get("name")
|
|
761
765
|
for entry in devicegroup_configxml.findall(
|
|
762
|
-
"entry[@name
|
|
763
|
-
% (dg.name, dg_serial)
|
|
766
|
+
"entry[@name=%s]/devices/entry[@name=%s]/vsys/entry"
|
|
767
|
+
% (_xpath_safe(dg.name), _xpath_safe(dg_serial))
|
|
764
768
|
)
|
|
765
769
|
]
|
|
766
770
|
# Collect the firewall serial entry to get current status information
|
|
767
771
|
fw_entry = devicegroup_configxml.find(
|
|
768
|
-
"entry[@name
|
|
772
|
+
"entry[@name=%s]/devices/entry[@name=%s]"
|
|
773
|
+
% (_xpath_safe(dg.name), _xpath_safe(dg_serial))
|
|
769
774
|
)
|
|
770
775
|
if not all_dg_vsys:
|
|
771
776
|
# This is a single-context firewall, assume vsys1
|
|
@@ -807,7 +812,8 @@ class Panorama(base.PanDevice):
|
|
|
807
812
|
shared_policy_status = fw_entry.findtext("shared-policy-status")
|
|
808
813
|
if shared_policy_status is None:
|
|
809
814
|
shared_policy_status = fw_entry.findtext(
|
|
810
|
-
"vsys/entry[@name
|
|
815
|
+
"vsys/entry[@name=%s]/shared-policy-status"
|
|
816
|
+
% _xpath_safe(dg_vsys)
|
|
811
817
|
)
|
|
812
818
|
fw.state.set_shared_policy_synced(shared_policy_status)
|
|
813
819
|
|
|
@@ -990,7 +996,6 @@ class PanoramaCommit(object):
|
|
|
990
996
|
self.log_collector_groups,
|
|
991
997
|
self.exclude_device_and_network,
|
|
992
998
|
self.exclude_shared_objects,
|
|
993
|
-
self.force,
|
|
994
999
|
]
|
|
995
1000
|
|
|
996
1001
|
return any(x for x in pp_list)
|
|
@@ -1031,12 +1036,10 @@ class PanoramaCommit(object):
|
|
|
1031
1036
|
ET.SubElement(partial, "device-and-network").text = "excluded"
|
|
1032
1037
|
if self.exclude_shared_objects:
|
|
1033
1038
|
ET.SubElement(partial, "shared-object").text = "excluded"
|
|
1039
|
+
root.append(partial)
|
|
1034
1040
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
fe.append(partial)
|
|
1038
|
-
else:
|
|
1039
|
-
root.append(partial)
|
|
1041
|
+
if self.force:
|
|
1042
|
+
fe = ET.SubElement(root, "force")
|
|
1040
1043
|
|
|
1041
1044
|
return root
|
|
1042
1045
|
|
|
@@ -21,6 +21,7 @@ from pan.xapi import PanXapiError
|
|
|
21
21
|
|
|
22
22
|
import panos.errors as err
|
|
23
23
|
from panos import getlogger, objects
|
|
24
|
+
from panos.base import _xpath_safe
|
|
24
25
|
from panos.updater import PanOSVersion
|
|
25
26
|
|
|
26
27
|
logger = getlogger(__name__)
|
|
@@ -43,7 +44,7 @@ class Predefined(object):
|
|
|
43
44
|
|
|
44
45
|
# xpath
|
|
45
46
|
XPATH = "/config/predefined"
|
|
46
|
-
SINGLE_ENTRY_XPATH = "/entry[@name=
|
|
47
|
+
SINGLE_ENTRY_XPATH = "/entry[@name={0}]"
|
|
47
48
|
ALL_ENTRIES_XPATH = "/entry"
|
|
48
49
|
CHILDTYPES = (
|
|
49
50
|
"objects.ApplicationContainer",
|
|
@@ -76,7 +77,7 @@ class Predefined(object):
|
|
|
76
77
|
x.parent = self
|
|
77
78
|
xpath = x.xpath_nosuffix()
|
|
78
79
|
if name is not None:
|
|
79
|
-
xpath += self.SINGLE_ENTRY_XPATH.format(name)
|
|
80
|
+
xpath += self.SINGLE_ENTRY_XPATH.format(_xpath_safe(name))
|
|
80
81
|
else:
|
|
81
82
|
xpath += self.ALL_ENTRIES_XPATH
|
|
82
83
|
|
|
@@ -19,11 +19,13 @@
|
|
|
19
19
|
|
|
20
20
|
import xml.etree.ElementTree as ET
|
|
21
21
|
from copy import deepcopy
|
|
22
|
+
from xml.sax.saxutils import escape, quoteattr
|
|
22
23
|
|
|
23
24
|
from pan.xapi import PanXapiError
|
|
24
25
|
|
|
25
26
|
import panos.errors as err
|
|
26
27
|
from panos import getlogger, string_or_list, string_or_list_or_none
|
|
28
|
+
from panos.base import _xpath_safe
|
|
27
29
|
from panos.updater import PanOSVersion
|
|
28
30
|
|
|
29
31
|
logger = getlogger(__name__)
|
|
@@ -244,7 +246,7 @@ class UserId(object):
|
|
|
244
246
|
return
|
|
245
247
|
tags = [self.prefix + t for t in tags]
|
|
246
248
|
for c_ip in ip:
|
|
247
|
-
tagelement = register.find("./entry[@ip
|
|
249
|
+
tagelement = register.find("./entry[@ip=%s]/tag" % _xpath_safe(c_ip))
|
|
248
250
|
if tagelement is None:
|
|
249
251
|
entry = ET.SubElement(register, "entry", {"ip": c_ip})
|
|
250
252
|
tagelement = ET.SubElement(entry, "tag")
|
|
@@ -275,7 +277,7 @@ class UserId(object):
|
|
|
275
277
|
return
|
|
276
278
|
tags = [self.prefix + t for t in tags]
|
|
277
279
|
for c_ip in ip:
|
|
278
|
-
tagelement = unregister.find("./entry[@ip
|
|
280
|
+
tagelement = unregister.find("./entry[@ip=%s]/tag" % _xpath_safe(c_ip))
|
|
279
281
|
if tagelement is None:
|
|
280
282
|
entry = ET.SubElement(unregister, "entry", {"ip": c_ip})
|
|
281
283
|
tagelement = ET.SubElement(entry, "tag")
|
|
@@ -550,7 +552,7 @@ class UserId(object):
|
|
|
550
552
|
"<show><user><group><list>",
|
|
551
553
|
]
|
|
552
554
|
if style is not None:
|
|
553
|
-
msg.append("<entry name=
|
|
555
|
+
msg.append("<entry name={0}/>".format(quoteattr(style)))
|
|
554
556
|
msg.append("</list></group></user></show>")
|
|
555
557
|
cmd = "".join(msg)
|
|
556
558
|
vsys = self.device.vsys or "vsys1"
|
|
@@ -594,7 +596,11 @@ class UserId(object):
|
|
|
594
596
|
list
|
|
595
597
|
|
|
596
598
|
"""
|
|
597
|
-
cmd =
|
|
599
|
+
cmd = (
|
|
600
|
+
"<show><user><group><name>"
|
|
601
|
+
+ escape(group)
|
|
602
|
+
+ "</name></group></user></show>"
|
|
603
|
+
)
|
|
598
604
|
vsys = self.device.vsys or "vsys1"
|
|
599
605
|
|
|
600
606
|
resp = self.device.op(cmd, vsys=vsys, cmd_xml=False)
|
|
@@ -644,12 +650,12 @@ class UserId(object):
|
|
|
644
650
|
if user is None:
|
|
645
651
|
msg.append(
|
|
646
652
|
"<all>"
|
|
647
|
-
+ "<limit>{0}</limit>".format(limit)
|
|
648
|
-
+ "<start-point>{0}</start-point>".format(start)
|
|
653
|
+
+ "<limit>{0}</limit>".format(escape(str(limit)))
|
|
654
|
+
+ "<start-point>{0}</start-point>".format(escape(str(start)))
|
|
649
655
|
+ "</all>"
|
|
650
656
|
)
|
|
651
657
|
else:
|
|
652
|
-
msg.append("<user>{0}</user>".format(user))
|
|
658
|
+
msg.append("<user>{0}</user>".format(escape(user)))
|
|
653
659
|
msg.append("</registered-user></object></show>")
|
|
654
660
|
|
|
655
661
|
cmd = ET.fromstring("".join(msg))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|