Add an API for exporting activity log data (#15586)
* Add an API for exporting activity log data * Add changelog entry * Switch to error logs
This commit is contained in:
parent
6d668aa60a
commit
df8ae055be
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
core: Add an export API for historical activity log data
|
||||||
|
```
|
|
@ -88,6 +88,8 @@ func buildLogicalRequestNoAuth(perfStandby bool, w http.ResponseWriter, r *http.
|
||||||
responseWriter = w
|
responseWriter = w
|
||||||
case path == "sys/storage/raft/snapshot":
|
case path == "sys/storage/raft/snapshot":
|
||||||
responseWriter = w
|
responseWriter = w
|
||||||
|
case path == "sys/internal/counters/activity/export":
|
||||||
|
responseWriter = w
|
||||||
case path == "sys/monitor":
|
case path == "sys/monitor":
|
||||||
passHTTPReq = true
|
passHTTPReq = true
|
||||||
responseWriter = w
|
responseWriter = w
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
client_id,namespace_id,timestamp,non_entity,mount_accessor
|
||||||
|
111122222-3333-4444-5555-000000000000,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000001,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000002,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000003,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000004,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000005,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000006,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000007,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000008,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000009,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000010,bbbbb,1,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000011,bbbbb,1,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000012,bbbbb,1,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000013,bbbbb,2,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000014,bbbbb,2,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000015,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000016,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000017,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000018,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000019,root,2,false,auth_4
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000000","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000001","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000002","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000003","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000004","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000005","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000006","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000007","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000008","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000009","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000010","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000011","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000012","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000013","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000014","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000015","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000016","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000017","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000018","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000019","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
|
@ -0,0 +1,41 @@
|
||||||
|
client_id,namespace_id,timestamp,non_entity,mount_accessor
|
||||||
|
111122222-3333-4444-5555-000000000000,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000001,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000002,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000003,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000004,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000005,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000006,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000007,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000008,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000009,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000010,bbbbb,1,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000011,bbbbb,1,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000012,bbbbb,1,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000013,bbbbb,2,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000014,bbbbb,2,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000015,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000016,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000017,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000018,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000019,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000020,root,3,false,auth_5
|
||||||
|
111122222-3333-4444-5555-000000000021,root,3,false,auth_5
|
||||||
|
111122222-3333-4444-5555-000000000022,root,3,false,auth_5
|
||||||
|
111122222-3333-4444-5555-000000000023,root,3,false,auth_5
|
||||||
|
111122222-3333-4444-5555-000000000024,root,3,false,auth_5
|
||||||
|
111122222-3333-4444-5555-000000000025,ccccc,3,false,auth_6
|
||||||
|
111122222-3333-4444-5555-000000000026,ccccc,3,false,auth_6
|
||||||
|
111122222-3333-4444-5555-000000000027,ccccc,3,false,auth_6
|
||||||
|
111122222-3333-4444-5555-000000000028,ccccc,3,false,auth_6
|
||||||
|
111122222-3333-4444-5555-000000000029,ccccc,3,false,auth_6
|
||||||
|
111122222-3333-4444-5555-000000000030,root,4,false,auth_7
|
||||||
|
111122222-3333-4444-5555-000000000031,root,4,false,auth_7
|
||||||
|
111122222-3333-4444-5555-000000000032,root,4,false,auth_7
|
||||||
|
111122222-3333-4444-5555-000000000033,root,4,false,auth_7
|
||||||
|
111122222-3333-4444-5555-000000000034,root,4,false,auth_7
|
||||||
|
111122222-3333-4444-5555-000000000035,bbbbb,4,false,auth_8
|
||||||
|
111122222-3333-4444-5555-000000000036,bbbbb,4,false,auth_8
|
||||||
|
111122222-3333-4444-5555-000000000037,bbbbb,4,false,auth_8
|
||||||
|
111122222-3333-4444-5555-000000000038,bbbbb,4,false,auth_8
|
||||||
|
111122222-3333-4444-5555-000000000039,bbbbb,4,false,auth_8
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000000","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000001","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000002","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000003","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000004","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000005","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000006","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000007","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000008","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000009","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000010","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000011","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000012","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000013","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000014","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000015","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000016","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000017","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000018","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000019","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000020","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000021","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000022","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000023","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000024","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000025","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000026","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000027","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000028","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000029","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000030","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000031","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000032","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000033","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000034","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000035","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000036","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000037","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000038","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000039","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
|
@ -0,0 +1,31 @@
|
||||||
|
client_id,namespace_id,timestamp,non_entity,mount_accessor
|
||||||
|
111122222-3333-4444-5555-000000000000,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000001,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000002,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000003,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000004,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000005,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000006,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000007,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000008,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000009,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000010,bbbbb,1,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000011,bbbbb,1,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000012,bbbbb,1,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000013,bbbbb,2,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000014,bbbbb,2,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000015,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000016,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000017,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000018,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000019,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000020,root,3,false,auth_5
|
||||||
|
111122222-3333-4444-5555-000000000021,root,3,false,auth_5
|
||||||
|
111122222-3333-4444-5555-000000000022,root,3,false,auth_5
|
||||||
|
111122222-3333-4444-5555-000000000023,root,3,false,auth_5
|
||||||
|
111122222-3333-4444-5555-000000000024,root,3,false,auth_5
|
||||||
|
111122222-3333-4444-5555-000000000025,ccccc,3,false,auth_6
|
||||||
|
111122222-3333-4444-5555-000000000026,ccccc,3,false,auth_6
|
||||||
|
111122222-3333-4444-5555-000000000027,ccccc,3,false,auth_6
|
||||||
|
111122222-3333-4444-5555-000000000028,ccccc,3,false,auth_6
|
||||||
|
111122222-3333-4444-5555-000000000029,ccccc,3,false,auth_6
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000000","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000001","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000002","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000003","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000004","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000005","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000006","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000007","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000008","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000009","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000010","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000011","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000012","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000013","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000014","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000015","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000016","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000017","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000018","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000019","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000020","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000021","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000022","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000023","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000024","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000025","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000026","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000027","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000028","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000029","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
|
@ -0,0 +1,46 @@
|
||||||
|
client_id,namespace_id,timestamp,non_entity,mount_accessor
|
||||||
|
111122222-3333-4444-5555-000000000040,rrrrr,0,false,auth_9
|
||||||
|
111122222-3333-4444-5555-000000000041,rrrrr,0,false,auth_9
|
||||||
|
111122222-3333-4444-5555-000000000042,rrrrr,0,false,auth_9
|
||||||
|
111122222-3333-4444-5555-000000000043,rrrrr,0,false,auth_9
|
||||||
|
111122222-3333-4444-5555-000000000044,rrrrr,0,false,auth_9
|
||||||
|
111122222-3333-4444-5555-000000000000,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000001,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000002,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000003,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000004,root,1,false,auth_1
|
||||||
|
111122222-3333-4444-5555-000000000005,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000006,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000007,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000008,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000009,aaaaa,1,false,auth_2
|
||||||
|
111122222-3333-4444-5555-000000000010,bbbbb,1,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000011,bbbbb,1,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000012,bbbbb,1,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000013,bbbbb,2,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000014,bbbbb,2,false,auth_3
|
||||||
|
111122222-3333-4444-5555-000000000015,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000016,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000017,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000018,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000019,root,2,false,auth_4
|
||||||
|
111122222-3333-4444-5555-000000000020,root,3,false,auth_5
|
||||||
|
111122222-3333-4444-5555-000000000021,root,3,false,auth_5
|
||||||
|
111122222-3333-4444-5555-000000000022,root,3,false,auth_5
|
||||||
|
111122222-3333-4444-5555-000000000023,root,3,false,auth_5
|
||||||
|
111122222-3333-4444-5555-000000000024,root,3,false,auth_5
|
||||||
|
111122222-3333-4444-5555-000000000025,ccccc,3,false,auth_6
|
||||||
|
111122222-3333-4444-5555-000000000026,ccccc,3,false,auth_6
|
||||||
|
111122222-3333-4444-5555-000000000027,ccccc,3,false,auth_6
|
||||||
|
111122222-3333-4444-5555-000000000028,ccccc,3,false,auth_6
|
||||||
|
111122222-3333-4444-5555-000000000029,ccccc,3,false,auth_6
|
||||||
|
111122222-3333-4444-5555-000000000030,root,4,false,auth_7
|
||||||
|
111122222-3333-4444-5555-000000000031,root,4,false,auth_7
|
||||||
|
111122222-3333-4444-5555-000000000032,root,4,false,auth_7
|
||||||
|
111122222-3333-4444-5555-000000000033,root,4,false,auth_7
|
||||||
|
111122222-3333-4444-5555-000000000034,root,4,false,auth_7
|
||||||
|
111122222-3333-4444-5555-000000000035,bbbbb,4,false,auth_8
|
||||||
|
111122222-3333-4444-5555-000000000036,bbbbb,4,false,auth_8
|
||||||
|
111122222-3333-4444-5555-000000000037,bbbbb,4,false,auth_8
|
||||||
|
111122222-3333-4444-5555-000000000038,bbbbb,4,false,auth_8
|
||||||
|
111122222-3333-4444-5555-000000000039,bbbbb,4,false,auth_8
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000040","namespace_id":"rrrrr","mount_accessor":"auth_9"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000041","namespace_id":"rrrrr","mount_accessor":"auth_9"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000042","namespace_id":"rrrrr","mount_accessor":"auth_9"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000043","namespace_id":"rrrrr","mount_accessor":"auth_9"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000044","namespace_id":"rrrrr","mount_accessor":"auth_9"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000000","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000001","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000002","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000003","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000004","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000005","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000006","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000007","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000008","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000009","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000010","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000011","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000012","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000013","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000014","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000015","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000016","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000017","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000018","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000019","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000020","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000021","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000022","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000023","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000024","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000025","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000026","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000027","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000028","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000029","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000030","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000031","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000032","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000033","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000034","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000035","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000036","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000037","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000038","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||||
|
{"client_id":"111122222-3333-4444-5555-000000000039","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
|
@ -2,9 +2,12 @@ package vault
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/csv"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -20,6 +23,7 @@ import (
|
||||||
"github.com/hashicorp/vault/helper/timeutil"
|
"github.com/hashicorp/vault/helper/timeutil"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
"github.com/hashicorp/vault/vault/activity"
|
"github.com/hashicorp/vault/vault/activity"
|
||||||
|
"go.uber.org/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -160,6 +164,8 @@ type ActivityLog struct {
|
||||||
|
|
||||||
// partialMonthClientTracker tracks active clients this month. Protected by fragmentLock.
|
// partialMonthClientTracker tracks active clients this month. Protected by fragmentLock.
|
||||||
partialMonthClientTracker map[string]*activity.EntityRecord
|
partialMonthClientTracker map[string]*activity.EntityRecord
|
||||||
|
|
||||||
|
inprocessExport *atomic.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// These non-persistent configuration options allow us to disable
|
// These non-persistent configuration options allow us to disable
|
||||||
|
@ -207,6 +213,7 @@ func NewActivityLog(core *Core, logger log.Logger, view *BarrierView, metrics me
|
||||||
clientSequenceNumber: 0,
|
clientSequenceNumber: 0,
|
||||||
},
|
},
|
||||||
standbyFragmentsReceived: make([]*activity.LogFragment, 0),
|
standbyFragmentsReceived: make([]*activity.LogFragment, 0),
|
||||||
|
inprocessExport: atomic.NewBool(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
config, err := a.loadConfigOrDefault(core.activeContext)
|
config, err := a.loadConfigOrDefault(core.activeContext)
|
||||||
|
@ -525,10 +532,7 @@ func (a *ActivityLog) getLastEntitySegmentNumber(ctx context.Context, startTime
|
||||||
}
|
}
|
||||||
|
|
||||||
// WalkEntitySegments loads each of the entity segments for a particular start time
|
// WalkEntitySegments loads each of the entity segments for a particular start time
|
||||||
func (a *ActivityLog) WalkEntitySegments(ctx context.Context,
|
func (a *ActivityLog) WalkEntitySegments(ctx context.Context, startTime time.Time, walkFn func(*activity.EntityActivityLog, time.Time) error) error {
|
||||||
startTime time.Time,
|
|
||||||
walkFn func(*activity.EntityActivityLog, time.Time),
|
|
||||||
) error {
|
|
||||||
basePath := activityEntityBasePath + fmt.Sprint(startTime.Unix()) + "/"
|
basePath := activityEntityBasePath + fmt.Sprint(startTime.Unix()) + "/"
|
||||||
pathList, err := a.view.List(ctx, basePath)
|
pathList, err := a.view.List(ctx, basePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -550,7 +554,10 @@ func (a *ActivityLog) WalkEntitySegments(ctx context.Context,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to parse segment %v%v: %w", basePath, path, err)
|
return fmt.Errorf("unable to parse segment %v%v: %w", basePath, path, err)
|
||||||
}
|
}
|
||||||
walkFn(out, startTime)
|
err = walkFn(out, startTime)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to walk entities: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -2054,7 +2061,7 @@ func (a *ActivityLog) precomputedQueryWorker(ctx context.Context) error {
|
||||||
byNamespace := make(map[string]*processByNamespace)
|
byNamespace := make(map[string]*processByNamespace)
|
||||||
byMonth := make(map[int64]*processMonth)
|
byMonth := make(map[int64]*processMonth)
|
||||||
|
|
||||||
walkEntities := func(l *activity.EntityActivityLog, startTime time.Time) {
|
walkEntities := func(l *activity.EntityActivityLog, startTime time.Time) error {
|
||||||
for _, e := range l.Clients {
|
for _, e := range l.Clients {
|
||||||
processClientRecord(e, byNamespace, byMonth, startTime)
|
processClientRecord(e, byNamespace, byMonth, startTime)
|
||||||
|
|
||||||
|
@ -2102,6 +2109,8 @@ func (a *ActivityLog) precomputedQueryWorker(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
walkTokens := func(l *activity.TokenCount) {
|
walkTokens := func(l *activity.TokenCount) {
|
||||||
|
@ -2569,3 +2578,163 @@ func (a *ActivityLog) partialMonthClientCount(ctx context.Context) (map[string]i
|
||||||
|
|
||||||
return responseData, nil
|
return responseData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *ActivityLog) writeExport(ctx context.Context, rw http.ResponseWriter, format string, startTime, endTime time.Time) error {
|
||||||
|
// For capacity reasons only allow a single in-process export at a time.
|
||||||
|
// TODO do we really need to do this?
|
||||||
|
if !a.inprocessExport.CAS(false, true) {
|
||||||
|
return fmt.Errorf("existing export in progress")
|
||||||
|
}
|
||||||
|
defer a.inprocessExport.Store(false)
|
||||||
|
|
||||||
|
// Find the months with activity log data that are between the start and end
|
||||||
|
// months. We want to walk this in cronological order so the oldest instance of a
|
||||||
|
// client usage is recorded, not the most recent.
|
||||||
|
times, err := a.getMostRecentNonContiguousActivityLogSegments(ctx)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Warn("failed to list recent segments", "error", err)
|
||||||
|
return fmt.Errorf("failed to list recent segments: %w", err)
|
||||||
|
}
|
||||||
|
sort.Slice(times, func(i, j int) bool {
|
||||||
|
// sort in chronological order to produce the output we want showing what
|
||||||
|
// month an entity first had activity.
|
||||||
|
return times[i].Before(times[j])
|
||||||
|
})
|
||||||
|
|
||||||
|
// Filter over just the months we care about
|
||||||
|
filteredList := make([]time.Time, 0, len(times))
|
||||||
|
for _, t := range times {
|
||||||
|
if timeutil.InRange(t, startTime, endTime) {
|
||||||
|
filteredList = append(filteredList, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(filteredList) == 0 {
|
||||||
|
a.logger.Info("no data to export", "start_time", startTime, "end_time", endTime)
|
||||||
|
return fmt.Errorf("no data to export in provided time range")
|
||||||
|
}
|
||||||
|
|
||||||
|
actualStartTime := filteredList[len(filteredList)-1]
|
||||||
|
a.logger.Trace("choose start time for export", "actualStartTime", actualStartTime, "months_included", filteredList)
|
||||||
|
|
||||||
|
// Add headers here because we start to immediately write in the csv encoder
|
||||||
|
// constructor.
|
||||||
|
rw.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=\"activity_export_%d_to_%d.%s\"", actualStartTime.Unix(), endTime.Unix(), format))
|
||||||
|
rw.Header().Add("Content-Type", fmt.Sprintf("application/%s", format))
|
||||||
|
|
||||||
|
var encoder encoder
|
||||||
|
switch format {
|
||||||
|
case "json":
|
||||||
|
encoder = newJSONEncoder(rw)
|
||||||
|
case "csv":
|
||||||
|
var err error
|
||||||
|
encoder, err = newCSVEncoder(rw)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create csv encoder: %w", err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid format: %s", format)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.logger.Info("starting activity log export", "start_time", startTime, "end_time", endTime, "format", format)
|
||||||
|
|
||||||
|
dedupedIds := make(map[string]struct{})
|
||||||
|
walkEntities := func(l *activity.EntityActivityLog, startTime time.Time) error {
|
||||||
|
for _, e := range l.Clients {
|
||||||
|
if _, ok := dedupedIds[e.ClientID]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dedupedIds[e.ClientID] = struct{}{}
|
||||||
|
err := encoder.Encode(e)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each month in the filtered list walk all the log segments
|
||||||
|
for _, startTime := range filteredList {
|
||||||
|
err := a.WalkEntitySegments(ctx, startTime, walkEntities)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Error("failed to load segments for export", "error", err)
|
||||||
|
return fmt.Errorf("failed to load segments for export: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush and error check the encoder. This is neccessary for buffered
|
||||||
|
// encoders like csv.
|
||||||
|
encoder.Flush()
|
||||||
|
if err := encoder.Error(); err != nil {
|
||||||
|
a.logger.Error("failed to flush export encoding", "error", err)
|
||||||
|
return fmt.Errorf("failed to flush export encoding: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type encoder interface {
|
||||||
|
Encode(*activity.EntityRecord) error
|
||||||
|
Flush()
|
||||||
|
Error() error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ encoder = (*jsonEncoder)(nil)
|
||||||
|
|
||||||
|
type jsonEncoder struct {
|
||||||
|
e *json.Encoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func newJSONEncoder(w io.Writer) *jsonEncoder {
|
||||||
|
return &jsonEncoder{
|
||||||
|
e: json.NewEncoder(w),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *jsonEncoder) Encode(er *activity.EntityRecord) error {
|
||||||
|
return j.e.Encode(er)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush is a no-op because json.Encoder doesn't buffer data
|
||||||
|
func (j *jsonEncoder) Flush() {}
|
||||||
|
|
||||||
|
// Error is a no-op because flushing is a no-op.
|
||||||
|
func (j *jsonEncoder) Error() error { return nil }
|
||||||
|
|
||||||
|
var _ encoder = (*csvEncoder)(nil)
|
||||||
|
|
||||||
|
type csvEncoder struct {
|
||||||
|
*csv.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCSVEncoder(w io.Writer) (*csvEncoder, error) {
|
||||||
|
writer := csv.NewWriter(w)
|
||||||
|
|
||||||
|
err := writer.Write([]string{
|
||||||
|
"client_id",
|
||||||
|
"namespace_id",
|
||||||
|
"timestamp",
|
||||||
|
"non_entity",
|
||||||
|
"mount_accessor",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &csvEncoder{
|
||||||
|
Writer: writer,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode converts an export bundle into a set of strings and writes them to the
|
||||||
|
// csv writer.
|
||||||
|
func (c *csvEncoder) Encode(e *activity.EntityRecord) error {
|
||||||
|
return c.Writer.Write([]string{
|
||||||
|
e.ClientID,
|
||||||
|
e.NamespaceID,
|
||||||
|
fmt.Sprintf("%d", e.Timestamp),
|
||||||
|
fmt.Sprintf("%t", e.NonEntity),
|
||||||
|
e.MountAccessor,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
package vault
|
package vault
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -1715,6 +1719,192 @@ func TestActivityLog_refreshFromStoredLogPreviousMonth(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestActivityLog_Export(t *testing.T) {
|
||||||
|
timeutil.SkipAtEndOfMonth(t)
|
||||||
|
|
||||||
|
january := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
august := time.Date(2020, 8, 15, 12, 0, 0, 0, time.UTC)
|
||||||
|
september := timeutil.StartOfMonth(time.Date(2020, 9, 1, 0, 0, 0, 0, time.UTC))
|
||||||
|
october := timeutil.StartOfMonth(time.Date(2020, 10, 1, 0, 0, 0, 0, time.UTC))
|
||||||
|
november := timeutil.StartOfMonth(time.Date(2020, 11, 1, 0, 0, 0, 0, time.UTC))
|
||||||
|
|
||||||
|
core, _, _, _ := TestCoreUnsealedWithMetrics(t)
|
||||||
|
a := core.activityLog
|
||||||
|
ctx := namespace.RootContext(nil)
|
||||||
|
|
||||||
|
// Generate overlapping sets of entity IDs from this list.
|
||||||
|
// january: 40-44 RRRRR
|
||||||
|
// first month: 0-19 RRRRRAAAAABBBBBRRRRR
|
||||||
|
// second month: 10-29 BBBBBRRRRRRRRRRCCCCC
|
||||||
|
// third month: 15-39 RRRRRRRRRRCCCCCRRRRRBBBBB
|
||||||
|
|
||||||
|
entityRecords := make([]*activity.EntityRecord, 45)
|
||||||
|
entityNamespaces := []string{"root", "aaaaa", "bbbbb", "root", "root", "ccccc", "root", "bbbbb", "rrrrr"}
|
||||||
|
authMethods := []string{"auth_1", "auth_2", "auth_3", "auth_4", "auth_5", "auth_6", "auth_7", "auth_8", "auth_9"}
|
||||||
|
|
||||||
|
for i := range entityRecords {
|
||||||
|
entityRecords[i] = &activity.EntityRecord{
|
||||||
|
ClientID: fmt.Sprintf("111122222-3333-4444-5555-%012v", i),
|
||||||
|
NamespaceID: entityNamespaces[i/5],
|
||||||
|
MountAccessor: authMethods[i/5],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toInsert := []struct {
|
||||||
|
StartTime int64
|
||||||
|
Segment uint64
|
||||||
|
Clients []*activity.EntityRecord
|
||||||
|
}{
|
||||||
|
// January, should not be included
|
||||||
|
{
|
||||||
|
january.Unix(),
|
||||||
|
0,
|
||||||
|
entityRecords[40:45],
|
||||||
|
},
|
||||||
|
// Artifically split August and October
|
||||||
|
{ // 1
|
||||||
|
august.Unix(),
|
||||||
|
0,
|
||||||
|
entityRecords[:13],
|
||||||
|
},
|
||||||
|
{ // 2
|
||||||
|
august.Unix(),
|
||||||
|
1,
|
||||||
|
entityRecords[13:20],
|
||||||
|
},
|
||||||
|
{ // 3
|
||||||
|
september.Unix(),
|
||||||
|
0,
|
||||||
|
entityRecords[10:30],
|
||||||
|
},
|
||||||
|
{ // 4
|
||||||
|
october.Unix(),
|
||||||
|
0,
|
||||||
|
entityRecords[15:40],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
october.Unix(),
|
||||||
|
1,
|
||||||
|
entityRecords[15:40],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
october.Unix(),
|
||||||
|
2,
|
||||||
|
entityRecords[17:23],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, segment := range toInsert {
|
||||||
|
eal := &activity.EntityActivityLog{
|
||||||
|
Clients: segment.Clients,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mimic a lower time stamp for earlier clients
|
||||||
|
for _, c := range eal.Clients {
|
||||||
|
c.Timestamp = int64(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := proto.Marshal(eal)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
path := fmt.Sprintf("%ventity/%v/%v", ActivityLogPrefix, segment.StartTime, segment.Segment)
|
||||||
|
WriteToStorage(t, core, path, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
tCases := []struct {
|
||||||
|
format string
|
||||||
|
startTime time.Time
|
||||||
|
endTime time.Time
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
format: "json",
|
||||||
|
startTime: august,
|
||||||
|
endTime: timeutil.EndOfMonth(september),
|
||||||
|
expected: "aug_sep.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "csv",
|
||||||
|
startTime: august,
|
||||||
|
endTime: timeutil.EndOfMonth(september),
|
||||||
|
expected: "aug_sep.csv",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "json",
|
||||||
|
startTime: january,
|
||||||
|
endTime: timeutil.EndOfMonth(november),
|
||||||
|
expected: "full_history.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "csv",
|
||||||
|
startTime: january,
|
||||||
|
endTime: timeutil.EndOfMonth(november),
|
||||||
|
expected: "full_history.csv",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "json",
|
||||||
|
startTime: august,
|
||||||
|
endTime: timeutil.EndOfMonth(october),
|
||||||
|
expected: "aug_oct.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "csv",
|
||||||
|
startTime: august,
|
||||||
|
endTime: timeutil.EndOfMonth(october),
|
||||||
|
expected: "aug_oct.csv",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "json",
|
||||||
|
startTime: august,
|
||||||
|
endTime: timeutil.EndOfMonth(august),
|
||||||
|
expected: "aug.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "csv",
|
||||||
|
startTime: august,
|
||||||
|
endTime: timeutil.EndOfMonth(august),
|
||||||
|
expected: "aug.csv",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tCase := range tCases {
|
||||||
|
rw := &fakeResponseWriter{
|
||||||
|
buffer: &bytes.Buffer{},
|
||||||
|
headers: http.Header{},
|
||||||
|
}
|
||||||
|
if err := a.writeExport(ctx, rw, tCase.format, tCase.startTime, tCase.endTime); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected, err := os.ReadFile(filepath.Join("activity", "test_fixtures", tCase.expected))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(rw.buffer.Bytes(), expected) {
|
||||||
|
t.Fatal(rw.buffer.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type fakeResponseWriter struct {
|
||||||
|
buffer *bytes.Buffer
|
||||||
|
headers http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeResponseWriter) Write(b []byte) (int, error) {
|
||||||
|
return f.buffer.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeResponseWriter) Header() http.Header {
|
||||||
|
return f.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeResponseWriter) WriteHeader(statusCode int) {
|
||||||
|
panic("unimplmeneted")
|
||||||
|
}
|
||||||
|
|
||||||
func TestActivityLog_IncludeNamespace(t *testing.T) {
|
func TestActivityLog_IncludeNamespace(t *testing.T) {
|
||||||
root := namespace.RootNamespace
|
root := namespace.RootNamespace
|
||||||
a := &ActivityLog{}
|
a := &ActivityLog{}
|
||||||
|
|
|
@ -5198,6 +5198,10 @@ This path responds to the following HTTP methods.
|
||||||
"Query the historical count of clients.",
|
"Query the historical count of clients.",
|
||||||
"Query the historical count of clients.",
|
"Query the historical count of clients.",
|
||||||
},
|
},
|
||||||
|
"activity-export": {
|
||||||
|
"Export the historical activity of clients.",
|
||||||
|
"Export the historical activity of clients.",
|
||||||
|
},
|
||||||
"activity-monthly": {
|
"activity-monthly": {
|
||||||
"Count of active clients so far this month.",
|
"Count of active clients so far this month.",
|
||||||
"Count of active clients so far this month.",
|
"Count of active clients so far this month.",
|
||||||
|
|
|
@ -2,7 +2,9 @@ package vault
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -97,15 +99,37 @@ func (b *SystemBackend) rootActivityPaths() []*framework.Path {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Pattern: "internal/counters/activity/export$",
|
||||||
|
Fields: map[string]*framework.FieldSchema{
|
||||||
|
"start_time": {
|
||||||
|
Type: framework.TypeTime,
|
||||||
|
Description: "Start of query interval",
|
||||||
|
},
|
||||||
|
"end_time": {
|
||||||
|
Type: framework.TypeTime,
|
||||||
|
Description: "End of query interval",
|
||||||
|
},
|
||||||
|
"format": {
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: "Format of the file. Either a CSV or a JSON file with an object per line.",
|
||||||
|
Default: "json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
HelpSynopsis: strings.TrimSpace(sysHelp["activity-export"][0]),
|
||||||
|
HelpDescription: strings.TrimSpace(sysHelp["activity-export"][1]),
|
||||||
|
|
||||||
|
Operations: map[logical.Operation]framework.OperationHandler{
|
||||||
|
logical.ReadOperation: &framework.PathOperation{
|
||||||
|
Callback: b.handleClientExport,
|
||||||
|
Summary: "Report the client count metrics, for this namespace and all child namespaces.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *SystemBackend) handleClientMetricQuery(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
func parseStartEndTimes(a *ActivityLog, d *framework.FieldData) (time.Time, time.Time, error) {
|
||||||
a := b.Core.activityLog
|
|
||||||
if a == nil {
|
|
||||||
return logical.ErrorResponse("no activity log present"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
startTime := d.Get("start_time").(time.Time)
|
startTime := d.Get("start_time").(time.Time)
|
||||||
endTime := d.Get("end_time").(time.Time)
|
endTime := d.Get("end_time").(time.Time)
|
||||||
|
|
||||||
|
@ -126,7 +150,52 @@ func (b *SystemBackend) handleClientMetricQuery(ctx context.Context, req *logica
|
||||||
startTime = startTime.UTC()
|
startTime = startTime.UTC()
|
||||||
}
|
}
|
||||||
if startTime.After(endTime) {
|
if startTime.After(endTime) {
|
||||||
return logical.ErrorResponse("start_time is later than end_time"), nil
|
return time.Time{}, time.Time{}, fmt.Errorf("start_time is later than end_time")
|
||||||
|
}
|
||||||
|
|
||||||
|
return startTime, endTime, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SystemBackend) handleClientExport(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||||
|
a := b.Core.activityLog
|
||||||
|
if a == nil {
|
||||||
|
return logical.ErrorResponse("no activity log present"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
startTime, endTime, err := parseStartEndTimes(a, d)
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse(err.Error()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is to avoid the default 90s context timeout.
|
||||||
|
timeout := 10 * time.Minute
|
||||||
|
if durationRaw := os.Getenv("VAULT_ACTIVITY_EXPORT_DURATION"); durationRaw != "" {
|
||||||
|
d, err := time.ParseDuration(durationRaw)
|
||||||
|
if err == nil {
|
||||||
|
timeout = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runCtx, cancelFunc := context.WithTimeout(b.Core.activeContext, timeout)
|
||||||
|
defer cancelFunc()
|
||||||
|
|
||||||
|
err = a.writeExport(runCtx, req.ResponseWriter, d.Get("format").(string), startTime, endTime)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SystemBackend) handleClientMetricQuery(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||||
|
a := b.Core.activityLog
|
||||||
|
if a == nil {
|
||||||
|
return logical.ErrorResponse("no activity log present"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
startTime, endTime, err := parseStartEndTimes(a, d)
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse(err.Error()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
results, err := a.handleQuery(ctx, startTime, endTime)
|
results, err := a.handleQuery(ctx, startTime, endTime)
|
||||||
|
|
|
@ -848,3 +848,58 @@ $ curl \
|
||||||
"warnings": null
|
"warnings": null
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Activity Export
|
||||||
|
|
||||||
|
This endpoint returns an export of the clients that had activity within the
|
||||||
|
provided start and end times. The returned set of client information will be
|
||||||
|
deduplicated over the time window and will show the earliest activity logged for
|
||||||
|
each client. The output will be ordered chronologically by month of activity.
|
||||||
|
|
||||||
|
~> **NOTE**: This endpoint is currently in tech preview status.
|
||||||
|
|
||||||
|
There are a few things to keep in mind while using this API.
|
||||||
|
|
||||||
|
- The response includes the actual time period covered, which may not exactly
|
||||||
|
match the query parameters due to the month granularity of data or missing
|
||||||
|
months in the requested time range.
|
||||||
|
|
||||||
|
- If the `end_date` supplied to the API is for the current month, the activity
|
||||||
|
information returned by this API will include activity for this month, however
|
||||||
|
it may be up to 20 minutes delayed.
|
||||||
|
|
||||||
|
This endpoint was added in Vault 1.11.
|
||||||
|
|
||||||
|
| Method | Path |
|
||||||
|
| :----- | :---------------------------------------- |
|
||||||
|
| `GET` | `/sys/internal/counters/activity/export` |
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
- `start_time` `(string, optional)` - An RFC3339 timestamp or Unix epoch time. Specifies the start of the
|
||||||
|
period for which client counts will be reported. If no start time is specified, the `default_report_months`
|
||||||
|
prior to the `end_time` will be used.
|
||||||
|
- `end_time` `(string, optional)` - An RFC3339 timestamp or Unix epoch time. Specifies the end of the period
|
||||||
|
for which client counts will be reported. If no end time is specified, the end of the previous calendar
|
||||||
|
month will be used.
|
||||||
|
- `format` `(string, optional)` - The desired format of the output file. Allowed
|
||||||
|
values are `csv` and `json`. If no format is provided a default of `json`
|
||||||
|
will be used.
|
||||||
|
|
||||||
|
### Sample Request
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ curl \
|
||||||
|
--header "X-Vault-Token: ..." \
|
||||||
|
--request GET \
|
||||||
|
http://127.0.0.1:8200/v1/sys/internal/counters/activity/export
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sample Response
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"client_id":"3f210722-7210-98e8-1f0d-e6a39ffb29c6","namespace_id":"root","timestamp":1653350457,"mount_accessor":"auth_userpass_bb52979d"}
|
||||||
|
{"client_id":"X/Yed4Oj4cqODj9tSHjKwnRy5QVSBRlX3COxjjWSXyI=","namespace_id":"root","timestamp":1653350491,"non_entity":true,"mount_accessor":"auth_token_f6f2c11c"}
|
||||||
|
{"client_id":"d93405dc-b592-b1c3-a520-14e618d359c1","namespace_id":"root","timestamp":1653350501,"mount_accessor":"auth_userpass_bb52979d"}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue