Skip to content

Commit 0f83a7e

Browse files
committed
Merge branch 'release/0.5.2'
2 parents 4fac592 + 2c1acdb commit 0f83a7e

File tree

8 files changed

+124
-45
lines changed

8 files changed

+124
-45
lines changed

HISTORY.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@
33
History
44
=======
55

6+
0.5.2
7+
-----
8+
9+
Released: 2017-11-30
10+
11+
Status: Alpha
12+
13+
- Adding DHCP management interface options to `device.SystemSettings`
14+
- Various bug fixes
15+
16+
617
0.5.1
718
-----
819

pandevice/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
__author__ = 'Palo Alto Networks'
2525
__email__ = '[email protected]'
26-
__version__ = '0.5.1'
26+
__version__ = '0.5.2'
2727

2828

2929
import logging

pandevice/base.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,6 +1493,8 @@ def _gather_bulk_info(self, func=None):
14931493
if node._requires_import_consideration():
14941494
vsys = node.vsys
14951495
if vsys is None and node.ALWAYS_IMPORT:
1496+
if getattr(node, 'mode', None) in ('ha', 'aggregate-group'):
1497+
continue
14961498
vsys = 'vsys1'
14971499
vsys_dict.setdefault(vsys, {})
14981500
vsys_dict[vsys].setdefault(node.xpath_import_base(), [])
@@ -1623,7 +1625,9 @@ def delete_similar(self):
16231625

16241626
def _perform_vsys_dict_import_set(self, dev, vsys_dict):
16251627
"""Iterates of a vsys_dict, doing imports for all instances."""
1626-
for vsys_spec in vsys_dict.values():
1628+
for vsys, vsys_spec in vsys_dict.items():
1629+
if vsys is None:
1630+
continue
16271631
for xpath_import_base, objs in vsys_spec.items():
16281632
xpath_tokens = xpath_import_base.split('/')
16291633
new_root = xpath_tokens.pop()
@@ -3736,7 +3740,7 @@ def map_ha(self, method_name, *args, **kwargs):
37363740
return result1, result2
37373741

37383742
def show_highavailability_state(self):
3739-
ha_state = self.active().op("show high-availability state")
3743+
ha_state = self.op("show high-availability state")
37403744
enabled = ha_state.findtext("result/enabled")
37413745
if enabled is None or enabled == "no":
37423746
return "disabled", None
@@ -3756,17 +3760,15 @@ def refresh_ha_active(self):
37563760
self_state = self.show_highavailability_state()[0]
37573761
peer_state = self.ha_peer.show_highavailability_state()[0]
37583762
states = (self_state, peer_state)
3759-
if "disabled" not in states:
3760-
if "initial" in states:
3761-
logger.debug("HA is initializing on one or both devices, try again soon")
3762-
return "initial"
3763-
elif self_state == "active":
3764-
logger.debug("Current firewall is active, no change made")
3765-
return "active"
3766-
else:
3767-
logger.debug("Current firewall state is %s, switching to use other firewall" % self_state)
3768-
self.toggle_ha_active()
3769-
return self_state
3763+
if 'disabled' in states:
3764+
return
3765+
elif 'initial' in states:
3766+
logger.debug("HA is initializing on one or both devices, try again soon")
3767+
return 'initial'
3768+
else:
3769+
for fw, state in ((self, self_state), (self.ha_peer, peer_state)):
3770+
fw._ha_active = state == 'active'
3771+
return self_state
37703772

37713773
def synchronize_config(self):
37723774
"""Force configuration synchronization from this device to its HA peer"""

pandevice/device.py

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -197,11 +197,14 @@ class NTPServerSecondary(NTPServer):
197197
XPATH = "/ntp-servers/secondary-ntp-server"
198198

199199

200-
class SystemSettings(PanObject):
200+
class SystemSettings(VersionedPanObject):
201201
"""Firewall or Panorama device system settings
202202
203203
Add only one of these to a parent object.
204204
205+
If you want to configure DHCP on the management interface, you should
206+
specify settings for `dhcp_send_hostname` and `dhcp_send_client_id`.
207+
205208
Args:
206209
hostname (str): The hostname of the device
207210
domain (str): The domain of the device
@@ -217,35 +220,73 @@ class SystemSettings(PanObject):
217220
panorama2 (str): IP address of secondary Panorama
218221
login_banner (str): Login banner text
219222
update_server (str): IP or hostname of the update server
223+
verify_update_server (bool): Verify the update server identity
224+
dhcp_send_hostname (bool): (DHCP Mngt) Send Hostname
225+
dhcp_send_client_id (bool): (DHCP Mngt) Send Client ID
226+
accept_dhcp_hostname (bool): (DHCP Mngt) Accept DHCP hostname
227+
accept_dhcp_domain (bool): (DHCP Mngt) Accept DHCP domain name
220228
221229
"""
222230
NAME = None
223231
ROOT = Root.DEVICE
224-
XPATH = "/deviceconfig/system"
225232
HA_SYNC = False
226233
CHILDTYPES = (
227234
"device.NTPServerPrimary",
228235
"device.NTPServerSecondary",
229236
)
230237

