Module harvester_e2e_tests.integrations.test_5_vm_networks_interact

Functions

def check_vm_ip_exists(api_client, vm_name, wait_timeout)
Expand source code
def check_vm_ip_exists(api_client, vm_name, wait_timeout):
    endtime = datetime.now() + timedelta(seconds=wait_timeout)

    while endtime > datetime.now():
        code, data = api_client.vms.get_status(vm_name)
        assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")
        if 'ipAddress' in data['status']['interfaces'][0]:
            break
        sleep(5)
    else:
        raise AssertionError(
            f"Failed to get VM {vm_name} IP address, exceed the given timed out\n"
            f"Still got {code} with {data}"
        )
def check_vm_running(api_client, vm_name, wait_timeout)
Expand source code
def check_vm_running(api_client, vm_name, wait_timeout):
    endtime = datetime.now() + timedelta(seconds=wait_timeout)

    while endtime > datetime.now():
        code, data = api_client.vms.get(vm_name)
        vm_fields = data['metadata']['fields']

        assert 200 == code, (code, data)
        if vm_fields[2] == 'Running':
            break
        sleep(5)
    else:
        raise AssertionError(
            f"Failed to create VM {vm_name} in Running status, exceed given timeout\n"
            f"Still got {code} with {data}"
        )
def client()
Expand source code
@pytest.fixture(scope="session")
def client():
    client = paramiko.client.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    yield client
    client.close()
def cluster_network(vlan_nic, api_client, unique_name)
Expand source code
@pytest.fixture(scope='module')
def cluster_network(vlan_nic, api_client, unique_name):
    code, data = api_client.clusternetworks.get_config()
    assert 200 == code, (code, data)

    node_key = 'network.harvesterhci.io/matched-nodes'
    cnet_nodes = dict()  # cluster_network: items
    for cfg in data['items']:
        if vlan_nic in cfg['spec']['uplink']['nics']:
            nodes = json.loads(cfg['metadata']['annotations'][node_key])
            cnet_nodes.setdefault(cfg['spec']['clusterNetwork'], []).extend(nodes)

    code, data = api_client.hosts.get()
    assert 200 == code, (code, data)
    all_nodes = set(n['id'] for n in data['data'])
    try:
        # vlad_nic configured on specific cluster network, reuse it
        yield next(cnet for cnet, nodes in cnet_nodes.items() if all_nodes == set(nodes))
        return None
    except StopIteration:
        configured_nodes = reduce(add, cnet_nodes.values(), [])
        if any(n in configured_nodes for n in all_nodes):
            raise AssertionError(
                "Not all nodes' VLAN NIC {vlan_nic} are available.\n"
                f"VLAN NIC configured nodes: {configured_nodes}\n"
                f"All nodes: {all_nodes}\n"
            )

    # Create cluster network
    cnet = f"cnet-{datetime.strptime(unique_name, '%Hh%Mm%Ss%f-%m-%d').strftime('%H%M%S')}"
    created = []
    code, data = api_client.clusternetworks.create(cnet)
    assert 201 == code, (code, data)
    while all_nodes:
        node = all_nodes.pop()
        code, data = api_client.clusternetworks.create_config(node, cnet, vlan_nic, hostname=node)
        assert 201 == code, (
            f"Failed to create cluster config for {node}\n"
            f"Created: {created}\t Remaining: {all_nodes}\n"
            f"API Status({code}): {data}"
        )
        created.append(node)

    yield cnet

    # Teardown
    deleted = {name: api_client.clusternetworks.delete_config(name) for name in created}
    failed = [(name, code, data) for name, (code, data) in deleted.items() if 200 != code]
    if failed:
        fmt = "Unable to delete VLAN Config {} with error ({}): {}"
        raise AssertionError(
            "\n".join(fmt.format(name, code, data) for (name, code, data) in failed)
        )

    code, data = api_client.clusternetworks.delete(cnet)
    assert 200 == code, (code, data)
def create_image_url(api_client, display_name, image_url, wait_timeout)
Expand source code
def create_image_url(api_client, display_name, image_url, wait_timeout):
    code, data = api_client.images.create_by_url(display_name, image_url)

    assert 201 == code, (code, data)
    image_spec = data.get('spec')

    assert display_name == image_spec.get('displayName')
    assert "download" == image_spec.get('sourceType')

    endtime = datetime.now() + timedelta(seconds=wait_timeout)

    while endtime > datetime.now():
        code, data = api_client.images.get(display_name)
        image_status = data.get('status', {})

        assert 200 == code, (code, data)
        if image_status.get('progress') == 100:
            break
        sleep(5)
    else:
        raise AssertionError(
            f"Failed to download image {display_name} with {wait_timeout} timed out\n"
            f"Still got {code} with {data}"
        )
def vm_network(api_client, unique_name, wait_timeout, cluster_network, vlan_id)
Expand source code
@pytest.fixture(scope="module")
def vm_network(api_client, unique_name, wait_timeout, cluster_network, vlan_id):
    code, data = api_client.networks.create(
        unique_name, vlan_id, cluster_network=cluster_network
    )
    assert 201 == code, (code, data)
    endtime = datetime.now() + timedelta(seconds=wait_timeout)
    while endtime > datetime.now():
        code, data = api_client.networks.get(unique_name)
        annotations = data['metadata'].get('annotations', {})
        if 200 == code and annotations.get('network.harvesterhci.io/route'):
            route = json.loads(annotations['network.harvesterhci.io/route'])
            if route['cidr']:
                break
        sleep(3)
    else:
        raise AssertionError(
            "VM network created but route info not available\n"
            f"API Status({code}): {data}"
        )

    yield dict(name=unique_name, cidr=route['cidr'], namespace=data['metadata']['namespace'])

    code, data = api_client.networks.delete(unique_name)
    endtime = datetime.now() + timedelta(seconds=wait_timeout)
    while endtime > datetime.now():
        code, data = api_client.networks.get(unique_name)
        if 404 == code:
            break
        sleep(3)
    else:
        raise AssertionError(
            f"Failed to remote VM network {unique_name} after {wait_timeout}s\n"
            f"API Status({code}): {data}"
        )

Classes

