Merge lp:~ack/landscape-client/detect-openstack-vm into lp:~landscape/landscape-client/trunk

Proposed by Alberto Donato
Status: Merged
Approved by: Alberto Donato
Approved revision: 700
Merged at revision: 695
Proposed branch: lp:~ack/landscape-client/detect-openstack-vm
Merge into: lp:~landscape/landscape-client/trunk
Diff against target: 348 lines (+131/-114)
2 files modified
landscape/lib/tests/test_vm_info.py (+106/-91)
landscape/lib/vm_info.py (+25/-23)
To merge this branch: bzr merge lp:~ack/landscape-client/detect-openstack-vm
Reviewer Review Type Date Requested Status
Geoff Teale (community) Approve
Björn Tillenius (community) Approve
Review via email: mp+171318@code.launchpad.net

Commit message

This changes the detection logic for kvm virtual machines, looking for the "Bochs" as reported by OpenStack Folsom) or "OpenStack" (as reported by Grizzly) vendor in /sys rather than the CPU model.
It also refactors a bit the existing logic, and only tries to detect HyperV/VmWare/KVM if the "hypervisor" flag is present in /proc/cpuinfo, which is meant to report that the machine is running under an hypervisor.

Description of the change

This changes the detection logic for kvm virtual machines, looking for the "Bochs" as reported by OpenStack Folsom) or "OpenStack" (as reported by Grizzly) vendor in /sys rather than the CPU model.
It also refactors a bit the existing logic, and only tries to detect HyperV/VmWare/KVM if the "hypervisor" flag is present in /proc/cpuinfo, which is meant to report that the machine is running under an hypervisor.

To post a comment you must log in.
Revision history for this message
Björn Tillenius (bjornt) wrote :

Looks good! I have some nitpicks only.

[1]

Have you confirmed that this works on lucid and precise?

[2]

+ if line.startswith("flags") and "hypervisor" in line:
+ has_hypervisor_flag = True
+ break

Can multiple lines start with flags? If not, it might be better to
write it as:

    if line.startswith("flags"):
        has_hypervisor_flag = "hypervisor" in line
        break

[3]

+ if has_hypervisor_flag:
+ sys_vendor_path = join_root_path("sys/class/dmi/id/sys_vendor")
+ if os.path.exists(sys_vendor_path):
+ content = read_file(sys_vendor_path)
+ if "VMware, Inc." in content:

I tend to prefer early returns. I.e. at the top, return "" if there is
not hypervisor flag. That way you don't have to indent the main logic.

review: Approve
Revision history for this message
Alberto Donato (ack) wrote :

> Looks good! I have some nitpicks only.
>
> [1]
>
> Have you confirmed that this works on lucid and precise?
>

Yeah, I tried it on Canonistack.

>
> [2]
>
> + if line.startswith("flags") and "hypervisor" in line:
> + has_hypervisor_flag = True
> + break
>
> Can multiple lines start with flags? If not, it might be better to
> write it as:
>
> if line.startswith("flags"):
> has_hypervisor_flag = "hypervisor" in line
> break

There might actually be more than one "flags" line, it's one for each stanza in /proc/cpuinfo (so one for each CPU core/thread).

>
> [3]
>

Fixed.

Revision history for this message
Geoff Teale (tealeg) wrote :

+1 Looks good.

[1] An empty what exactly? I think you the word "string". ;-)

