Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NAS-127416 / 24.10 / Initial commit #1

Merged
merged 1 commit into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/flake8.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: flake8

on:
pull_request:
types:
- 'synchronize'
- 'opened'

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.9
uses: actions/setup-python@v1
with:
python-version: 3.9
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8
- name: Run flake8
run: flake8 .
26 changes: 26 additions & 0 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: unit_tests

on:
pull_request:
types:
- 'synchronize'
- 'opened'

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.9
uses: actions/setup-python@v1
with:
python-version: 3.9
- name: Install pytest
run: |
python -m pip install --upgrade pip
pip install pytest
- name: Install package
run: python setup.py install
- name: Run tests
run: pytest -v tests
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.idea/*
build/*
dist/*
ixhardware.egg*
__pycache__
*.pyc
5 changes: 5 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ixhardware (1.0-0~truenas+1) electriceel-truenas-unstable; urgency=medium

* Initial release

-- Vladimir Vinogradenko <[email protected]> Tue, 20 Feb 2024 12:33:00 +0200
19 changes: 19 additions & 0 deletions debian/control
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Source: ixhardware
Section: contrib/python
Priority: optional
Maintainer: Vladimir Vinogradenko <[email protected]>
Build-Depends: debhelper-compat (= 12),
dh-python,
python3-dev,
python3-setuptools
Standards-Version: 4.4.1
Homepage: https://github.com/truenas/ixhardware
Testsuite: autopkgtest-pkg-python

Package: python3-ixhardware
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
${python3:Depends}
Description: Detect iXsystems hardware.
This package detects iXsystems hardware.
9 changes: 9 additions & 0 deletions debian/rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/make -f
export DH_VERBOSE = 1

export PYBUILD_NAME=ixhardware

%:
dh $@ --with python3 --buildsystem=pybuild

override_dh_auto_test:
1 change: 1 addition & 0 deletions debian/source/format
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.0 (quilt)
1 change: 1 addition & 0 deletions debian/source/options
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extend-diff-ignore = "^[^/]*[.]egg-info/"
4 changes: 4 additions & 0 deletions ixhardware/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .chassis import PLATFORM_PREFIXES, TRUENAS_UNKNOWN, get_chassis_hardware
from .dmi import DMIInfo, DMIParser, parse_dmi

__all__ = ["PLATFORM_PREFIXES", "TRUENAS_UNKNOWN", "get_chassis_hardware", "DMIInfo", "DMIParser", "parse_dmi"]
32 changes: 32 additions & 0 deletions ixhardware/chassis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from .dmi import DMIInfo

__all__ = ["PLATFORM_PREFIXES", "TRUENAS_UNKNOWN", "get_chassis_hardware"]


# We tag SMBIOS with relevant strings for each platform
# before we ship to customer. These are the various prefixes
# that represent each hardware platform.
# ("TRUENAS-X10", "TRUENAS-M50", "TRUENAS-MINI-X+", "FREENAS-MINI-X", etc)
PLATFORM_PREFIXES = (
"TRUENAS-Z", # z-series
"TRUENAS-X", # x-series
"TRUENAS-M", # m-series AND current mini platforms
"TRUENAS-F", # f-series (F60, F100, F130)
"TRUENAS-H", # h-series (H10, H20)
"TRUENAS-R", # freenas certified replacement
"FREENAS-MINI", # minis tagged with legacy information
)
TRUENAS_UNKNOWN = "TRUENAS-UNKNOWN"


def get_chassis_hardware(dmi: DMIInfo):
if dmi.system_product_name.startswith(PLATFORM_PREFIXES):
return dmi.system_product_name

if dmi.baseboard_product_name == "iXsystems TrueNAS X10":
# could be that production didn"t burn in the correct x-series
# model information so let"s check the motherboard model as a
# last resort
return "TRUENAS-X"

return TRUENAS_UNKNOWN
99 changes: 99 additions & 0 deletions ixhardware/dmi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from dataclasses import dataclass
from datetime import date, datetime
import logging
import subprocess
from typing import Optional

logger = logging.getLogger(__name__)

__all__ = ["DMIInfo", "DMIParser", "parse_dmi"]


@dataclass
class DMIInfo:
bios_release_date: Optional[date] = None
ecc_memory: bool = False
baseboard_manufacturer: str = ""
baseboard_product_name: str = ""
system_manufacturer: str = ""
system_product_name: str = ""
system_serial_number: str = ""
system_version: str = ""
has_ipmi: bool = False


class DMIParser:
command = ["dmidecode", "-t", "0,1,2,16,38"]

def parse(self, output: str) -> DMIInfo:
return self._parse_dmi(output.splitlines())

def _parse_dmi(self, lines: [str]) -> DMIInfo:
info = DMIInfo()

_type = None
for line in lines:
if "DMI type 0," in line:
_type = "RELEASE_DATE"
if "DMI type 1," in line:
_type = "SYSINFO"
if "DMI type 2," in line:
_type = "BBINFO"
if "DMI type 38," in line:
_type = "IPMI"

if not line or ":" not in line:
# "sections" are separated by the category name and then
# a newline so ignore those lines
continue

sect, val = [i.strip() for i in line.split(":", 1)]
if sect == "Release Date":
info.bios_release_date = self._parse_bios_release_date(val)
elif sect == "Manufacturer":
if _type == "SYSINFO":
info.system_manufacturer = val
else:
info.baseboard_manufacturer = val
elif sect == "Product Name":
if _type == "SYSINFO":
info.system_product_name = val
else:
info.baseboard_product_name = val
elif sect == "Serial Number" and _type == "SYSINFO":
info.system_serial_number = val
elif sect == "Version" and _type == "SYSINFO":
info.system_version = val
elif sect == "I2C Slave Address":
info.has_ipmi = True
elif sect == "Error Correction Type":
info.ecc_memory = "ECC" in val
# we break the for loop here since "16" is the last section
# that gets processed
break

return info

def _parse_bios_release_date(self, string):
parts = string.strip().split("/")
if len(parts) < 3:
# Don"t know what the BIOS is reporting so assume it"s invalid
return

# Give the best effort to convert to a date object.
# Searched hundreds of debugs that have been provided
# via end-users and 99% all reported the same date
# format, however, there are a couple that had a
# 2 digit year instead of a 4 digit year...gross
formatter = "%m/%d/%Y" if len(parts[-1]) == 4 else "%m/%d/%y"
try:
return datetime.strptime(string, formatter).date()
except Exception as e:
logger.warning(f"Failed to format BIOS release date to datetime object: {e!r}")


def parse_dmi() -> DMIInfo:
return DMIParser().parse(
subprocess.run(DMIParser.command, check=False, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
encoding="utf-8", errors="ignore").stdout
)
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[flake8]
max-line-length=120
13 changes: 13 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from distutils.core import setup
from setuptools import find_packages


setup(
name="ixhardware",
description="Detect iXsystems hardware",
version="1.0",
include_package_data=True,
packages=find_packages(),
license="GNU3",
platforms="any",
)
Loading
Loading