Ejemplo n.º 1
0
def test_reboot_without_version_change(client: IntegrationInstance):
    log = client.read_from_file("/var/log/cloud-init.log")
    assert "Python version change detected" not in log
    assert "Cache compatibility status is currently unknown." not in log
    _assert_no_pickle_problems(log)

    client.restart()
    log = client.read_from_file("/var/log/cloud-init.log")
    assert "Python version change detected" not in log
    assert "Could not determine Python version used to write cache" not in log
    _assert_no_pickle_problems(log)

    # Now ensure that loading a bad pickle gives us problems
    client.push_file(TEST_PICKLE, PICKLE_PATH)
    client.restart()
    log = client.read_from_file("/var/log/cloud-init.log")

    # no cache found is an "expected" upgrade error, and
    # "Failed" means we're unable to load the pickle
    assert any(
        [
            "Failed loading pickled blob from {}".format(PICKLE_PATH) in log,
            "no cache found" in log,
        ]
    )
Ejemplo n.º 2
0
def test_dual_stack(client: IntegrationInstance):
    # Drop IPv4 responses
    assert client.execute("iptables -I INPUT -s 169.254.169.254 -j DROP").ok
    _test_crawl(client, "http://[fd00:ec2::254]")

    # Block IPv4 requests
    assert client.execute("iptables -I OUTPUT -d 169.254.169.254 -j REJECT").ok
    _test_crawl(client, "http://[fd00:ec2::254]")

    # Re-enable IPv4
    assert client.execute("iptables -D OUTPUT -d 169.254.169.254 -j REJECT").ok
    assert client.execute("iptables -D INPUT -s 169.254.169.254 -j DROP").ok

    # Drop IPv6 responses
    assert client.execute("ip6tables -I INPUT -s fd00:ec2::254 -j DROP").ok
    _test_crawl(client, "http://169.254.169.254")

    # Block IPv6 requests
    assert client.execute("ip6tables -I OUTPUT -d fd00:ec2::254 -j REJECT").ok
    _test_crawl(client, "http://169.254.169.254")

    # Force NoDHCPLeaseError (by removing dhclient) and assert ipv6 still works
    # Destructive test goes last
    # dhclient is at /sbin/dhclient on bionic but /usr/sbin/dhclient elseware
    assert client.execute("rm $(which dhclient)").ok
    client.restart()
    log = client.read_from_file("/var/log/cloud-init.log")
    assert "Crawl of metadata service using link-local ipv6 took" in log
Ejemplo n.º 3
0
def test_sudoers_includedir(client: IntegrationInstance):
    """Ensure we don't add additional #includedir to sudoers.

    Newer versions of /etc/sudoers will use @includedir rather than
    #includedir. Ensure we handle that properly and don't include an
    additional #includedir when one isn't warranted.

    https://github.com/canonical/cloud-init/pull/783
    """
    if ImageSpecification.from_os_image().release in [
            'xenial', 'bionic', 'focal'
    ]:
        raise pytest.skip(
            'Test requires version of sudo installed on groovy and later')
    client.execute("sed -i 's/#include/@include/g' /etc/sudoers")

    sudoers = client.read_from_file('/etc/sudoers')
    if '@includedir /etc/sudoers.d' not in sudoers:
        client.execute("echo '@includedir /etc/sudoers.d' >> /etc/sudoers")
    client.instance.clean()
    client.restart()
    sudoers = client.read_from_file('/etc/sudoers')

    assert '#includedir' not in sudoers
    assert sudoers.count('includedir /etc/sudoers.d') == 1
Ejemplo n.º 4
0
def test_boot_event_disabled_by_default(client: IntegrationInstance):
    log = client.read_from_file("/var/log/cloud-init.log")
    if "network config is disabled" in log:
        pytest.skip("network config disabled. Test doesn't apply")
    assert "Applying network configuration" in log
    assert "dummy0" not in client.execute("ls /sys/class/net")

    _add_dummy_bridge_to_netplan(client)
    client.execute("rm /var/log/cloud-init.log")

    client.restart()
    log2 = client.read_from_file("/var/log/cloud-init.log")

    if "cache invalid in datasource" in log2:
        # Invalid cache will get cleared, meaning we'll create a new
        # "instance" and apply networking config, so events aren't
        # really relevant here
        pytest.skip("Test only valid for existing instances")

    # We attempt to apply network config twice on every boot.
    # Ensure neither time works.
    assert 2 == len(
        re.findall(r"Event Denied: scopes=\['network'\] EventType=boot[^-]",
                   log2))
    assert 2 == log2.count(
        "Event Denied: scopes=['network'] EventType=boot-legacy")
    assert 2 == log2.count("No network config applied. Neither a new instance"
                           " nor datasource network update allowed")

    assert "dummy0" in client.execute("ls /sys/class/net")
