Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Implement python-dmidecode/dmidecode as alternative for kernel DMI #2479

Closed
wants to merge 1 commit into from

4 participants

@dagwieers

This implementation falls back to python-dmidecode (RHEL5.5+) if the kernel as no DMI support. Alternatively, if python-dmidecode is missing, we attempt to use the dmidecode binary (for RHEL5.4 and older) before giving up.

This fixes #376 and #1657 and also helps @lwade on RHEL5.5+.

@haskjold

Hi

Two typos: missing , at EOL line 462 and line 490. Also at least for Dell servers some of the DMI_DICT XML paths are different:

@@ -459,7 +459,7 @@
 
             DMI_DICT = dict(
                 bios_date       = '/sys/devices/virtual/dmi/id/bios_date',
-                bios_version    = '/sys/devices/virtual/dmi/id/bios_version'
+                bios_version    = '/sys/devices/virtual/dmi/id/bios_version',
                 form_factor     = '/sys/devices/virtual/dmi/id/chassis_type',
                 product_name    = '/sys/devices/virtual/dmi/id/product_name',
                 product_serial  = '/sys/devices/virtual/dmi/id/product_serial',
@@ -486,9 +486,9 @@
                 import dmidecode
 
                 DMI_DICT = dict(
-                    bios_date       = '/dmidecode/BiosInfo/ReleaseDate',
-                    bios_version    = '/dmidecode/BiosInfo/BiosRevision'
-                    form_factor     = '/dmidecode/ChassisInfo/Type',
+                    bios_date       = '/dmidecode/BIOSinfo/ReleaseDate',
+                    bios_version    = '/dmidecode/BIOSinfo/BIOSrevision',
+                    form_factor     = '/dmidecode/ChassisInfo/ChassisType',
                     product_name    = '/dmidecode/SystemInfo/ProductName',
                     product_serial  = '/dmidecode/SystemInfo/SerialNumber',
                     product_uuid    = '/dmidecode/SystemInfo/SystemUUID',
@dagwieers

Thanks, I ordered the entries alphabetically before pushing late at night, and contrary to what I always do, the last line did not have a trailing comma.

@mpdehaan
Collaborator

the unguarded try/except really should be

except ImportError

so it doesn't eat other exceptions.

@mpdehaan
Collaborator

also question, are you intending to fix the FIXME?

# FIXME: Fall back to using dmidecode, if available

@dagwieers

I plan to implement dmidecode-binary mode at some point, can't say when though.

PS try/except fixed, thanks !

@mpdehaan
Collaborator

Seems instead of NA it should use Python None too, and the try/except should guard only the import statement, and do something like HAS_DMIDECODE=True and then use that conditionally.

Imports should occur at the top of the file.

@dagwieers

Yes, None makes more sense, however the original code returned NA. I will look into it.

Why should imports always occur at the top of the file ?

@mpdehaan
Collaborator

It's just a python convention thing, that way you can look at the top and see what the module is likely to require (or if any modules aren't being used)

NA actually makes sense I think, or maybe just omitting the facts would be ok too (since they are not available, almost as if it were another platform)

@bcoca
Collaborator
@mpdehaan
Collaborator

Yep, breaking things up into some smaller functions would also be good... I understand this is not your problem for it already being like that, but it makes the code easier to understand/organize.

@mpdehaan
Collaborator

@dagwieers

Any update on this one?

Code organization is not so important but imports at the top and guarding them should be done.

I also am not sure we want two ways to do it, so would we just require python-dmidecode to get DMI info?

Trying to control length/redundancy of the setup module, but perhaps it's not worth fighting.

Opinions welcome.

@dagwieers

I'll look at it today. Prefer to see it merged at some point ;-)

@dagwieers

Ok, found some time to improve the code. We now import at the top, but only conditionally (since we prefer the kernel DMI support over python-dmidecode and using subprocess/dmidecode as a last resort).

@dagwieers dagwieers Implement python-dmidecode/dmidecode as alternative for kernel DMI
This implementation falls back to python-dmidecode (RHEL5.5+) if the kernel as no DMI support. Alternatively, if python-dmidecode is missing, we attempt to use the dmidecode binary (for RHEL5.4 and older) before giving up.

This fixes #376 and #1657 and also helps @lwade on RHEL5.5+.
4135cff
@mpdehaan
Collaborator

merged in and pushing shortly, thank you!

@mpdehaan mpdehaan closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 24, 2013
  1. @dagwieers

    Implement python-dmidecode/dmidecode as alternative for kernel DMI

    dagwieers authored
    This implementation falls back to python-dmidecode (RHEL5.5+) if the kernel as no DMI support. Alternatively, if python-dmidecode is missing, we attempt to use the dmidecode binary (for RHEL5.4 and older) before giving up.
    
    This fixes #376 and #1657 and also helps @lwade on RHEL5.5+.
This page is out of date. Refresh to see the latest.
Showing with 91 additions and 31 deletions.
  1. +91 −31 library/setup
View
122 library/setup
@@ -18,6 +18,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+import os
import array
import fcntl
import fnmatch
@@ -29,6 +30,12 @@ import struct
import datetime
import getpass
+if not os.path.exists('/sys/devices/virtual/dmi/id/product_name'):
+ try:
+ import dmidecode
+ except ImportError:
+ import subprocess
+
DOCUMENTATION = '''
---
module: setup
@@ -390,26 +397,6 @@ class LinuxHardware(Hardware):
platform = 'Linux'
MEMORY_FACTS = ['MemTotal', 'SwapTotal', 'MemFree', 'SwapFree']
- # DMI bits
- DMI_DICT = dict(
- form_factor = '/sys/devices/virtual/dmi/id/chassis_type',
- product_name = '/sys/devices/virtual/dmi/id/product_name',
- product_serial = '/sys/devices/virtual/dmi/id/product_serial',
- product_uuid = '/sys/devices/virtual/dmi/id/product_uuid',
- product_version = '/sys/devices/virtual/dmi/id/product_version',
- system_vendor = '/sys/devices/virtual/dmi/id/sys_vendor',
- bios_date = '/sys/devices/virtual/dmi/id/bios_date',
- bios_version = '/sys/devices/virtual/dmi/id/bios_version'
- )
- # DMI SPEC -- http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.0.pdf
- FORM_FACTOR = [ "Unknown", "Other", "Unknown", "Desktop",
- "Low Profile Desktop", "Pizza Box", "Mini Tower", "Tower",
- "Portable", "Laptop", "Notebook", "Hand Held", "Docking Station",
- "All In One", "Sub Notebook", "Space-saving", "Lunch Box",
- "Main Server Chassis", "Expansion Chassis", "Sub Chassis",
- "Bus Expansion Chassis", "Peripheral Chassis", "RAID Chassis",
- "Rack Mount Chassis", "Sealed-case PC", "Multi-system",
- "CompactPCI", "AdvancedTCA", "Blade" ]
def __init__(self):
Hardware.__init__(self)
@@ -463,18 +450,91 @@ class LinuxHardware(Hardware):
self.facts['processor_cores'] = 'NA'
def get_dmi_facts(self):
- for (key,path) in LinuxHardware.DMI_DICT.items():
- data = get_file_content(path)
- if data is not None:
- if key == 'form_factor':
- try:
- self.facts['form_factor'] = LinuxHardware.FORM_FACTOR[int(data)]
- except IndexError, e:
- self.facts['form_factor'] = 'unknown (%s)' % data
+
+ def execute(cmd):
+ p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (out, err) = p.communicate()
+ if p.returncode or err:
+ return None
+ return out.rstrip()
+
+ if os.path.exists('/sys/devices/virtual/dmi/id/product_name'):
+ # Use kernel DMI info, if available
+
+ # DMI SPEC -- http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.0.pdf
+ FORM_FACTOR = [ "Unknown", "Other", "Unknown", "Desktop",
+ "Low Profile Desktop", "Pizza Box", "Mini Tower", "Tower",
+ "Portable", "Laptop", "Notebook", "Hand Held", "Docking Station",
+ "All In One", "Sub Notebook", "Space-saving", "Lunch Box",
+ "Main Server Chassis", "Expansion Chassis", "Sub Chassis",
+ "Bus Expansion Chassis", "Peripheral Chassis", "RAID Chassis",
+ "Rack Mount Chassis", "Sealed-case PC", "Multi-system",
+ "CompactPCI", "AdvancedTCA", "Blade" ]
+
+ DMI_DICT = dict(
+ bios_date = '/sys/devices/virtual/dmi/id/bios_date',
+ bios_version = '/sys/devices/virtual/dmi/id/bios_version',
+ form_factor = '/sys/devices/virtual/dmi/id/chassis_type',
+ product_name = '/sys/devices/virtual/dmi/id/product_name',
+ product_serial = '/sys/devices/virtual/dmi/id/product_serial',
+ product_uuid = '/sys/devices/virtual/dmi/id/product_uuid',
+ product_version = '/sys/devices/virtual/dmi/id/product_version',
+ system_vendor = '/sys/devices/virtual/dmi/id/sys_vendor',
+ )
+
+ for (key,path) in DMI_DICT.items():
+ data = get_file_content(path)
+ if data is not None:
+ if key == 'form_factor':
+ try:
+ self.facts['form_factor'] = FORM_FACTOR[int(data)]
+ except IndexError, e:
+ self.facts['form_factor'] = 'unknown (%s)' % data
+ else:
+ self.facts[key] = data
else:
- self.facts[key] = data
- else:
- self.facts[key] = 'NA'
+ self.facts[key] = 'NA'
+
+ elif 'dmidecode' in sys.modules.keys():
+ # Use python dmidecode, if available
+
+ DMI_DICT = dict(
+ bios_date = '/dmidecode/BIOSinfo/ReleaseDate',
+ bios_version = '/dmidecode/BIOSinfo/BIOSrevision',
+ form_factor = '/dmidecode/ChassisInfo/ChassisType',
+ product_name = '/dmidecode/SystemInfo/ProductName',
+ product_serial = '/dmidecode/SystemInfo/SerialNumber',
+ product_uuid = '/dmidecode/SystemInfo/SystemUUID',
+ product_version = '/dmidecode/SystemInfo/Version',
+ system_vendor = '/dmidecode/SystemInfo/Manufacturer',
+ )
+
+ dmixml = dmidecode.dmidecodeXML()
+ dmixml.SetResultType(dmidecode.DMIXML_DOC)
+ xmldoc = dmixml.QuerySection('all')
+ dmixp = xmldoc.xpathNewContext()
+
+ for (key,path) in DMI_DICT.items():
+ try:
+ data = dmixp.xpathEval(path)
+ if len(data) > 0:
+ self.facts[key] = data[0].get_content()
+ else:
+ self.facts[key] = 'Error'
+ except:
+ self.facts[key] = 'NA'
+
+ else:
+ # Fall back to using dmidecode, if available
+
+ self.facts['bios_date'] = execute('dmidecode -s bios-release-date') or 'NA'
+ self.facts['bios_version'] = execute('dmidecode -s bios-version') or 'NA'
+ self.facts['form_factor'] = execute('dmidecode -s chassis-type') or 'NA'
+ self.facts['product_name'] = execute('dmidecode -s system-product-name') or 'NA'
+ self.facts['product_serial'] = execute('dmidecode -s system-serial-number') or 'NA'
+ self.facts['product_uuid'] = execute('dmidecode -s system-uuid') or 'NA'
+ self.facts['product_version'] = execute('dmidecode -s system-version') or 'NA'
+ self.facts['system_vendor'] = execute('dmidecode -s system-manufacturer') or 'NA'
def get_mount_facts(self):
self.facts['mounts'] = []
Something went wrong with that request. Please try again.