@@ -187,7 +187,7 @@ def migrate(vm, env=None, mig_timeout=3600, mig_protocol="tcp",
# Clone the source VM and ask the clone to wait for incoming migration
dest_vm = vm.clone()
- if not dest_vm.create(extra_params=mig_extra_params):
+ if not dest_vm.create(extra_params=mig_extra_params, mac_source=vm):
raise error.TestError("Could not create dest VM")
try:
@@ -11,6 +11,17 @@ from autotest_lib.client.common_lib import error, logging_config
import kvm_subprocess
+def _lock_file(filename):
+ f = open(filename, "w")
+ fcntl.lockf(f, fcntl.LOCK_EX)
+ return f
+
+
+def _unlock_file(f):
+ fcntl.lockf(f, fcntl.LOCK_UN)
+ f.close()
+
+
def dump_env(obj, filename):
"""
Dump KVM test environment to a file.
@@ -83,119 +94,113 @@ 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 _open_mac_pool(lock_mode):
+ lock_file = open("/tmp/mac_lock", "w+")
+ fcntl.lockf(lock_file, lock_mode)
+ pool = shelve.open("/tmp/address_pool")
+ return pool, lock_file
-def generate_mac_address_prefix():
+def _close_mac_pool(pool, lock_file):
+ pool.close()
+ fcntl.lockf(lock_file, fcntl.LOCK_UN)
+ lock_file.close()
+
+
+def _generate_mac_address_prefix(mac_pool):
"""
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)
+ return what's in there. By convention we will set KVM autotest MAC
+ addresses to start with 0x9a.
- if mac_pool.get('prefix'):
- prefix = mac_pool.get('prefix')
- logging.debug('Retrieved previously generated MAC prefix for this '
- 'host: %s', prefix)
+ @param mac_pool: The MAC address pool object.
+ @return: The MAC address prefix.
+ """
+ if "prefix" in mac_pool:
+ prefix = mac_pool["prefix"]
+ logging.debug("Used previously generated MAC address 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()
-
+ r = random.SystemRandom()
+ prefix = "9a:%02x:%02x:%02x:" % (r.randint(0x00, 0xff),
+ r.randint(0x00, 0xff),
+ r.randint(0x00, 0xff))
+ mac_pool["prefix"] = prefix
+ logging.debug("Generated MAC address prefix for this host: %s", prefix)
return prefix
-def generate_mac_address(root_dir, instance_vm, nic_index, prefix=None):
+def generate_mac_address(vm_instance, nic_index):
"""
- Random generate a MAC address and add it to the MAC pool.
+ Randomly generate a MAC address and add it to the MAC address 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'}
+ Try to generate a MAC address based on a randomly generated MAC address
+ prefix and add it to a persistent dictionary.
+ key = VM instance + NIC index, value = MAC address
+ e.g. {'20100310-165222-Wt7l:0': '9a:5d: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.
+ @param vm_instance: The instance attribute of a VM.
+ @param nic_index: The index of the NIC.
@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()]:
+ mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
+ key = "%s:%s" % (vm_instance, nic_index)
+ if key in mac_pool:
+ mac = mac_pool[key]
+ else:
+ prefix = _generate_mac_address_prefix(mac_pool)
+ r = random.SystemRandom()
+ while key not in mac_pool:
+ mac = prefix + "%02x:%02x" % (r.randint(0x00, 0xff),
+ r.randint(0x00, 0xff))
+ if mac in mac_pool.values():
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()
+ mac_pool[key] = mac
+ logging.debug("Generated MAC address for NIC %s: %s", key, mac)
+ _close_mac_pool(mac_pool, lock_file)
return mac
-def free_mac_address(root_dir, instance_vm, nic_index):
+def free_mac_address(vm_instance, nic_index):
"""
- Free mac address from address pool
+ Remove a MAC address from the 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
+ @param vm_instance: The instance attribute of a VM.
+ @param nic_index: The index of the 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, lock_file = _open_mac_pool(fcntl.LOCK_EX)
+ key = "%s:%s" % (vm_instance, nic_index)
+ if key in mac_pool:
+ logging.debug("Freeing MAC address for NIC %s: %s", key, mac_pool[key])
+ del mac_pool[key]
+ _close_mac_pool(mac_pool, lock_file)
- mac_pool.close()
- fcntl.lockf(lock_file.fileno(), fcntl.LOCK_UN)
- lock_file.close()
+
+def set_mac_address(vm_instance, nic_index, mac):
+ """
+ Set a MAC address in the pool.
+
+ @param vm_instance: The instance attribute of a VM.
+ @param nic_index: The index of the NIC.
+ """
+ mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
+ mac_pool["%s:%s" % (vm_instance, nic_index)] = mac
+ _close_mac_pool(mac_pool, lock_file)
+
+
+def get_mac_address(vm_instance, nic_index):
+ """
+ Return a MAC address from the pool.
+
+ @param vm_instance: The instance attribute of a VM.
+ @param nic_index: The index of the NIC.
+ @return: MAC address string.
+ """
+ mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_SH)
+ mac = mac_pool.get("%s:%s" % (vm_instance, nic_index))
+ _close_mac_pool(mac_pool, lock_file)
+ return mac
def mac_str_to_int(addr):
@@ -109,16 +109,15 @@ class VM:
self.serial_console = None
self.redirs = {}
self.vnc_port = 5900
- self.uuid = None
self.monitors = []
self.pci_assignable = None
+ self.netdev_id = []
+ self.uuid = None
self.name = name
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
while True:
@@ -127,12 +126,8 @@ 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, preserve_mac=True):
+ def clone(self, name=None, params=None, root_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().
@@ -143,7 +138,6 @@ 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
@@ -153,20 +147,7 @@ class VM:
root_dir = self.root_dir
if address_cache is None:
address_cache = self.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)
+ return VM(name, params, root_dir, address_cache)
def make_qemu_command(self, name=None, params=None, root_dir=None):
@@ -423,16 +404,7 @@ class VM:
for nic_name in kvm_utils.get_sub_dict_names(params, "nics"):
nic_params = kvm_utils.get_sub_dict(params, nic_name)
# Handle the '-net nic' part
- 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)
-
+ mac = self.get_mac_address(vlan)
qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac,
self.netdev_id[vlan],
nic_params.get("nic_extra_params"))
@@ -536,7 +508,8 @@ class VM:
def create(self, name=None, params=None, root_dir=None,
- for_migration=False, timeout=5.0, extra_params=None):
+ for_migration=False, timeout=5.0, extra_params=None,
+ mac_source=None):
"""
Start the VM by running a qemu command.
All parameters are optional. The following applies to all parameters
@@ -551,6 +524,8 @@ class VM:
option
@param extra_params: extra params for qemu command.e.g -incoming option
Please use this parameter instead of for_migration.
+ @param mac_source: A VM object from which to copy MAC addresses. If not
+ specified, new addresses will be generated.
"""
self.destroy()
@@ -625,6 +600,15 @@ class VM:
self.uuid = f.read().strip()
f.close()
+ # Generate or copy MAC addresses for all NICs
+ num_nics = len(kvm_utils.get_sub_dict_names(params, "nics"))
+ for vlan in range(num_nics):
+ mac = mac_source and mac_source.get_mac_address(vlan)
+ if mac:
+ kvm_utils.set_mac_address(self.instance, vlan, mac)
+ else:
+ kvm_utils.generate_mac_address(self.instance, vlan)
+
# Assign a PCI assignable device
self.pci_assignable = None
pa_type = params.get("pci_assignable")
@@ -799,14 +783,10 @@ class VM:
"to go down...")
if kvm_utils.wait_for(self.is_dead, 60, 1, 1):
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...")
@@ -846,6 +826,9 @@ class VM:
os.unlink(f)
except OSError:
pass
+ num_nics = len(kvm_utils.get_sub_dict_names(self.params, "nics"))
+ for vlan in range(num_nics):
+ self.free_mac_address(vlan)
@property
@@ -1002,41 +985,25 @@ class VM:
def get_mac_address(self, nic_index=0):
"""
- Return the macaddr of guest nic.
+ Return the MAC address of a 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]
+ 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("address_index"):
+ return kvm_utils.get_mac_ip_pair_from_dict(nic_params)[0]
else:
- return None
+ return kvm_utils.get_mac_address(self.instance, nic_index)
- def set_mac_address(self, mac, nic_index=0, shareable=False):
+ def free_mac_address(self, nic_index=0):
"""
- Set mac address for guest. Note: It just update address pool.
+ Free a NIC's MAC address.
- @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()
+ kvm_utils.free_mac_address(self.instance, nic_index)
def get_pid(self):
@@ -24,14 +24,11 @@ def run_mac_change(test, params, env):
raise error.TestFail("Could not log into guest '%s'" % vm.name)
old_mac = vm.get_mac_address(0)
- new_different = False
- while not new_different:
- kvm_utils.free_mac_address(vm.root_dir, vm.instance, 0)
- new_mac = kvm_utils.generate_mac_address(vm.root_dir,
- vm.instance,
- 0, vm.mac_prefix)
+ while True:
+ vm.free_mac_address(0)
+ new_mac = kvm_utils.generate_mac_address(vm.instance, 0)
if old_mac != new_mac:
- new_different = True
+ break
logging.info("The initial MAC address is %s", old_mac)
interface = kvm_test_utils.get_linux_ifname(session, old_mac)
# Start change MAC address