Patch uvt-kvm for mgmt bridge
diff --git a/cord-setup.yml b/cord-setup.yml
index c5c5ec1..c2fb8e0 100644
--- a/cord-setup.yml
+++ b/cord-setup.yml
@@ -32,6 +32,10 @@
     - python-keystoneclient
     - python-glanceclient
 
+  - name: Patch uvt-kvm
+    copy: src=files/usr/lib/python2.7/dist-packages/uvtool/libvirt/__init__.py
+      dest=/usr/lib/python2.7/dist-packages/uvtool/libvirt/__init__.py
+
   - name: Get juju-ansible git repo
     git: repo=https://github.com/cmars/juju-ansible.git
       dest=/usr/local/src/juju-ansible
diff --git a/files/usr/lib/python2.7/dist-packages/uvtool/libvirt/__init__.py b/files/usr/lib/python2.7/dist-packages/uvtool/libvirt/__init__.py
new file mode 100644
index 0000000..3c72a8d
--- /dev/null
+++ b/files/usr/lib/python2.7/dist-packages/uvtool/libvirt/__init__.py
@@ -0,0 +1,246 @@
+# Copyright (C) 2013 Canonical Ltd.
+# Author: Robie Basak <robie.basak@canonical.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import
+from __future__ import print_function
+from __future__ import unicode_literals
+
+import codecs
+import contextlib
+import itertools
+import os
+import shutil
+import subprocess
+import tempfile
+
+import libvirt
+from lxml import etree
+from lxml.builder import E
+
+LIBVIRT_DNSMASQ_LEASE_FILE = '/var/lib/libvirt/dnsmasq/default.leases'
+
+
+def get_libvirt_pool_object(libvirt_conn, pool_name):
+    try:
+        pool = libvirt_conn.storagePoolLookupByName(pool_name)
+    except libvirt.libvirtError:
+        raise RuntimeError("Cannot find pool %s." % repr(pool_name))
+    return pool
+
+
+def create_volume_from_fobj(new_volume_name, fobj, image_type='raw',
+        pool_name='default'):
+    """Create a new libvirt volume and populate it from a file-like object."""
+
+    compressed_fobj = tempfile.NamedTemporaryFile()
+    decompressed_fobj = tempfile.NamedTemporaryFile()
+    with contextlib.closing(compressed_fobj):
+        with contextlib.closing(decompressed_fobj):
+            shutil.copyfileobj(fobj, compressed_fobj)
+            compressed_fobj.flush()
+            compressed_fobj.seek(0)  # is this necessary?
+            subprocess.check_call(
+                [
+                    'qemu-img', 'convert', '-f', image_type, '-O', image_type,
+                    compressed_fobj.name, decompressed_fobj.name
+                ],
+                shell=False, close_fds=False)
+            decompressed_fobj.seek(0)  # is this necessary?
+            return _create_volume_from_fobj_with_size(
+                new_volume_name=new_volume_name,
+                fobj=decompressed_fobj,
+                fobj_size=os.fstat(decompressed_fobj.fileno()).st_size,
+                image_type=image_type,
+                pool_name=pool_name
+            )
+
+
+def _create_volume_from_fobj_with_size(new_volume_name, fobj, fobj_size,
+        image_type, pool_name):
+    conn = libvirt.open('qemu:///system')
+    pool = get_libvirt_pool_object(conn, pool_name)
+
+    if image_type == 'raw':
+        extra = [E.allocation(str(fobj_size)), E.capacity(str(fobj_size))]
+    elif image_type == 'qcow2':
+        extra = [E.capacity('0')]
+    else:
+        raise NotImplementedError("Unknown image type %r." % image_type)
+
+    new_vol = E.volume(
+        E.name(new_volume_name),
+        E.target(E.format(type=image_type)),
+        *extra
+        )
+    vol = pool.createXML(etree.tostring(new_vol), 0)
+
+    try:
+        stream = conn.newStream(0)
+        vol.upload(stream, 0, fobj_size, 0)
+
+        def handler(stream_ignored, size, opaque_ignored):
+            return fobj.read(size)
+
+        try:
+            stream.sendAll(handler, None)
+        except Exception as e:
+            try:
+                # This unexpectedly raises an exception even on a normal call,
+                # so ignore it.
+                stream.abort()
+            except:
+                pass
+            raise e
+        stream.finish()
+    except:
+        vol.delete(flags=0)
+        raise
+
+    return vol
+
+
+def volume_names_in_pool(pool_name='default'):
+    conn = libvirt.open('qemu:///system')
+    pool = get_libvirt_pool_object(conn, pool_name)
+    return pool.listVolumes()
+
+
+def delete_volume_by_name(volume_name, pool_name='default'):
+    conn = libvirt.open('qemu:///system')
+    pool = get_libvirt_pool_object(conn, pool_name)
+    volume = pool.storageVolLookupByName(volume_name)
+    volume.delete(flags=0)
+
+
+def have_volume_by_name(volume_name, pool_name='default'):
+    conn = libvirt.open('qemu:///system')
+    pool = get_libvirt_pool_object(conn, pool_name)
+    try:
+        volume = pool.storageVolLookupByName(volume_name)
+    except libvirt.libvirtError:
+        return False
+    else:
+        return True
+
+
+def _get_all_domains(conn=None):
+    if conn is None:
+        conn = libvirt.open('qemu:///system')
+
+    # libvirt in Precise doesn't seem to have a binding for
+    # virConnectListAllDomains, and it seems that we must enumerate
+    # defined-by-not-running and running instances separately and in different
+    # ways.
+
+    for domain_id in conn.listDomainsID():
+        yield conn.lookupByID(domain_id)
+
+    for domain_name in conn.listDefinedDomains():
+        yield conn.lookupByName(domain_name)
+
+
+def _domain_element_to_volume_paths(element):
+    assert element.tag == 'domain'
+    return (
+        source.get('file')
+        for source in element.xpath(
+            "/domain/devices/disk[@type='file']/source[@file]"
+        )
+    )
+
+
+def _domain_volume_paths(domain):
+    volume_paths = set()
+
+    for flags in [0, libvirt.VIR_DOMAIN_XML_INACTIVE]:
+        element = etree.fromstring(domain.XMLDesc(flags))
+        volume_paths.update(_domain_element_to_volume_paths(element))
+
+    return frozenset(volume_paths)
+
+
+def _volume_element_to_volume_paths(element):
+    assert element.tag == 'volume'
+    return itertools.chain(
+        (path.text for path in element.xpath('/volume/target/path')),
+        (path.text for path in element.xpath('/volume/backingStore/path')),
+    )
+
+
+def _volume_volume_paths(volume):
+    # Volumes can depend on other volumes ("backing stores"), so return all
+    # paths a volume needs to function, including the top level one.
+    volume_paths = set()
+
+    element = etree.fromstring(volume.XMLDesc(0))
+    volume_paths.update(_volume_element_to_volume_paths(element))
+
+    return frozenset(volume_paths)
+
+
+def _get_all_domain_volume_paths(conn=None):
+    if conn is None:
+        conn = libvirt.open('qemu:///system')
+
+    all_volume_paths = set()
+    for domain in _get_all_domains(conn):
+        for path in _domain_volume_paths(domain):
+            try:
+                volume = conn.storageVolLookupByKey(path)
+            except libvirt.libvirtError:
+                # ignore a lookup failure, since if a volume doesn't exist,
+                # it isn't reasonable to consider what backing volumes it may
+                # have
+                continue
+            all_volume_paths.update(_volume_volume_paths(volume))
+
+    return frozenset(all_volume_paths)
+
+
+def get_all_domain_volume_names(conn=None, filter_by_dir=None):
+    # Limitation: filter_by_dir must currently end in a '/' and be the
+    # canonical path as libvirt returns it. Ideally I'd filter by pool instead,
+    # but the libvirt API appears to not provide any method to find what pool a
+    # volume is in when looked up by key.
+    if conn is None:
+        conn = libvirt.open('qemu:///system')
+
+    for path in _get_all_domain_volume_paths(conn=conn):
+        volume = conn.storageVolLookupByKey(path)
+        if filter_by_dir and not volume.path().startswith(filter_by_dir):
+            continue
+        yield volume.name()
+
+
+def get_domain_macs(domain_name, conn=None):
+    if conn is None:
+        conn = libvirt.open('qemu:///system')
+
+    domain = conn.lookupByName(domain_name)
+    xml = etree.fromstring(domain.XMLDesc(0))
+    for mac in xml.xpath(
+            "/domain/devices/interface[@type='network' or @type='bridge']/mac[@address]"):
+        yield mac.get('address')
+
+
+def mac_to_ip(mac):
+    canonical_mac = mac.lower()
+    with codecs.open(LIBVIRT_DNSMASQ_LEASE_FILE, 'r') as f:
+        for line in f:
+            fields = line.split()
+            if len(fields) > 1 and fields[1].lower() == canonical_mac:
+                return fields[2]
+        return None