def test_security(self, class_client: IntegrationInstance):
        """Test apt default security sources.

        Ported from
        tests/cloud_tests/testcases/modules/apt_configure_security.py
        """
        sources_list = class_client.read_from_file("/etc/apt/sources.list")

        # 3 lines from main, universe, and multiverse
        release = ImageSpecification.from_os_image().release
        sec_url = f"deb http://security.ubuntu.com/ubuntu {release}-security"
        if class_client.settings.PLATFORM == "azure":
            sec_url = (
                f"deb http://azure.archive.ubuntu.com/ubuntu/"
                f" {release}-security"
            )
        sec_src_url = sec_url.replace("deb ", "# deb-src ")
        assert 3 == sources_list.count(sec_url)
        assert 3 == sources_list.count(sec_src_url)
示例#2
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')
def _detect_reboot(instance: IntegrationInstance):
    # We'll wait for instance up here, but we don't know if we're
    # detecting the first boot or second boot, so we also check
    # the logs to ensure we've booted twice. If the logs show we've
    # only booted once, wait until we've booted twice
    instance.instance.wait()
    for _ in range(600):
        try:
            log = instance.read_from_file("/var/log/cloud-init.log")
            boot_count = log.count("running 'init-local'")
            if boot_count == 1:
                instance.instance.wait()
            elif boot_count > 1:
                break
        except Exception:
            pass
        time.sleep(1)
    else:
        raise Exception("Could not detect reboot")
    def test_ppa_source(self, class_client: IntegrationInstance):
        """Test the apt ppa functionality.

        Ported from
        tests/cloud_tests/testcases/modules/apt_configure_sources_ppa.py
        """
        release = ImageSpecification.from_os_image().release
        ppa_path_contents = class_client.read_from_file(
            "/etc/apt/sources.list.d/"
            "simplestreams-dev-ubuntu-trunk-{}.list".format(release)
        )
        assert (
            "://ppa.launchpad.net/simplestreams-dev/trunk/ubuntu"
            in ppa_path_contents
            or "://ppa.launchpadcontent.net/simplestreams-dev/trunk/ubuntu"
            in ppa_path_contents
        )

        assert TEST_PPA_KEY in self.get_keys(class_client)
示例#5
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')
示例#6
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",
            r"SUCCESS: found local data from DataSource(NoCloud|LXD)",
        ],
        log,
    )
def test_per_freq(client: IntegrationInstance):
    # Sanity test for scripts folder
    cmd = "test -d /var/lib/cloud/scripts"
    assert client.execute(cmd).ok
    # Test per-boot
    cmd = "test -f /var/lib/cloud/scripts/per-boot/always.sh"
    assert client.execute(cmd).ok
    cmd = "test -f /tmp/test_per_freq_always"
    assert client.execute(cmd).ok
    # Test per-instance
    cmd = "test -f /var/lib/cloud/scripts/per-instance/instance.sh"
    assert client.execute(cmd).ok
    cmd = "test -f /tmp/test_per_freq_instance"
    assert client.execute(cmd).ok
    # Test per-once
    cmd = "test -f /var/lib/cloud/scripts/per-once/once.sh"
    assert client.execute(cmd).ok
    cmd = "test -f /tmp/test_per_freq_once"
    assert client.execute(cmd).ok
示例#8
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")
示例#9
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"]
 def test_ntp_installed(self, class_client: IntegrationInstance):
     """Test that `ntpd --version` succeeds, indicating installation."""
     assert class_client.execute("ntpd --version").ok
 def test_ntpq_servers(self, class_client: IntegrationInstance):
     result = class_client.execute("ntpq -p -w -n")
     assert result.ok
     for expected_server_or_pool in [*EXPECTED_SERVERS, *EXPECTED_POOLS]:
         assert expected_server_or_pool in result.stdout
示例#12
0
 def test_bad_key(self, class_client: IntegrationInstance):
     """Test the apt signed-by functionality.
     """
     with pytest.raises(OSError):
         class_client.read_from_file(
             '/etc/apt/trusted.list.d/test_bad_key.gpg')
示例#13
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()
 def test_poweroff_false_condition(self, client: IntegrationInstance):
     log = client.read_from_file("/var/log/cloud-init.log")
     assert _can_connect(client)
     assert "Condition was false. Will not perform state change" in log
示例#15
0
def test_chef_license(client: IntegrationInstance):
    log = client.read_from_file('/var/log/cloud-init.log')
    assert 'Traceback' not in log
