예제 #1
0
def test_net_api_put_update_pre_boot(test_microvm_with_api):
    """
    Test PUT updates on network configurations before the microvm boots.

    @type: functional
    """
    test_microvm = test_microvm_with_api
    test_microvm.spawn()

    first_if_name = 'first_tap'
    tap1 = net_tools.Tap(first_if_name, test_microvm.jailer.netns)
    response = test_microvm.network.put(iface_id='1',
                                        guest_mac='06:00:00:00:00:01',
                                        host_dev_name=tap1.name)
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    # Adding new network interfaces is allowed.
    second_if_name = 'second_tap'
    tap2 = net_tools.Tap(second_if_name, test_microvm.jailer.netns)
    response = test_microvm.network.put(iface_id='2',
                                        guest_mac='07:00:00:00:00:01',
                                        host_dev_name=tap2.name)
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    # Updates to a network interface with an unavailable MAC are not allowed.
    guest_mac = '06:00:00:00:00:01'
    response = test_microvm.network.put(iface_id='2',
                                        host_dev_name=second_if_name,
                                        guest_mac=guest_mac)
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert \
        "The guest MAC address {} is already in use.".format(guest_mac) \
        in response.text

    # Updates to a network interface with an available MAC are allowed.
    response = test_microvm.network.put(iface_id='2',
                                        host_dev_name=second_if_name,
                                        guest_mac='08:00:00:00:00:01')
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    # Updates to a network interface with an unavailable name are not allowed.
    response = test_microvm.network.put(iface_id='1',
                                        host_dev_name=second_if_name,
                                        guest_mac='06:00:00:00:00:01')
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert "Could not create Network Device" \
        in response.text

    # Updates to a network interface with an available name are allowed.
    iface_id = '1'
    tapname = test_microvm.id[:8] + 'tap' + iface_id

    tap3 = net_tools.Tap(tapname, test_microvm.jailer.netns)
    response = test_microvm.network.put(iface_id=iface_id,
                                        host_dev_name=tap3.name,
                                        guest_mac='06:00:00:00:00:01')
    assert test_microvm.api_session.is_status_no_content(response.status_code)
예제 #2
0
def test_api_patch_post_boot(test_microvm_with_api):
    """Test PATCH updates after the microvm boots."""
    test_microvm = test_microvm_with_api
    test_microvm.spawn()

    # Sets up the microVM with 2 vCPUs, 256 MiB of RAM, 1 network iface and
    # a root file system with the rw permission.
    test_microvm.basic_config()

    fs1 = drive_tools.FilesystemFile(
        os.path.join(test_microvm.fsfiles, 'scratch')
    )
    response = test_microvm.drive.put(
        drive_id='scratch',
        path_on_host=test_microvm.create_jailed_resource(fs1.path),
        is_root_device=False,
        is_read_only=False
    )
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    # Configure logging.
    log_fifo_path = os.path.join(test_microvm.path, 'log_fifo')
    metrics_fifo_path = os.path.join(test_microvm.path, 'metrics_fifo')
    log_fifo = log_tools.Fifo(log_fifo_path)
    metrics_fifo = log_tools.Fifo(metrics_fifo_path)

    response = test_microvm.logger.put(
        log_fifo=test_microvm.create_jailed_resource(log_fifo.path),
        metrics_fifo=test_microvm.create_jailed_resource(metrics_fifo.path)
    )
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    iface_id = '1'
    tapname = test_microvm.id[:8] + 'tap' + iface_id
    tap1 = net_tools.Tap(tapname, test_microvm.jailer.netns)
    response = test_microvm.network.put(
        iface_id=iface_id,
        host_dev_name=tap1.name,
        guest_mac='06:00:00:00:00:01'
    )
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    test_microvm.start()

    # Partial updates to the boot source are not allowed.
    response = test_microvm.boot.patch(
        kernel_image_path='otherfile'
    )
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert "Invalid request method" in response.text

    # Partial updates to the machine configuration are not allowed.
    response = test_microvm.machine_cfg.patch(vcpu_count=4)
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert "Invalid request method" in response.text

    # Partial updates to the logger configuration are not allowed.
    response = test_microvm.logger.patch(level='Error')
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert "Invalid request method" in response.text
예제 #3
0
def test_api_patch_pre_boot(test_microvm_with_api):
    """
    Test that PATCH updates are not allowed before the microvm boots.

    @type: negative
    """
    test_microvm = test_microvm_with_api
    test_microvm.spawn()

    # Sets up the microVM with 2 vCPUs, 256 MiB of RAM, 1 network interface
    # and a root file system with the rw permission.
    test_microvm.basic_config()

    fs1 = drive_tools.FilesystemFile(
        os.path.join(test_microvm.fsfiles, 'scratch'))
    drive_id = 'scratch'
    response = test_microvm.drive.put(
        drive_id=drive_id,
        path_on_host=test_microvm.create_jailed_resource(fs1.path),
        is_root_device=False,
        is_read_only=False)
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    iface_id = '1'
    tapname = test_microvm.id[:8] + 'tap' + iface_id
    tap1 = net_tools.Tap(tapname, test_microvm.jailer.netns)
    response = test_microvm.network.put(iface_id=iface_id,
                                        host_dev_name=tap1.name,
                                        guest_mac='06:00:00:00:00:01')
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    # Partial updates to the boot source are not allowed.
    response = test_microvm.boot.patch(kernel_image_path='otherfile')
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert "Invalid request method" in response.text

    # Partial updates to the machine configuration are allowed before boot.
    response = test_microvm.machine_cfg.patch(vcpu_count=4)
    assert test_microvm.api_session.is_status_no_content(response.status_code)
    response_json = test_microvm.machine_cfg.get().json()
    assert response_json['vcpu_count'] == 4

    # Partial updates to the logger configuration are not allowed.
    response = test_microvm.logger.patch(level='Error')
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert "Invalid request method" in response.text

    # Patching drive before boot is not allowed.
    response = test_microvm.drive.patch(drive_id=drive_id,
                                        path_on_host='foo.bar')
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert "The requested operation is not supported before starting the " \
           "microVM." in response.text

    # Patching net before boot is not allowed.
    response = test_microvm.network.patch(iface_id=iface_id)
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert "The requested operation is not supported before starting the " \
           "microVM." in response.text