Ejemplo n.º 5
0
def _customize_envionment(client: IntegrationInstance):
    # Assert our platform can detect LXD during sytemd generator timeframe.
    ds_id_log = client.execute("cat /run/cloud-init/ds-identify.log").stdout
    assert "check for 'LXD' returned found" in ds_id_log

    # At some point Jammy will fail this test. We want to be informed
    # when Jammy images no longer ship NoCloud template files (LP: #1958460).
    assert "check for 'NoCloud' returned found" in ds_id_log
    if client.settings.PLATFORM == "lxd_vm":
        # ds-identify runs at systemd generator time before /dev/lxd/sock.
        # Assert we can expected artifact which indicates LXD is viable.
        result = client.execute("cat /sys/class/dmi/id/board_name")
        if not result.ok:
            raise AssertionError(
                "Missing expected /sys/class/dmi/id/board_name")
        if "LXD" != result.stdout:
            raise AssertionError(f"DMI board_name is not LXD: {result.stdout}")

    # Having multiple datasources prevents ds-identify from short-circuiting
    # detection logic with a log like:
    #     single entry in datasource_list (LXD) use that.
    # Also, NoCloud is detected during init-local timeframe.

    # If there is a race on VMs where /dev/lxd/sock is not setup in init-local
    # cloud-init will fallback to NoCloud and fail this test.
    client.write_to_file(
        "/etc/cloud/cloud.cfg.d/99-detect-lxd-first.cfg",
        "datasource_list: [LXD, NoCloud]\n",
    )
    client.execute("cloud-init clean --logs")
    client.restart()
def _check_iid_insensitive_across_kernel_upgrade(
    instance: IntegrationInstance, ):
    uuid = instance.read_from_file("/sys/class/dmi/id/product_uuid")
    assert (uuid.isupper()
            ), "Expected uppercase UUID on Ubuntu FIPS image {}".format(uuid)
    orig_kernel = instance.execute("uname -r").strip()
    assert "azure-fips" in orig_kernel
    result = instance.execute("apt-get update")
    # Install a 5.4+ kernel which provides lowercase product_uuid
    result = instance.execute("apt-get install linux-azure --assume-yes")
    if not result.ok:
        pytest.fail("Unable to install linux-azure kernel: {}".format(result))
    # Remove ubuntu-azure-fips metapkg which mandates FIPS-flavour kernel
    result = instance.execute("ua disable fips --assume-yes")
    assert result.ok, "Unable to disable fips: {}".format(result)
    instance.restart()
    new_kernel = instance.execute("uname -r").strip()
    assert orig_kernel != new_kernel
    assert "azure-fips" not in new_kernel
    assert "azure" in new_kernel
    new_uuid = instance.read_from_file("/sys/class/dmi/id/product_uuid")
    assert (
        uuid.lower() == new_uuid
    ), "Expected UUID on linux-azure to be lowercase of FIPS: {}".format(uuid)
    log = instance.read_from_file("/var/log/cloud-init.log")
    RE_CONFIG_SSH_SEMAPHORE = r"Writing.*sem/config_ssh "
    ssh_runs = len(re.findall(RE_CONFIG_SSH_SEMAPHORE, log))
    assert 1 == ssh_runs, "config_ssh ran too many times {}".format(ssh_runs)
Ejemplo n.º 7
0
def _customize_envionment(client: IntegrationInstance):
    client.write_to_file(
        "/etc/cloud/cloud.cfg.d/99-detect-lxd.cfg",
        "datasource_list: [LXD]\n",
    )
    client.execute("cloud-init clean --logs")
    client.restart()
Ejemplo n.º 8
0
def _customize_environment(client: IntegrationInstance):
    # Insert our "disable_network_activation" file here
    client.write_to_file(
        "/etc/cloud/cloud.cfg.d/99-disable-network-activation.cfg",
        "disable_network_activation: true\n",
    )
    client.execute("cloud-init clean --logs")
    client.restart()