231-
@classmethod
232-
def variables(cls):
233-
return (
234-
Var("hostname"),
235-
Var("domain"),
236-
Var("ip-address"),
237-
Var("netmask"),
238-
Var("default-gateway"),
239-
Var("ipv6-address"),
240-
Var("ipv6-default-gateway"),
241-
Var("dns-setting/servers/primary", "dns_primary"),
242-
Var("dns-setting/servers/secondary", "dns_secondary"),
243-
Var("timezone"),
244-
Var("panorama-server", "panorama"),
245-
Var("panorama-server-2", "panorama2"),
246-
Var("login-banner"),
247-
Var("update-server"),
248-
)
238+
def _setup(self):
239+
# xpaths
240+
self._xpaths.add_profile(value='/deviceconfig/system')
241+
242+
# params
243+
params = []
244+
245+
params.append(VersionedParamPath(
246+
'hostname', path='hostname'))
247+
params.append(VersionedParamPath(
248+
'domain', path='domain'))
249+
params.append(VersionedParamPath(
250+
'ip_address', path='ip-address'))
251+
params.append(VersionedParamPath(
252+
'netmask', path='netmask'))
253+
params.append(VersionedParamPath(
254+
'default_gateway', path='default-gateway'))
255+
params.append(VersionedParamPath(
256+
'ipv6_address', path='ipv6-address'))
257+
params.append(VersionedParamPath(
258+
'ipv6_default_gateway', path='ipv6-default-gateway'))
259+
params.append(VersionedParamPath(
260+
'dns_primary', path='dns-setting/servers/primary'))
261+
params.append(VersionedParamPath(
262+
'dns_secondary', path='dns-setting/servers/secondary'))
263+
params.append(VersionedParamPath(
264+
'timezone', path='timezone'))
265+
params.append(VersionedParamPath(
266+
'panorama', path='panorama-server'))
267+
params.append(VersionedParamPath(
268+
'panorama2', path='panorama-server-2'))
269+
params.append(VersionedParamPath(
270+
'login_banner', path='login-banner'))
271+
params.append(VersionedParamPath(
272+
'update_server', path='update-server'))
273+
params.append(VersionedParamPath(
274+
'verify_update_server', vartype='yesno',
275+
path='server-verification'))
276+
params.append(VersionedParamPath(
277+
'dhcp_send_hostname', vartype='yesno',
278+
path='type/dhcp-client/send-hostname'))
279+
params.append(VersionedParamPath(
280+
'dhcp_send_client_id', vartype='yesno',
281+
path='type/dhcp-client/send-client-id'))
282+
params.append(VersionedParamPath(
283+
'accept_dhcp_hostname', vartype='yesno',
284+
path='type/dhcp-client/accept-dhcp-hostname'))
285+
params.append(VersionedParamPath(
286+
'accept_dhcp_domain', vartype='yesno',
287+
path='type/dhcp-client/accept-dhcp-domain'))
288+
289+
self._params = tuple(params)
249290

250291

251292
class PasswordProfile(VersionedPanObject):

pandevice/network.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,7 @@ class Subinterface(Interface):
553553
554554
"""
555555
_BASE_INTERFACE_NAME = 'entry BASE_INTERFACE_NAME'
556+
_BASE_INTERFACE_TYPE = 'var BASE_INTERFACE_TYPE'
556557

557558
def set_name(self):
558559
"""Create a name appropriate for a subinterface if it isn't already"""
@@ -563,6 +564,13 @@ def set_name(self):
563564
def XPATH(self):
564565
path = super(Subinterface, self).XPATH
565566

567+
if self._BASE_INTERFACE_TYPE in path:
568+
if self.uid.startswith('ae'):
569+
rep = 'aggregate-ethernet'
570+
else:
571+
rep = 'ethernet'
572+
path = path.replace(self._BASE_INTERFACE_TYPE, rep)
573+
566574
if self._BASE_INTERFACE_NAME in path:
567575
base = self.uid.split('.')[0]
568576
path = path.replace(self._BASE_INTERFACE_NAME,
@@ -710,8 +718,8 @@ def _setup(self):
710718
# xpaths for parents: firewall.Firewall, device.Vsys
711719
self._xpaths.add_profile(
712720
parents=('Firewall', 'Vsys'),
713-
value=('/network/interface/ethernet/{0}/layer3/units'.format(
714-
self._BASE_INTERFACE_NAME)))
721+
value=('/network/interface/{0}/{1}/layer3/units'.format(
722+
self._BASE_INTERFACE_TYPE, self._BASE_INTERFACE_NAME)))
715723

