xref: /freebsd/tests/examples/test_examples.py (revision 8161b823d77f9d89ffabd47444a83d693f74c515)
1*8161b823SAlexander V. Chernikovimport pytest
2*8161b823SAlexander V. Chernikovfrom atf_python.utils import BaseTest
3*8161b823SAlexander V. Chernikovfrom atf_python.sys.net.tools import ToolsHelper
4*8161b823SAlexander V. Chernikovfrom atf_python.sys.net.vnet import SingleVnetTestTemplate
5*8161b823SAlexander V. Chernikovfrom atf_python.sys.net.vnet import VnetTestTemplate
6*8161b823SAlexander V. Chernikovfrom atf_python.sys.net.vnet import VnetInstance
7*8161b823SAlexander V. Chernikov
8*8161b823SAlexander V. Chernikovimport errno
9*8161b823SAlexander V. Chernikovimport socket
10*8161b823SAlexander V. Chernikovimport subprocess
11*8161b823SAlexander V. Chernikovimport json
12*8161b823SAlexander V. Chernikov
13*8161b823SAlexander V. Chernikovfrom typing import List
14*8161b823SAlexander V. Chernikov
15*8161b823SAlexander V. Chernikov
16*8161b823SAlexander V. Chernikov# Test classes should be inherited
17*8161b823SAlexander V. Chernikov# from the BaseTest
18*8161b823SAlexander V. Chernikov
19*8161b823SAlexander V. Chernikov
20*8161b823SAlexander V. Chernikovclass TestExampleSimplest(BaseTest):
21*8161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
22*8161b823SAlexander V. Chernikov    def test_one(self):
23*8161b823SAlexander V. Chernikov        assert ToolsHelper.get_output("uname -s").strip() == "FreeBSD"
24*8161b823SAlexander V. Chernikov
25*8161b823SAlexander V. Chernikov
26*8161b823SAlexander V. Chernikovclass TestExampleSimple(BaseTest):
27*8161b823SAlexander V. Chernikov    # List of required kernel modules (kldstat -v)
28*8161b823SAlexander V. Chernikov    # that needs to be present for the tests to run
29*8161b823SAlexander V. Chernikov    REQUIRED_MODULES = ["null"]
30*8161b823SAlexander V. Chernikov
31*8161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
32*8161b823SAlexander V. Chernikov    def test_one(self):
33*8161b823SAlexander V. Chernikov        """Optional test description
34*8161b823SAlexander V. Chernikov        This and the following lines are not propagated
35*8161b823SAlexander V. Chernikov        to the ATF test description.
36*8161b823SAlexander V. Chernikov        """
37*8161b823SAlexander V. Chernikov        pass
38*8161b823SAlexander V. Chernikov
39*8161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
40*8161b823SAlexander V. Chernikov    # List of all requirements supported by an atf runner
41*8161b823SAlexander V. Chernikov    # See atf-test-case(4) for the detailed description
42*8161b823SAlexander V. Chernikov    @pytest.mark.require_user("root")
43*8161b823SAlexander V. Chernikov    @pytest.mark.require_arch(["amd64", "i386"])
44*8161b823SAlexander V. Chernikov    @pytest.mark.require_files(["/path/file1", "/path/file2"])
45*8161b823SAlexander V. Chernikov    @pytest.mark.require_machine(["amd64", "i386"])
46*8161b823SAlexander V. Chernikov    @pytest.mark.require_memory("200M")
47*8161b823SAlexander V. Chernikov    @pytest.mark.require_progs(["prog1", "prog2"])
48*8161b823SAlexander V. Chernikov    @pytest.mark.timeout(300)
49*8161b823SAlexander V. Chernikov    def test_two(self):
50*8161b823SAlexander V. Chernikov        pass
51*8161b823SAlexander V. Chernikov
52*8161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
53*8161b823SAlexander V. Chernikov    @pytest.mark.require_user("unprivileged")
54*8161b823SAlexander V. Chernikov    def test_syscall_failure(self):
55*8161b823SAlexander V. Chernikov        s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
56*8161b823SAlexander V. Chernikov        with pytest.raises(OSError) as exc_info:
57*8161b823SAlexander V. Chernikov            s.bind(("::1", 42))
58*8161b823SAlexander V. Chernikov        assert exc_info.value.errno == errno.EACCES
59*8161b823SAlexander V. Chernikov
60*8161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
61*8161b823SAlexander V. Chernikov    @pytest.mark.parametrize(
62*8161b823SAlexander V. Chernikov        "family_tuple",
63*8161b823SAlexander V. Chernikov        [
64*8161b823SAlexander V. Chernikov            pytest.param([socket.AF_INET, None], id="AF_INET"),
65*8161b823SAlexander V. Chernikov            pytest.param([socket.AF_INET6, None], id="AF_INET6"),
66*8161b823SAlexander V. Chernikov            pytest.param([39, errno.EAFNOSUPPORT], id="FAMILY_39"),
67*8161b823SAlexander V. Chernikov        ],
68*8161b823SAlexander V. Chernikov    )
69*8161b823SAlexander V. Chernikov    def test_parametrize(self, family_tuple):
70*8161b823SAlexander V. Chernikov        family, error = family_tuple
71*8161b823SAlexander V. Chernikov        try:
72*8161b823SAlexander V. Chernikov            s = socket.socket(family, socket.SOCK_STREAM)
73*8161b823SAlexander V. Chernikov            s.close()
74*8161b823SAlexander V. Chernikov        except OSError as e:
75*8161b823SAlexander V. Chernikov            if error is None or error != e.errno:
76*8161b823SAlexander V. Chernikov                raise
77*8161b823SAlexander V. Chernikov
78*8161b823SAlexander V. Chernikov    # @pytest.mark.skip(reason="comment me to run the test")
79*8161b823SAlexander V. Chernikov    def test_with_cleanup(self):
80*8161b823SAlexander V. Chernikov        print("TEST BODY")
81*8161b823SAlexander V. Chernikov
82*8161b823SAlexander V. Chernikov    def cleanup_test_with_cleanup(self, test_id):
83*8161b823SAlexander V. Chernikov        print("CLEANUP HANDLER")
84*8161b823SAlexander V. Chernikov
85*8161b823SAlexander V. Chernikov
86*8161b823SAlexander V. Chernikovclass TestVnetSimple(SingleVnetTestTemplate):
87*8161b823SAlexander V. Chernikov    """
88*8161b823SAlexander V. Chernikov    SingleVnetTestTemplate creates a topology with a single
89*8161b823SAlexander V. Chernikov    vnet and a single epair between this vnet and the host system.
90*8161b823SAlexander V. Chernikov    Additionally, lo0 interface is created inside the vnet.
91*8161b823SAlexander V. Chernikov
92*8161b823SAlexander V. Chernikov    Both vnets and interfaces are aliased as vnetX and ifY.
93*8161b823SAlexander V. Chernikov    They can be accessed via maps:
94*8161b823SAlexander V. Chernikov        vnet: VnetInstance = self.vnet_map["vnet1"]
95*8161b823SAlexander V. Chernikov        iface: VnetInterface = vnet.iface_alias_map["if1"]
96*8161b823SAlexander V. Chernikov
97*8161b823SAlexander V. Chernikov    All prefixes from IPV4_PREFIXES and IPV6_PREFIXES are
98*8161b823SAlexander V. Chernikov    assigned to the single epair interface inside the jail.
99*8161b823SAlexander V. Chernikov
100*8161b823SAlexander V. Chernikov    One can rely on the fact that there are no IPv6 prefixes
101*8161b823SAlexander V. Chernikov    in the tentative state when the test method is called.
102*8161b823SAlexander V. Chernikov    """
103*8161b823SAlexander V. Chernikov
104*8161b823SAlexander V. Chernikov    IPV6_PREFIXES: List[str] = ["2001:db8::1/64"]
105*8161b823SAlexander V. Chernikov    IPV4_PREFIXES: List[str] = ["192.0.2.1/24"]
106*8161b823SAlexander V. Chernikov
107*8161b823SAlexander V. Chernikov    def setup_method(self, method):
108*8161b823SAlexander V. Chernikov        """
109*8161b823SAlexander V. Chernikov        Optional pre-setup for all of the tests inside the class
110*8161b823SAlexander V. Chernikov        """
111*8161b823SAlexander V. Chernikov        # Code to run before vnet setup
112*8161b823SAlexander V. Chernikov        #
113*8161b823SAlexander V. Chernikov        super().setup_method(method)
114*8161b823SAlexander V. Chernikov        #
115*8161b823SAlexander V. Chernikov        # Code to run after vnet setup
116*8161b823SAlexander V. Chernikov        # Executed inside the vnet
117*8161b823SAlexander V. Chernikov
118*8161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
119*8161b823SAlexander V. Chernikov    @pytest.mark.require_user("root")
120*8161b823SAlexander V. Chernikov    def test_ping(self):
121*8161b823SAlexander V. Chernikov        assert subprocess.run("ping -c1 192.0.2.1".split()).returncode == 0
122*8161b823SAlexander V. Chernikov        assert subprocess.run("ping -c1 2001:db8::1".split()).returncode == 0
123*8161b823SAlexander V. Chernikov
124*8161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
125*8161b823SAlexander V. Chernikov    def test_topology(self):
126*8161b823SAlexander V. Chernikov        vnet = self.vnet_map["vnet1"]
127*8161b823SAlexander V. Chernikov        iface = vnet.iface_alias_map["if1"]
128*8161b823SAlexander V. Chernikov        print("Iface {} inside vnet {}".format(iface.name, vnet.name))
129*8161b823SAlexander V. Chernikov
130*8161b823SAlexander V. Chernikov
131*8161b823SAlexander V. Chernikovclass TestVnetDual1(VnetTestTemplate):
132*8161b823SAlexander V. Chernikov    """
133*8161b823SAlexander V. Chernikov    VnetTestTemplate creates topology described in the self.TOPOLOGY
134*8161b823SAlexander V. Chernikov
135*8161b823SAlexander V. Chernikov    Each vnet (except vnet1) can have a handler function, named
136*8161b823SAlexander V. Chernikov      vnetX_handler. This function will be run in a separate process
137*8161b823SAlexander V. Chernikov      inside vnetX jail. The framework automatically creates a pipe
138*8161b823SAlexander V. Chernikov      to allow communication between the main test and the vnet handler.
139*8161b823SAlexander V. Chernikov
140*8161b823SAlexander V. Chernikov    This topology contains 2 VNETs connected with 2 epairs:
141*8161b823SAlexander V. Chernikov
142*8161b823SAlexander V. Chernikov    [           VNET1          ]     [          VNET2           ]
143*8161b823SAlexander V. Chernikov     if1(epair) 2001:db8:a::1/64 <-> 2001:db8:a::2/64 if1(epair)
144*8161b823SAlexander V. Chernikov     if2(epair) 2001:db8:b::1/64 <-> 2001:db8:b::2/64 if2(epair)
145*8161b823SAlexander V. Chernikov                 lo0                             lo0
146*8161b823SAlexander V. Chernikov
147*8161b823SAlexander V. Chernikov    """
148*8161b823SAlexander V. Chernikov
149*8161b823SAlexander V. Chernikov    TOPOLOGY = {
150*8161b823SAlexander V. Chernikov        "vnet1": {"ifaces": ["if1", "if2"]},
151*8161b823SAlexander V. Chernikov        "vnet2": {"ifaces": ["if1", "if2"]},
152*8161b823SAlexander V. Chernikov        "if1": {"prefixes6": [("2001:db8:a::1/64", "2001:db8:a::2/64")]},
153*8161b823SAlexander V. Chernikov        "if2": {"prefixes6": [("2001:db8:b::1/64", "2001:db8:b::2/64")]},
154*8161b823SAlexander V. Chernikov    }
155*8161b823SAlexander V. Chernikov
156*8161b823SAlexander V. Chernikov    def _get_iface_stat(self, os_ifname: str):
157*8161b823SAlexander V. Chernikov        out = ToolsHelper.get_output(
158*8161b823SAlexander V. Chernikov            "{} -I {} --libxo json".format(ToolsHelper.NETSTAT_PATH, os_ifname)
159*8161b823SAlexander V. Chernikov        )
160*8161b823SAlexander V. Chernikov        js = json.loads(out)
161*8161b823SAlexander V. Chernikov        return js["statistics"]["interface"][0]
162*8161b823SAlexander V. Chernikov
163*8161b823SAlexander V. Chernikov    def vnet2_handler(self, vnet: VnetInstance):
164*8161b823SAlexander V. Chernikov        """
165*8161b823SAlexander V. Chernikov        Test handler that runs in the vnet2 as a separate process.
166*8161b823SAlexander V. Chernikov
167*8161b823SAlexander V. Chernikov        This handler receives an interface name, fetches received/sent packets
168*8161b823SAlexander V. Chernikov         and returns this data back to the parent process.
169*8161b823SAlexander V. Chernikov        """
170*8161b823SAlexander V. Chernikov        while True:
171*8161b823SAlexander V. Chernikov            # receives 'ifX' with an infinite timeout
172*8161b823SAlexander V. Chernikov            iface_alias = self.wait_object(vnet.pipe, None)
173*8161b823SAlexander V. Chernikov            # Translates topology interface name to the actual OS-assigned name
174*8161b823SAlexander V. Chernikov            os_ifname = vnet.iface_alias_map[iface_alias].name
175*8161b823SAlexander V. Chernikov            self.send_object(vnet.pipe, self._get_iface_stat(os_ifname))
176*8161b823SAlexander V. Chernikov
177*8161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
178*8161b823SAlexander V. Chernikov    @pytest.mark.require_user("root")
179*8161b823SAlexander V. Chernikov    def test_ifstat(self):
180*8161b823SAlexander V. Chernikov        """Checks that RX interface packets are properly accounted for"""
181*8161b823SAlexander V. Chernikov        second_vnet = self.vnet_map["vnet2"]
182*8161b823SAlexander V. Chernikov        pipe = second_vnet.pipe
183*8161b823SAlexander V. Chernikov
184*8161b823SAlexander V. Chernikov        # Ping neighbor IP on if1 and verify that the counter was incremented
185*8161b823SAlexander V. Chernikov        self.send_object(pipe, "if1")
186*8161b823SAlexander V. Chernikov        old_stat = self.wait_object(pipe)
187*8161b823SAlexander V. Chernikov        assert subprocess.run("ping -c5 2001:db8:a::2".split()).returncode == 0
188*8161b823SAlexander V. Chernikov        self.send_object(pipe, "if1")
189*8161b823SAlexander V. Chernikov        new_stat = self.wait_object(pipe)
190*8161b823SAlexander V. Chernikov        assert new_stat["received-packets"] - old_stat["received-packets"] >= 5
191*8161b823SAlexander V. Chernikov
192*8161b823SAlexander V. Chernikov        # Ping neighbor IP on if2 and verify that the counter was incremented
193*8161b823SAlexander V. Chernikov        self.send_object(pipe, "if2")
194*8161b823SAlexander V. Chernikov        old_stat = self.wait_object(pipe)
195*8161b823SAlexander V. Chernikov        assert subprocess.run("ping -c5 2001:db8:b::2".split()).returncode == 0
196*8161b823SAlexander V. Chernikov        self.send_object(pipe, "if2")
197*8161b823SAlexander V. Chernikov        new_stat = self.wait_object(pipe)
198*8161b823SAlexander V. Chernikov        assert new_stat["received-packets"] - old_stat["received-packets"] >= 5
199