class TestBackendNetwork
Expand source code
@pytest.mark.p0
@pytest.mark.networks
class TestBackendNetwork:

    @pytest.mark.p0
    @pytest.mark.networks
    def test_mgmt_network_connection(
        self, api_client, request, client, image_opensuse, unique_name, wait_timeout,
        host_shell, vm_shell_from_host, vm_checker
    ):
        """
        Manual test plan reference:
        https://harvester.github.io/tests/manual/network/validate-network-management-network/


        Steps:
        1. Create a new VM
        2. Make sure that the network is set to the management network with masquerade as the type
        3. Wait until the VM boot in running state
        4. Check can ping VM with management network from Harvester node
        5. Check can SSH to VM with management network from Harvester node
        6. Check can't SSH to VM with management network from external host
        """
        vip = request.config.getoption('--endpoint').strip('https://')
        vm_user, vm_passwd = vm_credential['user'], vm_credential['password']

        # Check image exists
        code, data = api_client.images.get(image_opensuse.name)

        if code == 404:
            create_image_url(api_client, image_opensuse.name, image_opensuse.url, wait_timeout)

        # Update AllowTcpForwarding for ssh jumpstart

        spec = api_client.vms.Spec(1, 2)

        spec.user_data += cloud_user_data.format(password=vm_passwd)

        vm_name = unique_name + "-mgmt"
        # Create VM
        spec.add_image(image_opensuse.name, "default/" + image_opensuse.name)

        code, data = api_client.vms.create(vm_name, spec)
        assert 201 == code, (f"Failed to create vm with error: {code}, {data}")

        # Check VM start in running state
        check_vm_running(api_client, vm_name, wait_timeout)

        # Check until VM ip address exists
        check_vm_ip_exists(api_client, vm_name, wait_timeout)

        # Get VM interface ipAddresses
        code, data = api_client.vms.get_status(vm_name)
        assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")

        interfaces_data = data['status']['interfaces']

        assert 1 == len(interfaces_data), (
            f"Failed: get more than one interface: {interfaces_data}"
        )

        mgmt_ip = interfaces_data[0]['ipAddress']

        # Ping management ip address from Harvester node
        with host_shell.login(vip) as sh:
            stdout, stderr = sh.exec_command(f"ping -c 50 {mgmt_ip}")

        assert stdout.find(f"64 bytes from {mgmt_ip}") > 0, (
            f"Failed to ping VM management IP {mgmt_ip} "
            f"on management interface from Harvester node")

        # SSH to management ip address and execute command from Harvester node
        with vm_shell_from_host(vip, mgmt_ip, vm_user, vm_passwd) as sh:
            stdout, stderr = sh.exec_command("ls")

        assert stdout.find("bin") == 0, (
            f"Failed to ssh to VM management IP {mgmt_ip} "
            f"on management interface from Harvester node")

        # Check should not SSH to management ip address from external host
        command = ['/usr/bin/ssh', '-o', 'ConnectTimeout=5', mgmt_ip]

        with pytest.raises(subprocess.CalledProcessError) as ex:
            subprocess.check_output(command, stderr=subprocess.STDOUT,
                                    shell=False, encoding="utf-8")

        # OpenSSH returns the return code of the program that was executed on
        # the remote, unless there was an error for SSH itself, in which case
        # it returns 255
        assert ex.value.returncode == 255, ("Failed: should not be able to SSH"
                                            " to VM on management interface"
                                            f" {mgmt_ip} from external host")

        # teardown
        code, data = api_client.vms.get(vm_name)
        vm_spec = api_client.vms.Spec.from_dict(data)
        vm_checker.wait_deleted(vm_name)
        for vol in vm_spec.volumes:
            vol_name = vol['volume']['persistentVolumeClaim']['claimName']
            api_client.volumes.delete(vol_name)

    @pytest.mark.p0
    @pytest.mark.networks
    @pytest.mark.dependency(name="vlan_network_connection")
    def test_vlan_network_connection(self, api_client, request, client, unique_name,
                                     image_opensuse, vm_network, wait_timeout, vm_checker):
        """
        Manual test plan reference:
        https://harvester.github.io/tests/manual/network/validate-network-external-vlan/


        Steps:
        1. Create an external VLAN network
        2. Create a new VM and set the external vlan network to it
        3. Check can ping external VLAN IP from external host
        4. Check can SSH to VM from external IP from external host
        """
        vm_name = unique_name + "-vlan"

        # Check image exists
        code, data = api_client.images.get(image_opensuse.name)

        if code == 404:
            create_image_url(api_client, image_opensuse.name, image_opensuse.url, wait_timeout)

        spec = api_client.vms.Spec(1, 2, mgmt_network=False)
        spec.user_data += cloud_user_data.format(password=vm_credential["password"])

        # Create VM
        spec.add_image(image_opensuse.name, "default/" + image_opensuse.name)

        spec.add_network("nic-1", f"{vm_network['namespace']}/{vm_network['name']}")

        code, data = api_client.vms.create(vm_name, spec)
        assert 201 == code, (f"Failed to create vm with error: {code}, {data}")

        # Check VM start in running state
        check_vm_running(api_client, vm_name, wait_timeout)

        # Check until VM ip address exists
        check_vm_ip_exists(api_client, vm_name, wait_timeout)

        # Get VM interface ipAddresses
        code, data = api_client.vms.get_status(vm_name)
        assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")

        interfaces_data = data['status']['interfaces']

        assert 1 == len(interfaces_data), (
            f"Failed: get more than one interface: {interfaces_data}"
        )

        assert "nic-1" == interfaces_data[0]['name'], (
            f"Failed: Network name did not match to added vlan: {interfaces_data}"
        )

        vlan_ip = interfaces_data[0]['ipAddress']

        # Ping vlan ip address from external host
        command = ['/usr/bin/ping', '-c', '50', vlan_ip]

        result = subprocess.check_output(command, shell=False, encoding="utf-8")

        assert result.find(f"64 bytes from {vlan_ip}") > 0, (
            f"Failed to ping VM external vlan IP {vlan_ip} "
            f"on vlan interface from external host")

        # SSH to vlan ip address and execute command from external host
        _stdout, _stderr = self.ssh_client(
            client, vlan_ip, vm_credential["user"], vm_credential["password"], 'ls', wait_timeout)

        stdout = _stdout.read().decode('ascii').strip("\n")

        assert stdout.find("bin") == 0, (
            f"Failed to ssh to VM external vlan IP {vlan_ip}"
            f"on vlan interface from external host")

        # teardown
        code, data = api_client.vms.get(vm_name)
        vm_spec = api_client.vms.Spec.from_dict(data)
        vm_checker.wait_deleted(vm_name)
        for vol in vm_spec.volumes:
            vol_name = vol['volume']['persistentVolumeClaim']['claimName']
            api_client.volumes.delete(vol_name)

    @pytest.mark.p0
    @pytest.mark.networks
    @pytest.mark.dependency(name="reboot_vlan_connection",
                            depends=["vlan_network_connection"])
    def test_reboot_vlan_connection(self, api_client, request, unique_name,
                                    image_opensuse, vm_network, wait_timeout, vm_checker):
        """
        Manual test plan reference:
        https://harvester.github.io/tests/manual/network/negative-vlan-after-reboot/


        Steps:
        1. Create an external VLAN network
        2. Create a new VM and add the external vlan network
        3. Check can ping external VLAN IP
        4. Reboot VM
        5. Ping VM during reboot
        6. Check can't ping VM during reboot
        7. Check the VM should reboot
        8. Ping VM after reboot
        9. Check can ping VM
        """
        vm_name = unique_name + "-reboot-vlan"

        # Check image exists
        code, data = api_client.images.get(image_opensuse.name)

        if code == 404:
            create_image_url(api_client, image_opensuse.name, image_opensuse.url, wait_timeout)

        spec = api_client.vms.Spec(1, 2, mgmt_network=False)
        spec.user_data += cloud_user_data.format(password=vm_credential["password"])

        # Create VM
        spec.add_image(image_opensuse.name, "default/" + image_opensuse.name)

        spec.add_network("nic-1", f"{vm_network['namespace']}/{vm_network['name']}")

        code, data = api_client.vms.create(vm_name, spec)
        assert 201 == code, (f"Failed to create vm with error: {code}, {data}")

        # Check VM start in running state
        check_vm_running(api_client, vm_name, wait_timeout)

        # Check until VM ip address exists
        check_vm_ip_exists(api_client, vm_name, wait_timeout)

        # Get VM interface ipAddresses
        code, data = api_client.vms.get_status(vm_name)
        assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")

        interfaces_data = data['status']['interfaces']

        assert 1 == len(interfaces_data), (
            f"Failed: get more than one interface: {interfaces_data}"
        )

        assert "nic-1" == interfaces_data[0]['name'], (
            f"Failed: Network name did not match to added vlan: {interfaces_data}"
        )

        vlan_ip = interfaces_data[0]['ipAddress']

        # Check can ping vlan ip

        command = ['/usr/bin/ping', '-c', '10', vlan_ip]

        result = subprocess.check_output(command, shell=False, encoding="utf-8")

        assert result.find(f"64 bytes from {vlan_ip}") > 0, (
            f"Failed to ping VM external vlan IP {vlan_ip} "
            f"on vlan interface from external host")

        # Restart VM
        code, data = api_client.vms.restart(vm_name)
        assert 204 == code, (f"Failed to reboot vm with error: {code}, {data}")

        # Check VM start in Starting state
        endtime = datetime.now() + timedelta(seconds=wait_timeout)

        while endtime > datetime.now():
            code, data = api_client.vms.get(vm_name)
            assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")
            vm_fields = data['metadata']['fields']

            if vm_fields[2] == 'Starting':
                break
            sleep(5)
        else:
            raise AssertionError(
                f"Failed to restart VM {vm_name} in Starting status, exceed given timeout\n"
                f"Still got {code} with {data}"
            )

        # Check can't ping vlan ip during reboot

        command = ['/usr/bin/ping', '-c', '10', vlan_ip]

        process = subprocess.run(command, stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE, universal_newlines=True)

        result = process.stdout

        assert result.find(f"64 bytes from {vlan_ip}") < 0, (
            f"Failed: since can ping VM external vlan IP {vlan_ip} "
            f"on vlan interface from external host during reboot")

        # Check VM start in running state
        check_vm_running(api_client, vm_name, wait_timeout)

        # Check until VM ip address exists
        check_vm_ip_exists(api_client, vm_name, wait_timeout)

        # Get VM interface ipAddresses
        code, data = api_client.vms.get_status(vm_name)
        assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")

        interfaces_data = data['status']['interfaces']

        assert 1 == len(interfaces_data), (
            f"Failed: get more than one interface: {interfaces_data}"
        )

        assert "nic-1" == interfaces_data[0]['name'], (
            f"Failed: Network name did not match to added vlan: {interfaces_data}"
        )

        vlan_ip = interfaces_data[0]['ipAddress']

        # Ping vlan ip address

        command = ['/usr/bin/ping', '-c', '10', vlan_ip]

        result = subprocess.check_output(command, shell=False, encoding="utf-8")

        assert result.find(f"64 bytes from {vlan_ip}") > 0, (
            f"Failed to ping VM external vlan IP {vlan_ip} "
            f"on vlan interface from external host")

        # teardown
        code, data = api_client.vms.get(vm_name)
        vm_spec = api_client.vms.Spec.from_dict(data)
        vm_checker.wait_deleted(vm_name)
        for vol in vm_spec.volumes:
            vol_name = vol['volume']['persistentVolumeClaim']['claimName']
            api_client.volumes.delete(vol_name)

    @pytest.mark.p0
    @pytest.mark.networks
    def test_mgmt_to_vlan_connection(self, api_client, request, client, unique_name,
                                     image_opensuse, vm_network, wait_timeout, vm_checker):
        """
        Manual test plan reference:
        https://harvester.github.io/tests/manual/network/edit-network-form-change-management-to-vlan/


        Steps:
        1. Create an external VLAN network
        2. Create a new VM
        3. Make sure that the network is set to the management network with masquerade as the type
        4. Wait until the VM boot in running state
        5. Edit VM and change management network to external VLAN with bridge type
        6. Check VM should save and reboot
        7. Check can ping the VM from an external network host
        8. Check can ssh to the VM from an external network host
        """

        # Check image exists
        code, data = api_client.images.get(image_opensuse.name)

        if code == 404:
            create_image_url(api_client, image_opensuse.name, image_opensuse.url, wait_timeout)

        spec = api_client.vms.Spec(1, 2)
        spec.user_data += cloud_user_data.format(password=vm_credential["password"])
        vm_name = unique_name + "-mgmt-vlan"
        # Create VM
        spec.add_image(image_opensuse.name, "default/" + image_opensuse.name)
        code, data = api_client.vms.create(vm_name, spec)
        assert 201 == code, (f"Failed to create vm with error: {code}, {data}")

        # Check VM start in running state
        check_vm_running(api_client, vm_name, wait_timeout)

        # Check until VM ip address exists
        check_vm_ip_exists(api_client, vm_name, wait_timeout)

        # get data from running VM and transfer to spec
        code, data = api_client.vms.get(vm_name)
        assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")

        spec = spec.from_dict(data)

        # Switch to vlan network
        spec.mgmt_network = False

        spec.add_network("nic-1", f"{vm_network['namespace']}/{vm_network['name']}")

        # Update VM spec
        code, data = api_client.vms.update(vm_name, spec)
        assert 200 == code, (f"Failed to update specific vm with spec: {code}, {data}")

        code, data = api_client.vms.restart(vm_name)
        assert 204 == code, (f"Failed to restart specific vm: {code}, {data}")

        # Check VM start in running state
        check_vm_running(api_client, vm_name, wait_timeout)

        # Check until VM ip address exists
        check_vm_ip_exists(api_client, vm_name, wait_timeout)

        # Get VM interface ipAddresses
        code, data = api_client.vms.get_status(vm_name)
        assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")

        interfaces_data = data['status']['interfaces']

        assert 1 == len(interfaces_data), (
            f"Failed: get more than one interface: {interfaces_data}"
        )

        # Determine by vlan network Name
        endtime = datetime.now() + timedelta(seconds=wait_timeout)
        ip_addresses = []

        while endtime > datetime.now():
            code, data = api_client.vms.get_status(vm_name)
            assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")
            if 'interfaces' in data['status']:
                interfaces_data = data['status']['interfaces']
                ip_addresses = []
                for interface in interfaces_data:
                    # Check the ipAddress in digital format
                    if (
                        'ipAddress' in interface and
                        interface['ipAddress'].replace('.', '').isdigit()
                    ):
                        ip_addresses.append(interface['ipAddress'])

                if len(ip_addresses) > 0:
                    if 'nic-1' in interface['name']:
                        break
            sleep(5)
        else:
            raise AssertionError(
                f"Failed to get VM {vm_name} IP address, exceed given timeout\n"
                f"Still got {code} with {data}"
            )

        # Ping vlan ip address
        vlan_ip = ip_addresses[0]

        command = ['/usr/bin/ping', '-c', '50', vlan_ip]

        result = subprocess.check_output(command, shell=False, encoding="utf-8")

        assert result.find(f"64 bytes from {vlan_ip}") > 0, (
            f"Failed to ping VM external vlan IP {vlan_ip} "
            f"on vlan interface from external host")

        # SSH to vlan ip address and execute command
        _stdout, _stderr = self.ssh_client(
            client, vlan_ip, vm_credential["user"], vm_credential["password"], 'ls', wait_timeout)

        stdout = _stdout.read().decode('ascii').strip("\n")

        assert stdout.find("bin") == 0, (
            f"Failed to ssh to VM external vlan IP {vlan_ip}"
            f"on vlan interface from external host")

        # teardown
        code, data = api_client.vms.get(vm_name)
        vm_spec = api_client.vms.Spec.from_dict(data)
        vm_checker.wait_deleted(vm_name)
        for vol in vm_spec.volumes:
            vol_name = vol['volume']['persistentVolumeClaim']['claimName']
            api_client.volumes.delete(vol_name)

    @pytest.mark.p0
    @pytest.mark.networks
    def test_vlan_to_mgmt_connection(
        self, api_client, request, client, unique_name, image_opensuse, vm_network, wait_timeout,
        host_shell, vm_shell_from_host, vm_checker
    ):
        """
        Manual test plan reference:
        https://harvester.github.io/tests/manual/network/edit-network-form-change-management-to-vlan/


        Steps:
        1. Create an external VLAN network
        2. Create a new VM
        3. Make sure that the network is set to the vlan network with bridge as the type
        4. Wait until the VM boot in running state
        5. Edit VM and change from external VLAN to management network
        6. Check VM should save and reboot
        7. Check can ping VM with management network from Harvester node
        8. Check can SSH to VM with management network from Harvester node
        9. Check can't SSH to VM with management network from external host
        """

        vip = request.config.getoption('--endpoint').strip('https://')
        vm_user, vm_passwd = vm_credential['user'], vm_credential['password']

        # Check image exists
        code, data = api_client.images.get(image_opensuse.name)

        if code == 404:
            create_image_url(api_client, image_opensuse.name, image_opensuse.url, wait_timeout)

        spec = api_client.vms.Spec(1, 2, mgmt_network=False)
        spec.user_data += cloud_user_data.format(password=vm_passwd)
        vm_name = unique_name + "-vlan-mgmt"

        # Create VM
        spec.add_image(image_opensuse.name, "default/" + image_opensuse.name)
        spec.add_network("default", f"{vm_network['namespace']}/{vm_network['name']}")

        code, data = api_client.vms.create(vm_name, spec)
        assert 201 == code, (f"Failed to create vm with error: {code}, {data}")

        # Check VM start in running state
        check_vm_running(api_client, vm_name, wait_timeout)

        # Check until VM ip address exists
        check_vm_ip_exists(api_client, vm_name, wait_timeout)

        # get data from running VM and transfer to spec
        code, data = api_client.vms.get(vm_name)
        assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")
        spec = spec.from_dict(data)

        spec.networks = []
        spec.mgmt_network = True

        code, data = api_client.vms.update(vm_name, spec)
        assert 200 == code, (f"Failed to update specific vm with spec: {code}, {data}")

        code, data = api_client.vms.restart(vm_name)
        assert 204 == code, (f"Failed to restart specific vm: {code}, {data}")

        # Check VM start in running state
        check_vm_running(api_client, vm_name, wait_timeout)

        # Check until VM ip address exists
        check_vm_ip_exists(api_client, vm_name, wait_timeout)

        endtime = datetime.now() + timedelta(seconds=wait_timeout)

        while endtime > datetime.now():
            code, data = api_client.vms.get_status(vm_name)
            assert 200 == code, (f"Failed to get specific vm status: {code}, {data}")

            if 'interfaces' in data['status']:
                interfaces_data = data['status']['interfaces']
                ip_addresses = []
                if 'ipAddress' in data['status']['interfaces'][0]:

                    if 'default' in interfaces_data[0]['name']:
                        if 'domain, guest-agent' not in interfaces_data[0]['infoSource']:
                            ip_addresses.append(interfaces_data[0]['ipAddress'])
                            break
                sleep(5)
        else:
            raise AssertionError(
                f"Failed to get VM {vm_name} IP address, exceed the given timed out\n"
                f"Still got {code} with {data}"
            )

        # Check can ping management ip address from Harvester node
        mgmt_ip = ip_addresses[0]

        with host_shell.login(vip) as sh:
            stdout, stderr = sh.exec_command(f"ping -c 50 {mgmt_ip}")

        assert stdout.find(f"64 bytes from {mgmt_ip}") > 0, (
            f"Failed to ping VM management IP {mgmt_ip} "
            f"on management interface from Harvester node")

        # Check can ssh to host and execute command from Harvester node
        with vm_shell_from_host(vip, mgmt_ip, vm_user, vm_passwd) as sh:
            stdout, stderr = sh.exec_command("ls")

        assert stdout.find("bin") == 0, (
            f"Failed to ssh to VM management IP {mgmt_ip} "
            f"on management interface from Harvester node")

        # Check should not SSH to management ip address from external host
        command = ['/usr/bin/ssh', '-o', 'ConnectTimeout=5', mgmt_ip]

        with pytest.raises(subprocess.CalledProcessError) as ex:
            subprocess.check_output(command, stderr=subprocess.STDOUT,
                                    shell=False, encoding="utf-8")

        # OpenSSH returns the return code of the program that was executed on
        # the remote, unless there was an error for SSH itself, in which case
        # it returns 255
        assert ex.value.returncode == 255, ("Failed: should not be able to SSH"
                                            " to VM on management interface"
                                            f" {mgmt_ip} from external host")

        # teardown
        code, data = api_client.vms.get(vm_name)
        vm_spec = api_client.vms.Spec.from_dict(data)
        vm_checker.wait_deleted(vm_name)
        for vol in vm_spec.volumes:
            vol_name = vol['volume']['persistentVolumeClaim']['claimName']
            api_client.volumes.delete(vol_name)

    @pytest.mark.p0
    @pytest.mark.networks
    def test_delete_vlan_from_multiple(
        self, api_client, request, client, unique_name, image_opensuse, vm_network, wait_timeout,
        host_shell, vm_checker
    ):
        """
        Manual test plan reference:
        https://harvester.github.io/tests/manual/network/delete-vlan-network-form/

        Steps:
        1. Create an external VLAN network
        2. Make sure that the network is set to the management network with masquerade as the type
        3. Add another external VLAN management
        4. Create VM
        5. Wait until the VM boot in running state
        6. Delete the external VLAN from VM
        7. Check can ping the VM on the management network
        8. Check can't SSH to VM with management network from external host
        """

        vip = request.config.getoption('--endpoint').strip('https://')

        # Check image exists
        code, data = api_client.images.get(image_opensuse.name)

        if code == 404:
            create_image_url(api_client, image_opensuse.name, image_opensuse.url, wait_timeout)

        spec = api_client.vms.Spec(1, 2)
        spec.user_data += cloud_user_data.format(password=vm_credential["password"])

        # Add network data to trigger DHCP on multiple NICs
        spec.network_data += cloud_network_data

        vm_name = unique_name + "-delete-vlan"

        # Add image
        spec.add_image(image_opensuse.name, "default/" + image_opensuse.name)

        # Add external vlan network
        spec.add_network("nic-1", f"{vm_network['namespace']}/{vm_network['name']}")

        # Create VM
        code, data = api_client.vms.create(vm_name, spec)
        assert 201 == code, (f"Failed to create vm with error: {code}, {data}")

        # Check VM start in running state
        check_vm_running(api_client, vm_name, wait_timeout)

        # Check have 2 NICs and wait until all ip address exists
        endtime = datetime.now() + timedelta(seconds=wait_timeout)

        while endtime > datetime.now():
            code, data = api_client.vms.get_status(vm_name)
            assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")
            if len(data['status']['interfaces']) == 2:
                if 'ipAddress' in data['status']['interfaces'][0]:
                    if 'ipAddress' in data['status']['interfaces'][1]:
                        break
                sleep(5)

        else:
            raise AssertionError(
                f"Failed to get multiple IPs on VM: {vm_name}, exceed the given timed out\n"
                f"Still got {code} with {data}"
            )

        # get data from running VM and transfer to spec
        code, data = api_client.vms.get(vm_name)
        assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")
        spec = spec.from_dict(data)

        spec.networks = []
        spec.mgmt_network = True

        code, data = api_client.vms.update(vm_name, spec)
        assert 200 == code, (f"Failed to update specific vm with spec: {code}, {data}")

        code, data = api_client.vms.restart(vm_name)
        assert 204 == code, (f"Failed to restart specific vm: {code}, {data}")

        # Check VM start in running state
        check_vm_running(api_client, vm_name, wait_timeout)

        # Check until VM ip address exists
        check_vm_ip_exists(api_client, vm_name, wait_timeout)

        endtime = datetime.now() + timedelta(seconds=wait_timeout)
        ip_addresses = []

        while endtime > datetime.now():
            code, data = api_client.vms.get_status(vm_name)
            assert 200 == code, (f"Failed to get specific vm status: {code}, {data}")
            if 'interfaces' in data['status']:
                interfaces_data = data['status']['interfaces']
                ip_addresses = []

                interfaces = data['status']['interfaces']

                if len(interfaces) == 1 and 'ipAddress' in interfaces[0]:
                    ip_addresses.append(interfaces_data[0]['ipAddress'])

                    if 'default' in interfaces_data[0]['name']:
                        break
            sleep(5)
        else:
            raise AssertionError(
                f"Failed to get VM {vm_name} IP address, exceed the given timed out\n"
                f"Still got {code} with {data}"
            )

        # Ping management ip address
        mgmt_ip = ip_addresses[0]

        ping_command = "ping -c 50 {0}".format(mgmt_ip)

        with host_shell.login(vip) as sh:
            stdout, stderr = sh.exec_command(ping_command)

        assert stdout.find(f"64 bytes from {mgmt_ip}") > 0, (
            f"Failed to ping VM management IP {mgmt_ip} "
            f"on management interface from Harvester node: {code}, {data}")

        # #Check should not SSH to management ip address from external host
        command = ['/usr/bin/ssh', '-o', 'ConnectTimeout=5', mgmt_ip]

        with pytest.raises(subprocess.CalledProcessError) as ex:
            subprocess.check_output(command, stderr=subprocess.STDOUT,
                                    shell=False, encoding="utf-8")

        # OpenSSH returns the return code of the program that was executed on
        # the remote, unless there was an error for SSH itself, in which case
        # it returns 255
        assert ex.value.returncode == 255, ("Failed: should not be able to SSH"
                                            " to VM on management interface"
                                            f" {mgmt_ip} from external host")

        # teardown
        code, data = api_client.vms.get(vm_name)
        vm_spec = api_client.vms.Spec.from_dict(data)
        vm_checker.wait_deleted(vm_name)
        for vol in vm_spec.volumes:
            vol_name = vol['volume']['persistentVolumeClaim']['claimName']
            api_client.volumes.delete(vol_name)

    def ssh_client(self, client, dest_ip, username, password, command, timeout,
                   allow_agent=False, look_for_keys=False):
        client.connect(dest_ip, username=username, password=password,
                       allow_agent=allow_agent, look_for_keys=look_for_keys,
                       timeout=timeout)

        split_command = shlex.split(command)
        _stdin, _stdout, _stderr = client.exec_command(' '.join(
            shlex.quote(part) for part in split_command), get_pty=True)
        return _stdout, _stderr

    def ssh_jumpstart(self, client, dest_ip, client_ip, client_user, client_password,
                      dest_user, dest_password, command, allow_agent=False, look_for_keys=False):
        client.connect(client_ip, username=client_user, password=client_password,
                       allow_agent=allow_agent, look_for_keys=look_for_keys)

        client_transport = client.get_transport()
        dest_addr = (dest_ip, 22)
        client_addr = (client_ip, 22)
        client_channel = client_transport.open_channel("direct-tcpip", dest_addr, client_addr)

        jumpstart = paramiko.SSHClient()
        jumpstart.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        jumpstart.connect(dest_ip, username=dest_user, password=dest_password, sock=client_channel)

        split_command = shlex.split(command)
        _stdin, _stdout, _stderr = jumpstart.exec_command(' '.join(
            shlex.quote(part) for part in split_command))
        return _stdout, _stderr

