44
55import re
66
7+ from enum import Enum , auto
8+
79import host_tools .network as net_tools # pylint: disable=import-error
810
911
10- def test_1vcpu (test_microvm_with_ssh , network_config ):
11- """Test CPU feature emulation with 1 vCPU."""
12- test_microvm = test_microvm_with_ssh
13- test_microvm .spawn ()
12+ class CpuVendor (Enum ):
13+ """CPU vendors enum."""
1414
15- # Set up the microVM with 1 vCPUs, 256 MiB of RAM, no network ifaces, and
16- # a root file system with the rw permission. The network interfaces is
17- # added after we get a unique MAC and IP.
18- test_microvm .basic_config (vcpu_count = 1 )
15+ AMD = auto ()
16+ INTEL = auto ()
1917
20- _tap , _ , _ = test_microvm .ssh_network_config (network_config , '1' )
21- test_microvm .start ()
18+
19+ def _get_cpu_vendor ():
20+ cif = open ('/proc/cpuinfo' , 'r' )
21+ host_vendor_id = None
22+ while True :
23+ line = cif .readline ()
24+ if line == '' :
25+ break
26+ mo = re .search ("^vendor_id\\ s+:\\ s+(.+)$" , line )
27+ if mo :
28+ host_vendor_id = mo .group (1 )
29+ cif .close ()
30+ assert host_vendor_id is not None
31+
32+ if host_vendor_id == "AuthenticAMD" :
33+ return CpuVendor .AMD
34+ return CpuVendor .INTEL
35+
36+
37+ def _check_guest_cmd_output (test_microvm , guest_cmd , expected_header ,
38+ expected_separator ,
39+ expected_key_value_store ):
40+ ssh_connection = net_tools .SSHConnection (test_microvm .ssh_config )
41+ _ , stdout , stderr = ssh_connection .execute_command (guest_cmd )
42+
43+ assert stderr .read ().decode ("utf-8" ) == ''
44+ while True :
45+ line = stdout .readline ().decode ('utf-8' )
46+ if line != '' :
47+ # All the keys have been matched. Stop.
48+ if not expected_key_value_store :
49+ break
50+
51+ # Try to match the header if needed.
52+ if expected_header not in (None , '' ):
53+ if line .strip () == expected_header :
54+ expected_header = None
55+ continue
56+ else :
57+ continue
58+
59+ # See if any key matches.
60+ # We Use a try-catch block here since line.split() may fail.
61+ try :
62+ [key , value ] = list (
63+ map (lambda x : x .strip (), line .split (expected_separator )))
64+ except ValueError :
65+ continue
66+
67+ if key in expected_key_value_store .keys ():
68+ assert value == expected_key_value_store [key ], \
69+ "%s does not have the expected value" % key
70+ del expected_key_value_store [key ]
71+
72+ else :
73+ break
74+
75+ assert not expected_key_value_store , \
76+ "some keys in dictionary have not been found in the output: %s" \
77+ % expected_key_value_store
78+
79+
80+ def _check_cpu_topology (test_microvm , expected_cpu_count ,
81+ expected_threads_per_core ,
82+ expected_cpus_list ):
2283 expected_cpu_topology = {
23- "CPU(s)" : "1" ,
24- "On-line CPU(s) list" : "0" ,
25- "Thread(s) per core" : "1" ,
26- "Core(s) per socket" : "1" ,
84+ "CPU(s)" : str (expected_cpu_count ),
85+ "On-line CPU(s) list" : expected_cpus_list ,
86+ "Thread(s) per core" : str (expected_threads_per_core ),
87+ "Core(s) per socket" : str (
88+ int (expected_cpu_count / expected_threads_per_core )),
2789 "Socket(s)" : "1" ,
2890 "NUMA node(s)" : "1"
2991 }
30- _check_cpu_topology (test_microvm , expected_cpu_topology )
92+
93+ _check_guest_cmd_output (test_microvm , "lscpu" , None , ':' ,
94+ expected_cpu_topology )
95+
96+
97+ def _check_cpu_features (test_microvm , expected_cpu_count , expected_htt ):
98+ expected_cpu_features = {
99+ "cpu count" : '{} ({})' .format (hex (expected_cpu_count ),
100+ expected_cpu_count ),
101+ "CLFLUSH line size" : "0x8 (8)" ,
102+ "hypervisor guest status" : "true" ,
103+ "hyper-threading / multi-core supported" : expected_htt
104+ }
105+
106+ _check_guest_cmd_output (test_microvm , "cpuid -1" , None , '=' ,
107+ expected_cpu_features )
108+
109+
110+ def _check_cache_topology (test_microvm , num_vcpus_on_lvl_1_cache ,
111+ num_vcpus_on_lvl_3_cache ):
112+ expected_lvl_1_str = '{} ({})' .format (hex (num_vcpus_on_lvl_1_cache ),
113+ num_vcpus_on_lvl_1_cache )
114+ expected_lvl_3_str = '{} ({})' .format (hex (num_vcpus_on_lvl_3_cache ),
115+ num_vcpus_on_lvl_3_cache )
116+
117+ cpu_vendor = _get_cpu_vendor ()
118+ if cpu_vendor == CpuVendor .AMD :
119+ expected_level_1_topology = {
120+ "level" : '0x1 (1)' ,
121+ "extra cores sharing this cache" : expected_lvl_1_str
122+ }
123+ expected_level_3_topology = {
124+ "level" : '0x3 (3)' ,
125+ "extra cores sharing this cache" : expected_lvl_3_str
126+ }
127+ elif cpu_vendor == CpuVendor .INTEL :
128+ expected_level_1_topology = {
129+ "cache level" : '0x1 (1)' ,
130+ "extra threads sharing this cache" : expected_lvl_1_str ,
131+ }
132+ expected_level_3_topology = {
133+ "cache level" : '0x3 (3)' ,
134+ "extra threads sharing this cache" : expected_lvl_3_str ,
135+ }
136+
137+ _check_guest_cmd_output (test_microvm , "cpuid -1" , "--- cache 0 ---" , '=' ,
138+ expected_level_1_topology )
139+ _check_guest_cmd_output (test_microvm , "cpuid -1" , "--- cache 1 ---" , '=' ,
140+ expected_level_1_topology )
141+ _check_guest_cmd_output (test_microvm , "cpuid -1" , "--- cache 2 ---" , '=' ,
142+ expected_level_1_topology )
143+ _check_guest_cmd_output (test_microvm , "cpuid -1" , "--- cache 3 ---" , '=' ,
144+ expected_level_3_topology )
145+
146+
147+ def test_1vcpu_ht_disabled (test_microvm_with_ssh , network_config ):
148+ """Check the CPUID for a microvm with the specified config."""
149+ test_microvm_with_ssh .spawn ()
150+ test_microvm_with_ssh .basic_config (vcpu_count = 1 , ht_enabled = False )
151+ _tap , _ , _ = test_microvm_with_ssh .ssh_network_config (network_config , '1' )
152+ test_microvm_with_ssh .start ()
153+
154+ _check_cpu_topology (test_microvm_with_ssh , 1 , 1 , "0" )
155+ _check_cpu_features (test_microvm_with_ssh , 1 , "false" )
156+ _check_cache_topology (test_microvm_with_ssh , 0 , 0 )
157+
158+
159+ def test_1vcpu_ht_enabled (test_microvm_with_ssh , network_config ):
160+ """Check the CPUID for a microvm with the specified config."""
161+ test_microvm_with_ssh .spawn ()
162+ test_microvm_with_ssh .basic_config (vcpu_count = 1 , ht_enabled = True )
163+ _tap , _ , _ = test_microvm_with_ssh .ssh_network_config (network_config , '1' )
164+ test_microvm_with_ssh .start ()
165+
166+ _check_cpu_topology (test_microvm_with_ssh , 1 , 1 , "0" )
167+ _check_cpu_features (test_microvm_with_ssh , 1 , "false" )
168+ _check_cache_topology (test_microvm_with_ssh , 0 , 0 )
31169
32170
33171def test_2vcpu_ht_disabled (test_microvm_with_ssh , network_config ):
34- """Test CPU feature emulation with 2 vCPUs, and no hyperthreading."""
35- test_microvm = test_microvm_with_ssh
36- test_microvm .spawn ()
172+ """Check the CPUID for a microvm with the specified config."""
173+ test_microvm_with_ssh .spawn ()
174+ test_microvm_with_ssh .basic_config (vcpu_count = 2 , ht_enabled = False )
175+ _tap , _ , _ = test_microvm_with_ssh .ssh_network_config (network_config , '1' )
176+ test_microvm_with_ssh .start ()
37177
38- # Set up the microVM with 2 vCPUs, 256 MiB of RAM, 0 network ifaces, and
39- # a root file system with the rw permission. The network interfaces is
40- # added after we get a unique MAC and IP.
41- test_microvm .basic_config (vcpu_count = 2 , ht_enabled = False )
178+ _check_cpu_topology (test_microvm_with_ssh , 2 , 1 , "0,1" )
179+ _check_cpu_features (test_microvm_with_ssh , 2 , "true" )
180+ _check_cache_topology (test_microvm_with_ssh , 0 , 1 )
42181
43- _tap , _ , _ = test_microvm .ssh_network_config (network_config , '1' )
44182
45- test_microvm .start ()
183+ def test_2vcpu_ht_enabled (test_microvm_with_ssh , network_config ):
184+ """Check the CPUID for a microvm with the specified config."""
185+ test_microvm_with_ssh .spawn ()
186+ test_microvm_with_ssh .basic_config (vcpu_count = 2 , ht_enabled = True )
187+ _tap , _ , _ = test_microvm_with_ssh .ssh_network_config (network_config , '1' )
188+ test_microvm_with_ssh .start ()
46189
47- expected_cpu_topology = {
48- "CPU(s)" : "2" ,
49- "On-line CPU(s) list" : "0,1" ,
50- "Thread(s) per core" : "1" ,
51- "Core(s) per socket" : "2" ,
52- "Socket(s)" : "1" ,
53- "NUMA node(s)" : "1"
54- }
55- _check_cpu_topology (test_microvm , expected_cpu_topology )
190+ _check_cpu_topology (test_microvm_with_ssh , 2 , 2 , "0,1" )
191+ _check_cpu_features (test_microvm_with_ssh , 2 , "true" )
192+ _check_cache_topology (test_microvm_with_ssh , 1 , 1 )
56193
57194
58- def _check_cpu_topology (test_microvm , expected_cpu_topology ):
59- """Perform common microvm setup for different CPU topology tests.
195+ def test_16vcpu_ht_disabled (test_microvm_with_ssh , network_config ):
196+ """Check the CPUID for a microvm with the specified config."""
197+ test_microvm_with_ssh .spawn ()
198+ test_microvm_with_ssh .basic_config (vcpu_count = 16 , ht_enabled = False )
199+ _tap , _ , _ = test_microvm_with_ssh .ssh_network_config (network_config , '1' )
200+ test_microvm_with_ssh .start ()
60201
61- This is a wrapper function for calling lscpu and checking if the
62- command returns the expected cpu topology.
63- """
64- ssh_connection = net_tools .SSHConnection (test_microvm .ssh_config )
202+ _check_cpu_topology (test_microvm_with_ssh , 16 , 1 , "0-15" )
203+ _check_cpu_features (test_microvm_with_ssh , 16 , "true" )
204+ _check_cache_topology (test_microvm_with_ssh , 0 , 15 )
65205
66- # Execute the lscpu command to check the guest topology
67- _ , stdout , stderr = ssh_connection .execute_command ("lscpu" )
68- assert stderr .read ().decode ("utf-8" ) == ''
69- # Read the stdout of lscpu line by line to check the relevant information.
70- while True :
71- line = stdout .readline ().decode ('utf-8' )
72- if line != '' :
73- [key , value ] = list (map (lambda x : x .strip (), line .split (':' )))
74- if key in expected_cpu_topology .keys ():
75- assert value == expected_cpu_topology [key ],\
76- "%s does not have the expected value" % key
77- else :
78- break
206+
207+ def test_16vcpu_ht_enabled (test_microvm_with_ssh , network_config ):
208+ """Check the CPUID for a microvm with the specified config."""
209+ test_microvm_with_ssh .spawn ()
210+ test_microvm_with_ssh .basic_config (vcpu_count = 16 , ht_enabled = True )
211+ _tap , _ , _ = test_microvm_with_ssh .ssh_network_config (network_config , '1' )
212+ test_microvm_with_ssh .start ()
213+
214+ _check_cpu_topology (test_microvm_with_ssh , 16 , 2 , "0-15" )
215+ _check_cpu_features (test_microvm_with_ssh , 16 , "true" )
216+ _check_cache_topology (test_microvm_with_ssh , 1 , 15 )
79217
80218
81219def test_brand_string (test_microvm_with_ssh , network_config ):
@@ -85,24 +223,21 @@ def test_brand_string(test_microvm_with_ssh, network_config):
85223 Intel(R) Xeon(R) Processor @ {host frequency}
86224 where {host frequency} is the frequency reported by the host CPUID
87225 (e.g. 4.01GHz)
88- * For non-Intel CPUs, the guest brand string should be:
89- Intel(R) Xeon(R) Processor
226+ * For AMD CPUs, the guest brand string should be:
227+ AMD EPYC
228+ * For other CPUs, the guest brand string should be:
229+ ""
90230 """
91231 cif = open ('/proc/cpuinfo' , 'r' )
92232 host_brand_string = None
93- host_vendor_id = None
94233 while True :
95234 line = cif .readline ()
96235 if line == '' :
97236 break
98- mo = re .search ("^vendor_id\\ s+:\\ s+(.+)$" , line )
99- if mo :
100- host_vendor_id = mo .group (1 )
101237 mo = re .search ("^model name\\ s+:\\ s+(.+)$" , line )
102238 if mo :
103239 host_brand_string = mo .group (1 )
104240 cif .close ()
105- assert host_vendor_id is not None
106241 assert host_brand_string is not None
107242
108243 test_microvm = test_microvm_with_ssh
@@ -124,13 +259,14 @@ def test_brand_string(test_microvm_with_ssh, network_config):
124259 guest_brand_string = mo .group (1 )
125260 assert guest_brand_string
126261
262+ cpu_vendor = _get_cpu_vendor ()
127263 expected_guest_brand_string = ""
128- if host_vendor_id == "GenuineIntel" :
264+ if cpu_vendor == CpuVendor .AMD :
265+ expected_guest_brand_string += "AMD EPYC"
266+ elif cpu_vendor == CpuVendor .INTEL :
129267 expected_guest_brand_string = "Intel(R) Xeon(R) Processor"
130268 mo = re .search ("[.0-9]+[MG]Hz" , host_brand_string )
131269 if mo :
132270 expected_guest_brand_string += " @ " + mo .group (0 )
133- elif host_vendor_id == "AuthenticAMD" :
134- expected_guest_brand_string += "AMD EPYC"
135271
136272 assert guest_brand_string == expected_guest_brand_string
0 commit comments