716724
# xpath imports
717725
self._xpath_imports.add_profile(value='/network/interface')
@@ -785,8 +793,8 @@ def _setup(self):
785793
# xpaths for parents: firewall.Firewall, device.Vsys
786794
self._xpaths.add_profile(
787795
parents=('Firewall', 'Vsys'),
788-
value=('/network/interface/ethernet/{0}/layer2/units'.format(
789-
self._BASE_INTERFACE_NAME)))
796+
value=('/network/interface/{0}/{1}/layer2/units'.format(
797+
self._BASE_INTERFACE_TYPE, self._BASE_INTERFACE_NAME)))
790798

791799
# xpath imports
792800
self._xpath_imports.add_profile(value='/network/interface')

pandevice/panorama.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,8 @@ def refresh_devices(self, devices=(), only_connected=False, expand_vsys=True, in
333333
# of the device groups
334334
if devicegroup_configxml is not None:
335335
for dg_entry in devicegroup_configxml:
336+
if dg_entry.find('devices') is None:
337+
continue
336338
for fw_entry in dg_entry.find('devices'):
337339
fw_entry_op = devicegroup_opxml.find("entry/devices/entry[@name='%s']" % fw_entry.get("name"))
338340
if fw_entry_op is not None:

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
setup(
2525
name='pandevice',
26-
version='0.5.1',
26+
version='0.5.2',
2727
description='Framework for interacting with Palo Alto Networks devices via API',
2828
long_description='The Palo Alto Networks Device Framework is a way to interact with Palo Alto Networks devices (including Next-generation Firewalls and Panorama) using the device API that is object oriented and conceptually similar to interaction with the device via the GUI or CLI.',
2929
author='Palo Alto Networks',

tests/test_versioning.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
import pandevice.network
1010
import pandevice.objects
1111
import pandevice.policies
12+
import pandevice.device
1213

1314

1415
class TestObject(unittest.TestCase):
1516
OLD_CLS = None
1617
NEW_CLS = None
18+
# This is a tuple of two-element tuples.
1719
PARAMS = ()
1820

1921
def setUp(self):
@@ -23,8 +25,13 @@ def setUp(self):
2325
raise unittest.SkipTest('NEW_CLS not defined')
2426

2527
def test_empty_objects_are_equal(self):
26-
old = self.OLD_CLS('foo')
27-
new = self.NEW_CLS('foo')
28+
if self.OLD_CLS.SUFFIX is not None:
29+
old = self.OLD_CLS('foo')
30+
new = self.NEW_CLS('foo')
31+
else:
32+
old = self.OLD_CLS()
33+
new = self.NEW_CLS()
34+
2835
new.retrieve_panos_version = mock.Mock(return_value=(7, 0, 0))
2936

3037
if not hasattr(old, 'element_str'):
@@ -37,9 +44,13 @@ def test_empty_objects_are_equal(self):
3744

3845
def test_positionally_populated_objects_are_equal(self):
3946
args = tuple(y for x, y in self.PARAMS)
40-
new = self.NEW_CLS('jack', *args)
47+
if self.OLD_CLS.SUFFIX is not None:
48+
new = self.NEW_CLS('jack', *args)
49+
old = self.OLD_CLS('jack', *args)
50+
else:
51+
new = self.NEW_CLS(*args)
52+
old = self.OLD_CLS(*args)
4153
new.retrieve_panos_version = mock.Mock(return_value=(7, 0, 0))
42-
old = self.OLD_CLS('jack', *args)
4354

4455
if not hasattr(old, 'element_str'):
4556
raise unittest.SkipTest('OLD_CLS does not have element_str()')
@@ -51,8 +62,12 @@ def test_positionally_populated_objects_are_equal(self):
5162

5263
def test_keyword_populated_objects_are_equal(self):
5364
kwargs = dict(self.PARAMS)
54-
old = self.OLD_CLS('burton', **kwargs)
55-
new = self.NEW_CLS('burton', **kwargs)
65+
if self.OLD_CLS.SUFFIX is not None:
66+
old = self.OLD_CLS('burton', **kwargs)
67+
new = self.NEW_CLS('burton', **kwargs)
68+
else:
69+
old = self.OLD_CLS(**kwargs)
70+
new = self.NEW_CLS(**kwargs)
5671
new.retrieve_panos_version = mock.Mock(return_value=(7, 0, 0))
5772

5873
if not hasattr(old, 'element_str'):

0 commit comments

Comments
 (0)