Ejemplo n.º 9
0
def custom_client(client: IntegrationInstance,
                  tmpdir) -> Iterator[IntegrationInstance]:
    client.write_to_file(f"/etc/cloud/cloud.cfg.d/{CUSTOM_CLOUD_DIR_FN}",
                         CUSTOM_CLOUD_DIR)
    client.execute(f"rm -rf {DEFAULT_CLOUD_DIR}")  # Remove previous cloud_dir
    client.execute("cloud-init clean --logs")
    client.restart()
    yield client
Ejemplo n.º 10
0
def test_log_message_on_missing_version_file(client: IntegrationInstance):
    # Start by pushing a pickle so we can see the log message
    client.push_file(TEST_PICKLE, PICKLE_PATH)
    client.execute("rm /var/lib/cloud/data/python-version")
    client.restart()
    log = client.read_from_file('/var/log/cloud-init.log')
    assert ('Writing python-version file. '
            'Cache compatibility status is currently unknown.') in log
Ejemplo n.º 11
0
def _customize_envionment(client: IntegrationInstance):
    # Insert our "disable_network_config" file here
    client.write_to_file(
        "/etc/cloud/cloud.cfg.d/99-disable-network-config.cfg",
        "network: {config: disabled}\n",
    )
    client.execute("cloud-init clean --logs")
    client.restart()
Ejemplo n.º 12
0
def test_cache_purged_on_version_change(client: IntegrationInstance):
    # Start by pushing the invalid pickle so we'll hit an error if the
    # cache didn't actually get purged
    client.push_file(TEST_PICKLE, PICKLE_PATH)
    client.execute("echo '1.0' > /var/lib/cloud/data/python-version")
    client.restart()
    log = client.read_from_file("/var/log/cloud-init.log")
    assert "Python version change detected. Purging cache" in log
    _assert_no_pickle_problems(log)
Ejemplo n.º 13
0
def test_log_message_on_missing_version_file(client: IntegrationInstance):
    client.push_file(TEST_PICKLE, PICKLE_PATH)
    client.restart()
    assert client.execute('cloud-init status --wait').ok
    log = client.read_from_file('/var/log/cloud-init.log')
    verify_ordered_items_in_text([
        "Unable to unpickle datasource: 'MIMEMultipart' object has no "
        "attribute 'policy'. Ignoring current cache.", 'no cache found',
        'Searching for local data source',
        'SUCCESS: found local data from DataSourceNoCloud'
    ], log)
Ejemplo n.º 14
0
def test_disk_setup_no_partprobe(create_disk, client: IntegrationInstance):
    """Ensure disk setup still works as expected without partprobe."""
    # We can't do this part in a bootcmd because the path has already
    # been found by the time we get to the bootcmd
    client.execute('rm $(which partprobe)')
    client.execute('cloud-init clean --logs')
    client.restart()

    log = client.read_from_file('/var/log/cloud-init.log')
    _verify_first_disk_setup(client, log)

    assert 'partprobe' not in log
Ejemplo n.º 15
0
def customize_environment(
    client: IntegrationInstance,
    tmpdir,
    configure_secondary_nics: bool = False,
):
    cfg = tmpdir.join("01_oracle_datasource.cfg")
    with open(cfg, "w") as f:
        f.write(
            DS_CFG.format(configure_secondary_nics=configure_secondary_nics))
    client.push_file(cfg, "/etc/cloud/cloud.cfg.d/01_oracle_datasource.cfg")

    client.execute("cloud-init clean --logs")
    client.restart()
Ejemplo n.º 16
0
def _test_network_config_applied_on_reboot(client: IntegrationInstance):
    log = client.read_from_file('/var/log/cloud-init.log')
    assert 'Applying network configuration' in log
    assert 'dummy0' not in client.execute('ls /sys/class/net')

    _add_dummy_bridge_to_netplan(client)
    client.execute('rm /var/log/cloud-init.log')
    client.restart()
    log = client.read_from_file('/var/log/cloud-init.log')

    assert 'Event Allowed: scope=network EventType=boot' in log
    assert 'Applying network configuration' in log
    assert 'dummy0' not in client.execute('ls /sys/class/net')
