sync: auto-sync from GURU-5070 at 2026-06-15 18:57:26
Author: Mike Swanson Machine: GURU-5070 Timestamp: 2026-06-15 18:57:26
This commit is contained in:
@@ -51,20 +51,80 @@ print("REST payload per AP (what --apply WOULD PUT to /proxy/network/api/s/<site
|
||||
print(" { \"radio_table\": [ { \"radio\":\""+BAND+"\", \"tx_power_mode\":\""+MODE+"\""+(DBM?(", \"tx_power\":"+DBM):"")+" , ...other fields unchanged... } ] }");
|
||||
JS
|
||||
|
||||
if [ "$APPLY" = "1" ]; then
|
||||
RW_U="$(bash "$VAULT" get-field infrastructure/uos-server-network-api-rw credentials.username 2>/dev/null || true)"
|
||||
if [ -z "$RW_U" ]; then
|
||||
echo
|
||||
echo "[BLOCKED] --apply requested but no read-WRITE admin vaulted. This is intentional (live facility)."
|
||||
echo " 1) Create a read-WRITE admin in the UniFi UI (OS Settings -> Admins -> full/site admin)."
|
||||
echo " 2) Vault: bash .claude/skills/vault/scripts/vault-helper.sh new infrastructure/uos-server-network-api-rw \\"
|
||||
echo " --kind generic --name 'UOS Network API (read-write admin)' --tag unifi --set username=<u> --set password=<pw>"
|
||||
echo " 3) Re-run with --apply. (The write path will: capture old values, PUT per AP via the REST API,"
|
||||
echo " and you validate with watch-ap.sh before/after. Roll out per --zone, not site-wide.)"
|
||||
exit 2
|
||||
fi
|
||||
echo "[ERROR] write path is staged but not yet wired in this version — confirm read/watch loop with Howard first, then I'll enable it."
|
||||
exit 3
|
||||
if [ "$APPLY" != "1" ]; then
|
||||
echo
|
||||
echo "[dry-run] no changes made. Add --apply to write (needs the RW admin vaulted — see below)."
|
||||
exit 0
|
||||
fi
|
||||
echo
|
||||
echo "[dry-run] no changes made. Re-run with --apply once a read-write admin is vaulted (and after the live-watch loop is validated)."
|
||||
|
||||
# ----- WRITE PATH (login -> per-AP GET/modify/PUT via the controller REST API, with rollback) -----
|
||||
RWP="infrastructure/uos-server-network-api-rw"
|
||||
export RW_U="$(bash "$VAULT" get-field "$RWP" credentials.username 2>/dev/null || true)"
|
||||
export RW_P="$(bash "$VAULT" get-field "$RWP" credentials.password 2>/dev/null || true)"
|
||||
if [ -z "$RW_U" ] || [ -z "$RW_P" ]; then
|
||||
cat <<EOF
|
||||
|
||||
[BLOCKED] --apply needs a read-WRITE controller admin vaulted at: $RWP
|
||||
UniFi OS authenticates against unifi-core, so this admin must be created in the UI (it cannot be
|
||||
minted from Mongo). Howard has full controller access — this is his to create (~1 min):
|
||||
1) UniFi OS -> Settings -> Admins -> Add Admin (Full Management / site Admin; set username + password).
|
||||
2) bash .claude/skills/vault/scripts/vault-helper.sh new $RWP --kind generic \\
|
||||
--name 'UOS Network API (read-write admin)' --tag unifi --set username=<u> --set password=<pw>
|
||||
3) Re-run this command with --apply.
|
||||
Once vaulted, this works for the whole fleet (incl. HOWARD-HOME).
|
||||
EOF
|
||||
exit 2
|
||||
fi
|
||||
|
||||
export AR_SITE="$SITE" AR_BAND="$BAND" AR_MODE="$MODE" AR_DBM="$DBM" AR_ZONE="$ZONE"
|
||||
python - <<'PY'
|
||||
import os,sys,json,ssl,urllib.request,http.cookiejar
|
||||
H="172.16.3.29";PORT=11443;base=f"https://{H}:{PORT}"
|
||||
ctx=ssl.create_default_context();ctx.check_hostname=False;ctx.verify_mode=ssl.CERT_NONE
|
||||
cj=http.cookiejar.CookieJar();op=urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj),urllib.request.HTTPSHandler(context=ctx))
|
||||
def call(method,path,body=None,csrf=None,want_headers=False):
|
||||
data=json.dumps(body).encode() if body is not None else None
|
||||
r=urllib.request.Request(base+path,data=data,method=method)
|
||||
r.add_header('Content-Type','application/json')
|
||||
if csrf:r.add_header('X-CSRF-Token',csrf)
|
||||
resp=op.open(r,timeout=20);hdr=dict(resp.headers);txt=resp.read().decode('utf-8','replace')
|
||||
return (txt,hdr) if want_headers else txt
|
||||
# login + CSRF
|
||||
try:_,hd=call('POST','/api/auth/login',{'username':os.environ['RW_U'],'password':os.environ['RW_P']},want_headers=True)
|
||||
except Exception as e:print("[ERROR] login failed:",e);sys.exit(1)
|
||||
csrf=hd.get('X-CSRF-Token') or hd.get('X-Updated-Csrf-Token')
|
||||
# resolve short site name
|
||||
sites=json.loads(call('GET','/proxy/network/api/self/sites')).get('data',[])
|
||||
short=next((s['name'] for s in sites if s.get('_id')==os.environ['AR_SITE']),None)
|
||||
if not short:print("[ERROR] site resolve failed");sys.exit(1)
|
||||
band=os.environ['AR_BAND'];mode=os.environ['AR_MODE'];dbm=os.environ['AR_DBM'];zone=os.environ['AR_ZONE']
|
||||
def zof(n):
|
||||
import re;n=n or ''
|
||||
m=re.search(r'(\d)(?:st|nd|rd|th)\s*floor',n,re.I) or re.search(r'\b(\d)\d{2}\b',n)
|
||||
return ('Floor '+m.group(1)) if m else 'misc'
|
||||
devs=json.loads(call('GET',f'/proxy/network/api/s/{short}/stat/device')).get('data',[])
|
||||
roll=[];done=0;fail=0
|
||||
for d in devs:
|
||||
if d.get('type')!='uap':continue
|
||||
if zone and zof(d.get('name'))!=zone:continue
|
||||
rt=d.get('radio_table') or [];changed=False;old=[]
|
||||
for r in rt:
|
||||
if r.get('radio')!=band:continue
|
||||
old.append({'tx_power_mode':r.get('tx_power_mode'),'tx_power':r.get('tx_power')})
|
||||
if r.get('tx_power_mode')==mode and (mode!='custom' or str(r.get('tx_power'))==dbm):continue
|
||||
r['tx_power_mode']=mode
|
||||
if dbm:r['tx_power']=int(dbm)
|
||||
changed=True
|
||||
if not changed:continue
|
||||
try:
|
||||
call('PUT',f"/proxy/network/api/s/{short}/rest/device/{d['_id']}",{'radio_table':rt},csrf=csrf)
|
||||
roll.append({'id':d['_id'],'name':d.get('name'),'old':old});done+=1;print(f" [ok] {d.get('name')} -> {band} {mode}{('('+dbm+')') if dbm else ''}")
|
||||
except Exception as e:
|
||||
fail+=1;print(f" [FAIL] {d.get('name')}: {e}")
|
||||
rp=os.path.join(os.environ.get('REPO','.'),'.claude','tmp',f"apply-rollback-{short}-{band}.json")
|
||||
try:
|
||||
os.makedirs(os.path.dirname(rp),exist_ok=True);open(rp,'w').write(json.dumps(roll,indent=1))
|
||||
print(f"\n[APPLY] {done} changed, {fail} failed. Rollback saved: {rp}")
|
||||
except Exception as e:print("[APPLY] done; rollback save failed:",e)
|
||||
print("[validate] watch the target APs live: watch-ap.sh <ap-ip> (before/after). Roll out per --zone.")
|
||||
PY
|
||||
|
||||
Reference in New Issue
Block a user