perjantai 30. syyskuuta 2016

Reinstalling NOOBS and Rasbian on Raspberry Pi

I've got a small gluster of six Raspberry Pies, and I wanted to update them all. I'm just too lazy to update every SD card one by one, so I wondered if it is possible to do the update without removing SD cards from Raspberries. And it is!

Installation of NOOBS creates a partition on the SD card, /dev/mmcblk0p1. This partition contains files needed for install (details are available at https://github.com/raspberrypi/noobs/wiki/NOOBS-partitioning-explained). So only thing you need to do is to download new Noobs, mount /dev/mmcblk0p1 on device and replace files.

So you need do something like following to update NOOBS
curl https://downloads.raspberrypi.org/NOOBS_latest -L -o noobs.zip
sudo mount -t vfat  /dev/mmcblk0p1 /mnt
sudo rm -rf /mnt/*
sudo unzip noobs.zip -d /mnt/
Then you can boot up the Raspberry and start the NOOBS recovery by pressing shift -key during start up. But I'm too lazy to do even that. Luckily it is possible to make the NOOBS automatically start recovery and install new OS.

The behaviour of NOOBS can be controlled with commandline options. These options are defined in file called "recovery.cmdline"  in the root of  /dev/mmcblk0p1. The default contents of the file are following:

quiet ramdisk_size=32768 root=/dev/ram0 init=/init vt.cur_default=1 elevator=deadline

To make the installer start by default, you have to add "runinstaller" option. This only starts the installer, but it will need user input to continue. Another option, silentinstall, will tell the installer to go forth and install OS. Just make sure that there is only one OS in os/ -directory, and if it has more that one flavour, edit it's flavours.json file (details in https://github.com/raspberrypi/noobs#how-to-automatically-install-an-os).

So the recovery.cmdline should have following contents
runinstaller silentinstall quiet ramdisk_size=32768 root=/dev/ram0 init=/init vt.cur_default=1 elevator=deadline
After installation, the installer does remove the "runinstaller" -option from recovery.cmdline so it does not reinstall on every boot. The "silentinstall" option remains, though.

So when everything is in place, at next reboot, there's a new version of noobs, and it will install the OS automatically. Just remember, that everything on Raspberry will be wiped!

Here's a ansible playbook that does everything. It will take quite a while to complete, as the NOOBS image file is pretty big and takes a while to download and transfer to hosts. Reason why I'm downloading NOOBS to local machine is that I'm running this playbook with six Raspberries and it should be faster to download NOOBS once to local machine and the transfer it to Raspberries instead if downloading it on every Raspberry
- hosts: all
  vars:
    - noobs_file: noobs.zip
    - recovery_directory: /mnt/recovery
  tasks:
    - name: download noobs if not present
      local_action: get_url url=https://downloads.raspberrypi.org/NOOBS_latest dest={{playbook_dir}}/{{noobs_file}}
      become: no 
    - name: mount device
      mount: name=/mnt/recovery src=/dev/mmcblk0p1 fstype=vfat state=mounted 
    - name: remove old noobs
      file: path={{recovery_directory}}/* state=absent 
    - name: unzip noobs
      unarchive: src={{noobs_file}} dest={{recovery_directory}} owner=root group=root 
    - name: set reinstall
      lineinfile: dest={{recovery_directory}}/recovery.cmdline regexp='^(runinstaller)?\s?(silentinstall)?\s?(.*)$' line='runinstaller silentinstall \3' backrefs=yes 
    - name: unmount device
      mount: name=/mnt/recovery src=/dev/mmcblk0p1 fstype=vfat state=unmounted 
    - name: reboot
      command: shutdown -r now
      ignore_errors: True
  become: yes