Ejemplo n.º 17
0
    def test_disk_setup_when_mounted(
        self, create_disk, client: IntegrationInstance
    ):
        """Test lp-1920939.

        We insert an extra disk into our VM, format it to have two partitions,
        modify our cloud config to mount devices before disk setup, and modify
        our userdata to setup a single partition on the disk.

        This allows cloud-init to attempt disk setup on a mounted partition.
        When blockdev is in use, it will fail with
        "blockdev: ioctl error on BLKRRPART: Device or resource busy" along
        with a warning and a traceback. When partprobe is in use, everything
        should work successfully.
        """
        log = client.read_from_file("/var/log/cloud-init.log")
        self._verify_first_disk_setup(client, log)

        # Ensure NoCloud gets detected on reboot
        client.execute("mkdir -p /var/lib/cloud/seed/nocloud-net/")
        client.execute("touch /var/lib/cloud/seed/nocloud-net/meta-data")
        client.write_to_file(
            "/etc/cloud/cloud.cfg.d/99_nocloud.cfg",
            "datasource_list: [ NoCloud ]\n",
        )
        # Update our userdata and cloud.cfg to mount then perform new disk
        # setup
        client.write_to_file(
            "/var/lib/cloud/seed/nocloud-net/user-data",
            UPDATED_PARTPROBE_USERDATA,
        )
        client.execute(
            "sed -i 's/write-files/write-files\\n - mounts/' "
            "/etc/cloud/cloud.cfg"
        )

        client.execute("cloud-init clean --logs")
        client.restart()

        # Assert new setup works as expected
        verify_clean_log(log)

        lsblk = json.loads(client.execute("lsblk --json"))
        sdb = [x for x in lsblk["blockdevices"] if x["name"] == "sdb"][0]
        assert len(sdb["children"]) == 1
        assert sdb["children"][0]["name"] == "sdb1"
        if "mountpoint" in sdb["children"][0]:
            assert sdb["children"][0]["mountpoint"] == "/mnt3"
        else:
            assert sdb["children"][0]["mountpoints"] == ["/mnt3"]
Ejemplo n.º 18
0
def test_log_message_on_missing_version_file(client: IntegrationInstance):
    # Start by pushing a pickle so we can see the log message
    client.push_file(TEST_PICKLE, PICKLE_PATH)
    client.execute("rm /var/lib/cloud/data/python-version")
    client.execute("rm /var/log/cloud-init.log")
    client.restart()
    log = client.read_from_file("/var/log/cloud-init.log")
    if "no cache found" not in log:
        # We don't expect the python version file to exist if we have no
        # pre-existing cache
        assert (
            "Writing python-version file. "
            "Cache compatibility status is currently unknown." in log
        )
Ejemplo n.º 19
0
def test_datasource_rbx_no_stacktrace(client: IntegrationInstance):
    client.write_to_file(
        "/etc/cloud/cloud.cfg.d/90_dpkg.cfg",
        "datasource_list: [ RbxCloud, NoCloud, LXD ]\n",
    )
    client.write_to_file(
        "/etc/cloud/ds-identify.cfg",
        "policy: enabled\n",
    )
    client.execute("cloud-init clean --logs")
    client.restart()

    log = client.read_from_file("/var/log/cloud-init.log")
    verify_clean_log(log)
    assert "Failed to load metadata and userdata" not in log
    assert ("Getting data from <class 'cloudinit.sources.DataSourceRbxCloud."
            "DataSourceRbxCloud'> failed" not in log)
Ejemplo n.º 20
0
def test_reboot_without_version_change(client: IntegrationInstance):
    log = client.read_from_file('/var/log/cloud-init.log')
    assert 'Python version change detected' not in log
    assert 'Cache compatibility status is currently unknown.' not in log
    _assert_no_pickle_problems(log)

    client.restart()
    log = client.read_from_file('/var/log/cloud-init.log')
    assert 'Python version change detected' not in log
    assert 'Could not determine Python version used to write cache' not in log
    _assert_no_pickle_problems(log)

    # Now ensure that loading a bad pickle gives us problems
    client.push_file(TEST_PICKLE, PICKLE_PATH)
    client.restart()
    log = client.read_from_file('/var/log/cloud-init.log')
    assert 'Failed loading pickled blob from {}'.format(PICKLE_PATH) in log
