From patchwork Mon Jul 5 16:23:31 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amos Kong X-Patchwork-Id: 110265 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 o65GNnWF005922 for ; Mon, 5 Jul 2010 16:23:49 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756723Ab0GEQXj (ORCPT ); Mon, 5 Jul 2010 12:23:39 -0400 Received: from mx1.redhat.com ([209.132.183.28]:41190 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756396Ab0GEQXi (ORCPT ); Mon, 5 Jul 2010 12:23:38 -0400 Received: from int-mx03.intmail.prod.int.phx2.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.16]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o65GNbnI020973 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Mon, 5 Jul 2010 12:23:37 -0400 Received: from [127.0.1.1] (vpn1-5-28.sin2.redhat.com [10.67.5.28]) by int-mx03.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o65GNWJE003191; Mon, 5 Jul 2010 12:23:34 -0400 Subject: [PATCH] KVM-test: Add a new macaddress pool algorithm To: lmr@redhat.com From: Amos Kong Cc: autotest@test.kernel.org, jasowang@redhat.com, kvm@vger.kernel.org Date: Tue, 06 Jul 2010 00:23:31 +0800 Message-ID: <20100705162330.10432.18194.stgit@z> User-Agent: StGit/0.15 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.67 on 10.5.11.16 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, 05 Jul 2010 16:23:50 +0000 (UTC) diff --git a/client/tests/kvm/kvm_utils.py b/client/tests/kvm/kvm_utils.py index 0372565..432574b 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 @@ -76,6 +77,104 @@ def get_sub_dict_names(dict, keyword): # Functions related to MAC/IP addresses +def get_mac_from_pool(root_dir, vm, nic_index, prefix='00:11:22:33:'): + """ + random generated mac address. + + 1) First try to generate macaddress based on the mac address prefix. + 2) And then try to use total random generated mac address. + + @param root_dir: Root dir for kvm + @param 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_filename = os.path.join(root_dir, "mac_lock") + lock_file = open(lock_filename, 'w') + fcntl.lockf(lock_file.fileno() ,fcntl.LOCK_EX) + mac_filename = os.path.join(root_dir, "address_pool") + mac_shelve = shelve.open(mac_filename, writeback=False) + + mac_pool = mac_shelve.get("macpool") + + if not mac_pool: + mac_pool = {} + found = False + + val = "%s:%s" % (vm, nic_index) + for key in mac_pool.keys(): + if val in mac_pool[key]: + mac_pool[key].append(val) + found = True + mac = key + + while not found: + postfix = "%02x:%02x" % (random.randint(0x00,0xfe), + random.randint(0x00,0xfe)) + mac = prefix + postfix + 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 not in mac_pool.keys() or 0 == len(mac_pool[mac]): + mac_pool[mac] = ["%s:%s" % (vm,nic_index)] + found = True + mac_shelve["macpool"] = mac_pool + logging.debug("generating mac addr %s " % mac) + + mac_shelve.close() + fcntl.lockf(lock_file.fileno(), fcntl.LOCK_UN) + lock_file.close() + return mac + + +def put_mac_to_pool(root_dir, mac, vm): + """ + Put the macaddress back to address pool + + @param root_dir: Root dir for kvm + @param vm: Here we use instance attribute of vm + @param mac: mac address will be put. + @Return: mac address. + """ + + lock_filename = os.path.join(root_dir, "mac_lock") + lock_file = open(lock_filename, 'w') + fcntl.lockf(lock_file.fileno() ,fcntl.LOCK_EX) + mac_filename = os.path.join(root_dir, "address_pool") + mac_shelve = shelve.open(mac_filename, writeback=False) + + mac_pool = mac_shelve.get("macpool") + + if not mac_pool or (not mac in mac_pool): + logging.debug("Try to free a macaddress does no in pool") + logging.debug("macaddress is %s" % mac) + logging.debug("pool is %s" % mac_pool) + else: + if len(mac_pool[mac]) <= 1: + mac_pool.pop(mac) + else: + for value in mac_pool[mac]: + if vm in value: + mac_pool[mac].remove(value) + break + if len(mac_pool[mac]) == 0: + mac_pool.pop(mac) + + mac_shelve["macpool"] = mac_pool + logging.debug("freeing mac addr %s " % mac) + + mac_shelve.close() + fcntl.lockf(lock_file.fileno(), fcntl.LOCK_UN) + lock_file.close() + return mac + + 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 87b9126..0d7d17d 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 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,16 @@ class VM: if not glob.glob("/tmp/*%s" % self.instance): break + if self.mac_prefix is None: + # FIXME: we should drop the hard-coded mac address fetching command. + 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, mac_clone=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 +147,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 mac_clone: Clone mac address or not. """ if name is None: name = self.name @@ -147,9 +157,19 @@ 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 mac_clone: + # Clone mac address by coping 'self.instance' to the new vm. + vm.instance = self.instance + return vm + def free_mac_address(self): + nic_num = len(kvm_utils.get_sub_dict_names(self.params, "nics")) + for nic in range(nic_num): + mac = self.get_macaddr(nic_index=nic) + kvm_utils.put_mac_to_pool(self.root_dir, mac, vm=self.instance) + def make_qemu_command(self, name=None, params=None, root_dir=None): """ Generate a qemu command line. All parameters are optional. If a @@ -352,6 +372,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.get_mac_from_pool(self.root_dir, + vm=self.instance, + nic_index=vlan, + prefix=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 @@ -362,7 +389,7 @@ class VM: if downscript: downscript = kvm_utils.get_path(root_dir, downscript) qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"), - nic_params.get("nic_ifname"), + self.get_ifname(vlan), script, downscript, self.netdev_id[vlan]) # Proceed to next NIC vlan += 1 @@ -675,7 +702,7 @@ class VM: lockfile.close() - def destroy(self, gracefully=True): + def destroy(self, gracefully=True, free_macaddr=True): """ Destroy the VM. @@ -686,6 +713,7 @@ class VM: @param gracefully: Whether an attempt will be made to end the VM using a shell command before trying to end the qemu process with a 'quit' or a kill signal. + @param free_macaddr: Whether free macaddresses when destory a vm. """ try: # Is it already dead? @@ -706,11 +734,18 @@ 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, free mac address.") + # free mac address + if free_macaddr: + self.free_mac_address() return finally: session.close() + # free mac address + if free_macaddr: + self.free_mac_address() + if self.monitor: # Try to destroy with a monitor command logging.debug("Trying to kill VM with monitor command...") @@ -844,10 +879,14 @@ 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 + else: + 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) @@ -860,6 +899,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)) @@ -888,6 +928,71 @@ class VM: "redirected" % port) return self.redirs.get(port) + def get_ifname(self, nic_index=0): + """ + Return the ifname of tap device for the guest nic. + + @param nic_index: Index of the NIC + """ + + nics = kvm_utils.get_sub_dict_names(self.params, "nics") + nic_name = nics[nic_index] + nic_params = kvm_utils.get_sub_dict(self.params, nic_name) + if nic_params.get("nic_ifname"): + return nic_params.get("nic_ifname") + else: + return "%s_%s_%s" % (nic_params.get("nic_model"), + nic_index, self.vnc_port) + + def get_macaddr(self, nic_index=0): + """ + Return the macaddr of guest nic. + + @param nic_index: Index of the NIC + """ + mac_filename = os.path.join(self.root_dir, "address_pool") + mac_shelve = shelve.open(mac_filename, writeback=False) + mac_pool = mac_shelve.get("macpool") + val = "%s:%s" % (self.instance, nic_index) + for key in mac_pool.keys(): + if val in mac_pool[key]: + return key + 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_filename = os.path.join(self.root_dir, "mac_lock") + lock_file = open(lock_filename, 'w') + fcntl.lockf(lock_file.fileno() ,fcntl.LOCK_EX) + mac_filename = os.path.join(self.root_dir, "address_pool") + mac_shelve = shelve.open(mac_filename, writeback=False) + mac_pool = mac_shelve.get("macpool") + if not mac_pool: + mac_pool = {} + value = "%s:%s" % (self.instance, nic_index) + if mac not in mac_pool.keys(): + for key in mac_pool.keys(): + if value in mac_pool[key]: + mac_pool[key].remove(value) + if len(mac_pool[key]) == 0: + mac_pool.pop(key) + mac_pool[mac] = [value] + else: + if shareable: + mac_pool[mac].append(value) + else: + logging.error("Mac address already be used!") + return False + mac_shelve["macpool"] = mac_pool + mac_shelve.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 1ed5237..02f92f3 100644 --- a/client/tests/kvm/tests_base.cfg.sample +++ b/client/tests/kvm/tests_base.cfg.sample @@ -51,7 +51,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