Skip to content

Commit 20dd7b7

Browse files
authored
fix(panos.base.PanObject.refresh_variable): Refresh works again for regular and attrib style params (#446)
Fixes #444
1 parent ab4d088 commit 20dd7b7

File tree

2 files changed

+129
-11
lines changed

2 files changed

+129
-11
lines changed

panos/base.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,7 @@ def update(self, variable):
696696
% (type(self), self.uid, variable)
697697
)
698698
device.set_config_changed()
699-
path, value, var_path = self._get_param_specific_info(variable)
699+
path, attr, value, var_path = self._get_param_specific_info(variable)
700700
if var_path.vartype == "attrib":
701701
raise NotImplementedError("Cannot update 'attrib' style params")
702702
xpath = "{0}/{1}".format(self.xpath(), path)
@@ -868,7 +868,17 @@ def _get_param_specific_info(self, variable):
868868
# Not an 'entry' variable
869869
varpath = re.sub(regex, getattr(self, matchedvar.variable), varpath)
870870

871-
return (varpath, value, var)
871+
# For vartype=attrib params, we need the containing XML element.
872+
attr = None
873+
if var.vartype == "attrib":
874+
tokens = varpath.rsplit("/", 1)
875+
attr = tokens[-1]
876+
if len(tokens) == 1:
877+
varpath = None
878+
else:
879+
varpath = tokens[0]
880+
881+
return (varpath, attr, value, var)
872882

