On Mon, 2010-08-23 at 17:07 +0800, Amos Kong wrote:
> Old method uses the addresses in the config files which could lead serious
> problem when multiple tests running in different hosts.
> This patch adds a new macaddress pool algorithm, it generates the mac prefix
> based on mac address of the host, and fix it to correspond to IEEE802.
> When user have set the mac_prefix in the config file, we should use it instead
> of the dynamic generated mac prefix.
> Add a parameter like 'preserve_mac', to preserve the original mac address, for
> things like migration.
> 
> MAC addresses are recorded into a dictionary 'address_pool' in following
> format: {{'20100310-165222-Wt7l:0' : 'AE:9D:94:6A:9b:f9'},...}
>   20100310-165222-Wt7l : instance attribute of VM
>   0                    : index of NIC
>   AE:9D:94:6A:9b:f9    : mac address
> Use 'vm instance' + 'nic index' as the key, macaddress is the value.
> 
> Changs from v1:
> - Use 'vm instance' + 'nic index' as the key of address_pool, address is 
> value.
> - Put 'mac_lock' and 'address_pool' to '/tmp', for sharing them to other
>   autotest instances running on the same host.
> - Change function names for less confusion.
> - Do not copy 'vm.instance' in vm.clone()
> - Split 'adding get_ifname function' to another patch

Hi Amos, one thing that did strike me is that we don't need to base
ourselves in a host's physical address, instead, we can generate a
prefix and keep it in the MAC pool. Also, I found some inconsistencies,
that I've fixed in a later version of the patch, which I already sent to
the mailing list. Let me know what you think.

