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)
Exemple #2
0
def test_oci_networking_iscsi_instance(client: IntegrationInstance, tmpdir):
    customize_environment(client, tmpdir, configure_secondary_nics=False)
    result_net_files = client.execute("ls /run/net-*.conf")
    assert result_net_files.ok, "No net files found under /run"

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

    assert ("opc/v2/vnics/"
            not in log), "vnic data was fetched and it should not have been"

    netplan_yaml = client.read_from_file("/etc/netplan/50-cloud-init.yaml")
    netplan_cfg = yaml.safe_load(netplan_yaml)
    configured_interfaces = extract_interface_names(netplan_cfg["network"])
    assert 1 <= len(configured_interfaces
                    ), "Expected at least 1 primary network configuration."

    expected_interfaces = set(
        re.findall(r"/run/net-(.+)\.conf", result_net_files.stdout))
    for expected_interface in expected_interfaces:
        assert (f"Reading from /run/net-{expected_interface}.conf"
                in log), "Expected {expected_interface} not found in: {log}"

    not_found_interfaces = expected_interfaces.difference(
        configured_interfaces)
    assert not not_found_interfaces, (
        f"Interfaces, {not_found_interfaces}, expected to be configured in"
        f" {netplan_cfg['network']}")
    assert client.execute("ping -c 2 canonical.com").ok
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
Exemple #4
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,
        ]
    )
Exemple #5
0
def test_hotplug_add_remove(client: IntegrationInstance):
    ips_before = _get_ip_addr(client)
    log = client.read_from_file('/var/log/cloud-init.log')
    assert 'Exiting hotplug handler' not in log

    # Add new NIC
    added_ip = client.instance.add_network_interface()
    _wait_till_hotplug_complete(client)
    ips_after_add = _get_ip_addr(client)
    new_addition = [ip for ip in ips_after_add if ip.ip4 == added_ip][0]

    assert len(ips_after_add) == len(ips_before) + 1
    assert added_ip not in [ip.ip4 for ip in ips_before]
    assert added_ip in [ip.ip4 for ip in ips_after_add]
    assert new_addition.state == 'UP'

    netplan_cfg = client.read_from_file('/etc/netplan/50-cloud-init.yaml')
    config = yaml.safe_load(netplan_cfg)
    assert new_addition.interface in config['network']['ethernets']

    # Remove new NIC
    client.instance.remove_network_interface(added_ip)
    _wait_till_hotplug_complete(client, expected_runs=2)
    ips_after_remove = _get_ip_addr(client)
    assert len(ips_after_remove) == len(ips_before)
    assert added_ip not in [ip.ip4 for ip in ips_after_remove]

    netplan_cfg = client.read_from_file('/etc/netplan/50-cloud-init.yaml')
    config = yaml.safe_load(netplan_cfg)
    assert new_addition.interface not in config['network']['ethernets']
Exemple #6
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")
def test_hotplug_add_remove(client: IntegrationInstance):
    ips_before = _get_ip_addr(client)
    log = client.read_from_file("/var/log/cloud-init.log")
    assert "Exiting hotplug handler" not in log
    assert client.execute(
        "test -f /etc/udev/rules.d/10-cloud-init-hook-hotplug.rules").ok

    # Add new NIC
    added_ip = client.instance.add_network_interface()
    _wait_till_hotplug_complete(client, expected_runs=1)
    ips_after_add = _get_ip_addr(client)
    new_addition = [ip for ip in ips_after_add if ip.ip4 == added_ip][0]

    assert len(ips_after_add) == len(ips_before) + 1
    assert added_ip not in [ip.ip4 for ip in ips_before]
    assert added_ip in [ip.ip4 for ip in ips_after_add]
    assert new_addition.state == "UP"

    netplan_cfg = client.read_from_file("/etc/netplan/50-cloud-init.yaml")
    config = yaml.safe_load(netplan_cfg)
    assert new_addition.interface in config["network"]["ethernets"]

    # Remove new NIC
    client.instance.remove_network_interface(added_ip)
    _wait_till_hotplug_complete(client, expected_runs=2)
    ips_after_remove = _get_ip_addr(client)
    assert len(ips_after_remove) == len(ips_before)
    assert added_ip not in [ip.ip4 for ip in ips_after_remove]

    netplan_cfg = client.read_from_file("/etc/netplan/50-cloud-init.yaml")
    config = yaml.safe_load(netplan_cfg)
    assert new_addition.interface not in config["network"]["ethernets"]

    assert "enabled" == client.execute(
        "cloud-init devel hotplug-hook -s net query")
Exemple #8
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')
Exemple #9
0
def test_chrony(client: IntegrationInstance):
    if client.execute('test -f /etc/chrony.conf').ok:
        chrony_conf = '/etc/chrony.conf'
    else:
        chrony_conf = '/etc/chrony/chrony.conf'
    contents = client.read_from_file(chrony_conf)
    assert '.pool.ntp.org' in contents
def test_chrony(client: IntegrationInstance):
    if client.execute('test -f /etc/chrony.conf').ok:
        chrony_conf = '/etc/chrony.conf'
    else:
        chrony_conf = '/etc/chrony/chrony.conf'
    contents = client.read_from_file(chrony_conf)
    assert 'server 172.16.15.14' in contents
Exemple #11
0
    def test_device_alias(self, create_disk, client: IntegrationInstance):
        log = client.read_from_file("/var/log/cloud-init.log")
        assert ("updated disk_setup device entry 'my_alias' to '/dev/sdb'"
                in log)
        assert "changed my_alias.1 => /dev/sdb1" in log
        assert "changed my_alias.2 => /dev/sdb2" in log
        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"]) == 2
        assert sdb["children"][0]["name"] == "sdb1"
        assert sdb["children"][1]["name"] == "sdb2"
        if "mountpoint" in sdb["children"][0]:
            assert sdb["children"][0]["mountpoint"] == "/mnt1"
            assert sdb["children"][1]["mountpoint"] == "/mnt2"
        else:
            assert sdb["children"][0]["mountpoints"] == ["/mnt1"]
            assert sdb["children"][1]["mountpoints"] == ["/mnt2"]
        result = client.execute("mount -a")
        assert result.return_code == 0
        assert result.stdout.strip() == ""
        assert result.stderr.strip() == ""
        result = client.execute("findmnt -J /mnt1")
        assert result.return_code == 0
        result = client.execute("findmnt -J /mnt2")
        assert result.return_code == 0
Exemple #12
0
def test_apt_proxy(client: IntegrationInstance):
    """Test the apt proxy data gets written correctly."""
    out = client.read_from_file('/etc/apt/apt.conf.d/90cloud-init-aptproxy')
    assert 'Acquire::http::Proxy "http://proxy.internal:3128";' in out
    assert 'Acquire::http::Proxy "http://squid.internal:3128";' in out
    assert 'Acquire::ftp::Proxy "ftp://squid.internal:3128";' in out
    assert 'Acquire::https::Proxy "https://squid.internal:3128";' in out
def test_chrony(client: IntegrationInstance):
    if client.execute("test -f /etc/chrony.conf").ok:
        chrony_conf = "/etc/chrony.conf"
    else:
        chrony_conf = "/etc/chrony/chrony.conf"
    contents = client.read_from_file(chrony_conf)
    assert "server 172.16.15.14" in contents
Exemple #14
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
 def test_ntp_entries(self, class_client: IntegrationInstance):
     ntp_conf = class_client.read_from_file("/etc/ntp.conf")
     for expected_server in EXPECTED_SERVERS:
         assert re.search(r"^server {} iburst".format(expected_server),
                          ntp_conf, re.MULTILINE)
     for expected_pool in EXPECTED_POOLS:
         assert re.search(r"^pool {} iburst".format(expected_pool),
                          ntp_conf, re.MULTILINE)
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
Exemple #17
0
    def test_primary_on_openstack(self, class_client: IntegrationInstance):
        """Test apt default primary source on openstack.

        When no uri is provided.
        """
        zone = class_client.execute('cloud-init query v1.availability_zone')
        sources_list = class_client.read_from_file('/etc/apt/sources.list')
        assert '{}.clouds.archive.ubuntu.com'.format(zone) in sources_list
Exemple #18
0
    def test_primary(self, class_client: IntegrationInstance):
        """Test apt default primary sources.

        Ported from
        tests/cloud_tests/testcases/modules/apt_configure_primary.py
        """
        sources_list = class_client.read_from_file('/etc/apt/sources.list')
        assert 'deb http://archive.ubuntu.com/ubuntu' in sources_list
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
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
Exemple #21
0
def test_gpg_no_tty(client: IntegrationInstance):
    log = client.read_from_file('/var/log/cloud-init.log')
    to_verify = [
        "Running command ['gpg', '--no-tty', "
        "'--keyserver=keyserver.ubuntu.com', '--recv-keys', 'E4D304DF'] "
        "with allowed return codes [0] (shell=False, capture=True)",
        "Imported key 'E4D304DF' from keyserver 'keyserver.ubuntu.com'",
    ]
    verify_ordered_items_in_text(to_verify, log)
