diff --git a/libvirt/tests/cfg/virtual_network/update_device/update_device_coalesce.cfg b/libvirt/tests/cfg/virtual_network/update_device/update_device_coalesce.cfg new file mode 100644 index 0000000000..15c053c137 --- /dev/null +++ b/libvirt/tests/cfg/virtual_network/update_device/update_device_coalesce.cfg @@ -0,0 +1,35 @@ +- virtual_network.update_device.coalesce: + type = update_device_coalesce + host_iface = + start_vm = no + timeout = 240 + outside_ip = 'www.redhat.com' + extra_attrs = {} + vm_ping_outside = pass + variants: + - delete_iface_coalesce: + rx_frames = 32 + coalesce = {'coalesce': {'max': '${rx_frames}'}} + - add_iface_coalesce: + coalesce = {} + updated_rx_frames = 64 + updated_coalesce = {'coalesce': {'max': '${updated_rx_frames}'}} + - update_iface_coalesce: + rx_frames = 64 + coalesce = {'coalesce': {'max': '${rx_frames}'}} + updated_rx_frames = 32 + updated_coalesce = {'coalesce': {'max': '${updated_rx_frames}'}} + variants: + - nat_net: + iface_type = network + iface_source = {'source': {'network': 'default'}} + - linux_br: + iface_type = bridge + br_type = linux_br + iface_source = {'source': {'bridge': br_name}} + - ovs_br: + iface_type = bridge + br_type = ovs_br + iface_source = {'source': {'bridge': br_name}} + extra_attrs = {'virtualport': {'type': 'openvswitch'}} + iface_attrs = {'type_name': '${iface_type}', **${coalesce}, **${iface_source}, 'model': 'virtio', 'mac_address': mac, **${extra_attrs}} diff --git a/libvirt/tests/src/virtual_network/update_device/unsupported_live_update.py b/libvirt/tests/src/virtual_network/update_device/unsupported_live_update.py index a997ea957c..e981f635ae 100644 --- a/libvirt/tests/src/virtual_network/update_device/unsupported_live_update.py +++ b/libvirt/tests/src/virtual_network/update_device/unsupported_live_update.py @@ -48,7 +48,7 @@ def run(test, params, env): vm.start() vm.wait_for_serial_login().close() - iface = network_base.get_iface_xml_inst(vmxml, 'on VM') + iface = network_base.get_iface_xml_inst(vm_name, 'on VM') iface_original = iface.copy() if del_attr: if del_attr == 'boot': @@ -64,7 +64,7 @@ def run(test, params, env): if err_msg: libvirt.check_result(up_result, err_msg) - iface_update = network_base.get_iface_xml_inst(vmxml, + iface_update = network_base.get_iface_xml_inst(vm_name, 'after update-device') if iface_update != iface_original: test.fail(f'Interface xml changed after unsuccessful update.\n' diff --git a/libvirt/tests/src/virtual_network/update_device/update_device_coalesce.py b/libvirt/tests/src/virtual_network/update_device/update_device_coalesce.py new file mode 100644 index 0000000000..bd87def340 --- /dev/null +++ b/libvirt/tests/src/virtual_network/update_device/update_device_coalesce.py @@ -0,0 +1,108 @@ +import logging + +from avocado.utils import process +from virttest import utils_misc +from virttest import utils_net +from virttest import virsh +from virttest.libvirt_xml import vm_xml +from virttest.utils_libvirt import libvirt_vmxml +from virttest.utils_test import libvirt + +from provider.virtual_network import network_base + +VIRSH_ARGS = {'ignore_status': False, 'debug': True} + +LOG = logging.getLogger('avocado.' + __name__) + + +def check_coalesce_rx_frames(tap_device, rx_frames, test): + """ + Check whether coalesce rx-frames meets expectation + + :param tap_device: tap device to be checked + :param rx_frames: expected rx-frames + :param test: test instance + """ + coal_params = network_base.get_ethtool_coalesce(tap_device) + LOG.debug(f'coalesce parameters from ethtool: {coal_params}') + coal_rx_frames = coal_params.get('rx-frames') + if coal_rx_frames != rx_frames: + test.fail(f'rx-frames of {tap_device} should be {rx_frames}, ' + f'not {coal_rx_frames}') + + +def run(test, params, env): + """ + Test update-device command to update coalesce of interface + """ + vm_name = params.get('main_vm') + vm = env.get_vm(vm_name) + rand_id = utils_misc.generate_random_string(3) + br_type = params.get('br_type', '') + br_name = br_type + '_' + rand_id + mac = utils_net.generate_mac_address_simple() + iface_attrs = eval(params.get('iface_attrs', '{}')) + host_iface = params.get('host_iface') + host_iface = host_iface if host_iface else utils_net.get_net_if( + state='UP')[0] + rx_frames = params.get('rx_frames', '0') + updated_rx_frames = params.get('updated_rx_frames', '0') + updated_coalesce = eval(params.get('updated_coalesce', '{}')) + + vmxml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) + bkxml = vmxml.copy() + + try: + LOG.info('Create bridge for test.') + if br_type == 'linux_br': + utils_net.create_linux_bridge_tmux(br_name, host_iface) + process.run(f'ip l show type bridge {br_name}', shell=True) + elif br_type == 'ovs_br': + utils_net.create_ovs_bridge(br_name) + + LOG.info('Setup interface on vm.') + vmxml.del_device('interface', by_tag=True) + libvirt_vmxml.modify_vm_device(vmxml, 'interface', iface_attrs) + LOG.debug(f'VMXML of {vm_name}:\n{virsh.dumpxml(vm_name).stdout_text}') + + vm.start() + session = vm.wait_for_serial_login() + + LOG.info('Check the rx-frames value for the tap device on the host.') + iflist = libvirt.get_interface_details(vm_name) + tap_device = iflist[0]['interface'] + check_coalesce_rx_frames(tap_device, rx_frames, test) + + LOG.info('Prepare interface xml to be updated with.') + iface = network_base.get_iface_xml_inst(vm_name, 'on VM') + if not updated_coalesce: + iface.del_coalesce() + else: + iface.setup_attrs(**updated_coalesce) + + virsh.update_device(vm_name, iface.xml, **VIRSH_ARGS) + + LOG.info('Check the live xml is updated.') + iface_update = network_base.get_iface_xml_inst( + vm_name, 'after update-device') + expect_coalesce = updated_coalesce.get('coalesce') + network_base.check_iface_attrs(iface_update, 'coalesce', + expect_coalesce) + + LOG.info('Check the rx-frames value for the tap device on the host.') + check_coalesce_rx_frames(tap_device, updated_rx_frames, test) + + LOG.info('Login vm to check whether the network works well via ping.') + ips = {'outside_ip': params.get('outside_ip')} + network_base.ping_check(params, ips, session, force_ipv4=True) + + finally: + if 'session' in locals(): + session.close() + bkxml.sync() + if br_type == 'linux_br': + LOG.debug(f'Delete linux bridge: {br_name}') + utils_net.delete_linux_bridge_tmux(br_name, host_iface) + if br_type == 'ovs_br': + LOG.debug(f'Delete ovs bridge: {br_name}') + utils_net.delete_ovs_bridge(br_name) diff --git a/provider/virtual_network/network_base.py b/provider/virtual_network/network_base.py index d86d38cd48..a2f856855c 100644 --- a/provider/virtual_network/network_base.py +++ b/provider/virtual_network/network_base.py @@ -2,15 +2,13 @@ import shutil import aexpect - from avocado.core import exceptions from avocado.utils import process - from virttest import remote from virttest import utils_net from virttest import virsh - from virttest.libvirt_xml import network_xml +from virttest.libvirt_xml import vm_xml VIRSH_ARGS = {'ignore_status': False, 'debug': True} @@ -266,15 +264,46 @@ def set_static_ip(iface, ip, netmask, session): session.cmd(f'ifconfig {iface} {ip}/{netmask}') -def get_iface_xml_inst(vmxml, comment, index=0): +def get_iface_xml_inst(vm_name, comment, index=0): """ Get iface xml instance with given vm and index - :param vmxml: xml instance of vm + :param vm_name: name of vm :param comment: comment to log :param index: index of interface on vm, defaults to 0 :return: xml instance of interface """ + vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) iface = vmxml.get_devices('interface')[index] LOG.debug(f'Interface xml ({comment}):\n{iface}') return iface + + +def get_ethtool_coalesce(iface): + """ + Get coalesce parameters from ethtool command output + + :param iface: interface name + :return: dict-type coalesce parameters + """ + eth_out = process.run(f'ethtool -c {iface}').stdout_text + items = [x.split(':') for x in eth_out.replace('\t', '').splitlines() if x] + coalesce = {item[0]: item[1] for item in items[1:] if len(item) == 2} + + return coalesce + + +def check_iface_attrs(iface, key, expect_val): + """ + Check whether interface attribute value meets expectation + + :param iface: interface name + :param key: interface attribute key + :param expect_val: expect attribute value + """ + actual_val = iface.fetch_attrs().get(key) + LOG.debug(f'Expect {key}: {expect_val}\n' + f'Actual {key}: {actual_val}') + if actual_val != expect_val: + raise exceptions.TestFail(f'Updated {key} of iface should be ' + f'{expect_val}, NOT {actual_val}')