> Signed-off-by: Jason Wang <[email protected]>
> Signed-off-by: Feng Yang <[email protected]>
> Signed-off-by: Amos Kong <[email protected]>
> ---
>  0 files changed, 0 insertions(+), 0 deletions(-)
> 
> diff --git a/client/tests/kvm/kvm_utils.py b/client/tests/kvm/kvm_utils.py
> index fb2d1c2..b019fc5 100644
> --- a/client/tests/kvm/kvm_utils.py
> +++ b/client/tests/kvm/kvm_utils.py
> @@ -5,6 +5,7 @@ KVM test utility functions.
>  """
>  
>  import time, string, random, socket, os, signal, re, logging, commands, 
> cPickle
> +import fcntl, shelve
>  from autotest_lib.client.bin import utils
>  from autotest_lib.client.common_lib import error, logging_config
>  import kvm_subprocess
> @@ -82,6 +83,79 @@ def get_sub_dict_names(dict, keyword):
>  
>  # Functions related to MAC/IP addresses
>  
> +def generate_mac_address(root_dir, instance_vm, nic_index, 
> prefix='00:11:22:33:'):
> +    """
> +    Random generate a MAC address and add it to the MAC pool.
> +
> +    Try to generate macaddress based on the mac address prefix, add it to a
> +    dictionary 'address_pool'.
> +    key = VM instance + nic index, value = mac address
> +    {['20100310-165222-Wt7l:0'] : 'AE:9D:94:6A:9b:f9'}
> +
> +    @param root_dir: Root dir for kvm
> +    @param instance_vm: Here we use instance of vm
> +    @param nic_index: The index of nic
> +    @param prefix: Prefix of mac address
> +    @Return: Return mac address.
> +    """
> +    lock_file = open("/tmp/mac_lock", 'w')
> +    fcntl.lockf(lock_file.fileno() ,fcntl.LOCK_EX)
> +    mac_pool = shelve.open("/tmp/address_pool", writeback=False)
> +    found = False
> +    key = "%s:%s" % (instance_vm, nic_index)
> +
> +    if mac_pool.get(key):
> +        found = True
> +        mac = mac_pool.get(key)
> +
> +    while not found:
> +        suffix = "%02x:%02x" % (random.randint(0x00,0xfe),
> +                                random.randint(0x00,0xfe))
> +        mac = prefix + suffix
> +        mac_list = mac.split(":")
> +        # Clear multicast bit
> +        mac_list[0] = int(mac_list[0],16) & 0xfe
> +        # Set local assignment bit (IEEE802)
> +        mac_list[0] = mac_list[0] | 0x02
> +        mac_list[0] = "%02x" % mac_list[0]
> +        mac = ":".join(mac_list)
> +        if mac in [mac_pool.get(k) for k in mac_pool.keys()]:
> +                continue
> +        mac_pool[key] = mac
> +        found = True
> +    logging.debug("Generated mac addr %s " % mac)
> +
> +    mac_pool.close()
> +    fcntl.lockf(lock_file.fileno(), fcntl.LOCK_UN)
> +    lock_file.close()
> +    return mac
> +
> +
> +def free_mac_address(root_dir, instance_vm, nic_index):
> +    """
> +    Free mac address from address pool
> +
> +    @param root_dir: Root dir for kvm
> +    @param instance_vm: Here we use instance attribute of vm
> +    @param nic_index: The index of nic
> +    """
> +    lock_file = open("/tmp/mac_lock", 'w')
> +    fcntl.lockf(lock_file.fileno() ,fcntl.LOCK_EX)
> +    mac_pool = shelve.open("/tmp/address_pool", writeback=False)
> +    key = "%s:%s" % (instance_vm, nic_index)
> +    if not mac_pool or (not key in mac_pool.keys()):
> +        logging.debug("Nic not present in the MAC pool, not modifying pool")
> +        logging.debug("Key : %s" % key)
> +        logging.debug("pool is %s" % mac_pool)
> +    else:
> +        logging.debug("Freeing mac addr %s" % mac_pool[key])
> +        mac_pool.pop(key)
> +
> +    mac_pool.close()
> +    fcntl.lockf(lock_file.fileno(), fcntl.LOCK_UN)
> +    lock_file.close()
> +
> +
>  def mac_str_to_int(addr):
>      """
>      Convert MAC address string to integer.
> diff --git a/client/tests/kvm/kvm_vm.py b/client/tests/kvm/kvm_vm.py
> index bdc9aab..6812c98 100755
> --- a/client/tests/kvm/kvm_vm.py
> +++ b/client/tests/kvm/kvm_vm.py
> @@ -5,7 +5,7 @@ Utility classes and functions to handle Virtual Machine 
> creation using qemu.
>  @copyright: 2008-2009 Red Hat Inc.
>  """
>  
> -import time, socket, os, logging, fcntl, re, commands, glob
> +import time, socket, os, logging, fcntl, re, commands, shelve, glob
>  import kvm_utils, kvm_subprocess, kvm_monitor, rss_file_transfer
>  from autotest_lib.client.common_lib import error
>  from autotest_lib.client.bin import utils
> @@ -117,6 +117,7 @@ class VM:
>          self.params = params
>          self.root_dir = root_dir
>          self.address_cache = address_cache
> +        self.mac_prefix = params.get('mac_prefix')
>          self.netdev_id = []
>  
>          # Find a unique identifier for this VM
> @@ -126,8 +127,15 @@ class VM:
>              if not glob.glob("/tmp/*%s" % self.instance):
>                  break
>  
> +        if self.mac_prefix is None:
> +            s, o = commands.getstatusoutput("ifconfig eth0")
> +            if s == 0:
> +                mac = re.findall("HWaddr (\S*)", o)[0]
> +                self.mac_prefix = '00' + mac[8:] + ':'
> +
>  
> -    def clone(self, name=None, params=None, root_dir=None, 
> address_cache=None):
> +    def clone(self, name=None, params=None, root_dir=None,
> +                    address_cache=None, preserve_mac=True):
>          """
>          Return a clone of the VM object with optionally modified parameters.
>          The clone is initially not alive and needs to be started using 
> create().
> @@ -138,6 +146,7 @@ class VM:
>          @param params: Optional new VM creation parameters
>          @param root_dir: Optional new base directory for relative filenames
>          @param address_cache: A dict that maps MAC addresses to IP addresses
> +        @param preserve_mac: Clone mac address or not.
>          """
>          if name is None:
>              name = self.name
> @@ -147,7 +156,20 @@ class VM:
>              root_dir = self.root_dir
>          if address_cache is None:
>              address_cache = self.address_cache
> -        return VM(name, params, root_dir, address_cache)
> +        vm = VM(name, params, root_dir, address_cache)
> +        if preserve_mac:
> +            vlan = 0
> +            for nic_name in kvm_utils.get_sub_dict_names(params, "nics"):
> +                nic_params = kvm_utils.get_sub_dict(params, nic_name)
> +                vm.set_macaddr(self.get_macaddr(vlan), vlan, True)
> +                vlan += 1
> +        return vm
> +
> +
> +    def free_mac_addresses(self):
> +        nic_num = len(kvm_utils.get_sub_dict_names(self.params, "nics"))
> +        for i in range(nic_num):
> +            kvm_utils.free_mac_address(self.root_dir, self.instance, i)
>  
> 
>      def make_qemu_command(self, name=None, params=None, root_dir=None):
> @@ -386,6 +408,13 @@ class VM:
>              mac = None
>              if "address_index" in nic_params:
>                  mac = kvm_utils.get_mac_ip_pair_from_dict(nic_params)[0]
> +                self.set_macaddr(mac=mac, nic_index=vlan)
> +            else:
> +                mac = kvm_utils.generate_mac_address(self.root_dir,
> +                                                     self.instance,
> +                                                     vlan,
> +                                                     self.mac_prefix)
> +
>              qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac,
>                                  self.netdev_id[vlan])
>              # Handle the '-net tap' or '-net user' part
> @@ -749,11 +778,15 @@ class VM:
>                          logging.debug("Shutdown command sent; waiting for VM 
> "
>                                        "to go down...")
>                          if kvm_utils.wait_for(self.is_dead, 60, 1, 1):
> -                            logging.debug("VM is down")
> +                            logging.debug("VM is down, freeing mac address.")
> +                            self.free_mac_addresses()
>                              return
>                      finally:
>                          session.close()
>  
> +            # Free mac addresses
> +            self.free_mac_addresses()
> +
>              if self.monitor:
>                  # Try to destroy with a monitor command
>                  logging.debug("Trying to kill VM with monitor command...")
> @@ -879,10 +912,13 @@ class VM:
>          nic_name = nics[index]
>          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)
> +            mac = self.get_macaddr(index)
>              if not mac:
>                  logging.debug("MAC address unavailable")
>                  return None
> +            mac = mac.lower()
> +            ip = 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)
> @@ -895,6 +931,7 @@ class VM:
>                               for nic in nics]
>                  macs = [kvm_utils.get_mac_ip_pair_from_dict(dict)[0]
>                          for dict in nic_dicts]
> +                macs.append(mac)
>                  if not kvm_utils.verify_ip_address_ownership(ip, macs):
>                      logging.debug("Could not verify MAC-IP address mapping: "
>                                    "%s ---> %s" % (mac, ip))
> @@ -923,6 +960,42 @@ class VM:
>                               "redirected" % port)
>              return self.redirs.get(port)
>  
> +    def get_macaddr(self, nic_index=0):
> +        """
> +        Return the macaddr of guest nic.
> +
> +        @param nic_index: Index of the NIC
> +        """
> +        mac_pool = shelve.open("/tmp/address_pool", writeback=False)
> +        key = "%s:%s" % (self.instance, nic_index)
> +        if key in mac_pool.keys():
> +            return mac_pool[key]
> +        else:
> +            return None
> +
> +    def set_macaddr(self, mac, nic_index=0, shareable=False):
> +        """
> +        Set mac address for guest. Note: It just update address pool.
> +
> +        @param mac: address will set to guest
> +        @param nic_index: Index of the NIC
> +        @param shareable: Where VM can share mac with other VM or not.
> +        """
> +        lock_file = open("/tmp/mac_lock", 'w')
> +        fcntl.lockf(lock_file.fileno() ,fcntl.LOCK_EX)
> +        mac_pool = shelve.open("/tmp/address_pool", writeback=False)
> +        key = "%s:%s" % (self.instance, nic_index)
> +
> +        if not mac in [mac_pool[i] for i in mac_pool.keys()]:
> +            mac_pool[key] = mac
> +        else:
> +            if shareable:
> +                mac_pool[key] = mac
> +            else:
> +                logging.error("Mac address already be used!")
> +        mac_pool.close()
> +        fcntl.lockf(lock_file.fileno(), fcntl.LOCK_UN)
> +        lock_file.close()
>  
>      def get_pid(self):
>          """
> diff --git a/client/tests/kvm/tests_base.cfg.sample 
> b/client/tests/kvm/tests_base.cfg.sample
> index cb35f5e..26760f6 100644
> --- a/client/tests/kvm/tests_base.cfg.sample
> +++ b/client/tests/kvm/tests_base.cfg.sample
> @@ -54,7 +54,7 @@ guest_port_remote_shell = 22
>  nic_mode = user
>  #nic_mode = tap
>  nic_script = scripts/qemu-ifup
> -address_index = 0
> +#address_index = 0
>  run_tcpdump = yes
>  
>  # Misc
> 
> _______________________________________________
> Autotest mailing list
> [email protected]
> http://test.kernel.org/cgi-bin/mailman/listinfo/autotest


_______________________________________________
Autotest mailing list
[email protected]
http://test.kernel.org/cgi-bin/mailman/listinfo/autotest

Reply via email to