Ejemplo n.º 21
0
def test_frequency_override(client: IntegrationInstance):
    # Some pre-checks
    assert ("running config-scripts-user with frequency once-per-instance"
            in client.read_from_file("/var/log/cloud-init.log"))
    assert client.read_from_file("/var/tmp/hi").strip().count("hi") == 1

    # Change frequency of scripts-user to always
    config = client.read_from_file("/etc/cloud/cloud.cfg")
    new_config = config.replace("- scripts-user", "- [ scripts-user, always ]")
    client.write_to_file("/etc/cloud/cloud.cfg", new_config)

    client.restart()

    # Ensure the script was run again
    assert ("running config-scripts-user with frequency always"
            in client.read_from_file("/var/log/cloud-init.log"))
    assert client.read_from_file("/var/tmp/hi").strip().count("hi") == 2
Ejemplo n.º 22
0
def test_datasource_rbx_no_stacktrace(client: IntegrationInstance):
    client.write_to_file(
        '/etc/cloud/cloud.cfg.d/90_dpkg.cfg',
        'datasource_list: [ RbxCloud, NoCloud ]\n',
    )
    client.write_to_file(
        '/etc/cloud/ds-identify.cfg',
        'policy: enabled\n',
    )
    client.execute('cloud-init clean --logs')
    client.restart()

    log = client.read_from_file('/var/log/cloud-init.log')
    assert 'WARNING' not in log
    assert 'Traceback' not in log
    assert 'Failed to load metadata and userdata' not in log
    assert ("Getting data from <class 'cloudinit.sources.DataSourceRbxCloud."
            "DataSourceRbxCloud'> failed") not in log
Ejemplo n.º 23
0
def test_nocloud_seedfrom_vendordata(client: IntegrationInstance):
    seed_dir = "/var/tmp/test_seed_dir"
    result = client.execute(
        "mkdir {seed_dir} && "
        "touch {seed_dir}/user-data && "
        "touch {seed_dir}/meta-data && "
        "echo 'seedfrom: {seed_dir}/' > "
        "/var/lib/cloud/seed/nocloud-net/meta-data".format(seed_dir=seed_dir))
    assert result.return_code == 0

    client.write_to_file(
        "{}/vendor-data".format(seed_dir),
        VENDOR_DATA,
    )
    client.execute("cloud-init clean --logs")
    client.restart()
    assert client.execute("cloud-init status").ok
    assert "seeded_vendordata_test_file" in client.execute("ls /var/tmp")
Ejemplo n.º 24
0
def test_nocloud_seedfrom_vendordata(client: IntegrationInstance):
    seed_dir = '/var/tmp/test_seed_dir'
    result = client.execute(
        "mkdir {seed_dir} && "
        "touch {seed_dir}/user-data && "
        "touch {seed_dir}/meta-data && "
        "echo 'seedfrom: {seed_dir}/' > "
        "/var/lib/cloud/seed/nocloud-net/meta-data".format(seed_dir=seed_dir)
    )
    assert result.return_code == 0

    client.write_to_file(
        '{}/vendor-data'.format(seed_dir),
        VENDOR_DATA,
    )
    client.execute('cloud-init clean --logs')
    client.restart(raise_on_cloudinit_failure=True)
    assert 'seeded_vendordata_test_file' in client.execute('ls /var/tmp')
Ejemplo n.º 25
0
    def test_disk_setup_when_mounted(
        self, create_disk, client: IntegrationInstance
    ):
        """Test lp-1920939.

        We insert an extra disk into our VM, format it to have two partitions,
        modify our cloud config to mount devices before disk setup, and modify
        our userdata to setup a single partition on the disk.

        This allows cloud-init to attempt disk setup on a mounted partition.
        When blockdev is in use, it will fail with
        "blockdev: ioctl error on BLKRRPART: Device or resource busy" along
        with a warning and a traceback. When partprobe is in use, everything
        should work successfully.
        """
        log = client.read_from_file('/var/log/cloud-init.log')
        self._verify_first_disk_setup(client, log)

        # Update our userdata and cloud.cfg to mount then perform new disk
        # setup
        client.write_to_file(
            '/var/lib/cloud/seed/nocloud-net/user-data',
            UPDATED_PARTPROBE_USERDATA,
        )
        client.execute(
            "sed -i 's/write-files/write-files\\n - mounts/' "
            "/etc/cloud/cloud.cfg"
        )

        client.execute('cloud-init clean --logs')
        client.restart()

        # Assert new setup works as expected
        assert 'Traceback' not in log
        assert 'WARN' not in log

        lsblk = json.loads(client.execute('lsblk --json'))
        sdb = [x for x in lsblk['blockdevices'] if x['name'] == 'sdb'][0]
        assert len(sdb['children']) == 1
        assert sdb['children'][0]['name'] == 'sdb1'
        assert sdb['children'][0]['mountpoint'] == '/mnt3'