Exemple #22
0
    def test_disable_apt_pipelining(self, class_client: IntegrationInstance):
        """Test disabling of apt pipelining.

        Ported from
        tests/cloud_tests/testcases/modules/apt_pipelining_disable.py
        """
        conf = class_client.read_from_file(
            '/etc/apt/apt.conf.d/90cloud-init-pipelining')
        assert 'Acquire::http::Pipeline-Depth "0";' in conf
Exemple #23
0
    def test_apt_conf(self, class_client: IntegrationInstance):
        """Test the apt conf functionality.

        Ported from tests/cloud_tests/testcases/modules/apt_configure_conf.py
        """
        apt_config = class_client.read_from_file(
            '/etc/apt/apt.conf.d/94cloud-init-config')
        assert 'Assume-Yes "true";' in apt_config
        assert 'Fix-Broken "true";' in apt_config
Exemple #24
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)
Exemple #25
0
def test_no_hotplug_in_userdata(client: IntegrationInstance):
    ips_before = _get_ip_addr(client)
    log = client.read_from_file('/var/log/cloud-init.log')
    assert 'Exiting hotplug handler' not in log

    # Add new NIC
    client.instance.add_network_interface()
    _wait_till_hotplug_complete(client)
    log = client.read_from_file('/var/log/cloud-init.log')
    assert 'hotplug not enabled for event of type network' in log

    ips_after_add = _get_ip_addr(client)
    if len(ips_after_add) == len(ips_before) + 1:
        # We can see the device, but it should not have been brought up
        new_ip = [ip for ip in ips_after_add if ip not in ips_before][0]
        assert new_ip.state == 'DOWN'
    else:
        assert len(ips_after_add) == len(ips_before)
Exemple #26
0
def test_network_disabled_via_etc_cloud(client: IntegrationInstance):
    """Test that network can be disabled via config file in /etc/cloud"""
    if client.settings.CLOUD_INIT_SOURCE == "IN_PLACE":
        pytest.skip(
            "IN_PLACE not supported as we mount /etc/cloud contents into the "
            "container")
    _customize_envionment(client)

    log = client.read_from_file("/var/log/cloud-init.log")
    assert "network config is disabled by system_cfg" in log
Exemple #27
0
def test_default_primary_with_uri(client: IntegrationInstance):
    """Test apt default primary sources.

    Ported from
    tests/cloud_tests/testcases/modules/apt_configure_primary.py
    """
    sources_list = client.read_from_file('/etc/apt/sources.list')
    assert 'archive.ubuntu.com' not in sources_list

    assert 'something.random.invalid' in sources_list
Exemple #28
0
    def test_apt_proxy(self, class_client: IntegrationInstance):
        """Test the apt proxy functionality.

        Ported from tests/cloud_tests/testcases/modules/apt_configure_proxy.py
        """
        out = class_client.read_from_file(
            '/etc/apt/apt.conf.d/90cloud-init-aptproxy')
        assert 'Acquire::http::Proxy "http://proxy.internal:3128";' in out
        assert 'Acquire::http::Proxy "http://squid.internal:3128";' in out
        assert 'Acquire::ftp::Proxy "ftp://squid.internal:3128";' in out
        assert 'Acquire::https::Proxy "https://squid.internal:3128";' in out
Exemple #29
0
    def test_grow_part(self, client: IntegrationInstance):
        """Verify """
        log = client.read_from_file('/var/log/cloud-init.log')
        assert ("cc_growpart.py[INFO]: '/dev/sdb1' resized:"
                " changed (/dev/sdb, 1) from") 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['size'] == '16M'
    def test_dist_config_file_is_empty(self,
                                       class_client: IntegrationInstance):
        """Test that the distributed config file is empty.

        (This test is skipped on all currently supported Ubuntu releases, so
        may not actually be needed any longer.)
        """
        if class_client.execute("test -e /etc/ntp.conf.dist").failed:
            pytest.skip("/etc/ntp.conf.dist does not exist")
        dist_file = class_client.read_from_file("/etc/ntp.conf.dist")
        assert 0 == len(dist_file.strip().splitlines())