Skip to content

Commit 76bf104

Browse files
authored
feat: Include django command to update hardware registry information (#1905)
into db Closes #1899 Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
1 parent f2895f3 commit 76bf104

3 files changed

Lines changed: 364 additions & 0 deletions

File tree

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
"""Parse and display KernelCI hardware registry YAML files."""
2+
3+
from __future__ import annotations
4+
5+
from pathlib import Path
6+
from typing import Any
7+
8+
import yaml
9+
from django.core.management.base import BaseCommand, CommandError
10+
11+
from kernelCI_app.models import (
12+
HardwareRegistryPlatform,
13+
HardwareRegistryPlatformVendor,
14+
HardwareRegistryProcessor,
15+
HardwareRegistrySiliconVendor,
16+
HardwareRegistrySystemModule,
17+
)
18+
19+
20+
def get_update_fields(model):
21+
return [
22+
f.column if f.is_relation else f.name
23+
for f in model._meta.concrete_fields
24+
if not f.primary_key
25+
]
26+
27+
28+
EXPECTED_SECTIONS = (
29+
"silicon_vendors",
30+
"platform_vendors",
31+
"processors",
32+
"system_modules",
33+
"platforms",
34+
)
35+
36+
37+
class Command(BaseCommand):
38+
help = (
39+
"Parse a KernelCI hardware registry YAML file (same shape as "
40+
"kernelci-pipeline config/hardware_registry/*.yaml) and print its data."
41+
)
42+
43+
def add_arguments(self, parser):
44+
parser.add_argument(
45+
"registry_file",
46+
type=str,
47+
help="Path to the hardware registry YAML file.",
48+
)
49+
50+
def load_yaml(self, path: str) -> dict:
51+
if not path.is_file():
52+
raise CommandError(f"Registry file not found: {path}")
53+
try:
54+
raw = path.read_text(encoding="utf-8")
55+
except OSError as exc:
56+
raise CommandError(f"Cannot read registry file: {exc}") from exc
57+
58+
try:
59+
data = yaml.safe_load(raw)
60+
return data
61+
except yaml.YAMLError as exc:
62+
raise CommandError(f"Invalid YAML: {exc}") from exc
63+
64+
def handle(self, *args: Any, registry_file: str, **options: Any) -> None:
65+
path = Path(registry_file).expanduser().resolve()
66+
data = self.load_yaml(path)
67+
68+
if data is None:
69+
raise CommandError("Registry file is empty or parses to null.")
70+
if not isinstance(data, dict):
71+
raise CommandError(
72+
f"Expected a YAML mapping at root, got {type(data).__name__}."
73+
)
74+
75+
missing = [k for k in EXPECTED_SECTIONS if k not in data]
76+
if missing:
77+
self.stderr.write(
78+
self.style.WARNING(
79+
f"Missing typical hardware-registry sections : {', '.join(missing)}"
80+
)
81+
)
82+
83+
silicon_vendors = data["silicon_vendors"] or []
84+
platform_vendors = data["platform_vendors"] or []
85+
processors = data["processors"] or []
86+
system_modules = data["system_modules"] or []
87+
platforms = data["platforms"] or []
88+
89+
HardwareRegistrySiliconVendor.objects.bulk_create(
90+
[HardwareRegistrySiliconVendor(**sv) for sv in silicon_vendors.values()],
91+
update_conflicts=True,
92+
unique_fields=["id"],
93+
update_fields=get_update_fields(HardwareRegistrySiliconVendor),
94+
)
95+
96+
HardwareRegistryProcessor.objects.bulk_create(
97+
[HardwareRegistryProcessor(**p) for p in processors.values()],
98+
update_conflicts=True,
99+
unique_fields=["id"],
100+
update_fields=get_update_fields(HardwareRegistryProcessor),
101+
)
102+
103+
HardwareRegistryPlatformVendor.objects.bulk_create(
104+
[HardwareRegistryPlatformVendor(**pv) for pv in platform_vendors.values()],
105+
update_conflicts=True,
106+
unique_fields=["id"],
107+
update_fields=get_update_fields(HardwareRegistryPlatformVendor),
108+
)
109+
110+
HardwareRegistrySystemModule.objects.bulk_create(
111+
[HardwareRegistrySystemModule(**sm) for sm in system_modules.values()],
112+
update_conflicts=True,
113+
unique_fields=["id"],
114+
update_fields=get_update_fields(HardwareRegistrySystemModule),
115+
)
116+
117+
HardwareRegistryPlatform.objects.bulk_create(
118+
[HardwareRegistryPlatform(**p) for p in platforms.values()],
119+
update_conflicts=True,
120+
unique_fields=["id"],
121+
update_fields=get_update_fields(HardwareRegistryPlatform),
122+
)
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# Generated by Django 5.2.11 on 2026-05-18 12:07
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
dependencies = [
9+
("kernelCI_app", "0017_add_tree_tests_rollup_table"),
10+
]
11+
12+
operations = [
13+
migrations.CreateModel(
14+
name="HardwareRegistryPlatformVendor",
15+
fields=[
16+
("id", models.TextField(primary_key=True, serialize=False)),
17+
("type", models.CharField(default="platform_vendor", max_length=64)),
18+
("url", models.TextField(blank=True, null=True)),
19+
("details", models.TextField(blank=True, null=True)),
20+
],
21+
options={
22+
"db_table": "hardware_registry_platform_vendors",
23+
},
24+
),
25+
migrations.CreateModel(
26+
name="HardwareRegistrySiliconVendor",
27+
fields=[
28+
("id", models.TextField(primary_key=True, serialize=False)),
29+
("type", models.CharField(blank=True, max_length=64)),
30+
("url", models.TextField(blank=True, null=True)),
31+
("details", models.TextField(blank=True, null=True)),
32+
],
33+
options={
34+
"db_table": "hardware_registry_silicon_vendors",
35+
},
36+
),
37+
migrations.CreateModel(
38+
name="HardwareRegistryProcessor",
39+
fields=[
40+
("id", models.TextField(primary_key=True, serialize=False)),
41+
("type", models.CharField(default="soc", max_length=64)),
42+
("url", models.TextField(blank=True, null=True)),
43+
("details", models.TextField(blank=True, null=True)),
44+
("architecture", models.TextField(blank=True, null=True)),
45+
("cores", models.IntegerField(blank=True, null=True)),
46+
("max_clock_speed_mhz", models.IntegerField(blank=True, null=True)),
47+
(
48+
"vendor",
49+
models.ForeignKey(
50+
db_column="vendor_id",
51+
on_delete=django.db.models.deletion.CASCADE,
52+
related_name="hardware_registry_processors",
53+
to="kernelCI_app.hardwareregistrysiliconvendor",
54+
),
55+
),
56+
],
57+
options={
58+
"db_table": "hardware_registry_processors",
59+
},
60+
),
61+
migrations.CreateModel(
62+
name="HardwareRegistrySystemModule",
63+
fields=[
64+
("id", models.TextField(primary_key=True, serialize=False)),
65+
("type", models.CharField(max_length=64)),
66+
("url", models.TextField(blank=True, null=True)),
67+
("details", models.TextField(blank=True, null=True)),
68+
("form_factor", models.TextField(blank=True, null=True)),
69+
(
70+
"processor",
71+
models.ForeignKey(
72+
db_column="processor_id",
73+
on_delete=django.db.models.deletion.CASCADE,
74+
related_name="hardware_registry_system_modules",
75+
to="kernelCI_app.hardwareregistryprocessor",
76+
),
77+
),
78+
(
79+
"vendor",
80+
models.ForeignKey(
81+
db_column="vendor_id",
82+
on_delete=django.db.models.deletion.CASCADE,
83+
related_name="hardware_registry_system_modules",
84+
to="kernelCI_app.hardwareregistryplatformvendor",
85+
),
86+
),
87+
],
88+
options={
89+
"db_table": "hardware_registry_system_modules",
90+
},
91+
),
92+
migrations.CreateModel(
93+
name="HardwareRegistryPlatform",
94+
fields=[
95+
("id", models.TextField(primary_key=True, serialize=False)),
96+
("type", models.CharField(max_length=64)),
97+
("url", models.TextField(blank=True, null=True)),
98+
("details", models.TextField(blank=True, null=True)),
99+
("form_factor", models.TextField(blank=True, null=True)),
100+
(
101+
"vendor",
102+
models.ForeignKey(
103+
db_column="vendor_id",
104+
on_delete=django.db.models.deletion.CASCADE,
105+
related_name="hardware_registry_platforms",
106+
to="kernelCI_app.hardwareregistryplatformvendor",
107+
),
108+
),
109+
(
110+
"processor",
111+
models.ForeignKey(
112+
db_column="processor_id",
113+
on_delete=django.db.models.deletion.CASCADE,
114+
related_name="hardware_registry_platforms",
115+
to="kernelCI_app.hardwareregistryprocessor",
116+
),
117+
),
118+
(
119+
"system_module",
120+
models.ForeignKey(
121+
blank=True,
122+
db_column="system_module_id",
123+
null=True,
124+
on_delete=django.db.models.deletion.CASCADE,
125+
related_name="hardware_registry_platforms",
126+
to="kernelCI_app.hardwareregistrysystemmodule",
127+
),
128+
),
129+
],
130+
options={
131+
"db_table": "hardware_registry_platforms",
132+
},
133+
),
134+
]

backend/kernelCI_app/models.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,3 +499,111 @@ class Meta:
499499
name="tree_tests_rollup_group_total",
500500
),
501501
]
502+
503+
504+
class HardwareRegistrySiliconVendor(models.Model):
505+
id = models.TextField(primary_key=True)
506+
type = models.CharField(max_length=64, blank=True)
507+
url = models.TextField(blank=True, null=True)
508+
details = models.TextField(blank=True, null=True)
509+
510+
class Meta:
511+
db_table = "hardware_registry_silicon_vendors"
512+
513+
def __str__(self) -> str:
514+
return self.id
515+
516+
517+
class HardwareRegistryPlatformVendor(models.Model):
518+
id = models.TextField(primary_key=True)
519+
type = models.CharField(max_length=64, default="platform_vendor")
520+
url = models.TextField(blank=True, null=True)
521+
details = models.TextField(blank=True, null=True)
522+
523+
class Meta:
524+
db_table = "hardware_registry_platform_vendors"
525+
526+
def __str__(self) -> str:
527+
return self.id
528+
529+
530+
class HardwareRegistryProcessor(models.Model):
531+
id = models.TextField(primary_key=True)
532+
type = models.CharField(max_length=64, default="soc")
533+
vendor = models.ForeignKey(
534+
HardwareRegistrySiliconVendor,
535+
db_column="vendor_id",
536+
on_delete=models.CASCADE,
537+
related_name="hardware_registry_processors",
538+
)
539+
url = models.TextField(blank=True, null=True)
540+
details = models.TextField(blank=True, null=True)
541+
architecture = models.TextField(blank=True, null=True)
542+
cores = models.IntegerField(blank=True, null=True)
543+
max_clock_speed_mhz = models.IntegerField(blank=True, null=True)
544+
545+
class Meta:
546+
db_table = "hardware_registry_processors"
547+
548+
def __str__(self) -> str:
549+
return self.id
550+
551+
552+
class HardwareRegistrySystemModule(models.Model):
553+
id = models.TextField(primary_key=True)
554+
type = models.CharField(max_length=64)
555+
vendor = models.ForeignKey(
556+
HardwareRegistryPlatformVendor,
557+
db_column="vendor_id",
558+
on_delete=models.CASCADE,
559+
related_name="hardware_registry_system_modules",
560+
)
561+
processor = models.ForeignKey(
562+
HardwareRegistryProcessor,
563+
db_column="processor_id",
564+
on_delete=models.CASCADE,
565+
related_name="hardware_registry_system_modules",
566+
)
567+
url = models.TextField(blank=True, null=True)
568+
details = models.TextField(blank=True, null=True)
569+
form_factor = models.TextField(blank=True, null=True)
570+
571+
class Meta:
572+
db_table = "hardware_registry_system_modules"
573+
574+
def __str__(self) -> str:
575+
return self.id
576+
577+
578+
class HardwareRegistryPlatform(models.Model):
579+
id = models.TextField(primary_key=True)
580+
type = models.CharField(max_length=64)
581+
vendor = models.ForeignKey(
582+
HardwareRegistryPlatformVendor,
583+
db_column="vendor_id",
584+
on_delete=models.CASCADE,
585+
related_name="hardware_registry_platforms",
586+
)
587+
processor = models.ForeignKey(
588+
HardwareRegistryProcessor,
589+
db_column="processor_id",
590+
on_delete=models.CASCADE,
591+
related_name="hardware_registry_platforms",
592+
)
593+
system_module = models.ForeignKey(
594+
HardwareRegistrySystemModule,
595+
db_column="system_module_id",
596+
on_delete=models.CASCADE,
597+
related_name="hardware_registry_platforms",
598+
null=True,
599+
blank=True,
600+
)
601+
url = models.TextField(blank=True, null=True)
602+
details = models.TextField(blank=True, null=True)
603+
form_factor = models.TextField(blank=True, null=True)
604+
605+
class Meta:
606+
db_table = "hardware_registry_platforms"
607+
608+
def __str__(self) -> str:
609+
return self.id

0 commit comments

Comments
 (0)