feat(Performance): Add 2M/1G hugepages
[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             page_size=kwargs[u"page_size"]
159         )
160         self.machines[name].add_default_params()
161         self.machines[name].add_kernelvm_params()
162         self.machines[name].configure_kernelvm_vnf(
163             mac1=f"52:54:00:00:{qemu_id:02x}:01",
164             mac2=f"52:54:00:00:{qemu_id:02x}:02",
165             vif1_mac=kwargs[u"vif1_mac"],
166             vif2_mac=kwargs[u"vif2_mac"],
167             queues=kwargs[u"queues"],
168             jumbo_frames=kwargs[u"jumbo"]
169         )
170         self.machines[name].add_vhost_user_if(
171             f"/run/vpp/sock-{qemu_id}-1",
172             jumbo_frames=kwargs[u"jumbo"],
173             queues=kwargs[u"queues"],
174             queue_size=kwargs[u"perf_qemu_qsz"],
175             virtio_feature_mask=virtio_feature_mask
176         )
177         self.machines[name].add_vhost_user_if(
178             f"/run/vpp/sock-{qemu_id}-2",
179             jumbo_frames=kwargs[u"jumbo"],
180             queues=kwargs[u"queues"],
181             queue_size=kwargs[u"perf_qemu_qsz"],
182             virtio_feature_mask=virtio_feature_mask
183         )
184
185     def _c_vpp_2vfpt_ip4base_plen24(self, **kwargs):
186         """Instantiate one VM with vpp_2vfpt_ip4base_plen24 configuration.
187
188         :param kwargs: Named parameters.
189         :type kwargs: dict
190         """
191         qemu_id = kwargs[u"qemu_id"]
192         name = kwargs[u"name"]
193
194         self.machines[name] = QemuUtils(
195             node=self.nodes[kwargs[u"node"]],
196             qemu_id=qemu_id,
197             smp=len(self.machines_affinity[name]),
198             mem=4096,
199             vnf=kwargs[u"vnf"],
200             img=Constants.QEMU_VM_KERNEL
201         )
202         self.machines[name].add_default_params()
203         self.machines[name].add_kernelvm_params()
204         if u"DUT1" in name:
205             self.machines[name].configure_kernelvm_vnf(
206                 ip1=u"2.2.2.1/30",
207                 ip2=u"1.1.1.2/30",
208                 route1=u"20.0.0.0/24",
209                 routeif1=u"avf-0/0/6/0",
210                 nexthop1=u"2.2.2.2",
211                 route2=u"10.0.0.0/24",
212                 routeif2=u"avf-0/0/7/0",
213                 nexthop2=u"1.1.1.1",
214                 arpmac1=u"3c:fd:fe:d1:5c:d8",
215                 arpip1=u"1.1.1.1",
216                 arpif1=u"avf-0/0/7/0",
217                 queues=kwargs[u"queues"],
218                 jumbo_frames=kwargs[u"jumbo"]
219             )
220         else:
221             self.machines[name].configure_kernelvm_vnf(
222                 ip1=u"3.3.3.2/30",
223                 ip2=u"2.2.2.2/30",
224                 route1=u"10.0.0.0/24",
225                 routeif1=u"avf-0/0/7/0",
226                 nexthop1=u"2.2.2.1",
227                 route2=u"20.0.0.0/24",
228                 routeif2=u"avf-0/0/6/0",
229                 nexthop2=u"3.3.3.1",
230                 arpmac1=u"3c:fd:fe:d1:5c:d9",
231                 arpip1=u"3.3.3.1",
232                 arpif1=u"avf-0/0/6/0",
233                 queues=kwargs[u"queues"],
234                 jumbo_frames=kwargs[u"jumbo"]
235             )
236         self.machines[name].add_vfio_pci_if(
237             pci=Topology.get_interface_pci_addr(
238                 self.nodes[kwargs[u"node"]], kwargs[u"if2"])
239         )
240         self.machines[name].add_vfio_pci_if(
241             pci=Topology.get_interface_pci_addr(
242                 self.nodes[kwargs[u"node"]], kwargs[u"if1"])
243         )
244
245     def _c_vpp_2vfpt_ip4scale2k_plen30(self, **kwargs):
246         """Instantiate one VM with vpp_2vfpt_ip4scale2k_plen30 configuration.
247
248         :param kwargs: Named parameters.
249         :type kwargs: dict
250         """
251         qemu_id = kwargs[u"qemu_id"]
252         name = kwargs[u"name"]
253
254         self.machines[name] = QemuUtils(
255             node=self.nodes[kwargs[u"node"]],
256             qemu_id=qemu_id,
257             smp=len(self.machines_affinity[name]),
258             mem=4096,
259             vnf=kwargs[u"vnf"],
260             img=Constants.QEMU_VM_KERNEL
261         )
262         self.machines[name].add_default_params()
263         self.machines[name].add_kernelvm_params()
264         if u"DUT1" in name:
265             self.machines[name].configure_kernelvm_vnf(
266                 ip1=u"2.2.2.1/30",
267                 ip2=u"1.1.1.2/30",
268                 route1=u"20.0.0.0/30",
269                 routeif1=u"avf-0/0/6/0",
270                 nexthop1=u"2.2.2.2",
271                 route2=u"10.0.0.0/30",
272                 routeif2=u"avf-0/0/7/0",
273                 nexthop2=u"1.1.1.1",
274                 arpmac1=u"3c:fd:fe:d1:5c:d8",
275                 arpip1=u"1.1.1.1",
276                 arpif1=u"avf-0/0/7/0",
277                 queues=kwargs[u"queues"],
278                 jumbo_frames=kwargs[u"jumbo"]
279             )
280         else:
281             self.machines[name].configure_kernelvm_vnf(
282                 ip1=u"3.3.3.2/30",
283                 ip2=u"2.2.2.2/30",
284                 route1=u"10.0.0.0/30",
285                 routeif1=u"avf-0/0/7/0",
286                 nexthop1=u"2.2.2.1",
287                 route2=u"20.0.0.0/30",
288                 routeif2=u"avf-0/0/6/0",
289                 nexthop2=u"3.3.3.1",
290                 arpmac1=u"3c:fd:fe:d1:5c:d9",
291                 arpip1=u"3.3.3.1",
292                 arpif1=u"avf-0/0/6/0",
293                 queues=kwargs[u"queues"],
294                 jumbo_frames=kwargs[u"jumbo"]
295             )
296         self.machines[name].add_vfio_pci_if(
297             pci=Topology.get_interface_pci_addr(
298                 self.nodes[kwargs[u"node"]], kwargs[u"if2"])
299         )
300         self.machines[name].add_vfio_pci_if(
301             pci=Topology.get_interface_pci_addr(
302                 self.nodes[kwargs[u"node"]], kwargs[u"if1"])
303         )
304
305     def _c_vpp_2vfpt_ip4scale20k_plen30(self, **kwargs):
306         """Instantiate one VM with vpp_2vfpt_ip4scale20k_plen30 configuration.
307
308         :param kwargs: Named parameters.
309         :type kwargs: dict
310         """
311         qemu_id = kwargs[u"qemu_id"]
312         name = kwargs[u"name"]
313
314         self.machines[name] = QemuUtils(
315             node=self.nodes[kwargs[u"node"]],
316             qemu_id=qemu_id,
317             smp=len(self.machines_affinity[name]),
318             mem=4096,
319             vnf=kwargs[u"vnf"],
320             img=Constants.QEMU_VM_KERNEL
321         )
322         self.machines[name].add_default_params()
323         self.machines[name].add_kernelvm_params()
324         if u"DUT1" in name:
325             self.machines[name].configure_kernelvm_vnf(
326                 ip1=u"2.2.2.1/30",
327                 ip2=u"1.1.1.2/30",
328                 route1=u"20.0.0.0/30",
329                 routeif1=u"avf-0/0/6/0",
330                 nexthop1=u"2.2.2.2",
331                 route2=u"10.0.0.0/30",
332                 routeif2=u"avf-0/0/7/0",
333                 nexthop2=u"1.1.1.1",
334                 arpmac1=u"3c:fd:fe:d1:5c:d8",
335                 arpip1=u"1.1.1.1",
336                 arpif1=u"avf-0/0/7/0",
337                 queues=kwargs[u"queues"],
338                 jumbo_frames=kwargs[u"jumbo"]
339             )
340         else:
341             self.machines[name].configure_kernelvm_vnf(
342                 ip1=u"3.3.3.2/30",
343                 ip2=u"2.2.2.2/30",
344                 route1=u"10.0.0.0/30",
345                 routeif1=u"avf-0/0/7/0",
346                 nexthop1=u"2.2.2.1",
347                 route2=u"20.0.0.0/30",
348                 routeif2=u"avf-0/0/6/0",
349                 nexthop2=u"3.3.3.1",
350                 arpmac1=u"3c:fd:fe:d1:5c:d9",
351                 arpip1=u"3.3.3.1",
352                 arpif1=u"avf-0/0/6/0",
353                 queues=kwargs[u"queues"],
354                 jumbo_frames=kwargs[u"jumbo"]
355             )
356         self.machines[name].add_vfio_pci_if(
357             pci=Topology.get_interface_pci_addr(
358                 self.nodes[kwargs[u"node"]], kwargs[u"if2"])
359         )
360         self.machines[name].add_vfio_pci_if(
361             pci=Topology.get_interface_pci_addr(
362                 self.nodes[kwargs[u"node"]], kwargs[u"if1"])
363         )
364
365     def _c_vpp_2vfpt_ip4scale200k_plen30(self, **kwargs):
366         """Instantiate one VM with vpp_2vfpt_ip4scale200k_plen30 configuration.
367
368         :param kwargs: Named parameters.
369         :type kwargs: dict
370         """
371         qemu_id = kwargs[u"qemu_id"]
372         name = kwargs[u"name"]
373
374         self.machines[name] = QemuUtils(
375             node=self.nodes[kwargs[u"node"]],
376             qemu_id=qemu_id,
377             smp=len(self.machines_affinity[name]),
378             mem=4096,
379             vnf=kwargs[u"vnf"],
380             img=Constants.QEMU_VM_KERNEL
381         )
382         self.machines[name].add_default_params()
383         self.machines[name].add_kernelvm_params()
384         if u"DUT1" in name:
385             self.machines[name].configure_kernelvm_vnf(
386                 ip1=u"2.2.2.1/30",
387                 ip2=u"1.1.1.2/30",
388                 route1=u"20.0.0.0/30",
389                 routeif1=u"avf-0/0/6/0",
390                 nexthop1=u"2.2.2.2",
391                 route2=u"10.0.0.0/30",
392                 routeif2=u"avf-0/0/7/0",
393                 nexthop2=u"1.1.1.1",
394                 arpmac1=u"3c:fd:fe:d1:5c:d8",
395                 arpip1=u"1.1.1.1",
396                 arpif1=u"avf-0/0/7/0",
397                 queues=kwargs[u"queues"],
398                 jumbo_frames=kwargs[u"jumbo"]
399             )
400         else:
401             self.machines[name].configure_kernelvm_vnf(
402                 ip1=u"3.3.3.2/30",
403                 ip2=u"2.2.2.2/30",
404                 route1=u"10.0.0.0/30",
405                 routeif1=u"avf-0/0/7/0",
406                 nexthop1=u"2.2.2.1",
407                 route2=u"20.0.0.0/30",
408                 routeif2=u"avf-0/0/6/0",
409                 nexthop2=u"3.3.3.1",
410                 arpmac1=u"3c:fd:fe:d1:5c:d9",
411                 arpip1=u"3.3.3.1",
412                 arpif1=u"avf-0/0/6/0",
413                 queues=kwargs[u"queues"],
414                 jumbo_frames=kwargs[u"jumbo"]
415             )
416         self.machines[name].add_vfio_pci_if(
417             pci=Topology.get_interface_pci_addr(
418                 self.nodes[kwargs[u"node"]], kwargs[u"if2"])
419         )
420         self.machines[name].add_vfio_pci_if(
421             pci=Topology.get_interface_pci_addr(
422                 self.nodes[kwargs[u"node"]], kwargs[u"if1"])
423         )
424
425     def _c_iperf3(self, **kwargs):
426         """Instantiate one VM with iperf3 configuration.
427
428         :param kwargs: Named parameters.
429         :type kwargs: dict
430         """
431         qemu_id = kwargs[u"qemu_id"]
432         name = kwargs[u"name"]
433         virtio_feature_mask = kwargs[u"virtio_feature_mask"] \
434             if u"virtio_feature_mask" in kwargs else None
435
436         self.machines[name] = QemuUtils(
437             node=self.nodes[kwargs[u"node"]],
438             qemu_id=qemu_id,
439             smp=len(self.machines_affinity[name]),
440             mem=4096,
441             vnf=kwargs[u"vnf"],
442             img=Constants.QEMU_VM_KERNEL
443         )
444         self.machines[name].add_default_params()
445         self.machines[name].add_kernelvm_params()
446         self.machines[name].configure_kernelvm_vnf(
447             queues=kwargs[u"queues"],
448             jumbo_frames=kwargs[u"jumbo"]
449         )
450         self.machines[name].add_net_user()
451         self.machines[name].add_vhost_user_if(
452             f"/run/vpp/sock-{qemu_id}-1",
453             server=False,
454             jumbo_frames=kwargs[u"jumbo"],
455             queues=kwargs[u"queues"],
456             queue_size=kwargs[u"perf_qemu_qsz"],
457             virtio_feature_mask=virtio_feature_mask
458         )