CSIT-1386 KernelVM - Part II
[csit.git] / resources / libraries / python / QemuManager.py
1 # Copyright (c) 2019 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 robot.libraries.BuiltIn import BuiltIn
19
20 from resources.libraries.python.Constants import Constants
21 from resources.libraries.python.CpuUtils import CpuUtils
22 from resources.libraries.python.QemuUtils import QemuUtils
23 from resources.libraries.python.topology import NodeType, Topology
24
25 __all__ = ["QemuManager"]
26
27
28 def get_affinity_vm(nodes, node, nf_chains=1, nf_nodes=1, nf_chain=1, nf_node=1,
29                     cpu_count_int=1):
30     """Get affinity of VM. Result will be used to compute the amount of
31     CPUs and also affinity.
32
33     :param node: SUT nodes.
34     :param node: DUT node.
35     :param nf_chains: Number of NF chains.
36     :param nf_nodes: Number of NF nodes in chain.
37     :param nf_chain: Chain ID.
38     :param nf_node: Node ID.
39     :param cpu_count_int: Amount of Dataplane threads of vswitch.
40     :type nodes: dict
41     :type node: dict
42     :type nf_chains: int
43     :type nf_nodes: int
44     :type nf_chain: int
45     :type nf_node: int
46     :type cpu_count_int: int
47     :returns: List of CPUs allocated to VM.
48     :rtype: list
49     """
50     sut_sc = 1
51     dut_mc = 1
52     dut_dc = cpu_count_int
53     skip_cnt = sut_sc + dut_mc + dut_dc
54     dtc = cpu_count_int
55
56     interface_list = []
57     interface_list.append(
58         BuiltIn().get_variable_value('${{{node}_if1}}'.format(node=node)))
59     interface_list.append(
60         BuiltIn().get_variable_value('${{{node}_if2}}'.format(node=node)))
61
62     cpu_node = Topology.get_interfaces_numa_node(nodes[node], *interface_list)
63
64     nf_cpus = CpuUtils.cpu_slice_of_list_for_nf(
65         node=nodes[node], cpu_node=cpu_node, chains=nf_chains,
66         nodeness=nf_nodes, chain_id=nf_chain, node_id=nf_node, mtcr=2, dtcr=1,
67         dtc=dtc, skip_cnt=skip_cnt)
68
69     return nf_cpus
70
71 class QemuManager(object):
72     """QEMU lifecycle management class"""
73
74     # Use one instance of class per tests.
75     ROBOT_LIBRARY_SCOPE = 'TEST CASE'
76
77     def __init__(self, nodes):
78         """Init QemuManager object."""
79         self.machines = None
80         self.machines_affinity = None
81         self.nodes = nodes
82
83     def initialize(self):
84         """Initialize QemuManager object."""
85         self.machines = OrderedDict()
86         self.machines_affinity = OrderedDict()
87
88     def construct_vms_on_node(self, **kwargs):
89         """Construct 1..Mx1..N VMs(s) on node with specified name.
90
91         :param kwargs: Named parameters.
92         :type kwargs: dict
93         """
94         node = kwargs['node']
95         nf_chains = int(kwargs['nf_chains'])
96         nf_nodes = int(kwargs['nf_nodes'])
97         queues = kwargs['rxq_count_int'] if kwargs['auto_scale'] else 1
98         cpu_count_int = kwargs['cpu_count_int'] if kwargs['auto_scale'] else 1
99
100         img = Constants.QEMU_PERF_VM_KERNEL
101
102         for nf_chain in range(1, nf_chains + 1):
103             for nf_node in range(1, nf_nodes + 1):
104                 qemu_id = (nf_chain - 1) * nf_nodes + nf_node
105                 name = '{node}_{qemu_id}'.format(node=node, qemu_id=qemu_id)
106                 sock1 = '/var/run/vpp/sock-{qemu_id}-1'.format(qemu_id=qemu_id)
107                 sock2 = '/var/run/vpp/sock-{qemu_id}-2'.format(qemu_id=qemu_id)
108                 vif1_mac = kwargs['tg_if1_mac'] if nf_node == 1 \
109                         else '52:54:00:00:{id:02x}:02'.format(id=qemu_id - 1)
110                 vif2_mac = kwargs['tg_if2_mac'] if nf_node == nf_nodes \
111                         else '52:54:00:00:{id:02x}:01'.format(id=qemu_id + 1)
112
113                 self.machines_affinity[name] = get_affinity_vm(
114                     nodes=self.nodes, node=node, nf_chains=nf_chains,
115                     nf_nodes=nf_nodes, nf_chain=nf_chain, nf_node=nf_node,
116                     cpu_count_int=cpu_count_int)
117
118                 self.machines[name] = QemuUtils(
119                     node=self.nodes[node], qemu_id=qemu_id,
120                     smp=len(self.machines_affinity[name]), mem=4096,
121                     vnf=kwargs['vnf'], img=img)
122                 self.machines[name].configure_kernelvm_vnf(
123                     mac1='52:54:00:00:{id:02x}:01'.format(id=qemu_id),
124                     mac2='52:54:00:00:{id:02x}:02'.format(id=qemu_id),
125                     vif1_mac=vif1_mac,
126                     vif2_mac=vif2_mac)
127                 self.machines[name].qemu_add_vhost_user_if(
128                     sock1, jumbo_frames=kwargs['jumbo'], queues=queues,
129                     queue_size=1024)
130                 self.machines[name].qemu_add_vhost_user_if(
131                     sock2, jumbo_frames=kwargs['jumbo'], queues=queues,
132                     queue_size=1024)
133
134     def construct_vms_on_all_nodes(self, **kwargs):
135         """Construct 1..Mx1..N VMs(s) with specified name on all nodes.
136
137         :param kwargs: Named parameters.
138         :type kwargs: dict
139         """
140         self.initialize()
141         for node in self.nodes:
142             if self.nodes[node]['type'] == NodeType.DUT:
143                 self.construct_vms_on_node(node=node, **kwargs)
144
145     def start_all_vms(self, pinning=False):
146         """Start all added VMs in manager.
147
148         :param pinning: If True, then do also QEMU process pinning.
149         :type pinning: bool
150         """
151         for machine, machine_affinity in zip(self.machines.values(),
152                                              self.machines_affinity.values()):
153             machine.qemu_start()
154             if pinning:
155                 machine.qemu_set_affinity(*machine_affinity)
156
157     def set_scheduler_all_vms(self):
158         """Set CFS scheduler policy on all VMs in manager."""
159         for machine in self.machines.values():
160             machine.qemu_set_scheduler_policy()
161
162     def kill_all_vms(self, force=False):
163         """Kill all added VMs in manager.
164
165         :param force: Force kill all Qemu instances by pkill qemu if True.
166         :type force: bool
167         """
168         for machine in self.machines.values():
169             if force:
170                 machine.qemu_kill_all()
171             else:
172                 machine.qemu_kill()