[Kimchi-devel] [PATCH v2] List IPs of VM Ifaces

Christy Perez christy at linux.vnet.ibm.com
Wed Jun 17 14:15:52 UTC 2015



On 06/17/2015 08:24 AM, Aline Manera wrote:
> 
> Reviewed-by: Aline Manera <alinefm at 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 at 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')
> 




More information about the Kimchi-devel mailing list