Skip to content

Commit 471082e

Browse files
shinmogbtorresgil
authored andcommitted
Fixes #72 - rearchitect of bulk operations to be called on object, not parent of object (#81)
1 parent abf1511 commit 471082e

File tree

5 files changed

+409
-130
lines changed

5 files changed

+409
-130
lines changed

examples/bulk_address_objects.py

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -91,24 +91,15 @@ def main():
9191
fw.add(ao)
9292

9393
# Now we can bulk create all the address objects. This is accomplished by
94-
# invoking `create_type()` and giving it the AddressObject type. What
95-
# would have been 600 individual API calls is now condensed into a single
96-
# API call, taking much less time to complete.
97-
#
98-
# Although we would typically invoke `create()` on the object we want to
99-
# create, the bulk operations are all invoked from the parent object, not
100-
# on any given child object. In this case, the parent is the firewall
101-
# object, so we need to invoke `create_type()` from the firewall object.
102-
#
103-
# NOTE: All child type searches the bulk operations perform are
104-
# non-recursive, so only the AddressObjects directly attached to
105-
# the `fw` object will be found.
94+
# invoking `create_similar()` on any of the address objects in our tree,
95+
# turning what would have been 600 individual API calls and condensing it
96+
# into a single API call.
10697
start = datetime.datetime.now()
107-
fw.create_type(pandevice.objects.AddressObject)
98+
bulk_objects[0].create_similar()
10899
print('Creating {0} address objects took: {1}'.format(
109100
len(bulk_objects), datetime.datetime.now() - start))
110101

111-
# We've done a create, now let's look at bulk apply.
102+
# We've done a bulk create, now let's look at bulk apply.
112103
#
113104
# Some care is needed when using apply with pandevice. All "apply" methods
114105
# are doing a PANOS API `type=edit` under the hood, which does a replace of
@@ -128,13 +119,13 @@ def main():
128119
for num, x in enumerate(bulk_objects, 1):
129120
x.value = num_as_ip(num, 10)
130121

131-
# Now we can do our bulk apply, invoking `apply_type()`. As before,
132-
# we pass in the child type we want to invoke the bulk operation for,
133-
# which is AddressObject. And since the firewall has all the pre-existing
134-
# address objects in its tree, we won't accidentally truncate them from
135-
# the firewall config.
122+
# Now we can do our bulk apply, invoking `apply_similar()`. As before,
123+
# we invoke this on any of the related children in our pandevice
124+
# object tree. Most important of all, since our firewall object has all
125+
# the pre-existing address objects in its tree, we won't accidentally
126+
# truncate them from the firewall config.
136127
start = datetime.datetime.now()
137-
fw.apply_type(pandevice.objects.AddressObject)
128+
bulk_objects[0].apply_similar()
138129
print('Bulk apply {0} address objects took: {1}'.format(
139130
len(bulk_objects) + len(original_objects),
140131
datetime.datetime.now() - start))
@@ -146,10 +137,11 @@ def main():
146137
for x in original_objects:
147138
fw.remove(x)
148139

149-
# Finally, let's invoke `delete_type()` from the firewall. As should be
150-
# expected, we pass in AddressObject as the child type.
140+
# Finally, let's invoke `delete_similar()` from the firewall. As should be
141+
# expected, we invoke this from any of the objects currently in our
142+
# pandevice object tree.
151143
start = datetime.datetime.now()
152-
fw.delete_type(pandevice.objects.AddressObject)
144+
bulk_objects[0].delete_similar()
153145
print('Deleting {0} address objects took: {1}'.format(
154146
len(bulk_objects), datetime.datetime.now() - start))
155147