Class variables

var pytestmark

Methods

def ssh_client(self,
client,
dest_ip,
username,
password,
command,
timeout,
allow_agent=False,
look_for_keys=False)
Expand source code
def ssh_client(self, client, dest_ip, username, password, command, timeout,
               allow_agent=False, look_for_keys=False):
    client.connect(dest_ip, username=username, password=password,
                   allow_agent=allow_agent, look_for_keys=look_for_keys,
                   timeout=timeout)

    split_command = shlex.split(command)
    _stdin, _stdout, _stderr = client.exec_command(' '.join(
        shlex.quote(part) for part in split_command), get_pty=True)
    return _stdout, _stderr
def ssh_jumpstart(self,
client,
dest_ip,
client_ip,
client_user,
client_password,
dest_user,
dest_password,
command,
allow_agent=False,
look_for_keys=False)
Expand source code
def ssh_jumpstart(self, client, dest_ip, client_ip, client_user, client_password,
                  dest_user, dest_password, command, allow_agent=False, look_for_keys=False):
    client.connect(client_ip, username=client_user, password=client_password,
                   allow_agent=allow_agent, look_for_keys=look_for_keys)

    client_transport = client.get_transport()
    dest_addr = (dest_ip, 22)
    client_addr = (client_ip, 22)
    client_channel = client_transport.open_channel("direct-tcpip", dest_addr, client_addr)

    jumpstart = paramiko.SSHClient()
    jumpstart.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    jumpstart.connect(dest_ip, username=dest_user, password=dest_password, sock=client_channel)

    split_command = shlex.split(command)
    _stdin, _stdout, _stderr = jumpstart.exec_command(' '.join(
        shlex.quote(part) for part in split_command))
    return _stdout, _stderr