Ejemplo n.º 26
0
def _test_network_config_applied_on_reboot(client: IntegrationInstance):
    log = client.read_from_file("/var/log/cloud-init.log")
    if "network config is disabled" in log:
        pytest.skip("network config disabled. Test doesn't apply")
    assert "Applying network configuration" in log
    assert "dummy0" not in client.execute("ls /sys/class/net")

    _add_dummy_bridge_to_netplan(client)
    client.execute('echo "" > /var/log/cloud-init.log')
    client.restart()

    log = client.read_from_file("/var/log/cloud-init.log")
    if "cache invalid in datasource" in log:
        # Invalid cache will get cleared, meaning we'll create a new
        # "instance" and apply networking config, so events aren't
        # really relevant here
        pytest.skip("Test only valid for existing instances")

    assert "Event Allowed: scope=network EventType=boot" in log
    assert "Applying network configuration" in log
    assert "dummy0" not in client.execute("ls /sys/class/net")
Ejemplo n.º 27
0
def test_boot_event_disabled_by_default(client: IntegrationInstance):
    log = client.read_from_file('/var/log/cloud-init.log')
    assert 'Applying network configuration' in log
    assert 'dummy0' not in client.execute('ls /sys/class/net')

    _add_dummy_bridge_to_netplan(client)
    client.execute('rm /var/log/cloud-init.log')

    client.restart()
    log2 = client.read_from_file('/var/log/cloud-init.log')

    # We attempt to apply network config twice on every boot.
    # Ensure neither time works.
    assert 2 == len(
        re.findall(r"Event Denied: scopes=\['network'\] EventType=boot[^-]",
                   log2))
    assert 2 == log2.count(
        "Event Denied: scopes=['network'] EventType=boot-legacy")
    assert 2 == log2.count("No network config applied. Neither a new instance"
                           " nor datasource network update allowed")

    assert 'dummy0' in client.execute('ls /sys/class/net')
Ejemplo n.º 28
0
def _customize_envionment(client: IntegrationInstance):
    # Assert our platform can detect LXD during systemd generator timeframe.
    ds_id_log = client.execute("cat /run/cloud-init/ds-identify.log").stdout
    assert "check for 'LXD' returned found" in ds_id_log

    if client.settings.PLATFORM == "lxd_vm":
        # ds-identify runs at systemd generator time before /dev/lxd/sock.
        # Assert we can expected artifact which indicates LXD is viable.
        result = client.execute("cat /sys/class/dmi/id/board_name")
        if not result.ok:
            raise AssertionError(
                "Missing expected /sys/class/dmi/id/board_name")
        if "LXD" != result.stdout:
            raise AssertionError(f"DMI board_name is not LXD: {result.stdout}")

    # Having multiple datasources prevents ds-identify from short-circuiting
    # detection logic with a log like:
    #     single entry in datasource_list (LXD) use that.
    # Also, NoCloud is detected during init-local timeframe.

    # If there is a race on VMs where /dev/lxd/sock is not setup in init-local
    # cloud-init will fallback to NoCloud and fail this test.
    client.write_to_file(
        "/etc/cloud/cloud.cfg.d/99-detect-lxd-first.cfg",
        "datasource_list: [LXD, NoCloud]\n",
    )
    # This is also to ensure that NoCloud can be detected
    if ImageSpecification.from_os_image().release == "jammy":
        # Add nocloud-net seed files because Jammy no longer delivers NoCloud
        # (LP: #1958460).
        client.execute("mkdir -p /var/lib/cloud/seed/nocloud-net")
        client.write_to_file("/var/lib/cloud/seed/nocloud-net/meta-data", "")
        client.write_to_file("/var/lib/cloud/seed/nocloud-net/user-data",
                             "#cloud-config\n{}")
    client.execute("cloud-init clean --logs")
    client.restart()