On 06/17/2015 08:24 AM, Aline Manera wrote:
Reviewed-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
Christy, we are current in a code freeze for Kimchi 1.5, so I will merge
it as soon as 1.5 is released next week.
Thanks!
On 15/06/2015 19:56, Christy Perez wrote:
> v2:
> - Check VM's state for shutoff, and return empty list of IPs if so.
> - RestTests: Start VM before checking for an IP in the list returned.
>
> Signed-off-by: Christy Perez <christy(a)linux.vnet.ibm.com>
> ---
> docs/API.md | 1 +
> src/kimchi/mockmodel.py | 9 +++++++++
> src/kimchi/model/vmifaces.py | 35 +++++++++++++++++++++++++++++++++++
> tests/test_rest.py | 16 ++++++++++++++++
> 4 files changed, 61 insertions(+)
>
> diff --git a/docs/API.md b/docs/API.md
> index e022c9e..10e80ac 100644
> --- a/docs/API.md
> +++ b/docs/API.md
> @@ -299,6 +299,7 @@ A interface represents available network interface
> on VM.
> * bridge *(optional)*: the name of resource bridge, only be
> available when the
> interface type is bridge.
> * mac: Media Access Control Address of the VM interface.
> + * ips: A list of IP addresses associated with this MAC.
> * model *(optional)*: model of emulated network interface card.
> It will be one of these models:
> ne2k_pci, i82551, i82557b, i82559er, rtl8139, e1000,
> pcnet and virtio.
> * network *(optional)*: the name of resource network, only be
> available when the
> diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py
> index aaf1af2..3f74b80 100644
> --- a/src/kimchi/mockmodel.py
> +++ b/src/kimchi/mockmodel.py
> @@ -24,6 +24,7 @@ import random
> import time
>
> import kimchi.model.cpuinfo
> +import kimchi.model.vmifaces
>
> from lxml import objectify
> from lxml.builder import E
> @@ -76,6 +77,7 @@ class MockModel(Model):
>
> kimchi.model.cpuinfo.get_topo_capabilities = \
> MockModel.get_topo_capabilities
> + kimchi.model.vmifaces.getDHCPLeases = MockModel.getDHCPLeases
> libvirt.virConnect.defineXML = MockModel.domainDefineXML
> libvirt.virDomain.XMLDesc = MockModel.domainXMLDesc
> libvirt.virDomain.undefine = MockModel.undefineDomain
> @@ -235,6 +237,13 @@ class MockModel(Model):
> pool = vol.storagePoolLookupByVolume()
> pool.createXML(new_xml)
>
> + @staticmethod
> + def getDHCPLeases(net, mac):
> + return [{'iface': 'virbr1', 'ipaddr':
'192.168.0.167',
> + 'hostname': 'kimchi', 'expirytime':
1433285036L,
> + 'prefix': 24, 'clientid': '01:%s' % mac,
> + 'mac': mac, 'iaid': None, 'type': 0}]
> +
> def _probe_image(self, path):
> return ('unknown', 'unknown')
>
> diff --git a/src/kimchi/model/vmifaces.py b/src/kimchi/model/vmifaces.py
> index 93a769b..5df60fc 100644
> --- a/src/kimchi/model/vmifaces.py
> +++ b/src/kimchi/model/vmifaces.py
> @@ -29,6 +29,14 @@ from kimchi.model.vms import DOM_STATE_MAP, VMModel
> from kimchi.xmlutils.interface import get_iface_xml
>
>
> +def getDHCPLeases(net, mac):
> + try:
> + leases = net.DHCPLeases(mac)
> + return leases
> + except libvirt.libvirtError:
> + return []
> +
> +
> class VMIfacesModel(object):
> def __init__(self, **kargs):
> self.conn = kargs['conn']
> @@ -132,9 +140,36 @@ class VMIfaceModel(object):
> info['network'] = iface.source.get('network')
> if info['type'] == 'bridge':
> info['bridge'] = iface.source.get('bridge')
> + info['ips'] = self._get_ips(vm, info['mac'],
info['network'])
>
> return info
>
> + def _get_ips(self, vm, mac, network):
> + ips = []
> +
> + # Return empty list if shutoff, even if leases still valid or
> ARP
> + # cache has entries for this MAC.
> + conn = self.conn.get()
> + dom = VMModel.get_vm(vm, self.conn)
> + if DOM_STATE_MAP[dom.info()[0]] == "shutoff":
> + return ips
> +
> + # An iface may have multiple IPs
> + # An IP could have been assigned without libvirt.
> + # First check the ARP cache.
> + with open('/proc/net/arp') as f:
> + ips = [line.split()[0] for line in f.xreadlines() if mac
> in line]
> + # Some ifaces may be inactive, so if the ARP cache didn't
> have them,
> + # and they happen to be assigned via DHCP, we can check there
> too.
> + net = conn.networkLookupByName(network)
> + leases = getDHCPLeases(net, mac)
> + for lease in leases:
> + ip = lease.get('ipaddr')
> + if ip not in ips:
> + ips.append(ip)
> +
> + return ips
> +
> def delete(self, vm, mac):
> dom = VMModel.get_vm(vm, self.conn)
> iface = self._get_vmiface(vm, mac)
> diff --git a/tests/test_rest.py b/tests/test_rest.py
> index c2d142f..f0e27e7 100644
> --- a/tests/test_rest.py
> +++ b/tests/test_rest.py
> @@ -713,6 +713,7 @@ class RestTests(unittest.TestCase):
> self.assertEquals(17, len(res['mac']))
> self.assertEquals(get_template_default('old',
> 'nic_model'),
> res['model'])
> + self.assertTrue('ips' in res)
>
> # try to attach an interface without specifying 'model'
> req = json.dumps({'type': 'network'})
> @@ -743,6 +744,21 @@ class RestTests(unittest.TestCase):
> newMacAddr).read())
> self.assertEquals(newMacAddr, iface['mac'])
>
> + # Start the VM
> + resp = self.request('/vms/test-vm/start', '{}',
'POST')
> + vm = json.loads(self.request('/vms/test-vm').read())
> + self.assertEquals('running', vm['state'])
> +
> + # Check for an IP address
> + iface = json.loads(self.request('/vms/test-vm/ifaces/%s' %
> + newMacAddr).read())
> + self.assertTrue(len(iface['ips']) > 0)
> +
> + # Force poweroff the VM
> + resp = self.request('/vms/test-vm/poweroff', '{}',
'POST')
> + vm = json.loads(self.request('/vms/test-vm').read())
> + self.assertEquals('shutoff', vm['state'])
> +
> # detach network interface from vm
> resp = self.request('/vms/test-vm/ifaces/%s' %
> iface['mac'],
> '{}', 'DELETE')