Line length: Fix recent merges
[csit.git] / resources / libraries / python / QemuManager.py
1 # Copyright (c) 2021 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 """QEMU Manager library."""
15
16 from collections import OrderedDict
17
18 from resources.libraries.python.Constants import Constants
19 from resources.libraries.python.CpuUtils import CpuUtils
20 from resources.libraries.python.QemuUtils import QemuUtils
21 from resources.libraries.python.topology import NodeType, Topology
22
23 __all__ = [u"QemuManager"]
24
25
26 class QemuManager:
27     """QEMU lifecycle management class"""
28
29     # Use one instance of class per tests.
30     ROBOT_LIBRARY_SCOPE = u"TEST CASE"
31
32     def __init__(self, nodes):
33         """Init QemuManager object."""
34         self.machines = None
35         self.machines_affinity = None
36         self.nodes = nodes
37
38     def initialize(self):
39         """Initialize QemuManager object."""
40         self.machines = OrderedDict()
41         self.machines_affinity = OrderedDict()
42
43     def construct_vms_on_node(self, **kwargs):
44         """Construct 1..Mx1..N VMs(s) on node with specified name.
45
46         :param kwargs: Named parameters.
47         :type kwargs: dict
48         """
49         node = kwargs[u"node"]
50         nf_chains = int(kwargs[u"nf_chains"])
51         nf_nodes = int(kwargs[u"nf_nodes"])
52         queues = kwargs[u"rxq_count_int"] if kwargs[u"auto_scale"] else 1
53         vs_dtc = kwargs[u"vs_dtc"]
54         nf_dtc = kwargs[u"nf_dtc"]
55         if kwargs[u"auto_scale"] and not kwargs[u"fixed_auto_scale"]:
56             nf_dtc = kwargs[u"vs_dtc"]
57         nf_dtcr = kwargs[u"nf_dtcr"] \
58             if isinstance(kwargs[u"nf_dtcr"], int) else 2
59
60         for nf_chain in range(1, nf_chains + 1):
61             for nf_node in range(1, nf_nodes + 1):
62                 qemu_id = (nf_chain - 1) * nf_nodes + nf_node
63                 name = f"{node}_{qemu_id}"
64                 idx1 = (nf_chain - 1) * nf_nodes * 2 + nf_node * 2 - 1
65
66                 vif1_mac = Topology.get_interface_mac(
67                     self.nodes[node], f"vhost{idx1}"
68                 ) if kwargs[u"vnf"] == u"testpmd_mac" \
69                     else kwargs[u"tg_pf1_mac"] if nf_node == 1 \
70                     else f"52:54:00:00:{(qemu_id - 1):02x}:02"
71                 idx2 = (nf_chain - 1) * nf_nodes * 2 + nf_node * 2
72                 vif2_mac = Topology.get_interface_mac(
73                     self.nodes[node], f"vhost{idx2}"
74                 ) if kwargs[u"vnf"] == u"testpmd_mac" \
75                     else kwargs[u"tg_pf2_mac"] if nf_node == nf_nodes \
76                     else f"52:54:00:00:{(qemu_id + 1):02x}:01"
77
78                 self.machines_affinity[name] = CpuUtils.get_affinity_nf(
79                     nodes=self.nodes, node=node, nf_chains=nf_chains,
80                     nf_nodes=nf_nodes, nf_chain=nf_chain, nf_node=nf_node,
81                     vs_dtc=vs_dtc, nf_dtc=nf_dtc, nf_dtcr=nf_dtcr
82                 )
83
84                 try:
85                     getattr(self, f'_c_{kwargs["vnf"]}')(
86                         qemu_id=qemu_id, name=name, queues=queues, **kwargs
87                     )
88                 except AttributeError:
89                     self._c_default(
90                         qemu_id=qemu_id, name=name, queues=queues,
91                         vif1_mac=vif1_mac, vif2_mac=vif2_mac, **kwargs
92                     )
93
94     def construct_vms_on_all_nodes(self, **kwargs):
95         """Construct 1..Mx1..N VMs(s) with specified name on all nodes.
96
97         :param kwargs: Named parameters.
98         :type kwargs: dict
99         """
100         self.initialize()
101         for node in self.nodes:
102             if self.nodes[node][u"type"] == NodeType.DUT:
103                 self.construct_vms_on_node(node=node, **kwargs)
104
105     def start_all_vms(self, pinning=False):
106         """Start all added VMs in manager.
107
108         :param pinning: If True, then do also QEMU process pinning.
109         :type pinning: bool
110         """
111         cpus = []
112         for machine, machine_affinity in \
113                 zip(self.machines.values(), self.machines_affinity.values()):
114             index = list(self.machines.values()).index(machine)
115             name = list(self.machines.keys())[index]
116             self.nodes[name] = machine.qemu_start()
117             if pinning:
118                 machine.qemu_set_affinity(*machine_affinity)
119                 cpus.extend(machine_affinity)
120         return ",".join(str(cpu) for cpu in cpus)
121
122     def kill_all_vms(self, force=False):
123         """Kill all added VMs in manager.
124
125         :param force: Force kill all Qemu instances by pkill qemu if True.
126         :type force: bool
127         """
128         for node in list(self.nodes.values()):
129             if node["type"] == NodeType.VM:
130                 try:
131                     self.nodes.popitem(node)
132                 except TypeError:
133                     pass
134         for machine in self.machines.values():
135             if force:
136                 machine.qemu_kill_all()
137             else:
138                 machine.qemu_kill()
139
140     def _c_default(self, **kwargs):
141         """Instantiate one VM with default configuration.
142
143         :param kwargs: Named parameters.
144         :type kwargs: dict
145         """
146         qemu_id = kwargs[u"qemu_id"]
147         name = kwargs[u"name"]
148         virtio_feature_mask = kwargs[u"virtio_feature_mask"] \
149             if u"virtio_feature_mask" in kwargs else None
150
151         self.machines[name] = QemuUtils(
152             node=self.nodes[kwargs[u"node"]],
153             qemu_id=qemu_id,
154             smp=len(self.machines_affinity[name]),
155             mem=4096,
156             vnf=kwargs[u"vnf"],
157             img=Constants.QEMU_VM_KERNEL
158         )
159         self.machines[name].add_default_params()
160         self.machines[name].add_kernelvm_params()
161         self.machines[name].configure_kernelvm_vnf(
162             mac1=f"52:54:00:00:{qemu_id:02x}:01",
163             mac2=f"52:54:00:00:{qemu_id:02x}:02",
164             vif1_mac=kwargs[u"vif1_mac"],
165             vif2_mac=kwargs[u"vif2_mac"],
166             queues=kwargs[u"queues"],
167             jumbo_frames=kwargs[u"jumbo"]
168         )
169         self.machines[name].add_vhost_user_if(
170             f"/run/vpp/sock-{qemu_id}-1",
171             jumbo_frames=kwargs[u"jumbo"],
172             queues=kwargs[u"queues"],
173             queue_size=kwargs[u"perf_qemu_qsz"],
174             virtio_feature_mask=virtio_feature_mask
175         )
176         self.machines[name].add_vhost_user_if(
177             f"/run/vpp/sock-{qemu_id}-2",
178             jumbo_frames=kwargs[u"jumbo"],
179             queues=kwargs[u"queues"],
180             queue_size=kwargs[u"perf_qemu_qsz"],
181             virtio_feature_mask=virtio_feature_mask
182         )
183
184     def _c_vpp_2vfpt_ip4base_plen24(self, **kwargs):
185         """Instantiate one VM with vpp_2vfpt_ip4base_plen24 configuration.
186
187         :param kwargs: Named parameters.
188         :type kwargs: dict
189         """
190         qemu_id = kwargs[u"qemu_id"]
191         name = kwargs[u"name"]
192
193         self.machines[name] = QemuUtils(
194             node=self.nodes[kwargs[u"node"]],
195             qemu_id=qemu_id,
196             smp=len(self.machines_affinity[name]),
197             mem=4096,
198             vnf=kwargs[u"vnf"],
199             img=Constants.QEMU_VM_KERNEL
200         )
201         self.machines[name].add_default_params()
202         self.machines[name].add_kernelvm_params()
203         if u"DUT1" in name:
204             self.machines[name].configure_kernelvm_vnf(
205                 ip1=u"2.2.2.1/30",
206                 ip2=u"1.1.1.2/30",
207                 route1=u"20.0.0.0/24",
208                 routeif1=u"avf-0/0/6/0",
209                 nexthop1=u"2.2.2.2",
210                 route2=u"10.0.0.0/24",
211                 routeif2=u"avf-0/0/7/0",
212                 nexthop2=u"1.1.1.1",
213                 arpmac1=u"3c:fd:fe:d1:5c:d8",
214                 arpip1=u"1.1.1.1",
215                 arpif1=u"avf-0/0/7/0",
216                 queues=kwargs[u"queues"],
217                 jumbo_frames=kwargs[u"jumbo"]
218             )
219         else:
220             self.machines[name].configure_kernelvm_vnf(
221                 ip1=u"3.3.3.2/30",
222                 ip2=u"2.2.2.2/30",
223                 route1=u"10.0.0.0/24",
224                 routeif1=u"avf-0/0/7/0",
225                 nexthop1=u"2.2.2.1",
226                 route2=u"20.0.0.0/24",
227                 routeif2=u"avf-0/0/6/0",
228                 nexthop2=u"3.3.3.1",
229                 arpmac1=u"3c:fd:fe:d1:5c:d9",
230                 arpip1=u"3.3.3.1",
231                 arpif1=u"avf-0/0/6/0",
232                 queues=kwargs[u"queues"],
233                 jumbo_frames=kwargs[u"jumbo"]
234             )
235         self.machines[name].add_vfio_pci_if(
236             pci=Topology.get_interface_pci_addr(
237                 self.nodes[kwargs[u"node"]], kwargs[u"if2"])
238         )
239         self.machines[name].add_vfio_pci_if(
240             pci=Topology.get_interface_pci_addr(
241                 self.nodes[kwargs[u"node"]], kwargs[u"if1"])
242         )
243
244     def _c_vpp_2vfpt_ip4scale2k_plen30(self, **kwargs):
245         """Instantiate one VM with vpp_2vfpt_ip4scale2k_plen30 configuration.
246
247         :param kwargs: Named parameters.
248         :type kwargs: dict
249         """
250         qemu_id = kwargs[u"qemu_id"]
251         name = kwargs[u"name"]
252
253         self.machines[name] = QemuUtils(
254             node=self.nodes[kwargs[u"node"]],
255             qemu_id=qemu_id,
256             smp=len(self.machines_affinity[name]),
257             mem=4096,
258             vnf=kwargs[u"vnf"],
259             img=Constants.QEMU_VM_KERNEL
260         )
261         self.machines[name].add_default_params()
262         self.machines[name].add_kernelvm_params()
263         if u"DUT1" in name:
264             self.machines[name].configure_kernelvm_vnf(
265                 ip1=u"2.2.2.1/30",
266                 ip2=u"1.1.1.2/30",
267                 route1=u"20.0.0.0/30",
268                 routeif1=u"avf-0/0/6/0",
269                 nexthop1=u"2.2.2.2",
270                 route2=u"10.0.0.0/30",
271                 routeif2=u"avf-0/0/7/0",
272                 nexthop2=u"1.1.1.1",
273                 arpmac1=u"3c:fd:fe:d1:5c:d8",
274                 arpip1=u"1.1.1.1",
275                 arpif1=u"avf-0/0/7/0",
276                 queues=kwargs[u"queues"],
277                 jumbo_frames=kwargs[u"jumbo"]
278             )
279         else:
280             self.machines[name].configure_kernelvm_vnf(
281                 ip1=u"3.3.3.2/30",
282                 ip2=u"2.2.2.2/30",
283                 route1=u"10.0.0.0/30",
284                 routeif1=u"avf-0/0/7/0",
285                 nexthop1=u"2.2.2.1",
286                 route2=u"20.0.0.0/30",
287                 routeif2=u"avf-0/0/6/0",
288                 nexthop2=u"3.3.3.1",
289                 arpmac1=u"3c:fd:fe:d1:5c:d9",
290                 arpip1=u"3.3.3.1",
291                 arpif1=u"avf-0/0/6/0",
292                 queues=kwargs[u"queues"],
293                 jumbo_frames=kwargs[u"jumbo"]
294             )
295         self.machines[name].add_vfio_pci_if(
296             pci=Topology.get_interface_pci_addr(
297                 self.nodes[kwargs[u"node"]], kwargs[u"if2"])
298         )
299         self.machines[name].add_vfio_pci_if(
300             pci=Topology.get_interface_pci_addr(
301                 self.nodes[kwargs[u"node"]], kwargs[u"if1"])
302         )
303
304     def _c_vpp_2vfpt_ip4scale20k_plen30(self, **kwargs):
305         """Instantiate one VM with vpp_2vfpt_ip4scale20k_plen30 configuration.
306
307         :param kwargs: Named parameters.
308         :type kwargs: dict
309         """
310         qemu_id = kwargs[u"qemu_id"]
311         name = kwargs[u"name"]
312
313         self.machines[name] = QemuUtils(
314             node=self.nodes[kwargs[u"node"]],
315             qemu_id=qemu_id,
316             smp=len(self.machines_affinity[name]),
317             mem=4096,
318             vnf=kwargs[u"vnf"],
319             img=Constants.QEMU_VM_KERNEL
320         )
321         self.machines[name].add_default_params()
322         self.machines[name].add_kernelvm_params()
323         if u"DUT1" in name:
324             self.machines[name].configure_kernelvm_vnf(
325                 ip1=u"2.2.2.1/30",
326                 ip2=u"1.1.1.2/30",
327                 route1=u"20.0.0.0/30",
328                 routeif1=u"avf-0/0/6/0",
329                 nexthop1=u"2.2.2.2",
330                 route2=u"10.0.0.0/30",
331                 routeif2=u"avf-0/0/7/0",
332                 nexthop2=u"1.1.1.1",
333                 arpmac1=u"3c:fd:fe:d1:5c:d8",
334                 arpip1=u"1.1.1.1",
335                 arpif1=u"avf-0/0/7/0",
336                 queues=kwargs[u"queues"],
337                 jumbo_frames=kwargs[u"jumbo"]
338             )
339         else:
340             self.machines[name].configure_kernelvm_vnf(
341                 ip1=u"3.3.3.2/30",
342                 ip2=u"2.2.2.2/30",
343                 route1=u"10.0.0.0/30",
344                 routeif1=u"avf-0/0/7/0",
345                 nexthop1=u"2.2.2.1",
346                 route2=u"20.0.0.0/30",
347                 routeif2=u"avf-0/0/6/0",
348                 nexthop2=u"3.3.3.1",
349                 arpmac1=u"3c:fd:fe:d1:5c:d9",
350                 arpip1=u"3.3.3.1",
351                 arpif1=u"avf-0/0/6/0",
352                 queues=kwargs[u"queues"],
353                 jumbo_frames=kwargs[u"jumbo"]
354             )
355         self.machines[name].add_vfio_pci_if(
356             pci=Topology.get_interface_pci_addr(
357                 self.nodes[kwargs[u"node"]], kwargs[u"if2"])
358         )
359         self.machines[name].add_vfio_pci_if(
360             pci=Topology.get_interface_pci_addr(
361                 self.nodes[kwargs[u"node"]], kwargs[u"if1"])
362         )
363
364     def _c_vpp_2vfpt_ip4scale200k_plen30(self, **kwargs):
365         """Instantiate one VM with vpp_2vfpt_ip4scale200k_plen30 configuration.
366
367         :param kwargs: Named parameters.
368         :type kwargs: dict
369         """
370         qemu_id = kwargs[u"qemu_id"]
371         name = kwargs[u"name"]
372
373         self.machines[name] = QemuUtils(
374             node=self.nodes[kwargs[u"node"]],
375             qemu_id=qemu_id,
376             smp=len(self.machines_affinity[name]),
377             mem=4096,
378             vnf=kwargs[u"vnf"],
379             img=Constants.QEMU_VM_KERNEL
380         )
381         self.machines[name].add_default_params()
382         self.machines[name].add_kernelvm_params()
383         if u"DUT1" in name:
384             self.machines[name].configure_kernelvm_vnf(
385                 ip1=u"2.2.2.1/30",
386                 ip2=u"1.1.1.2/30",
387                 route1=u"20.0.0.0/30",
388                 routeif1=u"avf-0/0/6/0",
389                 nexthop1=u"2.2.2.2",
390                 route2=u"10.0.0.0/30",
391                 routeif2=u"avf-0/0/7/0",
392                 nexthop2=u"1.1.1.1",
393                 arpmac1=u"3c:fd:fe:d1:5c:d8",
394                 arpip1=u"1.1.1.1",
395                 arpif1=u"avf-0/0/7/0",
396                 queues=kwargs[u"queues"],
397                 jumbo_frames=kwargs[u"jumbo"]
398             )
399         else:
400             self.machines[name].configure_kernelvm_vnf(
401                 ip1=u"3.3.3.2/30",
402                 ip2=u"2.2.2.2/30",
403                 route1=u"10.0.0.0/30",
404                 routeif1=u"avf-0/0/7/0",
405                 nexthop1=u"2.2.2.1",
406                 route2=u"20.0.0.0/30",
407                 routeif2=u"avf-0/0/6/0",
408                 nexthop2=u"3.3.3.1",
409                 arpmac1=u"3c:fd:fe:d1:5c:d9",
410                 arpip1=u"3.3.3.1",
411                 arpif1=u"avf-0/0/6/0",
412                 queues=kwargs[u"queues"],
413                 jumbo_frames=kwargs[u"jumbo"]
414             )
415         self.machines[name].add_vfio_pci_if(
416             pci=Topology.get_interface_pci_addr(
417                 self.nodes[kwargs[u"node"]], kwargs[u"if2"])
418         )
419         self.machines[name].add_vfio_pci_if(
420             pci=Topology.get_interface_pci_addr(
421                 self.nodes[kwargs[u"node"]], kwargs[u"if1"])
422         )
423
424     def _c_iperf3(self, **kwargs):
425         """Instantiate one VM with iperf3 configuration.
426
427         :param kwargs: Named parameters.
428         :type kwargs: dict
429         """
430         qemu_id = kwargs[u"qemu_id"]
431         name = kwargs[u"name"]
432         virtio_feature_mask = kwargs[u"virtio_feature_mask"] \
433             if u"virtio_feature_mask" in kwargs else None
434
435         self.machines[name] = QemuUtils(
436             node=self.nodes[kwargs[u"node"]],
437             qemu_id=qemu_id,
438             smp=len(self.machines_affinity[name]),
439             mem=4096,
440             vnf=kwargs[u"vnf"],
441             img=Constants.QEMU_VM_KERNEL
442         )
443         self.machines[name].add_default_params()
444         self.machines[name].add_kernelvm_params()
445         self.machines[name].configure_kernelvm_vnf(
446             queues=kwargs[u"queues"],
447             jumbo_frames=kwargs[u"jumbo"]
448         )
449         self.machines[name].add_net_user()
450         self.machines[name].add_vhost_user_if(
451             f"/run/vpp/sock-{qemu_id}-1",
452             server=False,
453             jumbo_frames=kwargs[u"jumbo"],
454             queues=kwargs[u"queues"],
455             queue_size=kwargs[u"perf_qemu_qsz"],
456             virtio_feature_mask=virtio_feature_mask
457         )