@@ -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