All of lore.kernel.org
 help / color / mirror / Atom feed
* [KVM-AUTOTEST PATCH 11/12] KVM test: make VMs use a dynamic MAC-IP mapping generated by tcpdump
  2009-08-02 23:58                   ` [KVM-AUTOTEST PATCH 01/12] KVM test: add some MAC/IP address utilities to kvm_utils Michael Goldish
@ 2009-08-02 23:58                     ` Michael Goldish
  0 siblings, 0 replies; 7+ messages in thread
From: Michael Goldish @ 2009-08-02 23:58 UTC (permalink / raw)
  To: autotest, kvm; +Cc: Michael Goldish

In VM.get_address(), return an IP address from the MAC-IP cache if an IP
address base wasn't specified by the user, or if the user requested that IP
addresses be taken from the dynamic cache.

To force the system to obtain IP addresses from the dynamic cache even when
static base addresses are provided by the user, specify
'always_use_tcpdump = yes'.

Signed-off-by: Michael Goldish <mgoldish@redhat.com>
---
 client/tests/kvm/kvm_address_pools.cfg.sample |    8 +++
 client/tests/kvm/kvm_preprocessing.py         |    3 +-
 client/tests/kvm/kvm_utils.py                 |   28 +++++++++++-
 client/tests/kvm/kvm_vm.py                    |   62 +++++++++++++++++-------
 4 files changed, 81 insertions(+), 20 deletions(-)

diff --git a/client/tests/kvm/kvm_address_pools.cfg.sample b/client/tests/kvm/kvm_address_pools.cfg.sample
index 8a27ee1..debfe56 100644
--- a/client/tests/kvm/kvm_address_pools.cfg.sample
+++ b/client/tests/kvm/kvm_address_pools.cfg.sample
@@ -6,6 +6,14 @@
 # If you wish to use a static MAC-IP mapping, where each MAC address range is
 # mapped to a known corresponding IP address range, specify the bases of the IP
 # address ranges in this file.
+# If you specify a MAC address range without a corresponding IP address range,
+# the IP addresses for that range will be determined at runtime by listening
+# to DHCP traffic using tcpdump.
+# If you wish to determine IP addresses using tcpdump in any case, regardless
+# of any # IP addresses specified in this file, uncomment the following line:
+#always_use_tcpdump = yes
+# You may also specify this parameter for specific hosts by adding it in the
+# appropriate sections below.
 
 variants:
     # Rename host1 to an actual (short) hostname in the network that will be running the Autotest client
diff --git a/client/tests/kvm/kvm_preprocessing.py b/client/tests/kvm/kvm_preprocessing.py
index 04bdb59..a5a32dc 100644
--- a/client/tests/kvm/kvm_preprocessing.py
+++ b/client/tests/kvm/kvm_preprocessing.py
@@ -54,7 +54,8 @@ def preprocess_vm(test, params, env, name):
         logging.debug("VM object found in environment")
     else:
         logging.debug("VM object does not exist; creating it")
-        vm = kvm_vm.VM(name, params, qemu_path, image_dir, iso_dir, script_dir)
+        vm = kvm_vm.VM(name, params, qemu_path, image_dir, iso_dir, script_dir,
+                       env.get("address_cache"))
         kvm_utils.env_register_vm(env, name, vm)
 
     start_vm = False
diff --git a/client/tests/kvm/kvm_utils.py b/client/tests/kvm/kvm_utils.py
index 1c11fa4..e17d84f 100644
--- a/client/tests/kvm/kvm_utils.py
+++ b/client/tests/kvm/kvm_utils.py
@@ -1,5 +1,5 @@
 import md5, thread, subprocess, time, string, random, socket, os, signal, pty
-import select, re, logging
+import select, re, logging, commands
 #from autotest_lib.client.bin import utils
 #from autotest_lib.client.common_lib import error
 import kvm_subprocess
@@ -152,6 +152,32 @@ def get_mac_ip_pair_from_dict(dict):
     return (None, None)
 
 
+def verify_mac_ip_pair(mac, ip, timeout=3.0):
+    """
+    Connect to a given IP address and make sure its MAC address equals the
+    given MAC address.
+
+    @param mac: A MAC address.
+    @param ip: An IP address.
+    @return: True iff ip is assigned to mac.
+    """
+    s = socket.socket()
+    s.setblocking(False)
+    try:
+        s.connect((ip, 55555))
+    except socket.error:
+        pass
+    end_time = time.time() + timeout
+    while time.time() < end_time:
+        o = commands.getoutput("/sbin/arp -n")
+        if re.search(r"\b%s\b.*\b%s\b" % (ip, mac), o, re.IGNORECASE):
+            s.close()
+            return True
+        time.sleep(0.1)
+    s.close()
+    return False
+
+
 # Functions for working with the environment (a dict-like object)
 
 def is_vm(obj):
diff --git a/client/tests/kvm/kvm_vm.py b/client/tests/kvm/kvm_vm.py
index 0c35e64..6addf77 100644
--- a/client/tests/kvm/kvm_vm.py
+++ b/client/tests/kvm/kvm_vm.py
@@ -1,5 +1,5 @@
 #!/usr/bin/python
-import time, socket, os, logging, fcntl
+import time, socket, os, logging, fcntl, re, commands
 import kvm_utils, kvm_subprocess
 
 """
@@ -101,7 +101,7 @@ class VM:
     """
 
     def __init__(self, name, params, qemu_path, image_dir, iso_dir,
-                 script_dir):
+                 script_dir, address_cache):
         """
         Initialize the object and set a few attributes.
 
@@ -112,6 +112,7 @@ class VM:
         @param image_dir: The directory where images reside
         @param iso_dir: The directory where ISOs reside
         @param script_dir: The directory where -net tap scripts reside
+        @param address_cache: A dict that maps MAC addresses to IP addresses
         """
         self.process = None
         self.redirs = {}
@@ -124,6 +125,7 @@ class VM:
         self.image_dir = image_dir
         self.iso_dir = iso_dir
         self.script_dir = script_dir
+        self.address_cache = address_cache
 
         # Find available monitor filename
         while True:
@@ -137,7 +139,7 @@ class VM:
 
 
     def clone(self, name=None, params=None, qemu_path=None, image_dir=None,
-              iso_dir=None, script_dir=None):
+              iso_dir=None, script_dir=None, address_cache=None):
         """
         Return a clone of the VM object with optionally modified parameters.
         The clone is initially not alive and needs to be started using create().
@@ -150,6 +152,7 @@ class VM:
         @param image_dir: Optional new image dir
         @param iso_dir: Optional new iso directory
         @param script_dir: Optional new -net tap script directory
+        @param address_cache: A dict that maps MAC addresses to IP addresses
         """
         if name == None:
             name = self.name
@@ -163,7 +166,10 @@ class VM:
             iso_dir = self.iso_dir
         if script_dir == None:
             script_dir = self.script_dir