def test_delete_vlan_from_multiple(self,
api_client,
request,
client,
unique_name,
image_opensuse,
vm_network,
wait_timeout,
host_shell,
vm_checker)
Expand source code
@pytest.mark.p0
@pytest.mark.networks
def test_delete_vlan_from_multiple(
    self, api_client, request, client, unique_name, image_opensuse, vm_network, wait_timeout,
    host_shell, vm_checker
):
    """
    Manual test plan reference:
    https://harvester.github.io/tests/manual/network/delete-vlan-network-form/

    Steps:
    1. Create an external VLAN network
    2. Make sure that the network is set to the management network with masquerade as the type
    3. Add another external VLAN management
    4. Create VM
    5. Wait until the VM boot in running state
    6. Delete the external VLAN from VM
    7. Check can ping the VM on the management network
    8. Check can't SSH to VM with management network from external host
    """

    vip = request.config.getoption('--endpoint').strip('https://')

    # Check image exists
    code, data = api_client.images.get(image_opensuse.name)

    if code == 404:
        create_image_url(api_client, image_opensuse.name, image_opensuse.url, wait_timeout)

    spec = api_client.vms.Spec(1, 2)
    spec.user_data += cloud_user_data.format(password=vm_credential["password"])

    # Add network data to trigger DHCP on multiple NICs
    spec.network_data += cloud_network_data

    vm_name = unique_name + "-delete-vlan"

    # Add image
    spec.add_image(image_opensuse.name, "default/" + image_opensuse.name)

    # Add external vlan network
    spec.add_network("nic-1", f"{vm_network['namespace']}/{vm_network['name']}")

    # Create VM
    code, data = api_client.vms.create(vm_name, spec)
    assert 201 == code, (f"Failed to create vm with error: {code}, {data}")

    # Check VM start in running state
    check_vm_running(api_client, vm_name, wait_timeout)

    # Check have 2 NICs and wait until all ip address exists
    endtime = datetime.now() + timedelta(seconds=wait_timeout)

    while endtime > datetime.now():
        code, data = api_client.vms.get_status(vm_name)
        assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")
        if len(data['status']['interfaces']) == 2:
            if 'ipAddress' in data['status']['interfaces'][0]:
                if 'ipAddress' in data['status']['interfaces'][1]:
                    break
            sleep(5)

    else:
        raise AssertionError(
            f"Failed to get multiple IPs on VM: {vm_name}, exceed the given timed out\n"
            f"Still got {code} with {data}"
        )

    # get data from running VM and transfer to spec
    code, data = api_client.vms.get(vm_name)
    assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")
    spec = spec.from_dict(data)

    spec.networks = []
    spec.mgmt_network = True

    code, data = api_client.vms.update(vm_name, spec)
    assert 200 == code, (f"Failed to update specific vm with spec: {code}, {data}")

    code, data = api_client.vms.restart(vm_name)
    assert 204 == code, (f"Failed to restart specific vm: {code}, {data}")

    # Check VM start in running state
    check_vm_running(api_client, vm_name, wait_timeout)

    # Check until VM ip address exists
    check_vm_ip_exists(api_client, vm_name, wait_timeout)

    endtime = datetime.now() + timedelta(seconds=wait_timeout)
    ip_addresses = []

    while endtime > datetime.now():
        code, data = api_client.vms.get_status(vm_name)
        assert 200 == code, (f"Failed to get specific vm status: {code}, {data}")
        if 'interfaces' in data['status']:
            interfaces_data = data['status']['interfaces']
            ip_addresses = []

            interfaces = data['status']['interfaces']

            if len(interfaces) == 1 and 'ipAddress' in interfaces[0]:
                ip_addresses.append(interfaces_data[0]['ipAddress'])

                if 'default' in interfaces_data[0]['name']:
                    break
        sleep(5)
    else:
        raise AssertionError(
            f"Failed to get VM {vm_name} IP address, exceed the given timed out\n"
            f"Still got {code} with {data}"
        )

    # Ping management ip address
    mgmt_ip = ip_addresses[0]

    ping_command = "ping -c 50 {0}".format(mgmt_ip)

    with host_shell.login(vip) as sh:
        stdout, stderr = sh.exec_command(ping_command)

    assert stdout.find(f"64 bytes from {mgmt_ip}") > 0, (
        f"Failed to ping VM management IP {mgmt_ip} "
        f"on management interface from Harvester node: {code}, {data}")

    # #Check should not SSH to management ip address from external host
    command = ['/usr/bin/ssh', '-o', 'ConnectTimeout=5', mgmt_ip]

    with pytest.raises(subprocess.CalledProcessError) as ex:
        subprocess.check_output(command, stderr=subprocess.STDOUT,
                                shell=False, encoding="utf-8")

    # OpenSSH returns the return code of the program that was executed on
    # the remote, unless there was an error for SSH itself, in which case
    # it returns 255
    assert ex.value.returncode == 255, ("Failed: should not be able to SSH"
                                        " to VM on management interface"
                                        f" {mgmt_ip} from external host")

    # teardown
    code, data = api_client.vms.get(vm_name)
    vm_spec = api_client.vms.Spec.from_dict(data)
    vm_checker.wait_deleted(vm_name)
    for vol in vm_spec.volumes:
        vol_name = vol['volume']['persistentVolumeClaim']['claimName']
        api_client.volumes.delete(vol_name)

