From patchwork Mon Aug 23 08:51:55 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amos Kong X-Patchwork-Id: 123761 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o7N8t5oh026561 for ; Mon, 23 Aug 2010 09:00:23 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752151Ab0HWItA (ORCPT ); Mon, 23 Aug 2010 04:49:00 -0400 Received: from mx1.redhat.com ([209.132.183.28]:35586 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751772Ab0HWIs7 (ORCPT ); Mon, 23 Aug 2010 04:48:59 -0400 Received: from int-mx08.intmail.prod.int.phx2.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o7N8mxT1001691 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Mon, 23 Aug 2010 04:48:59 -0400 Received: from [0.0.0.190] (dhcp-91-190.nay.redhat.com [10.66.91.190]) by int-mx08.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o7N8muVS003724; Mon, 23 Aug 2010 04:48:57 -0400 Subject: [PATCH v2 01/17] KVM-test: Add a new macaddress pool algorithm To: autotest@vger.kernel.org From: Amos Kong Cc: lmr@redhat.com, kvm@vger.kernel.org Date: Mon, 23 Aug 2010 16:51:55 +0800 Message-ID: <20100823085155.19173.55699.stgit@190> In-Reply-To: <20100823084745.19173.61655.stgit@190> References: <20100823084745.19173.61655.stgit@190> User-Agent: StGit/0.15 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.67 on 10.5.11.21 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 (demeter.kernel.org [140.211.167.41]); Mon, 23 Aug 2010 09:35:39 +0000 (UTC) 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