diff options
| author | Christian Berendt <berendt@betacloud-solutions.de> | 2016-10-14 12:55:26 +0200 |
|---|---|---|
| committer | Christian Berendt <berendt@betacloud-solutions.de> | 2016-10-14 12:57:49 +0200 |
| commit | b725bd09dd73edf22bdd67bd13359e2a7d8b0796 (patch) | |
| tree | b088b5f48a51df9dfb40938b1f573f0b6f1685d5 /contrib/dev | |
| parent | dbf05f076fb2394439afcee95de3922a83f92219 (diff) | |
| download | kolla-ansible-b725bd09dd73edf22bdd67bd13359e2a7d8b0796.tar.gz kolla-ansible-b725bd09dd73edf22bdd67bd13359e2a7d8b0796.zip | |
Move demos and dev directory into a new contrib directory
TrivialFix
Change-Id: Ifb8f19afa1514488bd288670d08b96f84982a4e1
Diffstat (limited to 'contrib/dev')
| -rw-r--r-- | contrib/dev/vagrant/Vagrantfile | 252 | ||||
| -rw-r--r-- | contrib/dev/vagrant/Vagrantfile.custom.example | 92 | ||||
| -rw-r--r-- | contrib/dev/vagrant/bootstrap.sh | 226 | ||||
| -rw-r--r-- | contrib/dev/vagrant/newest_dhcp_lease.py | 134 |
4 files changed, 704 insertions, 0 deletions
diff --git a/contrib/dev/vagrant/Vagrantfile b/contrib/dev/vagrant/Vagrantfile new file mode 100644 index 000000000..f59fafaec --- /dev/null +++ b/contrib/dev/vagrant/Vagrantfile @@ -0,0 +1,252 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +require "ipaddr" + +# Check for required plugin(s) +['vagrant-hostmanager'].each do |plugin| + unless Vagrant.has_plugin?(plugin) + raise "#{plugin} plugin not found. Please install it via 'vagrant plugin install #{plugin}'" + end +end + +class VagrantConfigMissing < StandardError +end + +vagrant_dir = File.expand_path(File.dirname(__FILE__)) + +# Vagrantfile.custom contains user customization for the Vagrantfile +# You shouldn't have to edit the Vagrantfile, ever. +if File.exists?(File.join(vagrant_dir, 'Vagrantfile.custom')) + eval(IO.read(File.join(vagrant_dir, 'Vagrantfile.custom')), binding) +end + +# Either libvirt or virtualbox +PROVIDER ||= "libvirt" +# Either centos or ubuntu +DISTRO ||= "centos" + +# The libvirt graphics_ip used for each guest. Only applies if PROVIDER +# is libvirt. +GRAPHICSIP ||= "127.0.0.1" + +# The bootstrap.sh provision_script requires CentOS 7 or Ubuntu 15.10. +# Provisioning other boxes than the default ones may therefore +# require changes to bootstrap.sh. +PROVISION_SCRIPT ||= "bootstrap.sh" + +PROVIDER_DEFAULTS ||= { + libvirt: { + centos: { + base_image: "centos/7", + bridge_interface: "virbr0", + vagrant_shared_folder: "/home/vagrant/sync", + sync_method: "nfs", + kolla_path: "/home/vagrant/kolla" + } + }, + virtualbox: { + centos: { + base_image: "puppetlabs/centos-7.0-64-puppet", + bridge_interface: "wlp3s0b1", + vagrant_shared_folder: "/home/vagrant/sync", + sync_method: "virtualbox", + kolla_path: "/home/vagrant/kolla" + }, + ubuntu: { + base_image: "ubuntu/wily64", + bridge_interface: "wlp3s0b1", + vagrant_shared_folder: "/home/vagrant/sync", + sync_method: "virtualbox", + kolla_path: "/home/vagrant/kolla" + } + } +} + +# Whether the host network adapter is Wi-Fi. +# On VirtualBox, the user must first manually create a NAT-Network +# named "OSNetwork". The default network CIDR must be changed. +# The Neutron external interface will be connected to this Network. +WIFI = false unless self.class.const_defined?(:WIFI) + +# Whether to do Multi-node or All-in-One deployment +MULTINODE = false unless self.class.const_defined?(:MULTINODE) + +# The following is only used when deploying in Multi-nodes +NUMBER_OF_CONTROL_NODES ||= 3 +NUMBER_OF_COMPUTE_NODES ||= 1 +NUMBER_OF_STORAGE_NODES ||= 1 +NUMBER_OF_NETWORK_NODES ||= 1 + +NODE_SETTINGS ||= { + aio: { + cpus: 4, + memory: 4096 + }, + operator: { + cpus: 1, + memory: 1024 + }, + control: { + cpus: 1, + memory: 2048 + }, + compute: { + cpus: 1, + memory: 1024 + }, + storage: { + cpus: 1, + memory: 1024 + }, + network: { + cpus: 1, + memory: 1024 + } +} + +# Configure a new SSH key and config so the operator is able to connect with +# the other cluster nodes. +unless File.file?(File.join(vagrant_dir, 'vagrantkey')) + system("ssh-keygen -f #{File.join(vagrant_dir, 'vagrantkey')} -N '' -C this-is-vagrant") +end + +def get_default(setting) + PROVIDER_DEFAULTS[PROVIDER.to_sym][DISTRO.to_sym][setting] +rescue + raise VagrantConfigMissing, + "Missing configuration for PROVIDER_DEFAULTS[#{PROVIDER}][#{DISTRO}][#{setting}]" +end + +def get_setting(node, setting) + NODE_SETTINGS[node][setting] +rescue + raise VagrantConfigMissing, + "Missing configuration for NODE_SETTINGS[#{node}][#{setting}]" +end + +def configure_wifi_vbox_networking(vm) + # Even if adapters 1 & 2 don't need to be modified, if the order is to be + # maintained, some modification has to be done to them. This will maintain + # the association inside the guest OS: NIC1 -> eth0, NIC2 -> eth1, NIC3 -> + # eht2. The modifications for adapters 1 & 2 only change optional properties. + # Adapter 3 is enabled and connected to the NAT-Network named "OSNetwork", + # while also changing its optional properties. Since adapter 3 is used by + # Neutron for the external network, promiscuous mode is set to "allow-all". + # Also, use virtio as the adapter type, for better performance. + vm.customize ["modifyvm", :id, "--nictype1", "virtio"] + vm.customize ["modifyvm", :id, "--cableconnected1", "on"] + vm.customize ["modifyvm", :id, "--nicpromisc2", "deny"] + vm.customize ["modifyvm", :id, "--nictype2", "virtio"] + vm.customize ["modifyvm", :id, "--cableconnected2", "on"] + vm.customize ["modifyvm", :id, "--nic3", "natnetwork"] + vm.customize ["modifyvm", :id, "--nat-network3", "OSNetwork"] + vm.customize ["modifyvm", :id, "--nicpromisc3", "allow-all"] + vm.customize ["modifyvm", :id, "--nictype3", "virtio"] + vm.customize ["modifyvm", :id, "--cableconnected3", "on"] +end + +def configure_wifi_if_enabled(vm) + if WIFI + case PROVIDER + when "virtualbox" + configure_wifi_vbox_networking(vm) +# TODO(lucian-serb): Configure networking on Wi-Fi for other hypervisors. +# when "libvirt" +# configure_wifi_libvirt_networking(vm) + end + end +end + +Vagrant.configure(2) do |config| + config.vm.box = get_default(:base_image) + + # Next to the hostonly NAT-network there is a host-only network with all + # nodes attached. Plus, each node receives a 3rd adapter connected to the + # outside public network. + config.vm.network "private_network", type: "dhcp" + # On VirtualBox hosts with Wi-Fi, do not create a public bridged interface. + # A NAT-Network will be used instead. + # TODO(lucian-serb): Do the same for other hypervisors as well? + unless PROVIDER == "virtualbox" && WIFI + config.vm.network "public_network", dev: get_default(:bridge_interface), mode: 'bridge', type: 'bridge' + end + + my_privatekey = File.read(File.join(vagrant_dir, "vagrantkey")) + my_publickey = File.read(File.join(vagrant_dir, "vagrantkey.pub")) + + config.vm.provision :shell, inline: <<-EOS + mkdir -p /root/.ssh + echo '#{my_privatekey}' > /root/.ssh/id_rsa + chmod 600 /root/.ssh/id_rsa + echo '#{my_publickey}' > /root/.ssh/authorized_keys + chmod 600 /root/.ssh/authorized_keys + echo '#{my_publickey}' > /root/.ssh/id_rsa.pub + chmod 644 /root/.ssh/id_rsa.pub + mkdir -p /home/vagrant/.ssh + echo '#{my_privatekey}' >> /home/vagrant/.ssh/id_rsa + chmod 600 /home/vagrant/.ssh/* + echo 'Host *' > ~vagrant/.ssh/config + echo StrictHostKeyChecking no >> ~vagrant/.ssh/config + chown -R vagrant: /home/vagrant/.ssh + EOS + + config.hostmanager.enabled = true + # Make sure hostmanager picks IP address of eth1 + config.hostmanager.ip_resolver = proc do |vm, resolving_vm| + case PROVIDER + when "libvirt" + if vm.name + `python newest_dhcp_lease.py #{vm.name}`.chop + end + when "virtualbox" + if vm.id + `VBoxManage guestproperty get #{vm.id} "/VirtualBox/GuestInfo/Net/1/V4/IP"`.split()[1] + end + end + end + + # The operator controls the deployment + config.vm.define "operator", primary: true do |admin| + admin.vm.hostname = "operator.local" + admin.vm.provision :shell, path: PROVISION_SCRIPT, args: "operator #{MULTINODE ? 'multinode' : 'aio'} #{get_default(:kolla_path)}" + admin.vm.synced_folder File.join(vagrant_dir, '..', '..'), get_default(:kolla_path), create:"True", type: get_default(:sync_method) + admin.vm.synced_folder File.join(vagrant_dir, 'storage', 'operator'), "/data/host", create:"True", type: get_default(:sync_method) + admin.vm.synced_folder File.join(vagrant_dir, 'storage', 'shared'), "/data/shared", create:"True", type: get_default(:sync_method) + admin.vm.synced_folder ".", get_default(:vagrant_shared_folder), disabled: true + admin.vm.provider PROVIDER do |vm| + vm.memory = MULTINODE ? get_setting(:operator, :memory) : get_setting(:aio, :memory) + vm.cpus = MULTINODE ? get_setting(:operator, :cpus) : get_setting(:aio, :cpus) + if PROVIDER == "libvirt" + vm.graphics_ip = GRAPHICSIP + end + configure_wifi_if_enabled(vm) + end + admin.hostmanager.aliases = "operator" + end + + if MULTINODE + ['compute', 'storage', 'network', 'control'].each do |node_type| + (1..self.class.const_get("NUMBER_OF_#{node_type.upcase}_NODES")).each do |i| + hostname = "#{node_type}0#{i}" + config.vm.define hostname do |node| + node.vm.hostname = "#{hostname}.local" + node.vm.provision :shell, path: PROVISION_SCRIPT, args: "#{hostname} multinode #{get_default(:kolla_path)}" + node.vm.synced_folder File.join(vagrant_dir, 'storage', node_type), "/data/host", create:"True", type: get_default(:sync_method) + node.vm.synced_folder File.join(vagrant_dir, 'storage', 'shared'), "/data/shared", create:"True", type: get_default(:sync_method) + node.vm.synced_folder ".", get_default(:vagrant_shared_folder), disabled: true + node.vm.provider PROVIDER do |vm| + vm.memory = get_setting(node_type.to_sym, :memory) + vm.cpus = get_setting(node_type.to_sym, :cpus) + if PROVIDER == "libvirt" + vm.graphics_ip = GRAPHICSIP + end + configure_wifi_if_enabled(vm) + end + node.hostmanager.aliases = hostname + end + end + end + end + +end diff --git a/contrib/dev/vagrant/Vagrantfile.custom.example b/contrib/dev/vagrant/Vagrantfile.custom.example new file mode 100644 index 000000000..09af72f5b --- /dev/null +++ b/contrib/dev/vagrant/Vagrantfile.custom.example @@ -0,0 +1,92 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# This file is an example of Vagrant configuration. +# Copy it to Vagrantfile.custom and configure it to your liking to customize +# the Vagrant deployment. The Vagrantfile.custom file is sourced by the +# Vagrantfile, it has to be valid ruby code. + +# Either libvirt or virtualbox +# PROVIDER = "libvirt" + +# Either centos or ubuntu +# DISTRO = "centos" + +# The libvirt graphics_ip used for each guest. Only applies if PROVIDER +# is libvirt. +# GRAPHICSIP = "127.0.0.1" + +# The bootstrap.sh provision_script requires CentOS 7 or Ubuntu 15.10. +# Provisioning other boxes than the default ones may therefore +# require changes to bootstrap.sh. +# PROVISION_SCRIPT = "bootstrap.sh" + +# PROVIDER_DEFAULTS = { +# libvirt: { +# centos: { +# base_image: "centos/7", +# bridge_interface: "virbr0", +# vagrant_shared_folder: "/home/vagrant/sync", +# sync_method: "nfs", +# kolla_path: "/home/vagrant/kolla" +# } +# }, +# virtualbox: { +# centos: { +# base_image: "puppetlabs/centos-7.0-64-puppet", +# bridge_interface: "wlp3s0b1", +# vagrant_shared_folder: "/home/vagrant/sync", +# sync_method: "virtualbox", +# kolla_path: "/home/vagrant/kolla" +# }, +# ubuntu: { +# base_image: "ubuntu/wily64", +# bridge_interface: "wlp3s0b1", +# vagrant_shared_folder: "/home/vagrant/sync", +# sync_method: "virtualbox", +# kolla_path: "/home/vagrant/kolla" +# } +# } +# } + +# Whether the host network adapter is Wi-Fi. +# On VirtualBox, the user must first manually create a NAT-Network +# named OSNetwork. The default network CIDR must be changed. +# The Neutron external interface will be connected to this Network. +# WIFI = false + +# Whether to do Multi-node or All-in-One deployment +# MULTINODE = false + +# The following is only used when deploying in Multi-nodes +# NUMBER_OF_CONTROL_NODES = 3 +# NUMBER_OF_COMPUTE_NODES = 1 +# NUMBER_OF_STORAGE_NODES = 1 +# NUMBER_OF_NETWORK_NODES = 1 + +# NODE_SETTINGS = { +# aio: { +# cpus: 4, +# memory: 4096 +# }, +# operator: { +# cpus: 1, +# memory: 1024 +# }, +# control: { +# cpus: 1, +# memory: 2048 +# }, +# compute: { +# cpus: 1, +# memory: 1024 +# }, +# storage: { +# cpus: 1, +# memory: 1024 +# }, +# network: { +# cpus: 1, +# memory: 1024 +# } +# } diff --git a/contrib/dev/vagrant/bootstrap.sh b/contrib/dev/vagrant/bootstrap.sh new file mode 100644 index 000000000..ec89440b8 --- /dev/null +++ b/contrib/dev/vagrant/bootstrap.sh @@ -0,0 +1,226 @@ +#!/usr/bin/env bash +# +# Bootstrap script to configure all nodes. +# +# This script is intended to be used by vagrant to provision nodes. +# To use it, set it as 'PROVISION_SCRIPT' inside your Vagrantfile.custom. +# You can use Vagrantfile.custom.example as a template for this. + +VM=$1 +MODE=$2 +KOLLA_PATH=$3 + +export http_proxy= +export https_proxy= + +if [ "$MODE" = 'aio' ]; then + # Run registry on port 4000 since it may collide with keystone when doing AIO + REGISTRY_PORT=4000 +else + REGISTRY_PORT=5000 +fi +REGISTRY_URL="operator.local" +REGISTRY=${REGISTRY_URL}:${REGISTRY_PORT} +ADMIN_PROTOCOL="http" + +function _ensure_lsb_release { + if [[ -x $(type lsb_release 2>/dev/null) ]]; then + return + fi + + if [[ -x $(type apt-get 2>/dev/null) ]]; then + apt-get -y install lsb-release + elif [[ -x $(type yum 2>/dev/null) ]]; then + yum -y install redhat-lsb-core + fi +} + +function _is_distro { + if [[ -z "$DISTRO" ]]; then + _ensure_lsb_release + DISTRO=$(lsb_release -si) + fi + + [[ "$DISTRO" == "$1" ]] +} + +function is_ubuntu { + _is_distro "Ubuntu" +} + +function is_centos { + _is_distro "CentOS" +} + +# Install common packages and do some prepwork. +function prep_work { + if [[ "$(systemctl is-enabled firewalld)" = "enabled" ]]; then + systemctl stop firewalld + systemctl disable firewalld + fi + + # This removes the fqdn from /etc/hosts's 127.0.0.1. This name.local will + # resolve to the public IP instead of localhost. + sed -i -r "s,^127\.0\.0\.1\s+.*,127\.0\.0\.1 localhost localhost.localdomain localhost4 localhost4.localdomain4," /etc/hosts + + if is_centos; then + yum -y install epel-release + rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7 + yum -y install MySQL-python vim-enhanced python-pip python-devel gcc openssl-devel libffi-devel libxml2-devel libxslt-devel + elif is_ubuntu; then + apt-get update + apt-get -y install python-mysqldb python-pip python-dev build-essential libssl-dev libffi-dev libxml2-dev libxslt-dev + else + echo "Unsupported Distro: $DISTRO" 1>&2 + exit 1 + fi + + pip install --upgrade docker-py +} + +# Do some cleanup after the installation of kolla +function cleanup { + if is_centos; then + yum clean all + elif is_ubuntu; then + apt-get clean + else + echo "Unsupported Distro: $DISTRO" 1>&2 + exit 1 + fi +} + +# Install and configure a quick&dirty docker daemon. +function install_docker { + if is_centos; then + cat >/etc/yum.repos.d/docker.repo <<-EOF +[dockerrepo] +name=Docker Repository +baseurl=https://yum.dockerproject.org/repo/main/centos/7 +enabled=1 +gpgcheck=1 +gpgkey=https://yum.dockerproject.org/gpg +EOF + # Also upgrade device-mapper here because of: + # https://github.com/docker/docker/issues/12108 + # Upgrade lvm2 to get device-mapper installed + yum -y install docker-engine lvm2 device-mapper + + # Despite it shipping with /etc/sysconfig/docker, Docker is not configured to + # load it from it's service file. + sed -i -r "s|(ExecStart)=(.+)|\1=/usr/bin/docker daemon --insecure-registry ${REGISTRY} --registry-mirror=http://${REGISTRY}|" /usr/lib/systemd/system/docker.service + sed -i 's|^MountFlags=.*|MountFlags=shared|' /usr/lib/systemd/system/docker.service + + usermod -aG docker vagrant + elif is_ubuntu; then + apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D + echo "deb https://apt.dockerproject.org/repo ubuntu-wily main" > /etc/apt/sources.list.d/docker.list + apt-get update + apt-get -y install docker-engine + sed -i -r "s,(ExecStart)=(.+),\1=/usr/bin/docker daemon --insecure-registry ${REGISTRY} --registry-mirror=http://${REGISTRY}|" /lib/systemd/system/docker.service + else + echo "Unsupported Distro: $DISTRO" 1>&2 + exit 1 + fi + + if [[ "${http_proxy}" != "" ]]; then + mkdir -p /etc/systemd/system/docker.service.d + cat >/etc/systemd/system/docker.service.d/http-proxy.conf <<-EOF +[Service] +Environment="HTTP_PROXY=${http_proxy}" "HTTPS_PROXY=${https_proxy}" "NO_PROXY=localhost,127.0.0.1,${REGISTRY_URL}" +EOF + + if [[ "$(grep http_ /etc/bashrc)" == "" ]]; then + echo "export http_proxy=${http_proxy}" >> /etc/bashrc + echo "export https_proxy=${https_proxy}" >> /etc/bashrc + fi + fi + + systemctl daemon-reload + systemctl enable docker + systemctl start docker +} + +function configure_kolla { + # Use local docker registry + sed -i -r "s,^[# ]*namespace *=.+$,namespace = ${REGISTRY}/lokolla," /etc/kolla/kolla-build.conf + sed -i -r "s,^[# ]*push *=.+$,push = True," /etc/kolla/kolla-build.conf + sed -i -r "s,^[# ]*docker_registry:.+$,docker_registry: \"${REGISTRY}\"," /etc/kolla/globals.yml + sed -i -r "s,^[# ]*docker_namespace:.+$,docker_namespace: \"lokolla\"," /etc/kolla/globals.yml + sed -i -r "s,^[# ]*docker_insecure_registry:.+$,docker_insecure_registry: \"True\"," /etc/kolla/globals.yml + # Set network interfaces + sed -i -r "s,^[# ]*network_interface:.+$,network_interface: \"eth1\"," /etc/kolla/globals.yml + sed -i -r "s,^[# ]*neutron_external_interface:.+$,neutron_external_interface: \"eth2\"," /etc/kolla/globals.yml + # Set VIP address to be on the vagrant private network + sed -i -r "s,^[# ]*kolla_internal_vip_address:.+$,kolla_internal_vip_address: \"172.28.128.254\"," /etc/kolla/globals.yml +} + +# Configure the operator node and install some additional packages. +function configure_operator { + if is_centos; then + yum -y install git mariadb + elif is_ubuntu; then + apt-get -y install git mariadb-client selinux-utils + else + echo "Unsupported Distro: $DISTRO" 1>&2 + exit 1 + fi + + pip install --upgrade "ansible>=2" python-openstackclient python-neutronclient tox + + pip install ${KOLLA_PATH} + + # Set selinux to permissive + if [[ "$(getenforce)" == "Enforcing" ]]; then + sed -i -r "s,^SELINUX=.+$,SELINUX=permissive," /etc/selinux/config + setenforce permissive + fi + + tox -c ${KOLLA_PATH}/tox.ini -e genconfig + cp -r ${KOLLA_PATH}/etc/kolla/ /etc/kolla + ${KOLLA_PATH}/tools/generate_passwords.py + mkdir -p /usr/share/kolla + chown -R vagrant: /etc/kolla /usr/share/kolla + + configure_kolla + + # Make sure Ansible uses scp. + cat > ~vagrant/.ansible.cfg <<EOF +[defaults] +forks=100 +remote_user = root + +[ssh_connection] +scp_if_ssh=True +EOF + chown vagrant: ~vagrant/.ansible.cfg + + mkdir -p /etc/kolla/config/nova/ + cat > /etc/kolla/config/nova/nova-compute.conf <<EOF +[libvirt] +virt_type=qemu +EOF + + # Launch a local registry (and mirror) to speed up pulling images. + if [[ ! $(docker ps -a -q -f name=registry) ]]; then + docker run -d \ + --name registry \ + --restart=always \ + -p ${REGISTRY_PORT}:5000 \ + -e STANDALONE=True \ + -e MIRROR_SOURCE=https://registry-1.docker.io \ + -e MIRROR_SOURCE_INDEX=https://index.docker.io \ + -e STORAGE_PATH=/var/lib/registry \ + -v /data/host/registry-storage:/var/lib/registry \ + registry:2 + fi +} + +prep_work +install_docker + +if [[ "$VM" = "operator" ]]; then + configure_operator +fi + +cleanup diff --git a/contrib/dev/vagrant/newest_dhcp_lease.py b/contrib/dev/vagrant/newest_dhcp_lease.py new file mode 100644 index 000000000..bb229af1d --- /dev/null +++ b/contrib/dev/vagrant/newest_dhcp_lease.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Command-line utility to get the IP address from the newest DHCP lease. + +It's written for using with vagrant-hostmanager and vagrant-libvirt plugins. +Vagrant-hostmanager by default fetches only IP addresses from eth0 interfaces +on VM-s. Therefore, the first purpose of this utility is to be able to fetch +the address also from the other interfaces. + +Libvirt/virsh only lists all DHCP leases for the given network with timestamps. +DHCP leases have their expiration time, but are not cleaned up after destroying +VM. If someone destroys and sets up the VM with the same hostname, we have +many DHCP leases for the same hostname and we have to look up for timestamp. +That's the second purpose of this script. +""" + +import argparse +import csv +import functools +import operator +import xml.etree.ElementTree as etree + +import libvirt + + +class NoPrivateDHCPInterfaceException(Exception): + pass + + +class NoDHCPLeaseException(Exception): + pass + + +def libvirt_conn(f): + @functools.wraps(f) + def wrapper(*args, **kwargs): + conn = libvirt.openReadOnly('qemu:///system') + return f(conn, *args, **kwargs) + return wrapper + + +@libvirt_conn +def get_vir_network_dhcp_lease(conn, vm_name): + """Libvirt since 1.2.6 version provides DHCPLeases method in virNetwork. + + That's the current official way for getting DHCP leases and this + information isn't stored anywhere else anymore. + """ + domain_name = 'vagrant_' + vm_name + mac_address = get_mac_address(conn, domain_name) + + network = conn.networkLookupByName('vagrant-private-dhcp') + dhcp_leases = libvirt.virNetwork.DHCPLeases(network) + + vm_dhcp_leases = [lease for lease in dhcp_leases + if lease['mac'] == mac_address] + + newest_vm_dhcp_lease = sorted(vm_dhcp_leases, + key=operator.itemgetter('expirytime'), + reverse=True)[0]['ipaddr'] + return newest_vm_dhcp_lease + + +def get_mac_address(conn, domain_name): + """Get MAC address from domain XML.""" + domain = conn.lookupByName(domain_name) + domain_xml = domain.XMLDesc() + domain_tree = etree.fromstring(domain_xml) + devices = domain_tree.find('devices') + interfaces = devices.iterfind('interface') + + for interface in interfaces: + source = interface.find('source') + if source is None or source.get('network') != 'vagrant-private-dhcp': + continue + mac_element = interface.find('mac') + mac_address = mac_element.get('address') + return mac_address + + raise NoPrivateDHCPInterfaceException() + + +@libvirt_conn +def get_dnsmasq_dhcp_lease(conn, vm_name): + """In libvirt under 1.2.6 DHCP leases are stored in file. + + There is no API for DHCP leases yet. + """ + domain_name = 'vagrant_' + vm_name + mac_address = get_mac_address(conn, domain_name) + + with open( + '/var/lib/libvirt/dnsmasq/vagrant-private-dhcp.leases' + ) as leases_file: + reader = csv.reader(leases_file, delimiter=' ') + for row in reader: + lease_mac, lease_ip, lease_vm_name = row[1:4] + if not (lease_mac == mac_address and lease_vm_name == vm_name): + continue + return lease_ip + + raise NoDHCPLeaseException() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('vm_name', help='Name of the virtual machine') + + args = parser.parse_args() + vm_name = args.vm_name + + if libvirt.getVersion() >= 1002006: + newest_dhcp_lease = get_vir_network_dhcp_lease(vm_name) + else: + newest_dhcp_lease = get_dnsmasq_dhcp_lease(vm_name) + + print(newest_dhcp_lease) + + +if __name__ == '__main__': + main() |
