I recently needed to get an open-source circuit toolkit (HACKT)compiled and deployed on my local system. I already knew the C++ source compiled and installed fine on Linux, and didn't want to jump through the hoops to get it running on my Mac 10.11 (El Capitan) system, so I decided to bring up a reproducible build on a VM. I'd used Ansible in the past but had forgotten some of the syntax, so I decided to write a simple playbook that would get this done, as well as document some of the build details required for HACKT. Some of the details on using Ansible locally on a Vagrant VM weren't also immediately obvious to me, so this is also an attempt to collate that information for future reference.
1 Install Vagrant and Ansible
Vagrant is a nice way to bring up a virtual machine (running through VirtualBox, for example) from predefined 'boxes' very easily. Ansible is a separate provisioning system that lets you easily, and in a reproducible and documented fashion, customize these virtual machines, and only requires SSH running on the target system. Vagrant also ships with some nice bindings to Ansible, allowing for really easy provisioning of the deployed boxes. Even more, you don't even need Ansible installed on your local machine as Vagrant will install it automatically in your virtual machine and execute the provisioning inside it, which is a really lightweight way to do it.
I won't go into the details of installing these tools, but they were very straight-forward to get up and running.
2 Get the vanilla virtual machine up
First, just go to a directory where you will be keeping all the files needed to build your server. Then, within that directory, do the following:
vagrant init
vagrant box add centos/7
The first writes a template Vagrantfile to your current directory, that will contain the configuration information for customizing your new VM. The second command downloads a pre-built CentOS 7 system from the Vagrant community and stores it in your $HOME/.vagrant.d/boxes
Next, we want to configure and provision our VM that will be brought up from this downloaded image. So, edit your Vagrantfile, remove all the crud, and insert the following lines:
Vagrant.configure(2) do |config|
config.vm.box = "centos/7"
end
Now, run the following command to start your VM:
vagrant up
This will bring up your new CentOS VM, as well as rsync your current directory to the VM under /home/vagrant/sync.
You can ssh into the VM by doing:
vagrant ssh
3 Getting Ansible Provisioning working
To enable Ansible provisioning inside your , add the following lines to your Vagrantfile:
Vagrant.configure(2) do |config|
config.vm.box = "centos/7"
config.vm.provision "ansible_local" do |ansible|
ansible.provisioning_path = "/home/vagrant/sync"
ansible.playbook = "playbook.yml"
end
end
The playbook.yml is where we'll write the Ansible instructions (in YAML format) that will provision this VM. This file should stay in your current directory, but when Vagrant brings up the VM, it will rsync this file to your /home/vagrant/sync directory inside your VM, so that's why we need the provisioning_path option specified.
Also note that the configuration to Vagrant is specifying ansible_local, which means Vagrant will install and run Ansible inside the VM.
Next, we'll build up the playbook.yml file step by step.
3.1 Connection setting
At the top of the file is the connection setings:
---
- hosts: all
connection: local
This is all that is needed to run the provisioning locally on the VM.
3.2 Setup some variables
vars:
src_dir: /home/vagrant/src
hackt_dir: "{{ src_dir }}/hackt"
3.3 Start the tasks section and update packages
tasks:
- name: Update yum
yum: name=* state=latest
sudo: true
- name: Install packages
yum: name="{{ item }}" state=present
sudo: true
with_items:
- "@Development tools"
- guile
- libtool-ltdl-devel
- wget
- texinfo
The nice thing about using Ansible is that the actions are idempotent, so you can reprovision any number of times and it will keep a consistent state, as well as only running commands that will end up modifying the current state.
The other tricky thing here is the quotes when installing packages, especially the syntax for the "Development tools" group.
3.4 Downgrading Bison version
HACKT, as of this writing, seems to have issues with Bison 2.7 on CentOS7. The work-around for now is to downgrade to version 2.3. First, we yum remove bison, download version 2.3, build, and then install it.
- name: Remove newest bison
yum: name=bison state=removed
sudo: true
- name: Create src directory
file: path={{ src_dir }} state=directory
- name: Download old bison for hackt
get_url: url=http://ftp.gnu.org/gnu/bison/bison-2.3.tar.gz dest={{ src_dir }}
- name: Unzip bison
unarchive: src={{ src_dir }}/bison-2.3.tar.gz dest={{ src_dir }}
- name: Build and install new bison
shell: "{{ item }}"
args:
chdir: "{{ src_dir }}/bison-2.3"
with_items:
- ./configure
- make
- sudo make install
3.5 Building HACKT
Next, we clone the HACKT repository and checkout a specific snapshot, bootstrap, configure, and make.
The bootstrap step has a creates arg in it that doesn't rerun it if the configure script is already there.
The compiler CXXFLAGS does require warnings not to turn into errors because of some unused-typedef warnings in the current gcc.
- name: Get HACKT
git: repo=https://github.com/fangism/hackt.git
dest={{ src_dir }}/hackt
version=tags/SNAPSHOT-RELEASE-20141024
- name: Bootstrap HACKT
shell: ./bootstrap
args:
chdir: "{{ hackt_dir }}"
creates: "{{ hackt_dir }}/configure"
- name: Build HACKT
shell: "{{ item }}"
args:
chdir: "{{ hackt_dir }}"
creates: /usr/local/bin/hackt
with_items:
- env CXXFLAGS='-Wno-error' ./configure
- make
- sudo make install
4 Final steps and other tips
And that's basically it, for a very simple provisioning system. Ansible has some really cool features that we haven't touched on here, and it's probably a good idea to breakdown the playbook into a more hierarchical structure. But for now, here's the complete playbook:
---
- hosts: all
connection: local
vars:
src_dir: /home/vagrant/src
hackt_dir: "{{ src_dir }}/hackt"
tasks:
- name: Update yum
yum: name=* state=latest
sudo: true
- name: Install packages
yum: name="{{ item }}" state=present
sudo: true
with_items:
- "@Development tools"
- guile
- libtool-ltdl-devel
- wget
- texinfo
- name: Remove newest bison
yum: name=bison state=removed
sudo: true
- name: Create src directory
file: path={{ src_dir }} state=directory
- name: Download old bison for hackt
get_url: url=http://ftp.gnu.org/gnu/bison/bison-2.3.tar.gz dest={{ src_dir }}
- name: Unzip bison
unarchive: src={{ src_dir }}/bison-2.3.tar.gz dest={{ src_dir }}
- name: Build and install new bison
shell: "{{ item }}"
args:
chdir: "{{ src_dir }}/bison-2.3"
with_items:
- ./configure
- make
- sudo make install
- name: Get HACKT
git: repo=https://github.com/fangism/hackt.git
dest={{ src_dir }}/hackt
version=tags/SNAPSHOT-RELEASE-20141024
- name: Bootstrap HACKT
shell: ./bootstrap
args:
chdir: "{{ hackt_dir }}"
creates: "{{ hackt_dir }}/configure"
- name: Build HACKT
shell: "{{ item }}"
args:
chdir: "{{ hackt_dir }}"
with_items:
- env CXXFLAGS='-Wno-error' ./configure
- make
- sudo make install
Some further useful commands and tips are below.
4.1 Utility commands
- vagrant suspend - Shuts down but saves state on the disk
- vagrant resume - Resumes the suspended VM
- vagrant halt - Shutdown
- vagrant destroy - Destroy all the customizations and disk
- vagrant reload --provision - Restart a running VM after making changes to Vagrantfile
- vagrant provision - Rerun the provisioning on a running VM
4.2 Sharing files
I wasn't able to get the NFS shares working, but just stuck to the simple rsync option where a directory on the host can be copied to your VM on bootup/reload. Just add the following line to your Vagrantfile:
config.vm.synced_folder "share", "/share/dev", type: "rsync"
This will copy your share/ subdirectory to the VM's /share/dev directory