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

Aline Manera alinefm at linux.vnet.ibm.com
Mon Jun 29 20:04:10 UTC 2015



On 17/06/2015 11:15, Christy Perez wrote:
>
> 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!
>

Hi Christy,

As we decided for a stabilization release after 1.5, I will create a new 
branch upstream named 'next' to apply the patches not related to bug fixes.
Which is the case of this patch.
Once 1.5.1 is released, the next branch will be merged into master.

>> 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