예제 #4
0
def test_api_put_update_post_boot(test_microvm_with_api):
    """
    Test that PUT updates are rejected after the microvm boots.

    @type: negative
    """
    test_microvm = test_microvm_with_api
    test_microvm.spawn()

    # Set up the microVM with 2 vCPUs, 256 MiB of RAM  and
    # a root file system with the rw permission.
    test_microvm.basic_config()

    iface_id = '1'
    tapname = test_microvm.id[:8] + 'tap' + iface_id
    tap1 = net_tools.Tap(tapname, test_microvm.jailer.netns)
    response = test_microvm.network.put(iface_id=iface_id,
                                        host_dev_name=tap1.name,
                                        guest_mac='06:00:00:00:00:01')
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    test_microvm.start()

    expected_err = "The requested operation is not supported " \
                   "after starting the microVM"

    # Valid updates to `kernel_image_path` are not allowed after boot.
    response = test_microvm.boot.put(
        kernel_image_path=test_microvm.get_jailed_resource(
            test_microvm.kernel_file))
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert expected_err in response.text

    # Valid updates to the machine configuration are not allowed after boot.
    response = test_microvm.machine_cfg.patch(vcpu_count=4)
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert expected_err in response.text

    response = test_microvm.machine_cfg.put(vcpu_count=4,
                                            ht_enabled=False,
                                            mem_size_mib=128)
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert expected_err in response.text

    # Network interface update is not allowed after boot.
    response = test_microvm.network.put(iface_id='1',
                                        host_dev_name=tap1.name,
                                        guest_mac='06:00:00:00:00:02')
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert expected_err in response.text

    # Block device update is not allowed after boot.
    response = test_microvm.drive.put(
        drive_id='rootfs',
        path_on_host=test_microvm.jailer.jailed_path(test_microvm.rootfs_file),
        is_read_only=False,
        is_root_device=True)
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert expected_err in response.text
예제 #5
0
 def create_tap_and_ssh_config(self,
                               host_ip,
                               guest_ip,
                               netmask_len,
                               tapname=None):
     """Create tap device and configure ssh."""
     assert tapname is not None
     tap = net_tools.Tap(tapname,
                         self._jailer.netns,
                         ip="{}/{}".format(host_ip, netmask_len))
     self.config_ssh(guest_ip)
     return tap
