Lint / PHP (phpcs PSR-12) (push) Failing after 31s
Lint / JS (eslint) (push) Successful in 8s
Lint / PHP requirements (version + extensions) (push) Successful in 22s
Lint / Deploy (push) Skipped
Security / PHP Security (semgrep) (push) Successful in 1m17s
Lint / Notify on failure (push) Successful in 8s
69 lines
9.4 KiB
Python
69 lines
9.4 KiB
Python
import subprocess, hashlib, random, json
|
|
from datetime import datetime, timedelta
|
|
random.seed(7)
|
|
import pathlib
|
|
D=str(pathlib.Path(__file__).resolve().parent)
|
|
now=datetime.now().replace(microsecond=0)
|
|
def q(s): return "'"+str(s).replace("\\","\\\\").replace("'","\\'")+"'"
|
|
def ts(h): return q((now-timedelta(hours=h)).strftime('%Y-%m-%d %H:%M:%S'))
|
|
sql=[]
|
|
users=[(1,'alex.rivera','Alex Rivera','alex@example.com','admin,employee',1),
|
|
(2,'sam.chen','Sam Chen','sam@example.com','employee',0),
|
|
(3,'riley.brooks','Riley Brooks','riley@example.com','employee',0),
|
|
(4,'jordan.kim','Jordan Kim','jordan@example.com','employee',0),
|
|
(5,'hwmon-daemon','hwmonDaemon','','employee',0)]
|
|
for u in users: sql.append(f"INSERT INTO users(user_id,username,display_name,email,`groups`,is_admin,last_login) VALUES({u[0]},{q(u[1])},{q(u[2])},{q(u[3])},{q(u[4])},{u[5]},{ts(1)});")
|
|
tr=[('Open','In Progress',0,0),('Open','Pending',0,0),('Open','Closed',1,0),('Pending','Open',0,0),('Pending','In Progress',0,0),('Pending','Closed',1,0),('In Progress','Pending',0,0),('In Progress','Open',0,0),('In Progress','Closed',1,0),('Closed','Open',0,1)]
|
|
for a,b,c,d in tr: sql.append(f"INSERT INTO status_transitions(from_status,to_status,requires_comment,requires_admin) VALUES({q(a)},{q(b)},{c},{d});")
|
|
# (title, cat, type, status, prio, assignee, creator, age_h, desc)
|
|
T=[
|
|
("[storage-01][auto][hardware] Drive SN-4F21A8 has SMART issues [single-node][production][problem]","Hardware","Problem","Open",2,2,5,3,"SMART reports 12 reallocated sectors and 1 pending sector on /dev/sdc.\n\n```\nReallocated_Sector_Ct 12\nCurrent_Pending_Sector 1\n```\nOSD.14 is backed by this drive. Plan a replacement in the next maintenance window."),
|
|
("[node-02][auto][hardware] CPU temperature sustained above 85C [single-node][production][problem]","Hardware","Problem","Open",2,None,5,5,"15-minute average package temperature 87C (threshold 85C). Fans at 100%. Check airflow and heatsink seating."),
|
|
("[proxmox-cluster][auto][software] Ceph HEALTH_WARN: 3 slow ops on osd.9 [cluster-wide][production][problem]","Software","Problem","In Progress",3,1,5,26,"`ceph health detail` reports slow BlueStore ops on osd.9. PGs remain active+clean. Triage steps in the Ceph runbook."),
|
|
("[node-03][auto][hardware] LXC storage pool above 85% usage [single-node][production][maintenance]","Hardware","Maintenance","Open",3,3,5,9,"Pool plpSSDPool is at 86%. Grow or move guests before it reaches 90%."),
|
|
("[switch-02][auto][network] Link down on uplink port 14 [single-node][production][problem]","Network","Problem","Open",1,1,5,2,"Port 14 (10GbE uplink to node-03) went down 2 hours ago. Traffic failed over to the backup path. Cable or SFP+ suspected."),
|
|
("[proxmox-cluster][manual][software] Upgrade Proxmox VE 8.3 to 8.4 [cluster-wide][production][upgrade]","Software","Upgrade","Pending",3,2,1,70,"Rolling upgrade, one node at a time. Blocked until the Ceph warning on osd.9 is resolved."),
|
|
("[monitor-01][manual][software] Renew TLS certificate for status page [single-node][production][maintenance]","Software","Maintenance","In Progress",4,4,1,30,"Certificate expires in 12 days. Renewal job failed on the DNS challenge."),
|
|
("[proxmox-cluster][manual][security] Rotate API keys used by monitoring agents [cluster-wide][production][task]","Security","Task","Open",3,1,1,48,"Quarterly rotation. Keys: hwmonDaemon, gandalf, pulse workers."),
|
|
("[node-01][auto][hardware] Drive SN-88C0D2 has SMART issues [single-node][production][problem]","Hardware","Problem","Closed",2,2,5,150,"Replaced during maintenance. OSD rebuilt, cluster healthy."),
|
|
("[proxmox-cluster][manual][configuration] Add Prometheus scrape target for new exporter [cluster-wide][production][task]","General","Task","Closed",5,3,3,200,"Added and verified in Targets."),
|
|
("[node-02][manual][hardware] Replace failed NVMe in bay 2 [single-node][production][maintenance]","Hardware","Maintenance","Pending",2,2,1,52,"Replacement ordered. Awaiting delivery."),
|
|
("[storage-01][auto][hardware] Memory ECC correctable errors increasing [single-node][production][problem]","Hardware","Problem","Open",3,None,5,14,"EDAC reports 41 correctable errors on DIMM_B1 in 24 hours."),
|
|
("[proxmox-cluster][manual][software] LXC 134 has no log retention configured [single-node][production][maintenance]","Software","Maintenance","Open",4,None,3,96,"Log volume grows without bound. Add retention and a compactor."),
|
|
("[backup-01][manual][software] Verify weekly offsite backup restore [single-node][production][task]","General","Task","In Progress",4,4,1,40,"Restore one guest from the weekly backup and confirm it boots."),
|
|
("[switch-01][manual][network] Move camera VLAN to dedicated port group [single-node][testing][configuration]","Network","Task","Pending",5,3,3,120,"Needs a maintenance window."),
|
|
("[node-03][auto][hardware] Fan 2 speed below threshold [single-node][production][problem]","Hardware","Problem","Closed",3,1,5,310,"Fan replaced."),
|
|
("[proxmox-cluster][manual][software] Document UPS shutdown order [cluster-wide][production][task]","General","Task","Closed",4,2,2,400,"Documented in the wiki."),
|
|
("[node-02][auto][hardware] Drive SN-1B77E4 temperature above 55C [single-node][production][problem]","Hardware","Problem","Open",4,None,5,6,"Drive temperature 57C, threshold 55C."),
|
|
("[proxmox-cluster][manual][security] Review SSH access for decommissioned hosts [cluster-wide][production][task]","Security","Task","Pending",3,1,1,88,"Remove stale authorized_keys entries."),
|
|
("[monitor-01][manual][software] Grafana alert rules need review [single-node][production][maintenance]","Software","Maintenance","Open",4,4,1,60,"Several rules have no contact point."),
|
|
]
|
|
tids=[]
|
|
for i,t in enumerate(T):
|
|
tid=str(100000000+random.randint(1000000,899999999)); tids.append(tid)
|
|
title,cat,typ,status,pr,asg,cr,age,desc=t
|
|
closed=ts(max(age-5,1)) if status=='Closed' else 'NULL'
|
|
sql.append(f"INSERT INTO tickets(ticket_id,title,category,type,status,description,created_at,updated_at,closed_at,priority,hash,created_by,updated_by,assigned_to) VALUES({q(tid)},{q(title)},{q(cat)},{q(typ)},{q(status)},{q(desc)},{ts(age)},{ts(max(age-3,1))},{closed},{pr},{q(hashlib.sha256(title.encode()).hexdigest())},{cr},{cr},{asg if asg else 'NULL'});")
|
|
sql.append(f"INSERT INTO audit_log(user_id,action_type,entity_type,entity_id,details,created_at) VALUES({cr},'create','ticket',{q(tid)},{q(json.dumps({'title':title}))},{ts(age)});")
|
|
if asg: sql.append(f"INSERT INTO audit_log(user_id,action_type,entity_type,entity_id,details,created_at) VALUES(1,'assign','ticket',{q(tid)},{q(json.dumps({'assigned_to':{'from':'','to':users[asg-1][2]}}))},{ts(max(age-1,1))});")
|
|
if status!='Open': sql.append(f"INSERT INTO audit_log(user_id,action_type,entity_type,entity_id,details,created_at) VALUES({asg or 1},'status_change','ticket',{q(tid)},{q(json.dumps({'status':{'from':'Open','to':status}}))},{ts(max(age-2,1))});")
|
|
C={0:[(2,"Confirmed on the node. Pulling the drive from the pool tonight and ordering a replacement.",2),(1,"Approved. Use the spare from the shelf, then reorder.",1)],
|
|
2:[(1,"Slow ops are isolated to osd.9. `ceph tell osd.9 dump_historic_slow_ops` shows long waits on the DB device.",20),(2,"Marked the OSD out for now. Recovery is progressing.",12)],
|
|
4:[(1,"Failover worked as designed. Swapping the SFP+ module first.",1)],
|
|
5:[(2,"Waiting for the Ceph warning to clear before starting the first node.",30)],
|
|
6:[(4,"DNS challenge fails because the API token expired. Generated a new one.",8)]}
|
|
for idx,cs in C.items():
|
|
for uid,txt,age in cs:
|
|
sql.append(f"INSERT INTO ticket_comments(ticket_id,user_name,comment_text,created_at,markdown_enabled,user_id) VALUES({q(tids[idx])},{q(users[uid-1][2])},{q(txt)},{ts(age)},1,{uid});")
|
|
sql.append(f"INSERT INTO audit_log(user_id,action_type,entity_type,entity_id,details,created_at) VALUES({uid},'comment','comment','0',{q(json.dumps({'ticket_id':tids[idx]}))},{ts(age)});")
|
|
sql.append(f"INSERT INTO ticket_dependencies(ticket_id,depends_on_id,dependency_type,created_by) VALUES({q(tids[5])},{q(tids[2])},'blocked_by',1);")
|
|
sql.append(f"INSERT INTO ticket_dependencies(ticket_id,depends_on_id,dependency_type,created_by) VALUES({q(tids[0])},{q(tids[8])},'relates_to',1);")
|
|
sql.append(f"INSERT INTO ticket_dependencies(ticket_id,depends_on_id,dependency_type,created_by) VALUES({q(tids[4])},{q(tids[14])},'relates_to',1);")
|
|
for tid in (tids[0],tids[2],tids[4]): sql.append(f"INSERT INTO ticket_watchers(ticket_id,user_id) VALUES({q(tid)},1);")
|
|
for n,p,s in (('hwmonDaemon (prod)','hwm_9f3a','read_write'),('gandalf','gnd_71c2','read_write'),('readonly-dashboard','ro_55de','read')):
|
|
sql.append(f"INSERT INTO api_keys(key_name,key_hash,key_prefix,scope,created_by,last_used) VALUES({q(n)},{q(hashlib.sha256(p.encode()).hexdigest())},{q(p)},{q(s)},1,{ts(2)});")
|
|
sql.append(f"INSERT INTO recurring_tickets(title_template,description_template,category,type,priority,schedule_type,schedule_day,next_run_at,created_by) VALUES('[proxmox-cluster][auto][software] Monthly patch review [cluster-wide][production][maintenance]','Review pending updates and schedule a window.','Software','Maintenance',4,'monthly',1,{ts(-200)},1);")
|
|
sql.append(f"INSERT INTO recurring_tickets(title_template,description_template,category,type,priority,schedule_type,schedule_day,next_run_at,created_by) VALUES('[backup-01][auto][software] Weekly restore test [single-node][production][task]','Restore one guest and verify.','General','Task',4,'weekly',1,{ts(-60)},1);")
|
|
open(D+'/seed.sql','w').write('\n'.join(sql))
|
|
print(len(T),'tickets; ids sample',tids[:3])
|