├── solo ├── solo.json ├── solo.rb ├── run └── solo-from-scratch ├── README.md ├── cookbooks └── cephco-generic │ ├── files │ └── default │ │ ├── ceph-libvirt-dns.yaml │ │ ├── libvirt-net-back.xml │ │ ├── libvirt-net-pub.xml │ │ ├── libvirt-net-front.xml │ │ ├── ceph-libvirt-dns.conf │ │ ├── parser.py │ │ ├── rename-if-by-mac │ │ └── ceph-libvirt-dns.py │ ├── templates │ └── default │ │ ├── libvirt-net-isolated.xml.erb │ │ ├── interfaces-mira.erb │ │ └── interfaces.erb │ └── recipes │ ├── default.rb │ ├── ssh-keys.rb │ ├── libvirt-dns.rb │ ├── networking-mira.rb │ ├── networking.rb │ └── libvirt.rb └── data_bags └── ssh-keys ├── sage_flab.json ├── dmick_angus.json ├── alfredo_papaya_local.json ├── andrew_schoen_gmail_com.json ├── zack_zack_cerzas_macbook_pro_local.json ├── sage_ped.json ├── sage_autriche.json ├── sage_doctrine.json └── dgallowa_dgallowa_csb.json /solo/solo.json: -------------------------------------------------------------------------------- 1 | { 2 | "run_list": [ "recipe[cephco-generic::default]" ] 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | coobook-vm-general 2 | ============= 3 | 4 | Chef Solo recipes used to bring up KVM hypervisors in the Sepia lab 5 | -------------------------------------------------------------------------------- /solo/solo.rb: -------------------------------------------------------------------------------- 1 | root = File.absolute_path(File.dirname(__FILE__)) 2 | 3 | file_cache_path root 4 | cookbook_path root + '/../cookbooks' 5 | data_bag_path root + '/../data_bags' 6 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/files/default/ceph-libvirt-dns.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | database: 'mysql://username:password@hostname/databasename?charset=utf8&use_unicode=0' 3 | leasefile: '/var/lib/dhcp/dhcpd.leases' 4 | 5 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/files/default/libvirt-net-back.xml: -------------------------------------------------------------------------------- 1 | 2 | back 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/files/default/libvirt-net-pub.xml: -------------------------------------------------------------------------------- 1 | 2 | pub 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/files/default/libvirt-net-front.xml: -------------------------------------------------------------------------------- 1 | 2 | front 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/templates/default/libvirt-net-isolated.xml.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= @name %> 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/files/default/ceph-libvirt-dns.conf: -------------------------------------------------------------------------------- 1 | description "Libvirt/PowerDNS integration" 2 | 3 | start on runlevel [2345] 4 | stop on runlevel [!2345] 5 | 6 | console log 7 | 8 | respawn 9 | 10 | exec /srv/inktank.com/ceph-libvirt-dns/ceph-libvirt-dns.py 11 | 12 | -------------------------------------------------------------------------------- /data_bags/ssh-keys/sage_flab.json: -------------------------------------------------------------------------------- 1 | {"id": "sage_flab", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDn63qpJuh+ZwrRDfDoikZ9Tm5nPJ/m9tP1kyjALJF/CRSiL5g9jRqXiVDcsvdpYmKlFa8Ra0cL1AGq6G3oRue3Lfmw6gTAa4t6m2fTynorQ0V8g/j2//Jc4pIHv43zsWFUPbcIhJf0JIHPrz/jWf2zYsCD7HFBPD7cCVZUV8LmigwSr+TWd3b5t0BCMLs1iRFTx+12V6xwVYqK+74WPnCRTvs7MedSS6taxwSWLhVi7nTtW0XfDG5y26dpoPVoK+zIJVfOaqhh5cjq9K4ds/oJjq39tXRyZ8jJmunE+V92i52P+L3OiXhNfmRUsStKXoKJY4VjLQCELod5y+BULleZ sage@flab"} 2 | -------------------------------------------------------------------------------- /data_bags/ssh-keys/dmick_angus.json: -------------------------------------------------------------------------------- 1 | {"id": "dmick_angus", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC305rmql/Qm8Si+DFvAs77hSUZakwiXhhAhSetS89bWq3AdPQTL1MUXJHFm3AMzwzb00tndvsl1AUmFseXLfAi87daNcKJdwEj/JvXgcHKzAXOpVRfArHh08JnOumQiP4EoLYvq7rL+3LjEFURKOWJLLnGxh4E+iiJAnxLTG3WuUcnz9kXVTwtmIs60+2yH+gZJkkB04mnivPYjX46sHigoKgtlE2JnQfOU1lBkeZaBeUhoZX2rIKwRyGELPPcUBwHk10izyk6tePLAw9bSWeS9uEaa/m+qa8neJDyR4/hWQNR9yRXp0YjKkiKoFog+qtsBt3xBNzTPsFLxZ5zugmp dmick@angus"} 2 | -------------------------------------------------------------------------------- /data_bags/ssh-keys/alfredo_papaya_local.json: -------------------------------------------------------------------------------- 1 | {"id": "alfredo_papaya_local", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDCWy2i6Iv6IWLh53Z44O4fQ0ctxR8odCAWIZAaSQXAxxC++RS+95Zr+BZ5wJwsBOyP1G4AAhl+9uwnn3NmDDG7fKGexPoQrTZZ27KdJ3bHpZRq5LJf9byWFwz4rDsqIqkGjI9sUkfa6nV7ci2BdEkIgLupTT2uXUzTb/njOjI7Fc+cQ7ac4QfJ+yeIcqxeje/YYDF3068V4z1yynk9q2KRXYdERKQghaWDNJlbdBhn/fmsam6okOtUnsXjE88qGPd+LVHWfK0EfYvYXiAoOaJDK8d4EXnJ4RKJceQPBk8ravuQtKVwnRIr8WtmILHuoztthfwqb2WvZ5iMDt6v7oQZ alfredo@papaya.local"} 2 | -------------------------------------------------------------------------------- /data_bags/ssh-keys/andrew_schoen_gmail_com.json: -------------------------------------------------------------------------------- 1 | {"id": "andrew_schoen_gmail_com", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDehvF7TbkfX9Ga4fNdq/j9GHu3c9cNk4iIHVs7e14j0h9DlV14aPyn/M0+z90PK+5p2uL9EvRzkbsPrfxpG3pxUa1pmVmKIPvBruA7ln8tExOVwO3XEsBG7b8VWUMlQmwRS9WEfkKAIHlr9cgIFGb1yrMW30RyL7keO1giotAq/wR9pMicyqYAbZOtI3MuPsDVTNxtyD3RwRUQicz6vNaK/RwfMCZVgdvoinrJ7VSyFVEiitzuQqMlrF935sfLvhuyxmwvJKfZI8QMEYcFbKd1cDnCk9n/PySNRY96gb41nb5pUk+0eaDu37jnmj7LLqoidD3UWUuHNnGBAAk+zNlD andrew.schoen@gmail.com"} 2 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/recipes/default.rb: -------------------------------------------------------------------------------- 1 | package 'qemu-kvm' 2 | package 'libvirt-bin' 3 | package 'virtinst' 4 | package 'ebtables' 5 | package 'python-vm-builder' 6 | 7 | include_recipe "cephco-generic::ssh-keys" 8 | 9 | if node['hostname'].match(/^(mira|irvingi)/) 10 | include_recipe "cephco-generic::networking-mira" 11 | else 12 | include_recipe "cephco-generic::networking" 13 | end 14 | 15 | include_recipe "cephco-generic::libvirt" 16 | include_recipe "cephco-generic::libvirt-dns" 17 | -------------------------------------------------------------------------------- /data_bags/ssh-keys/zack_zack_cerzas_macbook_pro_local.json: -------------------------------------------------------------------------------- 1 | {"id": "zack_zack_cerzas_macbook_pro_local", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBwJCoMbbxQrI9jM5I/lX0MYDB57LzYXDJTXqmLfJ8E5IHwQbn708/EFXAHuJlCyoJAuA8FaU/Y5/l2LBlVXwp9v+8ftk/D6AcsuQ+Hi0ZLCTvW10OUIS2cwX/of//JJIu6roy3r3SWwUcQvwjaZYLm7QkC+1StsLNMHdgiVRFiibqml72Q53VTtSTt6z2aXjrHzhpZq0hBK/13aknzAjHvGprQnchKVzhA/7A5pArF3CXpnI7aCwKvvjU0AcvqqD8WNLMpF4hFSoTK7xluKB+5UAJ36wefIO2KFM/zMwUtl4/aDhDdPeSaQRWq/q7viKLVNS0X9j2PjSRTfYhJNZr zack@zack-cerzas-macbook-pro.local"} 2 | -------------------------------------------------------------------------------- /data_bags/ssh-keys/sage_ped.json: -------------------------------------------------------------------------------- 1 | {"id": "sage_ped", "key": "ssh-dss AAAAB3NzaC1kc3MAAACBAJhK1wkU1Drfn+UiBwK0WDL2IDGXpSiAQy9yr62gwSCBo+BRK1+wEsZ55bnl1wOXbYTPNpjYdMHL6kHyWyOsU2L+TbndZRqlLF/7Czq8qxirguZlL0rGI0cU/wAbmPlTReu79iCpiWrAgVCz9D7JfknZc+WPe9l7gvo0xZxadmnJAAAAFQDU1Sd06gjxy7rVDcNuGo5avW7pDQAAAIAAuLKZfwlFV0ng47jXUwApKPuipJWs8nLFdZstHFNfvk+mLan7gm8mGP/+c//6VaHIgH+4SsSIXydPHW+noR1qSuS4pmtJJzKYW3yocRRYXg1M0SbG7yfuhrcU6qBeC3KS2ErJY+Z2DnXg/ILPN8IZK1mYyGKe8/1g/biqgXCqnwAAAIEAlNbOGFpxjX2LabXLfNsf4ICN7AsJVoh9Doneh0GNCQoBmiQOE1S7uOm8knJLeXgtCEZt35tlMX9yZpbFxjkCeni5fuimujU1AK08vJFpYfILV6MP5PynwAfNBMCFp5n1UENnpSkn8drXa4F0F/riZk+GDeiS1R61rdC+5eZ0ieI= sage@ped"} 2 | -------------------------------------------------------------------------------- /data_bags/ssh-keys/sage_autriche.json: -------------------------------------------------------------------------------- 1 | {"id": "sage_autriche", "key": "ssh-dss AAAAB3NzaC1kc3MAAACBANFvOX/l/Q0sOxp9CoRl4XXYHKQGriGGbAgLEfqQz/NydGijoGrgDugjeG4Yzmx75LHb90v5mJejvtXM6QD1E6KVZwrSaWEHoSXN5LgG1BMZLUhWMJZI1dwaRlNGhIDz3pGxwmh9VXA8fNh2DYdmIuKMr76T3FMV7GeGKBiIGS3RAAAAFQDwCvlxKWXtM/4gdT+Ujxx4rojOZQAAAIEAmDBwmmdxsIftzOfbx/zTkg5FDr/jg0sfwRlSu+KFQaUMJNmYpJZMT44x9UB/OqbQitb1RhvGAkg/qE6aM0i6TYWP//5LPt5aLpVW7HUbwUot5j53KU/SyOmVbXVvi0FbdFtCuABLpMuxlY4XNaFrXnDVDPVWElVVA28U/FE6KzEAAACBAIBhOzyKHrT4erxG2av9PZ7JeGsfiPluiNHyEicvu7uitil86oZK7JjBagK0BGx+QQMdy1OjqpVHXx5KzN2UkknfgmgWW42UfgOsDpYnoGFuTtI+Pm9UX5g/K9UqoHNs2F+ulyVKfM4vuQkTZKG0mYoq7AGgEFCCDrUMBxfudCLH sage@autriche"} 2 | -------------------------------------------------------------------------------- /data_bags/ssh-keys/sage_doctrine.json: -------------------------------------------------------------------------------- 1 | {"id": "sage_doctrine", "key": "ssh-dss AAAAB3NzaC1kc3MAAACBAKfzZai/1ud7t3PS9WuR6CiSw0vpNShC3m8VoHXAgcQ0LLcC+b/+OpOvKdNhIqnR4uW1Hrm+BEdlUND0lcpuuJ8xOe0UK6AXXVAjtReKR6VzghpT5RDfL4pzWXxq/YzzE1+Yia++g+fJAmst6W9SYq63z80fKDLoRTi18ZN2GCFpAAAAFQDmAmlaOxukcQzTCrq2IcRi7KlMNQAAAIBxUTY4vWfJMgG9JqMoLQfNi98Ouyb31XJnuAAlT/KVSJebDNiMV/b5L79pOYGUjEVXvK1w6YNf4O9gcRsn93oys7NgcWaEsXizmp/7yS8A27Os+6bcWvZdbQd6NeCP17R/swiuUa3DjEjFmjMmt0MGZ/kL4MacEVJPWgaubg9aRwAAAIEApgppgTz98GP9rquAwksULxWy1zOHO2CL3lwKPD/fOOTLltIP0QxvNmRXJJUZQGxa93OwtU8aLvBv8VHYqw2BfZZvpdooTU3kxi56nevKXXda4w2c1TdVTKKP+aai+ijMrUoWOJQmuD6BSe+egLaAMc27YnuhuuLESBcm/V4dak4= sage@doctrine"} 2 | -------------------------------------------------------------------------------- /data_bags/ssh-keys/dgallowa_dgallowa_csb.json: -------------------------------------------------------------------------------- 1 | {"id": "dgallowa_dgallowa_csb", "key": "ssh-dss AAAAB3NzaC1kc3MAAACBAN3MlkYQNsm1uB6uJQ7hWJ1Ro9Gusrhi/b/KshPMFh51VWBQn71LdCCjdrsE6FWs5FYE/hr7MITJe+si7aZCmuf8U9tc+oiYh3spXtRhJVIM80FOVRXUmt2XR8I+oNbpcFDayYu956Volafksk3oYjsO356swj7+Q+AUQVF4bDJRAAAAFQCkB2YZKItSDL7XfAl6ZmJ21vqUMwAAAIEAsZspNIUwssVJGO4r3M3BECWklWLEFviIjGVKdPWulN10eRCczZrH7vMOs8coHKDQqYdS2RejlojT03FjFAqeBiTeAlcmgHoev+vqTg2Yhqx3gROzNc6NmG887owCOU4j7LJQP6SX8I8EpSXCMNOZliv2ZokjX2DuRsuIDxQdc84AAACBAMAp++fcqmGS7x7ic2x4rhyiYmdqlMIl8lrM3GkVR6qGuIVBvOGl38ieJKcTBLeRgyC04uIMfk8P9lLvZw3UF97qH3V0imKfq8aYnBGxCv+IjtdOrR5k4hrUW+P0qipvU+4dgHlpqwTKB4MtD9uDxvhENWvqcCnOS5rI6dddaZT6 dgallowa@dgallowa.csb"} 2 | -------------------------------------------------------------------------------- /solo/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | cd "$(dirname "$0")" 5 | 6 | for package in chef ruby1.9.1 ruby1.9.1-dev build-essential; do 7 | if [ "$(dpkg --status -- $package|sed -n 's/^Status: //p')" != "install ok installed" ]; then 8 | # add a space after old values 9 | missing="${missing:+$missing }$package" 10 | fi 11 | done 12 | if [ -n "$missing" ]; then 13 | echo "$0: installing missing required packages: $missing" 1>&2 14 | sudo \ 15 | env DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical \ 16 | apt-get \ 17 | -q \ 18 | -o Dpkg::Options::=--force-confnew \ 19 | install \ 20 | --no-install-recommends \ 21 | --assume-yes \ 22 | -- \ 23 | $missing 24 | fi 25 | 26 | 27 | sudo chef-solo -c solo.rb -j solo.json 28 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/templates/default/interfaces-mira.erb: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED with Chef, DO NOT EDIT 2 | 3 | # The loopback network interface 4 | auto lo 5 | iface lo inet loopback 6 | 7 | auto eth0 8 | iface eth0 inet manual 9 | pre-up /etc/network/rename-if-by-mac -- "$IFACE" <%= @macs['1g1'] %> 10 | pre-up ip link set dev "$IFACE" up 11 | 12 | auto br-front 13 | iface br-front inet static 14 | bridge_ports eth0 15 | bridge_fd 9 16 | bridge_hello 2 17 | bridge_maxage 12 18 | bridge_stp off 19 | # "front" is special: instead of just the vlan trunk, it has a 20 | # native vlan for PXE; this IP address is useful for SSH 21 | address <%= @ips['front'] %> 22 | netmask 255.255.240.0 23 | gateway 10.214.128.1 24 | dns-domain sepia.ceph.com 25 | dns-search front.sepia.ceph.com 26 | dns-nameservers 10.214.128.4 10.214.128.5 27 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/recipes/ssh-keys.rb: -------------------------------------------------------------------------------- 1 | directory '/home/ubuntu/.ssh' do 2 | owner "ubuntu" 3 | group "ubuntu" 4 | mode "0755" 5 | end 6 | 7 | ruby_block "set up ssh keys" do 8 | block do 9 | names = data_bag('ssh-keys') 10 | f = File.open('/home/ubuntu/.ssh/authorized_keys.chef', 'w') do |f| 11 | names.each do |name| 12 | data = data_bag_item('ssh-keys', name) 13 | f.puts(data['key']) 14 | end 15 | end 16 | end 17 | end 18 | 19 | execute "merge authorized ssh keys" do 20 | command <<-'EOH' 21 | set -e 22 | set -- ~ubuntu/.ssh/authorized_keys.chef 23 | if [ -e ~ubuntu/.ssh/authorized_keys ]; then 24 | set -- "$@" ~ubuntu/.ssh/authorized_keys 25 | fi 26 | sort -u -o ~ubuntu/.ssh/authorized_keys.tmp -- "$@" 27 | chown ubuntu:ubuntu -- ~ubuntu/.ssh/authorized_keys.tmp 28 | mv -- ~ubuntu/.ssh/authorized_keys.tmp ~ubuntu/.ssh/authorized_keys 29 | EOH 30 | end 31 | 32 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/recipes/libvirt-dns.rb: -------------------------------------------------------------------------------- 1 | package 'python-webpy' 2 | package 'python-requests' 3 | package 'python-sqlalchemy' 4 | package 'python-mysqldb' 5 | package 'python-yaml' 6 | package 'python-lxml' 7 | package 'python-pyparsing' 8 | 9 | execute "Create ceph-libvirt-dns dir" do 10 | command <<-EOH 11 | mkdir -p /srv/inktank.com/ceph-libvirt-dns 12 | EOH 13 | end 14 | 15 | cookbook_file '/srv/inktank.com/ceph-libvirt-dns/ceph-libvirt-dns.py' do 16 | source "ceph-libvirt-dns.py" 17 | mode 0755 18 | owner "root" 19 | group "root" 20 | end 21 | 22 | cookbook_file '/srv/inktank.com/ceph-libvirt-dns/parser.py' do 23 | source "parser.py" 24 | mode 0755 25 | owner "root" 26 | group "root" 27 | end 28 | 29 | cookbook_file '/etc/init/ceph-libvirt-dns.conf' do 30 | source "ceph-libvirt-dns.conf" 31 | mode 0755 32 | owner "root" 33 | group "root" 34 | end 35 | 36 | execute "Start ceph-libvirt-dns service" do 37 | command <<-EOH 38 | start ceph-libvirt-dns 39 | EOH 40 | end 41 | 42 | -------------------------------------------------------------------------------- /solo/solo-from-scratch: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This file is not chmod a+x on purpose, to avoid running it 4 | # accidentally. Automation will always run it through 5 | # 6 | # wget -q -O- https://raw.github.com/ceph/cookbook-vm-general/master/solo/solo-from-scratch | sh 7 | 8 | set -e 9 | 10 | for package in git; do 11 | if [ "$(dpkg --status -- $package|sed -n 's/^Status: //p')" != "install ok installed" ]; then 12 | # add a space after old values 13 | missing="${missing:+$missing }$package" 14 | fi 15 | done 16 | if [ -n "$missing" ]; then 17 | echo "solo-from-scratch: installing missing required packages: $missing" 1>&2 18 | sudo \ 19 | env DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical \ 20 | apt-get \ 21 | -q \ 22 | -o Dpkg::Options::=--force-confnew \ 23 | install \ 24 | --no-install-recommends \ 25 | --assume-yes \ 26 | -- \ 27 | $missing 28 | fi 29 | 30 | SCRATCH="$(mktemp -d --tmpdir 'solo-from-scratch.XXXXXXXXXXXX')" 31 | cd "$SCRATCH" 32 | 33 | cleanup () { 34 | rm -rf "$SCRATCH" 35 | } 36 | 37 | trap cleanup INT TERM EXIT 38 | git init 39 | git pull https://github.com/ceph/cookbook-vm-general.git 40 | ./solo/run 41 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/templates/default/interfaces.erb: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED with Chef, DO NOT EDIT 2 | 3 | # The loopback network interface 4 | auto lo 5 | iface lo inet loopback 6 | 7 | 8 | auto eth0 9 | iface eth0 inet manual 10 | pre-up /etc/network/rename-if-by-mac -- "$IFACE" <%= @macs['1g1'] %> 11 | pre-up ip link set dev "$IFACE" up 12 | 13 | auto br-front 14 | iface br-front inet static 15 | bridge_ports eth0 16 | bridge_fd 9 17 | bridge_hello 2 18 | bridge_maxage 12 19 | bridge_stp off 20 | # "front" is special: instead of just the vlan trunk, it has a 21 | # native vlan for PXE; this IP address is useful for SSH 22 | address <%= @ips['front'] %> 23 | netmask 255.255.240.0 24 | gateway 10.214.128.1 25 | dns-domain sepia.ceph.com 26 | dns-search front.sepia.ceph.com 27 | dns-nameservers 10.214.128.4 10.214.128.5 28 | 29 | 30 | # "back" side: trunk with native vlan 31 | auto eth2 32 | iface eth2 inet manual 33 | pre-up /etc/network/rename-if-by-mac -- "$IFACE" <%= @macs['10g2'] %> 34 | # ixgbe README warns against bridging/routing and LRO/GRO 35 | # http://downloadmirror.intel.com/14687/eng/README.txt 36 | pre-up ethtool -K "$IFACE" gro off lro off 37 | pre-up ip link set dev "$IFACE" up 38 | 39 | auto br-back 40 | iface br-back inet static 41 | bridge_ports eth2 42 | bridge_fd 9 43 | bridge_hello 2 44 | bridge_maxage 12 45 | bridge_stp off 46 | address <%= @ips['back'] %> 47 | netmask 255.255.240.0 48 | 49 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/recipes/networking-mira.rb: -------------------------------------------------------------------------------- 1 | package 'ethtool' 2 | package 'bridge-utils' 3 | 4 | front_mac = IO.popen('ifconfig eth0 | grep -i hwadd | awk "{print \$5}"').read.chop 5 | 6 | front_ip = IO.popen('ip addr show dev eth0 | grep -w inet | awk "{print \$2}" | cut -d"/" -f1').read.chop 7 | 8 | need_run = ! File.open('/etc/network/interfaces').read() =~ /br-front/ 9 | 10 | GENERIC_MACS = { 11 | node['hostname'] => { 12 | '1g1' => front_mac, 13 | }, 14 | } 15 | 16 | GENERIC_IPS = { 17 | node['hostname'] => { 18 | 'front' => front_ip, 19 | }, 20 | } 21 | 22 | cookbook_file '/etc/network/rename-if-by-mac' do 23 | backup false 24 | owner 'root' 25 | group 'root' 26 | mode 0755 27 | end 28 | 29 | 30 | # generate a .chef file from a template, and then be extra careful in 31 | # swapping it in place; effecting changes over ssh is DANGEROUS, 32 | # please have a serial console handy 33 | template '/etc/network/interfaces.chef' do 34 | source 'interfaces-mira.erb' 35 | mode 0644 36 | variables( 37 | 'macs' => GENERIC_MACS[node['hostname']], 38 | 'ips' => GENERIC_IPS[node['hostname']], 39 | ) 40 | only_if need_run 41 | end 42 | 43 | execute "activate network config" do 44 | command <<-'EOH' 45 | set -e 46 | ifdown -a 47 | mv /etc/network/interfaces.chef /etc/network/interfaces 48 | ifup -a 49 | EOH 50 | # don't run the ifdown/ifup if there's no change to the file 51 | only_if {need_run && "cmp /etc/network/interfaces.chef /etc/network/interfaces"} 52 | end 53 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/recipes/networking.rb: -------------------------------------------------------------------------------- 1 | package 'ethtool' 2 | package 'bridge-utils' 3 | 4 | 5 | front_mac = IO.popen('ifconfig eth0 | grep -i hwadd | awk "{print \$5}"').read.chop 6 | back_mac = IO.popen('ifconfig eth2 | grep -i hwadd | awk "{print \$5}"').read.chop 7 | 8 | front_ip = IO.popen('ip addr show dev eth0 | grep -w inet | awk "{print \$2}" | cut -d"/" -f1').read.chop 9 | back_ip = IO.popen('ip addr show dev eth2 | grep -w inet | awk "{print \$2}" | cut -d"/" -f1').read.chop 10 | 11 | need_run = ! File.open('/etc/network/interfaces').read() =~ /br-front/ 12 | 13 | GENERIC_MACS = { 14 | node['hostname'] => { 15 | '1g1' => front_mac, 16 | '10g1' => back_mac, 17 | }, 18 | } 19 | 20 | GENERIC_IPS = { 21 | node['hostname'] => { 22 | 'front' => front_ip, 23 | 'back' => back_ip, 24 | }, 25 | } 26 | 27 | cookbook_file '/etc/network/rename-if-by-mac' do 28 | backup false 29 | owner 'root' 30 | group 'root' 31 | mode 0755 32 | end 33 | 34 | 35 | # generate a .chef file from a template, and then be extra careful in 36 | # swapping it in place; effecting changes over ssh is DANGEROUS, 37 | # please have a serial console handy 38 | template '/etc/network/interfaces.chef' do 39 | source 'interfaces.erb' 40 | mode 0644 41 | variables( 42 | 'macs' => GENERIC_MACS[node['hostname']], 43 | 'ips' => GENERIC_IPS[node['hostname']], 44 | ) 45 | only_if need_run 46 | end 47 | 48 | execute "activate network config" do 49 | command <<-'EOH' 50 | set -e 51 | ifdown -a 52 | mv /etc/network/interfaces.chef /etc/network/interfaces 53 | ifup -a 54 | EOH 55 | # don't run the ifdown/ifup if there's no change to the file 56 | only_if {need_run && "cmp /etc/network/interfaces.chef /etc/network/interfaces"} 57 | end 58 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/files/default/parser.py: -------------------------------------------------------------------------------- 1 | from pyparsing import ( 2 | CharsNotIn, 3 | Combine, 4 | Keyword, 5 | Literal, 6 | ParseSyntaxException, 7 | TokenConverter, 8 | Word, 9 | alphas, 10 | hexnums, 11 | nestedExpr, 12 | nums, 13 | quotedString, 14 | ) 15 | 16 | 17 | # ungroup is only available in pyparsing >=1.5.6 18 | try: 19 | from pyparsing import ungroup 20 | _ungroup = ungroup 21 | except ImportError: 22 | def _ungroup(expr): 23 | return TokenConverter(expr).setParseAction(lambda t: t[0]) 24 | 25 | 26 | def dictify(s, loc, toks): 27 | return dict(toks) 28 | 29 | _p_ip_address = Combine(Word(nums) - ('.' + Word(nums)) * 3) 30 | 31 | _p_lease_deleted = Literal("deleted") 32 | _p_lease_deleted.setParseAction(lambda s, loc, toks: True) 33 | 34 | _p_lease_active = Literal("binding state active") 35 | _p_lease_active.setParseAction(lambda s, loc, toks: True) 36 | 37 | _p_hex_digit = Word(hexnums, exact=2) 38 | _p_mac = Combine( 39 | _p_hex_digit + ':' + _p_hex_digit + ':' + _p_hex_digit + ':' 40 | + _p_hex_digit + ':' + _p_hex_digit + ':' + _p_hex_digit) 41 | 42 | _p_lease_hardware_ethernet = _ungroup( 43 | Keyword("hardware").suppress() 44 | + Keyword("ethernet").suppress() 45 | + _p_mac 46 | ) 47 | 48 | _p_lease_junk = ( 49 | Word(alphas) 50 | # if we include { } ; here, they become greedy and eat the closing 51 | # brace or semicolon 52 | + CharsNotIn('{};') 53 | ).suppress() 54 | 55 | _p_lease_decl = ( 56 | _p_lease_deleted.setResultsName('deleted') 57 | | _p_lease_active.setResultsName('active') 58 | | _p_lease_hardware_ethernet.setResultsName('mac') 59 | | _p_lease_junk 60 | ) + Literal(';').suppress() 61 | 62 | _p_lease = ( 63 | Keyword("lease").suppress() 64 | + _p_ip_address.setResultsName('ip') 65 | + _ungroup( 66 | nestedExpr( 67 | opener='{', 68 | closer='}', 69 | content=_p_lease_decl, 70 | ignoreExpr=quotedString, 71 | ), 72 | ) 73 | ).setParseAction(dictify) 74 | 75 | 76 | def parse(s): 77 | g = _p_lease.scanString(s) 78 | while True: 79 | try: 80 | (toks, start, end) = next(g) 81 | except StopIteration: 82 | break 83 | except ParseSyntaxException: 84 | # hide errors from callers; probably encountered the last, 85 | # partial statement while dhcpd is appending to the file; 86 | # stop here, inotify will wake up the caller as the next 87 | # chunk arrives 88 | break 89 | else: 90 | (tok,) = toks 91 | yield tok 92 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/files/default/rename-if-by-mac: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # nameif is not useful when virtual interfaces are inheriting the mac 4 | # from the physical interface; e.g. after a "ip link add link ... name 5 | # ... type vlan id ...", it will try to rename the vlan interface, 6 | # collides with the physical interface, and aborts 7 | 8 | import argparse 9 | import os 10 | import re 11 | import subprocess 12 | import sys 13 | 14 | PROG = os.path.basename(sys.argv[0]) 15 | 16 | class Fail(SystemExit): 17 | def __init__(self, msg, **kwargs): 18 | msg = msg.format(**kwargs) 19 | super(Fail, self).__init__('{prog}: {msg}'.format( 20 | prog=PROG, 21 | msg=msg, 22 | )) 23 | 24 | LINK_RE = re.compile(r""" 25 | # ifindex 26 | ^\d+:[ ] 27 | # iface name 28 | (?P.*?):[ ] 29 | # uninteresting 30 | .*[ ] 31 | # mac address 32 | link/ether\ (?P[0-9a-f][0-9a-f](?::[0-9a-f][0-9a-f]){5}) 33 | # tailing uninterestingness 34 | [ ] 35 | """, re.MULTILINE|re.VERBOSE) 36 | 37 | def get_state(): 38 | output = subprocess.check_output(['ip', '-o', 'link', 'show']) 39 | matches = LINK_RE.findall(output) 40 | links = dict(matches) 41 | return links 42 | 43 | 44 | def main(): 45 | parser = argparse.ArgumentParser( 46 | description='Rename interface based on MAC address', 47 | ) 48 | parser.add_argument( 49 | 'iface', 50 | help='Desired interface name', 51 | ) 52 | parser.add_argument( 53 | 'mac', 54 | help='MAC address to look for', 55 | ) 56 | args = parser.parse_args() 57 | 58 | state = get_state() 59 | 60 | cur = state.get(args.iface) 61 | if cur is not None: 62 | if cur != args.mac: 63 | raise Fail( 64 | 'Interface exists with wrong MAC: {iface} {mac}', 65 | iface=args.iface, 66 | mac=cur, 67 | ) 68 | 69 | # all done here 70 | return 71 | 72 | # no interface by that name exists currently; look for it 73 | candidates = [iface 74 | for (iface,mac) in state.iteritems() 75 | if mac == args.mac] 76 | if not candidates: 77 | raise Fail( 78 | 'Cannot find any interface with MAC: {mac}', 79 | mac=args.mac, 80 | ) 81 | 82 | if len(candidates) > 1: 83 | only_eth = [iface 84 | for iface in candidates 85 | if iface.startswith('eth')] 86 | if only_eth: 87 | candidates = only_eth 88 | 89 | if len(candidates) > 1: 90 | raise Fail( 91 | 'Several candidate interfaces, aborting: {ifaces}', 92 | ifaces=' '.join(candidates), 93 | ) 94 | 95 | # and finally do it 96 | iface = candidates[0] 97 | os.execvp('ip', ['ip', 'link', 'set', iface, 'name', args.iface]) 98 | 99 | if __name__ == '__main__': 100 | import sys 101 | sys.exit(main()) 102 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/recipes/libvirt.rb: -------------------------------------------------------------------------------- 1 | # workaround for bug https://bugs.launchpad.net/ubuntu/+source/libvirt/+bug/1018956 2 | execute 'set up libvirt pool default' do 3 | command <<-'EOH' 4 | set -e 5 | if ! virsh pool-uuid default >/dev/null 2>/dev/null; then 6 | # does not exist 7 | virsh pool-define-as --name default dir --target /var/lib/libvirt/images 8 | fi 9 | virsh -q pool-info default | while read line; do 10 | case "$line" in 11 | State:\ *inactive) 12 | virsh pool-start default 13 | ;; 14 | Autostart:\ *no) 15 | virsh pool-autostart default 16 | ;; 17 | esac 18 | done 19 | EOH 20 | end 21 | 22 | # Remove default pool and crete vpm pools if vpm host: 23 | execute 'Setting up teuthology VM pools' do 24 | command <<-'EOH' 25 | set -e 26 | if [ -d /srv/libvirtpool ] 27 | then 28 | cd /srv/libvirtpool 29 | virsh pool-destroy default 30 | virsh pool-undefine default 31 | for pool in * 32 | do 33 | sudo virsh pool-define-as --name $pool --type dir --target /srv/libvirtpool/$pool 34 | sudo virsh pool-autostart $pool 35 | sudo virsh pool-build $pool 36 | sudo virsh pool-start $pool 37 | done 38 | fi 39 | EOH 40 | end 41 | 42 | 43 | directory '/srv/chef' do 44 | owner 'root' 45 | group 'root' 46 | mode 0755 47 | end 48 | 49 | cookbook_file '/srv/chef/libvirt-net-pub.xml' do 50 | owner 'root' 51 | group 'root' 52 | mode 0644 53 | end 54 | 55 | execute 'set up libvirt network pub' do 56 | command <<-'EOH' 57 | set -e 58 | if ! virsh net-uuid pub >/dev/null 2>/dev/null; then 59 | # does not exist 60 | virsh net-define /srv/chef/libvirt-net-pub.xml 61 | fi 62 | virsh -q net-info pub | while read line; do 63 | case "$line" in 64 | Active:\ *no) 65 | virsh net-start pub 66 | ;; 67 | Autostart:\ *no) 68 | virsh net-autostart pub 69 | ;; 70 | esac 71 | done 72 | EOH 73 | end 74 | 75 | cookbook_file '/srv/chef/libvirt-net-front.xml' do 76 | owner 'root' 77 | group 'root' 78 | mode 0644 79 | end 80 | 81 | 82 | execute 'set up libvirt network front' do 83 | command <<-'EOH' 84 | set -e 85 | if ! virsh net-uuid front >/dev/null 2>/dev/null; then 86 | # does not exist 87 | virsh net-define /srv/chef/libvirt-net-front.xml 88 | fi 89 | virsh -q net-info front | while read line; do 90 | case "$line" in 91 | Active:\ *no) 92 | virsh net-start front 93 | ;; 94 | Autostart:\ *no) 95 | virsh net-autostart front 96 | ;; 97 | esac 98 | done 99 | EOH 100 | end 101 | 102 | 103 | if !node['hostname'].match(/^(mira|irvingi)/) 104 | cookbook_file '/srv/chef/libvirt-net-back.xml' do 105 | owner 'root' 106 | group 'root' 107 | mode 0644 108 | end 109 | 110 | execute 'set up libvirt network back' do 111 | command <<-'EOH' 112 | set -e 113 | if ! virsh net-uuid back >/dev/null 2>/dev/null; then 114 | # does not exist 115 | virsh net-define /srv/chef/libvirt-net-back.xml 116 | fi 117 | virsh -q net-info back | while read line; do 118 | case "$line" in 119 | Active:\ *no) 120 | virsh net-start back 121 | ;; 122 | Autostart:\ *no) 123 | virsh net-autostart back 124 | ;; 125 | esac 126 | done 127 | EOH 128 | end 129 | end 130 | 131 | 132 | execute 'allow libvirt for user ubuntu' do 133 | command <<-'EOH' 134 | set -e 135 | gpasswd -a ubuntu libvirtd 136 | EOH 137 | end 138 | 139 | 140 | # TODO refactor into a libvirt_interface LWR? 141 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].each do |num| 142 | 143 | template "/srv/chef/libvirt-net-isolated#{num}.xml" do 144 | source 'libvirt-net-isolated.xml.erb' 145 | owner 'root' 146 | group 'root' 147 | mode 0644 148 | variables( 149 | 'name' => "isolated#{num}", 150 | ) 151 | end 152 | 153 | execute "set up libvirt network isolated#{num}" do 154 | environment ({ 155 | 'NET' => "isolated#{num}", 156 | }) 157 | command <<-'EOH' 158 | set -e 159 | if ! virsh net-uuid "$NET" >/dev/null 2>/dev/null; then 160 | # does not exist 161 | virsh net-define /srv/chef/libvirt-net-"$NET".xml 162 | fi 163 | virsh -q net-info "$NET" | while read line; do 164 | case "$line" in 165 | Active:\ *no) 166 | virsh net-start "$NET" 167 | ;; 168 | Autostart:\ *no) 169 | virsh net-autostart "$NET" 170 | ;; 171 | esac 172 | done 173 | EOH 174 | end 175 | 176 | end 177 | -------------------------------------------------------------------------------- /cookbooks/cephco-generic/files/default/ceph-libvirt-dns.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import argparse 3 | import requests 4 | import re 5 | import sys 6 | import time 7 | import datetime 8 | import traceback 9 | import sqlalchemy as sq 10 | import web 11 | import yaml 12 | import libvirt 13 | import json 14 | from lxml import etree 15 | import parser 16 | import os 17 | import subprocess 18 | 19 | url = 'http://10.214.128.1:8080/' 20 | domain = 'front.sepia.ceph.com' 21 | sleepinterval = 40 22 | guestlist = [] 23 | 24 | def parse_args(): 25 | parser = argparse.ArgumentParser( 26 | description='DNS for libvirt', 27 | ) 28 | parser.add_argument( 29 | '--config', 30 | metavar='CONFIGFILE', 31 | help='path to YAML config file', 32 | ) 33 | parser.add_argument( 34 | '--server', 35 | action='store_true', default=False, 36 | help='Run as the server (http/sql access).', 37 | ) 38 | parser.add_argument( 39 | 'remainder', 40 | nargs=argparse.REMAINDER, 41 | help='Remainder arguments for webpy, ip:port for listen address' 42 | ) 43 | args = parser.parse_args() 44 | return args 45 | 46 | def read_config(path): 47 | if path is None: 48 | raise NameError('Configuration file not specified with --config or cant be opened') 49 | else: 50 | with file(path) as f: 51 | obj = yaml.safe_load(f) 52 | assert 'config' in obj 53 | return obj['config'] 54 | 55 | 56 | urls = ( 57 | '/', 'index' 58 | ) 59 | 60 | def parseleases(s): 61 | leases = {} 62 | for l in parser.parse(s): 63 | if 'active' in l: 64 | if 'mac' in l: 65 | assert 'mac' in l 66 | leases[l['mac']] = l 67 | return leases 68 | 69 | def add(leases, dburl, name, mac): 70 | my_domain_id = 1 71 | db = sq.create_engine(dburl) 72 | metadata = sq.MetaData(bind=db) 73 | records_table = sq.Table('records', metadata, 74 | sq.Column('id', sq.Integer, primary_key=True), 75 | sq.Column('domain_id', sq.Integer), 76 | sq.Column('name', sq.String), 77 | sq.Column('type', sq.String), 78 | sq.Column('content', sq.String), 79 | sq.Column('ttl', sq.Integer), 80 | sq.Column('prio', sq.Integer), 81 | sq.Column('change_date', sq.Integer), 82 | sq.Column('ordername', sq.String), 83 | sq.Column('auth', sq.Integer), 84 | sq.Column('propernoun_epoch', sq.Integer), 85 | ) 86 | 87 | ip = leases[mac]['ip'] 88 | existcheck = sq.select([records_table], records_table.c.name==name).limit(1).execute().fetchone() 89 | if existcheck is None: 90 | state = "Added" 91 | ins = records_table.insert() 92 | db.execute(ins, domain_id=my_domain_id, name=name, type="A", content=ip, ttl="30", auth="1") 93 | else: 94 | if existcheck[4] == ip: 95 | state = "No Change" 96 | else: 97 | state = "Updated" 98 | records_table.update().where(records_table.c.name==name).values(content=ip).execute() 99 | returnstring = " " + state + " HOSTNAME: "+name+" MAC: "+mac+" IP: "+ip 100 | return returnstring 101 | 102 | def delete(dburl, name): 103 | my_domain_id = 1 104 | db = sq.create_engine(dburl) 105 | metadata = sq.MetaData(bind=db) 106 | records_table = sq.Table('records', metadata, 107 | sq.Column('id', sq.Integer, primary_key=True), 108 | sq.Column('domain_id', sq.Integer), 109 | sq.Column('name', sq.String), 110 | sq.Column('type', sq.String), 111 | sq.Column('content', sq.String), 112 | sq.Column('ttl', sq.Integer), 113 | sq.Column('prio', sq.Integer), 114 | sq.Column('change_date', sq.Integer), 115 | sq.Column('ordername', sq.String), 116 | sq.Column('auth', sq.Integer), 117 | sq.Column('propernoun_epoch', sq.Integer), 118 | ) 119 | state = 'Deleting' 120 | records_table.delete().where(records_table.c.name==name).execute() 121 | returnstring = " " + state + " HOSTNAME: "+name 122 | return returnstring 123 | 124 | class index: 125 | def GET(self): 126 | i = web.input() 127 | print i 128 | s = open(web.leasefile).read() 129 | leases = parseleases(s) 130 | string = '' 131 | for guest in i: 132 | if '|' not in i[guest]: 133 | print "Client sent junk data" 134 | else: 135 | name = i[guest].split('|')[0] 136 | mac = i[guest].split('|')[1] 137 | hostname = name + '.' + domain 138 | if mac in leases: 139 | string = string + '\n' + add(leases, web.dburl, name=hostname, mac=mac) 140 | else: 141 | #Delete entry mac string 142 | if mac == 'FF:FF:FF:FF:FF:FF': 143 | string = string + '\n' + delete(web.dburl, name=hostname) 144 | else: 145 | string = string + '\n ' + "Error: IP for: " + name + " not found from MAC: " + mac 146 | return string 147 | 148 | def is_gitbuilder(name): 149 | return name.startswith('gitbuilder-') 150 | 151 | def getLXCstring(): 152 | returnstring = "" 153 | dir = "/var/lib/lxc" 154 | if os.path.exists(dir): 155 | if os.listdir(dir): 156 | for o in os.listdir(dir): 157 | if os.path.isdir(dir + "/" + o): 158 | name = o 159 | if os.path.isfile(dir + "/" + o + "/config"): 160 | contents = open(dir + "/" + o + "/config").read() 161 | if "br-front" in contents: 162 | for line in open(dir + "/" + o + "/config").readlines(): 163 | if re.search('lxc.network.hwaddr', line): 164 | mac = line.split()[2] 165 | lxcinfo = subprocess.Popen(['lxc-info', '-n', name] ,stdout=subprocess.PIPE).stdout.read() 166 | if "RUNNING" in lxcinfo and not is_gitbuilder(name): 167 | returnstring = returnstring + name + '=' + name + '|' + mac + '&' 168 | else: 169 | return returnstring 170 | else: 171 | return returnstring 172 | return returnstring 173 | 174 | def getAllDomains(conn): 175 | """ 176 | List and get all domains, active or not. 177 | 178 | The python bindings don't seem to have this at version 179 | 0.9.8-2ubuntu17.1 and a combination of listDefinedDomains and 180 | listDomainsID is just miserable. 181 | 182 | http://libvirt.org/html/libvirt-libvirt.html#virConnectListAllDomains 183 | 184 | Also fetch the actual domain object, as we'll need the xml. 185 | """ 186 | for name in conn.listDefinedDomains(): 187 | try: 188 | domain = conn.lookupByName(name) 189 | except libvirt.libvirtError as e: 190 | if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN: 191 | # lost a race, someone undefined the domain 192 | # between listing names and fetching details 193 | pass 194 | else: 195 | raise 196 | else: 197 | yield domain 198 | 199 | for id_ in conn.listDomainsID(): 200 | try: 201 | domain = conn.lookupByID(id_) 202 | except libvirt.libvirtError as e: 203 | if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN: 204 | # lost a race, someone undefined the domain 205 | # between listing names and fetching details 206 | pass 207 | else: 208 | raise 209 | else: 210 | yield domain 211 | 212 | 213 | def get_interfaces(tree): 214 | networks = tree.xpath( 215 | "/domain/devices/interface[@type='network']", 216 | ) 217 | for net in networks: 218 | (name,) = net.xpath('./source/@network') 219 | (mac,) = net.xpath('./mac/@address') 220 | yield (name, mac) 221 | 222 | 223 | def _handle_event(conn, domain, event, detail, getstring): 224 | msg = dict( 225 | type='libvirt', 226 | vm=dict( 227 | name=domain.name(), 228 | uuid=domain.UUIDString(), 229 | ), 230 | ) 231 | if event == libvirt.VIR_DOMAIN_EVENT_DEFINED: 232 | xml_s = domain.XMLDesc(flags=2) 233 | tree = etree.fromstring(xml_s) 234 | ifaces = get_interfaces(tree) 235 | ifaces = list(ifaces) 236 | msg['vm']['interfaces'] = ifaces 237 | elif event == libvirt.VIR_DOMAIN_EVENT_UNDEFINED: 238 | pass 239 | else: 240 | print >>sys.stderr, \ 241 | ('unknown event:' 242 | + ' Domain {name} event={event} detail={detail}'.format( 243 | name=domain.name(), 244 | event=event, 245 | detail=detail, 246 | ) 247 | ) 248 | return 249 | name = '' 250 | for int in ifaces: 251 | name = domain.name() 252 | if 'front' or 'br-front' in int: 253 | mac = int[1] 254 | getstring = getstring + name + '=' + name + '|' + mac + '&' 255 | return getstring, name 256 | 257 | 258 | def libvirt_list_and_update_dns(guestlist): 259 | uri = 'qemu:///system' 260 | try: 261 | conn = libvirt.openReadOnly(uri) 262 | except Exception: 263 | print "Something went wrong connecting to libvirt. Is libvirt-bin installed/running? Sleeping for 5 minutes" 264 | time.sleep(300) 265 | return False 266 | pass 267 | 268 | getstring = '' 269 | deletestring = '' 270 | 271 | guests = [] 272 | for domain in getAllDomains(conn): 273 | if is_gitbuilder(guest) 274 | continue 275 | getstring, guest = _handle_event( 276 | conn=conn, 277 | domain=domain, 278 | event=libvirt.VIR_DOMAIN_EVENT_DEFINED, 279 | detail=None, 280 | getstring=getstring 281 | ) 282 | guests.append(guest) 283 | 284 | #If a guest was removed give it a fake mac so the server will delete the DNS entry 285 | for guest in guestlist: 286 | if guest not in guests: 287 | getstring = getstring + guest + '=' + guest + '|FF:FF:FF:FF:FF:FF&' 288 | 289 | getstring = getstring.rstrip('&') 290 | lxcstring = getLXCstring().rstrip('&') 291 | if lxcstring != '': 292 | getstring = getstring + "&" + lxcstring 293 | 294 | if getstring == '': 295 | print "Host has no guests with front network bridging. Not contacting server." 296 | return False, guests 297 | 298 | try: 299 | update = requests.get(url + '?' + getstring) 300 | except Exception: 301 | print "Contacting the server failed. Is it down? Sleeping for 5 minutes" 302 | time.sleep(300) 303 | return False, guests 304 | 305 | try: 306 | exception = update.raise_for_status() 307 | except Exception: 308 | print "Server response was abnormal (non 200) Sleeping for 5 minutes" 309 | traceback.print_exc() 310 | time.sleep(300) 311 | return False, guests 312 | 313 | print 'Server Response:'+update.content 314 | return True, guests 315 | 316 | def main(guestlist): 317 | args = parse_args() 318 | 319 | if args.server: 320 | print "Running in Server Mode:" 321 | config = read_config(args.config) 322 | web.dburl = config['database'] 323 | web.leasefile = config['leasefile'] 324 | # Webpy also uses Arguments. Replace sys.argv with argument 0 (script name) and add unused arguments by argparse) 325 | sys.argv = [sys.argv[0]] + args.remainder 326 | app = web.application(urls, globals()) 327 | app.run() 328 | else: 329 | print "Running in Client Mode:" 330 | while True: 331 | print datetime.datetime.now() 332 | updated, guestlist = libvirt_list_and_update_dns(guestlist) 333 | if updated == False: 334 | sleepinterval = 5 335 | else: 336 | sleepinterval = 40 337 | print "Sleeping for "+str(sleepinterval)+" Seconds..." 338 | time.sleep(sleepinterval) 339 | 340 | if __name__ == "__main__": 341 | main(guestlist) 342 | --------------------------------------------------------------------------------