+ L{get_vm_info} should return an empty when the sys_vendor is unknown.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'landscape/lib/tests/test_vm_info.py'
2--- landscape/lib/tests/test_vm_info.py 2012-12-06 10:16:31 +0000
3+++ landscape/lib/tests/test_vm_info.py 2013-06-28 08:49:23 +0000
4@@ -7,7 +7,7 @@
5
6 class VMInfoTest(LandscapeTest):
7
8- sample_kvm_cpuinfo = """
9+ sample_cpuinfo = """
10 processor : 0
11 vendor_id : GenuineIntel
12 cpu family : 6
13@@ -20,7 +20,7 @@
14 fpu_exception : yes
15 cpuid level : 4
16 wp : yes
17-flags : fpu de pse tsc msr pae mce cx8 apic sep mtrr pge mca
18+flags : {flags}
19 bogomips : 5306.22
20 clflush size : 64
21 cache_alignment : 64
22@@ -28,146 +28,161 @@
23 power management:
24 """
25
26+ def setUp(self):
27+ super(VMInfoTest, self).setUp()
28+ self.root_path = self.makeDir()
29+ self.proc_path = self.makeDir(
30+ path=os.path.join(self.root_path, "proc"))
31+ self.sys_path = self.makeDir(path=os.path.join(self.root_path, "sys"))
32+ self.proc_sys_path = self.makeDir(
33+ path=os.path.join(self.proc_path, "sys"))
34+
35+ def make_cpuinfo(self, flags=""):
36+ """Build a sample /proc/cpuinfo with specified processor flags."""
37+ cpuinfo_path = os.path.join(self.root_path, "proc/cpuinfo")
38+ self.makeFile(
39+ path=cpuinfo_path, content=self.sample_cpuinfo.format(flags=flags))
40+
41 def test_get_vm_info_empty_when_no_virtualization_is_found(self):
42 """
43 L{get_vm_info} should be empty when there's no virtualisation.
44 """
45- root_path = self.makeDir()
46- self.assertEqual(u"", get_vm_info(root_path=root_path))
47+ self.assertEqual(u"", get_vm_info(root_path=self.root_path))
48
49 def test_get_vm_info_is_openvz_when_proc_vz_exists(self):
50 """
51 L{get_vm_info} should return 'openvz' when /proc/vz exists.
52 """
53- root_path = self.makeDir()
54- proc_path = os.path.join(root_path, "proc")
55- self.makeDir(path=proc_path)
56-
57- proc_vz_path = os.path.join(proc_path, "vz")
58+ proc_vz_path = os.path.join(self.proc_path, "vz")
59 self.makeFile(path=proc_vz_path, content="foo")
60
61- self.assertEqual("openvz", get_vm_info(root_path=root_path))
62+ self.assertEqual("openvz", get_vm_info(root_path=self.root_path))
63
64 def test_get_vm_info_is_xen_when_proc_sys_xen_exists(self):
65 """
66 L{get_vm_info} should return 'xen' when /proc/sys/xen exists.
67 """
68- root_path = self.makeDir()
69- proc_path = os.path.join(root_path, "proc")
70- self.makeDir(path=proc_path)
71-
72- proc_sys_path = os.path.join(proc_path, "sys")
73- self.makeDir(path=proc_sys_path)
74-
75- proc_sys_xen_path = os.path.join(proc_sys_path, "xen")
76+ proc_sys_xen_path = os.path.join(self.proc_sys_path, "xen")
77 self.makeFile(path=proc_sys_xen_path, content="foo")
78
79- self.assertEqual("xen", get_vm_info(root_path=root_path))
80+ self.assertEqual("xen", get_vm_info(root_path=self.root_path))
81
82 def test_get_vm_info_is_xen_when_sys_bus_xen_is_non_empty(self):
83 """
84 L{get_vm_info} should return 'xen' when /sys/bus/xen exists and has
85 devices.
86 """
87- root_path = self.makeDir()
88- sys_path = os.path.join(root_path, "sys")
89- self.makeDir(path=sys_path)
90-
91- sys_bus_path = os.path.join(sys_path, "bus")
92- self.makeDir(path=sys_bus_path)
93-
94- sys_bus_xen_path = os.path.join(sys_bus_path, "xen")
95- self.makeDir(path=sys_bus_xen_path)
96-
97- devices_xen_path = os.path.join(sys_bus_xen_path, "devices")
98+ devices_xen_path = os.path.join(self.sys_path, "bus/xen/devices")
99 self.makeDir(path=devices_xen_path)
100-
101 foo_devices_path = os.path.join(devices_xen_path, "foo")
102 self.makeFile(path=foo_devices_path, content="bar")
103
104- self.assertEqual("xen", get_vm_info(root_path=root_path))
105+ self.assertEqual("xen", get_vm_info(root_path=self.root_path))
106
107 def test_get_vm_info_is_xen_when_proc_xen_exists(self):
108 """
109 L{get_vm_info} should return 'xen' when /proc/xen exists.
110 """
111- root_path = self.makeDir()
112- proc_path = os.path.join(root_path, "proc")
113- self.makeDir(path=proc_path)
114-
115- proc_xen_path = os.path.join(proc_path, "xen")
116+ proc_xen_path = os.path.join(self.proc_path, "xen")
117 self.makeFile(path=proc_xen_path, content="foo")
118
119- self.assertEqual("xen", get_vm_info(root_path=root_path))
120-
121- def test_get_vminfo_is_kvm_when_qemu_is_found_in_proc_cpuinfo(self):
122- """
123- L{get_vm_info} should return 'kvm' when QEMU Virtual CPU is found in
124- /proc/cpuinfo.
125- """
126- root_path = self.makeDir()
127- proc_path = os.path.join(root_path, "proc")
128- self.makeDir(path=proc_path)
129-
130- cpuinfo_path = os.path.join(proc_path, "cpuinfo")
131- self.makeFile(path=cpuinfo_path, content=self.sample_kvm_cpuinfo)
132-
133- self.assertEqual("kvm", get_vm_info(root_path=root_path))
134-
135- def test_get_vm_info_is_empty_when_qemu_is_not_found_in_proc_cpuinfo(self):
136- """
137- L{get_vm_info} should have an empty string when QEMU Virtual CPU is not
138- found in /proc/cpuinfo.
139- """
140- root_path = self.makeDir()
141- proc_path = os.path.join(root_path, "proc")
142- self.makeDir(path=proc_path)
143-
144- cpuinfo_path = os.path.join(proc_path, "cpuinfo")
145- self.makeFile(path=cpuinfo_path, content="foo")
146- self.assertEqual("", get_vm_info(root_path=root_path))
147+ self.assertEqual("xen", get_vm_info(root_path=self.root_path))
148+
149+ def test_get_vm_info_is_empty_when_no_hypervisor_in_proc_cpuinfo(self):
150+ """
151+ L{get_vm_info} returns an empty string when the "hypervisor" flag is
152+ not found in found in /proc/cpuinfo.
153+ """
154+ self.make_cpuinfo(flags="fpu vme")
155+
156+ # The content of sys_vendor is not checked if the "hypervisor" flag is
157+ # not present.
158+ dmi_path = os.path.join(self.root_path, "sys/class/dmi/id")
159+ self.makeDir(path=dmi_path)
160+ self.makeFile(
161+ path=os.path.join(dmi_path, "sys_vendor"), content="Bochs")
162+
163+ self.assertEqual("", get_vm_info(root_path=self.root_path))
164+
165+ def test_get_vm_info_with_bochs_sys_vendor(self):
166+ """
167+ L{get_vm_info} should return "kvm" when we detect the sys_vendor is
168+ Bochs.
169+ """
170+ self.make_cpuinfo(flags="fpu hypervisor vme")
171+
172+ dmi_path = os.path.join(self.root_path, "sys/class/dmi/id")
173+ self.makeDir(path=dmi_path)
174+ self.makeFile(
175+ path=os.path.join(dmi_path, "sys_vendor"), content="Bochs")
176+
177+ self.assertEqual("kvm", get_vm_info(root_path=self.root_path))
178+
179+ def test_get_vm_info_with_openstack_sys_vendor(self):
180+ """
181+ L{get_vm_info} should return "kvm" when we detect the sys_vendor is
182+ Openstack.
183+ """
184+ self.make_cpuinfo(flags="fpu hypervisor vme")
185+
186+ dmi_path = os.path.join(self.root_path, "sys/class/dmi/id")
187+ self.makeDir(path=dmi_path)
188+ self.makeFile(
189+ path=os.path.join(dmi_path, "sys_vendor"),
190+ content="OpenStack Foundation")
191+
192+ self.assertEqual("kvm", get_vm_info(root_path=self.root_path))
193
194 def test_get_vm_info_with_vmware_sys_vendor(self):
195 """
196 L{get_vm_info} should return "vmware" when we detect the sys_vendor is
197 VMware Inc.
198 """
199- root_path = self.makeDir()
200- dmi_path = os.path.join(root_path, "sys", "class", "dmi", "id")
201- os.makedirs(dmi_path)
202- fd = file(os.path.join(dmi_path, "sys_vendor"), "w")
203- fd.write("VMware, Inc.")
204- fd.close()
205- self.assertEqual("vmware", get_vm_info(root_path=root_path))
206+ self.make_cpuinfo(flags="fpu hypervisor vme")
207+
208+ dmi_path = os.path.join(self.root_path, "sys/class/dmi/id")
209+ self.makeDir(path=dmi_path)
210+ self.makeFile(
211+ path=os.path.join(dmi_path, "sys_vendor"), content="VMware, Inc.")
212+
213+ self.assertEqual("vmware", get_vm_info(root_path=self.root_path))
214
215 def test_get_vm_info_is_empty_without_xen_devices(self):
216 """
217 L{get_vm_info} returns an empty string if the /sys/bus/xen/devices
218 directory exists and but doesn't contain any file.
219 """
220- root_path = self.makeDir()
221- sys_path = os.path.join(root_path, "sys")
222- self.makeDir(path=sys_path)
223-
224- sys_bus_path = os.path.join(sys_path, "bus")
225- self.makeDir(path=sys_bus_path)
226-
227- sys_bus_xen_path = os.path.join(sys_bus_path, "xen")
228- self.makeDir(path=sys_bus_xen_path)
229-
230- devices_xen_path = os.path.join(sys_bus_xen_path, "devices")
231+ self.make_cpuinfo(flags="fpu hypervisor vme")
232+
233+ devices_xen_path = os.path.join(self.sys_path, "bus/xen/devices")
234 self.makeDir(path=devices_xen_path)
235
236- self.assertEqual("", get_vm_info(root_path=root_path))
237+ self.assertEqual("", get_vm_info(root_path=self.root_path))
238
239 def test_get_vm_info_with_microsoft_sys_vendor(self):
240 """
241 L{get_vm_info} returns "hyperv" if the sys_vendor is Microsoft.
242 """
243- root_path = self.makeDir()
244- dmi_path = os.path.join(root_path, "sys", "class", "dmi", "id")
245- os.makedirs(dmi_path)
246- fd = file(os.path.join(dmi_path, "sys_vendor"), "w")
247- fd.write("Microsoft Corporation")
248- fd.close()
249- self.assertEqual("hyperv", get_vm_info(root_path=root_path))
250+ self.make_cpuinfo(flags="fpu hypervisor vme")
251+
252+ dmi_path = os.path.join(self.root_path, "sys/class/dmi/id")
253+ self.makeDir(path=dmi_path)
254+ self.makeFile(
255+ path=os.path.join(dmi_path, "sys_vendor"),
256+ content="Microsoft Corporation")
257+ self.assertEqual("hyperv", get_vm_info(root_path=self.root_path))
258+
259+ def test_get_vm_info_with_other_vendor(self):
260+ """
261+ L{get_vm_info} should return an empty string when the sys_vendor is
262+ unknown.
263+ """
264+ self.make_cpuinfo(flags="fpu hypervisor vme")
265+
266+ dmi_path = os.path.join(self.root_path, "sys/class/dmi/id")
267+ self.makeDir(path=dmi_path)
268+ self.makeFile(
269+ path=os.path.join(dmi_path, "sys_vendor"),
270+ content="Some other vendor")
271+
272+ self.assertEqual("", get_vm_info(root_path=self.root_path))
273
274=== modified file 'landscape/lib/vm_info.py'
275--- landscape/lib/vm_info.py 2012-12-06 10:09:19 +0000
276+++ landscape/lib/vm_info.py 2013-06-28 08:49:23 +0000
277@@ -3,6 +3,8 @@
278 """
279 import os
280
281+from landscape.lib.fs import read_file
282+
283
284 def get_vm_info(root_path="/"):
285 """
286@@ -16,11 +18,10 @@
287
288 xen_paths = ["proc/sys/xen", "proc/xen"]
289 xen_paths = map(join_root_path, xen_paths)
290+ vz_path = join_root_path("proc/vz")
291
292- vz_path = os.path.join(root_path, "proc/vz")
293 if os.path.exists(vz_path):
294 return "openvz"
295-
296 elif filter(os.path.exists, xen_paths):
297 return "xen"
298
299@@ -30,27 +31,28 @@
300 if os.path.isdir(sys_xen_path) and os.listdir(sys_xen_path):
301 return "xen"
302
303- cpu_info_path = os.path.join(root_path, "proc/cpuinfo")
304+ has_hypervisor_flag = False
305+ cpu_info_path = join_root_path("proc/cpuinfo")
306 if os.path.exists(cpu_info_path):
307- try:
308- fd = open(cpu_info_path)
309- cpuinfo = fd.read()
310- if "QEMU Virtual CPU" in cpuinfo:
311- return "kvm"
312- finally:
313- fd.close()
314-
315- sys_vendor_path = os.path.join(root_path, "sys", "class", "dmi", "id",
316- "sys_vendor")
317- if os.path.exists(sys_vendor_path):
318- try:
319- fd = open(sys_vendor_path)
320- file_content = fd.read()
321- if "VMware, Inc." in file_content:
322- return "vmware"
323- elif "Microsoft Corporation" in file_content:
324- return "hyperv"
325- finally:
326- fd.close()
327+ content = read_file(cpu_info_path)
328+ for line in content.split("\n"):
329+ if line.startswith("flags") and "hypervisor" in line:
330+ has_hypervisor_flag = True
331+ break
332+
333+ if not has_hypervisor_flag:
334+ return ""
335+
336+ sys_vendor_path = join_root_path("sys/class/dmi/id/sys_vendor")
337+ if not os.path.exists(sys_vendor_path):
338+ return ""
339+
340+ content = read_file(sys_vendor_path)
341+ if "VMware, Inc." in content:
342+ return "vmware"
343+ elif "Microsoft Corporation" in content:
344+ return "hyperv"
345+ elif "Bochs" in content or "OpenStack" in content:
346+ return "kvm"
347
348 return ""

Subscribers

People subscribed via source and target branches

to all changes: