blancco: CMMC evidence pack, ServiceNow tick list, cloud-upload status
Automates the Blancco parts of the onsite-support CMMC playbook:
- ENT-056/158 evidence pack: /reports/evidence?month=YYYY-MM returns
ENT-056_<Site>_<YYYY-MM>.zip (summary.csv, README, each report's XML
and PDF). pxe-blancco-evidence.timer saves last month's pack to the
reports share's evidence/ folder on the 1st (Persistent).
- ENT-045/159 ServiceNow tick list: /reports/servicenow lists recent
reports as "Wiped" or "Blancco Wipe failed" with a pre-written
destruction note, copy buttons and Done (CSRF-protected).
- Cloud upload: blancco-progress.py posts each report's upload result
("Sending report document id" -> "Report sent to MC") to
/reports/cloud-status (CSRF-exempt like /imaging/status); the Reports
page and the CSVs show it, so a wipe missing from BMP is visible.
- CSV gains failure_reason (exception messages, failed steps, error
counts), business_location and cloud_upload.
- Preferences: business_location "West Jefferson" (config.BLANCCO_SITE)
and pdf+xml reports; the report copier now copies the PDF too, named
after its XML. .part and hidden files are not listed as reports.
State (cloud confirmations, ServiceNow ticks) is a JSON file at
/var/lib/pxe-webapp/blancco-state.json, off the report share.
This commit is contained in:
@@ -291,33 +291,39 @@ cat > /run/newroot/usr/local/sbin/blancco-report-sync << 'XEOF'
|
||||
#!/bin/bash
|
||||
# Copy Blancco reports to the SMB report share.
|
||||
#
|
||||
# In process=auto Albus saves each report to /reports/<document_id>.xml and
|
||||
# uploads it to the cloud, but never writes the share: auto_backup is
|
||||
# forced off outside workflow/manual mode. Albus has the share mounted at
|
||||
# /mnt/share already, so copy each finished report there, named the way a
|
||||
# manual Save names it (UTC YYYYMMDD_HHMMSS_report.xml).
|
||||
# In process=auto Albus saves each report to /reports/<document_id>.xml
|
||||
# (and .pdf when pdf+xml is selected) and uploads it to the cloud, but never
|
||||
# writes the share: auto_backup is forced off outside workflow/manual mode.
|
||||
# Albus has the share mounted at /mnt/share already, so copy each finished
|
||||
# file there, named the way a manual Save names it (UTC
|
||||
# YYYYMMDD_HHMMSS_report.<ext>, the time taken from the XML so the PDF of
|
||||
# the same report gets the same name).
|
||||
# Usage: blancco-report-sync [--once]
|
||||
DONE=/var/lib/blancco-report-sync
|
||||
mkdir -p "$DONE"
|
||||
while :; do
|
||||
for f in /reports/*.xml; do
|
||||
for f in /reports/*.xml /reports/*.pdf; do
|
||||
[ -s "$f" ] || continue
|
||||
id=$(basename "$f" .xml)
|
||||
[ -e "$DONE/$id" ] && continue
|
||||
base=$(basename "$f")
|
||||
[ -e "$DONE/$base" ] && continue
|
||||
# Leave files Albus may still be writing.
|
||||
[ $(( $(date +%s) - $(stat -c %Y "$f") )) -ge 5 ] || continue
|
||||
mountpoint -q /mnt/share || continue
|
||||
name="$(date -u -r "$f" +%Y%m%d_%H%M%S)_report.xml"
|
||||
id=${base%.*}
|
||||
ext=${base##*.}
|
||||
ref="/reports/$id.xml"
|
||||
[ -f "$ref" ] || ref="$f"
|
||||
name="$(date -u -r "$ref" +%Y%m%d_%H%M%S)_report.$ext"
|
||||
if cp "$f" "/mnt/share/$name.part" && mv -f "/mnt/share/$name.part" "/mnt/share/$name"; then
|
||||
touch "$DONE/$id"
|
||||
echo "copied $id to $name"
|
||||
serial=$(tr -d '"\\' < /sys/class/dmi/id/product_serial 2>/dev/null)
|
||||
touch "$DONE/$base"
|
||||
echo "copied $base to $name"
|
||||
serial=$(tr -d '"\\' 2>/dev/null < /sys/class/dmi/id/product_serial)
|
||||
[ -n "$serial" ] && curl -s -m 5 -o /dev/null -H 'Content-Type: application/json' \
|
||||
-d "{\"serial\":\"$serial\",\"log_lines\":[\"Report copied to share: $name\"]}" \
|
||||
http://172.16.9.1:9009/imaging/status || true
|
||||
else
|
||||
rm -f "/mnt/share/$name.part"
|
||||
echo "copy of $id failed, will retry"
|
||||
echo "copy of $base failed, will retry"
|
||||
fi
|
||||
done
|
||||
[ "$1" = "--once" ] && exit 0
|
||||
|
||||
@@ -252,7 +252,7 @@
|
||||
<creation_date>2026-06-22</creation_date>
|
||||
</image>
|
||||
<customer_data>
|
||||
<business_location></business_location>
|
||||
<business_location>West Jefferson</business_location>
|
||||
<customer_license>GE Aerospace</customer_license>
|
||||
<erasure_person></erasure_person>
|
||||
<erasure_provider></erasure_provider>
|
||||
@@ -312,8 +312,8 @@
|
||||
<per_drive>false</per_drive>
|
||||
<auto_backup>true</auto_backup>
|
||||
<extensions type="array" key="extension">
|
||||
<extension selected="true">xml</extension>
|
||||
<extension>pdf+xml</extension>
|
||||
<extension>xml</extension>
|
||||
<extension selected="true">pdf+xml</extension>
|
||||
</extensions>
|
||||
<utc_offset>0</utc_offset>
|
||||
<network_info>true</network_info>
|
||||
|
||||
@@ -21,6 +21,7 @@ import time
|
||||
import urllib.request
|
||||
|
||||
STATUS_URL = "http://172.16.9.1:9009/imaging/status"
|
||||
CLOUD_URL = "http://172.16.9.1:9009/reports/cloud-status"
|
||||
STAGE_TOTAL = 7
|
||||
# Progress lines arrive in bursts; post at most this often unless the
|
||||
# stage itself changes.
|
||||
@@ -69,6 +70,11 @@ RULES = [
|
||||
(re.compile(r"Report sent to MC"), 6, "Report sent to cloud", None),
|
||||
(re.compile(r"Auto process reached the final step"), 7, "Done", "succeeded"),
|
||||
]
|
||||
# Cloud upload of a report: "[comm.manager] Sending report document id: <id>"
|
||||
# then "Report sent to MC" (or an upload failure).
|
||||
SENDING = re.compile(r"Sending report document id: ([0-9a-fA-F-]{36})")
|
||||
SENT = re.compile(r"Report sent to MC")
|
||||
SEND_FAILED = re.compile(r"(report (upload|send|sending)[^\n]*fail|failed to (send|upload) report)", re.I)
|
||||
# Drive progress, e.g. "FORMAT NVM progress: 75%". Excludes the self-test
|
||||
# completion and volume lines that also carry a percentage.
|
||||
PROGRESS = re.compile(r"\bprogress[^%\d]{0,20}(\d{1,3})%", re.I)
|
||||
@@ -115,10 +121,35 @@ class Reporter:
|
||||
self.pending = []
|
||||
self.last_post = now
|
||||
|
||||
def cloud(self, document_id, sent, detail):
|
||||
"""Tell the webapp whether a report reached the Blancco cloud."""
|
||||
payload = {"document_id": document_id, "serial": SERIAL,
|
||||
"sent": sent, "detail": detail[:200]}
|
||||
if self.dry_run:
|
||||
print(json.dumps({"cloud": payload}))
|
||||
return
|
||||
req = urllib.request.Request(
|
||||
CLOUD_URL, data=json.dumps(payload).encode(),
|
||||
headers={"Content-Type": "application/json"})
|
||||
try:
|
||||
urllib.request.urlopen(req, timeout=5).read()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def line(self, text):
|
||||
text = text.strip()
|
||||
if not text:
|
||||
return
|
||||
m = SENDING.search(text)
|
||||
if m:
|
||||
self.sending = m.group(1)
|
||||
elif getattr(self, "sending", None):
|
||||
if SENT.search(text):
|
||||
self.cloud(self.sending, True, text)
|
||||
self.sending = None
|
||||
elif SEND_FAILED.search(text):
|
||||
self.cloud(self.sending, False, text)
|
||||
self.sending = None
|
||||
for pat, idx, stage, status in RULES:
|
||||
m = pat.search(text)
|
||||
if not m:
|
||||
|
||||
@@ -1148,6 +1148,44 @@
|
||||
enabled: yes
|
||||
daemon_reload: yes
|
||||
|
||||
# Monthly Blancco CMMC evidence pack (ENT-056/158): on the 1st, save last
|
||||
# month's ZIP to the reports share's evidence/ folder for the tech to copy
|
||||
# into Box. Persistent catches a run missed while the server was off.
|
||||
- name: "Create Blancco evidence pack service and timer"
|
||||
copy:
|
||||
dest: "/etc/systemd/system/{{ item.name }}"
|
||||
content: "{{ item.content }}"
|
||||
loop:
|
||||
- name: pxe-blancco-evidence.service
|
||||
content: |
|
||||
[Unit]
|
||||
Description=Save last month's Blancco CMMC evidence pack to the reports share
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
WorkingDirectory=/opt/pxe-webapp
|
||||
Environment=BLANCCO_REPORTS=/srv/samba/blancco-reports
|
||||
Environment=TZ=America/New_York
|
||||
ExecStart=/usr/bin/python3 /opt/pxe-webapp/blancco_evidence_monthly.py
|
||||
- name: pxe-blancco-evidence.timer
|
||||
content: |
|
||||
[Unit]
|
||||
Description=Monthly Blancco CMMC evidence pack
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-01 06:00:00
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
|
||||
- name: "Enable Blancco evidence pack timer"
|
||||
systemd:
|
||||
name: pxe-blancco-evidence.timer
|
||||
state: started
|
||||
enabled: yes
|
||||
daemon_reload: yes
|
||||
|
||||
- name: "Configure unified Apache site (static files + webapp proxy)"
|
||||
copy:
|
||||
dest: /etc/apache2/sites-available/pxe-server.conf
|
||||
|
||||
Reference in New Issue
Block a user