-        return VM(name, params, qemu_path, image_dir, iso_dir, script_dir)
+        if address_cache == None:
+            address_cache = self.address_cache
+        return VM(name, params, qemu_path, image_dir, iso_dir, script_dir,
+                  address_cache)
 
 
     def make_qemu_command(self, name=None, params=None, qemu_path=None,
@@ -625,21 +631,36 @@ class VM:
         nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
         if nic_params.get("nic_mode") == "tap":
             mac, ip = kvm_utils.get_mac_ip_pair_from_dict(nic_params)
+            if not mac:
+                logging.debug("MAC address unavailable")
+                return None
+            if not ip or nic_params.get("always_use_tcpdump") == "yes":
+                # Get the IP address from the cache
+                ip = self.address_cache.get(mac)
+                if not ip:
+                    logging.debug("Could not find IP address for MAC address: "
+                                  "%s" % mac)
+                    return None
+                # Make sure the IP address is assigned to the right MAC address
+                if not kvm_utils.verify_mac_ip_pair(mac, ip):
+                    logging.debug("Could not verify MAC-IP address pair: "
+                                  "%s ---> %s" % (mac, ip))
+                    return None
             return ip
         else:
             return "localhost"
 
 
-    def get_port(self, port, index=0):
+    def get_port(self, port, nic_index=0):
         """
         Return the port in host space corresponding to port in guest space.
 
         @param port: Port number in host space.
-        @param index: Index of the NIC.
+        @param nic_index: Index of the NIC.
         @return: If port redirection is used, return the host port redirected
                 to guest port port. Otherwise return port.
         """
-        nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[index]
+        nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[nic_index]
         nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
         if nic_params.get("nic_mode") == "tap":
             return port
@@ -666,17 +687,19 @@ class VM:
         """
         address = self.get_address()
         port = self.get_port(int(self.params.get("ssh_port")))
-        if not port:
+        if not address or not port:
+            logging.debug("IP address or port unavailable")
             return False
         return kvm_utils.is_sshd_running(address, port, timeout=timeout)
 
 
-    def ssh_login(self, timeout=10):
+    def ssh_login(self, nic_index=0, timeout=10):
         """
         Log into the guest via SSH/Telnet.
         If timeout expires while waiting for output from the guest (e.g. a
         password prompt or a shell prompt) -- fail.
 
+        @param nic_index: The index of the NIC to connect to.
         @param timeout: Time (seconds) before giving up logging into the
                 guest.
         @return: kvm_spawn object on success and None on failure.
@@ -685,9 +708,10 @@ class VM:
         password = self.params.get("password", "")
         prompt = self.params.get("ssh_prompt", "[\#\$]")
         use_telnet = self.params.get("use_telnet") == "yes"
-        address = self.get_address()
+        address = self.get_address(nic_index)
         port = self.get_port(int(self.params.get("ssh_port")))
-        if not port:
+        if not address or not port:
+            logging.debug("IP address or port unavailable")
             return None
 
         if use_telnet:
@@ -702,7 +726,7 @@ class VM:
         return session
 
 
-    def scp_to_remote(self, local_path, remote_path, timeout=300):
+    def scp_to_remote(self, local_path, remote_path, nic_index=0, timeout=300):
         """
         Transfer files to the guest via SCP.
 
@@ -713,15 +737,16 @@ class VM:
         """
         username = self.params.get("username", "")
         password = self.params.get("password", "")
-        address = self.get_address()
+        address = self.get_address(nic_index)
         port = self.get_port(int(self.params.get("ssh_port")))
-        if not port:
+        if not address or not port:
+            logging.debug("IP address or port unavailable")
             return None
         return kvm_utils.scp_to_remote(address, port, username, password,
                                        local_path, remote_path, timeout)
 
 
-    def scp_from_remote(self, remote_path, local_path, timeout=300):
+    def scp_from_remote(self, remote_path, local_path, nic_index=0, timeout=300):
         """
         Transfer files from the guest via SCP.
 
@@ -732,9 +757,10 @@ class VM:
         """
         username = self.params.get("username", "")
         password = self.params.get("password", "")
-        address = self.get_address()
+        address = self.get_address(nic_index)
         port = self.get_port(int(self.params.get("ssh_port")))
-        if not port:
+        if not address or not port:
+            logging.debug("IP address or port unavailable")
             return None
         return kvm_utils.scp_from_remote(address, port, username, password,
                                          remote_path, local_path, timeout)
@@ -749,7 +775,7 @@ class VM:
         @return: A tuple (status, output). status is 0 on success and 1 on
                 failure.
         """
-        session = self.ssh_login(timeout)
+        session = self.ssh_login(timeout=timeout)
         if not session:
             return (1, "")
 
-- 
1.5.4.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [KVM-AUTOTEST PATCH CORRECTIONS] Corrections to the TAP patchset
@ 2009-08-06 18:41 Michael Goldish
  2009-08-06 18:41 ` [KVM-AUTOTEST PATCH 01/12] KVM test: add some MAC/IP address utilities to kvm_utils Michael Goldish
  0 siblings, 1 reply; 7+ messages in thread
From: Michael Goldish @ 2009-08-06 18:41 UTC (permalink / raw)
  To: autotest, kvm


I found two mistakes (so far) in the TAP patchset:
- Two import lines in kvm_utils.py were commented out (for personal testing)
  and I forgot to uncomment them before committing, and this breaks kvm_install
- qemu-ifup should be executable, but isn't

The following patches (1, 3, 11) replace the respective ones from the original
patch set.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [KVM-AUTOTEST PATCH 01/12] KVM test: add some MAC/IP address utilities to kvm_utils
  2009-08-06 18:41 [KVM-AUTOTEST PATCH CORRECTIONS] Corrections to the TAP patchset Michael Goldish
@ 2009-08-06 18:41 ` Michael Goldish
  2009-08-06 18:41   ` [KVM-AUTOTEST PATCH 03/12] KVM test: add sample 'qemu-ifup' script Michael Goldish
  2009-08-06 18:41   ` [KVM-AUTOTEST PATCH 11/12] KVM test: make VMs use a dynamic MAC-IP mapping generated by tcpdump Michael Goldish
  0 siblings, 2 replies; 7+ messages in thread
From: Michael Goldish @ 2009-08-06 18:41 UTC (permalink / raw)
  To: autotest, kvm; +Cc: Michael Goldish

Add function get_mac_ip_pair_from_dict() which gets a dict (specified in the
config file by the user) and fetches a MAC-IP address pair according to a
certain syntax.  The syntax allows the user to specify a group of MAC-IP
address ranges.  For example:

address_ranges = r1 r2 r3

address_range_base_mac_r1 = 55:44:33:22:11:00
address_range_base_ip_r1 = 10.0.0.0
address_range_size_r1 = 16

address_range_base_mac_r2 = 55:44:33:22:11:40
address_range_base_ip_r2 = 10.0.0.60
address_range_size_r2 = 25

address_range_base_mac_r3 = 55:44:33:22:12:10
address_range_base_ip_r3 = 10.0.1.20
address_range_size_r3 = 230

The lines above may be specified globally, so that they apply to all VMs and
all NICs.

However, a line similar to the following must be specified per NIC (each VM may
have several NICs):

address_index = 0

Currently, we usually use a single VM and a single NIC, so specifying
address_index once should suffice.  If a test requires an additional NIC the
user should add something like:

address_index_nic2 = 1

The index is simply the index in the MAC-IP table that consists of all the
specified ranges.  In the above example, if the user specifies an index of 18,
the MAC-IP pair will be taken from the second range, because the first range
has only 16 entries.

When running migration, both the source and destination VMs should have the
same address_index, because they should have the same MAC and IP addresses.

Note that different copies of the KVM test, running simultaneously in the same
network environment, _must_ specify different MAC-IP pools.  This can be done
in several ways:
- By specifying the ranges (as in the example above) in an external file such
as /etc/kvm-autotest/client_mac_ip_pool.cfg, and setting up that file for each
host manually
- By keeping several .cfg files with different names that match the hostname
of each host, e.g. mac_ip_pool_hostname1.cfg, mac_ip_pool_hostname2.cfg
(hostname1 and hostname2 should be replaced by actual hostnames), and parsing
the right file in the control file at runtime
- By defining all the different pools in a variants block in a single file,
and specifying 'only <hostname>' at runtime in the control file (using
config.parse_string())
When we start running in server mode, assigning MAC and IP addresses to hosts
can be done automatically by the server, but the user will still be required to
specify a single global pool for the server (which the server will divide among
the hosts).  The address_index parameter will be specified inside the regular
config file, and does not need to be different for each host.

This patch also adds some small utility functions used by
get_mac_ip_pair_from_dict().

Signed-off-by: Michael Goldish <mgoldish@redhat.com>
---
 client/tests/kvm/kvm_utils.py |  106 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 106 insertions(+), 0 deletions(-)

diff --git a/client/tests/kvm/kvm_utils.py b/client/tests/kvm/kvm_utils.py
index e897e78..4891592 100644
--- a/client/tests/kvm/kvm_utils.py
+++ b/client/tests/kvm/kvm_utils.py
@@ -46,6 +46,112 @@ def get_sub_dict_names(dict, keyword):
         return []
 
 
+# Functions related to MAC/IP addresses
+
+def mac_str_to_int(addr):
+    """
+    Convert MAC address string to integer.
+
+    @param addr: String representing the MAC address.
+    """
+    return sum(int(s, 16) * 256 ** i
+               for i, s in enumerate(reversed(addr.split(":"))))
+
+
+def mac_int_to_str(addr):
+    """
+    Convert MAC address integer to string.
+
+    @param addr: Integer representing the MAC address.
+    """
+    return ":".join("%02x" % (addr >> 8 * i & 0xFF)
+                    for i in reversed(range(6)))
+
+
+def ip_str_to_int(addr):
+    """
+    Convert IP address string to integer.
+
+    @param addr: String representing the IP address.
+    """
+    return sum(int(s) * 256 ** i
+               for i, s in enumerate(reversed(addr.split("."))))
+
+
+def ip_int_to_str(addr):
+    """
+    Convert IP address integer to string.
+
+    @param addr: Integer representing the IP address.
+    """
+    return ".".join(str(addr >> 8 * i & 0xFF)
+                    for i in reversed(range(4)))
+
+
+def offset_mac(base, offset):
+    """
+    Add offset to a given MAC address.
+
+    @param base: String representing a MAC address.
+    @param offset: Offset to add to base (integer)
+    @return: A string representing the offset MAC address.
+    """
+    return mac_int_to_str(mac_str_to_int(base) + offset)
+
+
+def offset_ip(base, offset):
+    """
+    Add offset to a given IP address.
+
+    @param base: String representing an IP address.
+    @param offset: Offset to add to base (integer)
+    @return: A string representing the offset IP address.
+    """
+    return ip_int_to_str(ip_str_to_int(base) + offset)
+
+
+def get_mac_ip_pair_from_dict(dict):
+    """
+    Fetch a MAC-IP address pair from dict and return it.
+
+    The parameters in dict are expected to conform to a certain syntax.
+    Typical usage may be:
+
+    address_ranges = r1 r2 r3
+
+    address_range_base_mac_r1 = 55:44:33:22:11:00
+    address_range_base_ip_r1 = 10.0.0.0
+    address_range_size_r1 = 16
+
+    address_range_base_mac_r2 = 55:44:33:22:11:40
+    address_range_base_ip_r2 = 10.0.0.60
+    address_range_size_r2 = 25
+
+    address_range_base_mac_r3 = 55:44:33:22:12:10
+    address_range_base_ip_r3 = 10.0.1.20
+    address_range_size_r3 = 230
+
+    address_index = 0
+
+    All parameters except address_index specify a MAC-IP address pool.  The
+    pool consists of several MAC-IP address ranges.
+    address_index specified the index of the desired MAC-IP pair from the pool.
+
+    @param dict: The dictionary from which to fetch the addresses.
+    """
+    index = int(dict.get("address_index", 0))
+    for mac_range_name in get_sub_dict_names(dict, "address_ranges"):
+        mac_range_params = get_sub_dict(dict, mac_range_name)
+        mac_base = mac_range_params.get("address_range_base_mac")
+        ip_base = mac_range_params.get("address_range_base_ip")
+        size = int(mac_range_params.get("address_range_size", 1))
+        if index < size:
+            return (mac_base and offset_mac(mac_base, index),
+                    ip_base and offset_ip(ip_base, index))
+        index -= size
+    return (None, None)
+
+
 # Functions for working with the environment (a dict-like object)
 
 def is_vm(obj):
-- 
1.5.4.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [KVM-AUTOTEST PATCH 03/12] KVM test: add sample 'qemu-ifup' script
  2009-08-06 18:41 ` [KVM-AUTOTEST PATCH 01/12] KVM test: add some MAC/IP address utilities to kvm_utils Michael Goldish
@ 2009-08-06 18:41   ` Michael Goldish
  2009-08-06 18:41   ` [KVM-AUTOTEST PATCH 11/12] KVM test: make VMs use a dynamic MAC-IP mapping generated by tcpdump Michael Goldish
  1 sibling, 0 replies; 7+ messages in thread
From: Michael Goldish @ 2009-08-06 18:41 UTC (permalink / raw)
  To: autotest, kvm; +Cc: Michael Goldish

The script adds a requested interface to an existing bridge.  It is meant to be
used by qemu when running in TAP mode.

Note: the user is responsible for setting up the bridge before running any
tests.  This can be done with brctl or in any manner that is appropriate for
the host OS.  It can be done inside 'qemu-ifup' as well, but this sample script
doesn't do it.

Signed-off-by: Michael Goldish <mgoldish@redhat.com>
---
 client/tests/kvm/qemu-ifup |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)
 create mode 100755 client/tests/kvm/qemu-ifup

diff --git a/client/tests/kvm/qemu-ifup b/client/tests/kvm/qemu-ifup
new file mode 100755
index 0000000..bcd9a7a
--- /dev/null
+++ b/client/tests/kvm/qemu-ifup
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# The following expression selects the first bridge listed by 'brctl show'.
+# Modify it to suit your needs.
+switch=$(/usr/sbin/brctl show | awk 'NR==2 { print $1 }')
+
+/sbin/ifconfig $1 0.0.0.0 up
+/usr/sbin/brctl addif ${switch} $1
-- 
1.5.4.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [KVM-AUTOTEST PATCH 11/12] KVM test: make VMs use a dynamic MAC-IP mapping generated by tcpdump
  2009-08-06 18:41 ` [KVM-AUTOTEST PATCH 01/12] KVM test: add some MAC/IP address utilities to kvm_utils Michael Goldish
  2009-08-06 18:41   ` [KVM-AUTOTEST PATCH 03/12] KVM test: add sample 'qemu-ifup' script Michael Goldish
@ 2009-08-06 18:41   ` Michael Goldish
  2009-08-11 14:43     ` Jason Wang
  1 sibling, 1 reply; 7+ messages in thread
From: Michael Goldish @ 2009-08-06 18:41 UTC (permalink / raw)
  To: autotest, kvm; +Cc: Michael Goldish

In VM.get_address(), return an IP address from the MAC-IP cache if an IP
address base wasn't specified by the user, or if the user requested that IP
addresses be taken from the dynamic cache.

To force the system to obtain IP addresses from the dynamic cache even when
static base addresses are provided by the user, specify
'always_use_tcpdump = yes'.

Signed-off-by: Michael Goldish <mgoldish@redhat.com>
---
 client/tests/kvm/kvm_address_pools.cfg.sample |    8 +++
 client/tests/kvm/kvm_preprocessing.py         |    3 +-
 client/tests/kvm/kvm_utils.py                 |   28 +++++++++++-
 client/tests/kvm/kvm_vm.py                    |   62 +++++++++++++++++-------
 4 files changed, 81 insertions(+), 20 deletions(-)

diff --git a/client/tests/kvm/kvm_address_pools.cfg.sample b/client/tests/kvm/kvm_address_pools.cfg.sample
index 8a27ee1..debfe56 100644
--- a/client/tests/kvm/kvm_address_pools.cfg.sample
+++ b/client/tests/kvm/kvm_address_pools.cfg.sample
@@ -6,6 +6,14 @@
 # If you wish to use a static MAC-IP mapping, where each MAC address range is
 # mapped to a known corresponding IP address range, specify the bases of the IP
 # address ranges in this file.
+# If you specify a MAC address range without a corresponding IP address range,
+# the IP addresses for that range will be determined at runtime by listening
+# to DHCP traffic using tcpdump.
+# If you wish to determine IP addresses using tcpdump in any case, regardless
+# of any # IP addresses specified in this file, uncomment the following line:
+#always_use_tcpdump = yes
+# You may also specify this parameter for specific hosts by adding it in the
+# appropriate sections below.
 
 variants:
     # Rename host1 to an actual (short) hostname in the network that will be running the Autotest client
diff --git a/client/tests/kvm/kvm_preprocessing.py b/client/tests/kvm/kvm_preprocessing.py
index 04bdb59..a5a32dc 100644
--- a/client/tests/kvm/kvm_preprocessing.py
+++ b/client/tests/kvm/kvm_preprocessing.py
@@ -54,7 +54,8 @@ def preprocess_vm(test, params, env, name):
         logging.debug("VM object found in environment")
     else:
         logging.debug("VM object does not exist; creating it")
-        vm = kvm_vm.VM(name, params, qemu_path, image_dir, iso_dir, script_dir)
+        vm = kvm_vm.VM(name, params, qemu_path, image_dir, iso_dir, script_dir,
+                       env.get("address_cache"))
         kvm_utils.env_register_vm(env, name, vm)
 
     start_vm = False
diff --git a/client/tests/kvm/kvm_utils.py b/client/tests/kvm/kvm_utils.py
index ef257bc..17c2b73 100644
--- a/client/tests/kvm/kvm_utils.py
+++ b/client/tests/kvm/kvm_utils.py
@@ -1,5 +1,5 @@
 import md5, thread, subprocess, time, string, random, socket, os, signal, pty
-import select, re, logging
+import select, re, logging, commands
 from autotest_lib.client.bin import utils
 from autotest_lib.client.common_lib import error
 import kvm_subprocess
@@ -152,6 +152,32 @@ def get_mac_ip_pair_from_dict(dict):
     return (None, None)
 
 
+def verify_mac_ip_pair(mac, ip, timeout=3.0):
+    """
+    Connect to a given IP address and make sure its MAC address equals the
+    given MAC address.
+
+    @param mac: A MAC address.
+    @param ip: An IP address.
+    @return: True iff ip is assigned to mac.
+    """
+    s = socket.socket()
+    s.setblocking(False)
+    try:
+        s.connect((ip, 55555))
+    except socket.error:
+        pass
+    end_time = time.time() + timeout
+    while time.time() < end_time:
+        o = commands.getoutput("/sbin/arp -n")
+        if re.search(r"\b%s\b.*\b%s\b" % (ip, mac), o, re.IGNORECASE):
+            s.close()
+            return True
+        time.sleep(0.1)
+    s.close()
+    return False
+
+
 # Functions for working with the environment (a dict-like object)
 
 def is_vm(obj):
diff --git a/client/tests/kvm/kvm_vm.py b/client/tests/kvm/kvm_vm.py
index 0c35e64..6addf77 100644
--- a/client/tests/kvm/kvm_vm.py
+++ b/client/tests/kvm/kvm_vm.py
@@ -1,5 +1,5 @@
 #!/usr/bin/python
-import time, socket, os, logging, fcntl
+import time, socket, os, logging, fcntl, re, commands
 import kvm_utils, kvm_subprocess
 
 """
@@ -101,7 +101,7 @@ class VM:
     """
 
     def __init__(self, name, params, qemu_path, image_dir, iso_dir,
-                 script_dir):
+                 script_dir, address_cache):
         """
         Initialize the object and set a few attributes.
 
@@ -112,6 +112,7 @@ class VM:
         @param image_dir: The directory where images reside
         @param iso_dir: The directory where ISOs reside
         @param script_dir: The directory where -net tap scripts reside
+        @param address_cache: A dict that maps MAC addresses to IP addresses
         """
         self.process = None
         self.redirs = {}
@@ -124,6 +125,7 @@ class VM:
         self.image_dir = image_dir
         self.iso_dir = iso_dir
         self.script_dir = script_dir
+        self.address_cache = address_cache
 
         # Find available monitor filename
         while True:
@@ -137,7 +139,7 @@ class VM:
 
 
     def clone(self, name=None, params=None, qemu_path=None, image_dir=None,
-              iso_dir=None, script_dir=None):
+              iso_dir=None, script_dir=None, address_cache=None):
         """
         Return a clone of the VM object with optionally modified parameters.
         The clone is initially not alive and needs to be started using create().
@@ -150,6 +152,7 @@ class VM:
         @param image_dir: Optional new image dir
         @param iso_dir: Optional new iso directory
         @param script_dir: Optional new -net tap script directory
+        @param address_cache: A dict that maps MAC addresses to IP addresses
         """
         if name == None:
             name = self.name
@@ -163,7 +166,10 @@ class VM:
             iso_dir = self.iso_dir
         if script_dir == None:
             script_dir = self.script_dir
-        return VM(name, params, qemu_path, image_dir, iso_dir, script_dir)
+        if address_cache == None:
+            address_cache = self.address_cache
+        return VM(name, params, qemu_path, image_dir, iso_dir, script_dir,
+                  address_cache)
 
 
     def make_qemu_command(self, name=None, params=None, qemu_path=None,
@@ -625,21 +631,36 @@ class VM:
         nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
         if nic_params.get("nic_mode") == "tap":
             mac, ip = kvm_utils.get_mac_ip_pair_from_dict(nic_params)
+            if not mac:
+                logging.debug("MAC address unavailable")
+                return None
+            if not ip or nic_params.get("always_use_tcpdump") == "yes":
+                # Get the IP address from the cache
+                ip = self.address_cache.get(mac)
+                if not ip:
+                    logging.debug("Could not find IP address for MAC address: "
+                                  "%s" % mac)
+                    return None
+                # Make sure the IP address is assigned to the right MAC address
+                if not kvm_utils.verify_mac_ip_pair(mac, ip):
+                    logging.debug("Could not verify MAC-IP address pair: "
+                                  "%s ---> %s" % (mac, ip))
+                    return None
             return ip
         else:
             return "localhost"
 
 
-    def get_port(self, port, index=0):
+    def get_port(self, port, nic_index=0):
         """
         Return the port in host space corresponding to port in guest space.
 
         @param port: Port number in host space.
-        @param index: Index of the NIC.
+        @param nic_index: Index of the NIC.
         @return: If port redirection is used, return the host port redirected
                 to guest port port. Otherwise return port.
         """
-        nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[index]
+        nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[nic_index]
         nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
         if nic_params.get("nic_mode") == "tap":
             return port
@@ -666,17 +687,19 @@ class VM:
         """
         address = self.get_address()
         port = self.get_port(int(self.params.get("ssh_port")))
-        if not port:
+        if not address or not port:
+            logging.debug("IP address or port unavailable")
             return False
         return kvm_utils.is_sshd_running(address, port, timeout=timeout)
 
 
-    def ssh_login(self, timeout=10):
+    def ssh_login(self, nic_index=0, timeout=10):
         """
         Log into the guest via SSH/Telnet.
         If timeout expires while waiting for output from the guest (e.g. a
         password prompt or a shell prompt) -- fail.
 
+        @param nic_index: The index of the NIC to connect to.
         @param timeout: Time (seconds) before giving up logging into the
                 guest.
         @return: kvm_spawn object on success and None on failure.
@@ -685,9 +708,10 @@ class VM:
         password = self.params.get("password", "")
         prompt = self.params.get("ssh_prompt", "[\#\$]")
         use_telnet = self.params.get("use_telnet") == "yes"
-        address = self.get_address()
+        address = self.get_address(nic_index)
         port = self.get_port(int(self.params.get("ssh_port")))
-        if not port:
+        if not address or not port:
+            logging.debug("IP address or port unavailable")
             return None
 
         if use_telnet:
@@ -702,7 +726,7 @@ class VM:
         return session
 
 
-    def scp_to_remote(self, local_path, remote_path, timeout=300):
+    def scp_to_remote(self, local_path, remote_path, nic_index=0, timeout=300):
         """
         Transfer files to the guest via SCP.
 
@@ -713,15 +737,16 @@ class VM:
         """
         username = self.params.get("username", "")
         password = self.params.get("password", "")
-        address = self.get_address()
+        address = self.get_address(nic_index)
         port = self.get_port(int(self.params.get("ssh_port")))
-        if not port:
+        if not address or not port:
+            logging.debug("IP address or port unavailable")
             return None
         return kvm_utils.scp_to_remote(address, port, username, password,
                                        local_path, remote_path, timeout)
 
 
-    def scp_from_remote(self, remote_path, local_path, timeout=300):
+    def scp_from_remote(self, remote_path, local_path, nic_index=0, timeout=300):
         """
         Transfer files from the guest via SCP.
 
@@ -732,9 +757,10 @@ class VM:
         """
         username = self.params.get("username", "")
         password = self.params.get("password", "")
-        address = self.get_address()
+        address = self.get_address(nic_index)
         port = self.get_port(int(self.params.get("ssh_port")))
-        if not port:
+        if not address or not port:
+            logging.debug("IP address or port unavailable")
             return None
         return kvm_utils.scp_from_remote(address, port, username, password,
                                          remote_path, local_path, timeout)
@@ -749,7 +775,7 @@ class VM:
         @return: A tuple (status, output). status is 0 on success and 1 on
                 failure.
         """
-        session = self.ssh_login(timeout)
+        session = self.ssh_login(timeout=timeout)
         if not session:
             return (1, "")
 
-- 
1.5.4.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [KVM-AUTOTEST PATCH 11/12] KVM test: make VMs use a dynamic MAC-IP mapping generated by tcpdump
  2009-08-06 18:41   ` [KVM-AUTOTEST PATCH 11/12] KVM test: make VMs use a dynamic MAC-IP mapping generated by tcpdump Michael Goldish
@ 2009-08-11 14:43     ` Jason Wang
  0 siblings, 0 replies; 7+ messages in thread
From: Jason Wang @ 2009-08-11 14:43 UTC (permalink / raw)
  To: Michael Goldish; +Cc: autotest, kvm

Hello Michael:
Based on our experiences with TAP networking scripts, the
verify_mac_ip_pair() could not work well under multiple nics when
testing linux guests. The main problem is the policy of multiple
interfaces responding in linux, we should take care of the value of
net.ipv4.conf.all.arp_ignore. Its default value(0) may cause the host
get a mac address that does not configured on the target interface of guest.
Maybe we could run a simple 'setup' case for all linux guests to set the
value of arp_ignore to 1.
What's your opinion?
> In VM.get_address(), return an IP address from the MAC-IP cache if an IP
> address base wasn't specified by the user, or if the user requested that IP
> addresses be taken from the dynamic cache.
>
> To force the system to obtain IP addresses from the dynamic cache even when
> static base addresses are provided by the user, specify
> 'always_use_tcpdump = yes'.
>
> Signed-off-by: Michael Goldish <mgoldish@redhat.com>
> ---
>  client/tests/kvm/kvm_address_pools.cfg.sample |    8 +++
>  client/tests/kvm/kvm_preprocessing.py         |    3 +-
>  client/tests/kvm/kvm_utils.py                 |   28 +++++++++++-
>  client/tests/kvm/kvm_vm.py                    |   62 +++++++++++++++++-------
>  4 files changed, 81 insertions(+), 20 deletions(-)
>
> diff --git a/client/tests/kvm/kvm_address_pools.cfg.sample b/client/tests/kvm/kvm_address_pools.cfg.sample
> index 8a27ee1..debfe56 100644
> --- a/client/tests/kvm/kvm_address_pools.cfg.sample
> +++ b/client/tests/kvm/kvm_address_pools.cfg.sample
> @@ -6,6 +6,14 @@
>  # If you wish to use a static MAC-IP mapping, where each MAC address range is
>  # mapped to a known corresponding IP address range, specify the bases of the IP
>  # address ranges in this file.
> +# If you specify a MAC address range without a corresponding IP address range,
> +# the IP addresses for that range will be determined at runtime by listening
> +# to DHCP traffic using tcpdump.
> +# If you wish to determine IP addresses using tcpdump in any case, regardless
> +# of any # IP addresses specified in this file, uncomment the following line:
> +#always_use_tcpdump = yes
> +# You may also specify this parameter for specific hosts by adding it in the
> +# appropriate sections below.
>  
>  variants:
>      # Rename host1 to an actual (short) hostname in the network that will be running the Autotest client
> diff --git a/client/tests/kvm/kvm_preprocessing.py b/client/tests/kvm/kvm_preprocessing.py
> index 04bdb59..a5a32dc 100644
> --- a/client/tests/kvm/kvm_preprocessing.py
> +++ b/client/tests/kvm/kvm_preprocessing.py
> @@ -54,7 +54,8 @@ def preprocess_vm(test, params, env, name):
>          logging.debug("VM object found in environment")
>      else:
>          logging.debug("VM object does not exist; creating it")
> -        vm = kvm_vm.VM(name, params, qemu_path, image_dir, iso_dir, script_dir)
> +        vm = kvm_vm.VM(name, params, qemu_path, image_dir, iso_dir, script_dir,
> +                       env.get("address_cache"))
>          kvm_utils.env_register_vm(env, name, vm)
>  
>      start_vm = False
> diff --git a/client/tests/kvm/kvm_utils.py b/client/tests/kvm/kvm_utils.py
> index ef257bc..17c2b73 100644
> --- a/client/tests/kvm/kvm_utils.py
> +++ b/client/tests/kvm/kvm_utils.py
> @@ -1,5 +1,5 @@
>  import md5, thread, subprocess, time, string, random, socket, os, signal, pty
> -import select, re, logging
> +import select, re, logging, commands
>  from autotest_lib.client.bin import utils
>  from autotest_lib.client.common_lib import error
>  import kvm_subprocess
> @@ -152,6 +152,32 @@ def get_mac_ip_pair_from_dict(dict):
>      return (None, None)
>  
>  
> +def verify_mac_ip_pair(mac, ip, timeout=3.0):
> +    """
> +    Connect to a given IP address and make sure its MAC address equals the
> +    given MAC address.
> +
> +    @param mac: A MAC address.
> +    @param ip: An IP address.
> +    @return: True iff ip is assigned to mac.
> +    """
> +    s = socket.socket()
> +    s.setblocking(False)
> +    try:
> +        s.connect((ip, 55555))
> +    except socket.error:
> +        pass
> +    end_time = time.time() + timeout
> +    while time.time() < end_time:
> +        o = commands.getoutput("/sbin/arp -n")
> +        if re.search(r"\b%s\b.*\b%s\b" % (ip, mac), o, re.IGNORECASE):
> +            s.close()
> +            return True
> +        time.sleep(0.1)
> +    s.close()
> +    return False
> +
> +
>  # Functions for working with the environment (a dict-like object)
>  
>  def is_vm(obj):
> diff --git a/client/tests/kvm/kvm_vm.py b/client/tests/kvm/kvm_vm.py
> index 0c35e64..6addf77 100644
> --- a/client/tests/kvm/kvm_vm.py
> +++ b/client/tests/kvm/kvm_vm.py
> @@ -1,5 +1,5 @@
>  #!/usr/bin/python
> -import time, socket, os, logging, fcntl
> +import time, socket, os, logging, fcntl, re, commands
>  import kvm_utils, kvm_subprocess
>  
>  """
> @@ -101,7 +101,7 @@ class VM:
>      """
>  
>      def __init__(self, name, params, qemu_path, image_dir, iso_dir,
> -                 script_dir):
> +                 script_dir, address_cache):
>          """
>          Initialize the object and set a few attributes.
>  
> @@ -112,6 +112,7 @@ class VM:
>          @param image_dir: The directory where images reside
>          @param iso_dir: The directory where ISOs reside
>          @param script_dir: The directory where -net tap scripts reside
> +        @param address_cache: A dict that maps MAC addresses to IP addresses
>          """
>          self.process = None
>          self.redirs = {}
> @@ -124,6 +125,7 @@ class VM:
>          self.image_dir = image_dir
>          self.iso_dir = iso_dir
>          self.script_dir = script_dir
> +        self.address_cache = address_cache
>  
>          # Find available monitor filename
>          while True:
> @@ -137,7 +139,7 @@ class VM:
>  
>  
>      def clone(self, name=None, params=None, qemu_path=None, image_dir=None,
> -              iso_dir=None, script_dir=None):
> +              iso_dir=None, script_dir=None, address_cache=None):
>          """
>          Return a clone of the VM object with optionally modified parameters.
>          The clone is initially not alive and needs to be started using create().
> @@ -150,6 +152,7 @@ class VM:
>          @param image_dir: Optional new image dir
>          @param iso_dir: Optional new iso directory
>          @param script_dir: Optional new -net tap script directory
> +        @param address_cache: A dict that maps MAC addresses to IP addresses
>          """
>          if name == None:
>              name = self.name
> @@ -163,7 +166,10 @@ class VM:
>              iso_dir = self.iso_dir
>          if script_dir == None:
>              script_dir = self.script_dir
> -        return VM(name, params, qemu_path, image_dir, iso_dir, script_dir)
> +        if address_cache == None:
> +            address_cache = self.address_cache
> +        return VM(name, params, qemu_path, image_dir, iso_dir, script_dir,
> +                  address_cache)
>  
>  
>      def make_qemu_command(self, name=None, params=None, qemu_path=None,
> @@ -625,21 +631,36 @@ class VM:
>          nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
>          if nic_params.get("nic_mode") == "tap":
>              mac, ip = kvm_utils.get_mac_ip_pair_from_dict(nic_params)
> +            if not mac:
> +                logging.debug("MAC address unavailable")
> +                return None
> +            if not ip or nic_params.get("always_use_tcpdump") == "yes":
> +                # Get the IP address from the cache
> +                ip = self.address_cache.get(mac)
> +                if not ip:
> +                    logging.debug("Could not find IP address for MAC address: "
> +                                  "%s" % mac)
> +                    return None
> +                # Make sure the IP address is assigned to the right MAC address
> +                if not kvm_utils.verify_mac_ip_pair(mac, ip):
> +                    logging.debug("Could not verify MAC-IP address pair: "
> +                                  "%s ---> %s" % (mac, ip))
> +                    return None
>              return ip
>          else:
>              return "localhost"
>  
>  
> -    def get_port(self, port, index=0):
> +    def get_port(self, port, nic_index=0):
>          """
>          Return the port in host space corresponding to port in guest space.
>  
>          @param port: Port number in host space.
> -        @param index: Index of the NIC.
> +        @param nic_index: Index of the NIC.
>          @return: If port redirection is used, return the host port redirected
>                  to guest port port. Otherwise return port.
>          """
> -        nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[index]
> +        nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[nic_index]
>          nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
>          if nic_params.get("nic_mode") == "tap":
>              return port
> @@ -666,17 +687,19 @@ class VM:
>          """
>          address = self.get_address()
>          port = self.get_port(int(self.params.get("ssh_port")))
> -        if not port:
> +        if not address or not port:
> +            logging.debug("IP address or port unavailable")
>              return False
>          return kvm_utils.is_sshd_running(address, port, timeout=timeout)
>  
>  
> -    def ssh_login(self, timeout=10):
> +    def ssh_login(self, nic_index=0, timeout=10):
>          """
>          Log into the guest via SSH/Telnet.
>          If timeout expires while waiting for output from the guest (e.g. a
>          password prompt or a shell prompt) -- fail.
>  
> +        @param nic_index: The index of the NIC to connect to.
>          @param timeout: Time (seconds) before giving up logging into the
>                  guest.
>          @return: kvm_spawn object on success and None on failure.
> @@ -685,9 +708,10 @@ class VM:
>          password = self.params.get("password", "")
>          prompt = self.params.get("ssh_prompt", "[\#\$]")
>          use_telnet = self.params.get("use_telnet") == "yes"
> -        address = self.get_address()
> +        address = self.get_address(nic_index)
>          port = self.get_port(int(self.params.get("ssh_port")))
> -        if not port:
> +        if not address or not port:
> +            logging.debug("IP address or port unavailable")
>              return None
>  
>          if use_telnet:
> @@ -702,7 +726,7 @@ class VM:
>          return session
>  
>  
> -    def scp_to_remote(self, local_path, remote_path, timeout=300):
> +    def scp_to_remote(self, local_path, remote_path, nic_index=0, timeout=300):
>          """
>          Transfer files to the guest via SCP.
>  
> @@ -713,15 +737,16 @@ class VM:
>          """
>          username = self.params.get("username", "")
>          password = self.params.get("password", "")
> -        address = self.get_address()
> +        address = self.get_address(nic_index)
>          port = self.get_port(int(self.params.get("ssh_port")))
> -        if not port:
> +        if not address or not port:
> +            logging.debug("IP address or port unavailable")
>              return None
>          return kvm_utils.scp_to_remote(address, port, username, password,
>                                         local_path, remote_path, timeout)
>  
>  
> -    def scp_from_remote(self, remote_path, local_path, timeout=300):
> +    def scp_from_remote(self, remote_path, local_path, nic_index=0, timeout=300):
>          """
>          Transfer files from the guest via SCP.
>  
> @@ -732,9 +757,10 @@ class VM:
>          """
>          username = self.params.get("username", "")
>          password = self.params.get("password", "")
> -        address = self.get_address()
> +        address = self.get_address(nic_index)
>          port = self.get_port(int(self.params.get("ssh_port")))
> -        if not port:
> +        if not address or not port:
> +            logging.debug("IP address or port unavailable")
>              return None
>          return kvm_utils.scp_from_remote(address, port, username, password,
>                                           remote_path, local_path, timeout)
> @@ -749,7 +775,7 @@ class VM:
>          @return: A tuple (status, output). status is 0 on success and 1 on
>                  failure.
>          """
> -        session = self.ssh_login(timeout)
> +        session = self.ssh_login(timeout=timeout)
>          if not session:
>              return (1, "")
>  
>   


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [KVM-AUTOTEST PATCH 11/12] KVM test: make VMs use a dynamic MAC-IP mapping generated by tcpdump
       [not found] <1960092437.1769331250006819543.JavaMail.root@zmail05.collab.prod.int.phx2.redhat.com>
@ 2009-08-11 16:16 ` Michael Goldish
  0 siblings, 0 replies; 7+ messages in thread
From: Michael Goldish @ 2009-08-11 16:16 UTC (permalink / raw)
  To: Jason Wang; +Cc: autotest, kvm


----- "Jason Wang" <jasowang@redhat.com> wrote:

> Hello Michael:
> Based on our experiences with TAP networking scripts, the
> verify_mac_ip_pair() could not work well under multiple nics when
> testing linux guests. The main problem is the policy of multiple
> interfaces responding in linux, we should take care of the value of
> net.ipv4.conf.all.arp_ignore. Its default value(0) may cause the host
> get a mac address that does not configured on the target interface of
> guest.
> Maybe we could run a simple 'setup' case for all linux guests to set
> the value of arp_ignore to 1.
> What's your opinion?

Thanks for pointing this out.

You're right, there seems to be a problem with Linux guests (I tried
RHEL 3.9).  I started RHEL 3.9 with two NICs.  At boot time the guest
got 2 IP addresses and tcpdump collected them properly, but /sbin/arp -n
listed the addresses as though they were both assigned to the MAC address
of the first NIC.  It's possible that regardless of the NIC I try to
contact, the reply always comes from eth0, so it seems that both IP
addresses have the same MAC address.

I don't think we should solve this by messing with the guest, because
different guests may require different hackish solutions.  I think it's
better to fix the test framework instead.
The solution you suggested may not work for all Linux guests -- for
example, on RHEL 3.9 I couldn't find net.ipv4.conf.all.arp_ignore, or any
arp_ignore.  (I did find arp_filter and arp_announce though, and none of
them helped solve the problem.)

I'm considering 2 solutions:

1. Do not require the MAC address reported by 'arp -n' to be assigned to
   the NIC we are 'verifiying' -- require it to be assigned to any NIC of
   the guest.  This will probably work in all cases.

2. Stop using verify_mac_ip_pair() altogether.  Instead, when destroying
   a VM, remove the MAC addresses of its NICs from the address cache.
   Also, when starting a new test, check each address in the cache and
   make sure it belongs to some living VM, and otherwise remove it.
   This should work in most cases, but _theoretically_ a guest might
   release its IP address during a test, and then the DHCP server might
   assign the same IP address to some other machine, so when we try
   talking to our guest using that IP address, we'll end up talking to
   the other machine.  That's exactly the sort of situation
   verify_mac_ip_pair() was meant to prevent, but this situation sounds so
   unlikely to occur, that it might be safe to use this proposed solution.

What do you think?

> > In VM.get_address(), return an IP address from the MAC-IP cache if
> an IP
> > address base wasn't specified by the user, or if the user requested
> that IP
> > addresses be taken from the dynamic cache.
> >
> > To force the system to obtain IP addresses from the dynamic cache
> even when
> > static base addresses are provided by the user, specify
> > 'always_use_tcpdump = yes'.
> >
> > Signed-off-by: Michael Goldish <mgoldish@redhat.com>
> > ---
> >  client/tests/kvm/kvm_address_pools.cfg.sample |    8 +++
> >  client/tests/kvm/kvm_preprocessing.py         |    3 +-
> >  client/tests/kvm/kvm_utils.py                 |   28 +++++++++++-
> >  client/tests/kvm/kvm_vm.py                    |   62
> +++++++++++++++++-------
> >  4 files changed, 81 insertions(+), 20 deletions(-)
> >
> > diff --git a/client/tests/kvm/kvm_address_pools.cfg.sample
> b/client/tests/kvm/kvm_address_pools.cfg.sample
> > index 8a27ee1..debfe56 100644
> > --- a/client/tests/kvm/kvm_address_pools.cfg.sample
> > +++ b/client/tests/kvm/kvm_address_pools.cfg.sample
> > @@ -6,6 +6,14 @@
> >  # If you wish to use a static MAC-IP mapping, where each MAC
> address range is
> >  # mapped to a known corresponding IP address range, specify the
> bases of the IP
> >  # address ranges in this file.
> > +# If you specify a MAC address range without a corresponding IP
> address range,
> > +# the IP addresses for that range will be determined at runtime by
> listening
> > +# to DHCP traffic using tcpdump.
> > +# If you wish to determine IP addresses using tcpdump in any case,
> regardless
> > +# of any # IP addresses specified in this file, uncomment the
> following line:
> > +#always_use_tcpdump = yes
> > +# You may also specify this parameter for specific hosts by adding
> it in the
> > +# appropriate sections below.
> >  
> >  variants:
> >      # Rename host1 to an actual (short) hostname in the network
> that will be running the Autotest client
> > diff --git a/client/tests/kvm/kvm_preprocessing.py
> b/client/tests/kvm/kvm_preprocessing.py
> > index 04bdb59..a5a32dc 100644
> > --- a/client/tests/kvm/kvm_preprocessing.py
> > +++ b/client/tests/kvm/kvm_preprocessing.py
> > @@ -54,7 +54,8 @@ def preprocess_vm(test, params, env, name):
> >          logging.debug("VM object found in environment")
> >      else:
> >          logging.debug("VM object does not exist; creating it")
> > -        vm = kvm_vm.VM(name, params, qemu_path, image_dir, iso_dir,
> script_dir)
> > +        vm = kvm_vm.VM(name, params, qemu_path, image_dir, iso_dir,
> script_dir,
> > +                       env.get("address_cache"))
> >          kvm_utils.env_register_vm(env, name, vm)
> >  
> >      start_vm = False
> > diff --git a/client/tests/kvm/kvm_utils.py
> b/client/tests/kvm/kvm_utils.py
> > index ef257bc..17c2b73 100644
> > --- a/client/tests/kvm/kvm_utils.py
> > +++ b/client/tests/kvm/kvm_utils.py
> > @@ -1,5 +1,5 @@
> >  import md5, thread, subprocess, time, string, random, socket, os,
> signal, pty
> > -import select, re, logging
> > +import select, re, logging, commands
> >  from autotest_lib.client.bin import utils
> >  from autotest_lib.client.common_lib import error
> >  import kvm_subprocess
> > @@ -152,6 +152,32 @@ def get_mac_ip_pair_from_dict(dict):
> >      return (None, None)
> >  
> >  
> > +def verify_mac_ip_pair(mac, ip, timeout=3.0):
> > +    """
> > +    Connect to a given IP address and make sure its MAC address
> equals the
> > +    given MAC address.
> > +
> > +    @param mac: A MAC address.
> > +    @param ip: An IP address.
> > +    @return: True iff ip is assigned to mac.
> > +    """
> > +    s = socket.socket()
> > +    s.setblocking(False)
> > +    try:
> > +        s.connect((ip, 55555))
> > +    except socket.error:
> > +        pass
> > +    end_time = time.time() + timeout
> > +    while time.time() < end_time:
> > +        o = commands.getoutput("/sbin/arp -n")
> > +        if re.search(r"\b%s\b.*\b%s\b" % (ip, mac), o,
> re.IGNORECASE):
> > +            s.close()
> > +            return True
> > +        time.sleep(0.1)
> > +    s.close()
> > +    return False
> > +
> > +
> >  # Functions for working with the environment (a dict-like object)
> >  
> >  def is_vm(obj):
> > diff --git a/client/tests/kvm/kvm_vm.py
> b/client/tests/kvm/kvm_vm.py
> > index 0c35e64..6addf77 100644
> > --- a/client/tests/kvm/kvm_vm.py
> > +++ b/client/tests/kvm/kvm_vm.py
> > @@ -1,5 +1,5 @@
> >  #!/usr/bin/python
> > -import time, socket, os, logging, fcntl
> > +import time, socket, os, logging, fcntl, re, commands
> >  import kvm_utils, kvm_subprocess
> >  
> >  """
> > @@ -101,7 +101,7 @@ class VM:
> >      """
> >  
> >      def __init__(self, name, params, qemu_path, image_dir,
> iso_dir,
> > -                 script_dir):
> > +                 script_dir, address_cache):
> >          """
> >          Initialize the object and set a few attributes.
> >  
> > @@ -112,6 +112,7 @@ class VM:
> >          @param image_dir: The directory where images reside
> >          @param iso_dir: The directory where ISOs reside
> >          @param script_dir: The directory where -net tap scripts
> reside
> > +        @param address_cache: A dict that maps MAC addresses to IP
> addresses
> >          """
> >          self.process = None
> >          self.redirs = {}
> > @@ -124,6 +125,7 @@ class VM:
> >          self.image_dir = image_dir
> >          self.iso_dir = iso_dir
> >          self.script_dir = script_dir
> > +        self.address_cache = address_cache
> >  
> >          # Find available monitor filename
> >          while True:
> > @@ -137,7 +139,7 @@ class VM:
> >  
> >  
> >      def clone(self, name=None, params=None, qemu_path=None,
> image_dir=None,
> > -              iso_dir=None, script_dir=None):
> > +              iso_dir=None, script_dir=None, address_cache=None):
> >          """
> >          Return a clone of the VM object with optionally modified
> parameters.
> >          The clone is initially not alive and needs to be started
> using create().
> > @@ -150,6 +152,7 @@ class VM:
> >          @param image_dir: Optional new image dir
> >          @param iso_dir: Optional new iso directory
> >          @param script_dir: Optional new -net tap script directory
> > +        @param address_cache: A dict that maps MAC addresses to IP
> addresses
> >          """
> >          if name == None:
> >              name = self.name
> > @@ -163,7 +166,10 @@ class VM:
> >              iso_dir = self.iso_dir
> >          if script_dir == None:
> >              script_dir = self.script_dir
> > -        return VM(name, params, qemu_path, image_dir, iso_dir,
> script_dir)
> > +        if address_cache == None:
> > +            address_cache = self.address_cache
> > +        return VM(name, params, qemu_path, image_dir, iso_dir,
> script_dir,
> > +                  address_cache)
> >  
> >  
> >      def make_qemu_command(self, name=None, params=None,
> qemu_path=None,
> > @@ -625,21 +631,36 @@ class VM:
> >          nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
> >          if nic_params.get("nic_mode") == "tap":
> >              mac, ip =
> kvm_utils.get_mac_ip_pair_from_dict(nic_params)
> > +            if not mac:
> > +                logging.debug("MAC address unavailable")
> > +                return None
> > +            if not ip or nic_params.get("always_use_tcpdump") ==
> "yes":
> > +                # Get the IP address from the cache
> > +                ip = self.address_cache.get(mac)
> > +                if not ip:
> > +                    logging.debug("Could not find IP address for
> MAC address: "
> > +                                  "%s" % mac)
> > +                    return None
> > +                # Make sure the IP address is assigned to the right
> MAC address
> > +                if not kvm_utils.verify_mac_ip_pair(mac, ip):
> > +                    logging.debug("Could not verify MAC-IP address
> pair: "
> > +                                  "%s ---> %s" % (mac, ip))
> > +                    return None
> >              return ip
> >          else:
> >              return "localhost"
> >  
> >  
> > -    def get_port(self, port, index=0):
> > +    def get_port(self, port, nic_index=0):
> >          """
> >          Return the port in host space corresponding to port in
> guest space.
> >  
> >          @param port: Port number in host space.
> > -        @param index: Index of the NIC.
> > +        @param nic_index: Index of the NIC.
> >          @return: If port redirection is used, return the host port
> redirected
> >                  to guest port port. Otherwise return port.
> >          """
> > -        nic_name = kvm_utils.get_sub_dict_names(self.params,
> "nics")[index]
> > +        nic_name = kvm_utils.get_sub_dict_names(self.params,
> "nics")[nic_index]
> >          nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
> >          if nic_params.get("nic_mode") == "tap":
> >              return port
> > @@ -666,17 +687,19 @@ class VM:
> >          """
> >          address = self.get_address()
> >          port = self.get_port(int(self.params.get("ssh_port")))
> > -        if not port:
> > +        if not address or not port:
> > +            logging.debug("IP address or port unavailable")
> >              return False
> >          return kvm_utils.is_sshd_running(address, port,
> timeout=timeout)
> >  
> >  
> > -    def ssh_login(self, timeout=10):
> > +    def ssh_login(self, nic_index=0, timeout=10):
> >          """
> >          Log into the guest via SSH/Telnet.
> >          If timeout expires while waiting for output from the guest
> (e.g. a
> >          password prompt or a shell prompt) -- fail.
> >  
> > +        @param nic_index: The index of the NIC to connect to.
> >          @param timeout: Time (seconds) before giving up logging
> into the
> >                  guest.
> >          @return: kvm_spawn object on success and None on failure.
> > @@ -685,9 +708,10 @@ class VM:
> >          password = self.params.get("password", "")
> >          prompt = self.params.get("ssh_prompt", "[\#\$]")
> >          use_telnet = self.params.get("use_telnet") == "yes"
> > -        address = self.get_address()
> > +        address = self.get_address(nic_index)
> >          port = self.get_port(int(self.params.get("ssh_port")))
> > -        if not port:
> > +        if not address or not port:
> > +            logging.debug("IP address or port unavailable")
> >              return None
> >  
> >          if use_telnet:
> > @@ -702,7 +726,7 @@ class VM:
> >          return session
> >  
> >  
> > -    def scp_to_remote(self, local_path, remote_path, timeout=300):
> > +    def scp_to_remote(self, local_path, remote_path, nic_index=0,
> timeout=300):
> >          """
> >          Transfer files to the guest via SCP.
> >  
> > @@ -713,15 +737,16 @@ class VM:
> >          """
> >          username = self.params.get("username", "")
> >          password = self.params.get("password", "")
> > -        address = self.get_address()
> > +        address = self.get_address(nic_index)
> >          port = self.get_port(int(self.params.get("ssh_port")))
> > -        if not port:
> > +        if not address or not port:
> > +            logging.debug("IP address or port unavailable")
> >              return None
> >          return kvm_utils.scp_to_remote(address, port, username,
> password,
> >                                         local_path, remote_path,
> timeout)
> >  
> >  
> > -    def scp_from_remote(self, remote_path, local_path,
> timeout=300):
> > +    def scp_from_remote(self, remote_path, local_path, nic_index=0,
> timeout=300):
> >          """
> >          Transfer files from the guest via SCP.
> >  
> > @@ -732,9 +757,10 @@ class VM:
> >          """
> >          username = self.params.get("username", "")
> >          password = self.params.get("password", "")
> > -        address = self.get_address()
> > +        address = self.get_address(nic_index)
> >          port = self.get_port(int(self.params.get("ssh_port")))
> > -        if not port:
> > +        if not address or not port:
> > +            logging.debug("IP address or port unavailable")
> >              return None
> >          return kvm_utils.scp_from_remote(address, port, username,
> password,
> >                                           remote_path, local_path,
> timeout)
> > @@ -749,7 +775,7 @@ class VM:
> >          @return: A tuple (status, output). status is 0 on success
> and 1 on
> >                  failure.
> >          """
> > -        session = self.ssh_login(timeout)
> > +        session = self.ssh_login(timeout=timeout)
> >          if not session:
> >              return (1, "")
> >  
> >

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2009-08-11 16:16 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-08-06 18:41 [KVM-AUTOTEST PATCH CORRECTIONS] Corrections to the TAP patchset Michael Goldish
2009-08-06 18:41 ` [KVM-AUTOTEST PATCH 01/12] KVM test: add some MAC/IP address utilities to kvm_utils Michael Goldish
2009-08-06 18:41   ` [KVM-AUTOTEST PATCH 03/12] KVM test: add sample 'qemu-ifup' script Michael Goldish
2009-08-06 18:41   ` [KVM-AUTOTEST PATCH 11/12] KVM test: make VMs use a dynamic MAC-IP mapping generated by tcpdump Michael Goldish
2009-08-11 14:43     ` Jason Wang
     [not found] <1960092437.1769331250006819543.JavaMail.root@zmail05.collab.prod.int.phx2.redhat.com>
2009-08-11 16:16 ` Michael Goldish
  -- strict thread matches above, loose matches on Subject: below --
2009-08-02 23:58 [KVM-AUTOTEST PATCH 0/12] TAP support (and a few other small things) Michael Goldish
2009-08-02 23:58 ` [KVM-AUTOTEST PATCH 02/12] KVM test: add basic TAP support with static MAC-IP mapping Michael Goldish
2009-08-02 23:58   ` [KVM-AUTOTEST PATCH 03/12] KVM test: add sample 'qemu-ifup' script Michael Goldish
2009-08-02 23:58     ` [KVM-AUTOTEST PATCH 04/12] KVM test: add host-specific MAC-IP pool configuration Michael Goldish
2009-08-02 23:58       ` [KVM-AUTOTEST PATCH 05/12] KVM test: specify installation cdkeys in a dedicated file Michael Goldish
2009-08-02 23:58         ` [KVM-AUTOTEST PATCH 06/12] KVM test: minor style changes to kvm_config.py Michael Goldish
2009-08-02 23:58           ` [KVM-AUTOTEST PATCH 07/12] KVM test: kvm_config.py: do not fail when including a nonexistent file Michael Goldish
2009-08-02 23:58             ` [KVM-AUTOTEST PATCH 08/12] KVM test: kvm_subprocess: send user specified parameters to kvm_tail callbacks Michael Goldish
2009-08-02 23:58               ` [KVM-AUTOTEST PATCH 09/12] KVM test: dynamically detect and cache MAC-IP pairs using tcpdump Michael Goldish
2009-08-02 23:58                 ` [KVM-AUTOTEST PATCH 10/12] KVM test: kvm_utils.py: small fix for format_str_for_message() Michael Goldish
2009-08-02 23:58                   ` [KVM-AUTOTEST PATCH 01/12] KVM test: add some MAC/IP address utilities to kvm_utils Michael Goldish
2009-08-02 23:58                     ` [KVM-AUTOTEST PATCH 11/12] KVM test: make VMs use a dynamic MAC-IP mapping generated by tcpdump Michael Goldish

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.