Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -1,23 +1,41 @@
import sys
import subprocess
import os
import emoji as em
import textwrap
import re
import json

# Import user-defined modules located at sibling directory in the parent folder
from sev_certificate_version_3_0_0_0 import SEV_Certificate as sev_certificate_v_3_0_0_0
from sev_certificate_version_3_0_0_0 import SEV_Certificate as SEV_Certificate_v3_0_0_0
from sev_certificate_version import SEV_Certificate as SEV_Certificate_structured

sev_report = ''
FAIL_MARKER = em.emojize(':cross_mark:')

# Get SEV Certificate Version 3.0.0-0
sev_report_v_3_0_0_0 = sev_certificate_v_3_0_0_0()
sev_report += sev_report_v_3_0_0_0.generate_sev_certificate()
# Certification levels in ascending order.
# v3.0.0-0 uses its own class (service-based status extraction).
# All subsequent levels use the structured JSON class, just with different version strings.
levels = [
SEV_Certificate_v3_0_0_0(),
SEV_Certificate_structured("3.0.0-1"),
]

# Print SEV Certificate into the console
print(sev_report)
combined = ''
highest_passed = None

# Write certificate to file
sev_report_v_3_0_0_0.write_sev_certificate(sev_report, "~/sev_certificate_v3.0.0-0.txt")
for cert in levels:
content = cert.generate_sev_certificate()
combined += content
if FAIL_MARKER not in content:
highest_passed = cert.sev_version

Comment thread
amd-aliem marked this conversation as resolved.
print(combined)

# Write one combined cert named after highest achieved level
if highest_passed:
output_file = os.path.expanduser(f"~/sev_certificate_v{highest_passed}.txt")
else:
output_file = os.path.expanduser("~/sev_certificate.txt")

with open(output_file, "w") as f:
f.write(combined)

print(f"Certificate saved to: {output_file}")
if highest_passed:
print(f"Highest achieved level: {highest_passed}")
else:
print("No certification level achieved")
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import subprocess
import json
import emoji as em

test_status_emojis = {
'pass': em.emojize(':check_mark_button:'),
'fail': em.emojize(':cross_mark:'),
'skip': em.emojize(':fast_forward:', language='alias'),
}


class SEV_Certificate:
"""Generic certificate generator for structured JSON test results.

Parses step/summary JSON lines from journald, filtered by SEV_VERSION
and grouped by SEV_TEST_GROUP. Works for any certification level that
uses the emit_step/emit_summary JSON format.
"""

def __init__(self, sev_version):
self.sev_version = sev_version

def get_test_group_summary(self):
"""Generate per-group test summaries from structured JSON results."""
cmd = f"journalctl SEV_VERSION={self.sev_version} -o json"
result = subprocess.run(cmd, shell=True, text=True, capture_output=True, check=True)

Comment thread
amd-aliem marked this conversation as resolved.
groups = {}
for line in result.stdout.strip().splitlines():
try:
record = json.loads(line)
message = record.get("MESSAGE", "")
if not message.startswith("{"):
continue
entry = json.loads(message)
except (json.JSONDecodeError, ValueError):
continue
group = record.get("SEV_TEST_GROUP", "unknown")
if group not in groups:
groups[group] = {"steps": [], "summary": None}
if entry.get("type") == "step":
groups[group]["steps"].append(entry)
elif entry.get("type") == "summary":
groups[group]["summary"] = entry

content = ""

for group, data in groups.items():
summary = data["summary"]
steps = data["steps"]

if summary:
overall = summary.get("status", "?")
passed = summary.get("passed", 0)
failed = summary.get("failed", 0)
else:
passed = sum(1 for s in steps if s.get("status") == "pass")
failed = sum(1 for s in steps if s.get("status") == "fail")
overall = "fail" if failed > 0 else "pass"

overall_emoji = test_status_emojis.get(overall, "?")
content += f"\n[ {overall_emoji} ] {group} ({passed} passed, {failed} failed)\n"

for step in steps:
emoji = test_status_emojis.get(step.get("status", "?"), "?")
name = step.get("test", "?")
detail = step.get("detail", "")
line = f"\t{emoji} {name}"
if detail:
line += f" ({detail})"
content += line + "\n"

return content.expandtabs(2)

def get_sev_log(self):
"""Get raw journal log for this certification level."""
cmd = f"journalctl SEV_VERSION={self.sev_version} --no-hostname --utc"
result = subprocess.run(cmd, shell=True, text=True, capture_output=True, check=True)
return result.stdout
Comment thread
amd-aliem marked this conversation as resolved.

def generate_sev_certificate(self):
"""Generate the SEV Certificate content for this level."""
content = "\n ====== SEV CERTIFICATE ====== \n"
content += f"\n SEV VERSION: {self.sev_version} \n"

content += "\n=== SUMMARY ===\n"
content += self.get_test_group_summary()

