Compare commits
No commits in common. "3287df35c5ccaa72f793f309f2133a6774b5ec08" and "d81c3af3b327b37faacc5d434cadc17f46ebd829" have entirely different histories.
3287df35c5
...
d81c3af3b3
12 changed files with 0 additions and 494 deletions
|
|
@ -1,46 +0,0 @@
|
||||||
name: Build docs site
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: self-hosted
|
|
||||||
container:
|
|
||||||
image: python:3.13-bookworm
|
|
||||||
volumes:
|
|
||||||
- /srv/docs-makerfloss/html:/output
|
|
||||||
steps:
|
|
||||||
- name: Install git for actions/checkout
|
|
||||||
run: |
|
|
||||||
apt-get update -qq
|
|
||||||
apt-get install -y --no-install-recommends git rsync
|
|
||||||
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install Python dependencies
|
|
||||||
run: pip install --quiet -r requirements.txt
|
|
||||||
|
|
||||||
- name: Regenerate hardware index
|
|
||||||
run: python3 scripts/gen_overview.py --category hardware
|
|
||||||
|
|
||||||
- name: Fail on drift in docs/hardware/index.md
|
|
||||||
run: |
|
|
||||||
if ! git diff --exit-code docs/hardware/index.md; then
|
|
||||||
echo
|
|
||||||
echo "::error::docs/hardware/index.md is stale."
|
|
||||||
echo "Regenerate locally via 'make docs-index' and commit the result."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Build site (strict)
|
|
||||||
run: mkdocs build --strict
|
|
||||||
|
|
||||||
- name: Publish to /output (main only)
|
|
||||||
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
|
||||||
run: rsync -a --delete site/ /output/
|
|
||||||
9
.gitignore
vendored
9
.gitignore
vendored
|
|
@ -5,15 +5,6 @@
|
||||||
slides/*.html
|
slides/*.html
|
||||||
slides/*.pdf
|
slides/*.pdf
|
||||||
|
|
||||||
# ---> MkDocs build output
|
|
||||||
site/
|
|
||||||
.cache/
|
|
||||||
|
|
||||||
# ---> Python venvs / caches
|
|
||||||
.venv/
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
|
|
||||||
# ---> Local secrets (Forgejo API token, etc.)
|
# ---> Local secrets (Forgejo API token, etc.)
|
||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
|
|
|
||||||
25
Makefile
25
Makefile
|
|
@ -1,25 +0,0 @@
|
||||||
.PHONY: help docs-index docs-build docs-serve docs-check slides
|
|
||||||
|
|
||||||
help:
|
|
||||||
@echo "Targets:"
|
|
||||||
@echo " docs-index Regenerate docs/hardware/index.md from per-host frontmatter"
|
|
||||||
@echo " docs-build Build the static MkDocs site into ./site (strict)"
|
|
||||||
@echo " docs-serve Run a live-reload local preview server"
|
|
||||||
@echo " docs-check Drift-check: regenerate index, fail if it differs from the committed copy"
|
|
||||||
@echo " slides Run build-slides.sh (Marp slides)"
|
|
||||||
|
|
||||||
docs-index:
|
|
||||||
python3 scripts/gen_overview.py --category hardware
|
|
||||||
|
|
||||||
docs-build:
|
|
||||||
mkdocs build --strict
|
|
||||||
|
|
||||||
docs-serve:
|
|
||||||
mkdocs serve
|
|
||||||
|
|
||||||
docs-check:
|
|
||||||
python3 scripts/gen_overview.py --category hardware
|
|
||||||
git diff --exit-code docs/hardware/index.md
|
|
||||||
|
|
||||||
slides:
|
|
||||||
./build-slides.sh
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
---
|
|
||||||
hostname: fisi
|
|
||||||
kind: server
|
|
||||||
status: in-use
|
|
||||||
model: HP MicroServer Gen10 Plus
|
|
||||||
location: home rack
|
|
||||||
cpu: Xeon E-2226G
|
|
||||||
cpu_cores: 6
|
|
||||||
cpu_threads: 12
|
|
||||||
ram_gb: 64
|
|
||||||
storage_gb: 8000
|
|
||||||
storage_type: hdd
|
|
||||||
storage_notes: ZFS mirror 2×8 TB HDD + 1 TB NVMe cache
|
|
||||||
nic_gbps: 1
|
|
||||||
---
|
|
||||||
|
|
||||||
# fisi
|
|
||||||
|
|
||||||
Primary home server in the baobab.band homelab. Hosts the bulk of
|
|
||||||
self-hosted services: Nextcloud, Jellyfin + *arr stack, Technitium DNS,
|
|
||||||
PhotoPrism, Matrix (conduwuit + Element), Forgejo (internal), Vaultwarden,
|
|
||||||
and more.
|
|
||||||
|
|
||||||
Not part of the MakerFLOSS infrastructure proper, listed here for
|
|
||||||
Proxmox-style placement planning when we eventually share workloads.
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
# Hardware Overview
|
|
||||||
|
|
||||||
_Auto-generated from `docs/hardware/*.md` — do not edit by hand. Run `make docs-index` after changing a file._
|
|
||||||
|
|
||||||
## Laptops
|
|
||||||
|
|
||||||
| Hostname | Model | Location | CPU | RAM | Storage | NIC | Status |
|
|
||||||
|---|---|---|---|---|---|---|---|
|
|
||||||
| [tembo](tembo.md) | ThinkPad T480 | Orange Makerspace (kiosk) | Intel Core i5-8350U · 4c/8t | 16 GB | 512 GB NVME | 1 GbE | in-use |
|
|
||||||
|
|
||||||
## Servers
|
|
||||||
|
|
||||||
| Hostname | Model | Location | CPU | RAM | Storage | NIC | Status |
|
|
||||||
|---|---|---|---|---|---|---|---|
|
|
||||||
| [fisi](fisi.md) | HP MicroServer Gen10 Plus | home rack | Xeon E-2226G · 6c/12t | 64 GB | 8 TB HDD | 1 GbE | in-use |
|
|
||||||
| [makerfloss](makerfloss.md) | Hetzner CX22 | Hetzner HEL1 (cloud) | AMD EPYC (shared vCPU) · 2c | 4 GB | 40 GB NVME | 1 GbE | in-use |
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
---
|
|
||||||
hostname: makerfloss
|
|
||||||
kind: server
|
|
||||||
status: in-use
|
|
||||||
model: Hetzner CX22
|
|
||||||
location: Hetzner HEL1 (cloud)
|
|
||||||
cpu: AMD EPYC (shared vCPU)
|
|
||||||
cpu_cores: 2
|
|
||||||
cpu_threads: 2
|
|
||||||
ram_gb: 4
|
|
||||||
storage_gb: 40
|
|
||||||
storage_type: nvme
|
|
||||||
nic_gbps: 1
|
|
||||||
---
|
|
||||||
|
|
||||||
# makerfloss
|
|
||||||
|
|
||||||
Hetzner Cloud VPS running the public-facing MakerFLOSS stack: Forgejo
|
|
||||||
(self-hosted git forge), Traefik with Let's Encrypt, poste.io mail
|
|
||||||
server, a Forgejo Actions runner, and the nginx services that serve
|
|
||||||
`slides.makerfloss.eu` and `docs.makerfloss.eu`.
|
|
||||||
|
|
||||||
Managed via the [`AnsibleBaobabV4`](https://forgejo.nyumbani.baobab.band/sjat/AnsibleBaobabV4)
|
|
||||||
Ansible project. SSH on port 7576.
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
---
|
|
||||||
hostname: tembo
|
|
||||||
kind: laptop
|
|
||||||
status: in-use
|
|
||||||
model: ThinkPad T480
|
|
||||||
location: Orange Makerspace (kiosk)
|
|
||||||
cpu: Intel Core i5-8350U
|
|
||||||
cpu_cores: 4
|
|
||||||
cpu_threads: 8
|
|
||||||
ram_gb: 16
|
|
||||||
storage_gb: 512
|
|
||||||
storage_type: nvme
|
|
||||||
nic_gbps: 1
|
|
||||||
---
|
|
||||||
|
|
||||||
# tembo
|
|
||||||
|
|
||||||
XFCE-based touchscreen kiosk laptop at Orange Makerspace. Runs a
|
|
||||||
rotation of dashboards (Grafana, the Marp slides at
|
|
||||||
`slides.makerfloss.eu`, the radio page) and serves as the local
|
|
||||||
display for the room. Also runs a Grafana/Loki/Prometheus stack.
|
|
||||||
|
|
||||||
Acts as an example of a re-purposed laptop being treated as a fixed
|
|
||||||
piece of infrastructure rather than a personal device.
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
# MakerFLOSS Docs
|
|
||||||
|
|
||||||
Documentation for the [MakerFLOSS](https://forgejo.makerfloss.eu/sjat/MakerFLOSS)
|
|
||||||
initiative at [Orange Makerspace](https://orangemakers.dk) — a bi-weekly FLOSS
|
|
||||||
jam-session community focused on self-hosted, open-source infrastructure.
|
|
||||||
|
|
||||||
## Sections
|
|
||||||
|
|
||||||
- [Hardware](hardware/index.md) — every machine in the lab, auto-indexed from per-host
|
|
||||||
frontmatter blocks. Use this when planning where to deploy a new service.
|
|
||||||
- [House rules](makerFLOSS_house_rules.md) — working norms, governance, and
|
|
||||||
what we do (and don't) do.
|
|
||||||
|
|
||||||
## Working norms (summary)
|
|
||||||
|
|
||||||
- **Language:** English for code and docs. Danish allowed in meeting notes and
|
|
||||||
community communications.
|
|
||||||
- **Environments:** containerised and reproducible.
|
|
||||||
- **Hardware:** all setups documented (this site) and physically labelled.
|
|
||||||
- **Decisions:** lightweight markdown decision logs under
|
|
||||||
[`docs/superpowers/`](https://forgejo.makerfloss.eu/sjat/MakerFLOSS/src/branch/main/docs/superpowers).
|
|
||||||
- **License:** FLOSS by default; MIT for what we build.
|
|
||||||
55
mkdocs.yml
55
mkdocs.yml
|
|
@ -1,55 +0,0 @@
|
||||||
site_name: MakerFLOSS Docs
|
|
||||||
site_url: https://docs.makerfloss.eu/
|
|
||||||
site_description: Documentation for the MakerFLOSS initiative at Orange Makerspace.
|
|
||||||
|
|
||||||
repo_url: https://forgejo.makerfloss.eu/sjat/MakerFLOSS
|
|
||||||
repo_name: sjat/MakerFLOSS
|
|
||||||
edit_uri: _edit/main/
|
|
||||||
|
|
||||||
theme:
|
|
||||||
name: material
|
|
||||||
features:
|
|
||||||
- navigation.indexes
|
|
||||||
- navigation.sections
|
|
||||||
- navigation.top
|
|
||||||
- content.code.copy
|
|
||||||
- search.suggest
|
|
||||||
- search.highlight
|
|
||||||
palette:
|
|
||||||
- media: "(prefers-color-scheme: light)"
|
|
||||||
scheme: default
|
|
||||||
primary: deep orange
|
|
||||||
toggle:
|
|
||||||
icon: material/weather-night
|
|
||||||
name: Switch to dark mode
|
|
||||||
- media: "(prefers-color-scheme: dark)"
|
|
||||||
scheme: slate
|
|
||||||
primary: deep orange
|
|
||||||
toggle:
|
|
||||||
icon: material/weather-sunny
|
|
||||||
name: Switch to light mode
|
|
||||||
|
|
||||||
markdown_extensions:
|
|
||||||
- admonition
|
|
||||||
- toc:
|
|
||||||
permalink: true
|
|
||||||
- tables
|
|
||||||
- attr_list
|
|
||||||
- md_in_html
|
|
||||||
- pymdownx.superfences
|
|
||||||
- pymdownx.highlight:
|
|
||||||
anchor_linenums: true
|
|
||||||
- pymdownx.inlinehilite
|
|
||||||
- pymdownx.tabbed:
|
|
||||||
alternate_style: true
|
|
||||||
- pymdownx.tasklist:
|
|
||||||
custom_checkbox: true
|
|
||||||
|
|
||||||
plugins:
|
|
||||||
- search
|
|
||||||
|
|
||||||
nav:
|
|
||||||
- Home: index.md
|
|
||||||
- Hardware:
|
|
||||||
- hardware/index.md
|
|
||||||
- House rules: makerFLOSS_house_rules.md
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
mkdocs==1.6.*
|
|
||||||
mkdocs-material==9.5.*
|
|
||||||
pyyaml==6.*
|
|
||||||
|
|
@ -1,207 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""Generate a category overview Markdown file from per-item YAML frontmatter.
|
|
||||||
|
|
||||||
Reads `scripts/overview_config.yml`, picks the block named by `--category`,
|
|
||||||
walks `source_dir/*.md` (excluding `output_file`), validates each file's
|
|
||||||
frontmatter, and writes a grouped+sorted table to `output_file`.
|
|
||||||
|
|
||||||
Exits non-zero on any schema violation. Deterministic, offline, stdlib + PyYAML.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
||||||
CONFIG_PATH = REPO_ROOT / "scripts" / "overview_config.yml"
|
|
||||||
|
|
||||||
FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
|
|
||||||
|
|
||||||
|
|
||||||
class SchemaError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def parse_frontmatter(path: Path) -> dict | None:
|
|
||||||
text = path.read_text(encoding="utf-8")
|
|
||||||
m = FRONTMATTER_RE.match(text)
|
|
||||||
if not m:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
data = yaml.safe_load(m.group(1))
|
|
||||||
except yaml.YAMLError as e:
|
|
||||||
raise SchemaError(f"{path}: invalid YAML frontmatter: {e}") from e
|
|
||||||
if not isinstance(data, dict):
|
|
||||||
raise SchemaError(f"{path}: frontmatter is not a mapping")
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def validate(path: Path, fm: dict, cfg: dict) -> None:
|
|
||||||
for field in cfg["required_fields"]:
|
|
||||||
if field not in fm:
|
|
||||||
raise SchemaError(f"{path}: missing required field '{field}'")
|
|
||||||
for field, allowed in cfg.get("enums", {}).items():
|
|
||||||
if field in fm and fm[field] not in allowed:
|
|
||||||
raise SchemaError(
|
|
||||||
f"{path}: {field}={fm[field]!r} not in {allowed}"
|
|
||||||
)
|
|
||||||
stem = path.stem
|
|
||||||
hostname = fm["hostname"]
|
|
||||||
if stem != hostname:
|
|
||||||
raise SchemaError(
|
|
||||||
f"{path}: filename stem {stem!r} != hostname {hostname!r}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def fmt_cpu(fm: dict) -> str:
|
|
||||||
model = fm.get("cpu", "")
|
|
||||||
cores = fm.get("cpu_cores")
|
|
||||||
threads = fm.get("cpu_threads")
|
|
||||||
suffix = ""
|
|
||||||
if cores and threads and threads != cores:
|
|
||||||
suffix = f" · {cores}c/{threads}t"
|
|
||||||
elif cores:
|
|
||||||
suffix = f" · {cores}c"
|
|
||||||
return (model + suffix).strip()
|
|
||||||
|
|
||||||
|
|
||||||
def fmt_ram(fm: dict) -> str:
|
|
||||||
n = fm.get("ram_gb")
|
|
||||||
return f"{n} GB" if isinstance(n, int) else ""
|
|
||||||
|
|
||||||
|
|
||||||
def fmt_storage(fm: dict) -> str:
|
|
||||||
n = fm.get("storage_gb")
|
|
||||||
t = fm.get("storage_type", "").upper() if fm.get("storage_type") else ""
|
|
||||||
if not isinstance(n, int):
|
|
||||||
return t # type alone if no capacity
|
|
||||||
if n >= 1000 and n % 1000 == 0:
|
|
||||||
size = f"{n // 1000} TB"
|
|
||||||
elif n >= 1000:
|
|
||||||
size = f"{n / 1000:.1f} TB"
|
|
||||||
else:
|
|
||||||
size = f"{n} GB"
|
|
||||||
return f"{size} {t}".strip()
|
|
||||||
|
|
||||||
|
|
||||||
def fmt_nic(fm: dict) -> str:
|
|
||||||
g = fm.get("nic_gbps")
|
|
||||||
if g is None:
|
|
||||||
return ""
|
|
||||||
if isinstance(g, float) and not g.is_integer():
|
|
||||||
return f"{g} GbE"
|
|
||||||
return f"{int(g)} GbE"
|
|
||||||
|
|
||||||
|
|
||||||
def cell(fm: dict, col: dict) -> str:
|
|
||||||
kind = col.get("kind")
|
|
||||||
if kind == "hostname-link":
|
|
||||||
h = fm["hostname"]
|
|
||||||
return f"[{h}]({h}.md)"
|
|
||||||
if kind == "cpu":
|
|
||||||
return fmt_cpu(fm)
|
|
||||||
if kind == "ram":
|
|
||||||
return fmt_ram(fm)
|
|
||||||
if kind == "storage":
|
|
||||||
return fmt_storage(fm)
|
|
||||||
if kind == "nic":
|
|
||||||
return fmt_nic(fm)
|
|
||||||
value = fm.get(col["field"], "")
|
|
||||||
return "" if value is None else str(value)
|
|
||||||
|
|
||||||
|
|
||||||
def render(cfg: dict, items: list[dict]) -> str:
|
|
||||||
columns = cfg["columns"]
|
|
||||||
group_by = cfg.get("group_by")
|
|
||||||
sort_by = cfg.get("sort_by", "hostname")
|
|
||||||
group_titles = cfg.get("group_titles", {})
|
|
||||||
|
|
||||||
if group_by:
|
|
||||||
groups: dict[str, list[dict]] = {}
|
|
||||||
for fm in items:
|
|
||||||
groups.setdefault(fm.get(group_by, ""), []).append(fm)
|
|
||||||
ordered = sorted(groups.items())
|
|
||||||
else:
|
|
||||||
ordered = [("", items)]
|
|
||||||
|
|
||||||
lines: list[str] = []
|
|
||||||
lines.append(f"# {cfg['title']}")
|
|
||||||
lines.append("")
|
|
||||||
lines.append(
|
|
||||||
f"_Auto-generated from `{cfg['source_dir']}/*.md` — do not edit by hand. "
|
|
||||||
f"Run `make docs-index` after changing a file._"
|
|
||||||
)
|
|
||||||
lines.append("")
|
|
||||||
|
|
||||||
for group_key, rows in ordered:
|
|
||||||
rows.sort(key=lambda r: r.get(sort_by, ""))
|
|
||||||
if group_by:
|
|
||||||
title = group_titles.get(group_key, group_key.title() + "s")
|
|
||||||
lines.append(f"## {title}")
|
|
||||||
lines.append("")
|
|
||||||
header = "| " + " | ".join(c["header"] for c in columns) + " |"
|
|
||||||
sep = "|" + "|".join("---" for _ in columns) + "|"
|
|
||||||
lines.append(header)
|
|
||||||
lines.append(sep)
|
|
||||||
for fm in rows:
|
|
||||||
lines.append("| " + " | ".join(cell(fm, c) for c in columns) + " |")
|
|
||||||
lines.append("")
|
|
||||||
|
|
||||||
return "\n".join(lines).rstrip() + "\n"
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
||||||
parser.add_argument("--category", required=True, help="Category key from overview_config.yml")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
config_all = yaml.safe_load(CONFIG_PATH.read_text(encoding="utf-8"))
|
|
||||||
if args.category not in config_all:
|
|
||||||
print(f"ERROR: category {args.category!r} not in {CONFIG_PATH}", file=sys.stderr)
|
|
||||||
return 2
|
|
||||||
cfg = config_all[args.category]
|
|
||||||
|
|
||||||
source_dir = REPO_ROOT / cfg["source_dir"]
|
|
||||||
output_file = REPO_ROOT / cfg["output_file"]
|
|
||||||
output_abs = output_file.resolve()
|
|
||||||
|
|
||||||
items: list[dict] = []
|
|
||||||
errors: list[str] = []
|
|
||||||
for path in sorted(source_dir.glob("*.md")):
|
|
||||||
if path.resolve() == output_abs:
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
fm = parse_frontmatter(path)
|
|
||||||
except SchemaError as e:
|
|
||||||
errors.append(str(e))
|
|
||||||
continue
|
|
||||||
if fm is None:
|
|
||||||
print(f"WARNING: {path}: no YAML frontmatter, skipping", file=sys.stderr)
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
validate(path, fm, cfg)
|
|
||||||
except SchemaError as e:
|
|
||||||
errors.append(str(e))
|
|
||||||
continue
|
|
||||||
items.append(fm)
|
|
||||||
|
|
||||||
if errors:
|
|
||||||
for err in errors:
|
|
||||||
print(f"ERROR: {err}", file=sys.stderr)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
output_file.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
tmp = output_file.with_suffix(output_file.suffix + ".tmp")
|
|
||||||
tmp.write_text(render(cfg, items), encoding="utf-8")
|
|
||||||
tmp.replace(output_file)
|
|
||||||
print(f"Wrote {output_file.relative_to(REPO_ROOT)} ({len(items)} item(s))")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
# Configuration for scripts/gen_overview.py
|
|
||||||
#
|
|
||||||
# Each top-level key is a category. The generator is invoked with
|
|
||||||
# --category <name> and looks up its block here. To add a new category
|
|
||||||
# (services, vms, ...) later, copy a block and adjust the fields.
|
|
||||||
|
|
||||||
hardware:
|
|
||||||
title: "Hardware Overview"
|
|
||||||
source_dir: docs/hardware
|
|
||||||
output_file: docs/hardware/index.md
|
|
||||||
required_fields:
|
|
||||||
- hostname
|
|
||||||
- kind
|
|
||||||
- status
|
|
||||||
enums:
|
|
||||||
kind: [server, laptop, sbc, switch, ap, desktop]
|
|
||||||
status: [in-use, spare, broken, donated]
|
|
||||||
storage_type: [nvme, ssd, hdd, mixed]
|
|
||||||
group_by: kind
|
|
||||||
# Human-friendly H2 names per group_by value. Anything missing falls back
|
|
||||||
# to the raw value title-cased + "s".
|
|
||||||
group_titles:
|
|
||||||
server: Servers
|
|
||||||
laptop: Laptops
|
|
||||||
sbc: Single-board computers
|
|
||||||
switch: Switches
|
|
||||||
ap: Access points
|
|
||||||
desktop: Desktops
|
|
||||||
sort_by: hostname
|
|
||||||
columns:
|
|
||||||
- { header: Hostname, kind: hostname-link }
|
|
||||||
- { header: Model, field: model }
|
|
||||||
- { header: Location, field: location }
|
|
||||||
- { header: CPU, kind: cpu }
|
|
||||||
- { header: RAM, kind: ram }
|
|
||||||
- { header: Storage, kind: storage }
|
|
||||||
- { header: NIC, kind: nic }
|
|
||||||
- { header: Status, field: status }
|
|
||||||
Loading…
Add table
Reference in a new issue