예제 #6
0
    def ssh_network_config(
            self,
            network_config,
            iface_id,
            allow_mmds_requests=False,
            tx_rate_limiter=None,
            rx_rate_limiter=None
    ):
        """Create a host tap device and a guest network interface.

        'network_config' is used to generate 2 IPs: one for the tap device
        and one for the microvm. Adds the hostname of the microvm to the
        ssh_config dictionary.
        :param network_config: UniqueIPv4Generator instance
        :param iface_id: the interface id for the API request
        :param allow_mmds_requests: specifies whether requests sent from
        the guest on this interface towards the MMDS address are
        intercepted and processed by the device model.
        :param tx_rate_limiter: limit the tx rate
        :param rx_rate_limiter: limit the rx rate
        :return: an instance of the tap which needs to be kept around until
        cleanup is desired, the configured guest and host ips, respectively.
        """
        # Create tap before configuring interface.
        tapname = self.id[:8] + 'tap' + iface_id
        (host_ip, guest_ip) = network_config.get_next_available_ips(2)
        tap = net_tools.Tap(
            tapname,
            self._jailer.netns,
            ip="{}/{}".format(
                host_ip,
                network_config.get_netmask_len()
            )
        )
        guest_mac = net_tools.mac_from_ip(guest_ip)

        response = self.network.put(
            iface_id=iface_id,
            host_dev_name=tapname,
            guest_mac=guest_mac,
            allow_mmds_requests=allow_mmds_requests,
            tx_rate_limiter=tx_rate_limiter,
            rx_rate_limiter=rx_rate_limiter
        )
        assert self._api_session.is_status_no_content(response.status_code)

        self.ssh_config['hostname'] = guest_ip
        return tap, host_ip, guest_ip
예제 #7
0
def test_rate_limiters_api_config(test_microvm_with_api):
    """Test the Firecracker IO rate limiter API."""
    test_microvm = test_microvm_with_api
    test_microvm.spawn()

    # Test the DRIVE rate limiting API.

    # Test drive with bw rate-limiting.
    fs1 = drive_tools.FilesystemFile(os.path.join(test_microvm.fsfiles, 'bw'))
    response = test_microvm.drive.put(
        drive_id='bw',
        path_on_host=test_microvm.create_jailed_resource(fs1.path),
        is_read_only=False,
        is_root_device=False,
        rate_limiter={
            'bandwidth': {
                'size': 1000000,
                'refill_time': 100
            }
        }
    )
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    # Test drive with ops rate-limiting.
    fs2 = drive_tools.FilesystemFile(os.path.join(test_microvm.fsfiles, 'ops'))
    response = test_microvm.drive.put(
        drive_id='ops',
        path_on_host=test_microvm.create_jailed_resource(fs2.path),
        is_read_only=False,
        is_root_device=False,
        rate_limiter={
            'ops': {
                'size': 1,
                'refill_time': 100
            }
        }
    )
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    # Test drive with bw and ops rate-limiting.
    fs3 = drive_tools.FilesystemFile(
        os.path.join(test_microvm.fsfiles, 'bwops')
    )
    response = test_microvm.drive.put(
        drive_id='bwops',
        path_on_host=test_microvm.create_jailed_resource(fs3.path),
        is_read_only=False,
        is_root_device=False,
        rate_limiter={
            'bandwidth': {
                'size': 1000000,
                'refill_time': 100
            },
            'ops': {
                'size': 1,
                'refill_time': 100
            }
        }
    )
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    # Test drive with 'empty' rate-limiting (same as not specifying the field)
    fs4 = drive_tools.FilesystemFile(os.path.join(
        test_microvm.fsfiles, 'nada'
    ))
    response = test_microvm.drive.put(
        drive_id='nada',
        path_on_host=test_microvm.create_jailed_resource(fs4.path),
        is_read_only=False,
        is_root_device=False,
        rate_limiter={}
    )
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    # Test the NET rate limiting API.

    # Test network with tx bw rate-limiting.
    iface_id = '1'
    tapname = test_microvm.id[:8] + 'tap' + iface_id
    tap1 = net_tools.Tap(tapname, test_microvm.jailer.netns)

    response = test_microvm.network.put(
        iface_id=iface_id,
        guest_mac='06:00:00:00:00:01',
        host_dev_name=tap1.name,
        tx_rate_limiter={
            'bandwidth': {
                'size': 1000000,
                'refill_time': 100
            }
        }
    )
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    # Test network with rx bw rate-limiting.
    iface_id = '2'
    tapname = test_microvm.id[:8] + 'tap' + iface_id
    tap2 = net_tools.Tap(tapname, test_microvm.jailer.netns)
    response = test_microvm.network.put(
        iface_id=iface_id,
        guest_mac='06:00:00:00:00:02',
        host_dev_name=tap2.name,
        rx_rate_limiter={
            'bandwidth': {
                'size': 1000000,
                'refill_time': 100
            }
        }
    )
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    # Test network with tx and rx bw and ops rate-limiting.
    iface_id = '3'
    tapname = test_microvm.id[:8] + 'tap' + iface_id
    tap3 = net_tools.Tap(tapname, test_microvm.jailer.netns)
    response = test_microvm.network.put(
        iface_id=iface_id,
        guest_mac='06:00:00:00:00:03',
        host_dev_name=tap3.name,
        rx_rate_limiter={
            'bandwidth': {
                'size': 1000000,
                'refill_time': 100
            },
            'ops': {
                'size': 1,
                'refill_time': 100
            }
        },
        tx_rate_limiter={
            'bandwidth': {
                'size': 1000000,
                'refill_time': 100
            },
            'ops': {
                'size': 1,
                'refill_time': 100
            }
        }
    )
    assert test_microvm.api_session.is_status_no_content(response.status_code)
