pages tagged sexyNico Schotteliushttps://www.nico.schottelius.org//tags/sexy/Nico Schotteliusikiwiki2016-02-25T13:34:32ZKVM Virtual Machines managed with cdist and sexy @ local.chhttps://www.nico.schottelius.org//blog/kvm-vms-with-cdist-at-local.ch/2016-02-25T13:34:32Z2015-02-03T14:47:26Z
<h2>Introduction</h2>
<p>This article describes the KVM setup of <a href="http://www.local.ch">local.ch</a>, which is
managed by <a href="https://www.nico.schottelius.org//software/sexy/">sexy</a> and configured by <span class="createlink">cdist</span>.</p>
<p>If you haven't so far, you may want to have a look at the
<a href="https://www.nico.schottelius.org//blog/sexy-and-cdist-at-local.ch/">Sexy and cdist @ local.ch</a>
article before continuing to read this one.</p>
<h2>KVM Host configuration</h2>
<p>The KVM hosts are Dell R815 with CentOS 6.x installed. Why Dell? Because they
offered a good price/value combination. Why CentOS? Historical
reasons. The hosts got a minimal set of BIOS tuning to support the VM performance:</p>
<ul>
<li>Enable the usual virtualisation flags (don't forget to enable the IOMMU!)</li>
<li>Change the power profile to <strong>Maximum Perforamnce</strong></li>
</ul>
<p>Furthermore, as the CentOS kernel is pretty old (2.6.32-279) and
conservatively configured, the kernel needs the following
command line option to enable the IOMMU:</p>
<pre><code>amd_iommu=on
</code></pre>
<p>Not enabling this option degrades the performance.
In our case, enabling it reduced the latency of the
application running in the VM by a factor of 10.</p>
<p>One big design consideration of the the KVM setup at local.ch is to make the
KVM hosts as independent as possible and sensibly fault tolerant. That said,
VMs are stored on local storage and hosts are always redundantly connected
to two switches use <a href="https://en.wikipedia.org/wiki/Link_aggregation">LACP</a>.</p>
<h2>KVM Host Network Configuration</h2>
<p><a href="https://www.nico.schottelius.org//blog/kvm-vms-with-cdist-at-local.ch/kvm-setup-local.ch-overview.png"><img src="https://www.nico.schottelius.org//blog/kvm-vms-with-cdist-at-local.ch/kvm-setup-local.ch-overview.png" width="770" height="620" alt="Overview of KVM setup at local.ch" class="img" /></a></p>
<p>As can be seen in the picture above, every KVM host is connected to two
<strong>10G Arista switches (7050T-52-R)</strong> using LACP. Besides being capable
of running 10G, the Arista switches are actually pretty neat for the Unix geek,
because they are Linux based with a
<a href="https://en.wikipedia.org/wiki/Field-programmable_gate_array">FPGA</a>
attached. Furthermore you can easily
gain access to a shell by typing <strong>enable</strong> followed by <strong>bash</strong>.</p>
<p>The Arista switches are connected together with 2x 10G links, over which LACP+MLAG
is configured. This gives us the ability to connect every KVM host with LACP to two
<strong>different</strong> switches: They use MLAG to synchronise their LACP states.</p>
<p>On the KVM host, the network is configured as follows:</p>
<p>The dual Port 10G card (Intel Corporation 82599EB) is bonded together into bond0.</p>
<pre><code>[root@kvm-hw-inx01 network-scripts]# cat /proc/net/bonding/bond0
Ethernet Channel Bonding Driver: v3.6.0 (September 26, 2009)
Bonding Mode: IEEE 802.3ad Dynamic link aggregation
Transmit Hash Policy: layer2 (0)
MII Status: up
MII Polling Interval (ms): 0
Up Delay (ms): 0
Down Delay (ms): 0
802.3ad info
LACP rate: slow
Aggregator selection policy (ad_select): stable
Active Aggregator Info:
Aggregator ID: 3
Number of ports: 2
Actor Key: 33
Partner Key: 30
Partner Mac Address: 02:1c:73:1b:f5:b2
Slave Interface: eth4
MII Status: up
Speed: 10000 Mbps
Duplex: full
Link Failure Count: 0
Permanent HW addr: 68:05:ca:0b:5b:6a
Aggregator ID: 3
Slave queue ID: 0
Slave Interface: eth5
MII Status: up
Speed: 10000 Mbps
Duplex: full
Link Failure Count: 0
Permanent HW addr: 68:05:ca:0b:5b:6b
Aggregator ID: 3
Slave queue ID: 0
</code></pre>
<p>The following configuration is used to create the bond0 device:</p>
<pre><code>[root@kvm-hw-inx01 network-scripts]# cat ifcfg-bond0
DEVICE=bond0
BOOTPROTO=none
BONDING_OPTS="mode=802.3ad"
ONBOOT=yes
MTU=9000
[root@kvm-hw-inx01 sysconfig]# cat network-scripts/ifcfg-eth4
DEVICE="eth4"
NM_CONTROLLED="yes"
USERCTL=no
ONBOOT=yes
MASTER=bond0
SLAVE=yes
BOOTPROTO=none
[root@kvm-hw-inx01 sysconfig]# cat network-scripts/ifcfg-eth5
DEVICE="eth5"
NM_CONTROLLED="yes"
USERCTL=no
ONBOOT=yes
MASTER=bond0
SLAVE=yes
BOOTPROTO=none
</code></pre>
<p>The MTU of the 10G cards has been set to 9000, as the Arista switches support
<a href="https://en.wikipedia.org/wiki/Jumbo_frame">Jumbo Frames</a>.</p>
<p>Every VM is attached to two different networks:</p>
<ul>
<li>PZ: presentation zone (for general traffic) (10.18x.0.0/22 network)</li>
<li>FZ: filer zone (for NFS and database traffic) (10.18x.64.0/22 network)</li>
</ul>
<p>Both networks are seperated using the VLAN tags 2 (pz) and 3 (fz), which result
in <strong>bond0.2</strong> and <strong>bond0.3</strong>:</p>
<pre><code>[root@kvm-hw-inx01 network-scripts]# ip l | grep bond
6: eth4: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 9000 qdisc mq master bond0 state UP qlen 1000
7: eth5: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 9000 qdisc mq master bond0 state UP qlen 1000
8: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 9000 qdisc noqueue state UP
139: bond0.2@bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 9000 qdisc noqueue state UP
140: bond0.3@bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 9000 qdisc noqueue state UP
</code></pre>
<p>To keep things simple, the two vlan tagged (bonded) interfaces are added to a bridge each,
to which the VMs are attached later on. The configuration looks like this:</p>
<pre><code>[root@kvm-hw-inx01 network-scripts]# cat ifcfg-bond0.2
DEVICE="bond0.2"
ONBOOT=yes
VLAN=yes
BRIDGE=brpz
[root@kvm-hw-inx01 network-scripts]# cat ifcfg-brpz
DEVICE=brpz
TYPE=Bridge
ONBOOT=yes
DELAY=0
NM_CONTROLLED=no
MTU=9000
</code></pre>
<p>This is how a bridge looks like in production (with about 70 lines stripped):</p>
<pre><code>[root@kvm-hw-inx01 network-scripts]# brctl show
bridge name bridge id STP enabled interfaces
brfz 8000.024db29ca91f no bond0.3
tap13
tap73
[...]
brpz 8000.02f6742800b2 no bond0.2
tap0
tap1
[...]
</code></pre>
<p>Summarised, the network configuration of a KVM host looks like this:</p>
<pre><code>arista1 arista2
| |
[eth4 + eth5] -> bond0
|
|
/ \
bond0.2 bond0.3
/ \
brpz brfz
\ /
tap1 tap2
\ /
VM
</code></pre>
<h2>VM configuration</h2>
<p>The VM configuration can be found below <strong>/opt/local.ch/sys/kvm</strong>
on every KVM host. Every VM is stored below
<strong>/opt/local.ch/sys/kvm/vm/</strong> and contains the following
files:</p>
<pre><code>[root@kvm-hw-inx03 jira-vm-inx01.intra.local.ch]# ls
monitor pid start start-on-boot system-disk vnc
</code></pre>
<ul>
<li>monitor: socket to the monitor from KVM</li>
<li>pid: the pid of the VM</li>
<li>start: the script to start the VM (see below for an example)</li>
<li>start-on-boot: if this file exists, the VM will be started on boot</li>
<li>system-disk: the qcow2 image of the system disk</li>
<li>vnc: socket to the screen of the VM</li>
</ul>
<p>With the exception of monitor, pid and vnc are all files generated by cdist.
The start script of a VM looks like this:</p>
<pre><code>[root@kvm-hw-inx03 jira-vm-inx01.intra.local.ch]# cat start
#!/bin/sh
# Generated shell script - do not modify
#
/usr/libexec/qemu-kvm \
-name jira-vm-inx01.intra.local.ch \
-enable-kvm \
-m 8192 \
-drive file=/opt/local.ch/sys/kvm/vm/jira-vm-inx01.intra.local.ch/system-disk,if=virtio \
-vnc unix:/opt/local.ch/sys/kvm/vm/jira-vm-inx01.intra.local.ch/vnc \
-cpu host \
-boot order=nc \
-pidfile "/opt/local.ch/sys/kvm/vm/jira-vm-inx01.intra.local.ch/pid" \
-monitor "unix:/opt/local.ch/sys/kvm/vm/jira-vm-inx01.intra.local.ch/monitor,server,nowait" \
-net nic,macaddr=00:16:3e:02:00:ab,model=virtio,vlan=200 \
-net tap,script=/opt/local.ch/sys/kvm/bin/ifup-pz,downscript=/opt/local.ch/sys/kvm/bin/ifdown,vlan=200 \
-net nic,macaddr=00:16:3e:02:00:ac,model=virtio,vlan=300 \
-net tap,script=/opt/local.ch/sys/kvm/bin/ifup-fz,downscript=/opt/local.ch/sys/kvm/bin/ifdown,vlan=300 \
-smp 4
</code></pre>
<p>Most parameter values depend on output of sexy,
which uses the cdist type <strong>__localch_kvm_vm</strong>,
which in turn assembles this start script.
The above script may be useful for one or more of my readers,
as it includes a lot of tuning we have done to KVM.</p>
<h2>Automatic startup of VMs</h2>
<p>The virtual machines are brought up by an init script located at
<strong><em>/etc/init.d/kvm-vms</em></strong>. As every VM contains its own startup script
and is marked whether it should be started at boot, the init script
is pretty simple:</p>
<pre><code>basedir=/opt/local.ch/sys/kvm/vm
broken_lock_file_for_centos=/var/lock/subsys/kvm-vms
case "$1" in
start)
cd "$basedir"
# Specific VM given
if [ "$2" ]; then
vm_list=$2
else
vm_list=$(ls)
fi
for vm in $vm_list; do
vm_base_dir="$basedir/$vm"
start_script="$vm_base_dir/start"
# Skip start of machines which should not start
if [ ! -f "$vm/start-on-boot" ]; then
continue
fi
echo "Starting VM $vm ..."
logger -t kvm-vms "Starting VM $vm ..."
screen -d -m -S "$vm" "$start_script"
done
touch "$broken_lock_file_for_centos"
;;
</code></pre>
<p>As you can see, every VM is started in its own
<a href="http://www.gnu.org/software/screen/">screen</a> - so if screen decides to
hang up, only one VM is affected.
Furthermore screen supports only a limited number of windows it can server.
The process listing for a running virtual machine looks like this:</p>
<pre><code>root 64611 0.0 0.0 118840 852 ? Ss Mar11 0:00 SCREEN -d -m -S binarypool-vm-inx02.intra.local.ch /opt/local.ch/sys/kvm/vm/binarypool-vm-inx02.intra.local.ch/start
root 64613 0.0 0.0 106092 1180 pts/22 Ss+ Mar11 0:00 /bin/sh /opt/local.ch/sys/kvm/vm/binarypool-vm-inx02.intra.local.ch/start
root 64614 2.9 2.2 9106828 5819748 pts/22 Sl+ Mar11 5221:41 /usr/libexec/qemu-kvm -name binarypool-vm-inx02.intra.local.ch -enable-kvm -m 8192 -drive file=/opt/local.ch/sys/kvm/vm/binarypool-vm-inx02.intra.local.ch/system-disk,if=virtio -vnc unix:/opt/local.ch/sys/kvm/vm/binarypool-vm-inx02.intra.local.ch/vnc -cpu host -boot order=nc -pidfile /opt/local.ch/sys/kvm/vm/binarypool-vm-inx02.intra.local.ch/pid -monitor unix:/opt/local.ch/sys/kvm/vm/binarypool-vm-inx02.intra.local.ch/monitor,server,nowait -net nic,macaddr=00:16:3e:02:00:7f,model=virtio,vlan=200 -net tap,script=/opt/local.ch/sys/kvm/bin/ifup-pz,downscript=/opt/local.ch/sys/kvm/bin/ifdown,vlan=200 -net nic,macaddr=00:16:3e:02:00:80,model=virtio,vlan=300 -net tap,script=/opt/local.ch/sys/kvm/bin/ifup-fz,downscript=/opt/local.ch/sys/kvm/bin/ifdown,vlan=300 -smp 4
</code></pre>
<h2>Common Tasks</h2>
<p>The following sections show you how to do regular maintenance
tasks on the KVM infrastructure.</p>
<h3>Create a VM</h3>
<p>VMs can easily be created using the script <strong>vm/create-vm</strong> from the sysadmin-logs repository
(local.ch internally), which looks like this:</p>
<pre><code>sexy host add --type vm $fqdn
sexy host vm-host-set --vm-host $vmhost $fqdn
sexy host disk-add --size $disksize $fqdn
sexy host memory-set --memory $memory $fqdn
sexy host cores-set --cores $cores $fqdn
mac_pz=$(sexy mac generate)
mac_fz=$(sexy mac generate)
sexy host nic-add $fqdn -m $mac_pz -n pz
sexy host nic-add $fqdn -m $mac_fz -n fz
sexy net-ipv4 host-add "$net_pz" -m "$mac_pz" -f "$fqdn"
sexy net-ipv4 host-add "$net_fz" -m "$mac_fz" -f "$fz_fqdn"
echo "Updating git / github ..."
cd ~/.sexy
git add db
git commit -m "Added host $fqdn"
git pull
git push
# Apply changes: first network, so dhcp & dns are ok, then create VM
cat << eof
Todo for apply:
sexy net-ipv4 apply --all
sexy host apply --all
Start VM on $vmhost: ssh $vmhost /opt/local.ch/sys/kvm/vm/$fqdn/start
eof
</code></pre>
<h3>Delete a VM</h3>
<p>Run the script <strong>remove-host</strong>, which essentially does the following:</p>
<ul>
<li>Remove various monitoring / backup configurations</li>
<li>Detect if it is a VM, if so</li>
<li>Stop it</li>
<li>Remove it from the host</li>
<li>Add mac address to the list of free mac addresses</li>
<li>Delete host from the networks</li>
<li>Delete host from sexy database</li>
</ul>
<h3>Move VM to another server</h3>
<p>To move one VM to another host, the following steps are necessary:</p>
<ul>
<li>sexy host vm-host-set ... # to new host</li>
<li>stop vm</li>
<li>scp/rsync directory from old host to new host</li>
<li>sexy host apply --all # record db change</li>
<li>start vm on new host</li>
</ul>
Sexy example: Small backend change and you are managing DNShttps://www.nico.schottelius.org//blog/sexy-backend-change-dns-support/2016-02-25T13:34:32Z2015-02-03T14:47:26Z
<h2>Introduction</h2>
<p>This previous article about
<a href="https://www.nico.schottelius.org//blog/sexy-network-bootstrap/">bootstrapping a network with sexy</a>
explained in detail how to manage a network and how to configure
it with cdist.</p>
<p>This article shows you what needs to be changed to support DNS resolution
in addition to the configured DHCP service.</p>
<h2>Background</h2>
<p>I am using <a href="http://www.thekelleys.org.uk/dnsmasq/doc.html">dnsmasq</a> on my
router, which can act as a DNS and DHCP server. DNS A entries can be added
to the configuration using the <strong>host-record</strong> command.</p>
<h2>The change</h2>
<p>Taking the previously net-ipv4 backend,
<a href="http://git.schottelius.org/?p=sexy-database;a=commit;h=e7f45dccc1feace042bec1549079f073aa476739">the required change is very small</a>:</p>
<pre><code>- line="dhcp-host=${mac},$ipv4a,$hostname"
- echo "${line}" >> "${tmp}"
+ echo "dhcp-host=${mac},$ipv4a,$hostname" >> "${tmp}"
+ echo "host-record=$hostname,$fqdn,$ipv4a" >> "${tmp}"
</code></pre>
<p>Thanks to the modular configuration and the easiness of both sexy and cdist,
this change and a call to <strong>sexy net-ipv4 apply --all</strong> is everything that is needed
to make dnsmasq serve internal DNS names.</p>
<h2>The result</h2>
<p>What this article should show is that whatever you do in the backend, sexy is not affected
at all and you can dramatically change whatever happens on <strong>sexy net-ipv4 apply --all</strong>.</p>
<p>You can browse
<a href="http://git.schottelius.org/?p=sexy-database;a=summary">the sexy database</a>
as well as
the <a href="http://git.schottelius.org/?p=cdist-nico;a=summary">cdist configuration</a>.</p>
Sexy and cdist interaction: Sexy chooses hosts, cdist configureshttps://www.nico.schottelius.org//blog/sexy-cdist-interaction-sexy-chooses-cdist-configures/2016-02-25T13:34:32Z2015-02-03T14:47:26Z
<h2>Introduction</h2>
<p>Version 2 of <a href="https://www.nico.schottelius.org//software/sexy/">sexy</a>,
the Swiss Army Knife for inventory management, is already
<strong>using</strong> and <strong>usable</strong> from <span class="createlink">cdist</span>.</p>
<p>This is the first blog post of a series showing examples of
using sexy and cdist.</p>
<h2>Example</h2>
<p>Cdist is executed with a list of hosts to operate on:</p>
<pre><code>% cdist config
usage: cdist config [-h] [-d] [-v] [-c CDIST_HOME] [-i MANIFEST] [-p] [-s]
[--remote-copy REMOTE_COPY] [--remote-exec REMOTE_EXEC]
host [host ...]
</code></pre>
<p>Sexy in turn is able to manage hosts, mac addresses and networks:</p>
<pre><code>% sexy
usage: sexy [-h] [-d] [-v] [-V] {net-ipv4,host,mac} ...
sexy: error: too few arguments
</code></pre>
<p>Sexy knows about a command to list hosts, named <strong>host list</strong>.
So I can use sexy to tell cdist which hosts to configure. For instance
all dhcp servers:</p>
<pre><code>% sexy host list | grep dhcp
dhcp-vm-inx01.intra.local.ch
dhcp-vm-inx02.intra.local.ch
dhcp-vm-snr01.intra.local.ch
dhcp-vm-snr02.intra.local.ch
% ./bin/cdist config -vp $(sexy host list | grep dhcp)
INFO: dhcp-vm-inx01.intra.local.ch: Running global explorers
INFO: dhcp-vm-snr01.intra.local.ch: Running global explorers
INFO: dhcp-vm-snr02.intra.local.ch: Running global explorers
INFO: dhcp-vm-inx02.intra.local.ch: Running global explorers
...
</code></pre>
<p>Sexy, isn't it?</p>
Sexy is being renamed to cinvhttps://www.nico.schottelius.org//blog/sexy-is-being-renamed-to-cinv/2016-02-25T13:34:32Z2015-02-03T14:47:26Z
<p>You may have noticed that searching
for the magnificent tool named <strong>sexy</strong> on a search engine of your
choice did not easily lead you to the Swiss Army Knife for inventory management.</p>
<p>To fix this problem, sexy is currently being renamed to
<a href="https://www.nico.schottelius.org//software/cinv/">cinv</a>.</p>
Bootstrapping a network with sexyhttps://www.nico.schottelius.org//blog/sexy-network-bootstrap/2016-02-25T13:34:32Z2015-02-03T14:47:26Z
<h2>Introduction</h2>
<p>This article explains how to begin to manage a network
with <a href="https://www.nico.schottelius.org//software/sexy/">sexy</a>. Because I just moved house,
I take my home network as an example.</p>
<h2>Prerequisites</h2>
<p>First of all, you need to have sexy installed, as described on
the <a href="https://www.nico.schottelius.org//software/sexy/">sexy homepage</a>. Secondly, if you already played
around with sexy, you should empty the sexy database, which is located
at <strong>~/.sexy</strong>:</p>
<pre><code>% rm -rf ~/.sexy
</code></pre>
<p>Or, if you are using <strong>git</strong> to manage your ~/.sexy directory, create a fresh
branch, which does not contain any files:</p>
<pre><code>% cd ~/.sexy
% git checkout -b network_bootstrap
# Ensure all (committed and non-committed) files are gone
% rm -rf db/ backend/
% git rm -r db/ backend/
% git commit -m "Empty sexy database"
</code></pre>
<h2>Add the first host</h2>
<p>First of all, let us add a host. Sexy wants to know its type (virtual machine
or hardware). Sexy expects all names as fully qualified domain names (FQDNs):</p>
<pre><code>% sexy host add -t hw katze.intern.schottelius.org
</code></pre>
<p><strong>Hint:</strong> You can use the <strong>-h</strong> flag to get help for any command.
Using <strong>host list</strong>, we can verify the host has been added:</p>
<pre><code>% sexy host list
katze.intern.schottelius.org
</code></pre>
<p>Now we can network cards to this host:</p>
<pre><code>% sexy host nic-add -m 00:00:24:c8:da:bc -n eth0 katze.intern.schottelius.org
% sexy host nic-add -m 00:00:24:c8:da:bd -n eth1 katze.intern.schottelius.org
</code></pre>
<h2>Add the network</h2>
<p>Currently, sexy only allows you to manage IPv4 based networks
- IPv6 may be added in future releases. So the command to remember for now, is
<strong>net-ipv4</strong>:</p>
<pre><code>% sexy net-ipv4 add --mask 22 192.168.24.0
% sexy net-ipv4 list
192.168.24.0
</code></pre>
<p>Now we created the network 192.168.24.0/22.</p>
<h2>Add a host to a network</h2>
<p>In sexy, the host and net-ipv4 areas are disconnected: You can use sexy to manage
only hosts, to manage only networks or to manage both. To allow this flexibility,
the network part does not know about any information from the host part.
Luckily enough, you don't need to re-enter the information, but you can retrieve
them from the database.</p>
<p>The previously added host, <strong>katze.intern.schottelius.org</strong>, is the router of
my home network and it should use the first IPv4 address in the network.
The <strong>net-ipv4 host-add</strong> command can be used to add a host:</p>
<pre><code>% sexy net-ipv4 host-add
usage: sexy net-ipv4 host-add [-h] [-d] [-v] -m MAC_ADDRESS -f FQDN
[-i IPV4_ADDRESS]
network
</code></pre>
<p>So adding the host to a network requires giving in at least the mac address,
which we entered before. So we can use the following line to add the host to
our new network:</p>
<pre><code>% host=katze.intern.schottelius.org
% mac=$(sexy host nic-addr-get -n eth0 $host)
% sexy net-ipv4 host-add -m $mac -f $host 192.168.24.0
</code></pre>
<p>Sexy will be default use the next free address and as this is the first host in
the network, it used .1:</p>
<pre><code>% sexy net-ipv4 host-ipv4-address-get 192.168.24.0 -f katze.intern.schottelius.org
192.168.24.1
</code></pre>
<h2>Making use of the entered information</h2>
<p>Sexy does not know which DNS or DHCP server you may be using.
To implement changes to your architecture (probably using
a software like <a href="https://www.nico.schottelius.org//software/cdist/">cdist</a>), sexy supports
<strong>backends</strong> to do the change.</p>
<p>For my home network, I am going to use
<a href="http://www.thekelleys.org.uk/dnsmasq/doc.html">dnsmasq</a>, because the
router is a small <a href="http://soekris.com/net5501.htm">Soekris net5501</a>.</p>
<p>The backends are stored in <strong>~/.sexy/backend</strong> and for this
example tutorial, I will create <strong>~/.sexy/backend/net-ipv4/apply</strong>:</p>
<pre><code>% cat ~/.sexy/backend/net-ipv4/apply
#!/bin/sh -e
cdist_base="/home/users/nico/p/cdist/nico"
cdist_bin="$cdist_base/bin/cdist"
dst_dir="$cdist_base/conf/type/__nico_router/files/dnsmasq.d"
tmp=$(mktemp /tmp/foooooo.XXXXXXXXXXXXXXXX)
for network in "$@"; do
dstfile="${dst_dir}/${network}-dhcp.conf"
cat << eof > "$tmp"
# WARNING: sexy generated file, do *not* edit directly.
eof
for fqdn in $(sexy net-ipv4 host-list $network); do
mac=$(sexy net-ipv4 host-mac-address-get -f "$fqdn" "$network")
ipv4a=$(sexy net-ipv4 host-ipv4-address-get -f "$fqdn" "$network")
hostname=$(echo $fqdn | sed 's/\..*//')
line="dhcp-host=${mac},$ipv4a,$hostname"
echo "${line}" >> "${tmp}"
done
mv "${tmp}" "${dstfile}"
done
cd "${dst_dir}"
git add .
git commit -m "Update Sexy generated network configuration" -o -- . 2>/dev/null || true
echo "Transferring changes to git remote"
git pull --quiet
git push --quiet
"$cdist_bin" config -v zuhause.schottelius.org
</code></pre>
<p>In essence this backend creates the dnsmasq configuration and executes cdist afterwards
to apply the changes. I personally prefer a backend to be shell script, but it can be
any kind of executable.</p>
<h2>Adding more hosts</h2>
<p>To make this tutorial useful and my router actually provide a dhcp
server, I'll add my notebook and the fileserver to sexy:</p>
<pre><code>% sexy host add -t hw loch.intern.schottelius.org
% sexy host nic-add -m f4:6d:04:71:c5:ce loch.intern.schottelius.org
% sexy net-ipv4 host-add -m $(sexy host nic-addr-get -n nic0 loch.intern.schottelius.org) -f loch.intern.schottelius.org 192.168.24.0
% sexy host add -t hw brief.intern.schottelius.org
% sexy host nic-add -m b8:8d:12:15:fd:fa brief.intern.schottelius.org
% sexy net-ipv4 host-add -m $(sexy host nic-addr-get -n nic0 brief.intern.schottelius.org) -f brief.intern.schottelius.org 192.168.24.0
</code></pre>
<p>As you can see, if I do not specify the name of the nic, sexy automatically uses <strong>nic0</strong>
for the first nic. This decision was made, as network device names vary between
operating systems and even operating system versions.</p>
<h2>Applying the configuration</h2>
<p>The previously created backend will get executed with all existing networks,
if you run the apply command with the <strong>--all</strong> parameter:</p>
<pre><code>% sexy net-ipv4 apply --all
</code></pre>
<h2>The result</h2>
<p>Using only the steps above, I've created a sexy maintained network,
<strong>192.168.24.0/22</strong>, which calls <a href="https://www.nico.schottelius.org//software/cdist/">cdist</a> to configure
the router with dnsmasq.</p>
<p>You can browse
<a href="http://git.schottelius.org/?p=sexy-database;a=summary">the real sexy database</a>
created during this tutorial, as well as
the <a href="http://git.schottelius.org/?p=cdist-nico;a=summary">cdist configuration</a>
that is used to configure the router.</p>