content += f"\n=== SEV VERSION {self.sev_version} LOG ===\n"
content += self.get_sev_log()

return content.expandtabs(2)
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
#!/usr/bin/bash
set -euo pipefail

SEV_VERSIONS=("3.0.0-0")
SEV_CERT_FILE=""

# Temporarily hardcode the milestone name
MILESTONE="c3.0.0-0"
# Maximum milestone version to report via beacon.
# Milestones above this version are omitted from the report even if achieved.
# Bump this when a new certification level is ready to be officially reported.
MAX_MILESTONE="3.0.0-0"

# Determine OS name and version
if [ -f /etc/os-release ]; then
. /etc/os-release
OS_NAME="${ID}"
OS_NAME="${ID}"
OS_VERSION="${VERSION_ID:-""}"

# Initialize OS release with the OS VERSION_CODENAME if VERSION_ID is missing in /etc/os-release.
Expand All @@ -28,32 +27,43 @@ fi
# Fetch AMD processor model
PROC_LABEL=$(/usr/bin/python3 /usr/local/lib/scripts/get_processor_model.py series)

# Loop over to generate beacon report for all SEV certificates
for sev_version in "${SEV_VERSIONS[@]}"; do
# Build title
if [ -n "$OS_VERSION" ]; then
SEV_TITLE="${OS_NAME} ${OS_VERSION} SEV version ${sev_version}"
else
SEV_TITLE="${OS_NAME} SEV version ${sev_version}"
fi
# Find the combined certificate file (generator names it after the highest achieved level)
SEV_CERT_FILE=$(compgen -G "${HOME:-/root}/sev_certificate_v3.0.*.txt" | sort -V | tail -1 || true)
if [ -z "$SEV_CERT_FILE" ]; then
# Fallback: no level achieved, generator writes unversioned file
SEV_CERT_FILE="${HOME:-/root}/sev_certificate.txt"
fi

# Obtain SEV Version Content
SEV_CERT_FILE="${HOME:-/root}/sev_certificate_v${sev_version}.txt"
# Extract achieved version from filename (e.g. sev_certificate_v3.0.0-1.txt -> 3.0.0-1)
ACHIEVED=$(basename "$SEV_CERT_FILE" | sed -n 's/sev_certificate_v\(.*\)\.txt/\1/p')

# Cap achieved version to MAX_MILESTONE for reporting purposes
REPORTED="$ACHIEVED"
if [ -n "$ACHIEVED" ]; then
if [ "$(printf '%s\n' "$MAX_MILESTONE" "$ACHIEVED" | sort -V | head -1)" != "$ACHIEVED" ]; then
echo "Achieved milestone c${ACHIEVED} exceeds MAX_MILESTONE ${MAX_MILESTONE}, capping to c${MAX_MILESTONE}"
REPORTED="$MAX_MILESTONE"
fi
fi

# Set up parameters
PARAMS=()
# Build title
if [ -n "$OS_VERSION" ]; then
SEV_TITLE="${OS_NAME} ${OS_VERSION} SEV certification${REPORTED:+ v${REPORTED}}"
else
SEV_TITLE="${OS_NAME} SEV certification${REPORTED:+ v${REPORTED}}"
fi
Comment thread
amd-aliem marked this conversation as resolved.

# Add labels
PARAMS+=("--label" "certificate")
PARAMS+=("--label" "os-${OS_LABEL}")
PARAMS+=("--label" "proc-${PROC_LABEL}")
# Set up parameters
PARAMS=()
PARAMS+=("--label" "certificate")
PARAMS+=("--label" "os-${OS_LABEL}")
PARAMS+=("--label" "proc-${PROC_LABEL}")

# Add milestone for valid test
if [ -e "${SEV_CERT_FILE}" ] && [ -z "$(grep "❌" "${SEV_CERT_FILE}")" ]; then
PARAMS+=("--milestone" "$MILESTONE")
fi
# Add milestone if a level was achieved
if [ -n "$REPORTED" ]; then
PARAMS+=("--milestone" "c${REPORTED}")
fi

beacon report --title "$SEV_TITLE" --body "$SEV_CERT_FILE" "${PARAMS[@]}"
beacon report --title "$SEV_TITLE" --body "$SEV_CERT_FILE" "${PARAMS[@]}"

echo "Published SEV certificate via beacon with title: $SEV_TITLE"
done
echo "Published SEV certificate via beacon with title: $SEV_TITLE"
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[Unit]
Description=Run snguest ok to verify SNP enablement on guest
Description=Run snpguest ok to verify SNP enablement on guest
DefaultDependencies=no
After=boot-succesful.service
wants=boot-succesful.service
After=boot-successful.service systemd-modules-load.service
Wants=boot-successful.service systemd-modules-load.service

[Service]
Type=oneshot
Expand Down
1 change: 1 addition & 0 deletions modules/test/host/mkosi.conf
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[Include]
Include=./snphost-config-commit
Include=./test-done
Loading
Loading