Compare commits
492 Commits
v3.1.0
...
main_backu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fb88c94e8 | ||
|
|
44de1531a5 | ||
|
|
5e526ba81e | ||
|
|
506b0836ea | ||
|
|
c2992c3348 | ||
|
|
aaf1b16912 | ||
|
|
6bf348e5c3 | ||
|
|
4ed31a52e5 | ||
|
|
efb230a5fc | ||
|
|
da8f91c78c | ||
|
|
3fae05d971 | ||
|
|
5893ea8db4 | ||
|
|
10ce2923c7 | ||
|
|
cd7ba9986c | ||
|
|
1084be914b | ||
|
|
f01825b180 | ||
|
|
5bf9f7f4ea | ||
|
|
f71b3480f0 | ||
|
|
87741ba994 | ||
|
|
0a1d17c9cc | ||
|
|
e647b35f6f | ||
|
|
b15be9796d | ||
|
|
b40fb09c1f | ||
|
|
99ff8ca4e1 | ||
|
|
ebb901c2b2 | ||
|
|
094c2380cd | ||
|
|
1b8af0d862 | ||
|
|
0b93ca41ab | ||
|
|
69b0ea4572 | ||
|
|
0fc596e06b | ||
|
|
a5fa613bea | ||
|
|
46699880ce | ||
|
|
22f72139e3 | ||
|
|
eec1c25989 | ||
|
|
514f239cc6 | ||
|
|
21931e991c | ||
|
|
0c601d0383 | ||
|
|
8e3a0a0afa | ||
|
|
7fe5df7fc4 | ||
|
|
8bf1440f9b | ||
|
|
e9b52a69b7 | ||
|
|
4f16105b67 | ||
|
|
7e0f55cdb8 | ||
|
|
31955c0c84 | ||
|
|
ef6585a298 | ||
|
|
5d9dd7a2d9 | ||
|
|
a39541623a | ||
|
|
63297ef0ed | ||
|
|
4527b38aa6 | ||
|
|
050ca68907 | ||
|
|
cda0a28c03 | ||
|
|
b8ca5f1dca | ||
|
|
196d8855c3 | ||
|
|
e1a396981a | ||
|
|
10c8604159 | ||
|
|
722fe97c84 | ||
|
|
e62bee3052 | ||
|
|
7999eb56a5 | ||
|
|
21598fd792 | ||
|
|
df52236a7d | ||
|
|
470f52f35e | ||
|
|
5475155686 | ||
|
|
0a6078df65 | ||
|
|
fabbde9641 | ||
|
|
69e7b23d2c | ||
|
|
ae8afbdcdc | ||
|
|
6ce816f2e1 | ||
|
|
f840e924e3 | ||
|
|
3bbc7b8615 | ||
|
|
f6a9063484 | ||
|
|
994546046e | ||
|
|
90620e0c64 | ||
|
|
3731b2ba13 | ||
|
|
dd9613853b | ||
|
|
f22681d6b4 | ||
|
|
e9fc9a0d1a | ||
|
|
1e24ce7b9a | ||
|
|
afbe2e1400 | ||
|
|
f8959a65e9 | ||
|
|
ba6f8f054b | ||
|
|
52a7185583 | ||
|
|
d730a206d8 | ||
|
|
713aa279ec | ||
|
|
e070b247b1 | ||
|
|
a10a1d9267 | ||
|
|
9e3285350a | ||
|
|
c5f612889e | ||
|
|
9c6ee73b9e | ||
|
|
a2ef4328dd | ||
|
|
393505b98b | ||
|
|
e198c17ac0 | ||
|
|
a7a5fb8598 | ||
|
|
3d0e50823a | ||
|
|
15f37c938f | ||
|
|
9520cfea24 | ||
|
|
81295a4fc4 | ||
|
|
a2f43b5abc | ||
|
|
95b6211781 | ||
|
|
3aab6a3738 | ||
|
|
1721f2eb84 | ||
|
|
2d1a1e6db7 | ||
|
|
35af1e9d1e | ||
|
|
e1ac628793 | ||
|
|
bfce2f9a7b | ||
|
|
257be9532f | ||
|
|
57157db08c | ||
|
|
39b30460cd | ||
|
|
dd21f9440f | ||
|
|
a432822bcb | ||
|
|
ec938ce761 | ||
|
|
007a0c4b98 | ||
|
|
2526427f2f | ||
|
|
cc09fe9079 | ||
|
|
78d7f39342 | ||
|
|
da20c0e9e6 | ||
|
|
360eb38c2b | ||
|
|
6b76214eb2 | ||
|
|
75c91b9eb9 | ||
|
|
432da5ca90 | ||
|
|
9a0b14b111 | ||
|
|
707d8f6189 | ||
|
|
50697c6f75 | ||
|
|
9df8d132fc | ||
|
|
2bb71623fa | ||
|
|
ea7c1519fe | ||
|
|
8aa71c3ff8 | ||
|
|
7c2ff55da2 | ||
|
|
1b6244d13f | ||
|
|
661c865108 | ||
|
|
eb75d15722 | ||
|
|
31fedb945e | ||
|
|
4e53564670 | ||
|
|
8e2f105d57 | ||
|
|
f9c7cafe87 | ||
|
|
e743b2380c | ||
|
|
c2931bde6c | ||
|
|
436448beed | ||
|
|
7b929109dc | ||
|
|
118b05ede0 | ||
|
|
9650669c83 | ||
|
|
558223e8b6 | ||
|
|
77f717c649 | ||
|
|
738e5b5dca | ||
|
|
606354614a | ||
|
|
36e48dc8e9 | ||
|
|
0ab4dab72a | ||
|
|
f8cb18c654 | ||
|
|
fd2ab39edf | ||
|
|
f5e1ef7491 | ||
|
|
f4a70394e0 | ||
|
|
c61770825a | ||
|
|
a19fbd1e98 | ||
|
|
85c360548b | ||
|
|
06c20f6984 | ||
|
|
96b48aad79 | ||
|
|
f817efdc69 | ||
|
|
c7547f715e | ||
|
|
d4469f5226 | ||
|
|
3c20764096 | ||
|
|
11defda955 | ||
|
|
5b3663c3cf | ||
|
|
baa14b6cd7 | ||
|
|
79724d6884 | ||
|
|
30d8478a78 | ||
|
|
0ec2f79ac3 | ||
|
|
0c63d197e5 | ||
|
|
585d35338f | ||
|
|
f3750032be | ||
|
|
350b3db3f6 | ||
|
|
6a888d2efe | ||
|
|
5f6bc32b98 | ||
|
|
6b59ce960d | ||
|
|
9e936513d5 | ||
|
|
dc26863216 | ||
|
|
56fd9c4d0a | ||
|
|
1633900ce7 | ||
|
|
6c7ec62166 | ||
|
|
a07ce00852 | ||
|
|
dcf5bfddfa | ||
|
|
a303e9096a | ||
|
|
81a8058836 | ||
|
|
c641f67914 | ||
|
|
7f64d06ad4 | ||
|
|
c8191e60ba | ||
|
|
d555584e90 | ||
|
|
548ecf8e08 | ||
|
|
d8cfbc1042 | ||
|
|
831469d01c | ||
|
|
19a9cef130 | ||
|
|
5939fda2ba | ||
|
|
ecc06a5db0 | ||
|
|
30bcdba429 | ||
|
|
563ff72fd6 | ||
|
|
ca737e080f | ||
|
|
921dc934c2 | ||
|
|
5ec8d3c8c1 | ||
|
|
43a4f93626 | ||
|
|
3c9b8a272a | ||
|
|
e3ad1470df | ||
|
|
bd6f9fc309 | ||
|
|
47d7e9f3f6 | ||
|
|
0145532103 | ||
|
|
91287f8666 | ||
|
|
605a5b4cd6 | ||
|
|
17894376c6 | ||
|
|
9d49406df6 | ||
|
|
23adc740cd | ||
|
|
41fab6562e | ||
|
|
752a611b89 | ||
|
|
677d12b550 | ||
|
|
40381833d3 | ||
|
|
35b481a2f3 | ||
|
|
d49f73f139 | ||
|
|
d7bf678b1f | ||
|
|
e03c29b921 | ||
|
|
bbb305d561 | ||
|
|
4ef7831bfa | ||
|
|
7e0eee5d73 | ||
|
|
f4b98542b6 | ||
|
|
036e0fe342 | ||
|
|
04e52453b1 | ||
|
|
207601b81f | ||
|
|
3a75ed8ccc | ||
|
|
86725064ec | ||
|
|
2a08d3a2a5 | ||
|
|
82a5122276 | ||
|
|
4c03aaee24 | ||
|
|
1ade92f55c | ||
|
|
c9f4741655 | ||
|
|
82d7c168fe | ||
|
|
159d47fd6c | ||
|
|
46b44f9e23 | ||
|
|
3b91a99070 | ||
|
|
c5d7d55747 | ||
|
|
99f1f7cfc1 | ||
|
|
3438ce7e09 | ||
|
|
4371ca13fc | ||
|
|
f651937759 | ||
|
|
e174db5d9e | ||
|
|
87bdaa1ec3 | ||
|
|
c38dda34b9 | ||
|
|
8faf9d70a0 | ||
|
|
b426d5802e | ||
|
|
5c167293e3 | ||
|
|
0ae7158859 | ||
|
|
713225b432 | ||
|
|
7482aead76 | ||
|
|
f62b2aef1c | ||
|
|
171b5cb58e | ||
|
|
b3d4eb4ec0 | ||
|
|
075b11aeca | ||
|
|
f121b0e21c | ||
|
|
940f37bfc6 | ||
|
|
38a3666e8e | ||
|
|
ea01e2209b | ||
|
|
3ced1a1913 | ||
|
|
cda9819e72 | ||
|
|
eac79e0941 | ||
|
|
3f440c0a28 | ||
|
|
9001510fe7 | ||
|
|
0756e61e5d | ||
|
|
7bdcee5c46 | ||
|
|
d41e2ad590 | ||
|
|
5e270c7673 | ||
|
|
3de6208915 | ||
|
|
3abe39aa10 | ||
|
|
410afc1d40 | ||
|
|
e199dc52c0 | ||
|
|
34b294f89a | ||
|
|
410d5abe12 | ||
|
|
820f436f8e | ||
|
|
7816e94f83 | ||
|
|
dffa612ec8 | ||
|
|
4b5cf12aa3 | ||
|
|
7ddedfcc57 | ||
|
|
a883e45bbf | ||
|
|
8ab936fcdc | ||
|
|
d2764d53cc | ||
|
|
12c18adeb1 | ||
|
|
178bd4dc7f | ||
|
|
f47dd8bf61 | ||
|
|
dc4c2c8854 | ||
|
|
8101f49a21 | ||
|
|
63a4c1119b | ||
|
|
94ca28a85f | ||
|
|
cb9d40579f | ||
|
|
0943e9511b | ||
|
|
c3ec33e4df | ||
|
|
123ec8343c | ||
|
|
c38c13b829 | ||
|
|
6c4a538b41 | ||
|
|
ae56827bae | ||
|
|
4299170ce4 | ||
|
|
4f0f542570 | ||
|
|
ee94993344 | ||
|
|
41aed12f27 | ||
|
|
ccad245e6f | ||
|
|
16534237e0 | ||
|
|
27ca53878a | ||
|
|
605ff98a24 | ||
|
|
975ebd6de2 | ||
|
|
203cc98494 | ||
|
|
32d5889b11 | ||
|
|
2a8d28cba6 | ||
|
|
3e691b4f46 | ||
|
|
d5402cdce8 | ||
|
|
c3c4512544 | ||
|
|
73015802ec | ||
|
|
c77fde3dd3 | ||
|
|
f61d50a96f | ||
|
|
a2e02c3cfd | ||
|
|
7f912df383 | ||
|
|
1d3778f400 | ||
|
|
7482192046 | ||
|
|
9666b92caa | ||
|
|
498e6a7ac1 | ||
|
|
02f982afa8 | ||
|
|
b8a4ede83f | ||
|
|
8a1c0b2db7 | ||
|
|
cfeb1c6530 | ||
|
|
746f878747 | ||
|
|
a8c27ec975 | ||
|
|
cffdcdc0d2 | ||
|
|
8f2fd77634 | ||
|
|
301e8b6983 | ||
|
|
cf2f9788ec | ||
|
|
3adb02118c | ||
|
|
185dbb7f02 | ||
|
|
be7d0e1745 | ||
|
|
0bff2d2f36 | ||
|
|
79ecb8e0b9 | ||
|
|
09412cb43d | ||
|
|
ebc0a28460 | ||
|
|
ef4d2c6cdd | ||
|
|
e2d6abf833 | ||
|
|
feead2cd44 | ||
|
|
fb50b8becf | ||
|
|
e0f3d61f65 | ||
|
|
7f1c4ce036 | ||
|
|
5a6fdf58d4 | ||
|
|
a2cc4233b5 | ||
|
|
df42a791c9 | ||
|
|
1a5bd3af48 | ||
|
|
db67a829d5 | ||
|
|
0ab4520904 | ||
|
|
03ae4884e0 | ||
|
|
23bafde457 | ||
|
|
c2c396052f | ||
|
|
6849939265 | ||
|
|
c6a947fbaf | ||
|
|
5b59642695 | ||
|
|
fe102dff6f | ||
|
|
cf64990cff | ||
|
|
eb3368edd6 | ||
|
|
cdd7183d85 | ||
|
|
9b0e24c27b | ||
|
|
785409e12a | ||
|
|
a08ec8844a | ||
|
|
eac17b6e16 | ||
|
|
8f86bda4d2 | ||
|
|
3c8a8188fb | ||
|
|
29d1566789 | ||
|
|
c5175c500e | ||
|
|
605737cd5f | ||
|
|
747ea9d0c6 | ||
|
|
f5e195604f | ||
|
|
29ba64462a | ||
|
|
afc133cbc5 | ||
|
|
0dbd89e5cb | ||
|
|
af4e71db9b | ||
|
|
7382ed26b3 | ||
|
|
fd3b304373 | ||
|
|
9b4ed1199b | ||
|
|
647ba2fcc1 | ||
|
|
64bf4bf58a | ||
|
|
b917b65d49 | ||
|
|
6d502045cb | ||
|
|
de86c85b23 | ||
|
|
1809cbe1f4 | ||
|
|
61bd70a6c9 | ||
|
|
0795a7b4f8 | ||
|
|
02e37e5096 | ||
|
|
273876873e | ||
|
|
9417cd85ab | ||
|
|
6f3e40e390 | ||
|
|
89cea9289b | ||
|
|
038abb9be7 | ||
|
|
709fbc500e | ||
|
|
57b982eea3 | ||
|
|
ef3cef530b | ||
|
|
6cc9516744 | ||
|
|
bfd19280d5 | ||
|
|
2eff3912fb | ||
|
|
69c5c9458b | ||
|
|
af44b99b6f | ||
|
|
c969bc32f5 | ||
|
|
a987ffb745 | ||
|
|
babd4eb5f8 | ||
|
|
d922d4d054 | ||
|
|
940013f9a6 | ||
|
|
e15c8c8c89 | ||
|
|
dcab4b0d4a | ||
|
|
8558ca1020 | ||
|
|
17d036302f | ||
|
|
49707b0a17 | ||
|
|
e52dfababc | ||
|
|
55f13ae654 | ||
|
|
8b61fbebe4 | ||
|
|
a48b217cf3 | ||
|
|
051cd7da2b | ||
|
|
e5b60c2f95 | ||
|
|
cdd710d672 | ||
|
|
1ee6a37188 | ||
|
|
9d868c5090 | ||
|
|
25ec0d93a9 | ||
|
|
2330a30021 | ||
|
|
f37b943f7e | ||
|
|
2c95087056 | ||
|
|
d854773e84 | ||
|
|
b6ce1cbae9 | ||
|
|
875562c385 | ||
|
|
85c102cfc1 | ||
|
|
484a31d082 | ||
|
|
c0b214612a | ||
|
|
3e208bad9b | ||
|
|
7041b79d2a | ||
|
|
2b25573378 | ||
|
|
8efa0295df | ||
|
|
46352ff9c2 | ||
|
|
c276a31b9c | ||
|
|
c904334c83 | ||
|
|
3b0d05e9c9 | ||
|
|
37d606eda6 | ||
|
|
bfd20b7a6b | ||
|
|
a0960555fc | ||
|
|
76ec9683cb | ||
|
|
4ee77656be | ||
|
|
6e58c01e8d | ||
|
|
81e9ddca9b | ||
|
|
a8cc50a495 | ||
|
|
9a231821ea | ||
|
|
d8cd96100e | ||
|
|
31b5031808 | ||
|
|
5906ed7e39 | ||
|
|
c6bb855456 | ||
|
|
2260cb1668 | ||
|
|
65f2d6202f | ||
|
|
733025bd53 | ||
|
|
c6db98c1c2 | ||
|
|
fbb26bbc63 | ||
|
|
5ea77f3e75 | ||
|
|
83694fe537 | ||
|
|
045e074d0f | ||
|
|
6507529db3 | ||
|
|
fa44dd1a26 | ||
|
|
0227769c34 | ||
|
|
375e20a67b | ||
|
|
2724838cf8 | ||
|
|
91dec9e83d | ||
|
|
0483eeca82 | ||
|
|
77a6fd6aff | ||
|
|
8a24427bf7 | ||
|
|
dc011a489c | ||
|
|
9d3d8d5945 | ||
|
|
b255f557db | ||
|
|
05ebd15053 | ||
|
|
6245ad9298 | ||
|
|
3ac2399115 | ||
|
|
182bf177a3 | ||
|
|
d3aa69757b | ||
|
|
56bce1431b | ||
|
|
fa0e836f65 | ||
|
|
e2cc1cb28a | ||
|
|
1d0fd04393 | ||
|
|
ddb6adae2b | ||
|
|
4cabc8a87a | ||
|
|
04c27cc7d5 | ||
|
|
057fb44061 | ||
|
|
51c72aa5be | ||
|
|
769256f0a5 | ||
|
|
7bbdbd6997 | ||
|
|
95a0669e5c | ||
|
|
71f33ed44e | ||
|
|
18f89faf03 | ||
|
|
9bd7aade43 | ||
|
|
754b16c8c8 | ||
|
|
e473c710a2 | ||
|
|
39da5bbe01 | ||
|
|
027709d1e8 | ||
|
|
959b43743c | ||
|
|
43a2b1fa3c | ||
|
|
8fc0316253 |
@@ -1,6 +0,0 @@
|
||||
[distutils]
|
||||
Index-servers =
|
||||
PrimAITE
|
||||
|
||||
[PrimAITE]
|
||||
Repository = https://pkgs.dev.azure.com/ma-dev-uk/PrimAITE/_packaging/PrimAITE/pypi/upload/
|
||||
@@ -1,38 +0,0 @@
|
||||
trigger:
|
||||
- main
|
||||
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
Python310:
|
||||
python.version: '3.10'
|
||||
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '$(python.version)'
|
||||
displayName: 'Use Python $(python.version)'
|
||||
|
||||
- script: |
|
||||
python -m pip install --upgrade pip==23.0.1
|
||||
pip install wheel==0.38.4 --upgrade
|
||||
pip install setuptools==66 --upgrade
|
||||
pip install build==0.10.0
|
||||
pip install twine
|
||||
pip install keyring
|
||||
pip install artifacts-keyring
|
||||
displayName: 'Install build dependencies'
|
||||
|
||||
- script: |
|
||||
python -m build
|
||||
displayName: 'Build PrimAITE sdist and wheel'
|
||||
|
||||
- task: TwineAuthenticate@1
|
||||
displayName: 'Twine Authenticate'
|
||||
inputs:
|
||||
artifactFeed: PrimAITE/PrimAITE
|
||||
|
||||
- script: |
|
||||
python -m twine upload --verbose -r PrimAITE --config-file $(PYPIRC_PATH) dist/*.whl
|
||||
displayName: 'Artifact Upload'
|
||||
@@ -1,108 +0,0 @@
|
||||
trigger:
|
||||
- release/*
|
||||
|
||||
schedules:
|
||||
- cron: "0 2 * * 1-5" # Run at 2 AM every weekday
|
||||
displayName: "Weekday Schedule"
|
||||
branches:
|
||||
include:
|
||||
- 'refs/heads/dev'
|
||||
variables:
|
||||
VERSION: ''
|
||||
MAJOR_VERSION: ''
|
||||
|
||||
jobs:
|
||||
- job: PrimAITE_Benchmark
|
||||
timeoutInMinutes: 360 # 6-hour maximum
|
||||
pool:
|
||||
name: 'Imaginary Yak Pool'
|
||||
workspace:
|
||||
clean: all
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
persistCredentials: true
|
||||
|
||||
- script: |
|
||||
python3.10 -m venv venv
|
||||
displayName: 'Create venv'
|
||||
|
||||
- script: |
|
||||
VERSION=$(cat src/primaite/VERSION | tr -d '\n')
|
||||
if [[ "$(Build.SourceBranch)" == "refs/heads/dev" ]]; then
|
||||
DATE=$(date +%Y%m%d)
|
||||
echo "${VERSION}+dev.${DATE}" > src/primaite/VERSION
|
||||
fi
|
||||
displayName: 'Update VERSION file for Dev Benchmark'
|
||||
|
||||
- script: |
|
||||
VERSION=$(cat src/primaite/VERSION | tr -d '\n')
|
||||
MAJOR_VERSION=$(echo $VERSION | cut -d. -f1)
|
||||
echo "##vso[task.setvariable variable=VERSION]$VERSION"
|
||||
echo "##vso[task.setvariable variable=MAJOR_VERSION]$MAJOR_VERSION"
|
||||
displayName: 'Set Version Variables'
|
||||
|
||||
- script: |
|
||||
source venv/bin/activate
|
||||
pip install --upgrade pip
|
||||
pip install -e .[dev,rl]
|
||||
primaite setup
|
||||
displayName: 'Install Dependencies'
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
source venv/bin/activate
|
||||
cd benchmark
|
||||
python primaite_benchmark.py
|
||||
cd ..
|
||||
displayName: 'Run Benchmarking Script'
|
||||
|
||||
- script: |
|
||||
tar czf primaite_v$(VERSION)_benchmark.tar.gz benchmark/results/v$(MAJOR_VERSION)/v$(VERSION)
|
||||
displayName: 'Prepare Artifacts for Publishing'
|
||||
|
||||
- task: PublishPipelineArtifact@1
|
||||
inputs:
|
||||
targetPath: primaite_v$(VERSION)_benchmark.tar.gz
|
||||
artifactName: 'benchmark-zip-output'
|
||||
publishLocation: 'pipeline'
|
||||
displayName: 'Publish Benchmark Output zip as Artifact'
|
||||
|
||||
- script: |
|
||||
git config --global user.email "oss@dstl.gov.uk"
|
||||
git config --global user.name "Defence Science and Technology Laboratory UK"
|
||||
workingDirectory: $(System.DefaultWorkingDirectory)
|
||||
displayName: 'Configure Git'
|
||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/heads/release'))
|
||||
|
||||
- script: |
|
||||
echo "Fetching all branches..."
|
||||
git fetch --all --prune
|
||||
|
||||
echo "Stashing files..."
|
||||
git stash push -u
|
||||
|
||||
echo "Resolving branch name..."
|
||||
# Extracting just the branch name from the full ref path
|
||||
branch_name=$(echo "$(Build.SourceBranch)" | sed 's|refs/heads/||')
|
||||
echo "Branch Name: $branch_name"
|
||||
|
||||
echo "Checking out branch $branch_name..."
|
||||
git checkout $branch_name
|
||||
|
||||
echo "Popping stash..."
|
||||
git stash pop
|
||||
|
||||
echo "Adding benchmark results..."
|
||||
git add benchmark/results/v$(MAJOR_VERSION)/v$(VERSION)/*
|
||||
|
||||
echo "Committing changes..."
|
||||
git commit -m "Automated benchmark output commit for version $(VERSION) [skip ci]"
|
||||
|
||||
echo "Pushing to remote..."
|
||||
git push origin $branch_name
|
||||
displayName: 'Commit and Push Benchmark Results'
|
||||
workingDirectory: $(System.DefaultWorkingDirectory)
|
||||
env:
|
||||
GIT_CREDENTIALS: $(System.AccessToken)
|
||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/heads/release'))
|
||||
@@ -1,53 +0,0 @@
|
||||
name: Azure Static Web Apps CI/CD
|
||||
|
||||
pr: none
|
||||
trigger:
|
||||
branches:
|
||||
include:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
- job: build_and_deploy_job
|
||||
displayName: Build and Deploy Job
|
||||
condition: or(eq(variables['Build.Reason'], 'Manual'),or(eq(variables['Build.Reason'], 'PullRequest'),eq(variables['Build.Reason'], 'IndividualCI')))
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
variables:
|
||||
- group: Azure-Static-Web-Apps-nice-bay-0ad032c03-variable-group
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: true
|
||||
|
||||
- script: |
|
||||
python -m pip install --upgrade pip==23.0.1
|
||||
pip install wheel==0.38.4 --upgrade
|
||||
pip install setuptools==66 --upgrade
|
||||
pip install build==0.10.0
|
||||
displayName: 'Install build dependencies'
|
||||
|
||||
- script: |
|
||||
pip install -e .[dev,rl]
|
||||
displayName: 'Install PrimAITE for docs autosummary'
|
||||
|
||||
- script: |
|
||||
sudo apt-get install pandoc
|
||||
displayName: 'Install Pandoc'
|
||||
|
||||
- script: |
|
||||
primaite setup
|
||||
displayName: 'Perform PrimAITE Setup'
|
||||
|
||||
- script: |
|
||||
cd docs
|
||||
make html
|
||||
cd ..
|
||||
cd ..
|
||||
displayName: 'Build Docs'
|
||||
|
||||
- task: AzureStaticWebApp@0
|
||||
inputs:
|
||||
azure_static_web_apps_api_token: $(AZURE_STATIC_WEB_APPS_API_TOKEN_NICE_BAY_0AD032C03)
|
||||
app_location: "/docs/_build/html"
|
||||
api_location: ""
|
||||
output_location: "/"
|
||||
displayName: 'Deploy Docs to nice-bay-0ad032c03'
|
||||
@@ -1,128 +0,0 @@
|
||||
trigger:
|
||||
- main
|
||||
- dev
|
||||
- feature/*
|
||||
- hotfix/*
|
||||
- bugfix/*
|
||||
- release/*
|
||||
|
||||
pr:
|
||||
autoCancel: true
|
||||
drafts: false
|
||||
parameters:
|
||||
# https://stackoverflow.com/a/70046417
|
||||
- name: matrix
|
||||
type: object
|
||||
default:
|
||||
# - job_name: 'UbuntuPython38'
|
||||
# py: '3.8'
|
||||
# img: 'ubuntu-latest'
|
||||
# every_time: false
|
||||
# publish_coverage: false
|
||||
- job_name: 'UbuntuPython311'
|
||||
py: '3.11'
|
||||
img: 'ubuntu-latest'
|
||||
every_time: true
|
||||
publish_coverage: true
|
||||
# - job_name: 'WindowsPython38'
|
||||
# py: '3.8'
|
||||
# img: 'windows-latest'
|
||||
# every_time: false
|
||||
# publish_coverage: false
|
||||
- job_name: 'WindowsPython311'
|
||||
py: '3.11'
|
||||
img: 'windows-latest'
|
||||
every_time: false
|
||||
publish_coverage: false
|
||||
# - job_name: 'MacOSPython38'
|
||||
# py: '3.8'
|
||||
# img: 'macOS-latest'
|
||||
# every_time: false
|
||||
# publish_coverage: false
|
||||
- job_name: 'MacOSPython311'
|
||||
py: '3.11'
|
||||
img: 'macOS-latest'
|
||||
every_time: false
|
||||
publish_coverage: false
|
||||
|
||||
stages:
|
||||
- stage: Test
|
||||
jobs:
|
||||
- ${{ each item in parameters.matrix }}:
|
||||
- job: ${{ item.job_name }}
|
||||
timeoutInMinutes: 90
|
||||
cancelTimeoutInMinutes: 1
|
||||
pool:
|
||||
vmImage: ${{ item.img }}
|
||||
condition: and(succeeded(), or( eq(variables['Build.Reason'], 'PullRequest'), ${{ item.every_time }} ))
|
||||
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: ${{ item.py }}
|
||||
displayName: 'Use Python ${{ item.py }}'
|
||||
|
||||
- script: |
|
||||
python -m pip install pre-commit
|
||||
pre-commit install
|
||||
pre-commit run --all-files
|
||||
displayName: 'Run pre-commits'
|
||||
|
||||
- script: |
|
||||
python -m pip install --upgrade pip==23.0.1
|
||||
pip install wheel==0.38.4 --upgrade
|
||||
pip install setuptools==66 --upgrade
|
||||
pip install build==0.10.0
|
||||
pip install pytest-azurepipelines
|
||||
displayName: 'Install build dependencies'
|
||||
|
||||
- script: |
|
||||
python -m build
|
||||
displayName: 'Build PrimAITE'
|
||||
|
||||
- script: |
|
||||
PRIMAITE_WHEEL=$(ls ./dist/primaite*.whl)
|
||||
python -m pip install $PRIMAITE_WHEEL[dev,rl]
|
||||
displayName: 'Install PrimAITE'
|
||||
condition: or(eq( variables['Agent.OS'], 'Linux' ), eq( variables['Agent.OS'], 'Darwin' ))
|
||||
|
||||
- script: |
|
||||
forfiles /p dist\ /m *.whl /c "cmd /c python -m pip install @file[dev,rl]"
|
||||
displayName: 'Install PrimAITE'
|
||||
condition: eq( variables['Agent.OS'], 'Windows_NT' )
|
||||
|
||||
- script: |
|
||||
primaite setup
|
||||
displayName: 'Perform PrimAITE Setup'
|
||||
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install dotnet dependencies'
|
||||
inputs:
|
||||
packageType: 'sdk'
|
||||
version: '2.1.x'
|
||||
|
||||
- script: |
|
||||
coverage run -m --source=primaite pytest -v -o junit_family=xunit2 --junitxml=junit/test-results.xml --cov-fail-under=80
|
||||
coverage xml -o coverage.xml -i
|
||||
coverage html -d htmlcov -i
|
||||
displayName: 'Run tests and code coverage'
|
||||
|
||||
- task: PublishTestResults@2
|
||||
condition: succeededOrFailed()
|
||||
inputs:
|
||||
testRunner: JUnit
|
||||
testResultsFiles: 'junit/**.xml'
|
||||
testRunTitle: 'Publish test results'
|
||||
failTaskOnFailedTests: true
|
||||
|
||||
- publish: $(System.DefaultWorkingDirectory)/htmlcov/
|
||||
# publish the html report - so we can debug the coverage if needed
|
||||
condition: ${{ item.publish_coverage }} # should only be run once
|
||||
artifact: coverage_report
|
||||
|
||||
- task: PublishCodeCoverageResults@2
|
||||
# publish the code coverage so it can be viewed in the run coverage page
|
||||
condition: ${{ item.publish_coverage }} # should only be run once
|
||||
inputs:
|
||||
codeCoverageTool: Cobertura
|
||||
summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml'
|
||||
@@ -1,16 +0,0 @@
|
||||
## Summary
|
||||
*Replace this text with an explanation of what the changes are and how you implemented them. Can this impact any other parts of the codebase that we should keep in mind?*
|
||||
|
||||
## Test process
|
||||
*How have you tested this (if applicable)?*
|
||||
|
||||
## Checklist
|
||||
- [ ] PR is linked to a **work item**
|
||||
- [ ] **acceptance criteria** of linked ticket are met
|
||||
- [ ] performed **self-review** of the code
|
||||
- [ ] written **tests** for any new functionality added with this PR
|
||||
- [ ] updated the **documentation** if this PR changes or adds functionality
|
||||
- [ ] written/updated **design docs** if this PR implements new functionality
|
||||
- [ ] updated the **change log**
|
||||
- [ ] ran **pre-commit** checks for code style
|
||||
- [ ] attended to any **TO-DOs** left in the code
|
||||
7
.flake8
@@ -9,12 +9,5 @@ extend-ignore =
|
||||
E712
|
||||
D401
|
||||
F811
|
||||
ANN002
|
||||
ANN003
|
||||
ANN101
|
||||
ANN102
|
||||
exclude =
|
||||
docs/source/*
|
||||
tests/*
|
||||
suppress-none-returning=True
|
||||
suppress-dummy-args=True
|
||||
|
||||
6
.github/workflows/build-sphinx.yml
vendored
@@ -1,8 +1,8 @@
|
||||
name: build-sphinx-to-github-pages
|
||||
|
||||
env:
|
||||
GITHUB_ACTOR: {todo:fill in URL}
|
||||
GITHUB_REPOSITORY: {todo:fill in URL}/PrimAITE
|
||||
GITHUB_ACTOR: Autonomous-Resilient-Cyber-Defence
|
||||
GITHUB_REPOSITORY: Autonomous-Resilient-Cyber-Defence/PrimAITE
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN}}
|
||||
|
||||
on:
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
- name: Install PrimAITE for docs autosummary
|
||||
run: |
|
||||
set -x
|
||||
python -m pip install -e .[dev,rl]
|
||||
python -m pip install -e .[dev]
|
||||
|
||||
- name: Run build script for Sphinx pages
|
||||
env:
|
||||
|
||||
13
.github/workflows/python-package.yml
vendored
@@ -5,11 +5,13 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
- dev-gui
|
||||
- 'release/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
- dev-gui
|
||||
- 'release/**'
|
||||
jobs:
|
||||
build:
|
||||
@@ -17,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.9", "3.10", "3.11"]
|
||||
python-version: ["3.8", "3.9", "3.10"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -46,12 +48,19 @@ jobs:
|
||||
- name: Install PrimAITE
|
||||
run: |
|
||||
PRIMAITE_WHEEL=$(ls ./dist/primaite*.whl)
|
||||
python -m pip install $PRIMAITE_WHEEL[dev,rl]
|
||||
python -m pip install $PRIMAITE_WHEEL[dev]
|
||||
|
||||
- name: Perform PrimAITE Setup
|
||||
run: |
|
||||
primaite setup
|
||||
|
||||
- name: Lint with flake8
|
||||
run: |
|
||||
# stop the build if there are Python syntax errors or undefined names
|
||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||
# exit-zero treats all errors as warnings.
|
||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=120 --statistics
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
pytest tests/
|
||||
|
||||
20
.gitignore
vendored
@@ -37,7 +37,6 @@ pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
junit/
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
@@ -82,10 +81,6 @@ target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
PPO_UC2/
|
||||
# ignore everything but the executed notebooks rst in the docs/source/notebooks directory
|
||||
!docs/source/notebooks/executed_notebooks.rst
|
||||
docs/source/notebooks/**/*
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
@@ -148,23 +143,10 @@ cython_debug/
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
|
||||
.vscode/
|
||||
docs/source/primaite-dependencies.rst
|
||||
|
||||
# outputs
|
||||
src/primaite/outputs/
|
||||
simulation_output/
|
||||
sessions/
|
||||
PrimAITE-PPO-example-agent.zip
|
||||
|
||||
# benchmark session outputs
|
||||
benchmark/output
|
||||
# src/primaite/notebooks/scratch.ipynb
|
||||
src/primaite/notebooks/scratch.py
|
||||
sandbox.py
|
||||
sandbox/
|
||||
sandbox.ipynb
|
||||
|
||||
# benchmarking
|
||||
**/benchmark/sessions/
|
||||
**/benchmark/output/
|
||||
|
||||
@@ -1,21 +1,12 @@
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: ensure-copyright-clause
|
||||
name: ensure copyright clause
|
||||
entry: python copyright_clause_pre_commit_hook.py
|
||||
language: python
|
||||
- repo: http://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: check-yaml
|
||||
exclude: |
|
||||
| scenario_with_placeholders/
|
||||
| mini_scenario_with_simulation_variation/
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- id: check-added-large-files
|
||||
args: ['--maxkb=5000']
|
||||
args: ['--maxkb=1000']
|
||||
- id: mixed-line-ending
|
||||
- id: requirements-txt-fixer
|
||||
- repo: http://github.com/psf/black
|
||||
@@ -36,8 +27,3 @@ repos:
|
||||
- id: flake8
|
||||
additional_dependencies:
|
||||
- flake8-docstrings
|
||||
- flake8-annotations
|
||||
- repo: https://github.com/kynan/nbstripout
|
||||
rev: 0.7.1
|
||||
hooks:
|
||||
- id: nbstripout
|
||||
|
||||
151
CHANGELOG.md
@@ -5,158 +5,7 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## 3.0.0b9
|
||||
- Removed deprecated `PrimaiteSession` class.
|
||||
- Added ability to set log levels via configuration.
|
||||
- Upgraded pydantic to version 2.7.0
|
||||
- Upgraded Ray to version >= 2.9
|
||||
- Added ipywidgets to the dependencies
|
||||
- Added ability to define scenarios that change depending on the episode number.
|
||||
- Standardised Environment API by renaming the config parameter of `PrimaiteGymEnv` from `game_config` to `env_config`
|
||||
- Database Connection ID's are now created/issued by DatabaseService and not DatabaseClient
|
||||
- Updated DatabaseClient so that it can now have a single native DatabaseClientConnection along with a collection of DatabaseClientConnection's.
|
||||
- Implemented the uninstall functionality for DatabaseClient so that all connections are terminated at the DatabaseService.
|
||||
- Added the ability for a DatabaseService to terminate a connection.
|
||||
- Added active_connection to DatabaseClientConnection so that if the connection is terminated active_connection is set to False and the object can no longer be used.
|
||||
- Added additional show functions to enable connection inspection.
|
||||
- Updates to agent logging, to include the reward both per step and per episode.
|
||||
- Introduced Developer CLI tools to assist with developing/debugging PrimAITE
|
||||
- Can be enabled via `primaite dev-mode enable`
|
||||
- Activating dev-mode will change the location where the sessions will be output - by default will output where the PrimAITE repository is located
|
||||
- Refactored all air-space usage to that a new instance of AirSpace is created for each instance of Network. This 1:1 relationship between network and airspace will allow parallelization.
|
||||
- Added notebook to demonstrate use of SubprocVecEnv from SB3 to vectorise environments to speed up training.
|
||||
|
||||
|
||||
|
||||
## [Unreleased]
|
||||
- Made requests fail to reach their target if the node is off
|
||||
- Added responses to requests
|
||||
- Made environment reset completely recreate the game object.
|
||||
- Changed the red agent in the data manipulation scenario to randomly choose client 1 or client 2 to start its attack.
|
||||
- Changed the data manipulation scenario to include a second green agent on client 1.
|
||||
- Refactored actions and observations to be configurable via object name, instead of UUID.
|
||||
- Made database patch correctly take 2 timesteps instead of being immediate
|
||||
- Made database patch only possible when the software is compromised or good, it's no longer possible when the software is OFF or RESETTING
|
||||
- Added a notebook which explains Data manipulation scenario, demonstrates the attack, and shows off blue agent's action space, observation space, and reward function.
|
||||
- Made packet capture and system logging optional (off by default). To turn on, change the io_settings.save_pcap_logs and io_settings.save_sys_logs settings in the config.
|
||||
- Made observation space flattening optional (on by default). To turn off for an agent, change the `agent_settings.flatten_obs` setting in the config.
|
||||
- Added support for SQL INSERT command.
|
||||
- Added ability to log each agent's action choices in each step to a JSON file.
|
||||
- Removal of Link bandwidth hardcoding. This can now be configured via the network configuraiton yaml. Will default to 100 if not present.
|
||||
- Added NMAP application to all host and layer-3 network nodes.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- ACL rules were not resetting on episode reset.
|
||||
- ACLs were not showing up correctly in the observation space.
|
||||
- Blue agent's ACL actions were being applied against the wrong IP addresses
|
||||
- Deleted files and folders did not reset correctly on episode reset.
|
||||
- Service health status was using the actual health state instead of the visible health state
|
||||
- Database file health status was using the incorrect value for negative rewards
|
||||
- Preventing file actions from reaching their intended file
|
||||
- The data manipulation attack was triggered at episode start.
|
||||
- FTP STOR stored an additional copy on the client machine's filesystem
|
||||
- The red agent acted to early
|
||||
- Order of service health state
|
||||
- Starting a node didn't start the services on it
|
||||
- Fixed an issue where the services were still able to run even though the node the service is installed on is turned off
|
||||
- The use of NODE_FILE_CHECKHASH and NODE_FOLDER_CHECKHASH in the current release is marked as 'Not Implemented'.
|
||||
|
||||
|
||||
### Added
|
||||
- Network Hardware - Added base hardware module with NIC, SwitchPort, Node, and Link. Nodes have
|
||||
fundamental services like ARP, ICMP, and PCAP running them by default.
|
||||
- Network Transmission - Modelled OSI Model layers 1 through to 5 with various classes for creating network frames and
|
||||
transmitting them from a Service/Application, down through the layers, over the wire, and back up through the layers to
|
||||
a Service/Application another machine.
|
||||
- Introduced `Router` and `Switch` classes to manage networking routes more effectively.
|
||||
- Added `ACLRule` and `RouteTableEntry` classes as part of the `Router`.
|
||||
- New `.show()` methods in all network component classes to inspect the state in either plain text or markdown formats.
|
||||
- Added `Computer` and `Server` class to better differentiate types of network nodes.
|
||||
- Integrated a new Use Case 2 network into the system.
|
||||
- New unit tests to verify routing between different subnets using `.ping()`.
|
||||
- system - Added the core structure of Application, Services, and Components. Also added a SoftwareManager and
|
||||
SessionManager.
|
||||
- Permission System - each action can define criteria that will be used to permit or deny agent actions.
|
||||
- File System - ability to emulate a node's file system during a simulation
|
||||
- Example notebooks - There are 5 jupyter notebook which walk through using PrimAITE
|
||||
1. Training a Stable Baselines 3 agent
|
||||
2. Training a single agent system using Ray RLLib
|
||||
3. Training a multi-agent system Ray RLLib
|
||||
4. Data manipulation end to end demonstration
|
||||
5. Data manipulation scenario with customised red agents
|
||||
- Database:
|
||||
- `DatabaseClient` and `DatabaseService` created to allow emulation of database actions
|
||||
- Ability for `DatabaseService` to backup its data to another server via FTP and restore data from backup
|
||||
- Red Agent Services:
|
||||
- Data Manipulator Bot - A red agent service which sends a payload to a target machine. (By default this payload is a SQL query that breaks a database). The attack runs in stages with a random, configurable probability of succeeding.
|
||||
- `DataManipulationAgent` runs the Data Manipulator Bot according to a configured start step, frequency and variance.
|
||||
- DNS Services: `DNSClient` and `DNSServer`
|
||||
- FTP Services: `FTPClient` and `FTPServer`
|
||||
- HTTP Services: `WebBrowser` to simulate a web client and `WebServer`
|
||||
- NTP Services: `NTPClient` and `NTPServer`
|
||||
- **RouterNIC Class**: Introduced a new class `RouterNIC`, extending the standard `NIC` functionality. This class is specifically designed for router operations, optimizing the processing and routing of network traffic.
|
||||
- **Custom Layer-3 Processing**: The `RouterNIC` class includes custom handling for network frames, bypassing standard Node NIC's Layer 3 broadcast/unicast checks. This allows for more efficient routing behavior in network scenarios where router-specific frame processing is required.
|
||||
- **Enhanced Frame Reception**: The `receive_frame` method in `RouterNIC` is tailored to handle frames based on Layer 2 (Ethernet) checks, focusing on MAC address-based routing and broadcast frame acceptance.
|
||||
- **Subnet-Wide Broadcasting for Services and Applications**: Implemented the ability for services and applications to conduct broadcasts across an entire IPv4 subnet within the network simulation framework.
|
||||
- Introduced the `NetworkInterface` abstract class to provide a common interface for all network interfaces. Subclasses are divided into two main categories: `WiredNetworkInterface` and `WirelessNetworkInterface`, each serving as an abstract base class (ABC) for more specific interface types. Under `WiredNetworkInterface`, the subclasses `NIC` and `SwitchPort` were added. For wireless interfaces, `WirelessNIC` and `WirelessAccessPoint` are the subclasses under `WirelessNetworkInterface`.
|
||||
- Added `Layer3Interface` as an abstract base class for networking functionalities at layer 3, including IP addressing and routing capabilities. This class is inherited by `NIC`, `WirelessNIC`, and `WirelessAccessPoint` to provide them with layer 3 capabilities, facilitating their role in both wired and wireless networking contexts with IP-based communication.
|
||||
- Created the `ARP` and `ICMP` service classes to handle Address Resolution Protocol operations and Internet Control Message Protocol messages, respectively, with `RouterARP` and `RouterICMP` for router-specific implementations.
|
||||
- Created `HostNode` as a subclass of `Node`, extending its functionality with host-specific services and applications. This class is designed to represent end-user devices like computers or servers that can initiate and respond to network communications.
|
||||
- Introduced a new `IPV4Address` type in the Pydantic model for enhanced validation and auto-conversion of IPv4 addresses from strings using an `ipv4_validator`.
|
||||
- Comprehensive documentation for the Node and its network interfaces, detailing the operational workflow from frame reception to application-level processing.
|
||||
- Detailed descriptions of the Session Manager and Software Manager functionalities, including their roles in managing sessions, software services, and applications within the simulation.
|
||||
- Documentation for the Packet Capture (PCAP) service and SysLog functionality, highlighting their importance in logging network frames and system events, respectively.
|
||||
- Expanded documentation on network devices such as Routers, Switches, Computers, and Switch Nodes, explaining their specific processing logic and protocol support.
|
||||
- **Firewall Node**: Introduced the `Firewall` class extending the functionality of the existing `Router` class. The `Firewall` class incorporates advanced features to scrutinize, direct, and filter traffic between various network zones, guided by predefined security rules and policies. Key functionalities include:
|
||||
- Access Control Lists (ACLs) for traffic filtering based on IP addresses, protocols, and port numbers.
|
||||
- Network zone segmentation for managing traffic across external, internal, and DMZ (De-Militarized Zone) networks.
|
||||
- Interface configuration to establish connectivity and define network parameters for external, internal, and DMZ interfaces.
|
||||
- Protocol and service management to oversee traffic and enforce security policies.
|
||||
- Dynamic traffic processing and filtering to ensure network security and integrity.
|
||||
- `AirSpace` class to simulate wireless communications, managing wireless interfaces and facilitating the transmission of frames within specified frequencies.
|
||||
- `AirSpaceFrequency` enum for defining standard wireless frequencies, including 2.4 GHz and 5 GHz bands, to support realistic wireless network simulations.
|
||||
- `WirelessRouter` class, extending the `Router` class, to incorporate wireless networking capabilities alongside traditional wired connections. This class allows the configuration of wireless access points with specific IP settings and operating frequencies.
|
||||
- Documentation Updates:
|
||||
- Examples include how to set up PrimAITE session via config
|
||||
- Examples include how to create nodes and install software via config
|
||||
- Examples include how to set up PrimAITE session via Python
|
||||
- Examples include how to create nodes and install software via Python
|
||||
- Added missing ``DoSBot`` documentation page
|
||||
- Added diagrams where needed to make understanding some things easier
|
||||
- Templated parts of the documentation to prevent unnecessary repetition and for easier maintaining of documentation
|
||||
- Separated documentation pages of some items i.e. client and server software were on the same pages - which may make things confusing
|
||||
- Configuration section at the bottom of the software pages specifying the configuration options available (and which ones are optional)
|
||||
- Ability to add ``Firewall`` node via config
|
||||
- Ability to add ``Router`` routes via config
|
||||
- Ability to add ``Router``/``Firewall`` ``ACLRule`` via config
|
||||
- NMNE capturing capabilities to `NetworkInterface` class for detecting and logging Malicious Network Events.
|
||||
- New `nmne_config` settings in the simulation configuration to enable NMNE capturing and specify keywords such as "DELETE".
|
||||
- Router-specific SessionManager Implementation: Introduced a specialized version of the SessionManager tailored for router operations. This enhancement enables the SessionManager to determine the routing path by consulting the route table.
|
||||
|
||||
### Changed
|
||||
- Integrated the RouteTable into the Routers frame processing.
|
||||
- Frames are now dropped when their TTL reaches 0
|
||||
- **NIC Functionality Update**: Updated the Network Interface Card (`NIC`) functionality to support Layer 3 (L3) broadcasts.
|
||||
- **Layer 3 Broadcast Handling**: Enhanced the existing `NIC` classes to correctly process and handle Layer 3 broadcasts. This update allows devices using standard NICs to effectively participate in network activities that involve L3 broadcasting.
|
||||
- **Improved Frame Reception Logic**: The `receive_frame` method of the `NIC` class has been updated to include additional checks and handling for L3 broadcasts, ensuring proper frame processing in a wider range of network scenarios.
|
||||
- Standardised the way network interfaces are accessed across all `Node` subclasses (`HostNode`, `Router`, `Switch`) by maintaining a comprehensive `network_interface` attribute. This attribute captures all network interfaces by their port number, streamlining the management and interaction with network interfaces across different types of nodes.
|
||||
- Refactored all tests to utilise new `Node` subclasses (`Computer`, `Server`, `Router`, `Switch`) instead of creating generic `Node` instances and manually adding network interfaces. This change aligns test setups more closely with the intended use cases and hierarchies within the network simulation framework.
|
||||
- Updated all tests to employ the `Network()` class for managing nodes and their connections, ensuring a consistent and structured approach to setting up network topologies in testing scenarios.
|
||||
- **ACLRule Wildcard Masking**: Updated the `ACLRule` class to support IP ranges using wildcard masking. This enhancement allows for more flexible and granular control over traffic filtering, enabling the specification of broader or more specific IP address ranges in ACL rules.
|
||||
- Updated `NetworkInterface` documentation to reflect the new NMNE capturing features and how to use them.
|
||||
- Integration of NMNE capturing functionality within the `NICObservation` class.
|
||||
- Changed blue action set to enable applying node scan, reset, start, and shutdown to every host in data manipulation scenario
|
||||
|
||||
### Removed
|
||||
- Removed legacy simulation modules: `acl`, `common`, `environment`, `links`, `nodes`, `pol`
|
||||
- Removed legacy training modules
|
||||
- Removed tests for legacy code
|
||||
|
||||
### Fixed
|
||||
- Addressed network transmission issues that previously allowed ARP requests to be incorrectly routed and repeated across different subnets. This fix ensures ARP requests are correctly managed and confined to their appropriate network segments.
|
||||
- Resolved problems in `Node` and its subclasses where the default gateway configuration was not properly utilized for communications across different subnets. This correction ensures that nodes effectively use their configured default gateways for outbound communications to other network segments, thereby enhancing the network's routing functionality and reliability.
|
||||
- Network Interface Port name/num being set properly for sys log and PCAP output.
|
||||
|
||||
## [2.0.0] - 2023-07-26
|
||||
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
### **Did you find a bug?**
|
||||
|
||||
|
||||
* **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/{todo:fill in URL}/PrimAITE/issues).
|
||||
* If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/{todo:fill in URL}/PrimAITE/issues/new?assignees=&labels=bug&projects=&template=bug_report.md&title=%5BBUG%5D+-+%3Cbug+title+goes+here%3E). Be sure to follow our bug report template with the headers **Describe the bug**, **To Reproduce**, **Expected behaviour**, **Screenshots/Outputs**, **Environment**, and **Additional context**
|
||||
* **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/Autonomous-Resilient-Cyber-Defence/PrimAITE/issues).
|
||||
* If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/Autonomous-Resilient-Cyber-Defence/PrimAITE/issues/new?assignees=&labels=bug&projects=&template=bug_report.md&title=%5BBUG%5D+-+%3Cbug+title+goes+here%3E). Be sure to follow our bug report template with the headers **Describe the bug**, **To Reproduce**, **Expected behaviour**, **Screenshots/Outputs**, **Environment**, and **Additional context**
|
||||
|
||||
|
||||
### **Do you have a solution to fix the bug?**
|
||||
|
||||
* [Fork the repository](https://github.com/{todo:fill in URL}/PrimAITE/fork).
|
||||
* [Fork the repository](https://github.com/Autonomous-Resilient-Cyber-Defence/PrimAITE/fork).
|
||||
* Install the pre-commit hook with `pre-commit install`.
|
||||
* Implement the bug fix.
|
||||
* Update documentation where applicable.
|
||||
* Update the **UNRELEASED** section of the [CHANGELOG.md](CHANGELOG.md) file
|
||||
* Write a suitable test/tests.
|
||||
* Commit the bug fix to the dev branch on your fork. If the bug has an open issue under [Issues](https://github.com/{todo:fill in URL}/PrimAITE/issues), reference the issue in the commit message (e.g. #1 references issue 1).
|
||||
* Submit a pull request from your dev branch to the {todo:fill in URL}/PrimAITE dev branch. Again, if the bug has an open issue under [Issues](https://github.com/{todo:fill in URL}/PrimAITE/issues), reference the issue in the pull request description.
|
||||
* Commit the bug fix to the dev branch on your fork. If the bug has an open issue under [Issues](https://github.com/Autonomous-Resilient-Cyber-Defence/PrimAITE/issues), reference the issue in the commit message (e.g. #1 references issue 1).
|
||||
* Submit a pull request from your dev branch to the Autonomous-Resilient-Cyber-Defence/PrimAITE dev branch. Again, if the bug has an open issue under [Issues](https://github.com/Autonomous-Resilient-Cyber-Defence/PrimAITE/issues), reference the issue in the pull request description.
|
||||
|
||||
### **Did you fix whitespace, format code, or make a purely cosmetic patch?**
|
||||
|
||||
@@ -25,7 +25,7 @@ Changes that are cosmetic in nature and do not add anything substantial to the s
|
||||
|
||||
### **Do you intend to add a new feature or change an existing one?**
|
||||
|
||||
* Submit a [feature request issue](https://github.com/{todo:fill in URL}/PrimAITE/issues/new?assignees=&labels=feature_request&projects=&template=feature_request.md&title=%5BREQUEST%5D+-+%3Crequest+title+goes+here%3E).
|
||||
* Submit a [feature request issue](https://github.com/Autonomous-Resilient-Cyber-Defence/PrimAITE/issues/new?assignees=&labels=feature_request&projects=&template=feature_request.md&title=%5BREQUEST%5D+-+%3Crequest+title+goes+here%3E).
|
||||
* Know how to implement the new feature or change? Follow the same steps in the bug fix section above to fork, build, document, test, commit, and submit a pull request.
|
||||
|
||||
### **Do you have questions about the source code?**
|
||||
|
||||
22
LICENSE
@@ -1,27 +1,21 @@
|
||||
MIT License License
|
||||
MIT License
|
||||
|
||||
MIT License Conditions
|
||||
Copyright (c) 2023 - 2025 Defence Science and Technology Laboratory UK (https://dstl.gov.uk)
|
||||
|
||||
These MIT License conditions confirm the provision of the following artefacts as MIT License by Defence Science and Technology
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
request to the QQ or FNC mailbox):
|
||||
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
- Use Case Release Packs
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
Suppliers are required to read and confirm acceptance of the {todo:fill in URL} Foundry SyOPs (https://github.com/{todo:fill in URL}/foundry-syops) before being admitted access to material hosted on the {todo:fill in URL} Foundry GitHub site.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
|
||||
The material is supplied in confidence to QQ / FNC and their subcontractors under SERAPIS, and is issued to inform only those
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
of DSTL. The material must be stored and protected appropriately. All material must be destroyed at the end of the task.
|
||||
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
include src/primaite/setup/_package_data/primaite_config.yaml
|
||||
include src/primaite/config/_package_data/*.yaml
|
||||
include src/primaite/simulator/_package_data/*.ipynb
|
||||
|
||||
87
README.md
@@ -12,28 +12,34 @@ The ARCD Primary-level AI Training Environment (**PrimAITE**) provides an effect
|
||||
|
||||
PrimAITE presents the following features:
|
||||
|
||||
- Highly configurable (via YAML files) to provide the means to model a variety of platform / system laydowns and adversarial attack scenarios;
|
||||
- Highly configurable (via YAML files) to provide the means to model a variety of platform / system laydowns, mission profiles and adversarial attack scenarios;
|
||||
|
||||
- A Reinforcement Learning (RL) reward function based on (a) the ability to counter the specific modelled adversarial cyber-attack, and (b) the ability to ensure success;
|
||||
- A Reinforcement Learning (RL) reward function based on (a) the ability to counter the specific modelled adversarial cyber-attack, and (b) the ability to ensure mission success;
|
||||
|
||||
- Provision of logging to support AI evaluation and metrics gathering;
|
||||
|
||||
- Realistic network traffic simulation, including address and sending packets via internet protocols like TCP, UDP, ICMP, and others
|
||||
- Uses the concept of Information Exchange Requirements (IERs) to model background pattern of life, adversarial behaviour and mission data (on a sliding scale of criticality);
|
||||
|
||||
- Routers with traffic routing and firewall capabilities
|
||||
- An Access Control List (ACL) function, mimicking the behaviour of a network firewall, is applied across the model, following standard ACL rule format (e.g. DENY/ALLOW, source IP address, destination IP address, protocol and port);
|
||||
|
||||
- Support for multiple agents, each having their own customisable observation space, action space, and reward function definition, and either deterministic or RL-directed behaviour
|
||||
- Application of IERs to the platform / system laydown adheres to the ACL ruleset;
|
||||
|
||||
- Presents an OpenAI gym or RLLib interface to the environment, allowing integration with any compliant defensive agents;
|
||||
|
||||
- Full capture of discrete logs relating to agent training (full system state, agent actions taken, instantaneous and average reward for every step of every episode);
|
||||
|
||||
- NetworkX provides laydown visualisation capability.
|
||||
|
||||
## Getting Started with PrimAITE
|
||||
|
||||
### 💫 Installation
|
||||
### 💫 Install & Run
|
||||
**PrimAITE** is designed to be OS-agnostic, and thus should work on most variations/distros of Linux, Windows, and MacOS.
|
||||
Currently, the PrimAITE wheel can only be installed from GitHub. This may change in the future with release to PyPi.
|
||||
|
||||
#### Windows (PowerShell)
|
||||
|
||||
**Prerequisites:**
|
||||
* Manual install of Python >= 3.9 < 3.12
|
||||
* Manual install of Python >= 3.8 < 3.11
|
||||
|
||||
**Install:**
|
||||
|
||||
@@ -43,15 +49,20 @@ cd ~\primaite
|
||||
python3 -m venv .venv
|
||||
attrib +h .venv /s /d # Hides the .venv directory
|
||||
.\.venv\Scripts\activate
|
||||
pip install primaite-{VERSION}-py3-none-any.whl[rl]
|
||||
pip install https://github.com/Autonomous-Resilient-Cyber-Defence/PrimAITE/releases/download/v2.0.0/primaite-2.0.0-py3-none-any.whl
|
||||
primaite setup
|
||||
```
|
||||
|
||||
**Run:**
|
||||
|
||||
``` bash
|
||||
primaite session
|
||||
```
|
||||
|
||||
#### Unix
|
||||
|
||||
**Prerequisites:**
|
||||
* Manual install of Python >= 3.8 < 3.12
|
||||
* Manual install of Python >= 3.8 < 3.11
|
||||
|
||||
``` bash
|
||||
sudo add-apt-repository ppa:deadsnakes/ppa
|
||||
@@ -66,10 +77,15 @@ mkdir ~/primaite
|
||||
cd ~/primaite
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install primaite-{VERSION}-py3-none-any.whl[rl]
|
||||
pip install https://github.com/Autonomous-Resilient-Cyber-Defence/PrimAITE/releases/download/v2.0.0/primaite-2.0.0-py3-none-any.whl
|
||||
primaite setup
|
||||
```
|
||||
|
||||
**Run:**
|
||||
|
||||
``` bash
|
||||
primaite session
|
||||
```
|
||||
|
||||
|
||||
### Developer Install from Source
|
||||
@@ -77,7 +93,7 @@ To make your own changes to PrimAITE, perform the install from source (developer
|
||||
|
||||
#### 1. Clone the PrimAITE repository
|
||||
``` unix
|
||||
git clone git@github.com:{todo:fill in URL}/PrimAITE.git
|
||||
git clone git@github.com:Autonomous-Resilient-Cyber-Defence/PrimAITE.git
|
||||
```
|
||||
|
||||
#### 2. CD into the repo directory
|
||||
@@ -105,7 +121,7 @@ source venv/bin/activate
|
||||
#### 5. Install `primaite` with the dev extra into the venv along with all of it's dependencies
|
||||
|
||||
```bash
|
||||
python3 -m pip install -e .[dev,rl]
|
||||
python3 -m pip install -e .[dev]
|
||||
```
|
||||
|
||||
#### 6. Perform the PrimAITE setup:
|
||||
@@ -114,48 +130,7 @@ python3 -m pip install -e .[dev,rl]
|
||||
primaite setup
|
||||
```
|
||||
|
||||
#### Note
|
||||
*It is possible to install PrimAITE without Ray RLLib, StableBaselines3, or any deep learning libraries by omitting the `rl` flag in the pip install command.*
|
||||
|
||||
### Running PrimAITE
|
||||
|
||||
Use the provided jupyter notebooks as a starting point to try running PrimAITE. They are automatically copied to your PrimAITE notebook folder when you run `primaite setup`.
|
||||
|
||||
#### 1. Activate the virtual environment
|
||||
|
||||
##### Windows (Powershell)
|
||||
```powershell
|
||||
.\venv\Scripts\activate
|
||||
```
|
||||
|
||||
##### Unix
|
||||
```bash
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
#### 2. Open jupyter notebook
|
||||
|
||||
```bash
|
||||
python -m jupyter notebook
|
||||
```
|
||||
Then, click the URL provided by the jupyter command to open the jupyter application in your browser. You can also open notebooks in your IDE if supported.
|
||||
|
||||
## 📚 Documentation
|
||||
|
||||
### Pre requisites
|
||||
|
||||
Building the documentation requires the installation of Pandoc
|
||||
|
||||
##### Unix
|
||||
```bash
|
||||
sudo apt-get install pandoc
|
||||
```
|
||||
|
||||
##### Other operating systems
|
||||
Follow the steps in https://pandoc.org/installing.html
|
||||
|
||||
### Building the documentation
|
||||
|
||||
## 📚 Building documentation
|
||||
The PrimAITE documentation can be built with the following commands:
|
||||
|
||||
##### Unix
|
||||
@@ -169,7 +144,3 @@ make html
|
||||
cd docs
|
||||
.\make.bat html
|
||||
```
|
||||
|
||||
|
||||
## Example notebooks
|
||||
Check out the example notebooks to learn more about how PrimAITE works and how you can use it to train agents. They are automatically copied to your primaite installation directory when you run `primaite setup`.
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
from typing import Any, Dict, Optional, Tuple
|
||||
|
||||
from gymnasium.core import ObsType
|
||||
|
||||
from primaite.session.environment import PrimaiteGymEnv
|
||||
|
||||
|
||||
class BenchmarkPrimaiteGymEnv(PrimaiteGymEnv):
|
||||
"""
|
||||
Class that extends the PrimaiteGymEnv.
|
||||
|
||||
The reset method is extended so that the average rewards per episode are recorded.
|
||||
"""
|
||||
|
||||
total_time_steps: int = 0
|
||||
|
||||
def reset(self, seed: Optional[int] = None) -> Tuple[ObsType, Dict[str, Any]]:
|
||||
"""Overrides the PrimAITEGymEnv reset so that the total timesteps is saved."""
|
||||
self.total_time_steps += self.game.step_counter
|
||||
|
||||
return super().reset(seed=seed)
|
||||
@@ -1,93 +1,206 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
# © Crown-owned copyright 2023, Defence Science and Technology Laboratory UK
|
||||
import json
|
||||
import platform
|
||||
import shutil
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Final, Tuple
|
||||
from typing import Any, Dict, Final, Optional, Tuple, Union
|
||||
from unittest.mock import patch
|
||||
|
||||
from report import build_benchmark_md_report
|
||||
from stable_baselines3 import PPO
|
||||
import GPUtil
|
||||
import plotly.graph_objects as go
|
||||
import polars as pl
|
||||
import psutil
|
||||
import yaml
|
||||
from plotly.graph_objs import Figure
|
||||
from pylatex import Command, Document
|
||||
from pylatex import Figure as LatexFigure
|
||||
from pylatex import Section, Subsection, Tabular
|
||||
from pylatex.utils import bold
|
||||
|
||||
import primaite
|
||||
from benchmark import BenchmarkPrimaiteGymEnv
|
||||
from primaite.config.load import data_manipulation_config_path
|
||||
from primaite.config.lay_down_config import data_manipulation_config_path
|
||||
from primaite.data_viz.session_plots import get_plotly_config
|
||||
from primaite.environment.primaite_env import Primaite
|
||||
from primaite.primaite_session import PrimaiteSession
|
||||
|
||||
_LOGGER = primaite.getLogger(__name__)
|
||||
|
||||
_MAJOR_V = primaite.__version__.split(".")[0]
|
||||
|
||||
_BENCHMARK_ROOT = Path(__file__).parent
|
||||
_RESULTS_ROOT: Final[Path] = _BENCHMARK_ROOT / "results" / f"v{_MAJOR_V}"
|
||||
_VERSION_ROOT: Final[Path] = _RESULTS_ROOT / f"v{primaite.__version__}"
|
||||
_SESSION_METADATA_ROOT: Final[Path] = _VERSION_ROOT / "session_metadata"
|
||||
_RESULTS_ROOT: Final[Path] = _BENCHMARK_ROOT / "results"
|
||||
_RESULTS_ROOT.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
_SESSION_METADATA_ROOT.mkdir(parents=True, exist_ok=True)
|
||||
_OUTPUT_ROOT: Final[Path] = _BENCHMARK_ROOT / "output"
|
||||
# Clear and recreate the output directory
|
||||
if _OUTPUT_ROOT.exists():
|
||||
shutil.rmtree(_OUTPUT_ROOT)
|
||||
_OUTPUT_ROOT.mkdir()
|
||||
|
||||
_TRAINING_CONFIG_PATH = _BENCHMARK_ROOT / "config" / "benchmark_training_config.yaml"
|
||||
_LAY_DOWN_CONFIG_PATH = data_manipulation_config_path()
|
||||
|
||||
|
||||
class BenchmarkSession:
|
||||
"""Benchmark Session class."""
|
||||
def get_size(size_bytes: int):
|
||||
"""
|
||||
Scale bytes to its proper format.
|
||||
|
||||
gym_env: BenchmarkPrimaiteGymEnv
|
||||
"""Gym environment used by the session to train."""
|
||||
e.g:
|
||||
1253656 => '1.20MB'
|
||||
1253656678 => '1.17GB'
|
||||
|
||||
num_episodes: int
|
||||
"""Number of episodes to run the training session."""
|
||||
:
|
||||
"""
|
||||
factor = 1024
|
||||
for unit in ["", "K", "M", "G", "T", "P"]:
|
||||
if size_bytes < factor:
|
||||
return f"{size_bytes:.2f}{unit}B"
|
||||
size_bytes /= factor
|
||||
|
||||
episode_len: int
|
||||
"""The number of steps per episode."""
|
||||
|
||||
total_steps: int
|
||||
"""Number of steps to run the training session."""
|
||||
def _get_system_info() -> Dict:
|
||||
"""Builds and returns a dict containing system info."""
|
||||
uname = platform.uname()
|
||||
cpu_freq = psutil.cpu_freq()
|
||||
virtual_mem = psutil.virtual_memory()
|
||||
swap_mem = psutil.swap_memory()
|
||||
gpus = GPUtil.getGPUs()
|
||||
return {
|
||||
"System": {
|
||||
"OS": uname.system,
|
||||
"OS Version": uname.version,
|
||||
"Machine": uname.machine,
|
||||
"Processor": uname.processor,
|
||||
},
|
||||
"CPU": {
|
||||
"Physical Cores": psutil.cpu_count(logical=False),
|
||||
"Total Cores": psutil.cpu_count(logical=True),
|
||||
"Max Frequency": f"{cpu_freq.max:.2f}Mhz",
|
||||
},
|
||||
"Memory": {"Total": get_size(virtual_mem.total), "Swap Total": get_size(swap_mem.total)},
|
||||
"GPU": [{"Name": gpu.name, "Total Memory": f"{gpu.memoryTotal}MB"} for gpu in gpus],
|
||||
}
|
||||
|
||||
batch_size: int
|
||||
"""Number of steps for each episode."""
|
||||
|
||||
learning_rate: float
|
||||
"""Learning rate for the model."""
|
||||
def _build_benchmark_latex_report(
|
||||
benchmark_metadata_dict: Dict, this_version_plot_path: Path, all_version_plot_path: Path
|
||||
):
|
||||
geometry_options = {"tmargin": "2.5cm", "rmargin": "2.5cm", "bmargin": "2.5cm", "lmargin": "2.5cm"}
|
||||
data = benchmark_metadata_dict
|
||||
primaite_version = data["primaite_version"]
|
||||
|
||||
start_time: datetime
|
||||
"""Start time for the session."""
|
||||
# Create a new document
|
||||
doc = Document("report", geometry_options=geometry_options)
|
||||
# Title
|
||||
doc.preamble.append(Command("title", f"PrimAITE {primaite_version} Learning Benchmark"))
|
||||
doc.preamble.append(Command("author", "PrimAITE Dev Team"))
|
||||
doc.preamble.append(Command("date", datetime.now().date()))
|
||||
doc.append(Command("maketitle"))
|
||||
|
||||
end_time: datetime
|
||||
"""End time for the session."""
|
||||
sessions = data["total_sessions"]
|
||||
episodes = data["training_config"]["num_train_episodes"]
|
||||
steps = data["training_config"]["num_train_steps"]
|
||||
|
||||
# Body
|
||||
with doc.create(Section("Introduction")):
|
||||
doc.append(
|
||||
f"PrimAITE v{primaite_version} was benchmarked automatically upon release. Learning rate metrics "
|
||||
f"were captured to be referenced during system-level testing and user acceptance testing (UAT)."
|
||||
)
|
||||
doc.append(
|
||||
f"\nThe benchmarking process consists of running {sessions} training session using the same "
|
||||
f"training and lay down config files. Each session trains an agent for {episodes} episodes, "
|
||||
f"with each episode consisting of {steps} steps."
|
||||
)
|
||||
doc.append(
|
||||
f"\nThe mean reward per episode from each session is captured. This is then used to calculate a "
|
||||
f"combined average reward per episode from the {sessions} individual sessions for smoothing. "
|
||||
f"Finally, a 25-widow rolling average of the combined average reward per session is calculated for "
|
||||
f"further smoothing."
|
||||
)
|
||||
|
||||
with doc.create(Section("System Information")):
|
||||
with doc.create(Subsection("Python")):
|
||||
with doc.create(Tabular("|l|l|")) as table:
|
||||
table.add_hline()
|
||||
table.add_row((bold("Version"), sys.version))
|
||||
table.add_hline()
|
||||
for section, section_data in data["system_info"].items():
|
||||
if section_data:
|
||||
with doc.create(Subsection(section)):
|
||||
if isinstance(section_data, dict):
|
||||
with doc.create(Tabular("|l|l|")) as table:
|
||||
table.add_hline()
|
||||
for key, value in section_data.items():
|
||||
table.add_row((bold(key), value))
|
||||
table.add_hline()
|
||||
elif isinstance(section_data, list):
|
||||
headers = section_data[0].keys()
|
||||
tabs_str = "|".join(["l" for _ in range(len(headers))])
|
||||
tabs_str = f"|{tabs_str}|"
|
||||
with doc.create(Tabular(tabs_str)) as table:
|
||||
table.add_hline()
|
||||
table.add_row([bold(h) for h in headers])
|
||||
table.add_hline()
|
||||
for item in section_data:
|
||||
table.add_row(item.values())
|
||||
table.add_hline()
|
||||
|
||||
headers_map = {
|
||||
"total_sessions": "Total Sessions",
|
||||
"total_episodes": "Total Episodes",
|
||||
"total_time_steps": "Total Steps",
|
||||
"av_s_per_session": "Av Session Duration (s)",
|
||||
"av_s_per_step": "Av Step Duration (s)",
|
||||
"av_s_per_100_steps_10_nodes": "Av Duration per 100 Steps per 10 Nodes (s)",
|
||||
}
|
||||
with doc.create(Section("Stats")):
|
||||
with doc.create(Subsection("Benchmark Results")):
|
||||
with doc.create(Tabular("|l|l|")) as table:
|
||||
table.add_hline()
|
||||
for section, header in headers_map.items():
|
||||
if section.startswith("av_"):
|
||||
table.add_row((bold(header), f"{data[section]:.4f}"))
|
||||
else:
|
||||
table.add_row((bold(header), data[section]))
|
||||
table.add_hline()
|
||||
|
||||
with doc.create(Section("Graphs")):
|
||||
with doc.create(Subsection(f"PrimAITE {primaite_version} Learning Benchmark Plot")):
|
||||
with doc.create(LatexFigure(position="h!")) as pic:
|
||||
pic.add_image(str(this_version_plot_path))
|
||||
pic.add_caption(f"PrimAITE {primaite_version} Learning Benchmark Plot")
|
||||
|
||||
with doc.create(Subsection("PrimAITE All Versions Learning Benchmark Plot")):
|
||||
with doc.create(LatexFigure(position="h!")) as pic:
|
||||
pic.add_image(str(all_version_plot_path))
|
||||
pic.add_caption("PrimAITE All Versions Learning Benchmark Plot")
|
||||
|
||||
doc.generate_pdf(str(this_version_plot_path).replace(".png", ""), clean_tex=True)
|
||||
|
||||
|
||||
class BenchmarkPrimaiteSession(PrimaiteSession):
|
||||
"""A benchmarking primaite session."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
gym_env: BenchmarkPrimaiteGymEnv,
|
||||
episode_len: int,
|
||||
num_episodes: int,
|
||||
n_steps: int,
|
||||
batch_size: int,
|
||||
learning_rate: float,
|
||||
training_config_path: Union[str, Path],
|
||||
lay_down_config_path: Union[str, Path],
|
||||
):
|
||||
"""Initialise the BenchmarkSession."""
|
||||
self.gym_env = gym_env
|
||||
self.episode_len = episode_len
|
||||
self.n_steps = n_steps
|
||||
self.num_episodes = num_episodes
|
||||
self.total_steps = self.num_episodes * self.episode_len
|
||||
self.batch_size = batch_size
|
||||
self.learning_rate = learning_rate
|
||||
super().__init__(training_config_path, lay_down_config_path)
|
||||
self.setup()
|
||||
|
||||
def train(self):
|
||||
"""Run the training session."""
|
||||
# start timer for session
|
||||
self.start_time = datetime.now()
|
||||
model = PPO(
|
||||
policy="MlpPolicy",
|
||||
env=self.gym_env,
|
||||
learning_rate=self.learning_rate,
|
||||
n_steps=self.n_steps,
|
||||
batch_size=self.batch_size,
|
||||
verbose=0,
|
||||
tensorboard_log="./PPO_UC2/",
|
||||
)
|
||||
model.learn(total_timesteps=self.total_steps)
|
||||
@property
|
||||
def env(self) -> Primaite:
|
||||
"""Direct access to the env for ease of testing."""
|
||||
return self._agent_session._env # noqa
|
||||
|
||||
# end timer for session
|
||||
self.end_time = datetime.now()
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
self.session_metadata = self.generate_learn_metadata_dict()
|
||||
def __exit__(self, type, value, tb):
|
||||
shutil.rmtree(self.session_path)
|
||||
_LOGGER.debug(f"Deleted benchmark session directory: {self.session_path}")
|
||||
|
||||
def _learn_benchmark_durations(self) -> Tuple[float, float, float]:
|
||||
"""
|
||||
@@ -101,100 +214,236 @@ class BenchmarkSession:
|
||||
:return: The learning benchmark durations as a Tuple of three floats:
|
||||
Tuple[total_s, s_per_step, s_per_100_steps_10_nodes].
|
||||
"""
|
||||
delta = self.end_time - self.start_time
|
||||
data = self.metadata_file_as_dict()
|
||||
start_dt = datetime.fromisoformat(data["start_datetime"])
|
||||
end_dt = datetime.fromisoformat(data["end_datetime"])
|
||||
delta = end_dt - start_dt
|
||||
total_s = delta.total_seconds()
|
||||
|
||||
total_steps = self.batch_size * self.num_episodes
|
||||
total_steps = data["learning"]["total_time_steps"]
|
||||
s_per_step = total_s / total_steps
|
||||
|
||||
num_nodes = len(self.gym_env.game.simulation.network.nodes)
|
||||
num_nodes = self.env.num_nodes
|
||||
num_intervals = total_steps / 100
|
||||
av_interval_time = total_s / num_intervals
|
||||
s_per_100_steps_10_nodes = av_interval_time / (num_nodes / 10)
|
||||
|
||||
return total_s, s_per_step, s_per_100_steps_10_nodes
|
||||
|
||||
def generate_learn_metadata_dict(self) -> Dict[str, Any]:
|
||||
def learn_metadata_dict(self) -> Dict[str, Any]:
|
||||
"""Metadata specific to the learning session."""
|
||||
total_s, s_per_step, s_per_100_steps_10_nodes = self._learn_benchmark_durations()
|
||||
self.gym_env.total_reward_per_episode.pop(0) # remove episode 0
|
||||
return {
|
||||
"total_episodes": self.gym_env.episode_counter,
|
||||
"total_time_steps": self.gym_env.total_time_steps,
|
||||
"total_episodes": self.env.actual_episode_count,
|
||||
"total_time_steps": self.env.total_step_count,
|
||||
"total_s": total_s,
|
||||
"s_per_step": s_per_step,
|
||||
"s_per_100_steps_10_nodes": s_per_100_steps_10_nodes,
|
||||
"total_reward_per_episode": self.gym_env.total_reward_per_episode,
|
||||
"av_reward_per_episode": self.learn_av_reward_per_episode_dict(),
|
||||
}
|
||||
|
||||
|
||||
def _get_benchmark_primaite_environment() -> BenchmarkPrimaiteGymEnv:
|
||||
"""
|
||||
Create an instance of the BenchmarkPrimaiteGymEnv.
|
||||
|
||||
This environment will be used to train the agents on.
|
||||
"""
|
||||
env = BenchmarkPrimaiteGymEnv(env_config=data_manipulation_config_path())
|
||||
return env
|
||||
def _get_benchmark_session_path(session_timestamp: datetime) -> Path:
|
||||
return _OUTPUT_ROOT / session_timestamp.strftime("%Y-%m-%d_%H-%M-%S")
|
||||
|
||||
|
||||
def _prepare_session_directory():
|
||||
"""Prepare the session directory so that it is easier to clean up after the benchmarking is done."""
|
||||
# override session path
|
||||
session_path = _BENCHMARK_ROOT / "sessions"
|
||||
|
||||
if session_path.is_dir():
|
||||
shutil.rmtree(session_path)
|
||||
|
||||
primaite.PRIMAITE_PATHS.user_sessions_path = session_path
|
||||
primaite.PRIMAITE_PATHS.user_sessions_path.mkdir(exist_ok=True, parents=True)
|
||||
def _get_benchmark_primaite_session() -> BenchmarkPrimaiteSession:
|
||||
with patch("primaite.agents.agent_abc.get_session_path", _get_benchmark_session_path) as mck:
|
||||
mck.session_timestamp = datetime.now()
|
||||
return BenchmarkPrimaiteSession(_TRAINING_CONFIG_PATH, _LAY_DOWN_CONFIG_PATH)
|
||||
|
||||
|
||||
def run(
|
||||
number_of_sessions: int = 5,
|
||||
num_episodes: int = 1000,
|
||||
episode_len: int = 128,
|
||||
n_steps: int = 1280,
|
||||
batch_size: int = 32,
|
||||
learning_rate: float = 3e-4,
|
||||
) -> None:
|
||||
"""Run the PrimAITE benchmark."""
|
||||
benchmark_start_time = datetime.now()
|
||||
def _build_benchmark_results_dict(start_datetime: datetime, metadata_dict: Dict) -> dict:
|
||||
n = len(metadata_dict)
|
||||
with open(_TRAINING_CONFIG_PATH, "r") as file:
|
||||
training_config_dict = yaml.safe_load(file)
|
||||
with open(_LAY_DOWN_CONFIG_PATH, "r") as file:
|
||||
lay_down_config_dict = yaml.safe_load(file)
|
||||
averaged_data = {
|
||||
"start_timestamp": start_datetime.isoformat(),
|
||||
"end_datetime": datetime.now().isoformat(),
|
||||
"primaite_version": primaite.__version__,
|
||||
"system_info": _get_system_info(),
|
||||
"total_sessions": n,
|
||||
"total_episodes": sum(d["total_episodes"] for d in metadata_dict.values()),
|
||||
"total_time_steps": sum(d["total_time_steps"] for d in metadata_dict.values()),
|
||||
"av_s_per_session": sum(d["total_s"] for d in metadata_dict.values()) / n,
|
||||
"av_s_per_step": sum(d["s_per_step"] for d in metadata_dict.values()) / n,
|
||||
"av_s_per_100_steps_10_nodes": sum(d["s_per_100_steps_10_nodes"] for d in metadata_dict.values()) / n,
|
||||
"combined_av_reward_per_episode": {},
|
||||
"session_av_reward_per_episode": {k: v["av_reward_per_episode"] for k, v in metadata_dict.items()},
|
||||
"training_config": training_config_dict,
|
||||
"lay_down_config": lay_down_config_dict,
|
||||
}
|
||||
|
||||
session_metadata_dict = {}
|
||||
episodes = metadata_dict[1]["av_reward_per_episode"].keys()
|
||||
|
||||
_prepare_session_directory()
|
||||
for episode in episodes:
|
||||
combined_av_reward = sum(metadata_dict[k]["av_reward_per_episode"][episode] for k in metadata_dict.keys()) / n
|
||||
averaged_data["combined_av_reward_per_episode"][episode] = combined_av_reward
|
||||
|
||||
# run training
|
||||
for i in range(1, number_of_sessions + 1):
|
||||
print(f"Starting Benchmark Session: {i}")
|
||||
return averaged_data
|
||||
|
||||
with _get_benchmark_primaite_environment() as gym_env:
|
||||
session = BenchmarkSession(
|
||||
gym_env=gym_env,
|
||||
num_episodes=num_episodes,
|
||||
n_steps=n_steps,
|
||||
episode_len=episode_len,
|
||||
batch_size=batch_size,
|
||||
learning_rate=learning_rate,
|
||||
)
|
||||
session.train()
|
||||
|
||||
# Dump the session metadata so that we're not holding it in memory as it's large
|
||||
with open(_SESSION_METADATA_ROOT / f"{i}.json", "w") as file:
|
||||
json.dump(session.session_metadata, file, indent=4)
|
||||
def _get_df_from_episode_av_reward_dict(data: Dict):
|
||||
data: Dict = {"episode": data.keys(), "av_reward": data.values()}
|
||||
|
||||
for i in range(1, number_of_sessions + 1):
|
||||
with open(_SESSION_METADATA_ROOT / f"{i}.json", "r") as file:
|
||||
session_metadata_dict[i] = json.load(file)
|
||||
# generate report
|
||||
build_benchmark_md_report(
|
||||
benchmark_start_time=benchmark_start_time,
|
||||
session_metadata=session_metadata_dict,
|
||||
config_path=data_manipulation_config_path(),
|
||||
results_root_path=_RESULTS_ROOT,
|
||||
return (
|
||||
pl.from_dict(data)
|
||||
.with_columns(rolling_mean=pl.col("av_reward").rolling_mean(window_size=25))
|
||||
.rename({"rolling_mean": "rolling_av_reward"})
|
||||
)
|
||||
|
||||
|
||||
def _plot_benchmark_metadata(
|
||||
benchmark_metadata_dict: Dict,
|
||||
title: Optional[str] = None,
|
||||
subtitle: Optional[str] = None,
|
||||
) -> Figure:
|
||||
if title:
|
||||
if subtitle:
|
||||
title = f"{title} <br>{subtitle}</sup>"
|
||||
else:
|
||||
if subtitle:
|
||||
title = subtitle
|
||||
|
||||
config = get_plotly_config()
|
||||
layout = go.Layout(
|
||||
autosize=config["size"]["auto_size"],
|
||||
width=config["size"]["width"],
|
||||
height=config["size"]["height"],
|
||||
)
|
||||
# Create the line graph with a colored line
|
||||
fig = go.Figure(layout=layout)
|
||||
fig.update_layout(template=config["template"])
|
||||
|
||||
for session, av_reward_dict in benchmark_metadata_dict["session_av_reward_per_episode"].items():
|
||||
df = _get_df_from_episode_av_reward_dict(av_reward_dict)
|
||||
fig.add_trace(
|
||||
go.Scatter(
|
||||
x=df["episode"],
|
||||
y=df["av_reward"],
|
||||
mode="lines",
|
||||
name=f"Session {session}",
|
||||
opacity=0.25,
|
||||
line={"color": "#a6a6a6"},
|
||||
)
|
||||
)
|
||||
|
||||
df = _get_df_from_episode_av_reward_dict(benchmark_metadata_dict["combined_av_reward_per_episode"])
|
||||
fig.add_trace(
|
||||
go.Scatter(
|
||||
x=df["episode"], y=df["av_reward"], mode="lines", name="Combined Session Av", line={"color": "#FF0000"}
|
||||
)
|
||||
)
|
||||
|
||||
fig.add_trace(
|
||||
go.Scatter(
|
||||
x=df["episode"],
|
||||
y=df["rolling_av_reward"],
|
||||
mode="lines",
|
||||
name="Rolling Av (Combined Session Av)",
|
||||
line={"color": "#4CBB17"},
|
||||
)
|
||||
)
|
||||
|
||||
# Set the layout of the graph
|
||||
fig.update_layout(
|
||||
xaxis={
|
||||
"title": "Episode",
|
||||
"type": "linear",
|
||||
},
|
||||
yaxis={"title": "Average Reward"},
|
||||
title=title,
|
||||
)
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
def _plot_all_benchmarks_combined_session_av():
|
||||
"""
|
||||
Plot the Benchmark results for each released version of PrimAITE.
|
||||
|
||||
Does this by iterating over the ``benchmark/results`` directory and
|
||||
extracting the benchmark metadata json for each version that has been
|
||||
benchmarked. The combined_av_reward_per_episode is extracted from each,
|
||||
converted into a polars dataframe, and plotted as a scatter line in plotly.
|
||||
"""
|
||||
title = "PrimAITE Versions Learning Benchmark"
|
||||
subtitle = "Rolling Av (Combined Session Av)"
|
||||
if title:
|
||||
if subtitle:
|
||||
title = f"{title} <br>{subtitle}</sup>"
|
||||
else:
|
||||
if subtitle:
|
||||
title = subtitle
|
||||
config = get_plotly_config()
|
||||
layout = go.Layout(
|
||||
autosize=config["size"]["auto_size"],
|
||||
width=config["size"]["width"],
|
||||
height=config["size"]["height"],
|
||||
)
|
||||
# Create the line graph with a colored line
|
||||
fig = go.Figure(layout=layout)
|
||||
fig.update_layout(template=config["template"])
|
||||
|
||||
for dir in _RESULTS_ROOT.iterdir():
|
||||
if dir.is_dir():
|
||||
metadata_file = dir / f"{dir.name}_benchmark_metadata.json"
|
||||
with open(metadata_file, "r") as file:
|
||||
metadata_dict = json.load(file)
|
||||
df = _get_df_from_episode_av_reward_dict(metadata_dict["combined_av_reward_per_episode"])
|
||||
|
||||
fig.add_trace(go.Scatter(x=df["episode"], y=df["rolling_av_reward"], mode="lines", name=dir.name))
|
||||
|
||||
# Set the layout of the graph
|
||||
fig.update_layout(
|
||||
xaxis={
|
||||
"title": "Episode",
|
||||
"type": "linear",
|
||||
},
|
||||
yaxis={"title": "Average Reward"},
|
||||
title=title,
|
||||
)
|
||||
fig["data"][0]["showlegend"] = True
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
def run():
|
||||
"""Run the PrimAITE benchmark."""
|
||||
start_datetime = datetime.now()
|
||||
av_reward_per_episode_dicts = {}
|
||||
for i in range(1, 11):
|
||||
print(f"Starting Benchmark Session: {i}")
|
||||
with _get_benchmark_primaite_session() as session:
|
||||
session.learn()
|
||||
av_reward_per_episode_dicts[i] = session.learn_metadata_dict()
|
||||
|
||||
benchmark_metadata = _build_benchmark_results_dict(
|
||||
start_datetime=start_datetime, metadata_dict=av_reward_per_episode_dicts
|
||||
)
|
||||
v_str = f"v{primaite.__version__}"
|
||||
|
||||
version_result_dir = _RESULTS_ROOT / v_str
|
||||
if version_result_dir.exists():
|
||||
shutil.rmtree(version_result_dir)
|
||||
version_result_dir.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
with open(version_result_dir / f"{v_str}_benchmark_metadata.json", "w") as file:
|
||||
json.dump(benchmark_metadata, file, indent=4)
|
||||
title = f"PrimAITE v{primaite.__version__.strip()} Learning Benchmark"
|
||||
fig = _plot_benchmark_metadata(benchmark_metadata, title=title)
|
||||
this_version_plot_path = version_result_dir / f"{title}.png"
|
||||
fig.write_image(this_version_plot_path)
|
||||
|
||||
fig = _plot_all_benchmarks_combined_session_av()
|
||||
|
||||
all_version_plot_path = _RESULTS_ROOT / "PrimAITE Versions Learning Benchmark.png"
|
||||
fig.write_image(all_version_plot_path)
|
||||
|
||||
_build_benchmark_latex_report(benchmark_metadata, this_version_plot_path, all_version_plot_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
|
||||
@@ -1,426 +0,0 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional
|
||||
|
||||
import plotly.graph_objects as go
|
||||
import polars as pl
|
||||
import yaml
|
||||
from plotly.graph_objs import Figure
|
||||
from utils import _get_system_info
|
||||
|
||||
import primaite
|
||||
|
||||
PLOT_CONFIG = {
|
||||
"size": {"auto_size": False, "width": 1500, "height": 900},
|
||||
"template": "plotly_white",
|
||||
"range_slider": False,
|
||||
"av_s_per_100_steps_10_nodes_benchmark_threshold": 5,
|
||||
"benchmark_line_color": "grey",
|
||||
}
|
||||
|
||||
|
||||
def _build_benchmark_results_dict(start_datetime: datetime, metadata_dict: Dict, config: Dict) -> dict:
|
||||
"""
|
||||
Constructs a dictionary aggregating benchmark results from multiple sessions.
|
||||
|
||||
:param start_datetime: The datetime when the benchmarking started.
|
||||
:param metadata_dict: Dictionary containing metadata for each session.
|
||||
:param config: Configuration settings used during the benchmarking.
|
||||
:return: A dictionary containing aggregated data and metadata from the benchmarking sessions.
|
||||
"""
|
||||
num_sessions = len(metadata_dict) # number of sessions
|
||||
|
||||
averaged_data = {
|
||||
"start_timestamp": start_datetime.isoformat(),
|
||||
"end_datetime": datetime.now().isoformat(),
|
||||
"primaite_version": primaite.__version__,
|
||||
"system_info": _get_system_info(),
|
||||
"total_sessions": num_sessions,
|
||||
"total_episodes": sum(d["total_episodes"] for d in metadata_dict.values()),
|
||||
"total_time_steps": sum(d["total_time_steps"] for d in metadata_dict.values()),
|
||||
"av_s_per_session": sum(d["total_s"] for d in metadata_dict.values()) / num_sessions,
|
||||
"av_s_per_step": sum(d["s_per_step"] for d in metadata_dict.values()) / num_sessions,
|
||||
"av_s_per_100_steps_10_nodes": sum(d["s_per_100_steps_10_nodes"] for d in metadata_dict.values())
|
||||
/ num_sessions,
|
||||
"combined_total_reward_per_episode": {},
|
||||
"session_total_reward_per_episode": {k: v["total_reward_per_episode"] for k, v in metadata_dict.items()},
|
||||
"config": config,
|
||||
}
|
||||
|
||||
# find the average of each episode across all sessions
|
||||
episodes = metadata_dict[1]["total_reward_per_episode"].keys()
|
||||
|
||||
for episode in episodes:
|
||||
combined_av_reward = (
|
||||
sum(metadata_dict[k]["total_reward_per_episode"][episode] for k in metadata_dict.keys()) / num_sessions
|
||||
)
|
||||
averaged_data["combined_total_reward_per_episode"][episode] = combined_av_reward
|
||||
|
||||
return averaged_data
|
||||
|
||||
|
||||
def _get_df_from_episode_av_reward_dict(data: Dict) -> pl.DataFrame:
|
||||
"""
|
||||
Converts a dictionary of episode average rewards into a Polars DataFrame.
|
||||
|
||||
:param data: Dictionary with episodes as keys and average rewards as values.
|
||||
:return: Polars DataFrame with episodes and average rewards, including a rolling average.
|
||||
"""
|
||||
data: Dict = {"episode": data.keys(), "av_reward": data.values()}
|
||||
|
||||
return (
|
||||
pl.from_dict(data)
|
||||
.with_columns(rolling_mean=pl.col("av_reward").rolling_mean(window_size=25))
|
||||
.rename({"rolling_mean": "rolling_av_reward"})
|
||||
)
|
||||
|
||||
|
||||
def _plot_benchmark_metadata(
|
||||
benchmark_metadata_dict: Dict,
|
||||
title: Optional[str] = None,
|
||||
subtitle: Optional[str] = None,
|
||||
) -> Figure:
|
||||
"""
|
||||
Plots benchmark metadata as a line graph using Plotly.
|
||||
|
||||
:param benchmark_metadata_dict: Dictionary containing the total reward per episode and session.
|
||||
:param title: Optional title for the graph.
|
||||
:param subtitle: Optional subtitle for the graph.
|
||||
:return: Plotly figure object representing the benchmark metadata plot.
|
||||
"""
|
||||
if title:
|
||||
if subtitle:
|
||||
title = f"{title} <br>{subtitle}</sup>"
|
||||
else:
|
||||
if subtitle:
|
||||
title = subtitle
|
||||
|
||||
layout = go.Layout(
|
||||
autosize=PLOT_CONFIG["size"]["auto_size"],
|
||||
width=PLOT_CONFIG["size"]["width"],
|
||||
height=PLOT_CONFIG["size"]["height"],
|
||||
)
|
||||
# Create the line graph with a colored line
|
||||
fig = go.Figure(layout=layout)
|
||||
fig.update_layout(template=PLOT_CONFIG["template"])
|
||||
|
||||
for session, av_reward_dict in benchmark_metadata_dict["session_total_reward_per_episode"].items():
|
||||
df = _get_df_from_episode_av_reward_dict(av_reward_dict)
|
||||
fig.add_trace(
|
||||
go.Scatter(
|
||||
x=df["episode"],
|
||||
y=df["av_reward"],
|
||||
mode="lines",
|
||||
name=f"Session {session}",
|
||||
opacity=0.25,
|
||||
line={"color": "#a6a6a6"},
|
||||
)
|
||||
)
|
||||
|
||||
df = _get_df_from_episode_av_reward_dict(benchmark_metadata_dict["combined_total_reward_per_episode"])
|
||||
fig.add_trace(
|
||||
go.Scatter(
|
||||
x=df["episode"], y=df["av_reward"], mode="lines", name="Combined Session Av", line={"color": "#FF0000"}
|
||||
)
|
||||
)
|
||||
|
||||
fig.add_trace(
|
||||
go.Scatter(
|
||||
x=df["episode"],
|
||||
y=df["rolling_av_reward"],
|
||||
mode="lines",
|
||||
name="Rolling Av (Combined Session Av)",
|
||||
line={"color": "#4CBB17"},
|
||||
)
|
||||
)
|
||||
|
||||
# Set the layout of the graph
|
||||
fig.update_layout(
|
||||
xaxis={
|
||||
"title": "Episode",
|
||||
"type": "linear",
|
||||
},
|
||||
yaxis={"title": "Total Reward"},
|
||||
title=title,
|
||||
)
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
def _plot_all_benchmarks_combined_session_av(results_directory: Path) -> Figure:
|
||||
"""
|
||||
Plot the Benchmark results for each released version of PrimAITE.
|
||||
|
||||
Does this by iterating over the ``benchmark/results`` directory and
|
||||
extracting the benchmark metadata json for each version that has been
|
||||
benchmarked. The combined_total_reward_per_episode is extracted from each,
|
||||
converted into a polars dataframe, and plotted as a scatter line in plotly.
|
||||
"""
|
||||
major_v = primaite.__version__.split(".")[0]
|
||||
title = f"Learning Benchmark of Minor and Bugfix Releases for Major Version {major_v}"
|
||||
subtitle = "Rolling Av (Combined Session Av)"
|
||||
if title:
|
||||
if subtitle:
|
||||
title = f"{title} <br>{subtitle}</sup>"
|
||||
else:
|
||||
if subtitle:
|
||||
title = subtitle
|
||||
layout = go.Layout(
|
||||
autosize=PLOT_CONFIG["size"]["auto_size"],
|
||||
width=PLOT_CONFIG["size"]["width"],
|
||||
height=PLOT_CONFIG["size"]["height"],
|
||||
)
|
||||
# Create the line graph with a colored line
|
||||
fig = go.Figure(layout=layout)
|
||||
fig.update_layout(template=PLOT_CONFIG["template"])
|
||||
|
||||
for dir in results_directory.iterdir():
|
||||
if dir.is_dir():
|
||||
metadata_file = dir / f"{dir.name}_benchmark_metadata.json"
|
||||
with open(metadata_file, "r") as file:
|
||||
metadata_dict = json.load(file)
|
||||
df = _get_df_from_episode_av_reward_dict(metadata_dict["combined_total_reward_per_episode"])
|
||||
|
||||
fig.add_trace(go.Scatter(x=df["episode"], y=df["rolling_av_reward"], mode="lines", name=dir.name))
|
||||
|
||||
# Set the layout of the graph
|
||||
fig.update_layout(
|
||||
xaxis={
|
||||
"title": "Episode",
|
||||
"type": "linear",
|
||||
},
|
||||
yaxis={"title": "Total Reward"},
|
||||
title=title,
|
||||
)
|
||||
fig["data"][0]["showlegend"] = True
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
def _get_performance_benchmark_for_all_version_dict(results_directory: Path) -> Dict[str, float]:
|
||||
"""
|
||||
Gathers performance benchmarks for all versions of the software stored in a specified directory.
|
||||
|
||||
This function iterates through each directory within the specified results directory,
|
||||
extracts the av_s_per_100_steps_10_nodes from the benchmark_metadata.json files, and aggregates it into a
|
||||
dictionary.
|
||||
|
||||
:param results_directory: The directory containing subdirectories for each version's benchmark data.
|
||||
:return: A dictionary with version numbers as keys and their corresponding average performance benchmark
|
||||
(average time per 100 steps on 10 nodes) as values.
|
||||
"""
|
||||
performance_benchmark_dict = {}
|
||||
for dir in results_directory.iterdir():
|
||||
if dir.is_dir():
|
||||
metadata_file = dir / f"{dir.name}_benchmark_metadata.json"
|
||||
with open(metadata_file, "r") as file:
|
||||
metadata_dict = json.load(file)
|
||||
version = metadata_dict["primaite_version"]
|
||||
performance_benchmark_dict[version] = metadata_dict["av_s_per_100_steps_10_nodes"]
|
||||
return performance_benchmark_dict
|
||||
|
||||
|
||||
def _plot_av_s_per_100_steps_10_nodes(
|
||||
version_times_dict: Dict[str, float],
|
||||
) -> Figure:
|
||||
"""
|
||||
Creates a bar chart visualising the performance of each version of PrimAITE.
|
||||
|
||||
Performance is based on the average training time per 100 steps on 10 nodes. The function also includes a benchmark
|
||||
line indicating the target maximum time.
|
||||
|
||||
Versions that perform under this time are marked in green, and those over are marked in red.
|
||||
|
||||
:param version_times_dict: A dictionary with software versions as keys and average times as values.
|
||||
:return: A Plotly figure object representing the bar chart of the performance metrics.
|
||||
"""
|
||||
major_v = primaite.__version__.split(".")[0]
|
||||
title = f"Performance of Minor and Bugfix Releases for Major Version {major_v}"
|
||||
subtitle = (
|
||||
f"Average Training Time per 100 Steps on 10 Nodes "
|
||||
f"(target: <= {PLOT_CONFIG['av_s_per_100_steps_10_nodes_benchmark_threshold']} seconds)"
|
||||
)
|
||||
title = f"{title} <br><sub>{subtitle}</sub>"
|
||||
|
||||
layout = go.Layout(
|
||||
autosize=PLOT_CONFIG["size"]["auto_size"],
|
||||
width=PLOT_CONFIG["size"]["width"],
|
||||
height=PLOT_CONFIG["size"]["height"],
|
||||
)
|
||||
fig = go.Figure(layout=layout)
|
||||
fig.update_layout(template=PLOT_CONFIG["template"])
|
||||
|
||||
versions = sorted(list(version_times_dict.keys()))
|
||||
times = [version_times_dict[version] for version in versions]
|
||||
av_s_per_100_steps_10_nodes_benchmark_threshold = PLOT_CONFIG["av_s_per_100_steps_10_nodes_benchmark_threshold"]
|
||||
benchmark_line_color = PLOT_CONFIG["benchmark_line_color"]
|
||||
|
||||
# Calculate the appropriate maximum y-axis value
|
||||
max_y_axis_value = max(max(times), av_s_per_100_steps_10_nodes_benchmark_threshold) + 1
|
||||
|
||||
fig.add_trace(
|
||||
go.Bar(
|
||||
x=versions,
|
||||
y=times,
|
||||
marker_color=[
|
||||
"green" if time < av_s_per_100_steps_10_nodes_benchmark_threshold else "red" for time in times
|
||||
],
|
||||
text=times,
|
||||
textposition="auto",
|
||||
)
|
||||
)
|
||||
|
||||
# Add a horizontal line for the benchmark
|
||||
fig.add_shape(
|
||||
type="line",
|
||||
x0=-0.5, # start slightly before the first bar
|
||||
x1=len(versions) - 0.5, # end slightly after the last bar
|
||||
y0=av_s_per_100_steps_10_nodes_benchmark_threshold,
|
||||
y1=av_s_per_100_steps_10_nodes_benchmark_threshold,
|
||||
line=dict(
|
||||
color=benchmark_line_color,
|
||||
width=2,
|
||||
dash="dot",
|
||||
),
|
||||
)
|
||||
|
||||
fig.update_layout(
|
||||
xaxis_title="PrimAITE Version",
|
||||
yaxis_title="Avg Time per 100 Steps on 10 Nodes (seconds)",
|
||||
yaxis=dict(range=[0, max_y_axis_value]),
|
||||
title=title,
|
||||
)
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
def build_benchmark_md_report(
|
||||
benchmark_start_time: datetime, session_metadata: Dict, config_path: Path, results_root_path: Path
|
||||
) -> None:
|
||||
"""
|
||||
Generates a Markdown report for a benchmarking session, documenting performance metrics and graphs.
|
||||
|
||||
This function orchestrates the creation of several graphs depicting various performance benchmarks and aggregates
|
||||
them into a markdown document that includes comprehensive system and benchmark information.
|
||||
|
||||
:param benchmark_start_time: The datetime object representing when the benchmarking process was initiated.
|
||||
:param session_metadata: A dictionary containing metadata for each benchmarking session.
|
||||
:param config_path: A pathlib.Path object pointing to the configuration file used for the benchmark sessions.
|
||||
:param results_root_path: A pathlib.Path object pointing to the directory where the results and graphs should be
|
||||
saved.
|
||||
"""
|
||||
# generate report folder
|
||||
v_str = f"v{primaite.__version__}"
|
||||
|
||||
version_result_dir = results_root_path / v_str
|
||||
version_result_dir.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
# load the config file as dict
|
||||
with open(config_path, "r") as f:
|
||||
cfg_data = yaml.safe_load(f)
|
||||
|
||||
# generate the benchmark metadata dict
|
||||
benchmark_metadata_dict = _build_benchmark_results_dict(
|
||||
start_datetime=benchmark_start_time, metadata_dict=session_metadata, config=cfg_data
|
||||
)
|
||||
major_v = primaite.__version__.split(".")[0]
|
||||
with open(version_result_dir / f"{v_str}_benchmark_metadata.json", "w") as file:
|
||||
json.dump(benchmark_metadata_dict, file, indent=4)
|
||||
title = f"PrimAITE v{primaite.__version__.strip()} Learning Benchmark"
|
||||
fig = _plot_benchmark_metadata(benchmark_metadata_dict, title=title)
|
||||
this_version_plot_path = version_result_dir / f"{title}.png"
|
||||
fig.write_image(this_version_plot_path)
|
||||
|
||||
fig = _plot_all_benchmarks_combined_session_av(results_directory=results_root_path)
|
||||
|
||||
filename = f"PrimAITE Learning Benchmark of Minor and Bugfix Releases for Major Version {major_v}.png"
|
||||
|
||||
all_version_plot_path = version_result_dir / filename
|
||||
fig.write_image(all_version_plot_path)
|
||||
|
||||
performance_benchmark_dict = _get_performance_benchmark_for_all_version_dict(results_directory=results_root_path)
|
||||
fig = _plot_av_s_per_100_steps_10_nodes(performance_benchmark_dict)
|
||||
filename = f"PrimAITE Performance of Minor and Bugfix Releases for Major Version {major_v}.png"
|
||||
performance_benchmark_plot_path = version_result_dir / filename
|
||||
fig.write_image(performance_benchmark_plot_path)
|
||||
|
||||
data = benchmark_metadata_dict
|
||||
primaite_version = data["primaite_version"]
|
||||
|
||||
with open(version_result_dir / f"PrimAITE v{primaite_version} Benchmark Report.md", "w") as file:
|
||||
# Title
|
||||
file.write(f"# PrimAITE v{primaite_version} Learning Benchmark\n")
|
||||
file.write("## PrimAITE Dev Team\n")
|
||||
file.write(f"### {datetime.now().date()}\n")
|
||||
file.write("\n---\n")
|
||||
|
||||
sessions = data["total_sessions"]
|
||||
episodes = session_metadata[1]["total_episodes"] - 1
|
||||
steps = data["config"]["game"]["max_episode_length"]
|
||||
|
||||
# Body
|
||||
file.write("## 1 Introduction\n")
|
||||
file.write(
|
||||
f"PrimAITE v{primaite_version} was benchmarked automatically upon release. Learning rate metrics "
|
||||
f"were captured to be referenced during system-level testing and user acceptance testing (UAT).\n"
|
||||
)
|
||||
file.write(
|
||||
f"The benchmarking process consists of running {sessions} training session using the same "
|
||||
f"config file. Each session trains an agent for {episodes} episodes, "
|
||||
f"with each episode consisting of {steps} steps.\n"
|
||||
)
|
||||
file.write(
|
||||
f"The total reward per episode from each session is captured. This is then used to calculate an "
|
||||
f"caverage total reward per episode from the {sessions} individual sessions for smoothing. "
|
||||
f"Finally, a 25-widow rolling average of the average total reward per session is calculated for "
|
||||
f"further smoothing.\n"
|
||||
)
|
||||
|
||||
file.write("## 2 System Information\n")
|
||||
i = 1
|
||||
file.write(f"### 2.{i} Python\n")
|
||||
file.write(f"**Version:** {sys.version}\n")
|
||||
|
||||
for section, section_data in data["system_info"].items():
|
||||
i += 1
|
||||
if section_data:
|
||||
file.write(f"### 2.{i} {section}\n")
|
||||
if isinstance(section_data, dict):
|
||||
for key, value in section_data.items():
|
||||
file.write(f"- **{key}:** {value}\n")
|
||||
|
||||
headers_map = {
|
||||
"total_sessions": "Total Sessions",
|
||||
"total_episodes": "Total Episodes",
|
||||
"total_time_steps": "Total Steps",
|
||||
"av_s_per_session": "Av Session Duration (s)",
|
||||
"av_s_per_step": "Av Step Duration (s)",
|
||||
"av_s_per_100_steps_10_nodes": "Av Duration per 100 Steps per 10 Nodes (s)",
|
||||
}
|
||||
|
||||
file.write("## 3 Stats\n")
|
||||
for section, header in headers_map.items():
|
||||
if section.startswith("av_"):
|
||||
file.write(f"- **{header}:** {data[section]:.4f}\n")
|
||||
else:
|
||||
file.write(f"- **{header}:** {data[section]}\n")
|
||||
|
||||
file.write("## 4 Graphs\n")
|
||||
|
||||
file.write(f"### 4.1 v{primaite_version} Learning Benchmark Plot\n")
|
||||
file.write(f"\n")
|
||||
|
||||
file.write(f"### 4.2 Learning Benchmark of Minor and Bugfix Releases for Major Version {major_v}\n")
|
||||
file.write(
|
||||
f"![Learning Benchmark of Minor and Bugfix Releases for Major Version {major_v}]"
|
||||
f"({all_version_plot_path.name})\n"
|
||||
)
|
||||
|
||||
file.write(f"### 4.3 Performance of Minor and Bugfix Releases for Major Version {major_v}\n")
|
||||
file.write(
|
||||
f"![Performance of Minor and Bugfix Releases for Major Version {major_v}]"
|
||||
f"({performance_benchmark_plot_path.name})\n"
|
||||
)
|
||||
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 225 KiB After Width: | Height: | Size: 225 KiB |
|
Before Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 60 KiB |
@@ -1,38 +0,0 @@
|
||||
# PrimAITE v3.0.0 Learning Benchmark
|
||||
## PrimAITE Dev Team
|
||||
### 2024-07-20
|
||||
|
||||
---
|
||||
## 1 Introduction
|
||||
PrimAITE v3.0.0 was benchmarked automatically upon release. Learning rate metrics were captured to be referenced during system-level testing and user acceptance testing (UAT).
|
||||
The benchmarking process consists of running 5 training session using the same config file. Each session trains an agent for 1000 episodes, with each episode consisting of 128 steps.
|
||||
The total reward per episode from each session is captured. This is then used to calculate an caverage total reward per episode from the 5 individual sessions for smoothing. Finally, a 25-widow rolling average of the average total reward per session is calculated for further smoothing.
|
||||
## 2 System Information
|
||||
### 2.1 Python
|
||||
**Version:** 3.10.14 (main, Apr 6 2024, 18:45:05) [GCC 9.4.0]
|
||||
### 2.2 System
|
||||
- **OS:** Linux
|
||||
- **OS Version:** #76~20.04.1-Ubuntu SMP Thu Jun 13 18:00:23 UTC 2024
|
||||
- **Machine:** x86_64
|
||||
- **Processor:** x86_64
|
||||
### 2.3 CPU
|
||||
- **Physical Cores:** 2
|
||||
- **Total Cores:** 4
|
||||
- **Max Frequency:** 0.00Mhz
|
||||
### 2.4 Memory
|
||||
- **Total:** 15.62GB
|
||||
- **Swap Total:** 0.00B
|
||||
## 3 Stats
|
||||
- **Total Sessions:** 5
|
||||
- **Total Episodes:** 5005
|
||||
- **Total Steps:** 640000
|
||||
- **Av Session Duration (s):** 1452.5910
|
||||
- **Av Step Duration (s):** 0.0454
|
||||
- **Av Duration per 100 Steps per 10 Nodes (s):** 4.5393
|
||||
## 4 Graphs
|
||||
### 4.1 v3.0.0 Learning Benchmark Plot
|
||||

|
||||
### 4.2 Learning Benchmark of Minor and Bugfix Releases for Major Version 3
|
||||

|
||||
### 4.3 Performance of Minor and Bugfix Releases for Major Version 3
|
||||

|
||||
|
Before Width: | Height: | Size: 316 KiB |
|
Before Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 67 KiB |
@@ -1,38 +0,0 @@
|
||||
# PrimAITE v3.1.0 Learning Benchmark
|
||||
## PrimAITE Dev Team
|
||||
### 2024-07-20
|
||||
|
||||
---
|
||||
## 1 Introduction
|
||||
PrimAITE v3.1.0 was benchmarked automatically upon release. Learning rate metrics were captured to be referenced during system-level testing and user acceptance testing (UAT).
|
||||
The benchmarking process consists of running 5 training session using the same config file. Each session trains an agent for 1000 episodes, with each episode consisting of 128 steps.
|
||||
The total reward per episode from each session is captured. This is then used to calculate an caverage total reward per episode from the 5 individual sessions for smoothing. Finally, a 25-widow rolling average of the average total reward per session is calculated for further smoothing.
|
||||
## 2 System Information
|
||||
### 2.1 Python
|
||||
**Version:** 3.10.14 (main, Apr 6 2024, 18:45:05) [GCC 9.4.0]
|
||||
### 2.2 System
|
||||
- **OS:** Linux
|
||||
- **OS Version:** #76~20.04.1-Ubuntu SMP Thu Jun 13 18:00:23 UTC 2024
|
||||
- **Machine:** x86_64
|
||||
- **Processor:** x86_64
|
||||
### 2.3 CPU
|
||||
- **Physical Cores:** 2
|
||||
- **Total Cores:** 4
|
||||
- **Max Frequency:** 0.00Mhz
|
||||
### 2.4 Memory
|
||||
- **Total:** 15.62GB
|
||||
- **Swap Total:** 0.00B
|
||||
## 3 Stats
|
||||
- **Total Sessions:** 5
|
||||
- **Total Episodes:** 5005
|
||||
- **Total Steps:** 640000
|
||||
- **Av Session Duration (s):** 1632.8888
|
||||
- **Av Step Duration (s):** 0.0510
|
||||
- **Av Duration per 100 Steps per 10 Nodes (s):** 5.1028
|
||||
## 4 Graphs
|
||||
### 4.1 v3.1.0 Learning Benchmark Plot
|
||||

|
||||
### 4.2 Learning Benchmark of Minor and Bugfix Releases for Major Version 3
|
||||

|
||||
### 4.3 Performance of Minor and Bugfix Releases for Major Version 3
|
||||

|
||||
|
Before Width: | Height: | Size: 304 KiB |
@@ -1,47 +0,0 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
import platform
|
||||
from typing import Dict
|
||||
|
||||
import psutil
|
||||
from GPUtil import GPUtil
|
||||
|
||||
|
||||
def get_size(size_bytes: int) -> str:
|
||||
"""
|
||||
Scale bytes to its proper format.
|
||||
|
||||
e.g:
|
||||
1253656 => '1.20MB'
|
||||
1253656678 => '1.17GB'
|
||||
|
||||
:
|
||||
"""
|
||||
factor = 1024
|
||||
for unit in ["", "K", "M", "G", "T", "P"]:
|
||||
if size_bytes < factor:
|
||||
return f"{size_bytes:.2f}{unit}B"
|
||||
size_bytes /= factor
|
||||
|
||||
|
||||
def _get_system_info() -> Dict:
|
||||
"""Builds and returns a dict containing system info."""
|
||||
uname = platform.uname()
|
||||
cpu_freq = psutil.cpu_freq()
|
||||
virtual_mem = psutil.virtual_memory()
|
||||
swap_mem = psutil.swap_memory()
|
||||
gpus = GPUtil.getGPUs()
|
||||
return {
|
||||
"System": {
|
||||
"OS": uname.system,
|
||||
"OS Version": uname.version,
|
||||
"Machine": uname.machine,
|
||||
"Processor": uname.processor,
|
||||
},
|
||||
"CPU": {
|
||||
"Physical Cores": psutil.cpu_count(logical=False),
|
||||
"Total Cores": psutil.cpu_count(logical=True),
|
||||
"Max Frequency": f"{cpu_freq.max:.2f}Mhz",
|
||||
},
|
||||
"Memory": {"Total": get_size(virtual_mem.total), "Swap Total": get_size(swap_mem.total)},
|
||||
"GPU": [{"Name": gpu.name, "Total Memory": f"{gpu.memoryTotal}MB"} for gpu in gpus],
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Constants
|
||||
CURRENT_YEAR = datetime.date.today().year
|
||||
COPYRIGHT_PY_STR = f"# © Crown-owned copyright {CURRENT_YEAR}, Defence Science and Technology Laboratory UK"
|
||||
COPYRIGHT_RST_LINES = [
|
||||
".. only:: comment",
|
||||
"",
|
||||
f" © Crown-owned copyright {CURRENT_YEAR}, Defence Science and Technology Laboratory UK",
|
||||
]
|
||||
PATHS = {Path("./src"), Path("./tests"), Path("./docs"), Path("./benchmark")}
|
||||
EXTENSIONS = {".py", ".rst"}
|
||||
|
||||
|
||||
def _is_copyright_line(line: str) -> bool:
|
||||
"""
|
||||
Check if a line is a copyright line.
|
||||
|
||||
:param line: The line to check.
|
||||
:return: True if the line is a copyright line, False otherwise.
|
||||
"""
|
||||
return line.startswith("#") and "copyright" in line.lower()
|
||||
|
||||
|
||||
def _is_rst_copyright_lines(lines: list) -> bool:
|
||||
"""
|
||||
Check if the lines match the RST copyright format.
|
||||
|
||||
:param lines: The lines to check.
|
||||
:return: True if the lines match the RST copyright format, False otherwise.
|
||||
"""
|
||||
return len(lines) >= 3 and lines[0] == ".. only:: comment" and "copyright" in lines[2].lower()
|
||||
|
||||
|
||||
def process_py_file(file_path: Path) -> bool:
|
||||
"""
|
||||
Process a Python file to check and add/update the copyright clause.
|
||||
|
||||
:param file_path: The path to the file to check and update.
|
||||
:return: True if the file was modified, False otherwise.
|
||||
"""
|
||||
modified = False
|
||||
try:
|
||||
content = file_path.read_text(encoding="utf-8")
|
||||
lines = content.splitlines(keepends=True) # Keep line endings
|
||||
|
||||
if lines and _is_copyright_line(lines[0]):
|
||||
if lines[0].strip() != COPYRIGHT_PY_STR:
|
||||
lines[0] = COPYRIGHT_PY_STR + "\n"
|
||||
modified = True
|
||||
print(f"Updated copyright clause in {file_path}")
|
||||
else:
|
||||
lines.insert(0, COPYRIGHT_PY_STR + "\n")
|
||||
modified = True
|
||||
print(f"Added copyright clause to {file_path}")
|
||||
|
||||
if modified:
|
||||
file_path.write_text("".join(lines), encoding="utf-8")
|
||||
except Exception as e:
|
||||
print(f"Failed to process {file_path}: {e}")
|
||||
return False
|
||||
|
||||
return modified
|
||||
|
||||
|
||||
def process_rst_file(file_path: Path) -> bool:
|
||||
"""
|
||||
Process an RST file to check and add/update the copyright clause.
|
||||
|
||||
:param file_path: The path to the file to check and update.
|
||||
:return: True if the file was modified, False otherwise.
|
||||
"""
|
||||
modified = False
|
||||
try:
|
||||
content = file_path.read_text(encoding="utf-8")
|
||||
lines = content.splitlines(keepends=True) # Keep line endings
|
||||
|
||||
existing_block = any(".. only:: comment" in line for line in lines)
|
||||
|
||||
if existing_block:
|
||||
# Check if the block is correct
|
||||
for i, line in enumerate(lines):
|
||||
if line.strip() == ".. only:: comment":
|
||||
if lines[i : i + 3] != [
|
||||
COPYRIGHT_RST_LINES[0] + "\n",
|
||||
COPYRIGHT_RST_LINES[1] + "\n",
|
||||
COPYRIGHT_RST_LINES[2] + "\n",
|
||||
]:
|
||||
# Update the incorrect block
|
||||
lines[i : i + 3] = [
|
||||
COPYRIGHT_RST_LINES[0] + "\n",
|
||||
COPYRIGHT_RST_LINES[1] + "\n",
|
||||
COPYRIGHT_RST_LINES[2] + "\n",
|
||||
]
|
||||
modified = True
|
||||
print(f"Updated copyright clause in {file_path}")
|
||||
break
|
||||
else:
|
||||
# Insert new copyright block
|
||||
lines = [line + "\n" for line in COPYRIGHT_RST_LINES] + ["\n"] + lines
|
||||
modified = True
|
||||
print(f"Added copyright clause to {file_path}")
|
||||
|
||||
if modified:
|
||||
file_path.write_text("".join(lines), encoding="utf-8")
|
||||
except Exception as e:
|
||||
print(f"Failed to process {file_path}: {e}")
|
||||
return False
|
||||
|
||||
return modified
|
||||
|
||||
|
||||
def process_file(file_path: Path) -> bool:
|
||||
"""
|
||||
Check if a file has the correct copyright clause and add or update it if necessary.
|
||||
|
||||
:param file_path: The path to the file to check and update.
|
||||
:return: True if the file was modified, False otherwise.
|
||||
"""
|
||||
if file_path.suffix == ".py":
|
||||
return process_py_file(file_path)
|
||||
elif file_path.suffix == ".rst":
|
||||
return process_rst_file(file_path)
|
||||
return False
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""
|
||||
Main function to walk through the root directories, check files, and update the copyright clause.
|
||||
|
||||
:return: 1 if any file was modified, 0 otherwise.
|
||||
"""
|
||||
files_checked = 0
|
||||
files_modified = 0
|
||||
any_file_modified = False
|
||||
for path in PATHS:
|
||||
for file_path in path.rglob("*"):
|
||||
if file_path.suffix in EXTENSIONS:
|
||||
files_checked += 1
|
||||
if process_file(file_path):
|
||||
files_modified += 1
|
||||
any_file_modified = True
|
||||
|
||||
if any_file_modified:
|
||||
print(f"Files Checked: {files_checked}. Files Modified: {files_modified}")
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -48,7 +48,7 @@ class "ActiveNode" as primaite.nodes.active_node.ActiveNode {
|
||||
file_system_state_actual : GOOD
|
||||
file_system_state_observed : REPAIRING, RESTORING, GOOD
|
||||
ip_address : str
|
||||
fixing_count : int
|
||||
patching_count : int
|
||||
software_state
|
||||
software_state : GOOD
|
||||
set_file_system_state(file_system_state: FileSystemState) -> None
|
||||
@@ -353,10 +353,10 @@ class "SB3Agent" as primaite.agents.sb3.SB3Agent {
|
||||
}
|
||||
class "Service" as primaite.common.service.Service {
|
||||
name : str
|
||||
fixing_count : int
|
||||
patching_count : int
|
||||
port : str
|
||||
software_state : GOOD
|
||||
reduce_fixing_count() -> None
|
||||
reduce_patching_count() -> None
|
||||
}
|
||||
class "ServiceNode" as primaite.nodes.service_node.ServiceNode {
|
||||
services : Dict[str, Service]
|
||||
@@ -455,7 +455,7 @@ class "TrainingConfig" as primaite.config.training_config.TrainingConfig {
|
||||
sb3_output_verbose_level
|
||||
scanning : float
|
||||
seed : Optional[int]
|
||||
service_fixing_duration : int
|
||||
service_patching_duration : int
|
||||
session_type
|
||||
time_delay : int
|
||||
from_dict(config_dict: Dict[str, Any]) -> TrainingConfig
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
# Minimal makefile for Sphinx documentation
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
@@ -7,7 +6,7 @@ SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
AUTOSUMMARY="source/_autosummary"
|
||||
AUTOSUMMARY="source\_autosummary"
|
||||
|
||||
# Remove command is different depending on OS
|
||||
ifdef OS
|
||||
@@ -30,5 +29,6 @@ clean:
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile | clean
|
||||
pip-licenses --format=rst --with-urls --output-file=source/primaite-dependencies.rst
|
||||
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
BIN
docs/_static/component_relationship.png
vendored
|
Before Width: | Height: | Size: 80 KiB |
BIN
docs/_static/firewall_acl.png
vendored
|
Before Width: | Height: | Size: 23 KiB |
BIN
docs/_static/four_node_two_switch_network.png
vendored
|
Before Width: | Height: | Size: 88 KiB |
BIN
docs/_static/node_nic_link_component_diagram.png
vendored
|
Before Width: | Height: | Size: 25 KiB |
BIN
docs/_static/notebooks/extensions.png
vendored
|
Before Width: | Height: | Size: 68 KiB |
BIN
docs/_static/notebooks/install_extensions.png
vendored
|
Before Width: | Height: | Size: 193 KiB |
BIN
docs/_static/primAITE_architecture.png
vendored
|
Before Width: | Height: | Size: 106 KiB |
BIN
docs/_static/switched_p2p_network.png
vendored
|
Before Width: | Height: | Size: 9.0 KiB |
2
docs/_templates/custom-class-template.rst
vendored
@@ -1,6 +1,6 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
© Crown-owned copyright 2023, Defence Science and Technology Laboratory UK
|
||||
|
||||
..
|
||||
Credit to https://github.com/JamesALeedham/Sphinx-Autosummary-Recursion for the custom templates.
|
||||
|
||||
2
docs/_templates/custom-module-template.rst
vendored
@@ -1,6 +1,6 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
© Crown-owned copyright 2023, Defence Science and Technology Laboratory UK
|
||||
|
||||
..
|
||||
Credit to https://github.com/JamesALeedham/Sphinx-Autosummary-Recursion for the custom templates.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
:orphan:
|
||||
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
© Crown-owned copyright 2023, Defence Science and Technology Laboratory UK
|
||||
|
||||
..
|
||||
DO NOT DELETE THIS FILE! It contains the all-important `.. autosummary::` directive with `:recursive:` option, without
|
||||
@@ -19,3 +17,4 @@
|
||||
:recursive:
|
||||
|
||||
primaite
|
||||
tests
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
#!/bin/bash
|
||||
set -x
|
||||
|
||||
@@ -43,7 +42,7 @@ touch .nojekyll
|
||||
# Add README
|
||||
cat > README.md <<EOF
|
||||
# README for the Sphinx Docs GitHub Pages Branch
|
||||
This branch is simply a cache for the website served from https://{todo:fill in URL}.github.io/PrimAITE/,
|
||||
This branch is simply a cache for the website served from https://Autonomous-Resilient-Cyber-Defence.github.io/PrimAITE/,
|
||||
and is not intended to be viewed on github.com.
|
||||
For more information on how this site is built using Sphinx, Read the Docs, GitHub Actions/Pages, and demo
|
||||
implementation from https://github.com/annegentle, see:
|
||||
|
||||
138
docs/conf.py
@@ -1,4 +1,4 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
# © Crown-owned copyright 2023, Defence Science and Technology Laboratory UK
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
@@ -9,15 +9,13 @@ import datetime
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any, List, Optional
|
||||
|
||||
import furo # noqa
|
||||
|
||||
sys.path.insert(0, os.path.abspath("../"))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
year = datetime.datetime.now().year
|
||||
project = "PrimAITE"
|
||||
@@ -30,11 +28,6 @@ with open("../src/primaite/VERSION", "r") as file:
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = version
|
||||
|
||||
# set global variables
|
||||
rst_prolog = f"""
|
||||
.. |VERSION| replace:: {release}
|
||||
"""
|
||||
|
||||
html_title = f"{project} v{release} docs"
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
@@ -50,136 +43,15 @@ extensions = [
|
||||
"sphinx.ext.viewcode", # Add a link to the Python source code for classes, functions etc.
|
||||
"sphinx.ext.todo",
|
||||
"sphinx_copybutton", # Adds a copy button to code blocks
|
||||
"nbsphinx",
|
||||
]
|
||||
|
||||
|
||||
templates_path = ["_templates"]
|
||||
exclude_patterns = [
|
||||
"_build",
|
||||
"Thumbs.db",
|
||||
".DS_Store",
|
||||
]
|
||||
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
html_theme = "furo"
|
||||
html_static_path = ["_static"]
|
||||
html_theme_options = {"globaltoc_collapse": True, "globaltoc_maxdepth": 2}
|
||||
html_copy_source = False
|
||||
nbsphinx_allow_errors = False # set to True to take shortcuts
|
||||
html_scaled_image_link = False
|
||||
|
||||
# make some stuff easier to read
|
||||
nbsphinx_prolog = """
|
||||
.. raw:: html
|
||||
|
||||
<style>
|
||||
.stderr {
|
||||
color: #000 !important
|
||||
}
|
||||
</style>
|
||||
"""
|
||||
|
||||
|
||||
def replace_token(app: Any, docname: Any, source: Any):
|
||||
"""Replaces a token from the list of tokens."""
|
||||
result = source[0]
|
||||
for key in app.config.tokens:
|
||||
result = result.replace(key, app.config.tokens[key])
|
||||
source[0] = result
|
||||
|
||||
|
||||
tokens = {
|
||||
"{VERSION}": release,
|
||||
} # Token VERSION is replaced by the value of the PrimAITE version in the version file
|
||||
"""Dict containing the tokens that need to be replaced in documentation."""
|
||||
|
||||
|
||||
def notebook_assets(ignored_files: Optional[List[str]] = [], include_file_types: Optional[List[str]] = []) -> Any:
|
||||
"""
|
||||
Creates a function to be used with `shutil.copytree`'s `ignore` parameter.
|
||||
|
||||
:param ignored_files: A list of specific file names to ignore. If a file in the directory matches one of these
|
||||
names, it will be excluded from the copy process.
|
||||
:type ignored_files: Optional[List[str]]
|
||||
:param include_file_types: A list of file extensions to include in the copy process. Files that do not match these
|
||||
extensions will be excluded. If this list is empty, all files will be excluded, effectively copying only
|
||||
directories.
|
||||
:type include_file_types: Optional[List[str]]
|
||||
"""
|
||||
|
||||
def ignore_items(directory: List[str], contents: List[str]) -> List[str]:
|
||||
"""
|
||||
Determines which files and directories should be ignored during the copy process.
|
||||
|
||||
:param directory: The directory being copied.
|
||||
:type directory: str
|
||||
:param contents: A list of contents in the directory.
|
||||
:type contents: List[str]
|
||||
:return: A list of items to exclude from the copy process.
|
||||
:rtype: List[str]
|
||||
"""
|
||||
exclude_items = []
|
||||
|
||||
for item in contents:
|
||||
if item in ignored_files:
|
||||
exclude_items.append(item)
|
||||
continue
|
||||
|
||||
if len(include_file_types) > 0:
|
||||
if not any(item.lower().endswith(ext.lower()) for ext in include_file_types) and os.path.isdir(item):
|
||||
exclude_items.append(item)
|
||||
else:
|
||||
# if we dont specify which files to include, exclude everything
|
||||
exclude_items.append(item)
|
||||
|
||||
# exclude files but not directories
|
||||
return [path for path in exclude_items if not (Path(directory) / path).is_dir()]
|
||||
|
||||
return ignore_items
|
||||
|
||||
|
||||
def copy_notebooks_to_docs() -> Any:
|
||||
"""
|
||||
Incredibly over-engineered method that copies the notebooks and its assets to a directory within the docs directory.
|
||||
|
||||
This allows developers to create new notebooks without having to worry about updating documentation when
|
||||
a new notebook is included within PrimAITE.
|
||||
"""
|
||||
notebook_asset_types = [".ipynb", ".png", ".svg"]
|
||||
notebook_directories = []
|
||||
|
||||
# find paths where notebooks are contained
|
||||
for notebook in Path("../src/primaite").rglob("*.ipynb"):
|
||||
# add parent path to notebook directory if not already added
|
||||
if notebook.parent not in notebook_directories:
|
||||
notebook_directories.append(notebook.parent)
|
||||
|
||||
# go through the notebook directories and copy the notebooks and extra assets
|
||||
for notebook_parent in notebook_directories:
|
||||
shutil.copytree(
|
||||
src=notebook_parent,
|
||||
dst=Path("source") / "notebooks" / notebook_parent.name,
|
||||
ignore=notebook_assets(include_file_types=notebook_asset_types),
|
||||
dirs_exist_ok=True,
|
||||
)
|
||||
|
||||
|
||||
def suppress_log_output():
|
||||
"""Sets the log level while building the documentation."""
|
||||
from primaite import _FILE_HANDLER, _LOGGER, _STREAM_HANDLER
|
||||
|
||||
log_level = "WARN"
|
||||
|
||||
_LOGGER.setLevel(log_level)
|
||||
_STREAM_HANDLER.setLevel(log_level)
|
||||
_FILE_HANDLER.setLevel(log_level)
|
||||
|
||||
|
||||
def setup(app: Any):
|
||||
"""Custom setup for sphinx."""
|
||||
suppress_log_output()
|
||||
copy_notebooks_to_docs()
|
||||
app.add_config_value("tokens", {}, True)
|
||||
app.connect("source-read", replace_token)
|
||||
|
||||
135
docs/index.rst
@@ -1,6 +1,6 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
© Crown-owned copyright 2023, Defence Science and Technology Laboratory UK
|
||||
|
||||
Welcome to PrimAITE's documentation
|
||||
====================================
|
||||
@@ -11,84 +11,71 @@ What is PrimAITE?
|
||||
Overview
|
||||
^^^^^^^^
|
||||
|
||||
The ARCD Primary-level AI Training Environment (**PrimAITE**) provides an effective simulation capability for training and evaluating AI in a cyber-defensive role. It incorporates the functionality required of a primary-level ARCD environment:
|
||||
The ARCD Primary-level AI Training Environment (**PrimAITE**) provides an effective simulation capability for the purposes of training and evaluating AI in a cyber-defensive role. It incorporates the functionality required of a primary-level ARCD environment, which includes:
|
||||
|
||||
- The ability to model a relevant system context;
|
||||
- The ability to model a relevant platform / system context;
|
||||
- Modelling an adversarial agent that the defensive agent can be trained and evaluated against;
|
||||
- The ability to model key characteristics of a system by representing hosts, servers, network devices, IP addresses, ports, operating systems, folders / files, applications, services and links;
|
||||
- Modelling background (green) pattern-of-life;
|
||||
- Operates at machine-speed to enable fast training cycles via Reinforcement Learning (RL).
|
||||
- The ability to model key characteristics of a platform / system by representing connections, IP addresses, ports, operating systems, services and traffic loading on links;
|
||||
- Modelling background pattern-of-life;
|
||||
- Operates at machine-speed to enable fast training cycles.
|
||||
|
||||
Features
|
||||
^^^^^^^^
|
||||
|
||||
PrimAITE incorporates the following features:
|
||||
|
||||
- Architected with a separate Simulation layer and Game layer. This separation of concerns defines a clear path towards transfer learning with environments of differing fidelity;
|
||||
- Ability to reconfigure an RL reward function based on (a) the ability to counter the modelled adversarial cyber-attack, and (b) the ability to ensure success for green agents;
|
||||
- Access Control List (ACL) functions for network devices (routers and firewalls), following standard ACL rule format (e.g., DENY / ALLOW, source / destination IP addresses, protocol and port);
|
||||
- Application of traffic to the links of the system laydown adheres to the ACL rulesets and routing tables contained within each network device;
|
||||
- Provides RL environments adherent to the Farama Foundation Gymnasium (Previously OpenAI Gym) API, allowing integration with any compliant RL Agent frameworks;
|
||||
- Provides RL environments adherent to Ray RLlib environment specifications for single-agent and multi-agent scenarios;
|
||||
- Assessed for compatibility with Stable-Baselines3 (SB3), Ray RLlib, and bespoke agents;
|
||||
- Persona-based adversarial (Red) agent behaviour; several out-the-box personas are provided, and more can be developed to suit the needs of the task. Stochastic variations in Red agent behaviour are also included as required;
|
||||
- A robust system logging tool, automatically enabled at the node level and featuring various log levels and terminal output options, enables PrimAITE users to conduct in-depth network simulations;
|
||||
- A PCAP service is seamlessly integrated within the simulation, automatically capturing and logging frames for both
|
||||
inbound and outbound traffic at the network interface level. This automatic functionality, combined with the ability
|
||||
to separate traffic directions, significantly enhances network analysis and troubleshooting capabilities;
|
||||
- Agent action logs provide a description of every action taken by each agent during the episode. This includes timestep, action, parameters, request and response, for all Blue agent activity, which is aligned with the Track 2 Common Action / Observation Space (CAOS) format. Action logs also details of all scripted / stochastic red / green agent actions;
|
||||
- Environment ground truth is provided at every timestep, providing a full description of the environment’s true state;
|
||||
- Alignment with CAOS provides the ability to transfer agents between CAOS compliant environments.
|
||||
- Highly configurable (via YAML files) to provide the means to model a variety of platform / system laydowns, mission profiles and adversarial attack scenarios;
|
||||
- A Reinforcement Learning (RL) reward function based on (a) the ability to counter the modelled adversarial cyber-attack, and (b) the ability to ensure mission success;
|
||||
- Provision of logging to support AI performance / effectiveness assessment;
|
||||
- Uses the concept of Information Exchange Requirements (IERs) to model background pattern of life, adversarial behaviour and mission data (on a sliding scale of criticality);
|
||||
- An Access Control List (ACL) function, mimicking the behaviour of a network firewall, is applied across the model, following standard ACL rule format (e.g. DENY/ALLOW, source IP address, destination IP address, protocol and port);
|
||||
- Application of traffic to the links of the platform / system laydown adheres to the ACL ruleset;
|
||||
- Presents both an OpenAI gym and Ray RLLib interface to the environment, allowing integration with any compliant defensive agents;
|
||||
- Allows for the saving and loading of trained defensive agents;
|
||||
- Stochastic adversarial agent behaviour;
|
||||
- Full capture of discrete logs relating to agent training or evaluation (system state, agent actions taken, instantaneous and average reward for every step of every episode);
|
||||
- Distinct control over running a training and / or evaluation session;
|
||||
- NetworkX provides laydown visualisation capability.
|
||||
|
||||
Architecture
|
||||
^^^^^^^^^^^^
|
||||
|
||||
PrimAITE is a Python application and will operate on multiple Operating Systems (Windows, Linux and Mac);
|
||||
a comprehensive installation and user guide is provided with each release to support its usage.
|
||||
PrimAITE is a Python application and is therefore Operating System agnostic. The OpenAI gym and Ray RLLib frameworks are employed to provide an interface and source for AI agents. Configuration of PrimAITE is achieved via included YAML files which support full control over the platform / system laydown being modelled, background pattern of life, adversarial (red agent) behaviour, and step and episode count. NetworkX based nodes and links host Python classes to present attributes and methods, and hence a more representative platform / system can be modelled within the simulation.
|
||||
|
||||
Configuration of PrimAITE is achieved via included YAML files which support full control over the network / system laydown being modelled, background pattern of life, adversarial (red agent) behaviour, and step and episode count.
|
||||
A Simulation Controller layer manages the overall running of the simulation, keeping track of all low-level objects.
|
||||
|
||||
It is agnostic to the number of agents, their action / observation spaces, and the RL library being used.
|
||||
|
||||
It presents a public API providing a method for describing the current state of the simulation, a method that accepts action requests and provides responses, and a method that triggers a timestep advancement.
|
||||
The Game Layer converts the simulation into a playable game for the agent(s).
|
||||
|
||||
It translates between simulation state and Gymnasium.Spaces to pass action / observation data between the agent(s) and the simulation. It is responsible for calculating rewards, managing Multi-Agent RL (MARL) action turns, and via a single agent interface can interact with Blue, Red and Green agents.
|
||||
|
||||
Agents can either generate their own scripted behaviour or accept input behaviour from an RL agent.
|
||||
|
||||
Finally, a Gymnasium / Ray RLlib Environment Layer forwards requests to the Game Layer as the agent sends them. This layer also manages most of the I/O, such as reading in the configuration files and saving agent logs.
|
||||
|
||||
.. image:: ../../_static/primAITE_architecture.png
|
||||
:width: 500
|
||||
:align: center
|
||||
|
||||
|
||||
Training & Evaluation Capability
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
PrimAITE provides a training and evaluation capability to AI agents in the context of cyber-attack, via its Gymnasium / Ray RLlib compliant interface.
|
||||
PrimAITE provides a training and evaluation capability to AI agents in the context of cyber-attack, via its OpenAI Gym and RLLib compliant interface. Scenarios can be constructed to reflect platform / system laydowns consisting of any configuration of nodes (e.g. PCs, servers, switches etc.) and network links between them. All nodes can be configured to model services (and their status) and the traffic loading between them over the network links. Traffic loading is broken down into a per service granularity, relating directly to a protocol (e.g. Service A would be configured as a TCP service, and TCP traffic then flows between instances of Service A under the direction of a tailored IER). Highlights of PrimAITE’s training and evaluation capability are:
|
||||
|
||||
Scenarios can be constructed to reflect network / system laydowns consisting of any configuration of nodes (e.g., PCs, servers etc.) and the networking equipment and links between them.
|
||||
- The scenario is not bound to a representation of any platform, system, technology or mission;
|
||||
- Fully configurable (network / system laydown, IERs, node pattern-of-life, ACL, number of episodes, steps per episode) and repeatable to suit the requirements of AI agents;
|
||||
- Can integrate with any OpenAI Gym or RLLib compliant AI agent.
|
||||
|
||||
All nodes can be configured to contain applications, services, folders and files (and their status).
|
||||
Use of PrimAITE default scenarios within ARCD is supported by a “Use Case Profile” tailored to the scenario.
|
||||
|
||||
Traffic flows between services and applications as directed by an ‘execution definition,’ with the traffic flow on the network governed by the network equipment (switches, routers and firewalls) and the ACL rules and routing tables they employ.
|
||||
AI Assessment Capability
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Highlights of PrimAITE’s training and evaluation capability are:
|
||||
PrimAITE includes the capability to support in-depth assessment of cyber defence AI by outputting logs of the environment state and AI behaviour throughout both training and evaluation sessions. These logs include the following data:
|
||||
|
||||
- Timestamp;
|
||||
- Episode and step number;
|
||||
- Agent identifier;
|
||||
- Observation space;
|
||||
- Action taken (by defensive AI);
|
||||
- Reward value.
|
||||
|
||||
Logs are available in CSV format and provide coverage of the above data for every step of every episode.
|
||||
|
||||
- The scenario is not bound to a representation of any platform, system, or technology;
|
||||
- Fully configurable (network / system laydown, green pattern-of-life, red personas, reward function, ACL rules for each device, number of episodes / steps, action / observation space) and repeatable to suit the requirements of AI agents;
|
||||
- Can integrate with any Gymnasium / Ray RLlib compliant AI agent .
|
||||
|
||||
|
||||
PrimAITE provides a number of use cases (network and red/green action configurations) by default which the user is able to extend and modify as required.
|
||||
|
||||
What is PrimAITE built with
|
||||
---------------------------
|
||||
|
||||
* `Gymnasium <https://gymnasium.farama.org/>`_ is used as the basis for AI blue agent interaction with the PrimAITE environment
|
||||
* `OpenAI's Gym <https://gym.openai.com/>`_ is used as the basis for AI blue agent interaction with the PrimAITE environment
|
||||
* `Networkx <https://github.com/networkx/networkx>`_ is used as the underlying data structure used for the PrimAITE environment
|
||||
* `Stable Baselines 3 <https://github.com/DLR-RM/stable-baselines3>`_ is used as a default source of RL algorithms (although PrimAITE is not limited to SB3 agents)
|
||||
* `Ray RLlib <https://github.com/ray-project/ray>`_ is used as an additional source of RL algorithms
|
||||
@@ -105,38 +92,28 @@ Head over to the :ref:`getting-started` page to install and setup PrimAITE!
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 8
|
||||
:caption: About PrimAITE:
|
||||
:hidden:
|
||||
|
||||
source/about
|
||||
source/dependencies
|
||||
source/glossary
|
||||
|
||||
.. toctree::
|
||||
:caption: Usage:
|
||||
:caption: Contents:
|
||||
:hidden:
|
||||
|
||||
source/getting_started
|
||||
source/simulation
|
||||
source/game_layer
|
||||
source/about
|
||||
source/config
|
||||
source/environment
|
||||
source/customising_scenarios
|
||||
source/varying_config_files
|
||||
|
||||
.. toctree::
|
||||
:caption: Notebooks:
|
||||
:hidden:
|
||||
|
||||
source/example_notebooks
|
||||
source/notebooks/executed_notebooks
|
||||
|
||||
.. toctree::
|
||||
:caption: Developer information:
|
||||
:hidden:
|
||||
|
||||
source/developer_tools
|
||||
source/state_system
|
||||
source/request_system
|
||||
source/primaite_session
|
||||
source/custom_agent
|
||||
PrimAITE API <source/_autosummary/primaite>
|
||||
PrimAITE Tests <source/_autosummary/tests>
|
||||
source/dependencies
|
||||
source/glossary
|
||||
source/migration_1.2_-_2.0
|
||||
|
||||
|
||||
.. TODO: Add project links once public repo has been created
|
||||
|
||||
.. toctree::
|
||||
:caption: Project Links:
|
||||
:hidden:
|
||||
|
||||
Code <https://github.com/Autonomous-Resilient-Cyber-Defence/PrimAITE>
|
||||
Issues <https://github.com/Autonomous-Resilient-Cyber-Defence/PrimAITE/issues>
|
||||
Pull Requests <https://github.com/Autonomous-Resilient-Cyber-Defence/PrimAITE/pulls>
|
||||
Discussions <https://github.com/Autonomous-Resilient-Cyber-Defence/PrimAITE/discussions>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
@ECHO OFF
|
||||
REM © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
@@ -37,6 +36,11 @@ IF EXIST %AUTOSUMMARYDIR% (
|
||||
RMDIR %AUTOSUMMARYDIR% /s /q
|
||||
)
|
||||
|
||||
REM print the YT licenses
|
||||
set LICENSEBUILD=pip-licenses --format=rst --with-urls
|
||||
set DEPS="%cd%\source\primaite-dependencies.rst"
|
||||
|
||||
%LICENSEBUILD% --output-file=%DEPS%
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
@@ -1,317 +1,414 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
© Crown-owned copyright 2023, Defence Science and Technology Laboratory UK
|
||||
|
||||
.. _about:
|
||||
|
||||
About PrimAITE
|
||||
==============
|
||||
|
||||
PrimAITE is a simulation environment for training agents to protect a computer network from cyber attacks.
|
||||
|
||||
Features
|
||||
********
|
||||
|
||||
PrimAITE provides the following features:
|
||||
|
||||
* A flexible system for defining network layouts and host configurations
|
||||
* Highly configurable network hosts, including definition of software, file system, and network interfaces,
|
||||
* Realistic network traffic simulation, including address and sending packets via internet protocols like TCP, UDP, ICMP, etc.
|
||||
* Routers with traffic routing and firewall capabilities
|
||||
* Simulation of customisable deterministic agents
|
||||
* Support for multiple agents, each having their own customisable observation space, action space, and reward function definition.
|
||||
* A flexible network / system laydown based on the Python networkx framework
|
||||
* Nodes and links (edges) host Python classes in order to present attributes and methods (and hence, a more representative model of a platform / system)
|
||||
* A 'green agent' Information Exchange Requirement (IER) function allows the representation of traffic (protocols and loading) on any / all links. Application of IERs is based on the status of node operating systems and services
|
||||
* A 'green agent' node Pattern-of-Life (PoL) function allows the representation of core behaviours on nodes (e.g. changing the Hardware state, Software State, Service state, or File System state)
|
||||
* An Access Control List (ACL) function, mimicking the behaviour of a network firewall, is applied across the model, following standard ACL rule format (e.g. DENY/ALLOW, source IP, destination IP, protocol and port). Application of IERs adheres to any ACL restrictions
|
||||
* Presents an OpenAI Gym interface to the environment, allowing integration with any OpenAI Gym compliant defensive agents
|
||||
* Red agent activity based on 'red' IERs and 'red' PoL
|
||||
* Defined reward function for use with RL agents (based on nodes status, and green / red IER success)
|
||||
* Fully configurable (network / system laydown, IERs, node PoL, ACL, episode step period, episode max steps) and repeatable to suit the training requirements of agents. Therefore, not bound to a representation of any particular platform, system or technology
|
||||
* Full capture of discrete metrics relating to agent training (full system state, agent actions taken, average reward)
|
||||
* Networkx provides laydown visualisation capability
|
||||
|
||||
Architecture - Nodes and Links
|
||||
******************************
|
||||
|
||||
**Nodes**
|
||||
|
||||
An inheritance model has been adopted in order to model nodes. All nodes have the following base attributes (Class: Node):
|
||||
|
||||
* ID
|
||||
* Name
|
||||
* Type (e.g. computer, switch, RTU - enumeration)
|
||||
* Priority (P1, P2, P3, P4 or P5 - enumeration)
|
||||
* Hardware State (ON, OFF, RESETTING, SHUTTING_DOWN, BOOTING - enumeration)
|
||||
|
||||
Active Nodes also have the following attributes (Class: Active Node):
|
||||
|
||||
* IP Address
|
||||
* Software State (GOOD, PATCHING, COMPROMISED - enumeration)
|
||||
* File System State (GOOD, CORRUPT, DESTROYED, REPAIRING, RESTORING - enumeration)
|
||||
|
||||
Service Nodes also have the following attributes (Class: Service Node):
|
||||
|
||||
* List of Services (where service is composed of service name and port). There is no theoretical limit on the number of services that can be modelled. Services and protocols are currently intrinsically linked (i.e. a service is an application on a node transmitting traffic of this protocol type)
|
||||
* Service state (GOOD, PATCHING, COMPROMISED, OVERWHELMED - enumeration)
|
||||
|
||||
Passive Nodes are currently not used (but may be employed for non IP-based components such as machinery actuators in future releases).
|
||||
|
||||
**Links**
|
||||
|
||||
Links are modelled both as network edges (networkx) and as Python classes, in order to extend their functionality. Links include the following attributes:
|
||||
|
||||
* ID
|
||||
* Name
|
||||
* Bandwidth (bits/s)
|
||||
* Source node ID
|
||||
* Destination node ID
|
||||
* Protocol list (containing the loading of protocols currently running on the link)
|
||||
|
||||
When the simulation runs, IERs are applied to the links in order to model traffic loading, individually assigned to each protocol. This allows green (background) and red agent behaviour to be modelled, and defensive agents to identify suspicious traffic patterns at a protocol / traffic loading level of fidelity.
|
||||
|
||||
Information Exchange Requirements (IERs)
|
||||
****************************************
|
||||
|
||||
PrimAITE adopts the concept of Information Exchange Requirements (IERs) to model both green agent (background) and red agent (adversary) behaviour. IERs are used to initiate modelling of traffic loading on the network, and have the following attributes:
|
||||
|
||||
* ID
|
||||
* Start step (i.e. which step in the training episode should the IER start)
|
||||
* End step (i.e. which step in the training episode should the IER end)
|
||||
* Source node ID
|
||||
* Destination node ID
|
||||
* Load (bits/s)
|
||||
* Protocol
|
||||
* Port
|
||||
* Running status (i.e. on / off)
|
||||
|
||||
The application of green agent IERs between a source and destination follows a number of rules. Specifically:
|
||||
|
||||
1. Does the current simulation time step fall between IER start and end step
|
||||
2. Is the source node operational (both physically and at an O/S level), and is the service (protocol / port) associated with the IER (a) present on this node, and (b) in an operational state (i.e. not PATCHING)
|
||||
3. Is the destination node operational (both physically and at an O/S level), and is the service (protocol / port) associated with the IER (a) present on this node, and (b) in an operational state (i.e. not PATCHING)
|
||||
4. Are there any Access Control List rules in place that prevent the application of this IER
|
||||
5. Are all switches in the (OSPF) path between source and destination operational (both physically and at an O/S level)
|
||||
|
||||
For red agent IERs, the application of IERs between a source and destination follows a number of subtly different rules. Specifically:
|
||||
|
||||
1. Does the current simulation time step fall between IER start and end step
|
||||
2. Is the source node operational, and is the service (protocol / port) associated with the IER (a) present on that node and (b) already in a compromised state
|
||||
3. Is the destination node operational, and is the service (protocol / port) associated with the IER present on that node
|
||||
4. Are there any Access Control List rules in place that prevent the application of this IER
|
||||
5. Are all switches in the (OSPF) path between source and destination operational (both physically and at an O/S level)
|
||||
|
||||
Assuming the rules pass, the IER is applied to all relevant links (based on use of OSPF) between source and destination.
|
||||
|
||||
Node Pattern-of-Life
|
||||
********************
|
||||
|
||||
Every node can be impacted (i.e. have a status change applied to it) by either green agent pattern-of-life or red agent pattern-of-life. This is distinct from IERs, and allows for attacks (and defence) to be modelled purely within the confines of a node.
|
||||
|
||||
The status changes that can be made to a node are as follows:
|
||||
|
||||
* All Nodes:
|
||||
|
||||
* Hardware State:
|
||||
|
||||
* ON
|
||||
* OFF
|
||||
* RESETTING - when a status of resetting is entered, the node will automatically exit this state after a number of steps (as defined by the nodeResetDuration configuration item) after which it returns to an ON state
|
||||
* BOOTING
|
||||
* SHUTTING_DOWN
|
||||
|
||||
* Active Nodes and Service Nodes:
|
||||
|
||||
* Software State:
|
||||
|
||||
* GOOD
|
||||
* PATCHING - when a status of patching is entered, the node will automatically exit this state after a number of steps (as defined by the osPatchingDuration configuration item) after which it returns to a GOOD state
|
||||
* COMPROMISED
|
||||
|
||||
* File System State:
|
||||
|
||||
* GOOD
|
||||
* CORRUPT (can be resolved by repair or restore)
|
||||
* DESTROYED (can be resolved by restore only)
|
||||
* REPAIRING - when a status of repairing is entered, the node will automatically exit this state after a number of steps (as defined by the fileSystemRepairingLimit configuration item) after which it returns to a GOOD state
|
||||
* RESTORING - when a status of repairing is entered, the node will automatically exit this state after a number of steps (as defined by the fileSystemRestoringLimit configuration item) after which it returns to a GOOD state
|
||||
|
||||
* Service Nodes only:
|
||||
|
||||
* Service State (for any associated service):
|
||||
|
||||
* GOOD
|
||||
* PATCHING - when a status of patching is entered, the service will automatically exit this state after a number of steps (as defined by the servicePatchingDuration configuration item) after which it returns to a GOOD state
|
||||
* COMPROMISED
|
||||
* OVERWHELMED
|
||||
|
||||
Red agent pattern-of-life has an additional feature not found in the green pattern-of-life. This is the ability to influence the state of the attributes of a node via a number of different conditions:
|
||||
|
||||
* DIRECT:
|
||||
|
||||
The pattern-of-life described by the configuration file item will be applied regardless of any other conditions in the network. This is particularly useful for direct red agent entry into the network.
|
||||
|
||||
* IER:
|
||||
|
||||
The pattern-of-life described by the configuration file item will be applied to the service on the node, only if there is an IER of the same protocol / service type incoming at the specified timestep.
|
||||
|
||||
* SERVICE:
|
||||
|
||||
The pattern-of-life described by the configuration file item will be applied to the node based on the state of a service. The service can either be on the same node, or a different node within the network.
|
||||
|
||||
Access Control List modelling
|
||||
*****************************
|
||||
|
||||
An Access Control List (ACL) is modelled to provide the means to manage traffic flows in the system. This will allow defensive agents the means to turn on / off rules, or potentially create new rules, to counter an attack.
|
||||
|
||||
The ACL follows a standard network firewall format. For example:
|
||||
|
||||
.. list-table:: ACL example
|
||||
:widths: 25 25 25 25 25
|
||||
:header-rows: 1
|
||||
|
||||
* - Permission
|
||||
- Source IP
|
||||
- Dest IP
|
||||
- Protocol
|
||||
- Port
|
||||
* - DENY
|
||||
- 192.168.1.2
|
||||
- 192.168.1.3
|
||||
- HTTPS
|
||||
- 443
|
||||
* - ALLOW
|
||||
- 192.168.1.4
|
||||
- ANY
|
||||
- SMTP
|
||||
- 25
|
||||
* - DENY
|
||||
- ANY
|
||||
- 192.168.1.5
|
||||
- ANY
|
||||
- ANY
|
||||
|
||||
All ACL rules are considered when applying an IER. Logic follows the order of rules, so a DENY or ALLOW for the same parameters will override an earlier entry.
|
||||
|
||||
Observation Spaces
|
||||
******************
|
||||
The observation space provides the blue agent with information about the current status of nodes and links.
|
||||
|
||||
PrimAITE builds on top of Gym Spaces to create an observation space that is easily configurable for users. It's made up of components which are managed by the :py:class:`primaite.environment.observations.ObservationsHandler`. Each training scenario can define its own observation space, and the user can choose which information to inlude, and how it should be formatted.
|
||||
|
||||
NodeLinkTable component
|
||||
-----------------------
|
||||
For example, the :py:class:`primaite.environment.observations.NodeLinkTable` component represents the status of nodes and links as a ``gym.spaces.Box`` with an example format shown below:
|
||||
|
||||
An example observation space is provided below:
|
||||
|
||||
.. list-table:: Observation Space example
|
||||
:widths: 25 25 25 25 25 25 25
|
||||
:header-rows: 1
|
||||
|
||||
* -
|
||||
- ID
|
||||
- Hardware State
|
||||
- Software State
|
||||
- File System State
|
||||
- Service / Protocol A
|
||||
- Service / Protocol B
|
||||
* - Node A
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
* - Node B
|
||||
- 2
|
||||
- 1
|
||||
- 3
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
* - Node C
|
||||
- 3
|
||||
- 2
|
||||
- 1
|
||||
- 1
|
||||
- 3
|
||||
- 2
|
||||
* - Link 1
|
||||
- 5
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 10000
|
||||
* - Link 2
|
||||
- 6
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 10000
|
||||
* - Link 3
|
||||
- 7
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 5000
|
||||
- 0
|
||||
|
||||
For the nodes, the following values are represented:
|
||||
|
||||
.. code-block::
|
||||
|
||||
[
|
||||
ID
|
||||
Hardware State (1=ON, 2=OFF, 3=RESETTING, 4=SHUTTING_DOWN, 5=BOOTING)
|
||||
Operating System State (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED)
|
||||
File System State (0=none, 1=GOOD, 2=CORRUPT, 3=DESTROYED, 4=REPAIRING, 5=RESTORING)
|
||||
Service1/Protocol1 state (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED)
|
||||
Service2/Protocol2 state (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED)
|
||||
]
|
||||
|
||||
(Note that each service available in the network is provided as a column, although not all nodes may utilise all services)
|
||||
|
||||
For the links, the following statuses are represented:
|
||||
|
||||
.. code-block::
|
||||
|
||||
[
|
||||
ID
|
||||
Hardware State (0=not applicable)
|
||||
Operating System State (0=not applicable)
|
||||
File System State (0=not applicable)
|
||||
Service1/Protocol1 state (Traffic load from this protocol on this link)
|
||||
Service2/Protocol2 state (Traffic load from this protocol on this link)
|
||||
]
|
||||
|
||||
NodeStatus component
|
||||
----------------------
|
||||
This is a MultiDiscrete observation space that can be though of as a one-dimensional vector of discrete states.
|
||||
The example above would have the following structure:
|
||||
|
||||
.. code-block::
|
||||
|
||||
[
|
||||
node1_info
|
||||
node2_info
|
||||
node3_info
|
||||
]
|
||||
|
||||
Each ``node_info`` contains the following:
|
||||
|
||||
.. code-block::
|
||||
|
||||
[
|
||||
hardware_state (0=none, 1=ON, 2=OFF, 3=RESETTING, 4=SHUTTING_DOWN, 5=BOOTING)
|
||||
software_state (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED)
|
||||
file_system_state (0=none, 1=GOOD, 2=CORRUPT, 3=DESTROYED, 4=REPAIRING, 5=RESTORING)
|
||||
service1_state (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED)
|
||||
service2_state (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED)
|
||||
]
|
||||
|
||||
In a network with three nodes and two services, the full observation space would have 15 elements. It can be written with ``gym`` notation to indicate the number of discrete options for each of the elements of the observation space. For example:
|
||||
|
||||
.. code-block::
|
||||
|
||||
gym.spaces.MultiDiscrete([4,5,6,4,4,4,5,6,4,4,4,5,6,4,4])
|
||||
|
||||
.. note::
|
||||
NodeStatus observation component provides information only about nodes. Links are not considered.
|
||||
|
||||
LinkTrafficLevels
|
||||
-----------------
|
||||
This component is a MultiDiscrete space showing the traffic flow levels on the links in the network, after applying a threshold to convert it from a continuous to a discrete value.
|
||||
There are two configurable parameters:
|
||||
* ``quantisation_levels`` determines how many discrete bins to use for converting the continuous traffic value to discrete (default is 5).
|
||||
* ``combine_service_traffic`` determines whether to separately output traffic use for each network protocol or whether to combine them into an overall value for the link. (default is ``True``)
|
||||
|
||||
For example, with default parameters and a network with three links, the structure of this component would be:
|
||||
|
||||
.. code-block::
|
||||
|
||||
[
|
||||
link1_status
|
||||
link2_status
|
||||
link3_status
|
||||
]
|
||||
|
||||
Each ``link_status`` is a number from 0-4 representing the network load in relation to bandwidth.
|
||||
|
||||
.. code-block::
|
||||
|
||||
0 = No traffic (0%)
|
||||
1 = low traffic (1%-33%)
|
||||
2 = medium traffic (33%-66%)
|
||||
3 = high traffic (66%-99%)
|
||||
4 = max traffic/ overwhelmed (100%)
|
||||
|
||||
Using ``gym`` notation, the shape of the obs space is: ``gym.spaces.MultiDiscrete([5,5,5])``.
|
||||
|
||||
|
||||
Structure
|
||||
*********
|
||||
Action Spaces
|
||||
**************
|
||||
|
||||
PrimAITE consists of a simulator and a 'game' layer that allows agents to interact with the simulator. The simulator is built in a modular way where each component such as network hosts, links, networking devices, softwares, etc. are implemented as instances of a base class, meaning they all support the same interface. This allows for standardised configuration using either the Python API or YAML files.
|
||||
The game layer is built on top of the simulator and it consumes the simulation action/state interface to allow agents to interact with the simulator. The game layer is also responsible for defining the reward function and observation space for the agents.
|
||||
The action space available to the blue agent comes in two types:
|
||||
|
||||
1. Node-based
|
||||
2. Access Control List
|
||||
3. Any (Agent can take both node-based and ACL-based actions)
|
||||
|
||||
..
|
||||
Architecture - Nodes and Links
|
||||
******************************
|
||||
**Nodes**
|
||||
An inheritance model has been adopted in order to model nodes. All nodes have the following base attributes (Class: Node):
|
||||
* ID
|
||||
* Name
|
||||
* Type (e.g. computer, switch, RTU - enumeration)
|
||||
* Priority (P1, P2, P3, P4 or P5 - enumeration)
|
||||
* Hardware State (ON, OFF, RESETTING, SHUTTING_DOWN, BOOTING - enumeration)
|
||||
Active Nodes also have the following attributes (Class: Active Node):
|
||||
* IP Address
|
||||
* Software State (GOOD, FIXING, COMPROMISED - enumeration)
|
||||
* File System State (GOOD, CORRUPT, DESTROYED, REPAIRING, RESTORING - enumeration)
|
||||
Service Nodes also have the following attributes (Class: Service Node):
|
||||
* List of Services (where service is composed of service name and port). There is no theoretical limit on the number of services that can be modelled. Services and protocols are currently intrinsically linked (i.e. a service is an application on a node transmitting traffic of this protocol type)
|
||||
* Service state (GOOD, FIXING, COMPROMISED, OVERWHELMED - enumeration)
|
||||
Passive Nodes are currently not used (but may be employed for non IP-based components such as machinery actuators in future releases).
|
||||
**Links**
|
||||
Links are modelled both as network edges (networkx) and as Python classes, in order to extend their functionality. Links include the following attributes:
|
||||
* ID
|
||||
* Name
|
||||
* Bandwidth (bits/s)
|
||||
* Source node ID
|
||||
* Destination node ID
|
||||
* Protocol list (containing the loading of protocols currently running on the link)
|
||||
When the simulation runs, IERs are applied to the links in order to model traffic loading, individually assigned to each protocol. This allows green (background) and red agent behaviour to be modelled, and defensive agents to identify suspicious traffic patterns at a protocol / traffic loading level of fidelity.
|
||||
Information Exchange Requirements (IERs)
|
||||
****************************************
|
||||
PrimAITE adopts the concept of Information Exchange Requirements (IERs) to model both green agent (background) and red agent (adversary) behaviour. IERs are used to initiate modelling of traffic loading on the network, and have the following attributes:
|
||||
* ID
|
||||
* Start step (i.e. which step in the training episode should the IER start)
|
||||
* End step (i.e. which step in the training episode should the IER end)
|
||||
* Source node ID
|
||||
* Destination node ID
|
||||
* Load (bits/s)
|
||||
* Protocol
|
||||
* Port
|
||||
* Running status (i.e. on / off)
|
||||
The application of green agent IERs between a source and destination follows a number of rules. Specifically:
|
||||
1. Does the current simulation time step fall between IER start and end step
|
||||
2. Is the source node operational (both physically and at an O/S level), and is the service (protocol / port) associated with the IER (a) present on this node, and (b) in an operational state (i.e. not FIXING)
|
||||
3. Is the destination node operational (both physically and at an O/S level), and is the service (protocol / port) associated with the IER (a) present on this node, and (b) in an operational state (i.e. not FIXING)
|
||||
4. Are there any Access Control List rules in place that prevent the application of this IER
|
||||
5. Are all switches in the (OSPF) path between source and destination operational (both physically and at an O/S level)
|
||||
For red agent IERs, the application of IERs between a source and destination follows a number of subtly different rules. Specifically:
|
||||
1. Does the current simulation time step fall between IER start and end step
|
||||
2. Is the source node operational, and is the service (protocol / port) associated with the IER (a) present on that node and (b) already in a compromised state
|
||||
3. Is the destination node operational, and is the service (protocol / port) associated with the IER present on that node
|
||||
4. Are there any Access Control List rules in place that prevent the application of this IER
|
||||
5. Are all switches in the (OSPF) path between source and destination operational (both physically and at an O/S level)
|
||||
Assuming the rules pass, the IER is applied to all relevant links (based on use of OSPF) between source and destination.
|
||||
Node Pattern-of-Life
|
||||
********************
|
||||
Every node can be impacted (i.e. have a status change applied to it) by either green agent pattern-of-life or red agent pattern-of-life. This is distinct from IERs, and allows for attacks (and defence) to be modelled purely within the confines of a node.
|
||||
The status changes that can be made to a node are as follows:
|
||||
* All Nodes:
|
||||
* Hardware State:
|
||||
* ON
|
||||
* OFF
|
||||
* RESETTING - when a status of resetting is entered, the node will automatically exit this state after a number of steps (as defined by the nodeResetDuration configuration item) after which it returns to an ON state
|
||||
* BOOTING
|
||||
* SHUTTING_DOWN
|
||||
* Active Nodes and Service Nodes:
|
||||
* Software State:
|
||||
* GOOD
|
||||
* FIXING - when a status of FIXING is entered, the node will automatically exit this state after a number of steps (as defined by the osFIXINGDuration configuration item) after which it returns to a GOOD state
|
||||
* COMPROMISED
|
||||
* File System State:
|
||||
* GOOD
|
||||
* CORRUPT (can be resolved by repair or restore)
|
||||
* DESTROYED (can be resolved by restore only)
|
||||
* REPAIRING - when a status of repairing is entered, the node will automatically exit this state after a number of steps (as defined by the fileSystemRepairingLimit configuration item) after which it returns to a GOOD state
|
||||
* RESTORING - when a status of repairing is entered, the node will automatically exit this state after a number of steps (as defined by the fileSystemRestoringLimit configuration item) after which it returns to a GOOD state
|
||||
* Service Nodes only:
|
||||
* Service State (for any associated service):
|
||||
* GOOD
|
||||
* FIXING - when a status of FIXING is entered, the service will automatically exit this state after a number of steps (as defined by the serviceFIXINGDuration configuration item) after which it returns to a GOOD state
|
||||
* COMPROMISED
|
||||
* OVERWHELMED
|
||||
Red agent pattern-of-life has an additional feature not found in the green pattern-of-life. This is the ability to influence the state of the attributes of a node via a number of different conditions:
|
||||
* DIRECT:
|
||||
The pattern-of-life described by the configuration file item will be applied regardless of any other conditions in the network. This is particularly useful for direct red agent entry into the network.
|
||||
* IER:
|
||||
The pattern-of-life described by the configuration file item will be applied to the service on the node, only if there is an IER of the same protocol / service type incoming at the specified timestep.
|
||||
* SERVICE:
|
||||
The pattern-of-life described by the configuration file item will be applied to the node based on the state of a service. The service can either be on the same node, or a different node within the network.
|
||||
Access Control List modelling
|
||||
*****************************
|
||||
An Access Control List (ACL) is modelled to provide the means to manage traffic flows in the system. This will allow defensive agents the means to turn on / off rules, or potentially create new rules, to counter an attack.
|
||||
The ACL follows a standard network firewall format. For example:
|
||||
.. list-table:: ACL example
|
||||
:widths: 25 25 25 25 25
|
||||
:header-rows: 1
|
||||
* - Permission
|
||||
- Source IP
|
||||
- Dest IP
|
||||
- Protocol
|
||||
- Port
|
||||
* - DENY
|
||||
- 192.168.1.2
|
||||
- 192.168.1.3
|
||||
- HTTPS
|
||||
- 443
|
||||
* - ALLOW
|
||||
- 192.168.1.4
|
||||
- ANY
|
||||
- SMTP
|
||||
- 25
|
||||
* - DENY
|
||||
- ANY
|
||||
- 192.168.1.5
|
||||
- ANY
|
||||
- ANY
|
||||
All ACL rules are considered when applying an IER. Logic follows the order of rules, so a DENY or ALLOW for the same parameters will override an earlier entry.
|
||||
Observation Spaces
|
||||
******************
|
||||
The observation space provides the blue agent with information about the current status of nodes and links.
|
||||
PrimAITE builds on top of Gymnasium Spaces to create an observation space that is easily configurable for users. It's made up of components which are managed by the :py:class:`primaite.environment.observations.ObservationsHandler`. Each training scenario can define its own observation space, and the user can choose which information to inlude, and how it should be formatted.
|
||||
NodeLinkTable component
|
||||
-----------------------
|
||||
For example, the :py:class:`primaite.environment.observations.NodeLinkTable` component represents the status of nodes and links as a ``gym.spaces.Box`` with an example format shown below:
|
||||
An example observation space is provided below:
|
||||
.. list-table:: Observation Space example
|
||||
:widths: 25 25 25 25 25 25 25
|
||||
:header-rows: 1
|
||||
* -
|
||||
- ID
|
||||
- Hardware State
|
||||
- Software State
|
||||
- File System State
|
||||
- Service / Protocol A
|
||||
- Service / Protocol B
|
||||
* - Node A
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
* - Node B
|
||||
- 2
|
||||
- 1
|
||||
- 3
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
* - Node C
|
||||
- 3
|
||||
- 2
|
||||
- 1
|
||||
- 1
|
||||
- 3
|
||||
- 2
|
||||
* - Link 1
|
||||
- 5
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 10000
|
||||
* - Link 2
|
||||
- 6
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 10000
|
||||
* - Link 3
|
||||
- 7
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 5000
|
||||
- 0
|
||||
For the nodes, the following values are represented:
|
||||
.. code-block::
|
||||
[
|
||||
ID
|
||||
Hardware State (1=ON, 2=OFF, 3=RESETTING, 4=SHUTTING_DOWN, 5=BOOTING)
|
||||
Operating System State (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED)
|
||||
File System State (0=none, 1=GOOD, 2=CORRUPT, 3=DESTROYED, 4=REPAIRING, 5=RESTORING)
|
||||
Service1/Protocol1 state (0=none, 1=GOOD, 2=FIXING, 3=COMPROMISED)
|
||||
Service2/Protocol2 state (0=none, 1=GOOD, 2=FIXING, 3=COMPROMISED)
|
||||
]
|
||||
(Note that each service available in the network is provided as a column, although not all nodes may utilise all services)
|
||||
For the links, the following statuses are represented:
|
||||
.. code-block::
|
||||
[
|
||||
ID
|
||||
Hardware State (0=not applicable)
|
||||
Operating System State (0=not applicable)
|
||||
File System State (0=not applicable)
|
||||
Service1/Protocol1 state (Traffic load from this protocol on this link)
|
||||
Service2/Protocol2 state (Traffic load from this protocol on this link)
|
||||
]
|
||||
NodeStatus component
|
||||
----------------------
|
||||
This is a MultiDiscrete observation space that can be though of as a one-dimensional vector of discrete states.
|
||||
The example above would have the following structure:
|
||||
.. code-block::
|
||||
[
|
||||
node1_info
|
||||
node2_info
|
||||
node3_info
|
||||
]
|
||||
Each ``node_info`` contains the following:
|
||||
.. code-block::
|
||||
[
|
||||
hardware_state (0=none, 1=ON, 2=OFF, 3=RESETTING, 4=SHUTTING_DOWN, 5=BOOTING)
|
||||
software_state (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED)
|
||||
file_system_state (0=none, 1=GOOD, 2=CORRUPT, 3=DESTROYED, 4=REPAIRING, 5=RESTORING)
|
||||
service1_state (0=none, 1=GOOD, 2=FIXING, 3=COMPROMISED)
|
||||
service2_state (0=none, 1=GOOD, 2=FIXING, 3=COMPROMISED)
|
||||
]
|
||||
In a network with three nodes and two services, the full observation space would have 15 elements. It can be written with ``gym`` notation to indicate the number of discrete options for each of the elements of the observation space. For example:
|
||||
.. code-block::
|
||||
gym.spaces.MultiDiscrete([4,5,6,4,4,4,5,6,4,4,4,5,6,4,4])
|
||||
.. note::
|
||||
NodeStatus observation component provides information only about nodes. Links are not considered.
|
||||
LinkTrafficLevels
|
||||
-----------------
|
||||
This component is a MultiDiscrete space showing the traffic flow levels on the links in the network, after applying a threshold to convert it from a continuous to a discrete value.
|
||||
There are two configurable parameters:
|
||||
* ``quantisation_levels`` determines how many discrete bins to use for converting the continuous traffic value to discrete (default is 5).
|
||||
* ``combine_service_traffic`` determines whether to separately output traffic use for each network protocol or whether to combine them into an overall value for the link. (default is ``True``)
|
||||
For example, with default parameters and a network with three links, the structure of this component would be:
|
||||
.. code-block::
|
||||
[
|
||||
link1_status
|
||||
link2_status
|
||||
link3_status
|
||||
]
|
||||
Each ``link_status`` is a number from 0-4 representing the network load in relation to bandwidth.
|
||||
.. code-block::
|
||||
0 = No traffic (0%)
|
||||
1 = low traffic (1%-33%)
|
||||
2 = medium traffic (33%-66%)
|
||||
3 = high traffic (66%-99%)
|
||||
4 = max traffic/ overwhelmed (100%)
|
||||
Using ``gym`` notation, the shape of the obs space is: ``gym.spaces.MultiDiscrete([5,5,5])``.
|
||||
Action Spaces
|
||||
**************
|
||||
The action space available to the blue agent comes in two types:
|
||||
1. Node-based
|
||||
2. Access Control List
|
||||
3. Any (Agent can take both node-based and ACL-based actions)
|
||||
The choice of action space used during a training session is determined in the config_[name].yaml file.
|
||||
**Node-Based**
|
||||
The agent is able to influence the status of nodes by switching them off, resetting, or FIXING operating systems and services. In this instance, the action space is a Gymnasium spaces.Discrete type, as follows:
|
||||
* Dictionary item {... ,1: [x1, x2, x3,x4] ...}
|
||||
The placeholders inside the list under the key '1' mean the following:
|
||||
* [0, num nodes] - Node ID (0 = nothing, node ID)
|
||||
* [0, 4] - What property it's acting on (0 = nothing, 1 = state, 2 = SoftwareState, 3 = service state, 4 = file system state)
|
||||
* [0, 3] - Action on property (0 = nothing, 1 = on / scan, 2 = off / repair, 3 = reset / patch / restore)
|
||||
* [0, num services] - Resolves to service ID (0 = nothing, resolves to service)
|
||||
**Access Control List**
|
||||
The blue agent is able to influence the configuration of the Access Control List rule set (which implements a system-wide firewall). In this instance, the action space is an Gymnasium spaces.Discrete type, as follows:
|
||||
* Dictionary item {... ,1: [x1, x2, x3, x4, x5, x6] ...}
|
||||
The placeholders inside the list under the key '1' mean the following:
|
||||
* [0, 2] - Action (0 = do nothing, 1 = create rule, 2 = delete rule)
|
||||
* [0, 1] - Permission (0 = DENY, 1 = ALLOW)
|
||||
* [0, num nodes] - Source IP (0 = any, then 1 -> x resolving to IP addresses)
|
||||
* [0, num nodes] - Dest IP (0 = any, then 1 -> x resolving to IP addresses)
|
||||
* [0, num services] - Protocol (0 = any, then 1 -> x resolving to protocol)
|
||||
* [0, num ports] - Port (0 = any, then 1 -> x resolving to port)
|
||||
**ANY**
|
||||
The agent is able to carry out both **Node-Based** and **Access Control List** operations.
|
||||
This means the dictionary will contain key-value pairs in the format of BOTH Node-Based and Access Control List as seen above.
|
||||
Rewards
|
||||
*******
|
||||
A reward value is presented back to the blue agent on the conclusion of every step. The reward value is calculated via two methods which combine to give the total value:
|
||||
1. Node and service status
|
||||
2. IER status
|
||||
**Node and service status**
|
||||
On every step, the status of each node is compared against both a reference environment (simulating the situation if the red and blue agents had not impacted the environment)
|
||||
and the before and after state of the environment. If the comparison against the reference environment shows no difference, then the score provided is "AllOK". If there is a
|
||||
difference with respect to the reference environment, the before and after states are compared, and a score determined. See :ref:`config` for details of reward values.
|
||||
**IER status**
|
||||
On every step, the full IER set is examined to determine whether green and red agent IERs are being permitted to run. Any red agent IERs running incur a penalty; any green agent
|
||||
IERs not permitted to run also incur a penalty. See :ref:`config` for details of reward values.
|
||||
Future Enhancements
|
||||
*******************
|
||||
The PrimAITE project has an ambition to include the following enhancements in future releases:
|
||||
* Integration with a suitable standardised framework to allow multi-agent integration
|
||||
* Integration with external threat emulation tools, either using off-line data, or integrating at runtime
|
||||
The choice of action space used during a training session is determined in the config_[name].yaml file.
|
||||
|
||||
**Node-Based**
|
||||
|
||||
The agent is able to influence the status of nodes by switching them off, resetting, or patching operating systems and services. In this instance, the action space is an OpenAI Gym spaces.Discrete type, as follows:
|
||||
|
||||
* Dictionary item {... ,1: [x1, x2, x3,x4] ...}
|
||||
The placeholders inside the list under the key '1' mean the following:
|
||||
|
||||
* [0, num nodes] - Node ID (0 = nothing, node ID)
|
||||
* [0, 4] - What property it's acting on (0 = nothing, 1 = state, 2 = SoftwareState, 3 = service state, 4 = file system state)
|
||||
* [0, 3] - Action on property (0 = nothing, 1 = on / scan, 2 = off / repair, 3 = reset / patch / restore)
|
||||
* [0, num services] - Resolves to service ID (0 = nothing, resolves to service)
|
||||
|
||||
**Access Control List**
|
||||
|
||||
The blue agent is able to influence the configuration of the Access Control List rule set (which implements a system-wide firewall). In this instance, the action space is an OpenAI spaces.Discrete type, as follows:
|
||||
|
||||
* Dictionary item {... ,1: [x1, x2, x3, x4, x5, x6] ...}
|
||||
The placeholders inside the list under the key '1' mean the following:
|
||||
|
||||
* [0, 2] - Action (0 = do nothing, 1 = create rule, 2 = delete rule)
|
||||
* [0, 1] - Permission (0 = DENY, 1 = ALLOW)
|
||||
* [0, num nodes] - Source IP (0 = any, then 1 -> x resolving to IP addresses)
|
||||
* [0, num nodes] - Dest IP (0 = any, then 1 -> x resolving to IP addresses)
|
||||
* [0, num services] - Protocol (0 = any, then 1 -> x resolving to protocol)
|
||||
* [0, num ports] - Port (0 = any, then 1 -> x resolving to port)
|
||||
|
||||
**ANY**
|
||||
The agent is able to carry out both **Node-Based** and **Access Control List** operations.
|
||||
|
||||
This means the dictionary will contain key-value pairs in the format of BOTH Node-Based and Access Control List as seen above.
|
||||
|
||||
Rewards
|
||||
*******
|
||||
|
||||
A reward value is presented back to the blue agent on the conclusion of every step. The reward value is calculated via two methods which combine to give the total value:
|
||||
|
||||
1. Node and service status
|
||||
2. IER status
|
||||
|
||||
**Node and service status**
|
||||
|
||||
On every step, the status of each node is compared against both a reference environment (simulating the situation if the red and blue agents had not impacted the environment)
|
||||
and the before and after state of the environment. If the comparison against the reference environment shows no difference, then the score provided is "AllOK". If there is a
|
||||
difference with respect to the reference environment, the before and after states are compared, and a score determined. See :ref:`config` for details of reward values.
|
||||
|
||||
**IER status**
|
||||
|
||||
On every step, the full IER set is examined to determine whether green and red agent IERs are being permitted to run. Any red agent IERs running incur a penalty; any green agent
|
||||
IERs not permitted to run also incur a penalty. See :ref:`config` for details of reward values.
|
||||
|
||||
Future Enhancements
|
||||
*******************
|
||||
|
||||
The PrimAITE project has an ambition to include the following enhancements in future releases:
|
||||
|
||||
* Integration with a suitable standardised framework to allow multi-agent integration
|
||||
* Integration with external threat emulation tools, either using off-line data, or integrating at runtime
|
||||
|
||||
@@ -1,41 +1,489 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
© Crown-owned copyright 2023, Defence Science and Technology Laboratory UK
|
||||
|
||||
PrimAITE |VERSION| Configuration
|
||||
********************************
|
||||
.. _config:
|
||||
|
||||
PrimAITE uses YAML configuration files to define everything needed to create the training environment for RL agents, including the network, the scripted agents, and the RL agent's action space, observation space, and reward function.
|
||||
The Config Files Explained
|
||||
==========================
|
||||
|
||||
Example Configuration Hierarchy
|
||||
###############################
|
||||
The top level configuration items in a configuration file is as follows
|
||||
PrimAITE uses two configuration files for its operation:
|
||||
|
||||
.. code-block:: yaml
|
||||
* **The Training Config**
|
||||
|
||||
io_settings:
|
||||
...
|
||||
game:
|
||||
...
|
||||
agents:
|
||||
...
|
||||
simulation:
|
||||
...
|
||||
Used to define the top-level settings of the PrimAITE environment, the reward values, and the session that is to be run.
|
||||
|
||||
These are expanded upon in the Configurable items section below
|
||||
* **The Lay Down Config**
|
||||
|
||||
Configurable items
|
||||
##################
|
||||
Used to define the low-level settings of a session, including the network laydown, green / red agent information exchange requirements (IERSs) and Access Control Rules.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
Training Config:
|
||||
*******************
|
||||
|
||||
configuration/io_settings.rst
|
||||
configuration/game.rst
|
||||
configuration/agents.rst
|
||||
configuration/simulation.rst
|
||||
The Training Config file consists of the following attributes:
|
||||
|
||||
Varying The Configuration Each Episode
|
||||
######################################
|
||||
**Generic Config Values**
|
||||
|
||||
PrimAITE allows for the configuration to be varied each episode. This is done by specifying a configuration folder instead of a single file. A full explanation is provided in the notebook `Using-Episode-Schedules.ipynb`. Please find the notebook in the user notebooks directory.
|
||||
|
||||
* **agent_framework** [enum]
|
||||
|
||||
This identifies the agent framework to be used to instantiate the agent algorithm. Select from one of the following:
|
||||
|
||||
* NONE - Where a user developed agent is to be used
|
||||
* SB3 - Stable Baselines3
|
||||
* RLLIB - Ray RLlib.
|
||||
|
||||
* **agent_identifier**
|
||||
|
||||
This identifies the agent to use for the session. Select from one of the following:
|
||||
|
||||
* A2C - Advantage Actor Critic
|
||||
* PPO - Proximal Policy Optimization
|
||||
* HARDCODED - A custom built deterministic agent
|
||||
* RANDOM - A Stochastic random agent
|
||||
|
||||
|
||||
* **random_red_agent** [bool]
|
||||
|
||||
Determines if the session should be run with a random red agent
|
||||
|
||||
* **action_type** [enum]
|
||||
|
||||
Determines whether a NODE, ACL, or ANY (combined NODE & ACL) action space format is adopted for the session
|
||||
|
||||
|
||||
* **OBSERVATION_SPACE** [dict]
|
||||
|
||||
Allows for user to configure observation space by combining one or more observation components. List of available
|
||||
components is in :py:mod:`primaite.environment.observations`.
|
||||
|
||||
The observation space config item should have a ``components`` key which is a list of components. Each component
|
||||
config must have a ``name`` key, and can optionally have an ``options`` key. The ``options`` are passed to the
|
||||
component while it is being initialised.
|
||||
|
||||
This example illustrates the correct format for the observation space config item
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
observation_space:
|
||||
components:
|
||||
- name: NODE_LINK_TABLE
|
||||
- name: NODE_STATUSES
|
||||
- name: LINK_TRAFFIC_LEVELS
|
||||
- name: ACCESS_CONTROL_LIST
|
||||
options:
|
||||
combine_service_traffic : False
|
||||
quantisation_levels: 99
|
||||
|
||||
|
||||
Currently available components are:
|
||||
|
||||
* :py:mod:`NODE_LINK_TABLE<primaite.environment.observations.NodeLinkTable>` this does not accept any additional options
|
||||
* :py:mod:`NODE_STATUSES<primaite.environment.observations.NodeStatuses>`, this does not accept any additional options
|
||||
* :py:mod:`ACCESS_CONTROL_LIST<primaite.environment.observations.AccessControlList>`, this does not accept additional options
|
||||
* :py:mod:`LINK_TRAFFIC_LEVELS<primaite.environment.observations.LinkTrafficLevels>`, this accepts the following options:
|
||||
|
||||
* ``combine_service_traffic`` - whether to consider bandwidth use separately for each network protocol or combine them into a single bandwidth reading (boolean)
|
||||
* ``quantisation_levels`` - how many discrete bandwidth usage levels to use for encoding. This can be an integer equal to or greater than 3.
|
||||
|
||||
The other configurable item is ``flatten`` which is false by default. When set to true, the observation space is flattened (turned into a 1-D vector). You should use this if your RL agent does not natively support observation space types like ``gym.Spaces.Tuple``.
|
||||
|
||||
* **num_train_episodes** [int]
|
||||
|
||||
This defines the number of episodes that the agent will train for.
|
||||
|
||||
|
||||
* **num_train_steps** [int]
|
||||
|
||||
Determines the number of steps to run in each episode of the training session.
|
||||
|
||||
|
||||
* **num_eval_episodes** [int]
|
||||
|
||||
This defines the number of episodes that the agent will be evaluated over.
|
||||
|
||||
|
||||
* **num_eval_steps** [int]
|
||||
|
||||
Determines the number of steps to run in each episode of the evaluation session.
|
||||
|
||||
|
||||
* **time_delay** [int]
|
||||
|
||||
The time delay (in milliseconds) to take between each step when running a GENERIC agent session
|
||||
|
||||
|
||||
* **session_type** [text]
|
||||
|
||||
Type of session to be run (TRAINING, EVALUATION, or BOTH)
|
||||
|
||||
* **load_agent** [bool]
|
||||
|
||||
Determine whether to load an agent from file
|
||||
|
||||
* **agent_load_file** [text]
|
||||
|
||||
File path and file name of agent if you're loading one in
|
||||
|
||||
* **observation_space_high_value** [int]
|
||||
|
||||
The high value to use for values in the observation space. This is set to 1000000000 by default, and should not need changing in most cases
|
||||
|
||||
* **implicit_acl_rule** [str]
|
||||
|
||||
Determines which Explicit rule the ACL list has - two options are: DENY or ALLOW.
|
||||
|
||||
* **max_number_acl_rules** [int]
|
||||
|
||||
Sets a limit on how many ACL rules there can be in the ACL list throughout the training session.
|
||||
|
||||
**Reward-Based Config Values**
|
||||
|
||||
Rewards are calculated based on the difference between the current state and reference state (the 'should be' state) of the environment.
|
||||
|
||||
* **Generic [all_ok]** [float]
|
||||
|
||||
The score to give when the current situation (for a given component) is no different from that expected in the baseline (i.e. as though no blue or red agent actions had been undertaken)
|
||||
|
||||
* **Node Hardware State [off_should_be_on]** [float]
|
||||
|
||||
The score to give when the node should be on, but is off
|
||||
|
||||
* **Node Hardware State [off_should_be_resetting]** [float]
|
||||
|
||||
The score to give when the node should be resetting, but is off
|
||||
|
||||
* **Node Hardware State [on_should_be_off]** [float]
|
||||
|
||||
The score to give when the node should be off, but is on
|
||||
|
||||
* **Node Hardware State [on_should_be_resetting]** [float]
|
||||
|
||||
The score to give when the node should be resetting, but is on
|
||||
|
||||
* **Node Hardware State [resetting_should_be_on]** [float]
|
||||
|
||||
The score to give when the node should be on, but is resetting
|
||||
|
||||
* **Node Hardware State [resetting_should_be_off]** [float]
|
||||
|
||||
The score to give when the node should be off, but is resetting
|
||||
|
||||
* **Node Hardware State [resetting]** [float]
|
||||
|
||||
The score to give when the node is resetting
|
||||
|
||||
* **Node Operating System or Service State [good_should_be_patching]** [float]
|
||||
|
||||
The score to give when the state should be patching, but is good
|
||||
|
||||
* **Node Operating System or Service State [good_should_be_compromised]** [float]
|
||||
|
||||
The score to give when the state should be compromised, but is good
|
||||
|
||||
* **Node Operating System or Service State [good_should_be_overwhelmed]** [float]
|
||||
|
||||
The score to give when the state should be overwhelmed, but is good
|
||||
|
||||
* **Node Operating System or Service State [patching_should_be_good]** [float]
|
||||
|
||||
The score to give when the state should be good, but is patching
|
||||
|
||||
* **Node Operating System or Service State [patching_should_be_compromised]** [float]
|
||||
|
||||
The score to give when the state should be compromised, but is patching
|
||||
|
||||
* **Node Operating System or Service State [patching_should_be_overwhelmed]** [float]
|
||||
|
||||
The score to give when the state should be overwhelmed, but is patching
|
||||
|
||||
* **Node Operating System or Service State [patching]** [float]
|
||||
|
||||
The score to give when the state is patching
|
||||
|
||||
* **Node Operating System or Service State [compromised_should_be_good]** [float]
|
||||
|
||||
The score to give when the state should be good, but is compromised
|
||||
|
||||
* **Node Operating System or Service State [compromised_should_be_patching]** [float]
|
||||
|
||||
The score to give when the state should be patching, but is compromised
|
||||
|
||||
* **Node Operating System or Service State [compromised_should_be_overwhelmed]** [float]
|
||||
|
||||
The score to give when the state should be overwhelmed, but is compromised
|
||||
|
||||
* **Node Operating System or Service State [compromised]** [float]
|
||||
|
||||
The score to give when the state is compromised
|
||||
|
||||
* **Node Operating System or Service State [overwhelmed_should_be_good]** [float]
|
||||
|
||||
The score to give when the state should be good, but is overwhelmed
|
||||
|
||||
* **Node Operating System or Service State [overwhelmed_should_be_patching]** [float]
|
||||
|
||||
The score to give when the state should be patching, but is overwhelmed
|
||||
|
||||
* **Node Operating System or Service State [overwhelmed_should_be_compromised]** [float]
|
||||
|
||||
The score to give when the state should be compromised, but is overwhelmed
|
||||
|
||||
* **Node Operating System or Service State [overwhelmed]** [float]
|
||||
|
||||
The score to give when the state is overwhelmed
|
||||
|
||||
* **Node File System State [good_should_be_repairing]** [float]
|
||||
|
||||
The score to give when the state should be repairing, but is good
|
||||
|
||||
* **Node File System State [good_should_be_restoring]** [float]
|
||||
|
||||
The score to give when the state should be restoring, but is good
|
||||
|
||||
* **Node File System State [good_should_be_corrupt]** [float]
|
||||
|
||||
The score to give when the state should be corrupt, but is good
|
||||
|
||||
* **Node File System State [good_should_be_destroyed]** [float]
|
||||
|
||||
The score to give when the state should be destroyed, but is good
|
||||
|
||||
* **Node File System State [repairing_should_be_good]** [float]
|
||||
|
||||
The score to give when the state should be good, but is repairing
|
||||
|
||||
* **Node File System State [repairing_should_be_restoring]** [float]
|
||||
|
||||
The score to give when the state should be restoring, but is repairing
|
||||
|
||||
* **Node File System State [repairing_should_be_corrupt]** [float]
|
||||
|
||||
The score to give when the state should be corrupt, but is repairing
|
||||
|
||||
* **Node File System State [repairing_should_be_destroyed]** [float]
|
||||
|
||||
The score to give when the state should be destroyed, but is repairing
|
||||
|
||||
* **Node File System State [repairing]** [float]
|
||||
|
||||
The score to give when the state is repairing
|
||||
|
||||
* **Node File System State [restoring_should_be_good]** [float]
|
||||
|
||||
The score to give when the state should be good, but is restoring
|
||||
|
||||
* **Node File System State [restoring_should_be_repairing]** [float]
|
||||
|
||||
The score to give when the state should be repairing, but is restoring
|
||||
|
||||
* **Node File System State [restoring_should_be_corrupt]** [float]
|
||||
|
||||
The score to give when the state should be corrupt, but is restoring
|
||||
|
||||
* **Node File System State [restoring_should_be_destroyed]** [float]
|
||||
|
||||
The score to give when the state should be destroyed, but is restoring
|
||||
|
||||
* **Node File System State [restoring]** [float]
|
||||
|
||||
The score to give when the state is restoring
|
||||
|
||||
* **Node File System State [corrupt_should_be_good]** [float]
|
||||
|
||||
The score to give when the state should be good, but is corrupt
|
||||
|
||||
* **Node File System State [corrupt_should_be_repairing]** [float]
|
||||
|
||||
The score to give when the state should be repairing, but is corrupt
|
||||
|
||||
* **Node File System State [corrupt_should_be_restoring]** [float]
|
||||
|
||||
The score to give when the state should be restoring, but is corrupt
|
||||
|
||||
* **Node File System State [corrupt_should_be_destroyed]** [float]
|
||||
|
||||
The score to give when the state should be destroyed, but is corrupt
|
||||
|
||||
* **Node File System State [corrupt]** [float]
|
||||
|
||||
The score to give when the state is corrupt
|
||||
|
||||
* **Node File System State [destroyed_should_be_good]** [float]
|
||||
|
||||
The score to give when the state should be good, but is destroyed
|
||||
|
||||
* **Node File System State [destroyed_should_be_repairing]** [float]
|
||||
|
||||
The score to give when the state should be repairing, but is destroyed
|
||||
|
||||
* **Node File System State [destroyed_should_be_restoring]** [float]
|
||||
|
||||
The score to give when the state should be restoring, but is destroyed
|
||||
|
||||
* **Node File System State [destroyed_should_be_corrupt]** [float]
|
||||
|
||||
The score to give when the state should be corrupt, but is destroyed
|
||||
|
||||
* **Node File System State [destroyed]** [float]
|
||||
|
||||
The score to give when the state is destroyed
|
||||
|
||||
* **Node File System State [scanning]** [float]
|
||||
|
||||
The score to give when the state is scanning
|
||||
|
||||
* **IER Status [red_ier_running]** [float]
|
||||
|
||||
The score to give when a red agent IER is permitted to run
|
||||
|
||||
* **IER Status [green_ier_blocked]** [float]
|
||||
|
||||
The score to give when a green agent IER is prevented from running
|
||||
|
||||
**Patching / Reset Durations**
|
||||
|
||||
* **os_patching_duration** [int]
|
||||
|
||||
The number of steps to take when patching an Operating System
|
||||
|
||||
* **node_reset_duration** [int]
|
||||
|
||||
The number of steps to take when resetting a node's hardware state
|
||||
|
||||
* **service_patching_duration** [int]
|
||||
|
||||
The number of steps to take when patching a service
|
||||
|
||||
* **file_system_repairing_limit** [int]:
|
||||
|
||||
The number of steps to take when repairing the file system
|
||||
|
||||
* **file_system_restoring_limit** [int]
|
||||
|
||||
The number of steps to take when restoring the file system
|
||||
|
||||
* **file_system_scanning_limit** [int]
|
||||
|
||||
The number of steps to take when scanning the file system
|
||||
|
||||
* **deterministic** [bool]
|
||||
|
||||
Set to true if the agent evaluation should be deterministic. Default is ``False``
|
||||
|
||||
* **seed** [int]
|
||||
|
||||
Seed used in the randomisation in agent training. Default is ``None``
|
||||
|
||||
The Lay Down Config
|
||||
*******************
|
||||
|
||||
The lay down config file consists of the following attributes:
|
||||
|
||||
|
||||
* **itemType: STEPS** [int]
|
||||
|
||||
* **item_type: PORTS** [int]
|
||||
|
||||
Provides a list of ports modelled in this session
|
||||
|
||||
* **item_type: SERVICES** [freetext]
|
||||
|
||||
Provides a list of services modelled in this session
|
||||
|
||||
* **item_type: NODE**
|
||||
|
||||
Defines a node included in the system laydown being simulated. It should consist of the following attributes:
|
||||
|
||||
* **id** [int]: Unique ID for this YAML item
|
||||
* **name** [freetext]: Human-readable name of the component
|
||||
* **node_class** [enum]: Relates to the base type of the node. Can be SERVICE, ACTIVE or PASSIVE. PASSIVE nodes do not have an operating system or services. ACTIVE nodes have an operating system, but no services. SERVICE nodes have both an operating system and one or more services
|
||||
* **node_type** [enum]: Relates to the component type. Can be one of CCTV, SWITCH, COMPUTER, LINK, MONITOR, PRINTER, LOP, RTU, ACTUATOR or SERVER
|
||||
* **priority** [enum]: Provides a priority for each node. Can be one of P1, P2, P3, P4 or P5 (which P1 being the highest)
|
||||
* **hardware_state** [enum]: The initial hardware state of the node. Can be one of ON, OFF or RESETTING
|
||||
* **ip_address** [IP address]: The IP address of the component in format xxx.xxx.xxx.xxx
|
||||
* **software_state** [enum]: The intial state of the node operating system. Can be GOOD, PATCHING or COMPROMISED
|
||||
* **file_system_state** [enum]: The initial state of the node file system. Can be GOOD, CORRUPT, DESTROYED, REPAIRING or RESTORING
|
||||
* **services**: For each service associated with the node:
|
||||
|
||||
* **name** [freetext]: Free-text name of the service, but must match one of the services defined for the system in the services list
|
||||
* **port** [int]: Integer value of the port related to this service, but must match one of the ports defined for the system in the ports list
|
||||
* **state** [enum]: The initial state of the service. Can be one of GOOD, PATCHING, COMPROMISED or OVERWHELMED
|
||||
|
||||
* **item_type: LINK**
|
||||
|
||||
Defines a link included in the system laydown being simulated. It should consist of the following attributes:
|
||||
|
||||
* **id** [int]: Unique ID for this YAML item
|
||||
* **name** [freetext]: Human-readable name of the component
|
||||
* **bandwidth** [int]: The bandwidth (in bits/s) of the link
|
||||
* **source** [int]: The ID of the source node
|
||||
* **destination** [int]: The ID of the destination node
|
||||
|
||||
* **item_type: GREEN_IER**
|
||||
|
||||
Defines a green agent Information Exchange Requirement (IER). It should consist of:
|
||||
|
||||
* **id** [int]: Unique ID for this YAML item
|
||||
* **start_step** [int]: The start step (in the episode) for this IER to begin
|
||||
* **end_step** [int]: The end step (in the episode) for this IER to finish
|
||||
* **load** [int]: The load (in bits/s) for this IER to apply to links
|
||||
* **protocol** [freetext]: The protocol to apply to the links. This must match a value in the services list
|
||||
* **port** [int]: The port that the protocol is running on. This must match a value in the ports list
|
||||
* **source** [int]: The ID of the source node
|
||||
* **destination** [int]: The ID of the destination node
|
||||
* **mission_criticality** [enum]: The mission criticality of this IER (with 5 being highest, 1 lowest)
|
||||
|
||||
* **item_type: RED_IER**
|
||||
|
||||
Defines a red agent Information Exchange Requirement (IER). It should consist of:
|
||||
|
||||
* **id** [int]: Unique ID for this YAML item
|
||||
* **start_step** [int]: The start step (in the episode) for this IER to begin
|
||||
* **end_step** [int]: The end step (in the episode) for this IER to finish
|
||||
* **load** [int]: The load (in bits/s) for this IER to apply to links
|
||||
* **protocol** [freetext]: The protocol to apply to the links. This must match a value in the services list
|
||||
* **port** [int]: The port that the protocol is running on. This must match a value in the ports list
|
||||
* **source** [int]: The ID of the source node
|
||||
* **destination** [int]: The ID of the destination node
|
||||
* **mission_criticality** [enum]: Not currently used. Default to 0
|
||||
|
||||
* **item_type: GREEN_POL**
|
||||
|
||||
Defines a green agent pattern-of-life instruction. It should consist of:
|
||||
|
||||
* **id** [int]: Unique ID for this YAML item
|
||||
* **start_step** [int]: The start step (in the episode) for this PoL to begin
|
||||
* **end_step** [int]: Not currently used. Default to same as start step
|
||||
* **nodeId** [int]: The ID of the node to apply the PoL to
|
||||
* **type** [enum]: The type of PoL to apply. Can be one of OPERATING, OS or SERVICE
|
||||
* **protocol** [freetext]: The protocol to be affected if SERVICE type is chosen. Must match a value in the services list
|
||||
* **state** [enuum]: The state to apply to the node (which represents the PoL change). Can be one of ON, OFF or RESETTING (for node state) or GOOD, PATCHING or COMPROMISED (for Software State) or GOOD, PATCHING, COMPROMISED or OVERWHELMED (for service state)
|
||||
|
||||
* **item_type: RED_POL**
|
||||
|
||||
Defines a red agent pattern-of-life instruction. It should consist of:
|
||||
|
||||
* **id** [int]: Unique ID for this YAML item
|
||||
* **start_step** [int]: The start step (in the episode) for this PoL to begin
|
||||
* **end_step** [int]: Not currently used. Default to same as start step
|
||||
* **targetNodeId** [int]: The ID of the node to apply the PoL to
|
||||
* **initiator** [enum]: What initiates the PoL. Can be DIRECT, IER or SERVICE
|
||||
* **type** [enum]: The type of PoL to apply. Can be one of OPERATING, OS or SERVICE
|
||||
* **protocol** [freetext]: The protocol to be affected if SERVICE type is chosen. Must match a value in the services list
|
||||
* **state** [enum]: The state to apply to the node (which represents the PoL change). Can be one of ON, OFF or RESETTING (for node state) or GOOD, PATCHING or COMPROMISED (for Software State) or GOOD, PATCHING, COMPROMISED or OVERWHELMED (for service state) or GOOD, CORRUPT, DESTROYED, REPAIRING or RESTORING (for file system state)
|
||||
* **sourceNodeId** [int] The ID of the source node containing the service to check (used for SERVICE initiator)
|
||||
* **sourceNodeService** [freetext]: The service on the source node to check (used for SERVICE initiator). Must match a value in the services list for this node
|
||||
* **sourceNodeServiceState** [enum]: The state of the source node service to check (used for SERVICE initiator). Can be one of GOOD, PATCHING, COMPROMISED or OVERWHELMED
|
||||
|
||||
* **item_type: ACL_RULE**
|
||||
|
||||
Defines an initial Access Control List (ACL) rule. It should consist of:
|
||||
|
||||
* **id** [int]: Unique ID for this YAML item
|
||||
* **permission** [enum]: Defines either an allow or deny rule. Value must be either DENY or ALLOW
|
||||
* **source** [IP address]: Defines the source IP address for the rule in xxx.xxx.xxx.xxx format
|
||||
* **destination** [IP address]: Defines the destination IP address for the rule in xxx.xxx.xxx.xxx format
|
||||
* **protocol** [freetext]: Defines the protocol for the rule. Must match a value in the services list
|
||||
* **port** [int]: Defines the port for the rule. Must match a value in the ports list
|
||||
* **position** [int]: Defines where to place the ACL rule in the list. Lower index or (higher up in the list) means they are checked first. Index starts at 0 (Python indexes).
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
|
||||
``agents``
|
||||
==========
|
||||
Agents can be scripted (deterministic and stochastic), or controlled by a reinforcement learning algorithm. Not to be confused with an RL agent, the term agent here is used to refer to an entity that sends requests to the simulated network. In this part of the config, each agent's action space, observation space, and reward function can be defined. All three are defined in a modular way.
|
||||
|
||||
``agents`` hierarchy
|
||||
--------------------
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
agents:
|
||||
- ref: red_agent_example
|
||||
...
|
||||
- ref: blue_agent_example
|
||||
...
|
||||
- ref: green_agent_example
|
||||
team: GREEN
|
||||
type: ProbabilisticAgent
|
||||
observation_space:
|
||||
type: UC2GreenObservation
|
||||
action_space:
|
||||
action_list:
|
||||
- type: DONOTHING
|
||||
- type: NODE_APPLICATION_EXECUTE
|
||||
options:
|
||||
nodes:
|
||||
- node_name: client_2
|
||||
applications:
|
||||
- application_name: WebBrowser
|
||||
max_folders_per_node: 1
|
||||
max_files_per_folder: 1
|
||||
max_services_per_node: 1
|
||||
max_applications_per_node: 1
|
||||
|
||||
reward_function:
|
||||
reward_components:
|
||||
- type: DUMMY
|
||||
|
||||
agent_settings:
|
||||
start_settings:
|
||||
start_step: 5
|
||||
frequency: 4
|
||||
variance: 3
|
||||
flatten_obs: False
|
||||
|
||||
``ref``
|
||||
-------
|
||||
The reference to be used for the given agent.
|
||||
|
||||
``team``
|
||||
--------
|
||||
Specifies if the agent is malicious (``RED``), benign (``GREEN``), or defensive (``BLUE``). Currently this value is not used for anything other than for human readability in the configuration file.
|
||||
|
||||
``type``
|
||||
--------
|
||||
Specifies which class should be used for the agent. ``ProxyAgent`` is used for agents that receive instructions from an RL algorithm. Scripted agents like ``RedDatabaseCorruptingAgent`` and ``ProbabilisticAgent`` generate their own behaviour.
|
||||
|
||||
Available agent types:
|
||||
|
||||
- ``ProbabilisticAgent``
|
||||
- ``ProxyAgent``
|
||||
- ``RedDatabaseCorruptingAgent``
|
||||
|
||||
``observation_space``
|
||||
---------------------
|
||||
Defines the observation space of the agent.
|
||||
|
||||
``type``
|
||||
^^^^^^^^
|
||||
|
||||
selects which python class from the :py:mod:`primaite.game.agent.observation` module is used for the overall observation structure.
|
||||
|
||||
``options``
|
||||
^^^^^^^^^^^
|
||||
|
||||
Allows configuration of the chosen observation type. These are optional.
|
||||
|
||||
* ``num_services_per_node``, ``num_folders_per_node``, ``num_files_per_folder``, ``num_nics_per_node`` all define the shape of the observation space. The size and shape of the obs space must remain constant, but the number of files, folders, ACL rules, and other components can change within an episode. Therefore padding is performed and these options set the size of the obs space.
|
||||
* ``nodes``: list of nodes that will be present in this agent's observation space. The ``node_ref`` relates to the human-readable unique reference defined later in the ``simulation`` part of the config. Each node can also be configured with services, and files that should be monitored.
|
||||
* ``links``: list of links that will be present in this agent's observation space. The ``link_ref`` relates to the human-readable unique reference defined later in the ``simulation`` part of the config.
|
||||
* ``acl``: configure how the agent reads the access control list on the router in the simulation. ``router_node_ref`` is for selecting which router's ACL table should be used. ``ip_list`` sets the encoding of ip addresses as integers within the observation space.
|
||||
|
||||
For more information see :py:mod:`primaite.game.agent.observations`
|
||||
|
||||
``action_space``
|
||||
----------------
|
||||
|
||||
The action space is configured to be made up of individual action types. Once configured, the agent can select an action type and some optional action parameters at every step. For example: The ``NODE_SERVICE_SCAN`` action takes the parameters ``node_id`` and ``service_id``.
|
||||
|
||||
``action_list``
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
A list of action modules. The options are listed in the :py:mod:`primaite.game.agent.actions.ActionManager.act_class_identifiers` module.
|
||||
|
||||
``action_map``
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Restricts the possible combinations of action type / action parameter values to reduce the overall size of the action space. By default, every possible combination of actions and parameters will be assigned an integer for the agent's ``MultiDiscrete`` action space. Instead, the ``action_map`` allows you to list the actions corresponding to each integer in the ``MultiDiscrete`` space.
|
||||
|
||||
This is Optional.
|
||||
|
||||
``options``
|
||||
^^^^^^^^^^^
|
||||
|
||||
Options that apply to all action components. These are optional.
|
||||
|
||||
* ``nodes``: list the nodes that the agent can act on, the order of this list defines the mapping between nodes and ``node_id`` integers.
|
||||
* ``max_folders_per_node``, ``max_files_per_folder``, ``max_services_per_node``, ``max_nics_per_node``, ``max_acl_rules`` all are used to define the size of the action space.
|
||||
|
||||
For more information see :py:mod:`primaite.game.agent.actions`
|
||||
|
||||
``reward_function``
|
||||
-------------------
|
||||
|
||||
Similar to action space, this is defined as a list of components from the :py:mod:`primaite.game.agent.rewards` module.
|
||||
|
||||
``reward_components``
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A list of reward types from :py:mod:`primaite.game.agent.rewards.RewardFunction.rew_class_identifiers`
|
||||
|
||||
e.g.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
reward_components:
|
||||
- type: DUMMY
|
||||
- type: DATABASE_FILE_INTEGRITY
|
||||
|
||||
|
||||
``agent_settings``
|
||||
------------------
|
||||
|
||||
Settings passed to the agent during initialisation. Determines how the agent will behave during training.
|
||||
|
||||
e.g.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
agent_settings:
|
||||
start_settings:
|
||||
start_step: 25
|
||||
frequency: 20
|
||||
variance: 5
|
||||
|
||||
``start_step``
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Optional. Default value is ``5``.
|
||||
|
||||
The timestep where the agent begins performing actions.
|
||||
|
||||
``frequency``
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Optional. Default value is ``5``.
|
||||
|
||||
The number of timesteps the agent will wait before performing another action.
|
||||
|
||||
``variance``
|
||||
^^^^^^^^^^^^
|
||||
|
||||
Optional. Default value is ``0``.
|
||||
|
||||
The amount of timesteps that the frequency can randomly change.
|
||||
|
||||
``flatten_obs``
|
||||
---------------
|
||||
|
||||
If ``True``, gymnasium flattening will be performed on the observation space before sending to the agent. Set this to ``True`` if your agent does not support nested observation spaces.
|
||||
@@ -1,56 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
|
||||
``game``
|
||||
========
|
||||
This section defines high-level settings that apply across the game, currently it's used to help shape the action and observation spaces by restricting which ports and internet protocols should be considered. Here, users can also set the maximum number of steps in an episode.
|
||||
|
||||
``game`` hierarchy
|
||||
------------------
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
game:
|
||||
max_episode_length: 256
|
||||
ports:
|
||||
- ARP
|
||||
- DNS
|
||||
- HTTP
|
||||
- POSTGRES_SERVER
|
||||
protocols:
|
||||
- ICMP
|
||||
- TCP
|
||||
- UDP
|
||||
thresholds:
|
||||
nmne:
|
||||
high: 10
|
||||
medium: 5
|
||||
low: 0
|
||||
|
||||
``max_episode_length``
|
||||
----------------------
|
||||
|
||||
Optional. Default value is ``256``.
|
||||
|
||||
The maximum number of episodes a Reinforcement Learning agent(s) can be trained for.
|
||||
|
||||
``ports``
|
||||
---------
|
||||
|
||||
A list of ports that the Reinforcement Learning agent(s) are able to see in the observation space.
|
||||
|
||||
See :ref:`List of Ports <List of Ports>` for a list of ports.
|
||||
|
||||
``protocols``
|
||||
-------------
|
||||
|
||||
A list of protocols that the Reinforcement Learning agent(s) are able to see in the observation space.
|
||||
|
||||
See :ref:`List of IPProtocols <List of IPProtocols>` for a list of protocols.
|
||||
|
||||
``thresholds``
|
||||
--------------
|
||||
|
||||
These are used to determine the thresholds of high, medium and low categories for counted observation occurrences.
|
||||
@@ -1,90 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
|
||||
``io_settings``
|
||||
===============
|
||||
This section configures how PrimAITE saves data during simulation and training.
|
||||
|
||||
``io_settings`` hierarchy
|
||||
-------------------------
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
io_settings:
|
||||
# save_logs: True
|
||||
save_agent_actions: True
|
||||
save_step_metadata: False
|
||||
save_pcap_logs: False
|
||||
save_sys_logs: False
|
||||
write_sys_log_to_terminal: False
|
||||
sys_log_level: WARNING
|
||||
|
||||
|
||||
``save_logs``
|
||||
-------------
|
||||
|
||||
*currently unused*.
|
||||
|
||||
``save_agent_actions``
|
||||
----------------------
|
||||
|
||||
Optional. Default value is ``True``.
|
||||
|
||||
If ``True``, this will create a JSON file each episode detailing every agent's action in each step of that episode, formatted according to the CAOS format. This includes scripted, RL, and red agents.
|
||||
|
||||
``save_step_metadata``
|
||||
----------------------
|
||||
|
||||
Optional. Default value is ``False``.
|
||||
|
||||
If ``True``, The RL agent(s) actions, environment states and other data will be saved at every single step.
|
||||
|
||||
|
||||
``save_pcap_logs``
|
||||
------------------
|
||||
|
||||
Optional. Default value is ``False``.
|
||||
|
||||
If ``True``, then the pcap files which contain all network traffic during the simulation will be saved.
|
||||
|
||||
|
||||
``save_sys_logs``
|
||||
-----------------
|
||||
|
||||
Optional. Default value is ``False``.
|
||||
|
||||
If ``True``, then the log files which contain all node actions during the simulation will be saved.
|
||||
|
||||
|
||||
``write_sys_log_to_terminal``
|
||||
-----------------------------
|
||||
|
||||
Optional. Default value is ``False``.
|
||||
|
||||
If ``True``, PrimAITE will print sys log to the terminal.
|
||||
|
||||
|
||||
``sys_log_level``
|
||||
-------------
|
||||
|
||||
Optional. Default value is ``WARNING``.
|
||||
|
||||
The level of logging that should be visible in the sys logs or the logs output to the terminal.
|
||||
|
||||
``save_sys_logs`` or ``write_sys_log_to_terminal`` has to be set to ``True`` for this setting to be used.
|
||||
|
||||
Available options are:
|
||||
|
||||
- ``DEBUG``: Debug level items and the items below
|
||||
- ``INFO``: Info level items and the items below
|
||||
- ``WARNING``: Warning level items and the items below
|
||||
- ``ERROR``: Error level items and the items below
|
||||
- ``CRITICAL``: Only critical level logs
|
||||
|
||||
See also |logging_levels|
|
||||
|
||||
.. |logging_levels| raw:: html
|
||||
|
||||
<a href="https://docs.python.org/3/library/logging.html#logging-levels" target="blank">Python logging levels</a>
|
||||
@@ -1,103 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
|
||||
``simulation``
|
||||
==============
|
||||
In this section the network layout is defined. This part of the config follows a hierarchical structure. Almost every component defines a ``ref`` field which acts as a human-readable unique identifier, used by other parts of the config, such as agents.
|
||||
|
||||
At the top level of the network are ``nodes`` and ``links``.
|
||||
|
||||
e.g.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
simulation:
|
||||
network:
|
||||
nodes:
|
||||
...
|
||||
links:
|
||||
...
|
||||
|
||||
``nodes``
|
||||
---------
|
||||
|
||||
This is where the list of nodes are defined. Some items will differ according to the node type, however, there will be common items such as a node's reference (which is used by the agent), the node's ``type`` and ``hostname``
|
||||
|
||||
To see the configuration for these nodes, refer to the following:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
simulation/nodes/computer
|
||||
simulation/nodes/firewall
|
||||
simulation/nodes/router
|
||||
simulation/nodes/server
|
||||
simulation/nodes/switch
|
||||
simulation/nodes/wireless_router
|
||||
simulation/nodes/network_examples
|
||||
|
||||
``links``
|
||||
---------
|
||||
|
||||
This is where the links between the nodes are formed.
|
||||
|
||||
e.g.
|
||||
|
||||
In order to recreate the network below, we will need to create 2 links:
|
||||
|
||||
- a link from computer_1 to the switch
|
||||
- a link from computer_2 to the switch
|
||||
|
||||
.. image:: ../../_static/switched_p2p_network.png
|
||||
:width: 500
|
||||
:align: center
|
||||
|
||||
this results in:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
links:
|
||||
- endpoint_a_hostname: computer_1
|
||||
endpoint_a_port: 1 # port 1 on computer_1
|
||||
endpoint_b_hostname: switch
|
||||
endpoint_b_port: 1 # port 1 on switch
|
||||
bandwidth: 100
|
||||
- endpoint_a_hostname: computer_2
|
||||
endpoint_a_port: 1 # port 1 on computer_2
|
||||
endpoint_b_hostname: switch
|
||||
endpoint_b_port: 2 # port 2 on switch
|
||||
bandwidth: 100
|
||||
|
||||
``ref``
|
||||
^^^^^^^
|
||||
|
||||
The human readable name for the link. Not used in code, however is useful for a human to understand what the link is for.
|
||||
|
||||
``endpoint_a_hostname``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``hostname`` of the node which must be connected.
|
||||
|
||||
``endpoint_a_port``
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The port on ``endpoint_a_hostname`` which is to be connected to ``endpoint_b_port``.
|
||||
This accepts an integer value e.g. if port 1 is to be connected, the configuration should be ``endpoint_a_port: 1``
|
||||
|
||||
``endpoint_b_hostname``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``hostname`` of the node which must be connected.
|
||||
|
||||
``endpoint_b_port``
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The port on ``endpoint_b_hostname`` which is to be connected to ``endpoint_a_port``.
|
||||
This accepts an integer value e.g. if port 1 is to be connected, the configuration should be ``endpoint_b_port: 1``
|
||||
|
||||
``bandwidth``
|
||||
|
||||
This is an integer value specifying the allowed bandwidth across the connection. Units are in Mbps.
|
||||
@@ -1,35 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
.. _Node Attributes:
|
||||
|
||||
Common Attributes
|
||||
#################
|
||||
|
||||
Node Attributes
|
||||
===============
|
||||
|
||||
Attributes that are shared by all nodes.
|
||||
|
||||
.. include:: common_node_attributes.rst
|
||||
|
||||
.. _Network Node Attributes:
|
||||
|
||||
Network Node Attributes
|
||||
=======================
|
||||
|
||||
Attributes that are shared by nodes that inherit from :py:mod:`primaite.simulator.network.hardware.nodes.network.network_node.NetworkNode`
|
||||
|
||||
.. include:: common_host_node_attributes.rst
|
||||
|
||||
.. _Host Node Attributes:
|
||||
|
||||
Host Node Attributes
|
||||
====================
|
||||
|
||||
Attributes that are shared by nodes that inherit from :py:mod:`primaite.simulator.network.hardware.nodes.host.host_node.HostNode`
|
||||
|
||||
.. include:: common_host_node_attributes.rst
|
||||
|
||||
.. |NODE| replace:: node
|
||||
@@ -1,26 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
.. _common_host_node_attributes:
|
||||
|
||||
``ip_address``
|
||||
--------------
|
||||
|
||||
The IP address of the |NODE| in the network.
|
||||
|
||||
``subnet_mask``
|
||||
---------------
|
||||
|
||||
Optional. Default value is ``255.255.255.0``.
|
||||
|
||||
The subnet mask for the |NODE| to use.
|
||||
|
||||
``default_gateway``
|
||||
-------------------
|
||||
|
||||
The IP address that the |NODE| will use as the default gateway. Typically, this is the IP address of the closest router that the |NODE| is connected to.
|
||||
|
||||
.. include:: ../software/applications.rst
|
||||
|
||||
.. include:: ../software/services.rst
|
||||
@@ -1,51 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
.. _common_network_node_attributes:
|
||||
|
||||
``routes``
|
||||
----------
|
||||
|
||||
A list of routes which tells the |NODE| where to forward the packet to depending on the target IP address.
|
||||
|
||||
e.g.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
nodes:
|
||||
- ref: node
|
||||
...
|
||||
routes:
|
||||
- address: 192.168.0.10
|
||||
subnet_mask: 255.255.255.0
|
||||
next_hop_ip_address: 192.168.1.1
|
||||
metric: 0
|
||||
|
||||
``address``
|
||||
"""""""""""
|
||||
|
||||
The target IP address for the route. If the packet destination IP address matches this, the |NODE| will route the packet according to the ``next_hop_ip_address``.
|
||||
|
||||
This must be a valid octet i.e. in the range of ``0.0.0.0`` and ``255.255.255.255``.
|
||||
|
||||
``subnet_mask``
|
||||
"""""""""""""""
|
||||
|
||||
Optional. Default value is ``255.255.255.0``.
|
||||
|
||||
The subnet mask setting for the route.
|
||||
|
||||
``next_hop_ip_address``
|
||||
"""""""""""""""""""""""
|
||||
|
||||
The IP address of the next hop IP address that the packet will follow if the address matches the packet's destination IP address.
|
||||
|
||||
This must be a valid octet i.e. in the range of ``0.0.0.0`` and ``255.255.255.255``.
|
||||
|
||||
``metric``
|
||||
""""""""""
|
||||
|
||||
Optional. Default value is ``0``. This value accepts floats.
|
||||
|
||||
The cost or distance of a route. The higher the value, the more cost or distance is attributed to the route.
|
||||
@@ -1,55 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
.. _common_node_attributes:
|
||||
|
||||
``ref``
|
||||
-------
|
||||
|
||||
Human readable name used as reference for the |NODE|. Not used in code.
|
||||
|
||||
``hostname``
|
||||
------------
|
||||
|
||||
The hostname of the |NODE|. This will be used to reference the |NODE|.
|
||||
|
||||
``operating_state``
|
||||
-------------------
|
||||
|
||||
The initial operating state of the node.
|
||||
|
||||
Optional. Default value is ``ON``.
|
||||
|
||||
Options available are:
|
||||
|
||||
- ``ON``
|
||||
- ``OFF``
|
||||
- ``BOOTING``
|
||||
- ``SHUTTING_DOWN``
|
||||
|
||||
Note that YAML may assume non quoted ``ON`` and ``OFF`` as ``True`` and ``False`` respectively. To prevent this, use ``"ON"`` or ``"OFF"``
|
||||
|
||||
See :py:mod:`primaite.simulator.network.hardware.node_operating_state.NodeOperatingState`
|
||||
|
||||
|
||||
``dns_server``
|
||||
--------------
|
||||
|
||||
Optional. Default value is ``None``.
|
||||
|
||||
The IP address of the node which holds an instance of the :ref:`DNSServer`. Some applications may use a domain name e.g. the :ref:`WebBrowser`
|
||||
|
||||
``start_up_duration``
|
||||
---------------------
|
||||
|
||||
Optional. Default value is ``3``.
|
||||
|
||||
The number of time steps required to occur in order for the node to cycle from ``OFF`` to ``BOOTING_UP`` and then finally ``ON``.
|
||||
|
||||
``shut_down_duration``
|
||||
----------------------
|
||||
|
||||
Optional. Default value is ``3``.
|
||||
|
||||
The number of time steps required to occur in order for the node to cycle from ``ON`` to ``SHUTTING_DOWN`` and then finally ``OFF``.
|
||||
@@ -1,18 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
``type``
|
||||
--------
|
||||
|
||||
The type of node to add.
|
||||
|
||||
Available options are:
|
||||
|
||||
- ``computer``
|
||||
- ``firewall``
|
||||
- ``router``
|
||||
- ``server``
|
||||
- ``switch``
|
||||
|
||||
To create a |NODE|, type must be |NODE_TYPE|.
|
||||
@@ -1,41 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
.. _computer_configuration:
|
||||
|
||||
``computer``
|
||||
============
|
||||
|
||||
A basic representation of a computer within the simulation.
|
||||
|
||||
See :py:mod:`primaite.simulator.network.hardware.nodes.host.computer.Computer`
|
||||
|
||||
example computer
|
||||
----------------
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
simulation:
|
||||
network:
|
||||
nodes:
|
||||
- ref: client_1
|
||||
hostname: client_1
|
||||
type: computer
|
||||
ip_address: 192.168.0.10
|
||||
subnet_mask: 255.255.255.0
|
||||
default_gateway: 192.168.0.1
|
||||
dns_server: 192.168.1.10
|
||||
applications:
|
||||
...
|
||||
services:
|
||||
...
|
||||
|
||||
.. include:: common/common_node_attributes.rst
|
||||
|
||||
.. include:: common/node_type_list.rst
|
||||
|
||||
.. include:: common/common_host_node_attributes.rst
|
||||
|
||||
.. |NODE| replace:: computer
|
||||
.. |NODE_TYPE| replace:: ``computer``
|
||||
@@ -1,300 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
.. _firewall_configuration:
|
||||
|
||||
``firewall``
|
||||
============
|
||||
|
||||
A basic representation of a network firewall within the simulation.
|
||||
|
||||
The firewall is similar to how :ref:`Router <router_configuration>` works, with the difference being how firewall has specific ACL rules for inbound and outbound traffic as well as firewall being limited to 3 ports.
|
||||
|
||||
See :py:mod:`primaite.simulator.network.hardware.nodes.network.firewall.Firewall`
|
||||
|
||||
example firewall
|
||||
----------------
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
simulation:
|
||||
network:
|
||||
nodes:
|
||||
- ref: firewall
|
||||
hostname: firewall
|
||||
type: firewall
|
||||
start_up_duration: 0
|
||||
shut_down_duration: 0
|
||||
ports:
|
||||
external_port: # port 1
|
||||
ip_address: 192.168.20.1
|
||||
subnet_mask: 255.255.255.0
|
||||
internal_port: # port 2
|
||||
ip_address: 192.168.1.2
|
||||
subnet_mask: 255.255.255.0
|
||||
dmz_port: # port 3
|
||||
ip_address: 192.168.10.1
|
||||
subnet_mask: 255.255.255.0
|
||||
acl:
|
||||
internal_inbound_acl:
|
||||
...
|
||||
internal_outbound_acl:
|
||||
...
|
||||
dmz_inbound_acl:
|
||||
...
|
||||
dmz_outbound_acl:
|
||||
...
|
||||
external_inbound_acl:
|
||||
...
|
||||
external_outbound_acl:
|
||||
...
|
||||
routes:
|
||||
...
|
||||
|
||||
.. include:: common/common_node_attributes.rst
|
||||
|
||||
.. include:: common/node_type_list.rst
|
||||
|
||||
``ports``
|
||||
---------
|
||||
|
||||
The firewall node only has 3 ports. These specifically are:
|
||||
|
||||
- ``external_port`` (port 1)
|
||||
- ``internal_port`` (port 2)
|
||||
- ``dmz_port`` (port 3) (can be optional)
|
||||
|
||||
The ports should be defined with an ip address and subnet mask e.g.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
nodes:
|
||||
- ref: firewall
|
||||
...
|
||||
ports:
|
||||
external_port: # port 1
|
||||
ip_address: 192.168.20.1
|
||||
subnet_mask: 255.255.255.0
|
||||
internal_port: # port 2
|
||||
ip_address: 192.168.1.2
|
||||
subnet_mask: 255.255.255.0
|
||||
dmz_port: # port 3
|
||||
ip_address: 192.168.10.1
|
||||
subnet_mask: 255.255.255.0
|
||||
|
||||
``ip_address``
|
||||
""""""""""""""
|
||||
|
||||
The IP address for the given port. This must be a valid octet i.e. in the range of ``0.0.0.0`` and ``255.255.255.255``.
|
||||
|
||||
``subnet_mask``
|
||||
"""""""""""""""
|
||||
|
||||
Optional. Default value is ``255.255.255.0``.
|
||||
|
||||
The subnet mask setting for the port.
|
||||
|
||||
``acl``
|
||||
-------
|
||||
|
||||
There are 6 ACLs that can be defined for a firewall
|
||||
|
||||
- ``internal_inbound_acl`` for traffic going towards the internal network
|
||||
- ``internal_outbound_acl`` for traffic coming from the internal network
|
||||
- ``dmz_inbound_acl`` for traffic going towards the dmz network
|
||||
- ``dmz_outbound_acl`` for traffic coming from the dmz network
|
||||
- ``external_inbound_acl`` for traffic coming from the external network
|
||||
- ``external_outbound_acl`` for traffic going towards the external network
|
||||
|
||||
.. image:: ../../../../_static/firewall_acl.png
|
||||
:width: 500
|
||||
:align: center
|
||||
|
||||
By default, ``external_inbound_acl`` and ``external_outbound_acl`` will permit any traffic through.
|
||||
|
||||
``internal_inbound_acl``, ``internal_outbound_acl``, ``dmz_inbound_acl`` and ``dmz_outbound_acl`` will deny any traffic by default, so must be configured to allow defined ``src_port`` and ``dst_port`` or ``protocol``.
|
||||
|
||||
See :py:mod:`primaite.simulator.network.hardware.nodes.network.router.AccessControlList`
|
||||
|
||||
See :ref:`List of Ports <List of Ports>` for a list of ports.
|
||||
|
||||
``internal_inbound_acl``
|
||||
""""""""""""""""""""""""
|
||||
|
||||
ACL rules for packets that have a destination IP address in what is considered the internal network.
|
||||
|
||||
example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
nodes:
|
||||
- ref: firewall
|
||||
...
|
||||
acl:
|
||||
internal_inbound_acl:
|
||||
21: # position 21 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
src_port: POSTGRES_SERVER # are emitted from the POSTGRES_SERVER port
|
||||
dst_port: POSTGRES_SERVER # are going towards an POSTGRES_SERVER port
|
||||
22: # position 22 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
src_port: ARP # are emitted from the ARP port
|
||||
dst_port: ARP # are going towards an ARP port
|
||||
23: # position 23 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
protocol: ICMP # are ICMP
|
||||
|
||||
``internal_outbound_acl``
|
||||
"""""""""""""""""""""""""
|
||||
|
||||
ACL rules for packets that have a source IP address in what is considered the internal network and is going towards the DMZ network or the external network.
|
||||
|
||||
example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
nodes:
|
||||
- ref: firewall
|
||||
...
|
||||
acl:
|
||||
internal_outbound_acl:
|
||||
21: # position 21 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
src_port: POSTGRES_SERVER # are emitted from the POSTGRES_SERVER port
|
||||
dst_port: POSTGRES_SERVER # are going towards an POSTGRES_SERVER port
|
||||
22: # position 22 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
src_port: ARP # are emitted from the ARP port
|
||||
dst_port: ARP # are going towards an ARP port
|
||||
23: # position 23 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
protocol: ICMP # are ICMP
|
||||
|
||||
|
||||
``dmz_inbound_acl``
|
||||
"""""""""""""""""""
|
||||
|
||||
ACL rules for packets that have a destination IP address in what is considered the DMZ network.
|
||||
|
||||
example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
nodes:
|
||||
- ref: firewall
|
||||
...
|
||||
acl:
|
||||
dmz_inbound_acl:
|
||||
19: # position 19 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
src_port: POSTGRES_SERVER # are emitted from the POSTGRES_SERVER port
|
||||
dst_port: POSTGRES_SERVER # are going towards an POSTGRES_SERVER port
|
||||
20: # position 20 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
src_port: HTTP # are emitted from the HTTP port
|
||||
dst_port: HTTP # are going towards an HTTP port
|
||||
21: # position 21 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
src_port: HTTPS # are emitted from the HTTPS port
|
||||
dst_port: HTTPS # are going towards an HTTPS port
|
||||
22: # position 22 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
src_port: ARP # are emitted from the ARP port
|
||||
dst_port: ARP # are going towards an ARP port
|
||||
23: # position 23 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
protocol: ICMP # are ICMP
|
||||
|
||||
``dmz_outbound_acl``
|
||||
""""""""""""""""""""
|
||||
|
||||
ACL rules for packets that have a source IP address in what is considered the DMZ network and is going towards the internal network or the external network.
|
||||
|
||||
example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
nodes:
|
||||
- ref: firewall
|
||||
...
|
||||
acl:
|
||||
dmz_outbound_acl:
|
||||
19: # position 19 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
src_port: POSTGRES_SERVER # are emitted from the POSTGRES_SERVER port
|
||||
dst_port: POSTGRES_SERVER # are going towards an POSTGRES_SERVER port
|
||||
20: # position 20 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
src_port: HTTP # are emitted from the HTTP port
|
||||
dst_port: HTTP # are going towards an HTTP port
|
||||
21: # position 21 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
src_port: HTTPS # are emitted from the HTTPS port
|
||||
dst_port: HTTPS # are going towards an HTTPS port
|
||||
22: # position 22 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
src_port: ARP # are emitted from the ARP port
|
||||
dst_port: ARP # are going towards an ARP port
|
||||
23: # position 23 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
protocol: ICMP # are ICMP
|
||||
|
||||
|
||||
|
||||
``external_inbound_acl``
|
||||
""""""""""""""""""""""""
|
||||
|
||||
Optional. By default, this will allow any traffic through.
|
||||
|
||||
ACL rules for packets that have a destination IP address in what is considered the external network.
|
||||
|
||||
example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
nodes:
|
||||
- ref: firewall
|
||||
...
|
||||
acl:
|
||||
external_inbound_acl:
|
||||
21: # position 19 on ACL list
|
||||
action: DENY # deny packets that
|
||||
src_port: POSTGRES_SERVER # are emitted from the POSTGRES_SERVER port
|
||||
dst_port: POSTGRES_SERVER # are going towards an POSTGRES_SERVER port
|
||||
22: # position 22 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
src_port: ARP # are emitted from the ARP port
|
||||
dst_port: ARP # are going towards an ARP port
|
||||
23: # position 23 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
protocol: ICMP # are ICMP
|
||||
|
||||
``external_outbound_acl``
|
||||
"""""""""""""""""""""""""
|
||||
|
||||
Optional. By default, this will allow any traffic through.
|
||||
|
||||
ACL rules for packets that have a source IP address in what is considered the external network and is going towards the DMZ network or the internal network.
|
||||
|
||||
example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
nodes:
|
||||
- ref: firewall
|
||||
...
|
||||
acl:
|
||||
external_outbound_acl:
|
||||
22: # position 22 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
src_port: ARP # are emitted from the ARP port
|
||||
dst_port: ARP # are going towards an ARP port
|
||||
23: # position 23 on ACL list
|
||||
action: PERMIT # allow packets that
|
||||
protocol: ICMP # are ICMP
|
||||
|
||||
.. include:: common/common_network_node_attributes.rst
|
||||
|
||||
.. |NODE| replace:: firewall
|
||||
.. |NODE_TYPE| replace:: ``firewall``
|
||||
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 199 KiB |
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 65 KiB |
@@ -1,127 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
.. _router_configuration:
|
||||
|
||||
``router``
|
||||
==========
|
||||
|
||||
A basic representation of a network router within the simulation.
|
||||
|
||||
See :py:mod:`primaite.simulator.network.hardware.nodes.network.router.Router`
|
||||
|
||||
example router
|
||||
--------------
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
simulation:
|
||||
network:
|
||||
nodes:
|
||||
- ref: router_1
|
||||
hostname: router_1
|
||||
type: router
|
||||
num_ports: 5
|
||||
ports:
|
||||
...
|
||||
acl:
|
||||
...
|
||||
|
||||
.. include:: common/common_node_attributes.rst
|
||||
|
||||
.. include:: common/node_type_list.rst
|
||||
|
||||
``num_ports``
|
||||
-------------
|
||||
|
||||
Optional. Default value is ``5``.
|
||||
|
||||
The number of ports the router will have.
|
||||
|
||||
``ports``
|
||||
---------
|
||||
|
||||
Sets up the router's ports with an IP address and a subnet mask.
|
||||
|
||||
Example of setting ports for a router with 2 ports:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
nodes:
|
||||
- ref: router_1
|
||||
...
|
||||
ports:
|
||||
1:
|
||||
ip_address: 192.168.1.1
|
||||
subnet_mask: 255.255.255.0
|
||||
2:
|
||||
ip_address: 192.168.10.1
|
||||
subnet_mask: 255.255.255.0
|
||||
|
||||
``ip_address``
|
||||
""""""""""""""
|
||||
|
||||
The IP address for the given port. This must be a valid octet i.e. in the range of ``0.0.0.0`` and ``255.255.255.255``.
|
||||
|
||||
``subnet_mask``
|
||||
"""""""""""""""
|
||||
|
||||
Optional. Default value is ``255.255.255.0``.
|
||||
|
||||
The subnet mask setting for the port.
|
||||
|
||||
``acl``
|
||||
-------
|
||||
|
||||
Sets up the ACL rules for the router.
|
||||
|
||||
e.g.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
nodes:
|
||||
- ref: router_1
|
||||
...
|
||||
acl:
|
||||
1:
|
||||
action: PERMIT
|
||||
src_port: ARP
|
||||
dst_port: ARP
|
||||
2:
|
||||
action: PERMIT
|
||||
protocol: ICMP
|
||||
|
||||
See :py:mod:`primaite.simulator.network.hardware.nodes.network.router.AccessControlList`
|
||||
|
||||
See :ref:`List of Ports <List of Ports>` for a list of ports.
|
||||
|
||||
``action``
|
||||
""""""""""
|
||||
|
||||
Available options are
|
||||
|
||||
- ``PERMIT`` : Allows the specified ``protocol`` or ``src_port`` and ``dst_port`` pairs
|
||||
- ``DENY`` : Blocks the specified ``protocol`` or ``src_port`` and ``dst_port`` pairs
|
||||
|
||||
``src_port``
|
||||
""""""""""""
|
||||
|
||||
Is used alongside ``dst_port``. Specifies the port where a packet originates. Used by the ACL Rule to determine if a packet with a specific source port is allowed to pass through the network node.
|
||||
|
||||
``dst_port``
|
||||
""""""""""""
|
||||
|
||||
Is used alongside ``src_port``. Specifies the port where a packet is destined to arrive. Used by the ACL Rule to determine if a packet with a specific destination port is allowed to pass through the network node.
|
||||
|
||||
``protocol``
|
||||
""""""""""""
|
||||
|
||||
Specifies which protocols are allowed by the ACL Rule to pass through the network node.
|
||||
|
||||
See :ref:`List of IPProtocols <List of IPProtocols>` for a list of protocols.
|
||||
|
||||
.. include:: common/common_network_node_attributes.rst
|
||||
|
||||
.. |NODE| replace:: router
|
||||
.. |NODE_TYPE| replace:: ``router``
|
||||
@@ -1,41 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
.. _server_configuration:
|
||||
|
||||
``server``
|
||||
==========
|
||||
|
||||
A basic representation of a server within the simulation.
|
||||
|
||||
See :py:mod:`primaite.simulator.network.hardware.nodes.host.server.Server`
|
||||
|
||||
example server
|
||||
--------------
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
simulation:
|
||||
network:
|
||||
nodes:
|
||||
- ref: server_1
|
||||
hostname: server_1
|
||||
type: server
|
||||
ip_address: 192.168.10.10
|
||||
subnet_mask: 255.255.255.0
|
||||
default_gateway: 192.168.10.1
|
||||
dns_server: 192.168.1.10
|
||||
applications:
|
||||
...
|
||||
services:
|
||||
...
|
||||
|
||||
.. include:: common/common_node_attributes.rst
|
||||
|
||||
.. include:: common/node_type_list.rst
|
||||
|
||||
.. include:: common/common_host_node_attributes.rst
|
||||
|
||||
.. |NODE| replace:: server
|
||||
.. |NODE_TYPE| replace:: ``server``
|
||||
@@ -1,39 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
.. _switch_configuration:
|
||||
|
||||
``switch``
|
||||
==========
|
||||
|
||||
A basic representation of a network switch within the simulation.
|
||||
|
||||
See :py:mod:`primaite.simulator.network.hardware.nodes.network.switch.Switch`
|
||||
|
||||
example switch
|
||||
--------------
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
simulation:
|
||||
network:
|
||||
nodes:
|
||||
- ref: switch_1
|
||||
hostname: switch_1
|
||||
type: switch
|
||||
num_ports: 8
|
||||
|
||||
.. include:: common/common_node_attributes.rst
|
||||
|
||||
.. include:: common/node_type_list.rst
|
||||
|
||||
``num_ports``
|
||||
-------------
|
||||
|
||||
Optional. Default value is ``8``.
|
||||
|
||||
The number of ports the switch will have.
|
||||
|
||||
.. |NODE| replace:: switch
|
||||
.. |NODE_TYPE| replace:: ``switch``
|
||||
@@ -1,25 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
``applications``
|
||||
----------------
|
||||
|
||||
List of available applications that can be installed on a |NODE| can be found in :ref:`List of Applications <List of Applications>`
|
||||
|
||||
application in configuration
|
||||
""""""""""""""""""""""""""""
|
||||
|
||||
Applications takes a list of applications as shown in the example below.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- ref: client_1
|
||||
hostname: client_1
|
||||
type: computer
|
||||
...
|
||||
applications:
|
||||
- ref: example_application
|
||||
type: example_application_type
|
||||
options:
|
||||
# this section is different for each application
|
||||
@@ -1,25 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
``services``
|
||||
------------
|
||||
|
||||
List of available services that can be installed on a |NODE| can be found in :ref:`List of Services <List of Services>`
|
||||
|
||||
services in configuration
|
||||
"""""""""""""""""""""""""
|
||||
|
||||
Services takes a list of services as shown in the example below.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- ref: client_1
|
||||
hostname: client_1
|
||||
type: computer
|
||||
...
|
||||
applications:
|
||||
- ref: example_service
|
||||
type: example_service_type
|
||||
options:
|
||||
# this section is different for each service
|
||||
142
docs/source/custom_agent.rst
Normal file
@@ -0,0 +1,142 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2023, Defence Science and Technology Laboratory UK
|
||||
|
||||
Custom Agents
|
||||
=============
|
||||
|
||||
|
||||
Integrating a user defined blue agent
|
||||
*************************************
|
||||
|
||||
.. note::
|
||||
|
||||
If you are planning to implement custom RL agents into PrimAITE, you must use the project as a repository. If you install PrimAITE as a python package from wheel, custom agents are not supported.
|
||||
|
||||
PrimAITE has integration with Ray RLLib and StableBaselines3 agents. All agents interface with PrimAITE through an :py:class:`primaite.agents.agent.AgentSessionABC<Agent Session>` which provides Input/Output of agent savefiles, as well as capturing and plotting performance metrics during training and evaluation. If you wish to integrate a custom blue agent, it is recommended to create a subclass of the :py:class:`primaite.agents.agent.AgentSessionABC` and implement the ``__init__()``, ``_setup()``, ``_save_checkpoint()``, ``learn()``, ``evaluate()``, ``_get_latest_checkpoint``, ``load()``, and ``save()`` methods.
|
||||
|
||||
Below is a barebones example of a custom agent implementation:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# src/primaite/agents/my_custom_agent.py
|
||||
|
||||
from primaite.agents.agent import AgentSessionABC
|
||||
from primaite.common.enums import AgentFramework, AgentIdentifier
|
||||
|
||||
class CustomAgent(AgentSessionABC):
|
||||
def __init__(self, training_config_path, lay_down_config_path):
|
||||
super().__init__(training_config_path, lay_down_config_path)
|
||||
assert self._training_config.agent_framework == AgentFramework.CUSTOM
|
||||
assert self._training_config.agent_identifier == AgentIdentifier.MY_AGENT
|
||||
self._setup()
|
||||
|
||||
def _setup(self):
|
||||
super()._setup()
|
||||
self._env = Primaite(
|
||||
training_config_path=self._training_config_path,
|
||||
lay_down_config_path=self._lay_down_config_path,
|
||||
session_path=self.session_path,
|
||||
timestamp_str=self.timestamp_str,
|
||||
)
|
||||
self._agent = ... # your code to setup agent
|
||||
|
||||
def _save_checkpoint(self):
|
||||
checkpoint_num = self._training_config.checkpoint_every_n_episodes
|
||||
episode_count = self._env.episode_count
|
||||
save_checkpoint = False
|
||||
if checkpoint_num:
|
||||
save_checkpoint = episode_count % checkpoint_num == 0
|
||||
# saves checkpoint if the episode count is not 0 and save_checkpoint flag was set to true
|
||||
if episode_count and save_checkpoint:
|
||||
...
|
||||
# your code to save checkpoint goes here.
|
||||
# The path should start with self.checkpoints_path and include the episode number.
|
||||
|
||||
def learn(self):
|
||||
...
|
||||
# call your agent's learning function here.
|
||||
|
||||
super().learn() # this will finalise learning and output session metadata
|
||||
self.save()
|
||||
|
||||
def evaluate(self):
|
||||
...
|
||||
# call your agent's evaluation function here.
|
||||
|
||||
self._env.close()
|
||||
super().evaluate()
|
||||
|
||||
def _get_latest_checkpoint(self):
|
||||
...
|
||||
# Load an agent from file.
|
||||
|
||||
@classmethod
|
||||
def load(cls, path):
|
||||
...
|
||||
# Create a CustomAgent object which loads model weights from file.
|
||||
|
||||
def save(self):
|
||||
...
|
||||
# Call your agent's function that saves it to a file
|
||||
|
||||
|
||||
You will also need to modify :py:class:`primaite.primaite_session.PrimaiteSession<PrimaiteSession>` and :py:mod:`primaite.common.enums` to capture your new agent identifiers.
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 17, 18
|
||||
|
||||
# src/primaite/common/enums.py
|
||||
|
||||
class AgentIdentifier(Enum):
|
||||
"""The Red Agent algo/class."""
|
||||
A2C = 1
|
||||
"Advantage Actor Critic"
|
||||
PPO = 2
|
||||
"Proximal Policy Optimization"
|
||||
HARDCODED = 3
|
||||
"The Hardcoded agents"
|
||||
DO_NOTHING = 4
|
||||
"The DoNothing agents"
|
||||
RANDOM = 5
|
||||
"The RandomAgent"
|
||||
DUMMY = 6
|
||||
"The DummyAgent"
|
||||
CUSTOM_AGENT = 7
|
||||
"Your custom agent"
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 3, 11, 12
|
||||
|
||||
# src/primaite_session.py
|
||||
|
||||
from primaite.agents.my_custom_agent import CustomAgent
|
||||
|
||||
# ...
|
||||
|
||||
def setup(self):
|
||||
"""Performs the session setup."""
|
||||
if self._training_config.agent_framework == AgentFramework.CUSTOM:
|
||||
_LOGGER.debug(f"PrimaiteSession Setup: Agent Framework = {AgentFramework.CUSTOM}")
|
||||
if self._training_config.agent_identifier == AgentIdentifier.CUSTOM_AGENT:
|
||||
self._agent_session = CustomAgent(self._training_config_path, self._lay_down_config_path)
|
||||
if self._training_config.agent_identifier == AgentIdentifier.HARDCODED:
|
||||
_LOGGER.debug(f"PrimaiteSession Setup: Agent Identifier =" f" {AgentIdentifier.HARDCODED}")
|
||||
if self._training_config.action_type == ActionType.NODE:
|
||||
# Deterministic Hardcoded Agent with Node Action Space
|
||||
self._agent_session = HardCodedNodeAgent(self._training_config_path, self._lay_down_config_path)
|
||||
|
||||
Finally, specify your agent in your training config.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# ~/primaite/2.0.0/config/path/to/your/config_main.yaml
|
||||
|
||||
# Training Config File
|
||||
|
||||
agent_framework: CUSTOM
|
||||
agent_identifier: CUSTOM_AGENT
|
||||
random_red_agent: False
|
||||
# ...
|
||||
|
||||
Now you can :ref:`run a primaite session<run a primaite session>` with your custom agent by passing in the custom ``config_main``.
|
||||
@@ -1,8 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
Customising Agents
|
||||
******************
|
||||
|
||||
For an example of how to customise red agent behaviour in the Data Manipulation scenario, please refer to the notebook ``Data-Manipulation-Customising-Red-Agent.ipynb``.
|
||||
@@ -1,12 +1,10 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
© Crown-owned copyright 2023, Defence Science and Technology Laboratory UK
|
||||
|
||||
.. role:: raw-html(raw)
|
||||
:format: html
|
||||
|
||||
.. _Dependencies:
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
.. _Developer Tools:
|
||||
|
||||
Developer Tools
|
||||
***************
|
||||
|
||||
PrimAITE includes developer CLI tools that are intended to be used by developers.
|
||||
|
||||
dev-mode
|
||||
========
|
||||
|
||||
The dev-mode contains configuration which override any of the config files during runtime.
|
||||
|
||||
This is intended to make debugging easier by removing the need to find the relevant configuration file/settings.
|
||||
|
||||
Enabling dev-mode
|
||||
-----------------
|
||||
|
||||
The PrimAITE dev-mode can be enabled via the use of
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode enable
|
||||
|
||||
Disabling dev-mode
|
||||
------------------
|
||||
|
||||
The PrimAITE dev-mode can be disabled via the use of
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode disable
|
||||
|
||||
Show current mode
|
||||
-----------------
|
||||
|
||||
To show if the dev-mode is enabled or not, use
|
||||
The PrimAITE dev-mode can be disabled via the use of
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode show
|
||||
|
||||
dev-mode configuration
|
||||
======================
|
||||
|
||||
The following configures some specific items that the dev-mode overrides, if enabled.
|
||||
|
||||
`--sys-log-level` or `-level`
|
||||
----------------------------
|
||||
|
||||
The level of system logs can be overridden by dev-mode.
|
||||
|
||||
By default, this is set to DEBUG
|
||||
|
||||
The available options are [DEBUG|INFO|WARNING|ERROR|CRITICAL]
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode config -level INFO
|
||||
|
||||
or
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode config --sys-log-level INFO
|
||||
|
||||
`--output-sys-logs` or `-sys`
|
||||
-----------------------------
|
||||
|
||||
The outputting of system logs can be overridden by dev-mode.
|
||||
|
||||
By default, this is set to False
|
||||
|
||||
Enabling system logs
|
||||
""""""""""""""""""""
|
||||
|
||||
To enable outputting of system logs
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode config --output-sys-logs
|
||||
|
||||
or
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode config -sys
|
||||
|
||||
Disabling system logs
|
||||
"""""""""""""""""""""
|
||||
|
||||
To disable outputting of system logs
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode config --no-sys-logs
|
||||
|
||||
or
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode config -nsys
|
||||
|
||||
`--output-pcap-logs` or `-pcap`
|
||||
-------------------------------
|
||||
|
||||
The outputting of packet capture logs can be overridden by dev-mode.
|
||||
|
||||
By default, this is set to False
|
||||
|
||||
Enabling PCAP logs
|
||||
""""""""""""""""""
|
||||
|
||||
To enable outputting of packet capture logs
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode config --output-pcap-logs
|
||||
|
||||
or
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode config -pcap
|
||||
|
||||
Disabling PCAP logs
|
||||
"""""""""""""""""""
|
||||
|
||||
To disable outputting of packet capture logs
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode config --no-pcap-logs
|
||||
|
||||
or
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode config -npcap
|
||||
|
||||
`--output-to-terminal` or `-t`
|
||||
------------------------------
|
||||
|
||||
The outputting of system logs to the terminal can be overridden by dev-mode.
|
||||
|
||||
By default, this is set to False
|
||||
|
||||
Enabling system log output to terminal
|
||||
""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
To enable outputting of system logs to terminal
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode config --output-to-terminal
|
||||
|
||||
or
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode config -t
|
||||
|
||||
Disabling system log output to terminal
|
||||
"""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
To disable outputting of system logs to terminal
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode config --no-terminal
|
||||
|
||||
or
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode config -nt
|
||||
|
||||
path
|
||||
----
|
||||
|
||||
PrimAITE dev-mode can override where sessions are output.
|
||||
|
||||
By default, PrimAITE will output the sessions in USER_HOME/primaite/sessions
|
||||
|
||||
With dev-mode enabled, by default, this will be changed to PRIMAITE_REPOSITORY_ROOT/sessions
|
||||
|
||||
However, providing a path will let dev-mode output sessions to the given path e.g.
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: Unix
|
||||
|
||||
primaite dev-mode config path ~/output/path
|
||||
|
||||
.. code-block:: powershell
|
||||
:caption: Windows (Powershell)
|
||||
|
||||
primaite dev-mode config path ~\output\path
|
||||
|
||||
default path
|
||||
""""""""""""
|
||||
|
||||
To reset the path to use the PRIMAITE_REPOSITORY_ROOT/sessions, run the command
|
||||
|
||||
.. code-block::
|
||||
|
||||
primaite dev-mode config path --default
|
||||
@@ -1,14 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
RL Environments
|
||||
***************
|
||||
|
||||
RL environments are the objects that directly interface with RL libraries such as Stable-Baselines3 and Ray RLLib. The PrimAITE simulation is exposed via three different environment APIs:
|
||||
|
||||
* Gymnasium API - this is the standard interface that works with many RL libraries like SB3, Ray, Tianshou, etc. ``PrimaiteGymEnv`` adheres to the `Official Gymnasium documentation <https://gymnasium.farama.org/api/env/>`_.
|
||||
* Ray Single agent API - For training a single Ray RLLib agent
|
||||
* Ray MARL API - For training multi-agent systems with Ray RLLib. ``PrimaiteRayMARLEnv`` adheres to the `Official Ray documentation <https://docs.ray.io/en/latest/rllib/package_ref/env/multi_agent_env.html>`_.
|
||||
|
||||
There are Jupyter notebooks which demonstrate integration with each of these three environments. They are located in ``~/primaite/<VERSION>/notebooks/example_notebooks``.
|
||||
@@ -1,82 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
Example Jupyter Notebooks
|
||||
=========================
|
||||
|
||||
Executed Notebooks
|
||||
------------------
|
||||
|
||||
There are a few example notebooks included which help with the understanding of PrimAITE's capabilities.
|
||||
|
||||
The PrimAITE documentation includes a pre executed example of notebooks. See :ref:`Executed Notebooks`.
|
||||
|
||||
In order to run the notebooks interactively, :ref:`install PrimAITE <getting-started>` and follow these steps:
|
||||
|
||||
Running Jupyter Notebooks
|
||||
-------------------------
|
||||
|
||||
1. Navigate to the PrimAITE directory
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: Unix
|
||||
|
||||
cd ~/primaite/{VERSION}
|
||||
|
||||
.. code-block:: powershell
|
||||
:caption: Windows (Powershell)
|
||||
|
||||
cd ~\primaite\{VERSION}
|
||||
|
||||
2. Run jupyter notebook (the python environment to which you installed PrimAITE must be active)
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: Unix
|
||||
|
||||
jupyter notebook
|
||||
|
||||
.. code-block:: powershell
|
||||
:caption: Windows (Powershell)
|
||||
|
||||
jupyter notebook
|
||||
|
||||
3. Opening the jupyter webpage (optional)
|
||||
|
||||
The default web browser may automatically open the webpage. However, if that is not the case, click the link shown in your command prompt output. It should look like this: ``http://localhost:8888/?token=0123456798abc0123456789abc``
|
||||
|
||||
|
||||
4. Navigate to the list of notebooks
|
||||
|
||||
The example notebooks are located in ``notebooks/example_notebooks/``. The file system shown in the jupyter webpage is relative to the location in which the ``jupyter notebook`` command was used.
|
||||
|
||||
|
||||
Running Jupyter Notebooks via VSCode
|
||||
------------------------------------
|
||||
|
||||
It is also possible to view the Jupyter notebooks within VSCode.
|
||||
|
||||
The best place to start is by opening a notebook file (.ipynb) in VSCode. If using VSCode to view a notebook for the first time, follow the steps below.
|
||||
|
||||
Installing extensions
|
||||
"""""""""""""""""""""
|
||||
|
||||
VSCode may need some extensions to be installed if not already done.
|
||||
To do this, press the "Select Kernel" button on the top right.
|
||||
|
||||
This should open a dialog which has the option to install python and jupyter extensions.
|
||||
|
||||
.. image:: ../../_static/notebooks/install_extensions.png
|
||||
:width: 700
|
||||
:align: center
|
||||
:alt: :: The top dialog option that appears will automatically install the extensions
|
||||
|
||||
The following extensions should now be installed
|
||||
|
||||
.. image:: ../../_static/notebooks/extensions.png
|
||||
:width: 300
|
||||
:align: center
|
||||
|
||||
VSCode will then ask for a Python environment version to use. PrimAITE is compatible with Python versions 3.9 - 3.11
|
||||
|
||||
You should now be able to interact with the notebook.
|
||||
@@ -1,90 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
PrimAITE Game layer
|
||||
*******************
|
||||
|
||||
The Primaite codebase consists of two main modules:
|
||||
|
||||
* ``simulator``: The simulation logic including the network topology, the network state, and behaviour of various hardware and software classes.
|
||||
* ``game``: The agent-training infrastructure which helps reinforcement learning agents interface with the simulation. This includes the observation, action, and rewards, for RL agents, but also scripted deterministic agents. The game layer orchestrates all the interactions between modules.
|
||||
|
||||
The simulator and game layer communicate using the PrimAITE State API and the PrimAITE Request API.
|
||||
|
||||
The game layer is responsible for managing agents and getting them to interface with the simulator correctly. It consists of several components:
|
||||
|
||||
|
||||
Agents
|
||||
======
|
||||
|
||||
All agents inherit from the :py:class:`primaite.game.agent.interface.AbstractAgent` class, which mandates that they have an ObservationManager, ActionManager, and RewardManager. The agent behaviour depends on the type of agent, but there are two main types:
|
||||
|
||||
* RL agents action during each step is decided by an appropriate RL algorithm. The agent within PrimAITE just acts to format and forward actions decided by an RL policy.
|
||||
* Deterministic agents perform all of their decision making within the PrimAITE game layer. They typically have a scripted policy which always performs the same action or a rule-based policy which performs actions based on the current state of the simulation. They can have a stochastic element, and their seed is settable.
|
||||
|
||||
|
||||
Observations
|
||||
============
|
||||
|
||||
An agent's observations are managed by the ``ObservationManager`` class. It generates observations based on the current simulation state dictionary. It also provides the observation space during initial setup. The data is formatted so it's compatible with ``Gymnasium.spaces``. Observation spaces are composed of one or more components which are defined by the ``AbstractObservation`` base class.
|
||||
|
||||
Actions
|
||||
=======
|
||||
|
||||
An agent's actions are managed by the ``ActionManager``. It converts actions selected by agents (which are typically integers chosen from a ``gymnasium.spaces.Discrete`` space) into simulation-friendly requests. It also provides the action space during initial setup. Action spaces are composed of one or more components which are defined by the ``AbstractAction`` base class.
|
||||
|
||||
Rewards
|
||||
=======
|
||||
|
||||
An agent's reward function is managed by the ``RewardManager``. It calculates rewards based on the simulation state (in a way similar to observations). Rewards can be defined as a weighted sum of small reward components. For example, an agents reward can be based on the uptime of a database service plus the loss rate of packets between clients and a web server.
|
||||
|
||||
Reward Components
|
||||
-----------------
|
||||
|
||||
Currently implemented are reward components tailored to the data manipulation scenario. View the full API and description of how they work here: :py:module:`primaite.game.agent.reward`.
|
||||
|
||||
Reward Sharing
|
||||
--------------
|
||||
|
||||
An agent's reward can be based on rewards of other agents. This is particularly useful for modelling a situation where the blue agent's job is to protect the ability of green agents to perform their pattern-of-life. This can be configured in the YAML file this way:
|
||||
|
||||
```yaml
|
||||
green_agent_1: # this agent sometimes tries to access the webpage, and sometimes the database
|
||||
# actions, observations, and agent settings go here
|
||||
reward_function:
|
||||
reward_components:
|
||||
|
||||
# When the webpage loads, the reward goes up by 0.25 when it fails to load, it goes down to -0.25
|
||||
- type: WEBPAGE_UNAVAILABLE_PENALTY
|
||||
weight: 0.25
|
||||
options:
|
||||
node_hostname: client_2
|
||||
|
||||
# When the database is reachable, the reward goes up by 0.05, when it is unreachable it goes down to -0.05
|
||||
- type: GREEN_ADMIN_DATABASE_UNREACHABLE_PENALTY
|
||||
weight: 0.05
|
||||
options:
|
||||
node_hostname: client_2
|
||||
|
||||
blue_agent:
|
||||
# actions, observations, and agent settings go here
|
||||
reward_function:
|
||||
reward_components:
|
||||
|
||||
# When the database file is in a good state, blue's reward is 0.4, when it's in a corrupted state the reward is -0.4
|
||||
- type: DATABASE_FILE_INTEGRITY
|
||||
weight: 0.40
|
||||
options:
|
||||
node_hostname: database_server
|
||||
folder_name: database
|
||||
file_name: database.db
|
||||
|
||||
# The green's reward is added onto the blue's reward.
|
||||
- type: SHARED_REWARD
|
||||
weight: 1.0
|
||||
options:
|
||||
agent_name: client_2_green_user
|
||||
```
|
||||
|
||||
When defining agent reward sharing, users must be careful to avoid circular references, as that would lead to an infinite calculation loop. PrimAITE will prevent circular dependencies and provide a helpful error message if they are detected in the yaml.
|
||||
@@ -1,6 +1,6 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
© Crown-owned copyright 2023, Defence Science and Technology Laboratory UK
|
||||
|
||||
.. _getting-started:
|
||||
|
||||
@@ -11,7 +11,7 @@ Getting Started
|
||||
|
||||
Pre-Requisites
|
||||
|
||||
In order to get **PrimAITE** installed, you will need Python, venv, and pip. If you don't already have them, this is how to install it:
|
||||
In order to get **PrimAITE** installed, you will need to have a python version between 3.8 and 3.10 installed. If you don't already have it, this is how to install it:
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
@@ -30,42 +30,43 @@ In order to get **PrimAITE** installed, you will need Python, venv, and pip. If
|
||||
|
||||
**PrimAITE** is designed to be OS-agnostic, and thus should work on most variations/distros of Linux, Windows, and MacOS.
|
||||
|
||||
Installing PrimAITE has been tested with all supported python versions, venv 20.24.1, and pip 23.
|
||||
|
||||
Install PrimAITE
|
||||
****************
|
||||
|
||||
1. Create a directory for your PrimAITE project:
|
||||
1. Create a primaite directory in your home directory:
|
||||
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: Unix
|
||||
|
||||
mkdir -p ~/primaite/{VERSION}
|
||||
mkdir ~/primaite/2.0.0
|
||||
|
||||
.. code-block:: powershell
|
||||
:caption: Windows (Powershell)
|
||||
|
||||
mkdir ~\primaite\{VERSION}
|
||||
|
||||
mkdir ~\primaite\2.0.0
|
||||
|
||||
2. Navigate to the primaite directory and create a new python virtual environment (venv)
|
||||
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: Unix
|
||||
|
||||
cd ~/primaite/{VERSION}
|
||||
cd ~/primaite/2.0.0
|
||||
python3 -m venv .venv
|
||||
|
||||
.. code-block:: powershell
|
||||
:caption: Windows (Powershell)
|
||||
|
||||
cd ~\primaite\{VERSION}
|
||||
cd ~\primaite\2.0.0
|
||||
python3 -m venv .venv
|
||||
attrib +h .venv /s /d # Hides the .venv directory
|
||||
|
||||
|
||||
3. Activate the venv
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: Unix
|
||||
|
||||
@@ -77,20 +78,22 @@ Install PrimAITE
|
||||
.\.venv\Scripts\activate
|
||||
|
||||
|
||||
4. Install PrimAITE from your saved wheel file
|
||||
4. Install PrimAITE using pip from PyPi
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: Unix
|
||||
|
||||
pip install path/to/your/primaite.whl[rl]
|
||||
pip install primaite
|
||||
|
||||
.. code-block:: powershell
|
||||
:caption: Windows (Powershell)
|
||||
|
||||
pip install path\to\your\primaite.whl
|
||||
pip install primaite
|
||||
|
||||
5. Perform the PrimAITE setup
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: Unix
|
||||
|
||||
@@ -107,16 +110,15 @@ Clone & Install PrimAITE for Development
|
||||
To be able to extend PrimAITE further, or to build wheels manually before install, clone the repository to a location
|
||||
of your choice:
|
||||
|
||||
1. Clone the repository.
|
||||
|
||||
For example:
|
||||
.. TODO:: Add repo path once we know what it is
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone https://github.com/{todo:fill in URL}/PrimAITE
|
||||
git clone <repo path>
|
||||
cd primaite
|
||||
|
||||
2. Create and activate your Python virtual environment (venv)
|
||||
Create and activate your Python virtual environment (venv)
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: Unix
|
||||
@@ -130,44 +132,18 @@ For example:
|
||||
python3 -m venv venv
|
||||
.\venv\Scripts\activate
|
||||
|
||||
3. Install PrimAITE with the dev extra
|
||||
Install PrimAITE with the dev extra
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: Unix
|
||||
|
||||
pip install -e .[dev,rl]
|
||||
pip install -e .[dev]
|
||||
|
||||
.. code-block:: powershell
|
||||
:caption: Windows (Powershell)
|
||||
|
||||
pip install -e .[dev,rl]
|
||||
pip install -e .[dev]
|
||||
|
||||
|
||||
To view the complete list of packages installed during PrimAITE installation, go to the dependencies page (:ref:`Dependencies`).
|
||||
|
||||
4. Set PrimAITE to run on development mode
|
||||
|
||||
Running step 3 should have installed PrimAITE, verify this by running
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: Unix
|
||||
|
||||
primaite setup
|
||||
|
||||
.. code-block:: powershell
|
||||
:caption: Windows (Powershell)
|
||||
|
||||
primaite setup
|
||||
|
||||
To set PrimAITE to run in development mode:
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: Unix
|
||||
|
||||
primaite dev-mode enable
|
||||
|
||||
.. code-block:: powershell
|
||||
:caption: Windows (Powershell)
|
||||
|
||||
primaite dev-mode enable
|
||||
|
||||
More information about :ref:`Developer Tools`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
© Crown-owned copyright 2023, Defence Science and Technology Laboratory UK
|
||||
|
||||
Glossary
|
||||
=============
|
||||
@@ -38,11 +38,14 @@ Glossary
|
||||
Blue Agent
|
||||
A defensive agent that protects the network from Red Agent attacks to minimise disruption to green agents and protect data.
|
||||
|
||||
Information Exchange Requirement (IER)
|
||||
Simulates network traffic by sending data from one network node to another via links for a specified amount of time. IERs can be part of green agent behaviour or red agent behaviour. PrimAITE can be configured to apply a penalty for green agents' IERs being blocked and a reward for red agents' IERs being blocked.
|
||||
|
||||
Pattern-of-Life (PoL)
|
||||
PoLs allow agents to change the current hardware, OS, file system, or service statuses of nodes during the course of an episode. For example, a green agent may restart a server node to represent scheduled maintainance. A red agent's Pattern-of-Life can be used to attack nodes by changing their states to CORRUPTED or COMPROMISED.
|
||||
|
||||
Reward
|
||||
The reward is a single number used by the blue agent to understand whether it's performing well or poorly. RL agents change their behaviour in an attempt to increase the expected reward each episode. The reward is generated based on the current states of the environment and is impacted positively by things like green PoL running successfully and negatively by things like nodes being compromised.
|
||||
The reward is a single number used by the blue agent to understand whether it's performing well or poorly. RL agents change their behaviour in an attempt to increase the expected reward each episode. The reward is generated based on the current states of the environment / :term:`reference environment` and is impacted positively by things like green IERS running successfully and negatively by things like nodes being compromised.
|
||||
|
||||
Observation
|
||||
An observation is a representation of the current state of the environment that is given to the learning agent so it can decide on which action to perform. If the environment is 'fully observable', the observation contains information about every possible aspect of the environment. More commonly, the environment is 'partially observable' which means the learning agent has to make decisions without knowing every detail of the current environment state.
|
||||
@@ -62,11 +65,17 @@ Glossary
|
||||
Episode
|
||||
When an episode starts, the network simulation is reset to an initial state. The agents take actions on each step of the episode until it reaches a terminal state, which usually happens after a predetermined number of steps. After the terminal state is reached, a new episode starts and the RL agent has another opportunity to protect the network.
|
||||
|
||||
Reference environment
|
||||
While the network simulation is unfolding, a parallel simulation takes place which is identical to the main one except that blue and red agent actions are not applied. This reference environment essentially shows what would be happening to the network if there had been no cyberattack or defense. The reference environment is used to calculate rewards.
|
||||
|
||||
Transaction
|
||||
PrimAITE records the decisions of the learning agent by saving its observation, action, and reward at every time step. During each session, this data is saved to disk to allow for full inspection.
|
||||
|
||||
Laydown
|
||||
The laydown is a file which defines the training scenario. It contains the network topology, firewall rules, services, protocols, and details about green and red agent behaviours.
|
||||
|
||||
Gymnasium
|
||||
PrimAITE uses the Gymnasium reinforcement learning framework API to create a training environment and interface with RL agents. Gymnasium defines a common way of creating observations, actions, and rewards.
|
||||
Gym
|
||||
PrimAITE uses the Gym reinforcement learning framework API to create a training environment and interface with RL agents. Gym defines a common way of creating observations, actions, and rewards.
|
||||
|
||||
User app home
|
||||
PrimAITE supports upgrading software version while retaining user data. The user data directory is where configs, notebooks, and results are stored, this location is `~/primaite<version>/` on linux/darwin and `C:\\Users\\<username>\\primaite<version>` on Windows.
|
||||
PrimAITE supports upgrading software version while retaining user data. The user data directory is where configs, notebooks, and results are stored, this location is `~/primaite<version>` on linux/darwin and `C:\Users\<username>\primaite\<version>` on Windows.
|
||||
|
||||
57
docs/source/migration_1.2_-_2.0.rst
Normal file
@@ -0,0 +1,57 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2023, Defence Science and Technology Laboratory UK
|
||||
|
||||
v1.2 to v2.0 Migration guide
|
||||
============================
|
||||
|
||||
**1. Installing PrimAITE**
|
||||
|
||||
Like before, you can install primaite from the repository by running ``pip install -e .``. But, there is now an additional setup step which does several things, like setting up user directories, copy default configs and notebooks, etc. Once you have installed PrimAITE to your virtual environment, run this command to finalise setup.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
primaite setup
|
||||
|
||||
**2. Running a training session**
|
||||
|
||||
In version 1.2 of PrimAITE, the main entry point for training or evaluating agents was the ``src/primaite/main.py`` file. v2.0.0 introduced managed 'sessions' which are responsible for reading configuration files, performing training, and writing outputs.
|
||||
|
||||
``main.py`` file still runs a training session but it now uses the new `PrimaiteSession`, and it now requires you to provide the path to your config files.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python src/primaite/main.py --tc path/to/training-config.yaml --ldc path/to/laydown-config.yaml
|
||||
|
||||
Alternatively, the session can be invoked via the commandline by running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
primaite session --tc path/to/training-config.yaml --ldc path/to/laydown-config.yaml
|
||||
|
||||
**3. Location of configs**
|
||||
|
||||
In version 1.2, training configs and laydown configs were all stored in the project repository under ``src/primaite/config``. Version 2.0.0 introduced user data directories, and now when you install and setup PrimAITE, config files are stored in your user data location. On Linux/OSX, this is stored in ``~/primaite/2.0.0/config``. On Windows, this is stored in ``C:\Users\<your username>\primaite\configs``. Upon first setup, the configs folder is populated with some default yaml files. It is recommended that you store all your custom configuration files here.
|
||||
|
||||
**4. Contents of configs**
|
||||
|
||||
Some things that were previously part of the laydown config are now part of the traning config.
|
||||
|
||||
* Actions
|
||||
|
||||
If you have custom configs which use these, you will need to adapt them by moving the configuration from the laydown config to the training config.
|
||||
|
||||
Also, there are new configurable items in the training config:
|
||||
|
||||
* Observations
|
||||
* Agent framework
|
||||
* Agent
|
||||
* Deep learning framework
|
||||
* random red agents
|
||||
* seed
|
||||
* deterministic
|
||||
* hard coded agent view
|
||||
|
||||
Each of these items have default values which are designed so that PrimAITE has the same behaviour as it did in 1.2.0, so you do not have to specify them.
|
||||
|
||||
ACL Rules in laydown configs have a new required parameter: ``position``. The lower the position, the higher up in the ACL table the rule will placed. If you have custom laydowns, you will need to go through them and add a position to each ACL_RULE.
|
||||
@@ -1,16 +0,0 @@
|
||||
.. only:: comment
|
||||
|
||||
© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
|
||||
.. _Executed Notebooks:
|
||||
|
||||
Executed Jupyter Notebooks
|
||||
==========================
|
||||
|
||||
Below is a list of available pre-executed notebooks.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
**/*
|
||||