873883
def refresh(
874884
self, running_config=False, refresh_children=True, exceptions=True, xml=None
@@ -937,10 +947,10 @@ def refresh_variable(self, variable, running_config=False, exceptions=False):
937947
msg = '{0}: refresh_variable({1}) called on {2} object "{3}"'
938948
logger.debug(msg.format(device.id, variable, self.__class__.__name__, self.uid))
939949

940-
info = self._get_param_specific_info(variable)
941-
path = info[0]
942-
var_path = info[2]
943-
xpath = "{0}/{1}".format(self.xpath(), path)
950+
path, attr, value, var_path = self._get_param_specific_info(variable)
951+
xpath = self.xpath()
952+
if path is not None:
953+
xpath += "/{0}".format(path)
944954
err_msg = "Object doesn't exist: {0}".format(xpath)
945955
setattr(self, variable, [] if var_path.vartype in ("member", "entry") else None)
946956

@@ -957,7 +967,7 @@ def refresh_variable(self, variable, running_config=False, exceptions=False):
957967
return
958968

959969
# Determine the first element to look for in the XML
960-
lasttag = path.rsplit("/", 1)[-1]
970+
lasttag = xpath.rsplit("/", 1)[-1]
961971
obj = root.find("result/" + lasttag)
962972
if obj is None:
963973
if exceptions:
@@ -967,13 +977,13 @@ def refresh_variable(self, variable, running_config=False, exceptions=False):
967977
if hasattr(var_path, "parse_value_from_xml_last_tag"):
968978
# Versioned class
969979
settings = {}
970-
var_path.parse_value_from_xml_last_tag(obj, settings)
980+
var_path.parse_value_from_xml_last_tag(obj, settings, attr)
971981
setattr(self, variable, settings.get(variable))
972982
else:
973983
# Classic class
974984
# Rebuild the elements that are lost by refreshing the
975985
# variable directly
976-
sections = path.split("/")[:-1]
986+
sections = xpath.split("/")[:-1]
977987
root = ET.Element("root")
978988
next_element = root
979989
for section in sections:
@@ -2677,7 +2687,8 @@ def _get_param_specific_info(self, param):
26772687
parameter attached to this PanObject / VersionedPanObject.
26782688
26792689
Returns:
2680-
A three element tuple of the variable's xpath (str), the value of
2690+
A four element tuple of the variable's xpath (str), the attribute
2691+
name (if this is vartype="attrib"), the value of
26812692
the variable, and the full ``VarPath`` or ``ParamPath`` object that
26822693
is responsible for handling this variable.
26832694
@@ -2725,7 +2736,12 @@ def _get_param_specific_info(self, param):
27252736
p = token.format(**settings)
27262737
xpath.append(p)
27272738

2728-
return ("/".join(xpath), value, var_path)
2739+
# Remove the last part of vartype=attrib variable xpath parts.
2740+
attr = None
2741+
if var_path.vartype == "attrib":
2742+
attr = xpath.pop()
2743+
2744+
return ("/".join(xpath) or None, attr, value, var_path)
27292745

27302746
def parse_xml(self, xml):
27312747
"""Parse the given XML into this object's parameters.

tests/test_base.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,10 +1245,112 @@ def _setup(self):
12451245
)
12461246
)
12471247
params.append(Base.VersionedParamPath("someint", path="someint", vartype="int"))
1248+
params.append(
1249+
Base.VersionedParamPath("base_uuid", path="uuid", vartype="attrib")
1250+
)
1251+
params.append(Base.VersionedParamPath("action", path="config/action"))
1252+
params.append(
1253+
Base.VersionedParamPath(
1254+
"action_uuid", path="config/action/uuid", vartype="attrib"
1255+
)
1256+
)
12481257

12491258
self._params = tuple(params)
12501259

12511260

1261+
class TestVariableRefreshes(unittest.TestCase):
1262+
def obj(self, key, attribs, value):
1263+
o = MyVersionedObject("foo")
1264+
o.xpath = mock.Mock(return_value="/unit/test/xpath/for/entry[@name='foo']")
1265+
1266+
av = ""
1267+
if attribs:
1268+
for k, v in attribs.items():
1269+
av += ' {0}="{1}"'.format(k, v)
1270+
respString = "<response><result><{0}{1}>{2}</{0}></result></response>".format(
1271+
key, av, value, key
1272+
)
1273+
resp = ET.fromstring(respString)
1274+
1275+
spec = {
1276+
"id": "unittest",
1277+
"get_device_version.return_value": o._UNKNOWN_PANOS_VERSION,
1278+
"xapi.show.return_value": resp,
1279+
"xapi.get.return_value": resp,
1280+
}
1281+
m = mock.Mock(**spec)
1282+
1283+
o.nearest_pandevice = mock.Mock(return_value=m)
1284+
1285+
return m, o
1286+
1287+
def test_entry_refresh(self):
1288+
m, o = self.obj("entries", None, "<entry name='one'/><entry name='two'/>")
1289+
1290+
ans = o.refresh_variable("entries")
1291+
1292+
m.xapi.get.assert_called_once_with(
1293+
o.xpath() + "/multiple/entries", retry_on_peer=o.HA_SYNC,
1294+
)
1295+
self.assertEqual(ans, o.entries)
1296+
self.assertEqual(ans, ["one", "two"])
1297+
1298+
def test_member_refresh(self):
1299+
m, o = self.obj(
1300+
"members", None, "<member>first</member><member>second</member>"
1301+
)
1302+
1303+
ans = o.refresh_variable("members")
1304+
1305+
m.xapi.get.assert_called_once_with(
1306+
o.xpath() + "/multiple/members", retry_on_peer=o.HA_SYNC,
1307+
)
1308+
self.assertEqual(ans, o.members)
1309+
self.assertEqual(ans, ["first", "second"])
1310+
1311+
def test_int_refresh(self):
1312+
m, o = self.obj("someint", None, "42")
1313+
1314+
ans = o.refresh_variable("someint")
1315+
1316+
m.xapi.get.assert_called_once_with(
1317+
o.xpath() + "/someint", retry_on_peer=o.HA_SYNC,
1318+
)
1319+
self.assertEqual(ans, o.someint)
1320+
self.assertEqual(ans, 42)
1321+
1322+
def test_base_attrib_refresh(self):
1323+
m, o = self.obj("entry", {"name": "foo", "uuid": "1234-56-789"}, "")
1324+
1325+
ans = o.refresh_variable("base_uuid")
1326+
1327+
m.xapi.get.assert_called_once_with(o.xpath(), retry_on_peer=o.HA_SYNC)
1328+
self.assertEqual(ans, o.base_uuid)
1329+
self.assertEqual(ans, "1234-56-789")
1330+
1331+
def test_string_refresh(self):
1332+
m, o = self.obj("action", {"uuid": "1234-56-789"}, "DENY")
1333+
1334+
ans = o.refresh_variable("action")
1335+
1336+
m.xapi.get.assert_called_once_with(
1337+
o.xpath() + "/config/action", retry_on_peer=o.HA_SYNC,
1338+
)
1339+
self.assertEqual(ans, o.action)
1340+
self.assertEqual(ans, "DENY")
1341+
1342+
def test_nested_attrib_refresh(self):
1343+
m, o = self.obj("action", {"uuid": "1234-56-789"}, "DENY")
1344+
1345+
ans = o.refresh_variable("action_uuid")
1346+
1347+
m.xapi.get.assert_called_once_with(
1348+
o.xpath() + "/config/action", retry_on_peer=o.HA_SYNC,
1349+
)
1350+
self.assertEqual(ans, o.action_uuid)
1351+
self.assertEqual(ans, "1234-56-789")
1352+
1353+
12521354
class TestEqual(unittest.TestCase):
12531355
def test_ordered(self):
12541356
o1 = MyVersionedObject("a", ["a", "b"], ["c", "d"], 5)

0 commit comments

Comments
 (0)