@@ -141,11 +141,10 @@ def vsys(self):
141141 if self .parent is not None :
142142 vsys = self .parent .vsys
143143 if vsys is None and self .ROOT == Root .VSYS :
144- return 'vsys1'
144+ return getattr ( self . parent , 'DEFAULT_VSYS' , None )
145145 else :
146146 return vsys
147147
148-
149148 @vsys .setter
150149 def vsys (self , value ):
151150 raise err .PanDeviceError ("Cannot set vsys on non-vsys object" )
@@ -270,60 +269,92 @@ def removeall(self, cls=None):
270269 self .children = []
271270 return children
272271
273- def xpath (self ):
272+ def xpath (self , root = None ):
274273 """Return the full xpath for this object
275274
276275 Xpath in the form: parent's xpath + this object's xpath + entry or member if applicable.
277276
277+ Args:
278+ root: The root to use for this object (default: this object's root)
279+
278280 Returns:
279281 str: The full xpath to this object
280282
281283 """
282- xpath = self ._parent_xpath () + self .XPATH
283- if self .SUFFIX is not None :
284- xpath += self .SUFFIX % (self .uid , )
285-
286- return xpath
284+ path = []
285+ p = self
286+ if root is None :
287+ root = self .ROOT
288+ vsys = self .vsys
289+ label = getattr (self , 'VSYS_LABEL' , 'vsys' )
290+ while True :
291+ if isinstance (p , PanDevice ) and p != self :
292+ # Stop on the first pandevice encountered, unless the
293+ # pandevice.PanDevice object is the object whose xpath
294+ # was asked for.
295+ path .insert (0 , p .xpath_root (root , vsys , label ))
296+ break
297+ elif not hasattr (p , 'VSYS_LABEL' ) or p == self :
298+ # Add on the xpath of this object, unless it is a
299+ # device.Vsys, unless the device.Vsys is the object whose
300+ # xpath was asked for.
301+ addon = p .XPATH
302+ if p .SUFFIX is not None :
303+ addon += p .SUFFIX % (p .uid , )
304+ path .insert (0 , addon )
305+ if p .__class__ .__name__ == 'Firewall' and p .parent is not None :
306+ if p .parent .__class__ .__name__ == 'DeviceGroup' :
307+ root = Root .VSYS
308+ p = p .parent
309+ if p is None :
310+ break
311+ if hasattr (p , 'VSYS_LABEL' ):
312+ # Either panorama.DeviceGroup or device.Vsys.
313+ label = p .VSYS_LABEL
314+ vsys = p .vsys
315+ elif p .__class__ .__name__ == 'Template' :
316+ # Hit a template, make sure that the appropriate /config/...
317+ # xpath has been saved.
318+ if not path [0 ].startswith ('/config/' ):
319+ path .insert (0 , self .xpath_root (root , vsys , label ))
320+ vsys = p .vsys
321+ root = p .ROOT
322+
323+ return '' .join (path )
287324
288325 def xpath_nosuffix (self ):
289326 """Return the xpath without the suffix
290327
328+ This is used by refreshall().
329+
291330 Returns:
292331 str: The xpath without entry or member on the end
293332
294333 """
295- xpath = self ._parent_xpath () + self .XPATH
296- return xpath
334+ if self .SUFFIX is None :
335+ return self .xpath ()
336+ else :
337+ return self .xpath_short ()
297338
298- def xpath_short (self ):
339+ def xpath_short (self , root = None ):
299340 """Return an xpath for this object without the final segment
300341
301342 Xpath in the form: parent's xpath + this object's xpath. Used for set API calls.
302343
344+ Args:
345+ root: The root to use for this object (default: this object's root)
346+
303347 Returns:
304348 str: The xpath without the final segment
305349
306350 """
307- xpath = self ._parent_xpath () + self .XPATH
308- if self .SUFFIX is None :
309- # Remove last segment of xpath
310- xpath = re .sub (r"/(?=[^/']*'[^']*'[^/']*$|[^/]*$).*$" , "" , xpath )
351+ xpath = self .xpath (root )
352+ xpath = re .sub (r"/(?=[^/']*'[^']*'[^/']*$|[^/]*$).*$" , "" , xpath )
311353 return xpath
312354
313- def _parent_xpath (self ):
314- if self .parent is None :
315- return ""
316- else :
317- return self .parent ._build_xpath (self .ROOT , None )
318-
319- def _build_xpath (self , root , vsys ):
320- if self .parent is None :
321- # self with no parent
322- return ""
323- parent_xpath = self .parent ._build_xpath (root , vsys ) + self .XPATH
324- if self .SUFFIX is not None :
325- parent_xpath += self .SUFFIX % (self .uid , )
326- return parent_xpath
355+ def xpath_root (self , root_type , vsys , label = 'vsys' ):
356+ if self .parent :
357+ return self .parent .xpath_root (root_type , vsys , label )
327358
328359 def xpath_vsys (self ):
329360 if self .parent is not None :
@@ -333,12 +364,13 @@ def xpath_panorama(self):
333364 if self .parent is not None :
334365 return self .parent .xpath_panorama ()
335366
336- def _root_xpath_vsys (self , vsys ):
367+ def _root_xpath_vsys (self , vsys , label = 'vsys' ):
337368 if vsys == 'shared' :
338- return '/config/shared'
369+ xpath = '/config/shared'
370+ else :
371+ xpath = "/config/devices/entry[@name='localhost.localdomain']"
372+ xpath += "/{0}/entry[@name='{1}']" .format (label , vsys or 'vsys1' )
339373
340- xpath = "/config/devices/entry[@name='localhost.localdomain']"
341- xpath += "/vsys/entry[@name='{0}']" .format (vsys or 'vsys1' )
342374 return xpath
343375
344376 def element (self , with_children = True , comparable = False ):
@@ -1008,27 +1040,6 @@ def find_index(self, name=None, class_type=None):
10081040 and type (child ) == class_type ):
10091041 return num
10101042
1011- @classmethod
1012- def applyall (cls , parent ):
1013- device = parent .nearest_pandevice ()
1014- logger .debug (device .id + ": applyall called on %s type" % cls )
1015- objects = parent .findall (cls )
1016- if not objects :
1017- return
1018- # Create the xpath
1019- xpath = objects [0 ].xpath_nosuffix ()
1020- # Create the root element
1021- lasttag = cls .XPATH .rsplit ("/" , 1 )[- 1 ]
1022- element = ET .Element (lasttag )
1023- # Build the full element from the objects
1024- for obj in objects :
1025- device .xml_combine (element , [obj .element (), ])
1026- # Apply the element to the xpath
1027- device .set_config_changed ()
1028- device .xapi .edit (xpath , ET .tostring (element , encoding = 'utf-8' ), retry_on_peer = cls .HA_SYNC )
1029- for obj in objects :
1030- obj ._check_child_methods ("apply" )
1031-
10321043 @classmethod
10331044 def refreshall (cls , parent , running_config = False , add = True ,
10341045 exceptions = False , name_only = False ):
@@ -1075,11 +1086,9 @@ def refreshall(cls, parent, running_config=False, add=True,
10751086 device = class_instance .nearest_pandevice ()
10761087 logger .debug (device .id + ": refreshall called on %s type" % cls )
10771088
1078- parent_xpath = class_instance ._parent_xpath ()
1079-
10801089 # Set api_action and xpath
10811090 api_action = device .xapi .show if running_config else device .xapi .get
1082- xpath = parent_xpath + class_instance .XPATH
1091+ xpath = class_instance .xpath_nosuffix ()
10831092 if name_only :
10841093 xpath = xpath + "/entry/@name"
10851094
@@ -1471,8 +1480,6 @@ def _gather_bulk_info(self, func=None):
14711480 logger .debug ('{0}: {1} called on {2} object "{3}"' .format (
14721481 dev .id , func , self , self .uid ))
14731482 dev .set_config_changed ()
1474- if self .HA_SYNC :
1475- dev = dev .active ()
14761483
14771484 # Determine base xpath to match against.
14781485 xpath = self .xpath_short ()
@@ -1608,7 +1615,7 @@ def delete_similar(self):
16081615 self ._perform_vsys_dict_import_delete (dev , vsys_dict )
16091616
16101617 # Now perform the bulk delete.
1611- xpath = self ._parent_xpath () + self . XPATH
1618+ xpath = self .xpath_nosuffix ()
16121619 if self .SUFFIX == ENTRY :
16131620 entries = ' or ' .join (
16141621 "@name='{0}'" .format (x .uid ) for x in instances )
@@ -1864,6 +1871,9 @@ class VersionedPanObject(PanObject):
18641871 """
18651872 _UNKNOWN_PANOS_VERSION = (sys .maxsize , 0 , 0 )
18661873 _DEFAULT_NAME = None
1874+ _TEMPLATE_DEVICE_XPATH = "/config/devices/entry[@name='localhost.localdomain']"
1875+ _TEMPLATE_VSYS_XPATH = _TEMPLATE_DEVICE_XPATH + "/vsys/entry[@name='{vsys}']"
1876+ _TEMPLATE_MGTCONFIG_XPATH = "/config/mgt-config"
18671877
18681878 def __init__ (self , * args , ** kwargs ):
18691879 if self .NAME is not None :
@@ -2234,7 +2244,8 @@ def __setattr__(self, name, value):
22342244 def XPATH (self ):
22352245 """Returns the version specific xpath of this object."""
22362246 panos_version = self .retrieve_panos_version ()
2237- return self ._xpaths ._get_versioned_value (panos_version , self .parent )
2247+ val = self ._xpaths ._get_versioned_value (panos_version , self .parent )
2248+ return val .format (vsys = self .vsys or 'vsys1' )
22382249
22392250
22402251class VersionedParamPath (VersioningSupport ):
@@ -2479,6 +2490,8 @@ def element(self, elm, settings, comparable=False):
24792490 if token .startswith ('entry ' ):
24802491 junk , var_to_use = token .split ()
24812492 child = ET .Element ('entry' , {'name' : settings [var_to_use ]})
2493+ elif token == "entry[@name='localhost.localdomain']" :
2494+ child = ET .Element ('entry' , {'name' : 'localhost.localdomain' })
24822495 else :
24832496 child = ET .Element (token .format (** settings ))
24842497 if child .tag == 'None' :
@@ -2670,14 +2683,14 @@ class VsysOperations(VersionedPanObject):
26702683 ALWAYS_IMPORT = False
26712684
26722685 def __init__ (self , * args , ** kwargs ):
2673- self ._xpath_imports = VersioningSupport ()
2686+ self ._xpath_imports = ParentAwareXpath ()
26742687 super (VsysOperations , self ).__init__ (* args , ** kwargs )
26752688
26762689 @property
26772690 def XPATH_IMPORT (self ):
26782691 """Returns the version specific xpath import for this object."""
26792692 panos_version = self .retrieve_panos_version ()
2680- return self ._xpath_imports ._get_versioned_value (panos_version )
2693+ return self ._xpath_imports ._get_versioned_value (panos_version , self . parent )
26812694
26822695 def create (self ):
26832696 super (VsysOperations , self ).create ()
@@ -2729,11 +2742,17 @@ def create_import(self, vsys=None):
27292742 device .active ().xapi .set (xpath , element , retry_on_peer = True )
27302743
27312744 def xpath_import_base (self , vsys = None ):
2732- if vsys :
2733- vsys_xpath = self ._root_xpath_vsys (vsys )
2734- else :
2735- vsys_xpath = self .xpath_vsys ()
2736- return "{0}/import{1}" .format (vsys_xpath , self .XPATH_IMPORT )
2745+ template = ''
2746+ p = self
2747+ while p is not None :
2748+ if p .__class__ .__name__ == 'Template' :
2749+ template = p .xpath ()
2750+ break
2751+ p = p .parent
2752+
2753+ vsys_xpath = self ._root_xpath_vsys (vsys or self .vsys or 'vsys1' )
2754+ return "{0}{1}/import{2}" .format (
2755+ template , vsys_xpath , self .XPATH_IMPORT )
27372756
27382757 def delete_import (self , vsys = None ):
27392758 """Delete a vsys import for the object
@@ -2804,8 +2823,7 @@ def refreshall(cls, parent, running_config=False, add=True,
28042823 api_action = device .xapi .show if running_config else device .xapi .get
28052824 if parent .vsys != "shared" and parent .vsys is not None and class_instance .XPATH_IMPORT is not None :
28062825 imports = []
2807- xpath = '{0}/import{1}' .format (
2808- parent .xpath_vsys (), class_instance .XPATH_IMPORT )
2826+ xpath = class_instance .xpath_import_base ()
28092827 try :
28102828 imports_xml = api_action (xpath , retry_on_peer = True )
28112829 except (err .PanNoSuchNode , pan .xapi .PanXapiError ) as e :
@@ -3283,11 +3301,11 @@ def set_config_changed(self, scope=None):
32833301 def _build_xpath (self , root , vsys ):
32843302 return self .xpath_root (root , vsys or self .vsys )
32853303
3286- def xpath_root (self , root_type , vsys ):
3304+ def xpath_root (self , root_type , vsys , label = 'vsys' ):
32873305 if root_type == Root .DEVICE :
32883306 xpath = self .xpath_device ()
32893307 elif root_type == Root .VSYS :
3290- xpath = self ._root_xpath_vsys (vsys )
3308+ xpath = self ._root_xpath_vsys (vsys , label )
32913309 elif root_type == Root .MGTCONFIG :
32923310 xpath = self .xpath_mgtconfig ()
32933311 elif root_type == Root .PANORAMA :
0 commit comments