Manual test plan reference: https://harvester.github.io/tests/manual/network/delete-vlan-network-form/

Steps: 1. Create an external VLAN network 2. Make sure that the network is set to the management network with masquerade as the type 3. Add another external VLAN management 4. Create VM 5. Wait until the VM boot in running state 6. Delete the external VLAN from VM 7. Check can ping the VM on the management network 8. Check can't SSH to VM with management network from external host

def test_mgmt_network_connection(self,
api_client,
request,
client,
image_opensuse,
unique_name,
wait_timeout,
host_shell,
vm_shell_from_host,
vm_checker)
Expand source code
@pytest.mark.p0
@pytest.mark.networks
def test_mgmt_network_connection(
    self, api_client, request, client, image_opensuse, unique_name, wait_timeout,
    host_shell, vm_shell_from_host, vm_checker
):
    """
    Manual test plan reference:
    https://harvester.github.io/tests/manual/network/validate-network-management-network/


    Steps:
    1. Create a new VM
    2. Make sure that the network is set to the management network with masquerade as the type
    3. Wait until the VM boot in running state
    4. Check can ping VM with management network from Harvester node
    5. Check can SSH to VM with management network from Harvester node
    6. Check can't SSH to VM with management network from external host
    """
    vip = request.config.getoption('--endpoint').strip('https://')
    vm_user, vm_passwd = vm_credential['user'], vm_credential['password']

    # Check image exists
    code, data = api_client.images.get(image_opensuse.name)

    if code == 404:
        create_image_url(api_client, image_opensuse.name, image_opensuse.url, wait_timeout)

    # Update AllowTcpForwarding for ssh jumpstart

    spec = api_client.vms.Spec(1, 2)

    spec.user_data += cloud_user_data.format(password=vm_passwd)

    vm_name = unique_name + "-mgmt"
    # Create VM
    spec.add_image(image_opensuse.name, "default/" + image_opensuse.name)

    code, data = api_client.vms.create(vm_name, spec)
    assert 201 == code, (f"Failed to create vm with error: {code}, {data}")

    # Check VM start in running state
    check_vm_running(api_client, vm_name, wait_timeout)

    # Check until VM ip address exists
    check_vm_ip_exists(api_client, vm_name, wait_timeout)

    # Get VM interface ipAddresses
    code, data = api_client.vms.get_status(vm_name)
    assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")

    interfaces_data = data['status']['interfaces']

    assert 1 == len(interfaces_data), (
        f"Failed: get more than one interface: {interfaces_data}"
    )

    mgmt_ip = interfaces_data[0]['ipAddress']

    # Ping management ip address from Harvester node
    with host_shell.login(vip) as sh:
        stdout, stderr = sh.exec_command(f"ping -c 50 {mgmt_ip}")

    assert stdout.find(f"64 bytes from {mgmt_ip}") > 0, (
        f"Failed to ping VM management IP {mgmt_ip} "
        f"on management interface from Harvester node")

    # SSH to management ip address and execute command from Harvester node
    with vm_shell_from_host(vip, mgmt_ip, vm_user, vm_passwd) as sh:
        stdout, stderr = sh.exec_command("ls")

    assert stdout.find("bin") == 0, (
        f"Failed to ssh to VM management IP {mgmt_ip} "
        f"on management interface from Harvester node")

    # Check should not SSH to management ip address from external host
    command = ['/usr/bin/ssh', '-o', 'ConnectTimeout=5', mgmt_ip]

    with pytest.raises(subprocess.CalledProcessError) as ex:
        subprocess.check_output(command, stderr=subprocess.STDOUT,
                                shell=False, encoding="utf-8")

    # OpenSSH returns the return code of the program that was executed on
    # the remote, unless there was an error for SSH itself, in which case
    # it returns 255
    assert ex.value.returncode == 255, ("Failed: should not be able to SSH"
                                        " to VM on management interface"
                                        f" {mgmt_ip} from external host")

    # teardown
    code, data = api_client.vms.get(vm_name)
    vm_spec = api_client.vms.Spec.from_dict(data)
    vm_checker.wait_deleted(vm_name)
    for vol in vm_spec.volumes:
        vol_name = vol['volume']['persistentVolumeClaim']['claimName']
        api_client.volumes.delete(vol_name)

