From patchwork Mon Sep 27 22:43:47 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucas Meneghel Rodrigues X-Patchwork-Id: 213542 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id o8RMforG032214 for ; Mon, 27 Sep 2010 22:41:51 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1760334Ab0I0WlR (ORCPT ); Mon, 27 Sep 2010 18:41:17 -0400 Received: from mx1.redhat.com ([209.132.183.28]:43877 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1760331Ab0I0WlP (ORCPT ); Mon, 27 Sep 2010 18:41:15 -0400 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o8RMf5nm031809 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Mon, 27 Sep 2010 18:41:05 -0400 Received: from virtlab105.virt.bos.redhat.com (virtlab105.virt.bos.redhat.com [10.16.72.124]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o8RMf33o003834; Mon, 27 Sep 2010 18:41:04 -0400 From: Lucas Meneghel Rodrigues To: autotest@test.kernel.org Cc: kvm@vger.kernel.org, mst@redhat.com, akong@redhat.com, jasowang@redhat.com, psuriset@linux.vnet.ibm.com, mgoldish@redhat.com, Feng Yang Subject: [PATCH 01/18] KVM test: Add a new macaddress pool algorithm Date: Mon, 27 Sep 2010 18:43:47 -0400 Message-Id: <1285627444-2732-2-git-send-email-lmr@redhat.com> In-Reply-To: <1285627444-2732-1-git-send-email-lmr@redhat.com> References: <1285627444-2732-1-git-send-email-lmr@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.12 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Mon, 27 Sep 2010 22:41:51 +0000 (UTC) diff --git a/client/tests/kvm/kvm_utils.py b/client/tests/kvm/kvm_utils.py index fb2d1c2..9390393 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,121 @@ def get_sub_dict_names(dict, keyword): # Functions related to MAC/IP addresses +def _generate_mac_address_prefix(): + """ + Generate a MAC address prefix. By convention we will set KVM autotest + MAC addresses to start with 0x9a. + """ + r = random.SystemRandom() + l = [0x9a, r.randint(0x00, 0x7f), r.randint(0x00, 0x7f), + r.randint(0x00, 0xff)] + prefix = ':'.join(map(lambda x: "%02x" % x, l)) + ":" + return prefix + + +def generate_mac_address_prefix(): + """ + Generate a random MAC address prefix and add it to the MAC pool dictionary. + If there's a MAC prefix there already, do not update the MAC pool and just + return what's in there. + """ + lock_file = open("/tmp/mac_lock", 'w') + fcntl.lockf(lock_file.fileno() ,fcntl.LOCK_EX) + mac_pool = shelve.open("/tmp/address_pool", writeback=False) + + if mac_pool.get('prefix'): + prefix = mac_pool.get('prefix') + logging.debug('Retrieved previously generated MAC prefix for this ' + 'host: %s', prefix) + else: + prefix = _generate_mac_address_prefix() + mac_pool['prefix'] = prefix + logging.debug('Generated MAC address prefix for this host: %s', prefix) + + mac_pool.close() + fcntl.lockf(lock_file.fileno(), fcntl.LOCK_UN) + lock_file.close() + + return prefix + + +def generate_mac_address(root_dir, instance_vm, nic_index, prefix=None): + """ + 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: MAC address string. + """ + if prefix is None: + prefix = generate_mac_address_prefix() + + r = random.SystemRandom() + 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" % (r.randint(0x00,0xfe), + r.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 address for NIC %s: %s ", key, 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("NIC: %s" % key) + logging.debug("Contents of MAC pool: %s" % mac_pool) + else: + logging.debug("Freeing MAC addr for NIC %s: %s", key, 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 135d08e..13eaac1 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,12 @@ class VM: if not glob.glob("/tmp/*%s" % self.instance): break + if self.mac_prefix is None: + self.mac_prefix = kvm_utils.generate_mac_address_prefix() - 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 +143,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 +153,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_mac_address(self.get_mac_address(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): @@ -387,6 +406,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_mac_address(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 @@ -750,11 +776,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...") @@ -880,10 +910,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_mac_address(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) @@ -896,6 +929,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)) @@ -925,6 +959,45 @@ class VM: return self.redirs.get(port) + def get_mac_address(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_mac_address(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 %s is already in use!", mac) + mac_pool.close() + fcntl.lockf(lock_file.fileno(), fcntl.LOCK_UN) + lock_file.close() + + def get_pid(self): """ Return the VM's PID. If the VM is dead return None. diff --git a/client/tests/kvm/tests_base.cfg.sample b/client/tests/kvm/tests_base.cfg.sample index 167e86d..5721bf1 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