examples/bulk_subinterfaces.py

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright (c) 2017, Palo Alto Networks
4+
#
5+
# Permission to use, copy, modify, and/or distribute this software for any
6+
# purpose with or without fee is hereby granted, provided that the above
7+
# copyright notice and this permission notice appear in all copies.
8+
#
9+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16+
17+
"""
18+
bulk_subinterfaces.py
19+
=====================
20+
21+
Use bulk operations to create / delete hundreds of firewall interfaces.
22+
23+
NOTE: Please update the hostname and auth credentials variables
24+
before running.
25+
26+
The purpose of this script is to use and explain both the bulk operations
27+
as it relates to subinterfaces as well as the new function that organizes
28+
objects into vsys. This script will show how the new bulk operations
29+
correctly handle when subinterface objects are in separate vsys trees.
30+
31+
"""
32+
33+
import datetime
34+
import random
35+
import sys
36+
37+
from pandevice import device
38+
from pandevice import firewall
39+
from pandevice import network
40+
41+
42+
HOSTNAME = '127.0.0.1'
43+
USERNAME = 'admin'
44+
PASSWORD = 'admin'
45+
INTERFACE = 'ethernet1/5'
46+
47+
48+
def main():
49+
# Before we begin, you'll need to use the pandevice documentation both
50+
# for this example and for any scripts you may write for yourself. The
51+
# docs can be found here:
52+
#
53+
# http://pandevice.readthedocs.io/en/latest/reference.html
54+
#
55+
# First, let's create the firewall object that we want to modify.
56+
fw = firewall.Firewall(HOSTNAME, USERNAME, PASSWORD)
57+
print('Firewall system info: {0}'.format(fw.refresh_system_info()))
58+
59+
print('Desired interface: {0}'.format(INTERFACE))
60+
61+
# Sanity Check #1: the intent here is that the interface we
62+
# specified above should not already be in use. If the interface is
63+
# already in use, then just quit out.
64+
print('Making sure interface is not currently in use...')
65+
interfaces = network.EthernetInterface.refreshall(fw, add=False)
66+
for eth in interfaces:
67+
if eth.name == INTERFACE:
68+
print('Interface {0} already in use! Please choose another'.format(
69+
INTERFACE))
70+
return
71+
72+
# Sanity Check #2: this has to be a multi-vsys system. So let's make
73+
# sure that we have multiple vsys to work with. If there is only one
74+
# vsys, quit out.
75+
#
76+
# Pulling the entire vsys config from each vsys is going to be large amount
77+
# of XML, so we'll specify that we only need the names of the different
78+
# vsys, not their entire subtrees.
79+
vsys_list = device.Vsys.refreshall(fw, name_only=True)
80+
print('Found the following vsys: {0}'.format(vsys_list))
81+
if len(vsys_list) < 2:
82+
print('Only {0} vsys present, need 2 or more.'.format(len(vsys_list)))
83+
return
84+
85+
# Let's make our base interface that we're going to make subinterfaces
86+
# out of.
87+
print('Creating base interface {0} in layer2 mode'.format(INTERFACE))
88+
base = network.EthernetInterface(INTERFACE, 'layer2')
89+
90+
# Like normal, after creating the object, we need to add it to the
91+
# firewall, then finally invoke "create()" to create it.
92+
fw.add(base)
93+
base.create()
94+
95+
# Now let's go ahead and make all of our subinterfaces.
96+
eth = None
97+
for tag in range(1, 601):
98+
name = '{0}.{1}'.format(INTERFACE, tag)
99+
eth = network.Layer2Subinterface(name, tag)
100+
# Choose one of the vsys at random to put it into.
101+
vsys = random.choice(vsys_list)
102+
# Now add the subinterface to that randomly chosen vsys.
103+
vsys.add(eth)
104+
105+
# You'll notice that we didn't invoke "create()" on the subinterfaces like
106+
# you would expect. This is because we're going to use the bulk create
107+
# function to create all of the subinterfaces in one shot, which has huge
108+
# performance gains from doing "create()" on each subinterface one-by-one.
109+
#
110+
# The function we'll use is "create_similar()". Create similar is saying,
111+
# "I want to create all objects similar to this one in my entire pandevice
112+
# object tree." In this case, since we'd be invoking it on a subinterface
113+
# of INTERFACE (our variable above), we are asking pandevice to create all
114+
# subinterfaces of INTERFACE, no matter which vsys it exists in.
115+
#
116+
# We just need any subinterface to do this. Since our last subinterface
117+
# was saved to the "eth" variable in the above loop, we can just use that
118+
# to invoke "create_similar()".
119+
print('Creating subinterfaces...')
120+
start = datetime.datetime.now()
121+
eth.create_similar()
122+
print('Creating subinterfaces took: {0}'.format(
123+
datetime.datetime.now() - start))
124+
125+
# Now let's explore updating them. Let's say this is a completely
126+
# different script, and we want to update all of the subinterfaces
127+
# for INTERFACE. Since this is a completely new script, we don't have any
128+
# information other than the firewall and the interface INTERFACE. So
129+
# let's start from scratch at this point, and remake the firewall object
130+
# and connect.
131+
print('\n--------\n')
132+
fw = firewall.Firewall(HOSTNAME, USERNAME, PASSWORD)
133+
print('Firewall system info: {0}'.format(fw.refresh_system_info()))
134+
135+
print('Desired interface: {0}'.format(INTERFACE))
136+
137+
# Make the base interface object and connect it to our pandevice tree.
138+
base = network.EthernetInterface(INTERFACE, 'layer2')
139+
fw.add(base)
140+
141+
# Now let's get all the subinterfaces for INTERFACE. Since our firewall's
142+
# default vsys is "None", this will get all subinterfaces of INTERFACE,
143+
# regardless of which vsys it exists in.
144+
print('Refreshing subinterfaces...')
145+
subinterfaces = network.Layer2Subinterface.refreshall(base)
146+
print('Found {0} subinterfaces'.format(len(subinterfaces)))
147+
148+
# Now let's go ahead and update all of them.
149+
for eth in subinterfaces:
150+
eth.comment = 'Tagged {0} and in vsys {1}'.format(eth.tag, eth.vsys)
151+
152+
# Now that we have updated all of the subinterfaces, we need to save
153+
# the changes to the firewall. But hold on a second, the vsys for these
154+
# subinterfaces is currently "None". We first need to organize these
155+
# subinterfaces into the vsys they actually exist in before we can
156+
# apply these changes to the firewall.
157+
#
158+
# This is where you can use the function "organize_into_vsys()". This
159+
# takes all objects currently attached to your pandevice object tree
160+
# and organizes them into the vsys they belong to.
161+
#
162+
# We haven't gotten the current vsys yet (this is a new script, remember),
163+
# but the function can take care of that for us. So let's just invoke it
164+
# to organize our pandevice object tree into vsys.
165+
print('Organizing subinterfaces into vsys...')
166+
fw.organize_into_vsys()
167+
168+
# Now we're ready to save our changes. We'll use "apply_similar()",
169+
# and it behaves similarly to "create_similar()" in that you can invoke
170+
# it from any subinterface of INTERFACE and it will apply all of the
171+
# changes to subinterfaces of INTERFACE only.
172+
#
173+
# We just need one subinterface to invoke this function. Again, we'll
174+
# simply use the subinterface currently saved in the "eth" variable
175+
# from our update loop we did just above.
176+
#
177+
# NOTE: As an "apply()" function, apply does a replace of config, not
178+
# a simple update. So you must be careful that all other objects are
179+
# currently attached to your pandevice object tree when using apply
180+
# functions. In our case, we have already refreshed all layer2
181+
# subinterfaces, and we are the only ones working with INTERFACE, so we
182+
# are safe to use this function.
183+
print('Updating all subinterfaces...')
184+
start = datetime.datetime.now()
185+
eth.apply_similar()
186+
print('Updating subinterfaces took: {0}'.format(
187+
datetime.datetime.now() - start))
188+
189+
# Finally, all that's left is to delete all of the subinterfaces. This
190+
# is just like you think: we first need to refresh all of the
191+
# subinterfaces of INTERFACE, organize them into their appropriate vsys,
192+
# then invoke "delete_similar()" to delete everything.
193+
print('Deleting all subinterfaces...')
194+
start = datetime.datetime.now()
195+
eth.delete_similar()
196+
print('Deleting subinterfaces took: {0}'.format(
197+
datetime.datetime.now() - start))
198+
199+
# Lastly, let's just delete the base interface INTERFACE.
200+
print('Deleting base interface...')
201+
base.delete()
202+
203+
# And now we're done! If performance is a bottleneck in your automation,
204+
# or dealing with vsys is troublesome, consider using the vsys organizing
205+
# and/or bulk functions!
206+
print('Done!')
207+
208+
209+
if __name__ == '__main__':
210+
# This script doesn't take command line arguments. If any are passed in,
211+
# then print out the script's docstring and exit.
212+
if len(sys.argv) != 1:
213+
print __doc__
214+
else:
215+
# No CLI args, so run the main function.
216+
main()

0 commit comments

Comments
 (0)