Manual test plan reference: https://harvester.github.io/tests/manual/network/validate-network-management-network/

Steps: 1. Create a new VM 2. Make sure that the network is set to the management network with masquerade as the type 3. Wait until the VM boot in running state 4. Check can ping VM with management network from Harvester node 5. Check can SSH to VM with management network from Harvester node 6. Check can't SSH to VM with management network from external host

def test_mgmt_to_vlan_connection(self,
api_client,
request,
client,
unique_name,
image_opensuse,
vm_network,
wait_timeout,
vm_checker)
Expand source code
@pytest.mark.p0
@pytest.mark.networks
def test_mgmt_to_vlan_connection(self, api_client, request, client, unique_name,
                                 image_opensuse, vm_network, wait_timeout, vm_checker):
    """
    Manual test plan reference:
    https://harvester.github.io/tests/manual/network/edit-network-form-change-management-to-vlan/


    Steps:
    1. Create an external VLAN network
    2. Create a new VM
    3. Make sure that the network is set to the management network with masquerade as the type
    4. Wait until the VM boot in running state
    5. Edit VM and change management network to external VLAN with bridge type
    6. Check VM should save and reboot
    7. Check can ping the VM from an external network host
    8. Check can ssh to the VM from an external network host
    """

    # Check image exists
    code, data = api_client.images.get(image_opensuse.name)

    if code == 404:
        create_image_url(api_client, image_opensuse.name, image_opensuse.url, wait_timeout)

    spec = api_client.vms.Spec(1, 2)
    spec.user_data += cloud_user_data.format(password=vm_credential["password"])
    vm_name = unique_name + "-mgmt-vlan"
    # Create VM
    spec.add_image(image_opensuse.name, "default/" + image_opensuse.name)
    code, data = api_client.vms.create(vm_name, spec)
    assert 201 == code, (f"Failed to create vm with error: {code}, {data}")

    # Check VM start in running state
    check_vm_running(api_client, vm_name, wait_timeout)

    # Check until VM ip address exists
    check_vm_ip_exists(api_client, vm_name, wait_timeout)

    # get data from running VM and transfer to spec
    code, data = api_client.vms.get(vm_name)
    assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")

    spec = spec.from_dict(data)

    # Switch to vlan network
    spec.mgmt_network = False

    spec.add_network("nic-1", f"{vm_network['namespace']}/{vm_network['name']}")

    # Update VM spec
    code, data = api_client.vms.update(vm_name, spec)
    assert 200 == code, (f"Failed to update specific vm with spec: {code}, {data}")

    code, data = api_client.vms.restart(vm_name)
    assert 204 == code, (f"Failed to restart specific vm: {code}, {data}")

    # Check VM start in running state
    check_vm_running(api_client, vm_name, wait_timeout)

    # Check until VM ip address exists
    check_vm_ip_exists(api_client, vm_name, wait_timeout)

    # Get VM interface ipAddresses
    code, data = api_client.vms.get_status(vm_name)
    assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")

    interfaces_data = data['status']['interfaces']

    assert 1 == len(interfaces_data), (
        f"Failed: get more than one interface: {interfaces_data}"
    )

    # Determine by vlan network Name
    endtime = datetime.now() + timedelta(seconds=wait_timeout)
    ip_addresses = []

    while endtime > datetime.now():
        code, data = api_client.vms.get_status(vm_name)
        assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")
        if 'interfaces' in data['status']:
            interfaces_data = data['status']['interfaces']
            ip_addresses = []
            for interface in interfaces_data:
                # Check the ipAddress in digital format
                if (
                    'ipAddress' in interface and
                    interface['ipAddress'].replace('.', '').isdigit()
                ):
                    ip_addresses.append(interface['ipAddress'])

            if len(ip_addresses) > 0:
                if 'nic-1' in interface['name']:
                    break
        sleep(5)
    else:
        raise AssertionError(
            f"Failed to get VM {vm_name} IP address, exceed given timeout\n"
            f"Still got {code} with {data}"
        )

    # Ping vlan ip address
    vlan_ip = ip_addresses[0]

    command = ['/usr/bin/ping', '-c', '50', vlan_ip]

    result = subprocess.check_output(command, shell=False, encoding="utf-8")

    assert result.find(f"64 bytes from {vlan_ip}") > 0, (
        f"Failed to ping VM external vlan IP {vlan_ip} "
        f"on vlan interface from external host")

    # SSH to vlan ip address and execute command
    _stdout, _stderr = self.ssh_client(
        client, vlan_ip, vm_credential["user"], vm_credential["password"], 'ls', wait_timeout)

    stdout = _stdout.read().decode('ascii').strip("\n")

    assert stdout.find("bin") == 0, (
        f"Failed to ssh to VM external vlan IP {vlan_ip}"
        f"on vlan interface from external host")

    # teardown
    code, data = api_client.vms.get(vm_name)
    vm_spec = api_client.vms.Spec.from_dict(data)
    vm_checker.wait_deleted(vm_name)
    for vol in vm_spec.volumes:
        vol_name = vol['volume']['persistentVolumeClaim']['claimName']
        api_client.volumes.delete(vol_name)