예제 #8
0
def test_get_full_config(test_microvm_with_api):
    """
    Test the reported configuration of a microVM configured with all resources.

    @type: functional
    """
    test_microvm = test_microvm_with_api

    expected_cfg = {}

    test_microvm.spawn()
    # Basic config also implies a root block device.
    test_microvm.basic_config()
    expected_cfg['machine-config'] = {
        'vcpu_count': 2,
        'mem_size_mib': 256,
        'ht_enabled': False,
        'track_dirty_pages': False
    }
    expected_cfg['boot-source'] = {
        'kernel_image_path': '/vmlinux.bin',
        'initrd_path': None
    }
    expected_cfg['drives'] = [{
        'drive_id': 'rootfs',
        'path_on_host': '/bionic.rootfs.ext4',
        'is_root_device': True,
        'partuuid': None,
        'is_read_only': False,
        'cache_type': 'Unsafe',
        'rate_limiter': None
    }]

    # Add a memory balloon device.
    response = test_microvm.balloon.put(amount_mib=1, deflate_on_oom=True)
    assert test_microvm.api_session.is_status_no_content(response.status_code)
    expected_cfg['balloon'] = {
        'amount_mib': 1,
        'deflate_on_oom': True,
        'stats_polling_interval_s': 0
    }

    # Add a vsock device.
    response = test_microvm.vsock.put(guest_cid=15, uds_path='vsock.sock')
    assert test_microvm.api_session.is_status_no_content(response.status_code)
    expected_cfg['vsock'] = {'guest_cid': 15, 'uds_path': 'vsock.sock'}

    # Add a net device.
    iface_id = '1'
    tapname = test_microvm.id[:8] + 'tap' + iface_id
    tap1 = net_tools.Tap(tapname, test_microvm.jailer.netns)
    guest_mac = '06:00:00:00:00:01'
    tx_rl = {
        'bandwidth': {
            'size': 1000000,
            'refill_time': 100,
            'one_time_burst': None
        },
        'ops': None
    }
    response = test_microvm.network.put(iface_id=iface_id,
                                        guest_mac=guest_mac,
                                        host_dev_name=tap1.name,
                                        tx_rate_limiter=tx_rl)
    assert test_microvm.api_session.is_status_no_content(response.status_code)
    expected_cfg['network-interfaces'] = [{
        'iface_id': iface_id,
        'host_dev_name': tap1.name,
        'guest_mac': '06:00:00:00:00:01',
        'rx_rate_limiter': None,
        'tx_rate_limiter': tx_rl,
        'allow_mmds_requests': False
    }]

    expected_cfg['logger'] = None
    expected_cfg['metrics'] = None
    expected_cfg['mmds-config'] = None

    # Getting full vm configuration should be available pre-boot.
    response = test_microvm.full_cfg.get()
    assert test_microvm.api_session.is_status_ok(response.status_code)
    assert response.json() == expected_cfg

    # Start the microvm.
    test_microvm.start()

    # Validate full vm configuration post-boot as well.
    response = test_microvm.full_cfg.get()
    assert test_microvm.api_session.is_status_ok(response.status_code)
    assert response.json() == expected_cfg