
I got myself a bunch of Raspberry Pi 3s a short while ago:

...the idea being that I can use those to run a few experiments on.
After the experiment, I would need to be able to somehow remove the
stuff that I put on them again. The most obvious way to do that is to
reimage them after every experiment; but the one downside of the
multi-pi
cases
that I've put them in is that removing the micro-SD cards from the Pis
without removing them from the case, while possible, is somewhat fiddly.
That, plus the fact that I cared more about price than about speed when
I got the micro-SD cards makes "image all the cards of these Raspberry
Pis" something I don't want to do
every day.
So I needed a different way to reset them to a clean state, and I decided to
use ansible to get it done. It required a bit of experimentation, but
eventually I came up with this:
---
- name: install package-list fact
hosts: all
remote_user: root
tasks:
- name: install libjson-perl
apt:
pkg: libjson-perl
state: latest
- name: create ansible directory
file:
path: /etc/ansible
state: directory
- name: create facts directory
file:
path: /etc/ansible/facts.d
state: directory
- name: copy fact into ansible facts directory
copy:
dest: /etc/ansible/facts.d/allowclean.fact
src: allowclean.fact
mode: 0755
- name: remove extra stuff
hosts: pis
remote_user: root
tasks:
- apt: pkg= item state=absent purge=yes
with_items: " ansible_local.allowclean.cleanable_packages "
- name: remove package facts
hosts: pis
remote_user: root
tasks:
- file:
path: /etc/ansible/facts.d
state: absent
- apt:
pkg: libjson-perl
state: absent
autoremove: yes
purge: yes
This ansible playbook has three plays. The first play installs a custom
ansible fact (written in perl) that emits a json file which defines
cleanable_packages as a list of packages to remove. The second uses
that fact to actually remove those packages; and the third removes the
fact (and the libjson-perl library we used to produce the JSON).
Which means, really, that all the hard work is done inside the
allowclean.fact file. That looks like this:
#!/usr/bin/perl
use strict;
use warnings;
use JSON;
open PACKAGES, "dpkg -l ";
my @packages;
my %whitelist = (
...
);
while(<PACKAGES>)
next unless /^ii\s+(\S+)/;
my $pack = $1;
next if(exists($whitelist $pack ));
if($pack =~ /(.*):armhf/)
$pack = $1;
push @packages, $1;
my %hash;
$hash cleanable_packages = \@packages;
print to_json(\%hash);
Basically, we create a whitelist, and compare the output of
dpkg -l
against that whitelist. If a certain package is in the whitelist, then
we don't add it to our "packages" list; if it isn't, we do.
The whitelist is just a list of
package => 1 tuples. We just need the
hash keys and don't care about the data, really; I could equally well
have made it
package => undef, I suppose, but whatever.
Creating the whitelist is not that hard. I did it by setting up one
raspberry pi to the minimum that I wanted, running
dpkg -l awk '/^ii/ print "\t\""$2"\" => 1," ' > packagelist
and then adding the contents of that
packagelist file in place of the
... in the above perl script.
Now whenever we apply the ansible playbook above,
all packages (except
for those in the whitelist) are removed from the system.
Doing the same for things like users and files is left as an exercise to
the reader.
Side note:
... is a valid perl construct. You can write it, and the
compiler won't complain. You can't run it, though.