Skip to content

Commit f986e03

Browse files
authored
chore: Fix issues with PANOS software updater subsystem
1 parent b76d567 commit f986e03

File tree

1 file changed

+81
-23
lines changed

1 file changed

+81
-23
lines changed

panos/updater.py

Lines changed: 81 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,10 @@ def install(self, version, load_config=None, sync=False):
123123
)
124124
response = self._op(
125125
'request system software install %s version "%s"'
126-
% ('load-config "{0}"'.format(load_config) if load_config else "", version,)
126+
% (
127+
'load-config "{0}"'.format(load_config) if load_config else "",
128+
version,
129+
)
127130
)
128131
if sync:
129132
result = self.pandevice.syncjob(response)
@@ -230,7 +233,7 @@ def download_install_reboot(
230233
self.download_install(target_version, load_config, sync=True)
231234
# Reboot the device
232235
self._logger.info(
233-
"Device %s is rebooting after upgrading to version %s. This will take a while."
236+
"Device %s is rebooting after upgrading to version %s. This will take a while."
234237
% (self.pandevice.id, version)
235238
)
236239
self.pandevice.restart()
@@ -253,36 +256,56 @@ def _next_upgrade_version(
253256
install_base: bool,
254257
) -> PanOSVersion:
255258
current_version = PanOSVersion(self.pandevice.version)
259+
if target_version != "latest" and current_version == target_version:
260+
return None
256261
available_versions = list(map(PanOSVersion, self.versions.keys()))
257262
latest_version = max(available_versions)
258263
next_minor_version = self._next_minor_version(current_version)
264+
if next_minor_version not in available_versions:
265+
next_minor_version = None
259266
if install_base:
260267
if target_version == "latest":
261-
return min(latest_version, next_minor_version)
262-
elif latest_version < target_version:
263-
return next_minor_version
264-
elif not self._direct_upgrade_possible(current_version, target_version):
265-
return next_minor_version
268+
return (
269+
next_minor_version
270+
if next_minor_version is not None
271+
else latest_version
272+
)
273+
elif self._direct_upgrade_possible(
274+
current_version, target_version, install_base
275+
):
276+
# No minor upgrade needed to target
277+
return target_version
278+
elif next_minor_version is None:
279+
return latest_version
266280
else:
267-
return cast(PanOSVersion, target_version)
281+
return next_minor_version
268282
else:
269283
if target_version == "latest":
270-
return latest_version
271-
elif latest_version < target_version:
272-
return latest_version
284+
if next_minor_version is None:
285+
return latest_version
286+
else:
287+
return self._latest_patch_version(
288+
next_minor_version, available_versions
289+
)
290+
elif self._direct_upgrade_possible(
291+
current_version, target_version, install_base
292+
):
293+
return target_version
273294
else:
274-
return cast(PanOSVersion, target_version)
295+
# More than one minor upgrade needed to target
296+
return self._latest_patch_version(
297+
next_minor_version, available_versions
298+
)
275299

276300
def _current_version_is_target(
277-
self, target_version: Union[PanOSVersion, str]
301+
self, target_version: Union[PanOSVersion, Literal["latest"]]
278302
) -> bool:
279-
target_version = PanOSVersion(str(target_version))
280303
current_version = PanOSVersion(self.pandevice.version)
281304
available_versions = list(map(PanOSVersion, self.versions.keys()))
282305
latest_version = max(available_versions)
283-
if current_version == target_version:
306+
if target_version == "latest" and current_version == latest_version:
284307
return True
285-
elif target_version == "latest" and current_version == latest_version:
308+
elif current_version == target_version:
286309
return True
287310
else:
288311
return False
@@ -370,14 +393,21 @@ def upgrade_to_version(
370393
not install_base
371394
and not self.versions[str(next_version.baseimage)]["downloaded"]
372395
):
373-
self.download(next_version.baseimage, sync=True)
396+
if dryrun:
397+
self._logger.info(
398+
"Device %s will download base image: %s"
399+
% (self.pandevice.id, next_version.baseimage)
400+
)
401+
else:
402+
self.download(next_version.baseimage, sync=True)
374403

375404
# Ensure the content pack is upgraded to the latest
376-
self.pandevice.content.download_and_install_latest(sync=True)
405+
if not dryrun:
406+
self.pandevice.content.download_and_install_latest(sync=True)
377407

378408
# Upgrade to the next version
379409
self._logger.info(
380-
"Device %s will be upgraded to version: %s"
410+
"Device %s will download and upgrade to version: %s"
381411
% (self.pandevice.id, next_version)
382412
)
383413
if dryrun:
@@ -409,7 +439,7 @@ def _next_minor_version(self, version: Union[PanOSVersion, str]) -> PanOSVersion
409439
# Account for 10.2.x (only release with minor version of '2')
410440
if version.major == 10 and version.minor == 1:
411441
next_version = PanOSVersion("10.2.0")
412-
elif version.minor == 1:
442+
elif version.minor > 0:
413443
next_version = PanOSVersion(str(version.major + 1) + ".0.0")
414444
# There is no PAN-OS 5.1 for firewalls, so next minor release from 5.0.x is 6.0.0.
415445
elif (
@@ -433,7 +463,22 @@ def _next_patch_version(self, version):
433463
)
434464
return next_version
435465

436-
def _direct_upgrade_possible(self, current_version, target_version):
466+
def _latest_patch_version(
467+
self, version: Union[str, PanOSVersion], available_versions: List[PanOSVersion]
468+
):
469+
if isstring(version):
470+
version = PanOSVersion(version)
471+
found_patch = False
472+
latest_patch: PanOSVersion = PanOSVersion("0.0.0")
473+
for v in available_versions:
474+
if v.major == version.major and v.minor == version.minor:
475+
latest_patch = max(latest_patch, v)
476+
found_patch = True
477+
return latest_patch if found_patch else None
478+
479+
def _direct_upgrade_possible(
480+
self, current_version, target_version, install_base=True
481+
):
437482
"""Check if current version can directly upgrade to target version
438483
439484
:returns True if a direct upgrade is possible, False if not
@@ -458,7 +503,7 @@ def _direct_upgrade_possible(self, current_version, target_version):
458503
current_version.major == target_version.major
459504
and current_version.minor == 0
460505
and target_version.minor == 1
461-
and target_version.patch == 0
506+
and (not install_base or target_version.patch == 0)
462507
):
463508
return True
464509

@@ -468,10 +513,12 @@ def _direct_upgrade_possible(self, current_version, target_version):
468513
current_version.major + 1 == target_version.major
469514
and current_version.minor == 1
470515
and target_version.minor == 0
471-
and target_version.patch == 0
516+
and (not install_base or target_version.patch == 0)
472517
):
473518
return True
474519

520+
# SPECIAL CASES
521+
475522
# Upgrading a firewall from PAN-OS 5.0.x to 6.0.x
476523
# This is a special case because there is no PAN-OS 5.1.x
477524
from panos.firewall import Firewall
@@ -484,6 +531,17 @@ def _direct_upgrade_possible(self, current_version, target_version):
484531
):
485532
return True
486533

534+
# Upgrade from PAN-OS 10.1.x to 10.2.x
535+
# This is a special case because only minor release with a 2
536+
if (
537+
current_version.major == 10
538+
and current_version.minor == 1
539+
and target_version.major == 10
540+
and target_version.minor == 2
541+
and (not install_base or target_version.patch == 0)
542+
):
543+
return True
544+
487545
return False
488546

489547

0 commit comments

Comments
 (0)