Solving Problems With python-jss: Finding the Printers in the Policies
I wanted to start showing some short examples of easy and quick python scripts that I have used in my day-to-day admin job. Today I got around to working on a broken printer installation. One of our printers had a driver that was not working, and needed to be switched out.
Adding the package to the JSS and creating an update policy was no big deal, but I wanted to know exactly which policies distributed that printer. If you need to change a printer’s PPD file, you have to remove and then re-add a printer; and when you remove a printer from Casper Admin, that printer gets silently dropped from all policies as well. We have a large number of policies, and I don’t like to make mistakes, so I wanted to get this right the first time.
python-jss to the rescue!
I of course already have my python-jss preferences set up (see the Wiki!), so we can start with:
import jss
jss_prefs = jss.JSSPrefs()
j = jss.JSS(jss_prefs)
and we have a functioning JSS object.
Next, we are interested in looking at Policies, so let’s grab them:
all_policies = j.Policy().retrieve_all()
Notice that I skip the intermediary step of assigning the results of j.Policy()
to a variable. This would just give us a list of policy ID’s and names, which isn’t enough information. We want the entire XML for the policies.
Speaking of which, we need to know a little bit about the structure of a policy to be able to find the data that we’re interested in (we can’t just “Find”) through the whole text, since this is XML, and structure is important! So, doing a little research, I pulled up one of my printer-installer policies and looked at the relevant data. Here’s how to do it, and what it looks like (I trimmed out all of the unimportant stuff):
>>> j.Policy(287)
<policy>
<general>
<id>287</id>
<name>Install PS Classroom and Faculty Printers</name>
</general>
<printers>
<size>2</size>
<leave_existing_default></leave_existing_default>
<printer>
<id>263</id>
<name>PSColor1</name>
<action>install</action>
<make_default>false</make_default>
</printer>
<printer>
<id>264</id>
<name>PSWorkroom</name>
<action>install</action>
<make_default>false</make_default>
</printer>
</printers>
</policy>
So we can see that a policy has a “printers” section, with a list of “printer”s. Each “printer” has its user-facing name in a subelement named “name”. Also, the policy has an ID and a name which I’m interested in because I want to make sure I know how to identify them later. Fortunately, pretty much all JSSObject subclasses have a convenience accessor for .id and .name.
Now, we just loop through all of the policies looking for a printer with the name we’re looking for; in this case “LSCopier”. This is slightly challenging because it involves using the interface to ElementTree.Element
. Let’s look at the fully expanded version first:
for policy in all_policies:
for printer in policy.findall('printers/printer'):
if printer.findtext('name') == 'LSCopier':
print('Printer found in ID %s: %s' %
(policy.id, policy.name))
Running this short (10 lines) gets me my results. Indeed, I could probably parameterize this and re-use it later… Which I’ll do next. Also, let’s trim it up into a list comprehension.
results = [(policy.id, policy.name) for policy in all_policies
for printer in policy.findall('printers/printer')
if printer.findtext('name') == search_name]
(There’s no way this will fit on a single line in this theme… Sorry!) This nets us a “results” array filled with tuples of policy names and ids that match our argument “search_name”. To pull this off we need to import sys, and make a few more changes.
Here is the entire finished script:
#!/usr/bin/python
import sys
import jss
jssPrefs = jss.JSSPrefs()
j = jss.JSS(jssPrefs)
if len(sys.argv) > 1:
all_policies = j.Policy().retrieve_all()
for search_name in sys.argv[1:]:
print "Searching for %s" % search_name
results = [(policy.id, policy.name) for policy in all_policies
for printer in policy.findall('printers/printer')
if printer.findtext('name') == search_name]
for result in results:
print('Found in %s: %s' % result)
This would be easy to expand into a more general XML search utility for auditing your Policies. For example, you may want to search for policies that install a particular package (although jss_helper already does that). Or you might want to search for policies that Recon.