Manual test plan reference: https://harvester.github.io/tests/manual/network/edit-network-form-change-management-to-vlan/

Steps: 1. Create an external VLAN network 2. Create a new VM 3. Make sure that the network is set to the management network with masquerade as the type 4. Wait until the VM boot in running state 5. Edit VM and change management network to external VLAN with bridge type 6. Check VM should save and reboot 7. Check can ping the VM from an external network host 8. Check can ssh to the VM from an external network host

def test_reboot_vlan_connection(self,
api_client,
request,
unique_name,
image_opensuse,
vm_network,
wait_timeout,
vm_checker)
Expand source code
@pytest.mark.p0
@pytest.mark.networks
@pytest.mark.dependency(name="reboot_vlan_connection",
                        depends=["vlan_network_connection"])
def test_reboot_vlan_connection(self, api_client, request, unique_name,
                                image_opensuse, vm_network, wait_timeout, vm_checker):
    """
    Manual test plan reference:
    https://harvester.github.io/tests/manual/network/negative-vlan-after-reboot/


    Steps:
    1. Create an external VLAN network
    2. Create a new VM and add the external vlan network
    3. Check can ping external VLAN IP
    4. Reboot VM
    5. Ping VM during reboot
    6. Check can't ping VM during reboot
    7. Check the VM should reboot
    8. Ping VM after reboot
    9. Check can ping VM
    """
    vm_name = unique_name + "-reboot-vlan"

    # Check image exists
    code, data = api_client.images.get(image_opensuse.name)

    if code == 404:
        create_image_url(api_client, image_opensuse.name, image_opensuse.url, wait_timeout)

    spec = api_client.vms.Spec(1, 2, mgmt_network=False)
    spec.user_data += cloud_user_data.format(password=vm_credential["password"])

    # Create VM
    spec.add_image(image_opensuse.name, "default/" + image_opensuse.name)

    spec.add_network("nic-1", f"{vm_network['namespace']}/{vm_network['name']}")

    code, data = api_client.vms.create(vm_name, spec)
    assert 201 == code, (f"Failed to create vm with error: {code}, {data}")

    # Check VM start in running state
    check_vm_running(api_client, vm_name, wait_timeout)

    # Check until VM ip address exists
    check_vm_ip_exists(api_client, vm_name, wait_timeout)

    # Get VM interface ipAddresses
    code, data = api_client.vms.get_status(vm_name)
    assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")

    interfaces_data = data['status']['interfaces']

    assert 1 == len(interfaces_data), (
        f"Failed: get more than one interface: {interfaces_data}"
    )

    assert "nic-1" == interfaces_data[0]['name'], (
        f"Failed: Network name did not match to added vlan: {interfaces_data}"
    )

    vlan_ip = interfaces_data[0]['ipAddress']

    # Check can ping vlan ip

    command = ['/usr/bin/ping', '-c', '10', vlan_ip]

    result = subprocess.check_output(command, shell=False, encoding="utf-8")

    assert result.find(f"64 bytes from {vlan_ip}") > 0, (
        f"Failed to ping VM external vlan IP {vlan_ip} "
        f"on vlan interface from external host")

    # Restart VM
    code, data = api_client.vms.restart(vm_name)
    assert 204 == code, (f"Failed to reboot vm with error: {code}, {data}")

    # Check VM start in Starting state
    endtime = datetime.now() + timedelta(seconds=wait_timeout)

    while endtime > datetime.now():
        code, data = api_client.vms.get(vm_name)
        assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")
        vm_fields = data['metadata']['fields']

        if vm_fields[2] == 'Starting':
            break
        sleep(5)
    else:
        raise AssertionError(
            f"Failed to restart VM {vm_name} in Starting status, exceed given timeout\n"
            f"Still got {code} with {data}"
        )

    # Check can't ping vlan ip during reboot

    command = ['/usr/bin/ping', '-c', '10', vlan_ip]

    process = subprocess.run(command, stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE, universal_newlines=True)

    result = process.stdout

    assert result.find(f"64 bytes from {vlan_ip}") < 0, (
        f"Failed: since can ping VM external vlan IP {vlan_ip} "
        f"on vlan interface from external host during reboot")

    # Check VM start in running state
    check_vm_running(api_client, vm_name, wait_timeout)

    # Check until VM ip address exists
    check_vm_ip_exists(api_client, vm_name, wait_timeout)

    # Get VM interface ipAddresses
    code, data = api_client.vms.get_status(vm_name)
    assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")

    interfaces_data = data['status']['interfaces']

    assert 1 == len(interfaces_data), (
        f"Failed: get more than one interface: {interfaces_data}"
    )

    assert "nic-1" == interfaces_data[0]['name'], (
        f"Failed: Network name did not match to added vlan: {interfaces_data}"
    )

    vlan_ip = interfaces_data[0]['ipAddress']

    # Ping vlan ip address

    command = ['/usr/bin/ping', '-c', '10', vlan_ip]

    result = subprocess.check_output(command, shell=False, encoding="utf-8")

    assert result.find(f"64 bytes from {vlan_ip}") > 0, (
        f"Failed to ping VM external vlan IP {vlan_ip} "
        f"on vlan interface from external host")

    # teardown
    code, data = api_client.vms.get(vm_name)
    vm_spec = api_client.vms.Spec.from_dict(data)
    vm_checker.wait_deleted(vm_name)
    for vol in vm_spec.volumes:
        vol_name = vol['volume']['persistentVolumeClaim']['claimName']
        api_client.volumes.delete(vol_name)

Manual test plan reference: https://harvester.github.io/tests/manual/network/negative-vlan-after-reboot/

Steps: 1. Create an external VLAN network 2. Create a new VM and add the external vlan network 3. Check can ping external VLAN IP 4. Reboot VM 5. Ping VM during reboot 6. Check can't ping VM during reboot 7. Check the VM should reboot 8. Ping VM after reboot 9. Check can ping VM