示例#16
0
def test_lxd_datasource_discovery(client: IntegrationInstance):
    """Test that DataSourceLXD is detected instead of NoCloud."""

    _customize_envionment(client)
    result = client.execute("cloud-init status --wait --long")
    if not result.ok:
        raise AssertionError("cloud-init failed:\n%s", result.stderr)
    if "DataSourceLXD" not in result.stdout:
        raise AssertionError("cloud-init did not discover DataSourceLXD",
                             result.stdout)
    netplan_yaml = client.execute("cat /etc/netplan/50-cloud-init.yaml")
    netplan_cfg = yaml.safe_load(netplan_yaml)

    platform = client.settings.PLATFORM
    nic_dev = "eth0" if platform == "lxd_container" else "enp5s0"
    assert {
        "network": {
            "ethernets": {
                nic_dev: {
                    "dhcp4": True
                }
            },
            "version": 2
        }
    } == netplan_cfg
    log = client.read_from_file("/var/log/cloud-init.log")
    verify_clean_log(log)
    result = client.execute("cloud-id")
    if result.stdout != "lxd":
        raise AssertionError("cloud-id didn't report lxd. Result: %s",
                             result.stdout)
    # Validate config instance data represented
    data = json.loads(
        client.read_from_file("/run/cloud-init/instance-data.json"))
    v1 = data["v1"]
    ds_cfg = data["ds"]
    assert "lxd" == v1["platform"]
    assert "LXD socket API v. 1.0 (/dev/lxd/sock)" == v1["subplatform"]
    ds_cfg = json.loads(client.execute("cloud-init query ds").stdout)
    assert ["_doc", "_metadata_api_version", "config",
            "meta-data"] == sorted(list(ds_cfg.keys()))
    if (client.settings.PLATFORM == "lxd_vm"
            and ImageSpecification.from_os_image().release == "bionic"):
        # pycloudlib injects user.vendor_data for lxd_vm on bionic
        # to start the lxd-agent.
        # https://github.com/canonical/pycloudlib/blob/main/pycloudlib/\
        #    lxd/defaults.py#L13-L27
        # Underscore-delimited aliases exist for any keys containing hyphens or
        # dots.
        lxd_config_keys = ["user.meta-data", "user.vendor-data"]
    else:
        lxd_config_keys = ["user.meta-data"]
    assert "1.0" == ds_cfg["_metadata_api_version"]
    assert lxd_config_keys == list(ds_cfg["config"].keys())
    assert {
        "public-keys": v1["public_ssh_keys"][0]
    } == (yaml.safe_load(ds_cfg["config"]["user.meta-data"]))
    assert "#cloud-config\ninstance-id" in ds_cfg["meta-data"]
    # Assert NoCloud seed data is still present in cloud image metadata
    # This will start failing if we redact metadata templates from
    # https://cloud-images.ubuntu.com/daily/server/jammy/current/\
    #    jammy-server-cloudimg-amd64-lxd.tar.xz
    nocloud_metadata = yaml.safe_load(
        client.read_from_file("/var/lib/cloud/seed/nocloud-net/meta-data"))
    assert client.instance.name == nocloud_metadata["instance-id"]
    assert (
        nocloud_metadata["instance-id"] == nocloud_metadata["local-hostname"])
    assert v1["public_ssh_keys"][0] == nocloud_metadata["public-keys"]
示例#17
0
def test_static_route_to_host(client: IntegrationInstance):
    route = client.execute("ip route | grep {}".format(DESTINATION_IP))
    assert route.startswith(EXPECTED_ROUTE)
示例#18
0
def test_timesyncd(client: IntegrationInstance):
    contents = client.read_from_file(
        '/etc/systemd/timesyncd.conf.d/cloud-init.conf'
    )
    assert '.pool.ntp.org' in contents
示例#19
0
def _remove_nocloud_dir_and_reboot(client: IntegrationInstance):
    # On Impish and below, NoCloud will be detected on an LXD container.
    # If we remove this directory, it will no longer be detected.
    client.execute("rm -rf /var/lib/cloud/seed/nocloud-net")
    client.execute("cloud-init clean --logs --reboot")
def test_puppet_service(client: IntegrationInstance):
    """Basic test that puppet gets installed and runs."""
    log = client.read_from_file("/var/log/cloud-init.log")
    verify_clean_log(log)
    assert client.execute("systemctl is-active puppet").ok
    assert "Running command ['puppet', 'agent'" not in log
示例#21
0
 def get_instance(self, cloud_instance, settings=integration_settings):
     return IntegrationInstance(self, cloud_instance, settings)
def test_pupet_exec(client: IntegrationInstance):
    """Basic test that puppet gets installed and runs."""
    log = client.read_from_file("/var/log/cloud-init.log")
    assert "Running command ['puppet', 'agent', '--noop']" in log
示例#23
0
def test_chef_license(client: IntegrationInstance):
    log = client.read_from_file("/var/log/cloud-init.log")
    verify_clean_log(log)
def test_wakeonlan(client: IntegrationInstance):
    netplan_cfg = client.execute("cat /etc/netplan/50-cloud-init.yaml")
    netplan_yaml = yaml.safe_load(netplan_cfg)
    assert "wakeonlan" in netplan_yaml["network"]["ethernets"]["eth0"]
    assert netplan_yaml["network"]["ethernets"]["eth0"]["wakeonlan"] is True
 def test_cloud_id_file_symlink(self, class_client: IntegrationInstance):
     cloud_id = class_client.execute("cloud-id").stdout
     expected_link_output = ("'/run/cloud-init/cloud-id' -> "
                             f"'/run/cloud-init/cloud-id-{cloud_id}'")
     assert expected_link_output == str(
         class_client.execute("stat -c %N /run/cloud-init/cloud-id"))
def test_timesyncd(client: IntegrationInstance):
    contents = client.read_from_file(
        "/etc/systemd/timesyncd.conf.d/cloud-init.conf")
    assert "NTP=172.16.15.14" in contents
示例#27
0
def test_runcmd(client: IntegrationInstance):
    log = client.read_from_file('/var/log/cloud-init-test-output')
    assert 'should be last line in cloud-init-test-output file' in log
def test_empty_ntp(client: IntegrationInstance):
    assert client.execute("ntpd --version").ok
    assert client.execute("test -f /etc/ntp.conf.dist").failed
    assert "pool.ntp.org iburst" in client.execute(
        'grep -v "^#" /etc/ntp.conf')