def test_vlan_network_connection(self,
api_client,
request,
client,
unique_name,
image_opensuse,
vm_network,
wait_timeout,
vm_checker)
Expand source code
@pytest.mark.p0
@pytest.mark.networks
@pytest.mark.dependency(name="vlan_network_connection")
def test_vlan_network_connection(self, api_client, request, client, unique_name,
                                 image_opensuse, vm_network, wait_timeout, vm_checker):
    """
    Manual test plan reference:
    https://harvester.github.io/tests/manual/network/validate-network-external-vlan/


    Steps:
    1. Create an external VLAN network
    2. Create a new VM and set the external vlan network to it
    3. Check can ping external VLAN IP from external host
    4. Check can SSH to VM from external IP from external host
    """
    vm_name = unique_name + "-vlan"

    # Check image exists
    code, data = api_client.images.get(image_opensuse.name)

    if code == 404:
        create_image_url(api_client, image_opensuse.name, image_opensuse.url, wait_timeout)

    spec = api_client.vms.Spec(1, 2, mgmt_network=False)
    spec.user_data += cloud_user_data.format(password=vm_credential["password"])

    # Create VM
    spec.add_image(image_opensuse.name, "default/" + image_opensuse.name)

    spec.add_network("nic-1", f"{vm_network['namespace']}/{vm_network['name']}")

    code, data = api_client.vms.create(vm_name, spec)
    assert 201 == code, (f"Failed to create vm with error: {code}, {data}")

    # Check VM start in running state
    check_vm_running(api_client, vm_name, wait_timeout)

    # Check until VM ip address exists
    check_vm_ip_exists(api_client, vm_name, wait_timeout)

    # Get VM interface ipAddresses
    code, data = api_client.vms.get_status(vm_name)
    assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")

    interfaces_data = data['status']['interfaces']

    assert 1 == len(interfaces_data), (
        f"Failed: get more than one interface: {interfaces_data}"
    )

    assert "nic-1" == interfaces_data[0]['name'], (
        f"Failed: Network name did not match to added vlan: {interfaces_data}"
    )

    vlan_ip = interfaces_data[0]['ipAddress']

    # Ping vlan ip address from external host
    command = ['/usr/bin/ping', '-c', '50', vlan_ip]

    result = subprocess.check_output(command, shell=False, encoding="utf-8")

    assert result.find(f"64 bytes from {vlan_ip}") > 0, (
        f"Failed to ping VM external vlan IP {vlan_ip} "
        f"on vlan interface from external host")

    # SSH to vlan ip address and execute command from external host
    _stdout, _stderr = self.ssh_client(
        client, vlan_ip, vm_credential["user"], vm_credential["password"], 'ls', wait_timeout)

    stdout = _stdout.read().decode('ascii').strip("\n")

    assert stdout.find("bin") == 0, (
        f"Failed to ssh to VM external vlan IP {vlan_ip}"
        f"on vlan interface from external host")

    # teardown
    code, data = api_client.vms.get(vm_name)
    vm_spec = api_client.vms.Spec.from_dict(data)
    vm_checker.wait_deleted(vm_name)
    for vol in vm_spec.volumes:
        vol_name = vol['volume']['persistentVolumeClaim']['claimName']
        api_client.volumes.delete(vol_name)

Manual test plan reference: https://harvester.github.io/tests/manual/network/validate-network-external-vlan/

Steps: 1. Create an external VLAN network 2. Create a new VM and set the external vlan network to it 3. Check can ping external VLAN IP from external host 4. Check can SSH to VM from external IP from external host

def test_vlan_to_mgmt_connection(self,
api_client,
request,
client,
unique_name,
image_opensuse,
vm_network,
wait_timeout,
host_shell,
vm_shell_from_host,
vm_checker)
Expand source code
@pytest.mark.p0
@pytest.mark.networks
def test_vlan_to_mgmt_connection(
    self, api_client, request, client, unique_name, image_opensuse, vm_network, wait_timeout,
    host_shell, vm_shell_from_host, vm_checker
):
    """
    Manual test plan reference:
    https://harvester.github.io/tests/manual/network/edit-network-form-change-management-to-vlan/


    Steps:
    1. Create an external VLAN network
    2. Create a new VM
    3. Make sure that the network is set to the vlan network with bridge as the type
    4. Wait until the VM boot in running state
    5. Edit VM and change from external VLAN to management network
    6. Check VM should save and reboot
    7. Check can ping VM with management network from Harvester node
    8. Check can SSH to VM with management network from Harvester node
    9. Check can't SSH to VM with management network from external host
    """

    vip = request.config.getoption('--endpoint').strip('https://')
    vm_user, vm_passwd = vm_credential['user'], vm_credential['password']

    # Check image exists
    code, data = api_client.images.get(image_opensuse.name)

    if code == 404:
        create_image_url(api_client, image_opensuse.name, image_opensuse.url, wait_timeout)

    spec = api_client.vms.Spec(1, 2, mgmt_network=False)
    spec.user_data += cloud_user_data.format(password=vm_passwd)
    vm_name = unique_name + "-vlan-mgmt"

    # Create VM
    spec.add_image(image_opensuse.name, "default/" + image_opensuse.name)
    spec.add_network("default", f"{vm_network['namespace']}/{vm_network['name']}")

    code, data = api_client.vms.create(vm_name, spec)
    assert 201 == code, (f"Failed to create vm with error: {code}, {data}")

    # Check VM start in running state
    check_vm_running(api_client, vm_name, wait_timeout)

    # Check until VM ip address exists
    check_vm_ip_exists(api_client, vm_name, wait_timeout)

    # get data from running VM and transfer to spec
    code, data = api_client.vms.get(vm_name)
    assert 200 == code, (f"Failed to get specific vm content: {code}, {data}")
    spec = spec.from_dict(data)

    spec.networks = []
    spec.mgmt_network = True

    code, data = api_client.vms.update(vm_name, spec)
    assert 200 == code, (f"Failed to update specific vm with spec: {code}, {data}")

    code, data = api_client.vms.restart(vm_name)
    assert 204 == code, (f"Failed to restart specific vm: {code}, {data}")

    # Check VM start in running state
    check_vm_running(api_client, vm_name, wait_timeout)

    # Check until VM ip address exists
    check_vm_ip_exists(api_client, vm_name, wait_timeout)

    endtime = datetime.now() + timedelta(seconds=wait_timeout)

    while endtime > datetime.now():
        code, data = api_client.vms.get_status(vm_name)
        assert 200 == code, (f"Failed to get specific vm status: {code}, {data}")

        if 'interfaces' in data['status']:
            interfaces_data = data['status']['interfaces']
            ip_addresses = []
            if 'ipAddress' in data['status']['interfaces'][0]:

                if 'default' in interfaces_data[0]['name']:
                    if 'domain, guest-agent' not in interfaces_data[0]['infoSource']:
                        ip_addresses.append(interfaces_data[0]['ipAddress'])
                        break
            sleep(5)
    else:
        raise AssertionError(
            f"Failed to get VM {vm_name} IP address, exceed the given timed out\n"
            f"Still got {code} with {data}"
        )

    # Check can ping management ip address from Harvester node
    mgmt_ip = ip_addresses[0]

    with host_shell.login(vip) as sh:
        stdout, stderr = sh.exec_command(f"ping -c 50 {mgmt_ip}")

    assert stdout.find(f"64 bytes from {mgmt_ip}") > 0, (
        f"Failed to ping VM management IP {mgmt_ip} "
        f"on management interface from Harvester node")

    # Check can ssh to host and execute command from Harvester node
    with vm_shell_from_host(vip, mgmt_ip, vm_user, vm_passwd) as sh:
        stdout, stderr = sh.exec_command("ls")

    assert stdout.find("bin") == 0, (
        f"Failed to ssh to VM management IP {mgmt_ip} "
        f"on management interface from Harvester node")

    # Check should not SSH to management ip address from external host
    command = ['/usr/bin/ssh', '-o', 'ConnectTimeout=5', mgmt_ip]

    with pytest.raises(subprocess.CalledProcessError) as ex:
        subprocess.check_output(command, stderr=subprocess.STDOUT,
                                shell=False, encoding="utf-8")

    # OpenSSH returns the return code of the program that was executed on
    # the remote, unless there was an error for SSH itself, in which case
    # it returns 255
    assert ex.value.returncode == 255, ("Failed: should not be able to SSH"
                                        " to VM on management interface"
                                        f" {mgmt_ip} from external host")

    # teardown
    code, data = api_client.vms.get(vm_name)
    vm_spec = api_client.vms.Spec.from_dict(data)
    vm_checker.wait_deleted(vm_name)
    for vol in vm_spec.volumes:
        vol_name = vol['volume']['persistentVolumeClaim']['claimName']
        api_client.volumes.delete(vol_name)

Manual test plan reference: https://harvester.github.io/tests/manual/network/edit-network-form-change-management-to-vlan/

Steps: 1. Create an external VLAN network 2. Create a new VM 3. Make sure that the network is set to the vlan network with bridge as the type 4. Wait until the VM boot in running state 5. Edit VM and change from external VLAN to management network 6. Check VM should save and reboot 7. Check can ping VM with management network from Harvester node 8. Check can SSH to VM with management network from Harvester node 9. Check can't SSH to VM with management network from external host