Compare commits
604 Commits
monitoring
...
3dc58cf644
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dc58cf644 | ||
|
|
d95477fc3b | ||
| 274ce1fd8a | |||
|
|
eed2fcb7c7 | ||
|
|
266b6c7be1 | ||
| e9924a2524 | |||
|
|
5ee8309d32 | ||
|
|
261f6be7db | ||
|
|
8ea19dbf70 | ||
|
|
e6cb187f8e | ||
|
|
56f19af578 | ||
|
|
a3c92f70bf | ||
|
|
f907acde95 | ||
|
|
53a55e7317 | ||
|
|
2c0db1c7a1 | ||
|
|
39c5fdca69 | ||
|
|
6bfcc76845 | ||
|
|
1af645d272 | ||
|
|
f3a5687adf | ||
|
|
9d6869ad9d | ||
|
|
2cc9370f3d | ||
|
|
60220e18b6 | ||
|
|
b3b925ff77 | ||
|
|
3d8eb1bf1c | ||
|
|
7f8ba8b859 | ||
|
|
aee61d4511 | ||
|
|
9bc29508d7 | ||
|
|
9bfc9384e4 | ||
|
|
152230c100 | ||
|
|
13df80ab43 | ||
|
|
a2123819b3 | ||
|
|
173d00504c | ||
|
|
7cdcc984a5 | ||
|
|
e301770adc | ||
|
|
ab1e32711d | ||
|
|
5c0df8c73c | ||
|
|
5cf4468754 | ||
|
|
bafd76a0b4 | ||
|
|
24735f7e5c | ||
|
|
7867be688a | ||
|
|
03b3ce9dee | ||
|
|
a2994bf55d | ||
|
|
7b44a41da3 | ||
|
|
efaff340a4 | ||
|
|
48536f2615 | ||
|
|
170a31d090 | ||
|
|
aa2730efd5 | ||
|
|
0dbb77b023 | ||
|
|
fee9965d0a | ||
|
|
d0f3ddba0d | ||
|
|
d9e41118f8 | ||
|
|
ad70b3439c | ||
|
|
a04435ee9b | ||
|
|
a2ddb65425 | ||
|
|
a87da82ebd | ||
|
|
7aea88724f | ||
|
|
6455d22752 | ||
|
|
a47b29d49f | ||
|
|
9c969f783d | ||
|
|
081156ecab | ||
|
|
3783ded62a | ||
|
|
5a2246a540 | ||
|
|
ba311a3ec6 | ||
|
|
d1f97ad5ac | ||
|
|
b4bdb63e4a | ||
|
|
b741f9b20b | ||
|
|
a3c1342837 | ||
|
|
d4ff2681ac | ||
|
|
75cb93f25c | ||
|
|
d10255297c | ||
|
|
79edb8f4e1 | ||
|
|
5dc76a8348 | ||
|
|
a76ad3195c | ||
|
|
73ef806dd6 | ||
|
|
628dae06a8 | ||
|
|
c3755aa29e | ||
|
|
782cbe33d1 | ||
|
|
aff792a061 | ||
|
|
aa8e229e64 | ||
|
|
22a020e4c7 | ||
|
|
e879cf73d3 | ||
|
|
423891001c | ||
|
|
dda6b91330 | ||
|
|
265d3f8fd6 | ||
|
|
b61d19cb91 | ||
|
|
00be18b1f1 | ||
|
|
6c7ec507ef | ||
|
|
63b0bc72fe | ||
|
|
02af5d26dc | ||
|
|
3eb38b74bd | ||
|
|
2b95acb8cc | ||
|
|
62e9f13a45 | ||
|
|
4cb87a57ad | ||
|
|
d2eaddfd11 | ||
|
|
0e741aab38 | ||
|
|
9ebd19ab52 | ||
|
|
e47cbf2044 | ||
|
|
ce632e88b9 | ||
|
|
11d8796764 | ||
|
|
d974c75d7c | ||
|
|
a5433dcb5b | ||
|
|
e0eb47f5ce | ||
|
|
a8822f0778 | ||
| bedf87b492 | |||
|
|
b6f7791c98 | ||
|
|
1a48e60afd | ||
|
|
c26b19793b | ||
|
|
dfe81a5c20 | ||
|
|
4afb05e56b | ||
|
|
46b49259d2 | ||
|
|
3f3ce68e18 | ||
|
|
e44805c9b6 | ||
|
|
1aa6b4234a | ||
|
|
4cde540e70 | ||
|
|
69fb5f5641 | ||
|
|
430552a0b1 | ||
|
|
18bb111843 | ||
|
|
712425ee17 | ||
|
|
1d77821e5f | ||
|
|
7ad40bb509 | ||
|
|
3950a2b069 | ||
|
|
d20fd80798 | ||
|
|
308ee553c3 | ||
|
|
56110d52bd | ||
|
|
1f07fdff45 | ||
|
|
317816558d | ||
|
|
ea22e4e407 | ||
|
|
490c483924 | ||
|
|
bc34a1f915 | ||
|
|
64e690737e | ||
|
|
0693fdcd26 | ||
| 19a807899f | |||
| 5bacf9fbca | |||
| 1da9bfd43c | |||
| 0bc9b2e788 | |||
| dcfb6825e8 | |||
| 0b9ac4dc74 | |||
|
|
aabf758c91 | ||
|
|
489b8aeb35 | ||
|
|
002d6799b1 | ||
|
|
150cef1aca | ||
|
|
a30ad99ee4 | ||
|
|
d2b6d95a49 | ||
|
|
adc415e95a | ||
|
|
e8303d5129 | ||
|
|
f37021346b | ||
|
|
5a99928c6f | ||
|
|
59c83c296b | ||
|
|
b6cb031edb | ||
|
|
cb5ffc16d8 | ||
|
|
f375c9567f | ||
|
|
f0400c02b6 | ||
|
|
d1d7331238 | ||
|
|
459dbc5d18 | ||
| a4a68eeb5a | |||
|
|
99958979d6 | ||
|
|
0e4d229df2 | ||
|
|
53883108d8 | ||
|
|
fcbf6ce092 | ||
|
|
cf21863b0f | ||
|
|
653a923fa8 | ||
|
|
13c819e66c | ||
|
|
28a653b203 | ||
|
|
cf7ab7fbe6 | ||
|
|
12d0a75b3a | ||
|
|
668e86d7c2 | ||
| 0fb593a313 | |||
|
|
b5dc130207 | ||
|
|
0efa1e5125 | ||
|
|
34e05725dd | ||
|
|
b6b1bee25c | ||
|
|
3b3461fd3c | ||
|
|
7cbed63c92 | ||
|
|
a008e766f2 | ||
|
|
543394a825 | ||
|
|
23d6d75117 | ||
|
|
86433a58d0 | ||
|
|
3344e24a48 | ||
|
|
2621bc9f36 | ||
|
|
72de87c8c4 | ||
| 0ef9703757 | |||
|
|
d77d213d89 | ||
|
|
e793794fdd | ||
|
|
d1ae5ba7a0 | ||
|
|
84e30c8ee2 | ||
|
|
644128cd3f | ||
|
|
6912f5c55d | ||
|
|
fc0e39b9c7 | ||
|
|
8627b00ed8 | ||
|
|
0212f0fdd2 | ||
|
|
edfe594e7e | ||
|
|
791f13fca5 | ||
|
|
c3248fde1f | ||
|
|
c137ea0881 | ||
|
|
818b6505dd | ||
|
|
4a1958876f | ||
|
|
6bdb536848 | ||
|
|
6023ee25e1 | ||
|
|
e5d24f557a | ||
|
|
f8e137b67b | ||
|
|
76241e75a8 | ||
|
|
d5ce6ff96a | ||
|
|
44b1a2fb33 | ||
|
|
7dc1999928 | ||
|
|
63d480927d | ||
|
|
fa86fa4c9c | ||
|
|
444b597ade | ||
|
|
c81a9b7704 | ||
|
|
6cab6519b1 | ||
|
|
ce992ca743 | ||
|
|
092d1ac209 | ||
|
|
6acf2f9944 | ||
|
|
8d18f42b2e | ||
|
|
152f10ed8b | ||
|
|
d99ebca829 | ||
| df91305e13 | |||
|
|
6f2b6e0290 | ||
|
|
fc34833472 | ||
|
|
7f37211a8b | ||
|
|
ccc956f70b | ||
|
|
511f32e521 | ||
|
|
87a3e84f5b | ||
|
|
1743145e9f | ||
|
|
d561ac6e04 | ||
|
|
99bc31dee9 | ||
|
|
ac8e7acbd4 | ||
|
|
0ad5dbe741 | ||
|
|
7d9b054340 | ||
|
|
4b1e8a7cac | ||
|
|
8f190eb188 | ||
|
|
95ae6919b0 | ||
|
|
52e97f3a7c | ||
|
|
461aa1bc54 | ||
|
|
c38461a6e8 | ||
|
|
6bcb6fa93f | ||
|
|
f4181349f8 | ||
|
|
9d860367cc | ||
|
|
f654c59dd5 | ||
|
|
d22ac5cef2 | ||
|
|
0fd69e0b90 | ||
|
|
e8d87ff092 | ||
|
|
23612a38f2 | ||
|
|
bd100c15e7 | ||
|
|
dc5392446c | ||
|
|
f8cf139b10 | ||
|
|
ac955d327f | ||
|
|
9e6339037a | ||
|
|
b647f6afee | ||
|
|
aabb3d5009 | ||
|
|
4ed64ab91c | ||
|
|
f57e0bef02 | ||
|
|
9ed7466fd8 | ||
|
|
4d7766d1b1 | ||
|
|
09ae954085 | ||
|
|
8fd9fd5b20 | ||
|
|
dcb764eca7 | ||
|
|
b6a4ad6816 | ||
|
|
bbaaf655fa | ||
|
|
7228dc6e11 | ||
|
|
8953702608 | ||
|
|
0be33cb8db | ||
|
|
0f0b5db29b | ||
|
|
009f244739 | ||
|
|
2f87039f17 | ||
|
|
72fa38e928 | ||
|
|
9e68802090 | ||
|
|
d05cfcf317 | ||
|
|
80f810fb0c | ||
|
|
9153324795 | ||
|
|
91b5817e5f | ||
|
|
1dfa7889ab | ||
|
|
08d7da0c35 | ||
|
|
9ebeb42023 | ||
|
|
075f34b1fb | ||
|
|
210c89c2c7 | ||
|
|
b93a6e50ab | ||
|
|
85cc1f8c6a | ||
|
|
8e781b0c54 | ||
|
|
d8ad35b8e9 | ||
|
|
a9973d1e0f | ||
| 4113011f63 | |||
|
|
018782d986 | ||
|
|
3681e8c03e | ||
|
|
8f377e4cf3 | ||
|
|
419acaa40d | ||
|
|
42b204bf8a | ||
|
|
1d7dcb7d82 | ||
|
|
d63ca0b4f9 | ||
|
|
e9440327aa | ||
|
|
dcc7e282c7 | ||
|
|
6d5fc7c5c6 | ||
| 312fdf9986 | |||
|
|
4e0b4fa049 | ||
|
|
cf7c2a1436 | ||
|
|
3dc6555ad1 | ||
|
|
27ff9286b4 | ||
|
|
6e50461999 | ||
|
|
3f1c3a40cf | ||
|
|
0116ec4cc3 | ||
|
|
f962d0a6d7 | ||
|
|
dc3c0d7cb1 | ||
|
|
b05f9fad09 | ||
|
|
8650995926 | ||
|
|
e5469d2cb8 | ||
|
|
702698ddcd | ||
|
|
d233d582d4 | ||
|
|
3839fac162 | ||
|
|
b01dac85da | ||
|
|
37a49824d0 | ||
|
|
0127016ab2 | ||
|
|
e0b6fcb24a | ||
|
|
e7d9a8fec5 | ||
|
|
1a9addc537 | ||
|
|
401f25b1c4 | ||
|
|
ece522074e | ||
|
|
c30c0074f1 | ||
|
|
fa51dc2c4d | ||
|
|
c1810fde8a | ||
| a781ef8b14 | |||
|
|
92b2a9d609 | ||
|
|
9250b0f193 | ||
| 24869f47ee | |||
| bb5a57e909 | |||
| 9f3d81729d | |||
| 064d3e8b3d | |||
| ef7e3c61ed | |||
| 64951e1e5e | |||
| 58931732f7 | |||
| be8e50d590 | |||
| e309acd67d | |||
| 4e7f14ea5a | |||
| b79f0505b7 | |||
| 045ac85353 | |||
| 60af4304b6 | |||
| 96e946ac09 | |||
| 6584ed9dc4 | |||
| 3017c27910 | |||
| c25c2a25ad | |||
| 0476e489d4 | |||
| bf0d7a20a7 | |||
| ed0091cd05 | |||
| 402f93ab38 | |||
| 8944da8c99 | |||
| d671700466 | |||
| 562e102a9b | |||
| f4c60d7560 | |||
| 0dae8327eb | |||
| 39b7b91f45 | |||
| a7f77514d6 | |||
| b45dcea4c3 | |||
| 2ed70b4964 | |||
| dbf0b18f07 | |||
| 1cfc9b5f5d | |||
| c8f75bef8a | |||
| fc171b48e9 | |||
| 5504d0c6f4 | |||
| 025e9a8f1c | |||
| 06db61c6c0 | |||
| 8601ad9cd9 | |||
| 1f7318f1c3 | |||
| cdc78955b1 | |||
| 6f0c751f63 | |||
| f1ef759206 | |||
| 5f5f8cb173 | |||
| eb93eb7352 | |||
| 9cd9a30fbe | |||
| 5a30521d66 | |||
| 725d403d34 | |||
| 9a604042ca | |||
| 10406e1705 | |||
| 4717f63bc3 | |||
| 2ff74cdc7c | |||
| 87f2d6bf95 | |||
| 88ec796267 | |||
| 344a29a374 | |||
| c7fbe8e963 | |||
| 03acab784a | |||
| 3ce3ecac66 | |||
| bc24c00c50 | |||
| 3e9a843e1b | |||
| 5e99408c1f | |||
| e4a2c47da2 | |||
| 13ebe67a35 | |||
| ca1cdd0634 | |||
| 74e53d1364 | |||
| e946bd71f0 | |||
| 905b4619d6 | |||
| 97e9889251 | |||
| 9f3ac95d8d | |||
| 84523d0054 | |||
| f8c6b327f9 | |||
| 79662aa545 | |||
| 69b91a2e1d | |||
| 9961fe1ed4 | |||
| 4db6f0b06f | |||
| 307413f3f2 | |||
| 836ef66cf0 | |||
| 4cbaebc98b | |||
| c1d85b7f89 | |||
| c087f32355 | |||
| 9ad585681f | |||
| 40ab77f061 | |||
| 1d0adb7689 | |||
| 1d6b216b74 | |||
| df0d81ec4d | |||
| ea7d079f27 | |||
| 5022fc90ab | |||
| 45c478bede | |||
| 03b8abbe4d | |||
| 42ea1e2d03 | |||
| b85b237129 | |||
| 1d6a8c154c | |||
| 6f14d48d5e | |||
| fa9fd9c65f | |||
| 86cf979453 | |||
| cc0fd647ab | |||
| 591522f643 | |||
| 643fefb4bf | |||
| e8810195cd | |||
| 3d2669497a | |||
| 42f7a2773d | |||
| 35c1bdb3bf | |||
| 9605b9d3bd | |||
| dfa219191d | |||
| 14502df261 | |||
| ea7e05dcc2 | |||
| 26f7d3214f | |||
| 173a36c518 | |||
| fcb1777336 | |||
| f82c13cd09 | |||
| e9a854faed | |||
| 344e8dff69 | |||
| 36c2e9540c | |||
| 40dda3241f | |||
| db67f71a3c | |||
| 920ea80261 | |||
| ad03943339 | |||
| a349ce13f8 | |||
| 7d7129632f | |||
| e3b3401544 | |||
| bba9278895 | |||
| d941557e88 | |||
| 4964552483 | |||
| 7e0b0a859b | |||
| b33bd5f252 | |||
| 63b9a8fd13 | |||
| 6350cb681f | |||
| 676fc02c91 | |||
| 75e0d99495 | |||
| b51cd1a868 | |||
| 7187dc8280 | |||
| 86708542c9 | |||
| 74172c4e5a | |||
| 8c8835d1d5 | |||
| 286d20f8c1 | |||
| 05ba1bbdb3 | |||
| 7c2ef04d47 | |||
| 73eee46101 | |||
| 510da9c2a4 | |||
| 702c71fcff | |||
| 3f31f77bc8 | |||
| 6f5bc65171 | |||
| 6107e474c1 | |||
| e7f8fd2ccd | |||
| 1272fa3cb3 | |||
| a76c99e825 | |||
| d1b6df7d94 | |||
| 14d28f7937 | |||
| a67818c0f3 | |||
| 0d03a42337 | |||
| 853cd90148 | |||
| 4108600a57 | |||
| 59826cb7a9 | |||
| cb99f1f5d2 | |||
| a61491acf3 | |||
| 7b483364be | |||
| 9a8978d046 | |||
| 72b943ee04 | |||
| dd3cf2cd0f | |||
| dbd1c94ada | |||
| c532e7b7de | |||
| 3c90b68896 | |||
| 36c5215ed4 | |||
| d7d0027b64 | |||
| 428809719a | |||
| 45d8c51a75 | |||
| 7c605a7337 | |||
| a3cd499297 | |||
| ac0a4c0580 | |||
| 932beed25a | |||
| 79f5a9d129 | |||
| b2ebe28397 | |||
| 6c300ca545 | |||
| eea499a32c | |||
| fcd839a00e | |||
| 45b5e65e36 | |||
| 900098b9c1 | |||
| 1b5213af1d | |||
| efcd022d27 | |||
| 9f328a01a4 | |||
| 97e9c8c037 | |||
| b885d1d707 | |||
| a084340ce0 | |||
| 1b5597ac09 | |||
| 8710665255 | |||
| 81e292dc2b | |||
| aa5087882d | |||
| 1959a667d4 | |||
| 405aae5209 | |||
| b7e090ed7c | |||
| 02b788ee63 | |||
| a642d2382d | |||
| 6ea3611e08 | |||
| a255908108 | |||
| 9b85eabf41 | |||
| 1fb37b2da9 | |||
| 4193f33183 | |||
| 2ae45aca00 | |||
| 19f7af6b1c | |||
| 94f609140b | |||
| d60f24183b | |||
| 1728ede010 | |||
| 1d828f455c | |||
| a12ff980c5 | |||
| 9cb44fb15c | |||
| ffd5e085e1 | |||
| 0403077495 | |||
| 2124f86879 | |||
| 86c03c882d | |||
| 2725aecb58 | |||
| 0c99612913 | |||
| 7e93e9b1b1 | |||
| d2d3ee7c78 | |||
| 8dc2243140 | |||
| 2a323f5675 | |||
| cfc70229ee | |||
| 593e71509b | |||
| bca855046f | |||
| 7aa29a3f67 | |||
| 0f27698fef | |||
| a493cb75f9 | |||
| 31a4d7e3d3 | |||
| 79706c90ff | |||
| fabc113c84 | |||
| 01bc13e0a2 | |||
| 1eb12c2582 | |||
| 6d43a0c7a8 | |||
| c85d4f7a3d | |||
| 7ffc2c983b | |||
| 4fd272ddf7 | |||
| 99fb15f8f0 | |||
| bd27975bf3 | |||
| ee3f2d7fcf | |||
| 81accaf9a2 | |||
| 23052096f2 | |||
| 0fc6bc340b | |||
| 36ccbcbab1 | |||
| 574fdfe25c | |||
| 0987226b0d | |||
| b200294ebc | |||
| 27553fc4d1 | |||
| 7925990631 | |||
| 4639725099 | |||
| edb019c38e | |||
| 1742dff512 | |||
| 01e02a6687 | |||
| bca03f901a | |||
| 4c72e35707 | |||
| ff925aee83 | |||
| 05fa4eaa43 | |||
| 48b8548528 | |||
| 0764cc8bd7 | |||
| 28e57361e2 | |||
| 5e1522fefb | |||
| 4a2120f830 | |||
| e09c33d307 | |||
| 943795984f | |||
| 796881ecc5 | |||
| cf507a70af | |||
| c104009aaa | |||
| d085f80ea7 | |||
| 8121e5c048 | |||
| c51615543b | |||
| 625c4b8fe1 | |||
| 23b43a16b3 | |||
| 44b9d099c7 | |||
| 350a149645 | |||
| b358c93c16 | |||
| 6feea89ec1 | |||
| b09f8e1ad5 | |||
| 06b28b1297 | |||
| f807f47642 | |||
| 79f231eaa1 | |||
| e6abae943d | |||
| e5f84ccbf2 | |||
| 9d482087e5 | |||
| face668dfc | |||
| 8293c842e6 | |||
| fc31dd2a5a | |||
| efecef6832 | |||
| 5612932691 | |||
| 0f4d2dcb7c | |||
| c625e48dee | |||
| 23bdd17e25 |
59
.gitignore
vendored
59
.gitignore
vendored
@@ -1,15 +1,9 @@
|
|||||||
# macOS system files
|
# macOS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.AppleDouble
|
.AppleDouble
|
||||||
.LSOverride
|
.LSOverride
|
||||||
|
|
||||||
# Icon must end with two \r
|
|
||||||
Icon
|
Icon
|
||||||
|
|
||||||
# Thumbnails
|
|
||||||
._*
|
._*
|
||||||
|
|
||||||
# Files that might appear in the root of a volume
|
|
||||||
.DocumentRevisions-V100
|
.DocumentRevisions-V100
|
||||||
.fseventsd
|
.fseventsd
|
||||||
.Spotlight-V100
|
.Spotlight-V100
|
||||||
@@ -17,36 +11,53 @@ Icon
|
|||||||
.Trashes
|
.Trashes
|
||||||
.VolumeIcon.icns
|
.VolumeIcon.icns
|
||||||
.com.apple.timemachine.donotpresent
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
# Directories potentially created on remote AFP share
|
|
||||||
.AppleDB
|
.AppleDB
|
||||||
.AppleDesktop
|
.AppleDesktop
|
||||||
Network Trash Folder
|
Network Trash Folder
|
||||||
Temporary Items
|
Temporary Items
|
||||||
.apdisk
|
.apdisk
|
||||||
|
|
||||||
# VS Code
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
# macOS metadata
|
|
||||||
*.icloud
|
*.icloud
|
||||||
|
|
||||||
# Terraform
|
# IDE
|
||||||
terraform/
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
.kiro/
|
||||||
|
|
||||||
|
# Terraform (track .tf files, ignore state and runtime)
|
||||||
.terraform/
|
.terraform/
|
||||||
*.tfstate
|
*.tfstate
|
||||||
*.tfstate.backup
|
*.tfstate.backup
|
||||||
|
# *.tfvars
|
||||||
# Crash log files
|
!*.tfvars.example
|
||||||
|
!*.pkrvars.hcl
|
||||||
crash.log
|
crash.log
|
||||||
|
|
||||||
# Ignore override files as they are usually used to override resources locally
|
|
||||||
*.override.tf
|
*.override.tf
|
||||||
*.override.tf.json
|
*.override.tf.json
|
||||||
|
|
||||||
# Ignore CLI configuration files
|
|
||||||
.terraformrc
|
.terraformrc
|
||||||
.terraformrc.json
|
terraform.rc
|
||||||
|
|
||||||
# Ignore secrets
|
# Ansible
|
||||||
|
*.retry
|
||||||
|
ansible/vault/*.vault
|
||||||
|
|
||||||
|
# Packer
|
||||||
|
packer_cache/
|
||||||
|
|
||||||
|
# Secrets
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
|
||||||
|
# Environment files with secrets
|
||||||
|
boilerplates/**/.env
|
||||||
|
|
||||||
|
# Local paths
|
||||||
|
~/
|
||||||
|
|
||||||
|
# Misc
|
||||||
doppler-token.yaml
|
doppler-token.yaml
|
||||||
|
|
||||||
|
# talhelper generated machine configs — contain secrets, never commit
|
||||||
|
talos/talhelper/clusterconfig/
|
||||||
|
# talenv.yaml plaintext — only commit the SOPS-encrypted version
|
||||||
|
talos/talhelper/talenv.yaml
|
||||||
|
!talos/talhelper/talenv.sops.yaml
|
||||||
3
.sops.yaml
Normal file
3
.sops.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
creation_rules:
|
||||||
|
- path_regex: talos/talhelper/talenv.yaml
|
||||||
|
age: age1xkyuv8r8ce6lu3d64jfspz4e50k6pxlaxwmuplzrtnpfnnrnycaq6mfsrn
|
||||||
160
COUCHDB-ERLANGCOOKIE-FIX.md
Normal file
160
COUCHDB-ERLANGCOOKIE-FIX.md
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
# CouchDB erlangCookie Fix - Implementation Guide
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**Problem**: CouchDB deployment fails because `erlangCookie` is missing from the ExternalSecret configuration.
|
||||||
|
|
||||||
|
**Decision**: Externalize `erlangCookie` to 1Password (pragmatic approach)
|
||||||
|
|
||||||
|
**Rationale**:
|
||||||
|
- ExternalSecret architecture requires ownership of the entire secret
|
||||||
|
- Mixing externalized and chart-generated fields in the same secret is not supported
|
||||||
|
- Single-node deployment makes erlangCookie rotation unnecessary
|
||||||
|
- This is an acceptable deviation from the pure Harbor pattern given the architectural constraints
|
||||||
|
|
||||||
|
## Implementation Steps
|
||||||
|
|
||||||
|
### 1. Generate erlangCookie Value
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openssl rand -hex 20
|
||||||
|
```
|
||||||
|
|
||||||
|
Example output: `f4e3c2b1a9d8e7f6c5b4a3d2e1f0a9b8c7d6e5f4`
|
||||||
|
|
||||||
|
### 2. Add to 1Password
|
||||||
|
|
||||||
|
- **Vault**: `mk-labs`
|
||||||
|
- **Item**: `couchdb`
|
||||||
|
- **Field Name**: `erlang-cookie`
|
||||||
|
- **Field Type**: password (concealed)
|
||||||
|
- **Value**: `<paste generated value from step 1>`
|
||||||
|
|
||||||
|
### 3. Update ExternalSecret Configuration
|
||||||
|
|
||||||
|
File: `cluster/applications/couchdb/externalsecret.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: external-secrets.io/v1beta1
|
||||||
|
kind: ExternalSecret
|
||||||
|
metadata:
|
||||||
|
name: couchdb-credentials
|
||||||
|
namespace: couchdb
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: couchdb
|
||||||
|
app.kubernetes.io/part-of: mk-labs
|
||||||
|
spec:
|
||||||
|
refreshInterval: 1h
|
||||||
|
secretStoreRef:
|
||||||
|
kind: ClusterSecretStore
|
||||||
|
name: onepassword-connect
|
||||||
|
target:
|
||||||
|
name: couchdb-admin
|
||||||
|
creationPolicy: Owner
|
||||||
|
template:
|
||||||
|
engineVersion: v2
|
||||||
|
data:
|
||||||
|
adminUsername: "admin"
|
||||||
|
adminPassword: "{{ .adminPassword }}"
|
||||||
|
cookieAuthSecret: "{{ .cookieAuthSecret }}"
|
||||||
|
erlangCookie: "{{ .erlangCookie }}" # ← ADD THIS LINE
|
||||||
|
data:
|
||||||
|
- secretKey: adminPassword
|
||||||
|
remoteRef:
|
||||||
|
key: couchdb
|
||||||
|
property: admin-password
|
||||||
|
- secretKey: cookieAuthSecret
|
||||||
|
remoteRef:
|
||||||
|
key: couchdb
|
||||||
|
property: cookie-auth-secret
|
||||||
|
- secretKey: erlangCookie # ← ADD THIS BLOCK
|
||||||
|
remoteRef:
|
||||||
|
key: couchdb
|
||||||
|
property: erlang-cookie
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Update values.yaml Documentation (Optional)
|
||||||
|
|
||||||
|
File: `cluster/applications/couchdb/values.yaml`
|
||||||
|
|
||||||
|
Update the comment block at line 9-10:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Admin credentials managed via ExternalSecret
|
||||||
|
# See externalsecret.yaml for 1Password integration
|
||||||
|
#
|
||||||
|
# NOTE: erlangCookie is externalized to 1Password for architectural
|
||||||
|
# simplicity (ExternalSecret ownership model). In a pure Harbor pattern,
|
||||||
|
# this would be chart-generated, but single-node deployment makes this
|
||||||
|
# acceptable. The erlangCookie is treated as an immutable infrastructure
|
||||||
|
# secret (generate once, never rotate).
|
||||||
|
createAdminSecret: false
|
||||||
|
extraSecretName: "couchdb-admin"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Commit and Push
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/git/homelab
|
||||||
|
git add cluster/applications/couchdb/externalsecret.yaml
|
||||||
|
git add cluster/applications/couchdb/values.yaml # if modified
|
||||||
|
git commit -m "fix(couchdb): add erlangCookie to ExternalSecret from 1Password"
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Verify Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Watch ExternalSecret sync
|
||||||
|
kubectl get externalsecret -n couchdb couchdb-credentials -w
|
||||||
|
# Wait for: SecretSynced
|
||||||
|
|
||||||
|
# Verify secret created with all four keys
|
||||||
|
kubectl get secret -n couchdb couchdb-admin -o yaml
|
||||||
|
# Should contain: adminUsername, adminPassword, cookieAuthSecret, erlangCookie
|
||||||
|
|
||||||
|
# Watch ArgoCD sync
|
||||||
|
kubectl get application -n argocd couchdb -w
|
||||||
|
# Wait for: Healthy/Synced
|
||||||
|
|
||||||
|
# Watch pod startup
|
||||||
|
kubectl get pods -n couchdb -w
|
||||||
|
# Wait for: Running
|
||||||
|
|
||||||
|
# Test CouchDB access
|
||||||
|
kubectl port-forward -n couchdb svc/couchdb-svc-couchdb 5984:5984 &
|
||||||
|
curl http://localhost:5984/
|
||||||
|
# Expected: {"couchdb":"Welcome","version":"3.5.1"}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Why Not Follow Harbor Pattern Exactly?
|
||||||
|
|
||||||
|
**Harbor Pattern**: Only user-facing credentials externalized, internal secrets chart-generated.
|
||||||
|
|
||||||
|
**CouchDB Constraint**: ExternalSecret uses `creationPolicy: Owner`, which takes full ownership of the target secret. This prevents the Helm chart from adding auto-generated fields to the same secret.
|
||||||
|
|
||||||
|
**Options Considered**:
|
||||||
|
1. ✅ **Externalize erlangCookie** (SELECTED) - Works with current architecture
|
||||||
|
2. ❌ Chart auto-generation - Conflicts with ExternalSecret ownership
|
||||||
|
3. ❌ Dual-secret approach - Requires Helm chart customization
|
||||||
|
4. ❌ Disable ExternalSecret - Loses 1Password integration for admin password
|
||||||
|
|
||||||
|
**Decision**: Pragmatic approach wins. erlangCookie is treated as an infrastructure secret (generate once, never rotate), which is acceptable for a single-node deployment.
|
||||||
|
|
||||||
|
## Secret Classification
|
||||||
|
|
||||||
|
| Secret | Type | 1Password? | Rationale |
|
||||||
|
|------------------|---------------|------------|------------------------------------|
|
||||||
|
| adminUsername | User-facing | No* | Static value, hardcoded in template |
|
||||||
|
| adminPassword | User-facing | ✅ YES | User login credential |
|
||||||
|
| cookieAuthSecret | Gray area | ✅ YES | Session security, periodic rotation |
|
||||||
|
| erlangCookie | Internal | ✅ YES** | Architectural constraint |
|
||||||
|
|
||||||
|
\* Hardcoded in ExternalSecret template (not fetched from 1Password)
|
||||||
|
\*\* Pragmatic deviation from Harbor pattern due to ExternalSecret architecture
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- Full analysis: `/home/hermes/couchdb-erlangcookie-analysis.txt`
|
||||||
|
- Harbor pattern: `/home/hermes/harbor-simplification-complete.txt`
|
||||||
|
- CouchDB Helm chart: `apache/couchdb` v4.6.3
|
||||||
166
README.md
166
README.md
@@ -1,102 +1,102 @@
|
|||||||
# A Homelab based on Red Hat Technologies
|
# mk-labs
|
||||||
|
|
||||||
This repository is the configuration of my homelab. In addition to providing services, the purpose of my homelab is to learn advanced concepts primarily based on Red Hat OpenShift.
|
Automated infrastructure provisioning and configuration for a personal homelab, built on GitOps practices with clear tool responsibility boundaries.
|
||||||
|
|
||||||
This implementation is built on easily accessible consumer based hardware and will focus heavily on GitOps practices and automation will be used wherever possible.
|
## Architecture
|
||||||
|
|
||||||
This repo is a mono-repo that is broken up into three sections:
|
A single operator action — setting a VM record's status to **Staged** in NetBox — triggers a fully automated provisioning pipeline:
|
||||||
|
```
|
||||||
|
NetBox (webhook) → n8n (validate & orchestrate) → Terraform (create VM + DHCP)
|
||||||
|
→ Ansible (OS config + DNS + status update)
|
||||||
|
```
|
||||||
|
|
||||||
- infra-config
|
| Tool | Host | IP | Responsibility |
|
||||||
- apps
|
|------|------|----|---------------|
|
||||||
- cluster
|
| NetBox | fire-station | 10.1.71.102 | Source of truth — VM records, IP allocation, VLAN data |
|
||||||
|
| n8n | tiki-room | 10.1.71.23 | Event orchestration, validation, pipeline sequencing |
|
||||||
|
| Terraform | city-hall | 10.1.71.35 | Proxmox VM lifecycle, Unifi DHCP reservations |
|
||||||
|
| Ansible / Semaphore | imagineering | 10.1.71.22 | OS configuration, DNS records, NetBox status updates |
|
||||||
|
| Proxmox | fantasyland | 10.1.71.13 | Target hypervisor |
|
||||||
|
|
||||||
|
All systems on the Server Trusted VLAN (10.1.71.0/24).
|
||||||
|
|
||||||
|
## Repository Structure
|
||||||
|
```
|
||||||
|
homelab/
|
||||||
|
├── ansible/
|
||||||
|
│ ├── inventory/ # NetBox dynamic inventory + static
|
||||||
|
│ ├── playbooks/ # Runnable playbooks (vm-provision, DNS, OS updates)
|
||||||
|
│ ├── roles/ # vm-baseline, dns-manager, common, haproxy, n8n, observer, etc.
|
||||||
|
│ ├── tasks/ # Shared includable task files
|
||||||
|
│ ├── group_vars/ # Group variable definitions
|
||||||
|
│ ├── host_vars/ # Per-host variable definitions
|
||||||
|
│ ├── templates/ # Jinja2 templates
|
||||||
|
│ └── ansible.cfg
|
||||||
|
│
|
||||||
|
├── terraform/
|
||||||
|
│ ├── proxmox/vm/ # bpg/proxmox provider — VM creation from templates
|
||||||
|
│ ├── unifi/dhcp/ # Unifi provider — DHCP static reservations on UDM Pro
|
||||||
|
│ └── dns/ # DNS record management
|
||||||
|
│
|
||||||
|
├── packer/
|
||||||
|
│ ├── ubuntu-24.04/ # Ubuntu 24.04 VM template (small → xlarge-plus sizes)
|
||||||
|
│ └── fedora-42/ # Fedora 42 VM template
|
||||||
|
│
|
||||||
|
├── n8n/
|
||||||
|
│ └── workflows/ # Exported n8n workflow JSON (vm-provisioning)
|
||||||
|
│
|
||||||
|
├── netbox/
|
||||||
|
│ └── initializers/ # Custom fields, VLANs, IP prefixes as code
|
||||||
|
│
|
||||||
|
└── docs/
|
||||||
|
└── decisions/ # Architecture decision records
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pipeline Flow
|
||||||
|
|
||||||
|
| # | System | Action |
|
||||||
|
|---|--------|--------|
|
||||||
|
| 1 | NetBox | Operator sets VM status to Staged → webhook fires |
|
||||||
|
| 2 | n8n | Validates payload (hostname, IP, VLAN, template, proxmox_node) |
|
||||||
|
| 3 | n8n → city-hall | SSH + `terraform apply` — creates VM on Proxmox |
|
||||||
|
| 4 | n8n | Queries Proxmox API for MAC address |
|
||||||
|
| 5 | n8n → NetBox | Writes MAC to VM interface record |
|
||||||
|
| 6 | n8n → city-hall | SSH + `terraform apply` — creates DHCP reservation on UDM Pro |
|
||||||
|
| 7 | n8n → imagineering | Triggers Ansible via Semaphore API |
|
||||||
|
| 8 | Ansible | OS baseline, SSH hardening, Technitium DNS A record |
|
||||||
|
| 9 | Ansible → NetBox | Sets VM status to Active |
|
||||||
|
|
||||||
|
On any failure, NetBox status is set to **Failed**. No auto-retry — operator investigates.
|
||||||
|
|
||||||
## Hardware
|
## Hardware
|
||||||
|
|
||||||
- Dell 7050 SFF (7)
|
- 7× Dell 7050 SFF
|
||||||
- Minisforum TH60 (3)
|
- 3× Minisforum TH60
|
||||||
- Minisforum MS01 (2)
|
- 2× Minisforum MS01
|
||||||
- Synology 1621+
|
- Synology DS1621+
|
||||||
|
- Ubiquiti UDM Pro
|
||||||
|
|
||||||
## Software
|
## Software Stack
|
||||||
|
|
||||||
- Proxmox (Virtualization)
|
- **Virtualization**: Proxmox
|
||||||
- Cloudflare (Domain Hosting, Public DNS)
|
- **Automation**: Terraform, Ansible, n8n, Semaphore
|
||||||
- Unbound (Recursive DNS)
|
- **DNS**: Technitium (authoritative), Unbound (recursive)
|
||||||
- FreeIPA (Identity management, Authoritive DNS)
|
- **IPAM/DCIM**: NetBox
|
||||||
- Matchbox (iPXE)
|
- **Networking**: Ubiquiti UDM Pro
|
||||||
- Red Hat OpenShift
|
- **Templates**: Packer (Ubuntu 24.04, Fedora 42)
|
||||||
- OpenShift Agent Based Installer (Install OpenShift)
|
|
||||||
- [Red Hat Advanced Cluster Management for Kubernetes](https://www.redhat.com/en/technologies/management/advanced-cluster-management)
|
|
||||||
- [Vault](https://www.hashicorp.com/en/products/vault)
|
|
||||||
- [OpenShift GitOps (ArgoCD)](https://www.redhat.com/en/technologies/cloud-computing/openshift/gitops)
|
|
||||||
- [Red Hat Ansible Automation Platform](https://www.redhat.com/en/technologies/management/ansible)
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- Ansible user created
|
|
||||||
- Ansible configured
|
|
||||||
- [Networking](docs/networks.md)
|
|
||||||
- [Proxmox](docs/proxmox.md) (In my homelab, internal DNS, identity manangement, and ipxe are hosted here.)
|
|
||||||
- Matchbox
|
|
||||||
- DNS
|
|
||||||
- Domain Registration
|
|
||||||
|
|
||||||
## Assumptions
|
|
||||||
|
|
||||||
There are a dozen different architectures you could use to deploy OpenShift in every which way.
|
|
||||||
For the sake of this documentation we'll assume the following:
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
[Step-by-Step Walkthrough](step-by-step.md)
|
See [docs/decisions/vm-provisioning-flow.md](docs/decisions/vm-provisioning-flow.md) for the full architecture decision record.
|
||||||
|
|
||||||
### Hub Cluster
|
Previous OpenShift/ACM/Fastpass content is preserved in the `archive/pre-mk-labs` branch.
|
||||||
|
|
||||||
You'll need an OpenShift "Hub Cluster" with access to persistant storage.
|
## Security
|
||||||
A Single Node OpenShift (SNO) instance, installed on bare metal, will act as a Hub cluster and run:
|
|
||||||
|
|
||||||
- Advanced Cluster Management
|
No sensitive data is stored in this repository. Secrets are managed via Ansible Vault and environment variables on pipeline hosts.
|
||||||
- Ansible Automation Platform
|
|
||||||
- Vault
|
|
||||||
- ~~OpenShift GitOps~~
|
|
||||||
|
|
||||||
#### Network Prerequisites
|
|
||||||
|
|
||||||
The prerequisites for OpenShift in traditional and HCP patterns are largely the same - it just kind of depends on where your DNS records go to.
|
|
||||||
|
|
||||||
| Cluster | Endpoint | VIP | DNS A Record | Notes |
|
|
||||||
|------------------|-------------|---------------|-----------------------------------|----------------------------------|
|
|
||||||
| Hub Cluster (SNO) | App Ingress | 192.168.0.10 | *.apps.hub-cluster.example.com | SNO App VIP goes to IP of node |
|
|
||||||
| Hub Cluster (SNO) | API | 192.168.0.10 | api.hub-cluster.example.com | SNO API goes to IP of node |
|
|
||||||
|
|
||||||
These DNS entries should be put in your Authoratitive DNS.
|
|
||||||
|
|
||||||
#### ACM & GitOps Configuration
|
|
||||||
|
|
||||||
Before you start creating clusters you may want to create some Policies, integrate ACM and ArgoCD, etc. This step is optional in case you're just interested in trying out Hosted Control Planes or copy/paste around a cluster for testing purposes.
|
|
||||||
|
|
||||||
Find additional details in the ./02-rhacm-config folder.
|
|
||||||
|
|
||||||
#### Creating a Cluster
|
|
||||||
|
|
||||||
With everything in its right place, you can now start to declaratively create clusters
|
|
||||||
|
|
||||||
./05-clusters/hcp-bmh - HCP to Bare Metal Hosts
|
|
||||||
|
|
||||||
### Internal Cluster
|
|
||||||
|
|
||||||
Two additional bare metal nodes, to be added to Advanced Cluster Management (ACM) running on the SNO Hub. These will be used to create another HCP cluster.
|
|
||||||
These servers have a BMC interface with Redfish - if not, then you'll need to manually manage the boot and installation of those servers.
|
|
||||||
This makes it to where you just need 3 bare metal nodes. You could run one HCP Bare Metal cluster with both of the other nodes, but then you have a shared storage requirement that can't be satisfied by ODF since that needs at least 3 nodes.
|
|
||||||
|
|
||||||
### External Cluster
|
|
||||||
|
|
||||||
You'll also either need you just need at least 2 bare metal nodes.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Credits
|
**Status**: 🚧 Active Development — VM Provisioning Pipeline
|
||||||
|
|
||||||
- Ken Moini - As I used his [repo](https://github.com/kenmoini/ztp-for-you-and-me) as the baseline for this project.
|
**Last Updated**: February 2026
|
||||||
- Ryan Etten
|
|
||||||
- Andrew Potozniak
|
|
||||||
|
|||||||
193
ansible/DNS_MANAGEMENT_REFACTOR.md
Executable file
193
ansible/DNS_MANAGEMENT_REFACTOR.md
Executable file
@@ -0,0 +1,193 @@
|
|||||||
|
# DNS Management Refactor - Modular & Repeatable
|
||||||
|
|
||||||
|
This document explains the refactoring of DNS management from standalone playbooks to modular, reusable tasks.
|
||||||
|
|
||||||
|
## 🎯 **What Changed**
|
||||||
|
|
||||||
|
### **Before (Monolithic)**
|
||||||
|
```yaml
|
||||||
|
# Standalone playbook: add_technitium_dns_entry.yml
|
||||||
|
- name: Add entry to Technitium DNS
|
||||||
|
hosts: all
|
||||||
|
tasks:
|
||||||
|
- name: Create DNS entry
|
||||||
|
effectivelywild.technitium_dns.technitium_dns_add_record:
|
||||||
|
# ... hardcoded parameters
|
||||||
|
```
|
||||||
|
|
||||||
|
### **After (Modular)**
|
||||||
|
```yaml
|
||||||
|
# Reusable task: tasks/add_technitium_dns_entry.yml
|
||||||
|
- name: Create DNS entry for {{ dns_record_name }}
|
||||||
|
effectivelywild.technitium_dns.technitium_dns_add_record:
|
||||||
|
# ... parameterized with variables
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📁 **New Structure**
|
||||||
|
|
||||||
|
```
|
||||||
|
ansible/
|
||||||
|
├── playbooks/
|
||||||
|
│ ├── tasks/
|
||||||
|
│ │ └── add_technitium_dns_entry.yml # ✅ Reusable task file
|
||||||
|
│ ├── add_dns_entry.yml # ✅ New playbook using task
|
||||||
|
│ ├── add_technitium_dns_entry.yml.backup # 📦 Backed up old version
|
||||||
|
│ └── roles/
|
||||||
|
│ └── dns-manager/ # ✅ Enhanced role
|
||||||
|
│ ├── tasks/main.yml # Uses task file
|
||||||
|
│ └── defaults/main.yml # Technitium defaults
|
||||||
|
└── scripts/
|
||||||
|
└── migrate-dns-references.sh # ✅ Migration helper
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 **Usage Examples**
|
||||||
|
|
||||||
|
### **1. In Playbooks (Direct Task Include)**
|
||||||
|
```yaml
|
||||||
|
- name: Add DNS entry for my server
|
||||||
|
hosts: my_servers
|
||||||
|
tasks:
|
||||||
|
- name: Create DNS entry
|
||||||
|
ansible.builtin.include_tasks: tasks/add_technitium_dns_entry.yml
|
||||||
|
vars:
|
||||||
|
dns_record_name: "{{ inventory_hostname }}"
|
||||||
|
dns_zone: "{{ base_domain }}"
|
||||||
|
dns_ip_address: "{{ ansible_default_ipv4.address }}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### **2. Using the DNS Manager Role**
|
||||||
|
```yaml
|
||||||
|
- name: Setup cluster DNS
|
||||||
|
hosts: control_plane[0]
|
||||||
|
roles:
|
||||||
|
- role: dns-manager
|
||||||
|
vars:
|
||||||
|
cluster_endpoint: "my-cluster.local.mk-labs.cloud"
|
||||||
|
cluster_vip: "10.1.71.100"
|
||||||
|
```
|
||||||
|
|
||||||
|
### **3. Using the New Playbook**
|
||||||
|
```yaml
|
||||||
|
# Import the new modular playbook
|
||||||
|
- import_playbook: add_dns_entry.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### **4. In Cluster Network Setup**
|
||||||
|
```yaml
|
||||||
|
- name: Setup complete cluster network
|
||||||
|
ansible.builtin.include_role:
|
||||||
|
name: cluster-network-setup
|
||||||
|
vars:
|
||||||
|
cluster_name: "fastpass"
|
||||||
|
cluster_endpoint: "fastpass.local.mk-labs.cloud"
|
||||||
|
cluster_vip: "10.1.71.53"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 **Migration Guide**
|
||||||
|
|
||||||
|
### **Automatic Migration**
|
||||||
|
```bash
|
||||||
|
# Run the migration script to find references
|
||||||
|
./scripts/migrate-dns-references.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Manual Updates**
|
||||||
|
|
||||||
|
1. **Replace playbook imports:**
|
||||||
|
```yaml
|
||||||
|
# OLD
|
||||||
|
- import_playbook: add_technitium_dns_entry.yml
|
||||||
|
|
||||||
|
# NEW
|
||||||
|
- import_playbook: add_dns_entry.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Use task includes in roles:**
|
||||||
|
```yaml
|
||||||
|
- ansible.builtin.include_tasks: tasks/add_technitium_dns_entry.yml
|
||||||
|
vars:
|
||||||
|
dns_record_name: "my-server"
|
||||||
|
dns_ip_address: "10.1.71.100"
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Use the dns-manager role:**
|
||||||
|
```yaml
|
||||||
|
- ansible.builtin.include_role:
|
||||||
|
name: dns-manager
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📋 **Variable Reference**
|
||||||
|
|
||||||
|
### **Task Variables (`tasks/add_technitium_dns_entry.yml`)**
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| `dns_record_name` | `inventory_hostname` | DNS record name |
|
||||||
|
| `dns_zone` | `base_domain` | DNS zone |
|
||||||
|
| `dns_ip_address` | `ip_address` | IP address for A record |
|
||||||
|
| `dns_record_type` | `A` | DNS record type |
|
||||||
|
| `dns_ttl` | `360` | TTL in seconds |
|
||||||
|
| `dns_create_ptr` | `true` | Create PTR record |
|
||||||
|
| `dns_debug` | `true` | Show debug output |
|
||||||
|
|
||||||
|
### **DNS Manager Role Variables**
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| `cluster_endpoint` | - | Full cluster FQDN |
|
||||||
|
| `cluster_vip` | - | Cluster VIP address |
|
||||||
|
| `dns_management_enabled` | `true` | Enable DNS management |
|
||||||
|
| `use_hosts_file_fallback` | `true` | Add to /etc/hosts |
|
||||||
|
| `dns_ttl` | `360` | DNS TTL |
|
||||||
|
| `create_ptr_record` | `true` | Create PTR record |
|
||||||
|
|
||||||
|
## 🎯 **Benefits**
|
||||||
|
|
||||||
|
### ✅ **Modularity**
|
||||||
|
- Single task file used across multiple contexts
|
||||||
|
- Consistent DNS management approach
|
||||||
|
- Easy to maintain and update
|
||||||
|
|
||||||
|
### ✅ **Flexibility**
|
||||||
|
- Works in playbooks, roles, and standalone
|
||||||
|
- Parameterized for different use cases
|
||||||
|
- Supports multiple DNS providers (extensible)
|
||||||
|
|
||||||
|
### ✅ **Maintainability**
|
||||||
|
- One place to update DNS logic
|
||||||
|
- Clear variable interface
|
||||||
|
- Better error handling and debugging
|
||||||
|
|
||||||
|
### ✅ **Integration**
|
||||||
|
- Seamlessly integrates with cluster setup
|
||||||
|
- Works with existing homelab infrastructure
|
||||||
|
- Compatible with Traefik load balancer setup
|
||||||
|
|
||||||
|
## 🔄 **Integration with Cluster Setup**
|
||||||
|
|
||||||
|
The DNS management now integrates seamlessly with your cluster deployment:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# In fastpass-first-control-plane role
|
||||||
|
- name: Setup network infrastructure for FastPass cluster
|
||||||
|
ansible.builtin.include_role:
|
||||||
|
name: cluster-network-setup
|
||||||
|
vars:
|
||||||
|
cluster_name: "{{ cluster_name }}"
|
||||||
|
cluster_endpoint: "{{ control_plane_endpoint }}"
|
||||||
|
cluster_vip: "{{ ansible_default_ipv4.address }}"
|
||||||
|
control_plane_nodes: "{{ groups['fastpass_control_plane'] }}"
|
||||||
|
```
|
||||||
|
|
||||||
|
This automatically:
|
||||||
|
1. ✅ Creates DNS entry for `fastpass.local.mk-labs.cloud`
|
||||||
|
2. ✅ Configures Traefik load balancer
|
||||||
|
3. ✅ Tests connectivity
|
||||||
|
4. ✅ Provides fallback to /etc/hosts
|
||||||
|
|
||||||
|
## 🚀 **Next Steps**
|
||||||
|
|
||||||
|
1. **Test the refactored approach** with your FastPass cluster
|
||||||
|
2. **Extend to other clusters** (Hub, Internal) using the same pattern
|
||||||
|
3. **Add support for other DNS providers** if needed
|
||||||
|
4. **Create monitoring** for DNS health checks
|
||||||
|
|
||||||
|
This modular approach makes your homelab's DNS management much more maintainable and repeatable across all your Kubernetes clusters!
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
|
|
||||||
# (pathspec) Colon-separated paths in which Ansible will search for collections content. Collections must be in nested *subdirectories*, not directly in these directories. For example, if ``COLLECTIONS_PATHS`` includes ``'{{ ANSIBLE_HOME ~ "/collections" }}'``, and you want to add ``my.collection`` to that directory, it must be saved as ``'{{ ANSIBLE_HOME} ~ "/collections/ansible_collections/my/collection" }}'``.
|
# (pathspec) Colon-separated paths in which Ansible will search for collections content. Collections must be in nested *subdirectories*, not directly in these directories. For example, if ``COLLECTIONS_PATHS`` includes ``'{{ ANSIBLE_HOME ~ "/collections" }}'``, and you want to add ``my.collection`` to that directory, it must be saved as ``'{{ ANSIBLE_HOME} ~ "/collections/ansible_collections/my/collection" }}'``.
|
||||||
|
|
||||||
;collections_path=/Users/rblundon/.ansible/collections:/usr/share/ansible/collections
|
collections_path=/opt/ansible-collections:/usr/share/ansible/collections
|
||||||
|
|
||||||
# (boolean) A boolean to enable or disable scanning the sys.path for installed collections.
|
# (boolean) A boolean to enable or disable scanning the sys.path for installed collections.
|
||||||
;collections_scan_sys_path=True
|
;collections_scan_sys_path=True
|
||||||
@@ -209,7 +209,7 @@ private_key_file=~/.ssh/ansible
|
|||||||
remote_user=wed
|
remote_user=wed
|
||||||
|
|
||||||
# (pathspec) Colon-separated paths in which Ansible will search for Roles.
|
# (pathspec) Colon-separated paths in which Ansible will search for Roles.
|
||||||
;roles_path=/Users/rblundon/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
|
roles_path=./roles
|
||||||
|
|
||||||
# (string) Set the main callback used to display Ansible output. You can only have one at a time.
|
# (string) Set the main callback used to display Ansible output. You can only have one at a time.
|
||||||
# You can have many other callbacks, but just one can be in charge of stdout.
|
# You can have many other callbacks, but just one can be in charge of stdout.
|
||||||
@@ -262,7 +262,7 @@ remote_user=wed
|
|||||||
|
|
||||||
# (path) The vault password file to use. Equivalent to ``--vault-password-file`` or ``--vault-id``.
|
# (path) The vault password file to use. Equivalent to ``--vault-password-file`` or ``--vault-id``.
|
||||||
# If executable, it will be run and the resulting stdout will be used as the password.
|
# If executable, it will be run and the resulting stdout will be used as the password.
|
||||||
;vault_password_file=
|
vault_password_file=/home/hermes/.vault_pass.txt
|
||||||
|
|
||||||
# (integer) Sets the default verbosity, equivalent to the number of ``-v`` passed in the command line.
|
# (integer) Sets the default verbosity, equivalent to the number of ``-v`` passed in the command line.
|
||||||
;verbosity=0
|
;verbosity=0
|
||||||
6
ansible/create_jarvis_user.yml
Normal file
6
ansible/create_jarvis_user.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
- name: Create jarvis user and deploy SSH key
|
||||||
|
hosts: all
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- jarvis_user
|
||||||
193
ansible/group_vars/all/semaphore.yml
Normal file
193
ansible/group_vars/all/semaphore.yml
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# Semaphore configuration-as-code
|
||||||
|
# ============================================================================
|
||||||
|
# Drives a freshly-deployed Semaphore instance into its desired state via
|
||||||
|
# the Semaphore REST API. Idempotent: every object is checked first; only
|
||||||
|
# missing ones are created. Existing objects are left alone.
|
||||||
|
#
|
||||||
|
# Loaded from group_vars/all/semaphore.yml so that the configuration is
|
||||||
|
# version-controlled in the homelab repo and survives a wipe-and-redeploy
|
||||||
|
# of the Semaphore VM.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# API connection (defaults to the local Traefik-fronted service-name URL).
|
||||||
|
# Override semaphore_api_url to point at a specific instance if needed.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
semaphore_api_url: "https://semaphore.local.mk-labs.cloud/api"
|
||||||
|
semaphore_api_validate_certs: true
|
||||||
|
semaphore_api_token: "{{ vault_semaphore_api_token }}"
|
||||||
|
|
||||||
|
# Feature flag — keeps day1_deploy_semaphore.yml deploy-only by default.
|
||||||
|
# Set true to also run the configuration pass.
|
||||||
|
semaphore_configure: false
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Declarative configuration of the Semaphore instance.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Top-level shape:
|
||||||
|
#
|
||||||
|
# semaphore_config:
|
||||||
|
# project: single dict — the lab uses one project ("mk-labs")
|
||||||
|
# keys: list of credentials Semaphore stores
|
||||||
|
# repositories: git repos Semaphore can clone
|
||||||
|
# inventories: Ansible inventories from those repos
|
||||||
|
# environments: env-var bundles
|
||||||
|
# templates: task templates that tie everything together
|
||||||
|
#
|
||||||
|
# Each list element has a unique "name" used as the natural identity key.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
semaphore_config:
|
||||||
|
project:
|
||||||
|
name: mk-labs
|
||||||
|
alert: false
|
||||||
|
max_parallel_tasks: 0 # 0 = unlimited
|
||||||
|
|
||||||
|
keys:
|
||||||
|
# The ansible-vault password. login_password type with empty login
|
||||||
|
# — only the password field is consumed by Semaphore at runtime.
|
||||||
|
- name: ansible-vault-pass
|
||||||
|
type: login_password
|
||||||
|
login: ""
|
||||||
|
password: "{{ vault_ansible_vault_password }}"
|
||||||
|
|
||||||
|
# SSH key for the gitea deploy access (clone the homelab repo).
|
||||||
|
- name: gitea-deploy
|
||||||
|
type: ssh
|
||||||
|
ssh_login: git
|
||||||
|
ssh_private_key: "{{ vault_gitea_deploy_key }}"
|
||||||
|
|
||||||
|
# SSH key for the universal automation account 'wed' — pre-baked in
|
||||||
|
# every mk-labs VM template. This is the canonical user Semaphore
|
||||||
|
# uses to reach the fleet.
|
||||||
|
- name: wed-ssh
|
||||||
|
type: ssh
|
||||||
|
ssh_login: wed
|
||||||
|
ssh_private_key: "{{ vault_wed_ssh_private_key }}"
|
||||||
|
|
||||||
|
# SSH key Semaphore can use to reach the fleet as jarvis (admin
|
||||||
|
# account provisioned by linux-baseline). Retained for jobs that
|
||||||
|
# specifically need jarvis-level access; the default is wed-ssh.
|
||||||
|
- name: jarvis-ssh
|
||||||
|
type: ssh
|
||||||
|
ssh_login: jarvis
|
||||||
|
ssh_private_key: "{{ vault_jarvis_ssh_private_key }}"
|
||||||
|
|
||||||
|
repositories:
|
||||||
|
- name: homelab
|
||||||
|
git_url: "ssh://git@gitea.mk-labs.cloud:2221/rblundon/homelab.git"
|
||||||
|
git_branch: main
|
||||||
|
ssh_key: gitea-deploy
|
||||||
|
|
||||||
|
inventories:
|
||||||
|
- name: production
|
||||||
|
type: file
|
||||||
|
inventory_file: ansible/inventory.yml
|
||||||
|
repository: homelab
|
||||||
|
# wed is the universal automation account pre-baked in every VM
|
||||||
|
# template. Semaphore uses it for fleet-wide jobs.
|
||||||
|
ssh_key: wed-ssh
|
||||||
|
# become_key is Semaphore's sudo PASSWORD slot, not a second SSH
|
||||||
|
# key. wed has passwordless sudo on every host, so reference the
|
||||||
|
# built-in "None" key. (Semaphore rejects an SSH-type key here.)
|
||||||
|
become_key: None
|
||||||
|
|
||||||
|
environments:
|
||||||
|
- name: default
|
||||||
|
env:
|
||||||
|
ANSIBLE_HOST_KEY_CHECKING: "False"
|
||||||
|
ANSIBLE_FORCE_COLOR: "True"
|
||||||
|
# Semaphore runs ansible-playbook from the cloned REPO ROOT (not
|
||||||
|
# from the playbook's directory as I first assumed). Path is
|
||||||
|
# therefore relative to repo root, not playbook dir.
|
||||||
|
ANSIBLE_ROLES_PATH: "ansible/roles"
|
||||||
|
# Collections are installed by the semaphore role into a host-side
|
||||||
|
# directory bind-mounted into the container at this path.
|
||||||
|
ANSIBLE_COLLECTIONS_PATH: "/opt/ansible-collections"
|
||||||
|
|
||||||
|
templates:
|
||||||
|
- name: "day0_linux_baseline"
|
||||||
|
description: "Apply the mk-labs Linux baseline to one or more hosts."
|
||||||
|
app: ansible
|
||||||
|
playbook: ansible/playbooks/day0_linux_baseline.yml
|
||||||
|
inventory: production
|
||||||
|
repository: homelab
|
||||||
|
environment: default
|
||||||
|
vault_password: ansible-vault-pass
|
||||||
|
arguments: '["--diff"]'
|
||||||
|
survey_vars:
|
||||||
|
- name: target
|
||||||
|
title: "Target host or group"
|
||||||
|
description: "Inventory target (e.g. figment, semaphore_server, all)"
|
||||||
|
required: true
|
||||||
|
type: TextVar
|
||||||
|
default_value: "all"
|
||||||
|
|
||||||
|
- name: "day1_deploy_semaphore"
|
||||||
|
description: "Re-deploy Semaphore + PostgreSQL on figment."
|
||||||
|
app: ansible
|
||||||
|
playbook: ansible/playbooks/day1_deploy_semaphore.yml
|
||||||
|
inventory: production
|
||||||
|
repository: homelab
|
||||||
|
environment: default
|
||||||
|
vault_password: ansible-vault-pass
|
||||||
|
arguments: '["--diff"]'
|
||||||
|
|
||||||
|
- name: "day0_linux_baseline_check"
|
||||||
|
description: "Dry-run the baseline — shows diffs, applies nothing."
|
||||||
|
app: ansible
|
||||||
|
playbook: ansible/playbooks/day0_linux_baseline.yml
|
||||||
|
inventory: production
|
||||||
|
repository: homelab
|
||||||
|
environment: default
|
||||||
|
vault_password: ansible-vault-pass
|
||||||
|
arguments: '["--check","--diff"]'
|
||||||
|
survey_vars:
|
||||||
|
- name: target
|
||||||
|
title: "Target host or group"
|
||||||
|
description: "Inventory target (e.g. figment, semaphore_server, all)"
|
||||||
|
required: true
|
||||||
|
type: TextVar
|
||||||
|
default_value: "all"
|
||||||
|
|
||||||
|
- name: "llm_inference_multimodel_stage_models"
|
||||||
|
description: >-
|
||||||
|
Stage additional GGUF models into /opt/models on astro-orbiter via the
|
||||||
|
llm-inference-multimodel role (--tags models only). Idempotent: skips
|
||||||
|
files already present at the correct byte size. Notifies the
|
||||||
|
llama-server-router restart handler ONLY when a new GGUF is actually
|
||||||
|
downloaded. Does NOT touch Phase 4 (verify) or the legacy
|
||||||
|
llama-server-qwen service. Safe to run repeatedly.
|
||||||
|
app: ansible
|
||||||
|
playbook: ansible/playbooks/day1_deploy_llm_inference_multimodel.yml
|
||||||
|
inventory: production
|
||||||
|
repository: homelab
|
||||||
|
environment: default
|
||||||
|
vault_password: ansible-vault-pass
|
||||||
|
arguments: '["--tags","models","--diff"]'
|
||||||
|
# Scoped to --tags models:
|
||||||
|
# Phase 0 (discover) -- skipped (no tag)
|
||||||
|
# Phase 1 (models) -- RUN (idempotent GGUF staging via stage_model.yml)
|
||||||
|
# Phase 2 (systemd) -- skipped
|
||||||
|
# Phase 3 (firewall) -- skipped
|
||||||
|
# Phase 4 (verify) -- SKIPPED (collision risk: verify.yml would start
|
||||||
|
# llama-server-qwen on :8002, conflicting with the
|
||||||
|
# production llama-server-router.service. Excluded
|
||||||
|
# here deliberately. See t_730f9584.)
|
||||||
|
|
||||||
|
- name: "llm_router_update_unit"
|
||||||
|
description: >-
|
||||||
|
Re-render and reload the llama-server-router systemd unit on astro-orbiter,
|
||||||
|
then restart the live service so new args (e.g. --models-max) take effect.
|
||||||
|
Drives playbooks/day2_bump_router_models_max.yml. Added 2026-08-12 (t_33acbb2e):
|
||||||
|
bump --models-max 1 -> 4 with full VRAM budget note in host_vars.
|
||||||
|
app: ansible
|
||||||
|
playbook: ansible/playbooks/day2_bump_router_models_max.yml
|
||||||
|
inventory: production
|
||||||
|
repository: homelab
|
||||||
|
environment: default
|
||||||
|
vault_password: ansible-vault-pass
|
||||||
|
arguments: '["--diff"]'
|
||||||
4
ansible/group_vars/all/step_ca.yml
Normal file
4
ansible/group_vars/all/step_ca.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
step_ca_url: "https://turnstile.local.mk-labs.cloud:9000"
|
||||||
|
step_ca_fingerprint: "f63c44e76381e359978bd2dca07c928d04ad44b575f0364fa66b5725c7e7891b"
|
||||||
|
step_ca_provisioner_name: "admin"
|
||||||
47
ansible/group_vars/all/vars
Normal file
47
ansible/group_vars/all/vars
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
# file: group_vars/all
|
||||||
|
|
||||||
|
# Proxmox variables
|
||||||
|
proxmox_user: "root@pam"
|
||||||
|
proxmox_password: "{{ vault_proxmox_root_password }}"
|
||||||
|
proxmox_host: "main-street-usa.local.mk-labs.cloud"
|
||||||
|
|
||||||
|
# DNS variables
|
||||||
|
dns_server: "monorail" # .local.mk-labs.cloud"
|
||||||
|
#dns_admin: "admin"
|
||||||
|
base_domain: "local.mk-labs.cloud"
|
||||||
|
# DHCP server
|
||||||
|
#dhcp_server: "matchbox"
|
||||||
|
|
||||||
|
# Terraform variables
|
||||||
|
terraform_server: "infra01"
|
||||||
|
|
||||||
|
# Traefik variables
|
||||||
|
traefik_server: "lightning-lane"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# JARVIS automation account
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Public key for the 'jarvis' user provisioned by the linux-baseline role on
|
||||||
|
# every host. Public keys are not secret; the matching private key lives on
|
||||||
|
# the JARVIS command centre (carousel-of-progress) and, when needed, in
|
||||||
|
# group_vars/all/vault as vault_jarvis_ssh_private_key.
|
||||||
|
jarvis_ssh_public_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID5sym5ajFvDyzw395BkHv7qVb66XPTx/OF1p19MGuNo jarvis@mk-labs"
|
||||||
|
|
||||||
|
step_ca_principal_mappings:
|
||||||
|
- local_user: wed
|
||||||
|
principals:
|
||||||
|
- ryan.blundon@protonmail.com
|
||||||
|
- ryan.blundon
|
||||||
|
- ryanblundon
|
||||||
|
- local_user: rblundon
|
||||||
|
principals:
|
||||||
|
- ryan.blundon@protonmail.com
|
||||||
|
- ryan.blundon
|
||||||
|
- ryanblundon
|
||||||
|
|
||||||
|
# Leviton My Leviton API
|
||||||
|
leviton_email: "{{ vault_leviton_email }}"
|
||||||
|
leviton_password: "{{ vault_leviton_password }}"
|
||||||
|
|
||||||
|
jmri_vnc_password: "{{ vault_jmri_vnc_password }}"
|
||||||
365
ansible/group_vars/all/vault
Normal file
365
ansible/group_vars/all/vault
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
|
66393233316132396639356564316439343234383066633231646134313361666463656536323732
|
||||||
|
6630616536646439613533363430306466306233643730350a343364633233333335643833326163
|
||||||
|
64393933613963313533623733316339396236363663343635346663323366663166363839663837
|
||||||
|
3331363062653239380a326566623264623837326636383939346430666537613361333638366630
|
||||||
|
63353062393335316663633739313532366363623739653631366539323435336361353331386230
|
||||||
|
39366634643964336233353961316630616462663166316266613037623363346335373638656365
|
||||||
|
38353733396636386133373836346336383231663661346137373164386338623733393566373563
|
||||||
|
35653933343036633365643535303934326537356136666539316137363433643266346630386439
|
||||||
|
38613332646238366536333536343031356532656336613530663830613264346339353034323362
|
||||||
|
66613530626361323535653232313730373463373332313561616631393461353730653464343063
|
||||||
|
38616338363938346161616636316232313838616463326432353639613837343162646363343232
|
||||||
|
33323064363139376566343866626364373662393138353666646234373461666163363139313631
|
||||||
|
64343261326566363265323463663538343034306136326234386664333837333937333136653563
|
||||||
|
61333531353434633339383661636363363535316366353330313566323133616438373161303135
|
||||||
|
35353630613037316466353832333033393030636331386438393133366333653832393731366363
|
||||||
|
62373638303737393162303461646239653865653834613662666636373364633165383062643831
|
||||||
|
34386232376361323638353361666530366432356331353963303930326535663536373339333062
|
||||||
|
32396266373430343339636635366434313635313766363863336464633961666332353834626163
|
||||||
|
61653637316163636465343630353431313863653033643237356434313564366361373435376662
|
||||||
|
63643737353830663236643862613533623237373531646136383763303766336139303632666235
|
||||||
|
64623834323966363363663730626437323432623966663537346162656265363562643836633731
|
||||||
|
65663631633462663764393132326165346639353033633035636432613039336164303538396632
|
||||||
|
66396264393865306666643636353638613661313230313337383634663839363439656533333932
|
||||||
|
36633432306131396539386539633063653230363932376264323537396434353364643432653661
|
||||||
|
32643162363066636432336363323534316436613838646562313538326566666239633234646236
|
||||||
|
30346534636533623365326564613561363362333364363037646561656635623935653466613565
|
||||||
|
65646361313436356261643762313339333864356338386136306162386262636464393130303963
|
||||||
|
38646464316432326431326661343632396235626234366133353461623862316662326432356234
|
||||||
|
37626366383861373831633639616465663564643866356664623066386535646163336134356534
|
||||||
|
33366664616232353863626465626364313530353335306565336665663866303736323162393362
|
||||||
|
63626261653161663664363833313461653034326330653835393737616135646462366665383935
|
||||||
|
30363639306330636634386433646231363530633061336364313338653632323831393630383934
|
||||||
|
37316362326338313733646332336263386239626539383330353362616132333161613464313066
|
||||||
|
34663434326662326233363432306433363666356132383866346336336261636435366332666135
|
||||||
|
34616231613638363339356333616536643266636363643131653330396162306264303566396461
|
||||||
|
64363763376365356533636430643866333361363062376237653237663731663934306265646630
|
||||||
|
33393637656335643366383564373966343265393630333835303731316339373133633462383364
|
||||||
|
33383435383331303264313334393532373932333334343862326635346135613932356337373034
|
||||||
|
62316262326331313135376465343336373266663338396533666431616462613932663861646238
|
||||||
|
62653563623535633738383033326235383666646333653731316233376231623661306462303732
|
||||||
|
32643064373236613336396233323435393939386530323331336138353364663762356538316562
|
||||||
|
33376530623664623733386133333433303031373337313366386236376539613964316135343865
|
||||||
|
33363963366165333238356663663435386439336366646138313034343636653463323938633136
|
||||||
|
39363966376238306662303265643034306136663661393738633436393432303139313132616534
|
||||||
|
66323432313635386162333838323136623634653264643438303264636430633232323434666532
|
||||||
|
32616664663063653735316237643539633133356661333132323238376333356464313262653836
|
||||||
|
39303566316332663737323437633031353330333365383837636336643763313433313937396531
|
||||||
|
38363536343438663966663436613132663661613134383431633765383164373762343435316161
|
||||||
|
62303631646235343063383230343232383336356562303563373933346530393333316634316437
|
||||||
|
32316330306163396434663031393965663163666537353031613365353437666466333464626238
|
||||||
|
35313739646535356665323734393965303064306132626261363062363438383164346261393463
|
||||||
|
30643438623363323161323230306230386332363635386234666639623566643536626637616533
|
||||||
|
37396136643930633262333331656363376433333234343630306535313262306235663263663362
|
||||||
|
32653434363035613732363136303363393939323337613661333439393637646262383039386661
|
||||||
|
62326163323562333339323636363565623664396164383332633666386130613766393138346134
|
||||||
|
33343338393536316431353439353062663164643634396363353131303038353965393466383030
|
||||||
|
36656465383938353936346361393963356630666630373236626237303064303062383638373730
|
||||||
|
35633866646535313432353338623462323235346433653431313031363163393666626432363238
|
||||||
|
39623361316132626230633336636163623466313666346631656134343762656566353432353264
|
||||||
|
30353436356237653231363564626134633039363035313232616333336436393638396233626638
|
||||||
|
32663230396539323761313838313466376165646430346634383332346134653662393161363337
|
||||||
|
62646161343665383364306665333164666231386531626465373366623761643161656462303733
|
||||||
|
37653438616233353432626466623163316565353764323762613635333832343634323665356336
|
||||||
|
35353162326233333836396337356466636131383838313436626336663132346339623261366465
|
||||||
|
30623261303933396562353331636638376135663330643638643536346261626632626139386535
|
||||||
|
66653332366361336636666437643165656239613031303638333232303836383132616636633938
|
||||||
|
31343034643037623731643931316463303639656266323231313666356336333133323135363330
|
||||||
|
63373365303131353161303630633738353536393631353034666139383435303461316131646138
|
||||||
|
61333731356538366366613831303565613365633965323235366166313534653965366433656533
|
||||||
|
62666136313662366638356237343734336333313034396465346632336262306531633535643238
|
||||||
|
35333831366532386235316565303936616264373337356134643066396531383533353336303131
|
||||||
|
31393837623564386535323532653733393734393164373235396566333565356237356438313762
|
||||||
|
32623765326639386262393639376461326163333237313232386138643130643231626466643663
|
||||||
|
39643061393566353434333136366335393536376234366266376265333234643536633035653933
|
||||||
|
36316132663539306465343039323935356361373439346437386234386464623962643464643562
|
||||||
|
61336434613834336161633237383361303930313464613666313834356330343138633735386530
|
||||||
|
36616233323366323961653965613438346136373738366266316134356266623664313539636235
|
||||||
|
37313033373466383134346361646562366531333338386330653736626530396238356639303131
|
||||||
|
38363738396236386461316433316261326435646130383336316234363461393237623633633336
|
||||||
|
30386630376565646337383738663939663462623232316635346635653830306664653336343033
|
||||||
|
64316430396664393532313766326437636636626232613036666666656430323136356436333564
|
||||||
|
36303334303562393832336433343438396430373833623137363736386665343866313064353063
|
||||||
|
32303634393131326464656535633734386462646339663533666430336265653965333538633866
|
||||||
|
61623666643839653239373335633735373738363736313665323365613635313766656635613832
|
||||||
|
33616461643539636165383233636533626230343138663630323731626139393230383464313430
|
||||||
|
61333438393337316239376435313337313437333931623238616133666138363235386533633437
|
||||||
|
35356163363231656536353934643539643562343732626630383565623730626533313230656164
|
||||||
|
35333735666135343364663233626163363930383262363266303265303638396239636361366534
|
||||||
|
31333863333565356135613232393165353266343632633532343061663331633337343538376265
|
||||||
|
39316630613439356262396634316361356436336634396337353339616536356336653930613966
|
||||||
|
34656439653366363562636639346430623561303463356337363830373966366632303337663564
|
||||||
|
35663632313265323365636238303364366230353039353561616636633664643233343430336237
|
||||||
|
63373264643935616331616632633065366638363833306337633563653065363464343137623533
|
||||||
|
36373231363739373335346464623533393336613634333636613937366136326464336332346166
|
||||||
|
61376263623835646163353134643963663964373732313833346163323138633230393537636664
|
||||||
|
30366234303334656130336630346130656237306161376566336534653630616439323764373665
|
||||||
|
61383338326163336164353265326163646165623235626137623237306666333832306461613630
|
||||||
|
66373331356465346261643466323662393661623433383265376666623932343861323139383531
|
||||||
|
64633536373362643935633734366235396433333237306166646164363930613862613365303663
|
||||||
|
38343833336137353634313362666665306666393635663633353934363832343739616331386130
|
||||||
|
66336561633039326434313833303465366638303961626138333165623331386230616130626639
|
||||||
|
34613962366230333065633761333335613636363533656461626632343631666563383738623330
|
||||||
|
30333834346233653938633330663166616331376436356533366461336264643264336139343262
|
||||||
|
35393665656230663232366133393037643536366234343537326631623332373131323739363638
|
||||||
|
39653162646366316639313631393631666261623230313538613666393732626438393763646330
|
||||||
|
36346661313131313630343432616365666633353762623261613039623331396330623939626132
|
||||||
|
34626333386538326434356432623965666662663437646237373537326534653634346239653634
|
||||||
|
31373038303639333037613637393862356263323066666630313262366633313932396465633337
|
||||||
|
66653930303934616236323064613761353935613835356561313334323762633064306661346666
|
||||||
|
37653262343865386236343634316336386630393739626437333065323433613531393738313432
|
||||||
|
31376233353463373237653164386363633334366332356538343966663939656165323465333030
|
||||||
|
39656532363363333432626638626438396539336461326338353732376235316133616666316261
|
||||||
|
37353063343366376433653961333233306461303133376661303332386230346231383837396133
|
||||||
|
37323137343066383966343535633363643233663530613566313330336232366638396165373631
|
||||||
|
30626531363033313833303836366434613736396339643032663066333865306535323739666162
|
||||||
|
64626133616433653864376662623464343131303938303237316264393765303035663833376464
|
||||||
|
32663264383236303766323935306463643138396237373338653238633464616238306132633735
|
||||||
|
31626538653262326533326266336633623532623935383266373533363466313033393235663538
|
||||||
|
66653038646233303665343634383666343363383238326533366136363838303332323230316662
|
||||||
|
35383235646638653539633961663036663933306463626335356631646662636230356261363261
|
||||||
|
65633261353830373865636630353932323937666331353635373736376436333361613330366633
|
||||||
|
30663939356165393132636131663966373433623063356265353131306532643066306630656363
|
||||||
|
66636636353262633437663264613266613663656137386231306231646264363661613035343538
|
||||||
|
37353633643065643236376537336238663137623735613038623766393231643131653436333262
|
||||||
|
31626463646432613563393665346532386161366435396364663239386236616233356131323536
|
||||||
|
33633936623762666534633862363466353736386137636363633733623366346337613365636439
|
||||||
|
66663035313430386464623833646135333062313830396637323961386135363461326539623432
|
||||||
|
32653865623530313637393561343465636430373162333162646631643235653931333830326266
|
||||||
|
36376631316165343631326165623838306239623764363262376634663236393933343838376663
|
||||||
|
39663834306165313330393739363133396436376437643232346336386531356638343063376465
|
||||||
|
61613037623137306666383231376539656361326132396662613061376134376266633764336266
|
||||||
|
64336334313335643635303632666431383637306334376462643630646339396435313830313363
|
||||||
|
33383162316261663035393962306234613865613366353465373035656434366261383133653331
|
||||||
|
63643235616362663663343330303765363263366130393837613939323264373937333162636639
|
||||||
|
31643438666338646135663538343231643235646364623761653064633566656663383465626133
|
||||||
|
31373935646266303565303539376162623132316438623565316537306337636630313861623937
|
||||||
|
34313832636533623033616139373965303839356530353935643363613464356364343162336466
|
||||||
|
66376130653162666661313139613530306666633432346639656466653364376435636461626362
|
||||||
|
32653633303561346233643463373534653434323134353434373839373937626663336464303866
|
||||||
|
33363264623038313835396231373132396163363662626264346461333539326365326165323066
|
||||||
|
62333139383334333334353031616430323339623066363232313937323465356266323934313761
|
||||||
|
66623835653961303830383030643537393130653935313265333062393034336562633535323263
|
||||||
|
65616237373232336534393834653162363461336262653862666637326266663966356665363036
|
||||||
|
35383437326465663635303664643236633435303862633965346133376536316233386333313634
|
||||||
|
33643565326633386565653961646463383866646636303537643436623734393234633938333933
|
||||||
|
65366164633165623333623362393639656661326332306538663738356364373734316563653038
|
||||||
|
34646531616662386232613034366332656262343164333531353037363036646262623663666236
|
||||||
|
38613238666136363431623664633863636365396236666532383930336636353031396232656435
|
||||||
|
61346238643431653231623861373964383931336535363262373437353532393165316562386134
|
||||||
|
36363263666135646237383666373833373737396330616163376439663736663937666161313831
|
||||||
|
63663531656635663339306365656663636633343733636165386230376332616331313638386538
|
||||||
|
32386466323232363533613334333333346161376430373436373961316564343061326164306138
|
||||||
|
33616263666262323430303730626266396535626439623364376239346564323730323534323938
|
||||||
|
33346364393033353865393864326361643734353234613563393138363334383536396535393166
|
||||||
|
34623163616336653436393639313965353237633566313039303137326234383230323235363234
|
||||||
|
37626161356166356365366164363863636563316332393638616535376466343537373966643839
|
||||||
|
32613930643533336264626136626465303339376632323034386161663661376466616233633065
|
||||||
|
66313739346162363838346663623266383130383736656334323430623463666439386532643630
|
||||||
|
33666639613830386136363535363830333234653961663739343537306634616531616263623762
|
||||||
|
64666230373830636238353062666330623061613663376638343763626264363130313464383661
|
||||||
|
38326530333362616163363735323861376366333665623536383566653837306131623732373639
|
||||||
|
31316661353332633630326162663738636562336666326637353764323431613666303038373532
|
||||||
|
62343661336338306561356235396636343130633365303466613637633363613862663233633731
|
||||||
|
66623530353132666261316637303763363830623734346262333633646238613131346564303734
|
||||||
|
62336434353432326239333232383833633962313537626430663130393733623162626131656366
|
||||||
|
64333535623138326239336165666562376663663334323036323539653734333835386331653438
|
||||||
|
63353861666239396437346361306634613462386335376137333963333838616138633730393865
|
||||||
|
62353539376136316564666136646639363635663736636439393462633165646632623664383663
|
||||||
|
31613137306461616361323832393036323933626531363536336261356636303531633239333362
|
||||||
|
32663134363263383039646162643539663737333861386437326337616362343963373532346238
|
||||||
|
37346137363933623839373838353939386630303461346438666534616434333031373730393537
|
||||||
|
30313134643963623564356266656430613430626238613266316335336265613132616562626261
|
||||||
|
35386435313933626634616463616166646466363939313639646264346464363337656339323366
|
||||||
|
36366665363739356564363232313762323565323134616134666337336534353464373637373130
|
||||||
|
63613265366436313131356332316531633732356461383064383031613337343363646432373936
|
||||||
|
33633339386632653032663837346130623636356464326637303338376132623734333932396232
|
||||||
|
61333033386265356630316134383066343164613130666664643732643362666561346132656266
|
||||||
|
31623633333039633837383264363937623435643061393935393762346430396335373864633634
|
||||||
|
33336136353332663366313334353739303539633364663231636539333132303966383432376262
|
||||||
|
61356563323232613433653262623663336634626532653465306638316633663564633862666666
|
||||||
|
30366133616336326661626238653933383164336366333438626235636631336165386664343736
|
||||||
|
62663961346664656333306435323833366632346366356238653731653937626333653630623334
|
||||||
|
64326662346138386433333232643262333835326263343239353264373038613634356436396630
|
||||||
|
38323931643361663238623766323930666130356339363564366661663033303831363138343737
|
||||||
|
66633535326131396236653261303836613364306537633637323031663166316338323533323731
|
||||||
|
30326235323066396663613531653061643661336631613835626266626436386662353465383065
|
||||||
|
64396562343966303362636136616438353661626466636635323961613438646634336563636534
|
||||||
|
38343566396530643961356434643933636235643561353232643062303232323437666261363061
|
||||||
|
62636530396466303466653333633930376465366561376363316137323263333561343334383364
|
||||||
|
39313863643062643766396564363137386231373136346138396162376264653538303464633161
|
||||||
|
61663363623937356138356430666461623130323466623162653863393736326264393836336637
|
||||||
|
34663931646566333535666664653237643732316663323230383239393763376266356135326438
|
||||||
|
35656266353865623663373366373830613361373664346632363031356265313364623866643438
|
||||||
|
61363866353934636337616239633330623734666138396166313864333939663563636138653930
|
||||||
|
35653137363033326432373661613434313137623163356134613265393238346438306165313639
|
||||||
|
36666662393165353565633531663536613037623230373063316639663632643139353235303462
|
||||||
|
31393263656265656131613164363035343233626433656135353331613532363236616439363731
|
||||||
|
62666432356363323937666435326638323437346136366131613636653430306131623966356263
|
||||||
|
39303739306233303862636535633431363630393432613663633836396566653039383735303336
|
||||||
|
62623331623034636363636661653236386337326666656532343737336262336462613762326531
|
||||||
|
34303066303834386366636430343161653665343362363038396562626133636135656538306435
|
||||||
|
36393364333066346238396362663664643236373532336263656233386663323663623137343462
|
||||||
|
64386337333130316434663564613665666238623132343437656637653035373738313735366630
|
||||||
|
66383639616166393265616434623463326437313530326130376339313662303836636664366232
|
||||||
|
32366634633030333130316435616233396231663937343732313066373834326464623139363663
|
||||||
|
31323931626364303230666162316436653065366137663631376265383063316534343736373261
|
||||||
|
31613637316235386539343766323439653062633137663730343236343661346162653366656332
|
||||||
|
32663932313063383561636266373766633535656131386133386135663863396261306530326632
|
||||||
|
63653936626236316539613262386231616433393064323461626536363831666461316131383837
|
||||||
|
30646266646266393666396362326238613231303335336532303836363264323233343534636635
|
||||||
|
66313538643033343262373463363866346566353263303966323933383963363463393761383865
|
||||||
|
64663932343830643531643466303438343161396133666463353762393737613036646166333265
|
||||||
|
66376231613232666164663964636134653061633330383863373836306366393838393235656331
|
||||||
|
35613231306263373230326634623262326333356263353961633836396531633431383163633361
|
||||||
|
30356534666466653734333437383964346564346165326664633738653338313263633837316531
|
||||||
|
36613034323433643839333264323864613033313137663131623265643364333664646235666232
|
||||||
|
33393039313666323266643362323337316465306564303230646561303434666630616137633831
|
||||||
|
34346439616634343337306636643733316464376631616266376437636439396337306637333432
|
||||||
|
39306333363035393436316434656436353738303861633933376531383862316466373736323639
|
||||||
|
35336137373866336631386436646231653366366435363932376434303063613961353261343661
|
||||||
|
33383638393165336438376662306431333837356435626137356130323836396335636166306662
|
||||||
|
36386161353739353637353861306666383966323339303262616239633930373633323937356632
|
||||||
|
65383032613031666665623631613430666662656336663931646533636230303261646530623765
|
||||||
|
64643939326435643539373564336531623236653731636636346361363064333963376566616530
|
||||||
|
62326639663632666634326233363635383830643163373938646165656163643864336436373466
|
||||||
|
36353832306632386230373832333234643638313238626333303963383962343265366137656136
|
||||||
|
37333261343161633562346638323632616566646162633133663466346535656463393932386135
|
||||||
|
62653332313066363965386335356430326539316366633537356364666230326237306236393563
|
||||||
|
35323363633936353034323232353366373566666332323737653237323135646665626139393436
|
||||||
|
65333762653536656161386532363765336538653763666236343166653933626666633130393033
|
||||||
|
31643531646633623663313237353333313136663863663430306131316165663765653732663164
|
||||||
|
36326531626365326330643064336230313466343731376437316563303339336333326636633066
|
||||||
|
61386135626430616661313236623030316362373338643233326365646531633265626238383830
|
||||||
|
65346132643537366537626132666165616138656139626132396639376230333262643766386363
|
||||||
|
39383034663034326165643636613237623234613666333532383733623462303331326238636461
|
||||||
|
34383139383466356139333934353837613964326538336463643832623062633034613762363061
|
||||||
|
61346361656363366136353336326433326266336564316366393565626262303637316564356566
|
||||||
|
39376661383763373436306238393666653561306538333638306233356139346634653363346164
|
||||||
|
31303835383062376638626266303237323832623735653066353936376339633637333562333561
|
||||||
|
36646462653834316131323166366661386161646538346464386232306239363030366363633663
|
||||||
|
65306439616339626635326531636435356134376561303235393337373564373937623636643432
|
||||||
|
61393535643038626562366331353831663338333838383066323632383633346564396566653330
|
||||||
|
34386563393832313537623061666466366661333934613766366165366330353835323637643635
|
||||||
|
38613363396663356564646132613536653033616337386566623662333832383938303138316662
|
||||||
|
34303339323166383863363831636233323335313565393933636435396666313337663037323432
|
||||||
|
63333139333165646262666339343736383966346133356138326437386334626461636530336432
|
||||||
|
61356631366163366561303636333230643732316261376365386463333565623533663966336365
|
||||||
|
39333365393766306536366231366435363030353263393534653534373064636361333532323735
|
||||||
|
61313163323831653362356333386638343566356261353534303738613730373632363534666337
|
||||||
|
62313339613138646361356431616236613435393233343732626263653332663265393934616134
|
||||||
|
35313165613766643938393839373261633439396661623961353934373130623865353639633038
|
||||||
|
34353631346433663131653965326337663561613330323562666336656237633163356562323931
|
||||||
|
62393833663233333538383063303937386365306135343962623333663435663431396438666362
|
||||||
|
61386266353838653532393233353939363738306634666537313761313835333864633764666262
|
||||||
|
30333032623766633334383031636633636539336237613235376466356430663938653565626235
|
||||||
|
64373537656566306136633630366130363630633462656330623633393735386630343437336436
|
||||||
|
65343635366531646130616534623136636666323139326462306533653532643962656530336633
|
||||||
|
35616537303932343539336638333730663639396330653761346136363431346536666138336462
|
||||||
|
66396565613166623934316532383835316137303134363466306163356233356530323231666464
|
||||||
|
30353933306530323734306564626234343864373964333264353366326265316333343330356532
|
||||||
|
38363837646635633461653562303264353633343461633339376665616331613733666663353130
|
||||||
|
65396363613731366234326466323738663563646166653237613364323734616465643764633537
|
||||||
|
35373865353532383566363632366564353536643739663761303565333138383638653665663664
|
||||||
|
65643366316461613630366437623736353739356538336237613431306363663234373265623962
|
||||||
|
34653565373335653563356135313835643266356261623037336536613733323733363933376538
|
||||||
|
36626134396563623733656534363331626262643339633932373035626134343531623634666463
|
||||||
|
61623036313334616639633930393562663631653565656136666537393731333430663062643362
|
||||||
|
63633663396562343965313261373965356163393538666466303661363531393266316462626166
|
||||||
|
38616536653665366462383064373766396438616665346666376232653031323566313164383164
|
||||||
|
36643231646439663637333165376439333432383532316661333766363136636236326338386537
|
||||||
|
33306130353634346136356234363438383865313136393839663066333935623565333730613538
|
||||||
|
63323831663866363831383930303434333936646564316435303931396362303534386335343330
|
||||||
|
65383635346662626363626365666166636361633365643735303762393832316436646139303835
|
||||||
|
63633733656361643233323332613632653837663262306661626438316262653931333061336366
|
||||||
|
35353939353835333361623261613738383734656132613139393264393038373765343131333330
|
||||||
|
37663962313566366463623437323965326365623437363038633661313461383634626661666236
|
||||||
|
32633335323861643037383261393164393933353531636134323765353962633732396230636331
|
||||||
|
32613865373739366530303538313566346434633933393330346637346136373036306666336164
|
||||||
|
35373732346334353432616561623031663331346431383235306537386466623339356366663335
|
||||||
|
36333733626433653465336431666530626665373564336339626163633131353330656437643638
|
||||||
|
31616563616665343635356231633135663665326131636664373338323736393364353636613762
|
||||||
|
66636135633766323866376235633535613735613465303239343036663438333331626431623435
|
||||||
|
61323537386434323638666537643236623632626430666263376534643336613635663762643736
|
||||||
|
30323237353265613062373265643562373637383337326264653639306263373865333262376665
|
||||||
|
62646331373931373762303461366163393839633135393964313937616437323865653735383630
|
||||||
|
32653936336534666565373437666130396265363561333635316461663766346336623865376133
|
||||||
|
35373665303433326265623531613038636166643130616637653165376263643634376439613765
|
||||||
|
35363031373630333966656466616235616337306335363132386335613462363664653634633864
|
||||||
|
61303635653932353730663666386263633662633736313461643932386161313762663761313336
|
||||||
|
62653665313033656537643936373465633932626166366430643763313030393838393039323230
|
||||||
|
62633334383938343433306262393536653930653030393033306661666264313630643564333166
|
||||||
|
35383237633932656331653030363434313534613637373465663264643061303538653666653861
|
||||||
|
35373936643037333866636131373338363062663035323531626431633362663364396365353139
|
||||||
|
66383737666437353764333231303662393630643933376161366430376530613365363830373534
|
||||||
|
38376633333936626430393163323830346166643537326430616236393733653761363235356363
|
||||||
|
35333131663032383861336262653936376565646662313965303265623763613330653461333835
|
||||||
|
63613563323135633438383931343731656333303362316533376339376636623037376431336366
|
||||||
|
61393236383364356162633062666265653534326363363862666539623761623065386537616563
|
||||||
|
62666561316437303763376635346536666437373361386666643139643737663333323933613661
|
||||||
|
38326566663932333930616435626133616531306461356466326437623235613233393434626563
|
||||||
|
34373966633834373430386132353163366465626262353863353335323830393266393562393133
|
||||||
|
65383632653438646435343333386261653066613663623232373564666465613136353039313036
|
||||||
|
61646462626433646330396664363938376530376438646262343231393262383733636233636333
|
||||||
|
35633862336566636439653464613564333162613836343636316334316665383164353131373431
|
||||||
|
63623030306564346562346237333934616134346536303365396533626262333937396432393830
|
||||||
|
38313763393463646437666137353835373735646365373934363936346564326362376565353133
|
||||||
|
36653362333432326133393837316331666663663263396461363239306239363733633137396633
|
||||||
|
38393865613431653337313665313762653635656531353465623436343132303064303564393066
|
||||||
|
63616639353962366666616261393766643364333634346630616436376565313236316539633537
|
||||||
|
66653239636561393433383639646462616433653166613130373134376535633937353366383230
|
||||||
|
61613335663434333835653236343633633038346335333861356637353965396632393833646635
|
||||||
|
36653034356234663831333764303338663464316362646339376338393236336161616263363538
|
||||||
|
63356631613239326161343031643936623366643432663732346438333265666535623664396333
|
||||||
|
62303239363339626566613439396234303536333333653433393666383635643235376165666234
|
||||||
|
30336236393962666335353233666463346530323531316438373130303933383465316638646461
|
||||||
|
62643066376666363236333231386237376466633932313836323163363061313333633434663763
|
||||||
|
64323365353336333736363436376232653436633739613437343538633632356665656364616637
|
||||||
|
62313666346436656564663335636635393632353430666236313863613464626434323939383538
|
||||||
|
65386265343434303632313739353239323565333734656566356164643430613538333234383566
|
||||||
|
37646431316363316139646435333732313339623666613738663039613239613738393565333330
|
||||||
|
33376432373933656435303737653762666464363865633831373330393435633332636261336139
|
||||||
|
34336236373535636165353262323966363164633135613534353661316364616637663465363864
|
||||||
|
39306163346365643339643937396165666366663339336438373031613937636464383531613962
|
||||||
|
34373433663533626665656364623634373335313033646165303764396563356235343033383138
|
||||||
|
62326361353630393938643764616636313461633734646661386536356235656665393864386465
|
||||||
|
37666262346561656436343036646330363664306135333464663265306165353039396665336664
|
||||||
|
62346631366330653762646538323565613864383534636532633033643533323736373931643130
|
||||||
|
32343435333333613734626234363132373734353035326232366264336161383631353133663230
|
||||||
|
32636533333866343763336439373336356237303636376334333433376630353338333261333037
|
||||||
|
31666537363666653238383939663464346662636133346561326335346163363061393830616237
|
||||||
|
36643430626534653331653665316535303139343763663965363164636238366533303038653935
|
||||||
|
32356432316633373137316237663331336463363431393033323635646564366639346230353363
|
||||||
|
38346663363039363962323137383366303862356530353238656563306131643236626536656334
|
||||||
|
38303462306562366532346163323061393437353063326539393466616439346564383036303235
|
||||||
|
35633731393661323962633631373061303930323638326565636162316436646337383266626561
|
||||||
|
32326430363031396530396238353862333133363731623736376239626561626165663337373261
|
||||||
|
39353461343461643238646635633562653865323336366634613264616662323232653861663038
|
||||||
|
64396330626633303031333334343335393039623135353266383561313231643433393963326637
|
||||||
|
39313530636361373831306234383166346266656261663830636631333564356536323565336266
|
||||||
|
38306561376366626236306633613564386166616630613032633163613837313462343662653261
|
||||||
|
63353437663436303634633336636532646439636465663362346138313665336334313039613631
|
||||||
|
65326135383831613531323265353831313562346161663265366434623236636635333038366536
|
||||||
|
33646631663662393331323162343438626666366636613438383665633136326439376166373462
|
||||||
|
33363864613136643461663436396362643066633437376631623031613366656238396165313832
|
||||||
|
31313131653263666334393664343239306235373862313339373563643137393633343663613936
|
||||||
|
61356564636238363136623031336638333566633766636362303938653531306131396665303033
|
||||||
|
63353362636463636236643464343562383161343432383766396330623764393837613435396162
|
||||||
|
31303162356437663932663964663239623764666366663061313535346438373334636263653531
|
||||||
|
34643133356638653031373036343162653135663734623035353033633561366266623566383233
|
||||||
|
36356434643538643430383532393762333535636639353361353763333363313131646264336332
|
||||||
|
34373331343930633962623963666365306132356334646636626461316236343839383266363635
|
||||||
|
65623434336239313330343437646333353362303232346638623161616133636636626236643465
|
||||||
|
36303636363965363765656533386534633839346363363738386532386531326538643134363132
|
||||||
|
66613235373362633166343565323766306335336365333439323764623964393263623236623832
|
||||||
|
34346132383136303038363764333039626234616132386464666633663536656230666133363533
|
||||||
|
38306665643361666539636666316432623430623939663636343164386438313765633031313534
|
||||||
|
65393633323837326166343936326263343833646331326464376138633461613532303135393036
|
||||||
|
65353830373065393038343039323937303634346665393135383639303162396565646232663736
|
||||||
|
39376434646634353330383933303164653431373433346335666131386165343035303964626665
|
||||||
|
35383035653631346638326637326235393833623264323030373238646335346332353362393230
|
||||||
|
61616664613562383639306564376661306665396138613066326631616531623132633966633832
|
||||||
|
65363637376264336635633132633332373634383864626564623966356464373864393832323738
|
||||||
|
37616338646633326636323461376137663632376262363738303336616463326238333465343533
|
||||||
|
31316132386530326539
|
||||||
18
ansible/group_vars/ntp_servers/vars
Normal file
18
ansible/group_vars/ntp_servers/vars
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
# file: group_vars/ntp_servers/vars
|
||||||
|
#Ansible vars template for NTP servers.
|
||||||
|
|
||||||
|
ntp_servers:
|
||||||
|
- 0.us.pool.ntp.org
|
||||||
|
- 1.us.pool.ntp.org
|
||||||
|
|
||||||
|
allowed_networks:
|
||||||
|
- "192.168.1.0/24"
|
||||||
|
- "192.168.2.0/24"
|
||||||
|
- "192.168.3.0/24"
|
||||||
|
- "192.168.5.0/24"
|
||||||
|
- "192.168.9.0/24"
|
||||||
|
- "192.168.10.0/24"
|
||||||
|
- "192.168.250.0/24"
|
||||||
|
- "10.1.71.0/24"
|
||||||
|
- "10.1.82.0/24"
|
||||||
19
ansible/group_vars/proxmox/oidc.yml
Normal file
19
ansible/group_vars/proxmox/oidc.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
# ansible/group_vars/proxmox/oidc.yml
|
||||||
|
#
|
||||||
|
# Proxmox OIDC configuration for Authentik integration.
|
||||||
|
# Client credentials come from vault.yml.
|
||||||
|
|
||||||
|
proxmox_oidc_realm_name: "authentik"
|
||||||
|
proxmox_oidc_issuer_url: "https://authentik.local.mk-labs.cloud/application/o/proxmox/"
|
||||||
|
proxmox_oidc_username_claim: "username"
|
||||||
|
proxmox_oidc_scopes: "openid email profile"
|
||||||
|
proxmox_oidc_autocreate: true
|
||||||
|
proxmox_oidc_default_realm: false
|
||||||
|
proxmox_oidc_comment: "Authentik SSO"
|
||||||
|
|
||||||
|
# ACL entries - grant your Authentik user admin access
|
||||||
|
proxmox_oidc_acl_entries:
|
||||||
|
- path: "/"
|
||||||
|
user: "rblundon@authentik"
|
||||||
|
role: "Administrator"
|
||||||
17
ansible/group_vars/proxmox/vault
Normal file
17
ansible/group_vars/proxmox/vault
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
|
64613361376364346139313833613465663361336634326430393261366630306466363935613139
|
||||||
|
3634363235343736643230623865386436333734663531310a343965396534336262356234623966
|
||||||
|
39353332626662636666383935383530613139626439373664323063633063316264383331646533
|
||||||
|
3366333837613731310a656462633638376432326365343135373863313665346566383933353630
|
||||||
|
37303565323630633739396433323165326262363863386233343134636236646366633765616432
|
||||||
|
33633165613239653762343935386637393437386431383961306436373765343736313634333933
|
||||||
|
30376463646464303066613561613564353938306664373464333966383664383034616439343735
|
||||||
|
38363966323366343465393530383736393364373361326234356365363163356632393834393464
|
||||||
|
39393530356235383830323065636535383138353864373237333164323436623737383435313261
|
||||||
|
64366362626232386438376131633533633162356432613835656465623762633464353936303236
|
||||||
|
63353633633262633461353963653534663336396134373435303266386162393965343165303335
|
||||||
|
65313436666334383965373837376664323861376162373834363439653539383231346634633737
|
||||||
|
30626431373739633930313937643938343165383536373465613535366562313439396533643864
|
||||||
|
36323032316131333962373732306334373939333363386564653465303261393337333466316130
|
||||||
|
32326533353165393635393130396164636363623937313566343333386562616662383038613565
|
||||||
|
35386638383365633362
|
||||||
59
ansible/host_vars/arcade/vars
Normal file
59
ansible/host_vars/arcade/vars
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts created via cloning.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Ubuntu
|
||||||
|
# - 24.04
|
||||||
|
# - Fedora
|
||||||
|
# - 42
|
||||||
|
|
||||||
|
vm_os_distribution: "ubuntu"
|
||||||
|
vm_os_version: "24.04"
|
||||||
|
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
|
||||||
|
|
||||||
|
vm_size: "large"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "liberty-tree"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "pve03"
|
||||||
|
proxmox_host_target: "pve03"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
|
||||||
|
ip_address: 10.1.71.111
|
||||||
|
# vm_mac_address: 'BC:24:11:11:BC:58'
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
235
ansible/host_vars/astro-orbiter/vars.yml
Normal file
235
ansible/host_vars/astro-orbiter/vars.yml
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: ansible/host_vars/astro_orbiter/vars.yml
|
||||||
|
# HOST: astro-orbiter (10.1.71.130)
|
||||||
|
# ROLE: llama.cpp LLM inference host — Ryzen 7 5800XT / RTX 3090 (ATX rebuild,
|
||||||
|
# 2026-08-04). Superseded the prior AMD RX 5700 / Ollama config below;
|
||||||
|
# drive was transplanted into new hardware, not reinstalled.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ansible_host: 10.1.71.130
|
||||||
|
ansible_user: jarvis
|
||||||
|
ansible_ssh_private_key_file: ~/.ssh/id_jarvis
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
|
# LVM root expansion — xlarge template uses sda3 partition, standard VG/LV names
|
||||||
|
common_expand_root_lvm: true
|
||||||
|
common_root_pv: /dev/sda3
|
||||||
|
common_root_vg: ubuntu-vg
|
||||||
|
common_root_lv: ubuntu-lv
|
||||||
|
|
||||||
|
# --- Staged GGUF models for the llama.cpp router (:8002) ---------------------
|
||||||
|
# Data-driven list consumed by roles/llm-inference-multimodel tasks/models.yml
|
||||||
|
# (loop -> tasks/stage_model.yml). Each entry is idempotently staged into
|
||||||
|
# /opt/models: stat + EXACT-size check vs HF manifest; skip (no download, no
|
||||||
|
# restart) when present + size matches. Source repos are public bartowski GGUFs
|
||||||
|
# on HuggingFace (no auth). A router restart is notified ONLY when a new GGUF
|
||||||
|
# is actually downloaded.
|
||||||
|
# Added 2026-08-12 (War Machine): codify Phi-3.5-mini-instruct-Q8_0 and
|
||||||
|
# Meta-Llama-3.1-8B-Instruct-Q4_K_M as router models alongside the production
|
||||||
|
# Qwen3.6-35B-A3B-UD-Q4_K_S. The live files were already present/correct on
|
||||||
|
# astro-orbiter; this pass codifies them. Future adds = append to this list.
|
||||||
|
# Router --models-max override for astro-orbiter.
|
||||||
|
# Default in defaults/main.yml is 1 (conservative). Bumped to 4 on 2026-08-12
|
||||||
|
# (t_33acbb2e) so the router can keep more than one GGUF resident on-demand
|
||||||
|
# and LRU-evict when needed.
|
||||||
|
#
|
||||||
|
# VRAM NOTE (t_33acbb2e, updated t_55c164f5, updated t_34b96e83, updated t_f5f7e9ad, updated t_441470b9, updated t_c5cef2b2):
|
||||||
|
# With models-max=4 and all 6 GGUFs registered, worst case is all 6 loaded simultaneously:
|
||||||
|
# Qwen3.8-27B Q4_K_M: ~20.0GB (weights ~17.1GB + KV ~2.9GB @ 65536 ctx, q4_0) ← CORRECTED (ctx rolled back from 128K to 65536, t_c9fed26c 2026-08-18)
|
||||||
|
# Phi-3.5-mini-instruct Q8_0: ~4.3GB (weights ~3.8GB + KV ~0.5GB @ 32K ctx)
|
||||||
|
# Meta-Llama-3.1-8B Q4_K_M: ~5.6GB (weights ~4.6GB + KV ~0.2GB @ 8K ctx)
|
||||||
|
# Qwen2.5-Coder-14B Q4_K_M: ~9.0GB (weights ~8.4GB + KV ~0.6GB @ 16K ctx)
|
||||||
|
# nomic-embed-text-v1.5 Q4_K_M: ~0.09GB (~84MB, embedding only — no KV cache)
|
||||||
|
# Qwen3-8B Q4_K_M: ~5.5GB (weights ~4.68GB + KV ~0.5GB @ 32K ctx, q4_0)
|
||||||
|
# Total worst-case: ~44.5GB >> 24GB RTX 3090
|
||||||
|
#
|
||||||
|
# OOM RISK: Full co-residency is impossible on 24GB. LRU eviction prevents this
|
||||||
|
# in practice: models-max=4 means the router can REGISTER 6 models but only keeps
|
||||||
|
# up to 4 LOADED simultaneously — the router will evict the LRU model when a new
|
||||||
|
# one is needed. nomic-embed-text-v1.5 is pinned via sleep-idle-seconds=-1 and
|
||||||
|
# load-on-startup=true but it uses only ~84MB, so it never meaningfully changes
|
||||||
|
# the budget. In single-user homelab operation, only one generative model is active
|
||||||
|
# at a time alongside the always-resident embedding model.
|
||||||
|
# Qwen3.8-27B alone uses ~17,804 MiB (weights+KV @ 65536 ctx); co-residency
|
||||||
|
# with Coder (~9GB) = ~27GB > 24GB. LRU eviction handles this automatically.
|
||||||
|
# Ryan should be aware this means model-switching always incurs a ~30-60s
|
||||||
|
# cold-load latency when switching between Qwen3.8-27B and any other model.
|
||||||
|
# Proceeding to models-max=4 as instructed; flagged for Ryan's attention.
|
||||||
|
# Router --models-max override for astro-orbiter.
|
||||||
|
# UPDATED (t_f5f7e9ad, 2026-08-16): Set to 2 because Qwen3.8-27B-Q4_K_M
|
||||||
|
# uses 17,804 MiB at 65536 ctx. Only nomic-embed (558MB, pinned) and ONE
|
||||||
|
# generative model can be resident simultaneously. Co-residency of Qwen3.8
|
||||||
|
# with any auxiliary model (Phi 8.3GB, Llama 5.9GB, Coder 9GB) exceeds 24GB.
|
||||||
|
# models-max=2: slot 1 = nomic-embed (pinned, always loaded), slot 2 = LRU
|
||||||
|
# generative model (Qwen3.8 primary, cold-loaded on first request ~30-60s;
|
||||||
|
# auxiliary models evict it on demand, and vice versa).
|
||||||
|
# NOTE: Qwen3.8 does NOT have load-on-startup — it loads on first request.
|
||||||
|
# This avoids an LRU eviction race with nomic-embed at startup.
|
||||||
|
# UPDATED (t_72646029, 2026-08-17): CPU offload for Coder + Llama changes the
|
||||||
|
# constraint. Coder and Llama now use CPU inference (n-gpu-layers=0). GPU-resident
|
||||||
|
# VRAM: Qwen3.8 (~17,804 MiB at 65536 ctx) + nomic-embed (558 MiB, pinned) plus
|
||||||
|
# the CUDA-context buffers llama.cpp 6ea215d allocates for the CPU models (~1.4-1.7GB
|
||||||
|
# each) = ~20,004 MiB steady-state, below the 24,576 MiB physical limit.
|
||||||
|
# CORRECTED (t_c5cef2b2, 2026-08-19): ctx-size was rolled back from 131072 to 65536
|
||||||
|
# (t_c9fed26c 2026-08-18). Qwen3.8 VRAM at 65536: 17,804 MiB (not 20,302 MiB).
|
||||||
|
# models-max raised to 4: nomic (slot 1, pinned) + Qwen3.8 (slot 2, GPU) +
|
||||||
|
# Llama (slot 3, CPU) + Coder (slot 4, CPU). Phi (GPU, ~8.3GB) and new
|
||||||
|
# Qwen3-8B (GPU, ~5.5GB) can also be requested but evict Qwen3.8 due to VRAM.
|
||||||
|
# models-max=4 is required so CPU-offloaded models count as loaded without
|
||||||
|
# evicting Qwen3.8.
|
||||||
|
llm_router_models_max: 4
|
||||||
|
|
||||||
|
llm_staged_models:
|
||||||
|
- filename: "Phi-3.5-mini-instruct-Q8_0.gguf"
|
||||||
|
url: "https://huggingface.co/bartowski/Phi-3.5-mini-instruct-GGUF/resolve/main/Phi-3.5-mini-instruct-Q8_0.gguf"
|
||||||
|
size_bytes: 4061222688
|
||||||
|
source_repo: "bartowski/Phi-3.5-mini-instruct-GGUF"
|
||||||
|
- filename: "Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf"
|
||||||
|
url: "https://huggingface.co/bartowski/Meta-Llama-3.1-8B-Instruct-GGUF/resolve/main/Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf"
|
||||||
|
size_bytes: 4920739232
|
||||||
|
source_repo: "bartowski/Meta-Llama-3.1-8B-Instruct-GGUF"
|
||||||
|
- filename: "Qwen2.5-Coder-14B-Instruct-Q4_K_M.gguf"
|
||||||
|
url: "https://huggingface.co/bartowski/Qwen2.5-Coder-14B-Instruct-GGUF/resolve/main/Qwen2.5-Coder-14B-Instruct-Q4_K_M.gguf"
|
||||||
|
size_bytes: 8988111072
|
||||||
|
source_repo: "bartowski/Qwen2.5-Coder-14B-Instruct-GGUF"
|
||||||
|
- filename: "nomic-embed-text-v1.5-Q4_K_M.gguf"
|
||||||
|
url: "https://huggingface.co/nomic-ai/nomic-embed-text-v1.5-GGUF/resolve/main/nomic-embed-text-v1.5.Q4_K_M.gguf"
|
||||||
|
size_bytes: 84106624
|
||||||
|
source_repo: "nomic-ai/nomic-embed-text-v1.5-GGUF"
|
||||||
|
# Added t_c5cef2b2 (2026-08-19, War Machine): Qwen3-8B dense 8B model for
|
||||||
|
# aux tasks (routing, rewriting, structured extraction, tool-call construction).
|
||||||
|
# Source: bartowski/Qwen_Qwen3-8B-GGUF (public, no auth). HF filename is
|
||||||
|
# Qwen_Qwen3-8B-Q4_K_M.gguf; stored locally as Qwen3-8B-Q4_K_M.gguf.
|
||||||
|
# Exact size verified from HF manifest (content-length): 5,027,784,224 bytes.
|
||||||
|
# VRAM: ~4.68GB weights + ~0.5GB KV @ 32K ctx (q4_0) ≈ 5.2GB total.
|
||||||
|
# Thinking mode ON by default; use /no_think for latency-sensitive aux tasks.
|
||||||
|
- filename: "Qwen3-8B-Q4_K_M.gguf"
|
||||||
|
url: "https://huggingface.co/bartowski/Qwen_Qwen3-8B-GGUF/resolve/main/Qwen_Qwen3-8B-Q4_K_M.gguf"
|
||||||
|
size_bytes: 5027784224
|
||||||
|
source_repo: "bartowski/Qwen_Qwen3-8B-GGUF"
|
||||||
|
|
||||||
|
# --- deploy-vllm role: vllm_models override (t_r1d32b_swap, 2026-09-01) -----
|
||||||
|
# Ansible's hash_behaviour is "replace" (see ansible.cfg) — a host_vars list
|
||||||
|
# variable REPLACES the role default list wholesale, it does not deep-merge.
|
||||||
|
#
|
||||||
|
# SWAP (Ryan direction, 2026-09-01): Qwen2.5-32B-Instruct-AWQ retired,
|
||||||
|
# replaced with DeepSeek-R1-Distill-Qwen-32B-AWQ, max_model_len=32768.
|
||||||
|
# "Single model only" — nomic-embed-text-v1.5 (embedding, :8020) and
|
||||||
|
# Qwen3-8B-AWQ (aux, :8010, already disabled) are BOTH disabled here.
|
||||||
|
# DeepSeek gets the full 24GB card to itself. Nothing in production
|
||||||
|
# consumed nomic-embed at the time of this swap (Hindsight uses its own
|
||||||
|
# bundled 384-dim embedder; OpenViking pointed at the old llama-swap
|
||||||
|
# endpoint, already stopped) — confirmed with Ryan before disabling.
|
||||||
|
#
|
||||||
|
# Model choice: casperhansen/deepseek-r1-distill-qwen-32b-awq — same
|
||||||
|
# quantizer/toolchain (AutoAWQ) as the outgoing Qwen2.5-32B-Instruct-AWQ,
|
||||||
|
# widely used, 4-bit GEMM AWQ, ~19.3GB on disk (4 safetensors shards).
|
||||||
|
# Architecture: Qwen2ForCausalLM (DeepSeek-R1 distilled onto Qwen2.5-32B
|
||||||
|
# base) — same vLLM code path as the outgoing model, no new serving
|
||||||
|
# support needed. Native max_position_embeddings=131072; we cap at 32768
|
||||||
|
# per the task's explicit max-model-len requirement.
|
||||||
|
#
|
||||||
|
# VRAM math: ~19.3GB weights (4-bit AWQ) + KV cache at 32768 ctx (GQA,
|
||||||
|
# 8 KV heads, 128 head_dim, 64 layers, fp16 KV by default) ≈ 19.3GB +
|
||||||
|
# ~4GB KV+overhead ≈ 23.3GB — tight but the FULL 24GB card is now
|
||||||
|
# available (no co-resident nomic-embed/Qwen3-8B taking a share, unlike
|
||||||
|
# the outgoing Qwen2.5-32B config). gpu_memory_utilization=0.95 (role
|
||||||
|
# default) + enforce_eager retained as the proven-stable mitigation from
|
||||||
|
# t_e6facb19/t_ca1af9fb (avoids CUDA graph capture VRAM spike; this host's
|
||||||
|
# only validated way to avoid crash-loop-to-stabilize behavior on this
|
||||||
|
# card). If 0.95 OOMs at 32768 ctx once tested live, drop to 0.90 next
|
||||||
|
# (documented fallback, same pattern as the outgoing model).
|
||||||
|
#
|
||||||
|
# DeepSeek-R1 output note: reasoning traces stream in <think> tags before
|
||||||
|
# the final answer — this is expected R1-distill behavior, not a bug.
|
||||||
|
# Model card recommends temperature 0.5-0.7 (not 0, not vLLM's greedy
|
||||||
|
# default) to avoid repetition/incoherence; not set here (server-side
|
||||||
|
# default), left to be set client-side per the model card's guidance —
|
||||||
|
# flagging for whoever wires this into Hermes profile configs next.
|
||||||
|
vllm_models:
|
||||||
|
- id: "Gemma-4-26B-A4B-it-AWQ"
|
||||||
|
hf_repo: "cyankiwi/gemma-4-26B-A4B-it-AWQ-4bit"
|
||||||
|
role: primary
|
||||||
|
# NO quantization field set (unlike the AutoAWQ-quantized DeepSeek/
|
||||||
|
# Qwen2.5 models above) — live test (2026-09-01) found this repo's
|
||||||
|
# config.json declares quant_method: "compressed-tensors" (llm-compressor
|
||||||
|
# tool output, not classic AutoAWQ), even though the repo name says
|
||||||
|
# "AWQ-4bit". Passing --quantization awq explicitly caused a hard
|
||||||
|
# pydantic ValidationError at every single startup attempt: "Quantization
|
||||||
|
# method specified in the model config (compressed-tensors) does not
|
||||||
|
# match the quantization method specified in the `quantization` argument
|
||||||
|
# (awq)." vLLM auto-detects the quant method correctly from the model's
|
||||||
|
# own config.json when --quantization is omitted — confirmed fix, clean
|
||||||
|
# start. Lesson: don't trust a HF repo's naming convention ("...-AWQ...")
|
||||||
|
# for the `quantization:` field here — check config.json's quant_method.
|
||||||
|
port: 8000
|
||||||
|
# Ryan direction (2026-09-01, t_gemma4_swap): DeepSeek-R1-Distill-Qwen-32B
|
||||||
|
# retired after confirming its `auto` tool-choice reliability is a known,
|
||||||
|
# documented DeepSeek-R1-distillation limitation (trained on pure
|
||||||
|
# reasoning traces, no function-calling data — GitHub-confirmed upstream,
|
||||||
|
# not a vLLM config gap). Replaced with Gemma 4 26B A4B (Google,
|
||||||
|
# Apache 2.0, US-origin — matches Ryan's standing model-origin
|
||||||
|
# preference, unlike Qwen/DeepSeek). Chose MoE (26B A4B, 3.8B active)
|
||||||
|
# over the dense 31B variant: ~3.7GB smaller on-disk AWQ footprint
|
||||||
|
# (17.2GB vs 20.9GB) buys more KV-cache headroom on this tight 24GB
|
||||||
|
# card, and decode should be faster (memory-bandwidth-bound on active
|
||||||
|
# params, not total params). Tradeoff accepted: MoE scores lower than
|
||||||
|
# dense on the Tau2 tool-use benchmark (68.2% vs 76.9%) but still beats
|
||||||
|
# every other size in the family except the 31B on most reasoning
|
||||||
|
# benchmarks. Model choice: cyankiwi/gemma-4-26B-A4B-it-AWQ-4bit —
|
||||||
|
# AutoAWQ 4-bit group_size=32, MoE expert layers (gate/up/down/router)
|
||||||
|
# explicitly excluded from quantization ("ignore" list in config.json)
|
||||||
|
# per standard llm-compressor MoE quant practice — only the dense
|
||||||
|
# attention/projection layers are 4-bit, experts stay higher precision.
|
||||||
|
# Native architecture: Gemma4ForConditionalGeneration (registered
|
||||||
|
# natively in this host's installed vLLM 0.28.0 — vllm/model_executor/
|
||||||
|
# models/registry.py line 415 — no plugin/trust-remote-code needed).
|
||||||
|
# Native max_position_embeddings: 262144 (256K) — Hermes's 64K floor is
|
||||||
|
# comfortably covered without any context-extension trick.
|
||||||
|
max_model_len: 65536
|
||||||
|
# VRAM math (not yet live-validated — see swap validation log below
|
||||||
|
# once run): AWQ weights ~17.2GB on disk (dense attn 4-bit + MoE
|
||||||
|
# experts higher-precision, per config.json's compressed-tensors
|
||||||
|
# ignore list). Starting the KV cache dtype at int4_per_token_head
|
||||||
|
# from the outset (rather than fp16 -> fp8 -> int4 trial-and-error like
|
||||||
|
# the DeepSeek swap) since that same escalation pattern is expected to
|
||||||
|
# repeat on this VRAM-constrained card for any 20+ GB model at >32K ctx.
|
||||||
|
kv_cache_dtype: int4_per_token_head
|
||||||
|
gpu_memory_utilization: 0.95
|
||||||
|
enforce_eager: true
|
||||||
|
# Native tool-calling + reasoning support (no `hermes` workaround
|
||||||
|
# needed, unlike DeepSeek-R1-Distill): Gemma4EngineToolParser and
|
||||||
|
# Gemma4ParserReasoningAdapter are both registered natively in this
|
||||||
|
# host's vLLM 0.28.0 (vllm/tool_parsers/__init__.py,
|
||||||
|
# vllm/reasoning/__init__.py) — purpose-built for this model's actual
|
||||||
|
# output format, not a same-family approximation.
|
||||||
|
enable_auto_tool_choice: true
|
||||||
|
tool_call_parser: gemma4
|
||||||
|
reasoning_parser: gemma4
|
||||||
|
enabled: true
|
||||||
|
- id: "Qwen3-8B-AWQ"
|
||||||
|
hf_repo: "Qwen/Qwen3-8B-AWQ"
|
||||||
|
role: aux
|
||||||
|
quantization: awq
|
||||||
|
port: 8010
|
||||||
|
max_model_len: 32768
|
||||||
|
gpu_memory_utilization: 0.15
|
||||||
|
enforce_eager: true
|
||||||
|
enabled: false # single-model deployment — see swap note above
|
||||||
|
- id: "nomic-embed-text-v1.5"
|
||||||
|
hf_repo: "nomic-ai/nomic-embed-text-v1.5"
|
||||||
|
role: embedding
|
||||||
|
quantization: none
|
||||||
|
port: 8020
|
||||||
|
max_model_len: 2048
|
||||||
|
gpu_memory_utilization: 0.05
|
||||||
|
trust_remote_code: true
|
||||||
|
enabled: false # single-model deployment — see swap note above
|
||||||
|
|
||||||
|
# --- deploy-vllm role: boot persistence (unchanged) -------------------------
|
||||||
|
# Still permanent/boot-persistent — same policy as the outgoing Qwen2.5-32B
|
||||||
|
# deployment (t_5508360a), just now serving one model instead of two.
|
||||||
|
vllm_service_enabled: true
|
||||||
|
vllm_service_state: started
|
||||||
|
|
||||||
58
ansible/host_vars/big-thunder-mountain/old_vars
Normal file
58
ansible/host_vars/big-thunder-mountain/old_vars
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts created via cloning.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Ubuntu
|
||||||
|
# - 24.04
|
||||||
|
# - Fedora
|
||||||
|
# - 42
|
||||||
|
|
||||||
|
vm_os_distribution: "ubuntu"
|
||||||
|
vm_os_version: "24.04"
|
||||||
|
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
|
||||||
|
|
||||||
|
vm_size: "large-plus"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "general"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "pve03"
|
||||||
|
proxmox_host_target: "pve03"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
|
||||||
|
ip_address: 10.1.71.52
|
||||||
|
vm_mac_address: 'BC:24:11:11:BC:58'
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
99
ansible/host_vars/big-thunder-mountain/vars
Normal file
99
ansible/host_vars/big-thunder-mountain/vars
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts created via cloning.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Ubuntu (clone)
|
||||||
|
# - 24.04
|
||||||
|
# - Fedora (clone)
|
||||||
|
# - 42
|
||||||
|
# - Talos (create)
|
||||||
|
# - 1.11.5
|
||||||
|
|
||||||
|
vm_os_distribution: "talos"
|
||||||
|
vm_os_version: "1.11.5"
|
||||||
|
|
||||||
|
# VM ISO
|
||||||
|
# Talos: talos-v1.11.5-nocloud-amd64.iso
|
||||||
|
|
||||||
|
vm_iso_storage: "templates"
|
||||||
|
vm_iso: "talos-v{{ vm_os_version }}-nocloud-amd64.iso"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
# Comment out for VM create
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "liberty-tree"
|
||||||
|
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
|
||||||
|
|
||||||
|
vm_size: "large-plus"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "pve03"
|
||||||
|
proxmox_host_target: "pve03"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
|
||||||
|
ip_address: 10.1.71.52
|
||||||
|
vm_mac_address: 'BC:24:11:11:BC:58'
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
|
|
||||||
|
# Dictionaries for VM resources
|
||||||
|
# - cores (quantity)
|
||||||
|
# - memory (MB)
|
||||||
|
# - virtual disk (GiB)
|
||||||
|
|
||||||
|
vm_definitions:
|
||||||
|
small:
|
||||||
|
cores: 2
|
||||||
|
memory: 2048
|
||||||
|
disk: 8
|
||||||
|
medium:
|
||||||
|
cores: 2
|
||||||
|
memory: 4096
|
||||||
|
disk: 16
|
||||||
|
large:
|
||||||
|
cores: 4
|
||||||
|
memory: 4096
|
||||||
|
disk: 32
|
||||||
|
large-plus:
|
||||||
|
cores: 4
|
||||||
|
memory: 4096
|
||||||
|
disk: 48
|
||||||
|
xlarge:
|
||||||
|
cores: 4
|
||||||
|
memory: 8192
|
||||||
|
disk: 64
|
||||||
|
xlarge-plus:
|
||||||
|
cores: 4
|
||||||
|
memory: 8192
|
||||||
|
disk: 128
|
||||||
16
ansible/host_vars/carousel-of-progress/vars.yml
Normal file
16
ansible/host_vars/carousel-of-progress/vars.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: ansible/host_vars/astro_orbiter/vars.yml
|
||||||
|
# HOST: astro-orbiter (10.1.71.130)
|
||||||
|
# ROLE: Ollama inference host with AMD RX 5700 GPU passthrough
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ansible_host: 10.1.71.131
|
||||||
|
ansible_user: wed
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
|
# LVM root expansion — xlarge template uses sda3 partition, standard VG/LV names
|
||||||
|
common_expand_root_lvm: true
|
||||||
|
common_root_pv: /dev/sda3
|
||||||
|
common_root_vg: ubuntu-vg
|
||||||
|
common_root_lv: ubuntu-lv
|
||||||
48
ansible/host_vars/city-hall/vars
Normal file
48
ansible/host_vars/city-hall/vars
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts created via cloning.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Fedora (42)
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
|
||||||
|
vm_clone_source: "fedora-42-small"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "mk-general"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "pve03"
|
||||||
|
proxmox_host_target: "pve03"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
|
||||||
|
ip_address: 10.1.71.21
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
# file: host_vars/matchbox/vars
|
# file: host_vars/docker01/vars
|
||||||
|
|
||||||
# Networking
|
# Networking
|
||||||
# primary_interface: "enp1s0f0"
|
# primary_interface: "enp1s0f0"
|
||||||
mac_address: "BC:24:11:03:A9:7B"
|
#mac_address: "BC:24:11:03:A9:7B"
|
||||||
ip_address: 10.1.71.211
|
ip_address: 10.1.71.211
|
||||||
hostname: "{{ inventory_hostname }}.{{ base_domain }}"
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}"
|
||||||
|
|
||||||
12
ansible/host_vars/guest-relations/vars
Normal file
12
ansible/host_vars/guest-relations/vars
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/guest-relations/vars
|
||||||
|
# guest-relations — Guest Relations
|
||||||
|
# VM provisioned by Terraform (pre-pipeline bootstrap)
|
||||||
|
|
||||||
|
# ─── Network ─────────────────────────────────────────────────────────────────
|
||||||
|
ip_address: 10.1.71.40
|
||||||
|
|
||||||
|
# ─── Application ─────────────────────────────────────────────────────────────
|
||||||
|
app_role: identity_provider
|
||||||
|
app_name: authentik
|
||||||
|
app_deployment: docker_compose
|
||||||
60
ansible/host_vars/haunted-mansion/old_vars
Normal file
60
ansible/host_vars/haunted-mansion/old_vars
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts created via cloning.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Ubuntu
|
||||||
|
# - 24.04
|
||||||
|
# - Fedora
|
||||||
|
# - 42
|
||||||
|
|
||||||
|
vm_os_distribution: "ubuntu"
|
||||||
|
vm_os_version: "24.04"
|
||||||
|
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
|
||||||
|
|
||||||
|
vm_size: "xlarge"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "general"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "pve03"
|
||||||
|
proxmox_host_target: "pve03"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
|
||||||
|
ip_address: 10.1.71.61
|
||||||
|
vm_mac_address: 'BC:24:11:C9:3A:13'
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
|
||||||
|
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
99
ansible/host_vars/haunted-mansion/vars
Normal file
99
ansible/host_vars/haunted-mansion/vars
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts created via cloning.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Ubuntu (clone)
|
||||||
|
# - 24.04
|
||||||
|
# - Fedora (clone)
|
||||||
|
# - 42
|
||||||
|
# - Talos (create)
|
||||||
|
# - 1.11.5
|
||||||
|
|
||||||
|
vm_os_distribution: "talos"
|
||||||
|
vm_os_version: "1.11.5"
|
||||||
|
|
||||||
|
# VM ISO
|
||||||
|
# Talos: talos-v1.11.5-nocloud-amd64.iso
|
||||||
|
|
||||||
|
vm_iso_storage: "templates"
|
||||||
|
vm_iso: "talos-v{{ vm_os_version }}-nocloud-amd64.iso"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
# Comment out for VM create
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "liberty-tree"
|
||||||
|
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
|
||||||
|
|
||||||
|
vm_size: "xlarge"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "pve03"
|
||||||
|
proxmox_host_target: "pve03"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
|
||||||
|
ip_address: 10.1.71.61
|
||||||
|
vm_mac_address: 'BC:24:11:C9:3A:13'
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
|
|
||||||
|
# Dictionaries for VM resources
|
||||||
|
# - cores (quantity)
|
||||||
|
# - memory (MB)
|
||||||
|
# - virtual disk (GiB)
|
||||||
|
|
||||||
|
vm_definitions:
|
||||||
|
small:
|
||||||
|
cores: 2
|
||||||
|
memory: 2048
|
||||||
|
disk: 8
|
||||||
|
medium:
|
||||||
|
cores: 2
|
||||||
|
memory: 4096
|
||||||
|
disk: 16
|
||||||
|
large:
|
||||||
|
cores: 4
|
||||||
|
memory: 4096
|
||||||
|
disk: 32
|
||||||
|
large-plus:
|
||||||
|
cores: 4
|
||||||
|
memory: 4096
|
||||||
|
disk: 48
|
||||||
|
xlarge:
|
||||||
|
cores: 4
|
||||||
|
memory: 8192
|
||||||
|
disk: 64
|
||||||
|
xlarge-plus:
|
||||||
|
cores: 4
|
||||||
|
memory: 8192
|
||||||
|
disk: 128
|
||||||
12
ansible/host_vars/lightning_lane/vars
Normal file
12
ansible/host_vars/lightning_lane/vars
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/lightning_lane/vars
|
||||||
|
# lightning-lane — Traefik Reverse Proxy / Load Balancer
|
||||||
|
# VM provisioned by Terraform (pre-pipeline bootstrap)
|
||||||
|
|
||||||
|
# ─── Network ─────────────────────────────────────────────────────────────────
|
||||||
|
ip_address: 10.1.71.35
|
||||||
|
|
||||||
|
# ─── Application ─────────────────────────────────────────────────────────────
|
||||||
|
app_role: reverse_proxy
|
||||||
|
app_name: traefik
|
||||||
|
app_deployment: docker_compose
|
||||||
16
ansible/host_vars/main-street-station/main.yml
Normal file
16
ansible/host_vars/main-street-station/main.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
# Host-specific vars for main-street-station (JMRI headless server)
|
||||||
|
# LCRR - Lake Country Railroad, Milwaukee Road Oct 1956, HO scale
|
||||||
|
|
||||||
|
# JMRI profile ID — find with: ls ~/.jmri/profiles/ on the old box
|
||||||
|
# Format: <name>.<8-char-hex> e.g. LCRR.3d3f1dfc
|
||||||
|
# TODO: fill in after restoring config from GitHub backup
|
||||||
|
jmri_profile_id: ""
|
||||||
|
|
||||||
|
# USB serial device for NCE command station
|
||||||
|
# Verify after install: ls -la /dev/ttyUSB* /dev/ttyACM*
|
||||||
|
jmri_serial_device: /dev/ttyUSB0
|
||||||
|
|
||||||
|
# Path to JMRI config backup for restore task (leave empty to skip)
|
||||||
|
# Point at a local checkout of the LCRR GitHub repo
|
||||||
|
jmri_config_src: ""
|
||||||
10
ansible/host_vars/main-street-station/vars.yml
Normal file
10
ansible/host_vars/main-street-station/vars.yml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
# main-street-station — JMRI / LCRR server
|
||||||
|
jmri_profile_id: "Lake_Country_Railroad.3e8b1d4b"
|
||||||
|
jmri_lcrr_repo: "ssh://git@gitea.mk-labs.cloud:2221/rblundon/LCRR.git"
|
||||||
|
jmri_lcrr_branch: "clean-profile"
|
||||||
|
jmri_leviton_email: "{{ leviton_email }}"
|
||||||
|
jmri_leviton_password: "{{ leviton_password }}"
|
||||||
|
jmri_ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINnSM/9fO8rz/amqkyoGUzUKNNzzmtSXPwOCr1O9zKNO ansible"
|
||||||
|
jmri_ssh_authorized_keys_extra:
|
||||||
|
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG6HaK4Y21UwPRbAZ986L7I9QnUdyq53114+9kO8X4bL rblundon@laptop"
|
||||||
59
ansible/host_vars/peter-pans-flight/old_vars
Normal file
59
ansible/host_vars/peter-pans-flight/old_vars
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts created via cloning.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Ubuntu
|
||||||
|
# - 24.04
|
||||||
|
# - Fedora
|
||||||
|
# - 42
|
||||||
|
|
||||||
|
vm_os_distribution: "ubuntu"
|
||||||
|
vm_os_version: "24.04"
|
||||||
|
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
|
||||||
|
|
||||||
|
vm_size: "xlarge"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "general"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "pve03"
|
||||||
|
proxmox_host_target: "pve03"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
|
||||||
|
ip_address: 10.1.71.62
|
||||||
|
vm_mac_address: 'BC:24:11:AA:8F:3A'
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
99
ansible/host_vars/peter-pans-flight/vars
Normal file
99
ansible/host_vars/peter-pans-flight/vars
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts created via cloning.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Ubuntu (clone)
|
||||||
|
# - 24.04
|
||||||
|
# - Fedora (clone)
|
||||||
|
# - 42
|
||||||
|
# - Talos (create)
|
||||||
|
# - 1.11.5
|
||||||
|
|
||||||
|
vm_os_distribution: "talos"
|
||||||
|
vm_os_version: "1.11.5"
|
||||||
|
|
||||||
|
# VM ISO
|
||||||
|
# Talos: talos-v1.11.5-nocloud-amd64.iso
|
||||||
|
|
||||||
|
vm_iso_storage: "templates"
|
||||||
|
vm_iso: "talos-v{{ vm_os_version }}-nocloud-amd64.iso"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
# Comment out for VM create
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "liberty-tree"
|
||||||
|
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
|
||||||
|
|
||||||
|
vm_size: "xlarge"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "pve03"
|
||||||
|
proxmox_host_target: "pve03"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
|
||||||
|
ip_address: 10.1.71.62
|
||||||
|
vm_mac_address: 'BC:24:11:AA:8F:3A'
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
|
|
||||||
|
# Dictionaries for VM resources
|
||||||
|
# - cores (quantity)
|
||||||
|
# - memory (MB)
|
||||||
|
# - virtual disk (GiB)
|
||||||
|
|
||||||
|
vm_definitions:
|
||||||
|
small:
|
||||||
|
cores: 2
|
||||||
|
memory: 2048
|
||||||
|
disk: 8
|
||||||
|
medium:
|
||||||
|
cores: 2
|
||||||
|
memory: 4096
|
||||||
|
disk: 16
|
||||||
|
large:
|
||||||
|
cores: 4
|
||||||
|
memory: 4096
|
||||||
|
disk: 32
|
||||||
|
large-plus:
|
||||||
|
cores: 4
|
||||||
|
memory: 4096
|
||||||
|
disk: 48
|
||||||
|
xlarge:
|
||||||
|
cores: 4
|
||||||
|
memory: 8192
|
||||||
|
disk: 64
|
||||||
|
xlarge-plus:
|
||||||
|
cores: 4
|
||||||
|
memory: 8192
|
||||||
|
disk: 128
|
||||||
59
ansible/host_vars/space-mountain/old_vars
Normal file
59
ansible/host_vars/space-mountain/old_vars
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts created via cloning.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Ubuntu
|
||||||
|
# - 24.04
|
||||||
|
# - Fedora
|
||||||
|
# - 42
|
||||||
|
|
||||||
|
vm_os_distribution: "ubuntu"
|
||||||
|
vm_os_version: "24.04"
|
||||||
|
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
|
||||||
|
|
||||||
|
vm_size: "large-plus"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "general"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "pve03"
|
||||||
|
proxmox_host_target: "pve03"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
|
||||||
|
ip_address: 10.1.71.51
|
||||||
|
vm_mac_address: 'BC:24:11:1A:E8:8C'
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
99
ansible/host_vars/space-mountain/vars
Normal file
99
ansible/host_vars/space-mountain/vars
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts created via cloning.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Ubuntu (clone)
|
||||||
|
# - 24.04
|
||||||
|
# - Fedora (clone)
|
||||||
|
# - 42
|
||||||
|
# - Talos (create)
|
||||||
|
# - 1.11.5
|
||||||
|
|
||||||
|
vm_os_distribution: "talos"
|
||||||
|
vm_os_version: "1.11.5"
|
||||||
|
|
||||||
|
# VM ISO
|
||||||
|
# Talos: talos-v1.11.5-nocloud-amd64.iso
|
||||||
|
|
||||||
|
vm_iso_storage: "templates"
|
||||||
|
vm_iso: "talos-v{{ vm_os_version }}-nocloud-amd64.iso"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
# Comment out for VM create
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "liberty-tree"
|
||||||
|
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
|
||||||
|
|
||||||
|
vm_size: "large-plus"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "pve03"
|
||||||
|
proxmox_host_target: "pve03"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
|
||||||
|
ip_address: 10.1.71.51
|
||||||
|
vm_mac_address: 'BC:24:11:1A:E8:8C'
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
|
|
||||||
|
# Dictionaries for VM resources
|
||||||
|
# - cores (quantity)
|
||||||
|
# - memory (MB)
|
||||||
|
# - virtual disk (GiB)
|
||||||
|
|
||||||
|
vm_definitions:
|
||||||
|
small:
|
||||||
|
cores: 2
|
||||||
|
memory: 2048
|
||||||
|
disk: 8
|
||||||
|
medium:
|
||||||
|
cores: 2
|
||||||
|
memory: 4096
|
||||||
|
disk: 16
|
||||||
|
large:
|
||||||
|
cores: 4
|
||||||
|
memory: 4096
|
||||||
|
disk: 32
|
||||||
|
large-plus:
|
||||||
|
cores: 4
|
||||||
|
memory: 4096
|
||||||
|
disk: 48
|
||||||
|
xlarge:
|
||||||
|
cores: 4
|
||||||
|
memory: 8192
|
||||||
|
disk: 64
|
||||||
|
xlarge-plus:
|
||||||
|
cores: 4
|
||||||
|
memory: 8192
|
||||||
|
disk: 128
|
||||||
59
ansible/host_vars/splash-mountain/old_vars
Normal file
59
ansible/host_vars/splash-mountain/old_vars
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts created via cloning.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Ubuntu
|
||||||
|
# - 24.04
|
||||||
|
# - Fedora
|
||||||
|
# - 42
|
||||||
|
|
||||||
|
vm_os_distribution: "ubuntu"
|
||||||
|
vm_os_version: "24.04"
|
||||||
|
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
|
||||||
|
|
||||||
|
vm_size: "large-plus"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "general"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "pve03"
|
||||||
|
proxmox_host_target: "pve03"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
|
||||||
|
ip_address: 10.1.71.53
|
||||||
|
vm_mac_address: 'BC:24:11:84:D7:2F'
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
99
ansible/host_vars/splash-mountain/vars
Normal file
99
ansible/host_vars/splash-mountain/vars
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts created via cloning.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Ubuntu (clone)
|
||||||
|
# - 24.04
|
||||||
|
# - Fedora (clone)
|
||||||
|
# - 42
|
||||||
|
# - Talos (create)
|
||||||
|
# - 1.11.5
|
||||||
|
|
||||||
|
vm_os_distribution: "talos"
|
||||||
|
vm_os_version: "1.11.5"
|
||||||
|
|
||||||
|
# VM ISO
|
||||||
|
# Talos: talos-v1.11.5-nocloud-amd64.iso
|
||||||
|
|
||||||
|
vm_iso_storage: "templates"
|
||||||
|
vm_iso: "talos-v{{ vm_os_version }}-nocloud-amd64.iso"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
# Comment out for VM create
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "liberty-tree"
|
||||||
|
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
|
||||||
|
|
||||||
|
vm_size: "large-plus"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "pve03"
|
||||||
|
proxmox_host_target: "pve03"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
|
||||||
|
ip_address: 10.1.71.53
|
||||||
|
vm_mac_address: 'BC:24:11:84:D7:2F'
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
|
|
||||||
|
# Dictionaries for VM resources
|
||||||
|
# - cores (quantity)
|
||||||
|
# - memory (MB)
|
||||||
|
# - virtual disk (GiB)
|
||||||
|
|
||||||
|
vm_definitions:
|
||||||
|
small:
|
||||||
|
cores: 2
|
||||||
|
memory: 2048
|
||||||
|
disk: 8
|
||||||
|
medium:
|
||||||
|
cores: 2
|
||||||
|
memory: 4096
|
||||||
|
disk: 16
|
||||||
|
large:
|
||||||
|
cores: 4
|
||||||
|
memory: 4096
|
||||||
|
disk: 32
|
||||||
|
large-plus:
|
||||||
|
cores: 4
|
||||||
|
memory: 4096
|
||||||
|
disk: 48
|
||||||
|
xlarge:
|
||||||
|
cores: 4
|
||||||
|
memory: 8192
|
||||||
|
disk: 64
|
||||||
|
xlarge-plus:
|
||||||
|
cores: 4
|
||||||
|
memory: 8192
|
||||||
|
disk: 128
|
||||||
100
ansible/host_vars/splash/vars
Normal file
100
ansible/host_vars/splash/vars
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts creation via clone or create.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Ubuntu (clone)
|
||||||
|
# - 24.04
|
||||||
|
# - Fedora (clone)
|
||||||
|
# - 42
|
||||||
|
# - Talos (create)
|
||||||
|
# - 1.11.5
|
||||||
|
|
||||||
|
vm_os_distribution: "talos"
|
||||||
|
vm_os_version: "1.11.5"
|
||||||
|
|
||||||
|
# VM ISO
|
||||||
|
# Talos: talos-v1.11.5-nocloud-amd64.iso
|
||||||
|
|
||||||
|
vm_iso_storage: "templates"
|
||||||
|
vm_iso: "talos-v{{ vm_os_version }}-nocloud-amd64.iso"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
# Comment out for VM create
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "liberty-tree"
|
||||||
|
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
|
||||||
|
|
||||||
|
vm_size: "small"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "pve03"
|
||||||
|
proxmox_host_target: "tomorrowland"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
# Comment out vm_mac_address if new MAC address should be generated
|
||||||
|
|
||||||
|
ip_address: 10.1.71.99
|
||||||
|
vm_mac_address: 'BC:24:11:97:C3:AA'
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
|
|
||||||
|
# Dictionaries for VM resources
|
||||||
|
# - cores (quantity)
|
||||||
|
# - memory (MB)
|
||||||
|
# - virtual disk (GiB)
|
||||||
|
|
||||||
|
vm_definitions:
|
||||||
|
small:
|
||||||
|
cores: 2
|
||||||
|
memory: 2048
|
||||||
|
disk: 8
|
||||||
|
medium:
|
||||||
|
cores: 2
|
||||||
|
memory: 4096
|
||||||
|
disk: 16
|
||||||
|
large:
|
||||||
|
cores: 4
|
||||||
|
memory: 4096
|
||||||
|
disk: 32
|
||||||
|
large-plus:
|
||||||
|
cores: 4
|
||||||
|
memory: 4096
|
||||||
|
disk: 48
|
||||||
|
xlarge:
|
||||||
|
cores: 4
|
||||||
|
memory: 8192
|
||||||
|
disk: 64
|
||||||
|
xlarge-plus:
|
||||||
|
cores: 4
|
||||||
|
memory: 8192
|
||||||
|
disk: 128
|
||||||
50
ansible/host_vars/sundial/vars
Normal file
50
ansible/host_vars/sundial/vars
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts created via cloning.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Fedora (42)
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
|
||||||
|
|
||||||
|
vm_clone_source: "fedora-42-small"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "general"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "fantasyland"
|
||||||
|
proxmox_host_target: "fantasyland"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
|
||||||
|
ip_address: 10.1.71.21
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
59
ansible/host_vars/tiki-room/vars
Normal file
59
ansible/host_vars/tiki-room/vars
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts created via cloning.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Ubuntu
|
||||||
|
# - 24.04
|
||||||
|
# - Fedora
|
||||||
|
# - 42
|
||||||
|
|
||||||
|
vm_os_distribution: "ubuntu"
|
||||||
|
vm_os_version: "24.04"
|
||||||
|
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
|
||||||
|
|
||||||
|
vm_size: "large"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "general"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "pve03"
|
||||||
|
proxmox_host_target: "pve03"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
|
||||||
|
ip_address: 10.1.71.23
|
||||||
|
# vm_mac_address: 'BC:24:11:11:BC:58'
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
27
ansible/host_vars/turnstile/vars
Normal file
27
ansible/host_vars/turnstile/vars
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/turnstile/vars
|
||||||
|
# turnstile — Smallstep step-ca SSH Certificate Authority
|
||||||
|
# VM provisioned by Terraform (pre-pipeline bootstrap)
|
||||||
|
|
||||||
|
# ─── Network ─────────────────────────────────────────────────────────────────
|
||||||
|
ip_address: 10.1.71.34
|
||||||
|
|
||||||
|
# ─── Application ─────────────────────────────────────────────────────────────
|
||||||
|
app_role: ssh_certificate_authority
|
||||||
|
app_name: step-ca
|
||||||
|
app_deployment: docker_compose
|
||||||
|
|
||||||
|
# ─── step-ca Configuration ───────────────────────────────────────────────────
|
||||||
|
stepca_hostname: turnstile.local.mk-labs.cloud
|
||||||
|
stepca_dns_names: "turnstile.local.mk-labs.cloud,10.1.71.34"
|
||||||
|
stepca_ssh_enabled: true
|
||||||
|
stepca_listen_port: 9000
|
||||||
|
|
||||||
|
# ─── OIDC Provisioner (Authentik) ────────────────────────────────────────────
|
||||||
|
# Client ID and secret stored in vault
|
||||||
|
stepca_oidc_provisioner_name: authentik
|
||||||
|
stepca_oidc_configuration_endpoint: "https://authentik.local.mk-labs.cloud/application/o/step-ca/.well-known/openid-configuration"
|
||||||
|
stepca_oidc_listen_address: ":10000"
|
||||||
|
stepca_oidc_domains:
|
||||||
|
- "local.mk-labs.cloud"
|
||||||
|
- "protonmail.com"
|
||||||
17
ansible/host_vars/turnstile/vault
Normal file
17
ansible/host_vars/turnstile/vault
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
|
30383930323363386234333433636465393263613336646464666365643730386430353864616334
|
||||||
|
3865343031653162313736656437373666636136653263360a613961646133376262633164393535
|
||||||
|
31363434633634303035663133316230633538363936303266373931313661346563333832653032
|
||||||
|
6239376363646530620a303231363333363233623038326165316463383662656565626534396232
|
||||||
|
62333133623530643932643430663166373930353733363866336533643233373261333130613635
|
||||||
|
31633337376337663833613634666436383862386431636537373363343333323932363761653366
|
||||||
|
61353166613162346436396138316561646165303566376366323462663861616364336539313138
|
||||||
|
30386430373566353761383636626335393463376661356666303861353564313832346333396134
|
||||||
|
30653861363866336331336164323130623230666237356165613934333239386536373664643065
|
||||||
|
33643139346562346663313533643433353462363665323166313364373335366665373435643935
|
||||||
|
65303863663864393238393732303065343364306264396333376233666233346664323334336532
|
||||||
|
38373164383835663163363137643531303163396236623565666436363261393563343133333161
|
||||||
|
36663934303633663062346161636333396135336135616136303664636562363862353764343562
|
||||||
|
39643430396636313139336130656363306562363430346233313530663963366238326334623135
|
||||||
|
62383632613061316262323038303333323739303137363334626637666663666263613132336433
|
||||||
|
65346266393733633632
|
||||||
104
ansible/host_vars/vm_template/vars
Normal file
104
ansible/host_vars/vm_template/vars
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
---
|
||||||
|
# file: host_vars/vm_template/vars
|
||||||
|
# Ansible vars template for hosts creation via clone or create.
|
||||||
|
|
||||||
|
# Supported hypervisors:
|
||||||
|
# - Proxmox
|
||||||
|
|
||||||
|
platform: "proxmox"
|
||||||
|
|
||||||
|
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
|
||||||
|
# Current OS offerings are:
|
||||||
|
# - Ubuntu (clone)
|
||||||
|
# - 24.04
|
||||||
|
# - Fedora (clone)
|
||||||
|
# - 42
|
||||||
|
# - Talos (create)
|
||||||
|
# - 1.11.5
|
||||||
|
|
||||||
|
vm_os_distribution: "ubuntu"
|
||||||
|
vm_os_version: "24.04"
|
||||||
|
|
||||||
|
# VM ISO
|
||||||
|
# Talos: talos-v1.11.5-nocloud-amd64.iso
|
||||||
|
|
||||||
|
vm_iso_storage: "templates"
|
||||||
|
vm_iso: "talos-v{{ vm_os_version }}-nocloud-amd64.iso"
|
||||||
|
|
||||||
|
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
|
||||||
|
# Comment out for VM create
|
||||||
|
|
||||||
|
proxmox_clone_node: "fantasyland"
|
||||||
|
|
||||||
|
# Proxmox storage target.
|
||||||
|
|
||||||
|
vm_storage: "general"
|
||||||
|
|
||||||
|
# Current VM sizes are:
|
||||||
|
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
|
||||||
|
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
|
||||||
|
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
|
||||||
|
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
|
||||||
|
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
|
||||||
|
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
|
||||||
|
|
||||||
|
vm_size: "large-plus"
|
||||||
|
|
||||||
|
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
|
||||||
|
# and backup groups. (ha_group will be factored out in the next functionality update.)
|
||||||
|
|
||||||
|
ha_group: "pve03"
|
||||||
|
proxmox_host_target: "tomorrowland"
|
||||||
|
|
||||||
|
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
|
||||||
|
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
|
||||||
|
# Comment out vm_mac_address if new MAC address should be generated
|
||||||
|
|
||||||
|
ip_address: 10.1.71.249
|
||||||
|
# vm_mac_address: 'BC:24:11:11:BC:58'
|
||||||
|
|
||||||
|
# Software
|
||||||
|
# Future enhancement will allow specification of additional software to automatically deploy to
|
||||||
|
# the VM after creation.
|
||||||
|
|
||||||
|
#terraform_version: "1.11.3"
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
|
||||||
|
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
|
||||||
|
|
||||||
|
# Dictionaries for VM resources
|
||||||
|
# - cores (quantity)
|
||||||
|
# - memory (MB)
|
||||||
|
# - virtual disk (GiB)
|
||||||
|
|
||||||
|
small:
|
||||||
|
cores: 2
|
||||||
|
memory: 2048
|
||||||
|
disk: 8
|
||||||
|
|
||||||
|
medium:
|
||||||
|
cores: 2
|
||||||
|
memory: 4096
|
||||||
|
disk: 16
|
||||||
|
|
||||||
|
large:
|
||||||
|
cores: 4
|
||||||
|
memory: 4096
|
||||||
|
disk: 32
|
||||||
|
|
||||||
|
large-plus:
|
||||||
|
cores: 4
|
||||||
|
memory: 4096
|
||||||
|
disk: 48
|
||||||
|
|
||||||
|
xlarge:
|
||||||
|
cores: 4
|
||||||
|
memory: 8192
|
||||||
|
disk: 64
|
||||||
|
|
||||||
|
xlarge-plus:
|
||||||
|
cores: 4
|
||||||
|
memory: 8192
|
||||||
|
disk: 128
|
||||||
123
ansible/inventory.yml
Executable file
123
ansible/inventory.yml
Executable file
@@ -0,0 +1,123 @@
|
|||||||
|
# file: inventory.yml
|
||||||
|
proxmox:
|
||||||
|
hosts:
|
||||||
|
main-street-usa:
|
||||||
|
ansible_host: 10.1.71.11
|
||||||
|
ansible_user: wed
|
||||||
|
ansible_become: true
|
||||||
|
tomorrowland:
|
||||||
|
fantasyland:
|
||||||
|
|
||||||
|
magic_kingdom:
|
||||||
|
hosts:
|
||||||
|
main-street-usa:
|
||||||
|
tomorrowland:
|
||||||
|
fantasyland:
|
||||||
|
|
||||||
|
dns_server:
|
||||||
|
hosts:
|
||||||
|
monorail:
|
||||||
|
|
||||||
|
ntp_servers:
|
||||||
|
hosts:
|
||||||
|
sundial:
|
||||||
|
|
||||||
|
load_balancers:
|
||||||
|
hosts:
|
||||||
|
lightning-lane:
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
|
step_ca_server:
|
||||||
|
hosts:
|
||||||
|
turnstile:
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
|
authentik_server:
|
||||||
|
hosts:
|
||||||
|
guest-relations:
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
|
gitea_servers:
|
||||||
|
hosts:
|
||||||
|
mad-tea-party:
|
||||||
|
ansible_user: wed
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
|
nextcloud_server:
|
||||||
|
hosts:
|
||||||
|
the-grid:
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
|
semaphore_server:
|
||||||
|
hosts:
|
||||||
|
figment:
|
||||||
|
ansible_host: 10.1.71.37
|
||||||
|
ansible_user: wed
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
|
n8n_server:
|
||||||
|
hosts:
|
||||||
|
tiki-room:
|
||||||
|
|
||||||
|
astro_orbiter:
|
||||||
|
hosts:
|
||||||
|
astro-orbiter:
|
||||||
|
|
||||||
|
hermes_server:
|
||||||
|
hosts:
|
||||||
|
carousel-of-progress:
|
||||||
|
ansible_host: 10.1.71.131
|
||||||
|
ansible_user: wed
|
||||||
|
ansible_become: true
|
||||||
|
ansible_ssh_private_key_file: ~/.ssh/ansible
|
||||||
|
|
||||||
|
honcho_server:
|
||||||
|
hosts:
|
||||||
|
lincoln:
|
||||||
|
ansible_host: 10.1.71.132
|
||||||
|
ansible_user: wed
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
|
jmri_server:
|
||||||
|
hosts:
|
||||||
|
main-street-station:
|
||||||
|
ansible_host: 192.168.10.40
|
||||||
|
ansible_user: wed
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
|
papermc_server:
|
||||||
|
# ansible-galaxy role install engonzal.papermc
|
||||||
|
hosts:
|
||||||
|
arcade:
|
||||||
|
|
||||||
|
dev_servers:
|
||||||
|
hosts:
|
||||||
|
scrim:
|
||||||
|
backstage:
|
||||||
|
ansible_host: 10.1.71.133
|
||||||
|
ansible_user: wed
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
|
# dhcp_server:
|
||||||
|
# hosts:
|
||||||
|
# matchbox:
|
||||||
|
|
||||||
|
backup_servers:
|
||||||
|
hosts:
|
||||||
|
timekeeper:
|
||||||
|
|
||||||
|
talos_control:
|
||||||
|
hosts:
|
||||||
|
city-hall:
|
||||||
|
ansible_become: true
|
||||||
|
vars:
|
||||||
|
talosctl_version: "v1.12.4"
|
||||||
|
talos_cluster_name: "fastpass"
|
||||||
|
|
||||||
|
# matchbox_server:
|
||||||
|
# hosts:
|
||||||
|
# matchbox:
|
||||||
|
|
||||||
|
terraform_server:
|
||||||
|
hosts:
|
||||||
|
infra01:
|
||||||
5
ansible/mk
Executable file
5
ansible/mk
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
space-mountain
|
||||||
|
splash-mountain
|
||||||
|
big-thunder-mountain
|
||||||
|
peter-pans-flight
|
||||||
|
haunted-mansion
|
||||||
84
ansible/playbooks/add_dhcp_reservation.yml
Executable file
84
ansible/playbooks/add_dhcp_reservation.yml
Executable file
@@ -0,0 +1,84 @@
|
|||||||
|
---
|
||||||
|
- name: Bind VM MAC address to IP address on Ubiquiti
|
||||||
|
hosts: all
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Set VM MAC address (if defined in vars)
|
||||||
|
delegate_to: "localhost"
|
||||||
|
community.proxmox.proxmox_kvm:
|
||||||
|
api_user: "{{ proxmox_user }}"
|
||||||
|
api_password: "{{ proxmox_password }}"
|
||||||
|
api_host: "{{ proxmox_host }}"
|
||||||
|
node: "{{ proxmox_clone_node }}"
|
||||||
|
vmid: "{{ vm_id }}"
|
||||||
|
net:
|
||||||
|
net0: "virtio={{ vm_mac_address }},bridge=vmbr0,firewall=1"
|
||||||
|
update: true
|
||||||
|
update_unsafe: true
|
||||||
|
when: vm_mac_address is defined
|
||||||
|
|
||||||
|
- name: Get VM MAC address from Proxmox (not defined in vars)
|
||||||
|
when: vm_mac_address is not defined
|
||||||
|
block:
|
||||||
|
- name: Retrieve information about specific VM by name and get current configuration
|
||||||
|
delegate_to: "localhost"
|
||||||
|
community.proxmox.proxmox_vm_info:
|
||||||
|
api_user: "{{ proxmox_user }}"
|
||||||
|
api_password: "{{ proxmox_password }}"
|
||||||
|
api_host: "{{ proxmox_host }}"
|
||||||
|
name: "{{ inventory_hostname }}"
|
||||||
|
config: current
|
||||||
|
register: proxmox_vm_info
|
||||||
|
|
||||||
|
- name: Extract net0 information
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
vm_net0: "{{ proxmox_vm_info.proxmox_vms[0].config.net0 }}"
|
||||||
|
|
||||||
|
- name: Extract MAC address using regex
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
vm_mac_address: "{{ vm_net0 | regex_search('([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}') }}"
|
||||||
|
|
||||||
|
- name: Assign VM MAC address to IP address on Ubiquiti with Terraform
|
||||||
|
when: vm_mac_address is defined
|
||||||
|
delegate_to: "{{ groups['terraform_server'][0] }}"
|
||||||
|
block:
|
||||||
|
- name: Create a directory if it does not exist
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "~/homelab/terraform/unifi-dhcp"
|
||||||
|
state: directory
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
- name: Create Unifi provider file from template
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: templates/unifi_provider.tf.j2
|
||||||
|
dest: ~/homelab/terraform/unifi-dhcp/provider.tf
|
||||||
|
mode: '0644'
|
||||||
|
|
||||||
|
- name: Create Unifi user (host) file from template
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: templates/unifi-user.tf.j2
|
||||||
|
dest: ~/homelab/terraform/unifi-dhcp/{{ inventory_hostname }}.tf
|
||||||
|
mode: '0644'
|
||||||
|
|
||||||
|
- name: Configure Unifi via Terraform
|
||||||
|
community.general.terraform:
|
||||||
|
project_path: ~/homelab/terraform/unifi-dhcp
|
||||||
|
state: present
|
||||||
|
force_init: true
|
||||||
|
|
||||||
|
# - name: Add line to hosts file
|
||||||
|
# delegate_to: "{{ groups['dhcp_server'][0] }}"
|
||||||
|
# become: true
|
||||||
|
# ansible.builtin.lineinfile:
|
||||||
|
# path: /etc/dnsmasq.d/hosts.conf
|
||||||
|
# regexp: "# {{ inventory_hostname }}$"
|
||||||
|
# line: "dhcp-host={{ vm_mac_address | lower }},{{ ip_address }} # {{ inventory_hostname }}"
|
||||||
|
# state: present
|
||||||
|
# # restart dnsmasq service
|
||||||
|
# - name: Restart service dnsmasq
|
||||||
|
# delegate_to: "{{ groups['dhcp_server'][0] }}"
|
||||||
|
# become: true
|
||||||
|
# ansible.builtin.service:
|
||||||
|
# name: dnsmasq
|
||||||
|
# state: restarted
|
||||||
13
ansible/playbooks/add_dns_entry.yml
Executable file
13
ansible/playbooks/add_dns_entry.yml
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
# Replacement for add_technitium_dns_entry.yml
|
||||||
|
# This playbook uses the modular task file approach
|
||||||
|
|
||||||
|
- name: Add DNS entry using Technitium DNS
|
||||||
|
hosts: all
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Include Technitium DNS entry task
|
||||||
|
ansible.builtin.include_tasks: tasks/add_technitium_dns_entry.yml
|
||||||
|
vars:
|
||||||
|
host_name: "{{ inventory_hostname }}"
|
||||||
95
ansible/playbooks/add_service_route.yml
Normal file
95
ansible/playbooks/add_service_route.yml
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/add_service_route.yml
|
||||||
|
# DESCRIPTION: Ensures all services in the Traefik dynamic config directory
|
||||||
|
# are routed and have DNS CNAME records on monorail.
|
||||||
|
#
|
||||||
|
# 1. Syncs boilerplates/traefik/dynamic/ to lightning-lane
|
||||||
|
# 2. Scans the directory for service configs
|
||||||
|
# 3. Extracts all hostnames from Host() rules (supports multi-host)
|
||||||
|
# 4. Creates CNAME records for each hostname -> lightning-lane
|
||||||
|
#
|
||||||
|
# PREREQUISITES:
|
||||||
|
# - Service dynamic config YAML committed to boilerplates/traefik/dynamic/
|
||||||
|
# - vault_technitium_api_key defined in group_vars/all/vault
|
||||||
|
#
|
||||||
|
# USAGE:
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/add_service_route.yml
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Sync Traefik routes and ensure DNS records
|
||||||
|
hosts: localhost
|
||||||
|
connection: local
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
|
vars:
|
||||||
|
base_domain: "local.mk-labs.cloud"
|
||||||
|
dns_server: "monorail"
|
||||||
|
traefik_host: "lightning-lane.local.mk-labs.cloud"
|
||||||
|
traefik_user: "wed"
|
||||||
|
traefik_dynamic_path: "/opt/docker/traefik/dynamic/"
|
||||||
|
dynamic_config_dir: "{{ playbook_dir }}/../../boilerplates/traefik/dynamic"
|
||||||
|
|
||||||
|
# Files in the dynamic directory that are NOT service routes
|
||||||
|
exclude_configs:
|
||||||
|
- default.yml
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
# ── Step 1: Sync dynamic config to lightning-lane ──
|
||||||
|
- name: Sync Traefik dynamic configuration to lightning-lane
|
||||||
|
ansible.builtin.shell: >
|
||||||
|
rsync -av --delete
|
||||||
|
{{ dynamic_config_dir }}/
|
||||||
|
{{ traefik_user }}@{{ traefik_host }}:{{ traefik_dynamic_path }}
|
||||||
|
register: sync_result
|
||||||
|
changed_when: "'sending incremental file list' in sync_result.stdout"
|
||||||
|
|
||||||
|
# ── Step 2: Discover hostnames from Traefik router rules ──
|
||||||
|
- name: Find all dynamic config files
|
||||||
|
ansible.builtin.find:
|
||||||
|
paths: "{{ dynamic_config_dir }}"
|
||||||
|
patterns: "*.yml"
|
||||||
|
register: config_files
|
||||||
|
|
||||||
|
- name: Read config files
|
||||||
|
ansible.builtin.slurp:
|
||||||
|
src: "{{ item.path }}"
|
||||||
|
register: slurped_configs
|
||||||
|
loop: "{{ config_files.files }}"
|
||||||
|
when: item.path | basename not in exclude_configs
|
||||||
|
|
||||||
|
- name: Extract all hostnames from Host() rules
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
hostnames: >-
|
||||||
|
{% set hosts = [] -%}
|
||||||
|
{% for result in slurped_configs.results if result.content is defined -%}
|
||||||
|
{% set content = result.content | b64decode -%}
|
||||||
|
{% for match in content | regex_findall('Host\(`([^`]+)`\)') -%}
|
||||||
|
{% for h in match.split(' || ') -%}
|
||||||
|
{% set h = h | regex_replace('`', '') | trim -%}
|
||||||
|
{% if h.endswith('.local.mk-labs.cloud') and h not in hosts -%}
|
||||||
|
{% set _ = hosts.append(h) -%}
|
||||||
|
{% endif -%}
|
||||||
|
{% endfor -%}
|
||||||
|
{% endfor -%}
|
||||||
|
{% endfor -%}
|
||||||
|
{{ hosts | unique | list }}
|
||||||
|
|
||||||
|
- name: Display hostnames to create
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "Hostnames found: {{ hostnames }}"
|
||||||
|
|
||||||
|
# ── Step 3: Create DNS CNAME records ──
|
||||||
|
- name: Create DNS CNAME record for each hostname
|
||||||
|
effectivelywild.technitium_dns.technitium_dns_add_record:
|
||||||
|
api_url: "http://{{ dns_server }}.{{ base_domain }}"
|
||||||
|
api_token: "{{ vault_technitium_api_key }}"
|
||||||
|
zone: "{{ base_domain }}"
|
||||||
|
name: "{{ item }}"
|
||||||
|
type: "CNAME"
|
||||||
|
cname: "lightning-lane.{{ base_domain }}"
|
||||||
|
ttl: 360
|
||||||
|
validate_certs: false
|
||||||
|
loop: "{{ hostnames }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item }}"
|
||||||
18
ansible/playbooks/add_technitium_dns_entry.yml.backup
Executable file
18
ansible/playbooks/add_technitium_dns_entry.yml.backup
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
- name: Add entry to Technitium DNS
|
||||||
|
hosts: all
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: "Create DNS entry for {{ inventory_hostname }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
effectivelywild.technitium_dns.technitium_dns_add_record:
|
||||||
|
api_url: "http://{{ dns_server }}.{{ base_domain }}"
|
||||||
|
api_token: "{{ vault_technitium_api_key }}"
|
||||||
|
zone: "{{ base_domain }}"
|
||||||
|
name: "{{ inventory_hostname }}.{{ base_domain }}"
|
||||||
|
type: "A"
|
||||||
|
ipAddress: "{{ ip_address }}"
|
||||||
|
ptr: true
|
||||||
|
ttl: 360
|
||||||
|
# validate_certs: false
|
||||||
20
ansible/playbooks/configure_proxmox_oidc.yml
Normal file
20
ansible/playbooks/configure_proxmox_oidc.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
# ansible/playbooks/configure_proxmox_oidc.yml
|
||||||
|
#
|
||||||
|
# Configures Proxmox OIDC authentication with Authentik.
|
||||||
|
# Only targets one node since realm config is cluster-wide.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# cd ansible
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/configure_proxmox_oidc.yml
|
||||||
|
#
|
||||||
|
# To also set up ACL entries for your user:
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/configure_proxmox_oidc.yml \
|
||||||
|
# -e '{"proxmox_oidc_acl_entries": [{"path": "/", "user": "rblundon@authentik", "role": "Administrator"}]}'
|
||||||
|
|
||||||
|
- name: Configure Proxmox Authentik OIDC
|
||||||
|
hosts: main-street-usa
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- role: proxmox
|
||||||
|
tags: [proxmox-oidc]
|
||||||
37
ansible/playbooks/configure_unbound.yml
Executable file
37
ansible/playbooks/configure_unbound.yml
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
- name: Master playbook to install and configure unbound
|
||||||
|
hosts: unbound_servers
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Permit traffic in default zone for dns service
|
||||||
|
ansible.posix.firewalld:
|
||||||
|
service: dns
|
||||||
|
permanent: true
|
||||||
|
immediate: true
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
- name: Create the directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /etc/systemd/resolved.conf.d
|
||||||
|
state: directory
|
||||||
|
mode: '0755'
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
|
||||||
|
- name: Put `unbound.conf` in the correct place
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: ../roles/common/files/unbound.conf
|
||||||
|
dest: /etc/systemd/resolved.conf.d/unbound.conf
|
||||||
|
mode: '0644'
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
|
||||||
|
- name: Restart service systemd-resolved
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: systemd-resolved
|
||||||
|
state: restarted
|
||||||
|
|
||||||
|
- name: Install unbound via role
|
||||||
|
ansible.builtin.import_role:
|
||||||
|
name: Anthony25.unbound
|
||||||
23
ansible/playbooks/create_vm.yml
Executable file
23
ansible/playbooks/create_vm.yml
Executable file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
- name: Master playbook to create VM
|
||||||
|
hosts: all
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
|
- name: Proxmox Create VM Playbook
|
||||||
|
ansible.builtin.import_playbook: proxmox_create_vm.yml
|
||||||
|
|
||||||
|
- name: Set cloud-init network
|
||||||
|
ansible.builtin.import_playbook: populate_cloud_init.yml
|
||||||
|
when: vm_os_distribution == "ubuntu"
|
||||||
|
|
||||||
|
- name: DNS Playbook
|
||||||
|
ansible.builtin.import_playbook: add_dns_entry.yml
|
||||||
|
|
||||||
|
- name: DHCP Playbook
|
||||||
|
ansible.builtin.import_playbook: add_dhcp_reservation.yml
|
||||||
|
|
||||||
|
- name: Start VM Playbook
|
||||||
|
ansible.builtin.import_playbook: proxmox_start_vm.yml
|
||||||
|
|
||||||
|
- name: Set Hostname Playbook
|
||||||
|
ansible.builtin.import_playbook: set_hostname.yml
|
||||||
6
infra-config/playbooks/create_vm_from_clone.yml → ansible/playbooks/create_vm_from_clone.yml
Normal file → Executable file
6
infra-config/playbooks/create_vm_from_clone.yml → ansible/playbooks/create_vm_from_clone.yml
Normal file → Executable file
@@ -6,11 +6,15 @@
|
|||||||
- name: Proxmox Clone VM Playbook
|
- name: Proxmox Clone VM Playbook
|
||||||
ansible.builtin.import_playbook: proxmox_clone_vm.yml
|
ansible.builtin.import_playbook: proxmox_clone_vm.yml
|
||||||
|
|
||||||
|
- name: Set cloud-init network
|
||||||
|
ansible.builtin.import_playbook: populate_cloud_init.yml
|
||||||
|
when: vm_os_distribution == "ubuntu"
|
||||||
|
|
||||||
- name: DNS Playbook
|
- name: DNS Playbook
|
||||||
ansible.builtin.import_playbook: add_dns_entry.yml
|
ansible.builtin.import_playbook: add_dns_entry.yml
|
||||||
|
|
||||||
- name: DHCP Playbook
|
- name: DHCP Playbook
|
||||||
ansible.builtin.import_playbook: add_dhcp_entry.yml
|
ansible.builtin.import_playbook: add_dhcp_reservation.yml
|
||||||
|
|
||||||
- name: Start VM Playbook
|
- name: Start VM Playbook
|
||||||
ansible.builtin.import_playbook: proxmox_start_vm.yml
|
ansible.builtin.import_playbook: proxmox_start_vm.yml
|
||||||
6
ansible/playbooks/day0_baseline.yml
Normal file
6
ansible/playbooks/day0_baseline.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
- name: Apply day0 baseline
|
||||||
|
hosts: "{{ target | default('all') }}"
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- day0-baseline
|
||||||
32
ansible/playbooks/day0_expand_root_lv.yml
Normal file
32
ansible/playbooks/day0_expand_root_lv.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# day0_expand_root_lv.yml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Reclaims unallocated PE on the root volume group, extending the root LV
|
||||||
|
# to fill the VG and resizing the underlying filesystem (ext4 or xfs).
|
||||||
|
#
|
||||||
|
# Belongs to the day0 host-provisioning lifecycle. The Ubuntu Server
|
||||||
|
# autoinstall template ships with the root LV at ~half the disk size by
|
||||||
|
# default; this playbook is the canonical one-shot fix-up for that.
|
||||||
|
#
|
||||||
|
# Idempotent and safe to re-run. Hosts without LVM are no-op'd cleanly.
|
||||||
|
#
|
||||||
|
# Opt-out: set `expand_root_lv_skip: true` in host_vars/<host>.yml for
|
||||||
|
# hosts where free PE should NOT be claimed by root (e.g. hosts with a
|
||||||
|
# planned second LV in the same VG for application data).
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ansible-playbook playbooks/day0_expand_root_lv.yml
|
||||||
|
# ansible-playbook playbooks/day0_expand_root_lv.yml -e target=lincoln
|
||||||
|
# ansible-playbook playbooks/day0_expand_root_lv.yml -e target=honcho_server
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Expand root logical volume to fill VG
|
||||||
|
hosts: "{{ target | default('all') }}"
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
tasks:
|
||||||
|
- name: Apply expand_root_lv role unless host opts out
|
||||||
|
ansible.builtin.include_role:
|
||||||
|
name: expand_root_lv
|
||||||
|
when: not (expand_root_lv_skip | default(false) | bool)
|
||||||
23
ansible/playbooks/day0_linux_baseline.yml
Normal file
23
ansible/playbooks/day0_linux_baseline.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# day0_linux_baseline.yml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Applies the mk-labs Linux baseline (linux-baseline role) to one or more
|
||||||
|
# hosts. Idempotent and safe to re-run.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ansible-playbook playbooks/day0_linux_baseline.yml
|
||||||
|
# ansible-playbook playbooks/day0_linux_baseline.yml -e target=figment
|
||||||
|
# ansible-playbook playbooks/day0_linux_baseline.yml -e target=semaphore_server
|
||||||
|
#
|
||||||
|
# To trigger an opt-in full system upgrade:
|
||||||
|
# ansible-playbook playbooks/day0_linux_baseline.yml \
|
||||||
|
# -e target=figment -e 'baseline_features={"full_upgrade": true}'
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Apply mk-labs Linux baseline
|
||||||
|
hosts: "{{ target | default('all') }}"
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
roles:
|
||||||
|
- linux-baseline
|
||||||
30
ansible/playbooks/day0_provision.yml
Normal file
30
ansible/playbooks/day0_provision.yml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# day0_provision.yml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Umbrella day0 playbook. Runs the full host-provisioning lifecycle in
|
||||||
|
# the correct order against newly-built VMs, so the operator runs ONE
|
||||||
|
# command per new host rather than chaining day0 steps manually.
|
||||||
|
#
|
||||||
|
# Order matters:
|
||||||
|
# 1. linux-baseline — timezone, NTP, packages, SSH hardening, jarvis user
|
||||||
|
# 2. expand_root_lv — reclaim PE left unallocated by the Ubuntu
|
||||||
|
# autoinstall template default
|
||||||
|
#
|
||||||
|
# Idempotent: every step is safe to re-run. Suitable to apply periodically
|
||||||
|
# from Semaphore as a baseline-drift check.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ansible-playbook playbooks/day0_provision.yml -e target=lincoln
|
||||||
|
# ansible-playbook playbooks/day0_provision.yml -e target=honcho_server
|
||||||
|
#
|
||||||
|
# For finer control over a single phase, the constituent playbooks are:
|
||||||
|
# playbooks/day0_linux_baseline.yml
|
||||||
|
# playbooks/day0_expand_root_lv.yml
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Import day0 linux baseline
|
||||||
|
ansible.builtin.import_playbook: day0_linux_baseline.yml
|
||||||
|
|
||||||
|
- name: Import day0 expand root LV
|
||||||
|
ansible.builtin.import_playbook: day0_expand_root_lv.yml
|
||||||
400
ansible/playbooks/day1_configure_netbox_catalog.yml
Normal file
400
ansible/playbooks/day1_configure_netbox_catalog.yml
Normal file
@@ -0,0 +1,400 @@
|
|||||||
|
---
|
||||||
|
# ansible/playbooks/day1_configure_netbox_catalog.yml
|
||||||
|
#
|
||||||
|
# Creates the tag taxonomy and custom fields in NetBox for service catalog
|
||||||
|
# documentation. Run once (idempotent — uses name-based checks).
|
||||||
|
#
|
||||||
|
# Usage (from ansible/ directory):
|
||||||
|
# ansible-playbook playbooks/day1_configure_netbox_catalog.yml
|
||||||
|
#
|
||||||
|
# Requires:
|
||||||
|
# - vault_netbox_token in Ansible Vault
|
||||||
|
# - netbox reachable at http://fire-station.local.mk-labs.cloud
|
||||||
|
|
||||||
|
- name: Configure NetBox service catalog taxonomy
|
||||||
|
hosts: localhost
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
|
vars_files:
|
||||||
|
- "{{ playbook_dir }}/../group_vars/all/vault"
|
||||||
|
|
||||||
|
vars:
|
||||||
|
netbox_url: "http://fire-station.local.mk-labs.cloud"
|
||||||
|
netbox_token: "{{ vault_netbox_token }}"
|
||||||
|
netbox_api: "{{ netbox_url }}/api"
|
||||||
|
headers:
|
||||||
|
Authorization: "Token {{ netbox_token }}"
|
||||||
|
Content-Type: "application/json"
|
||||||
|
Accept: "application/json"
|
||||||
|
|
||||||
|
# ── Tag definitions ───────────────────────────────────────────────────
|
||||||
|
tags:
|
||||||
|
# Infrastructure type
|
||||||
|
- name: k8s
|
||||||
|
slug: k8s
|
||||||
|
color: "2196f3" # blue
|
||||||
|
description: "Workload running in the fastpass Kubernetes cluster"
|
||||||
|
- name: vm
|
||||||
|
slug: vm
|
||||||
|
color: "4caf50" # green
|
||||||
|
description: "Traditional VM or LXC on Proxmox"
|
||||||
|
# Service tier
|
||||||
|
- name: platform
|
||||||
|
slug: platform
|
||||||
|
color: "9c27b0" # purple
|
||||||
|
description: "Platform/infrastructure service (not user-facing)"
|
||||||
|
- name: application
|
||||||
|
slug: application
|
||||||
|
color: "ff9800" # orange
|
||||||
|
description: "User-facing application workload"
|
||||||
|
# Service categories
|
||||||
|
- name: monitoring
|
||||||
|
slug: monitoring
|
||||||
|
color: "607d8b" # grey
|
||||||
|
description: "Metrics, logging, alerting"
|
||||||
|
- name: auth
|
||||||
|
slug: auth
|
||||||
|
color: "607d8b"
|
||||||
|
description: "Authentication and SSO"
|
||||||
|
- name: gitops
|
||||||
|
slug: gitops
|
||||||
|
color: "607d8b"
|
||||||
|
description: "GitOps and CI/CD"
|
||||||
|
- name: dashboard
|
||||||
|
slug: dashboard
|
||||||
|
color: "607d8b"
|
||||||
|
description: "Dashboard and portal services"
|
||||||
|
- name: storage
|
||||||
|
slug: storage
|
||||||
|
color: "607d8b"
|
||||||
|
description: "Storage and file services"
|
||||||
|
- name: dns
|
||||||
|
slug: dns
|
||||||
|
color: "607d8b"
|
||||||
|
description: "DNS and name resolution"
|
||||||
|
- name: automation
|
||||||
|
slug: automation
|
||||||
|
color: "607d8b"
|
||||||
|
description: "Automation and orchestration"
|
||||||
|
- name: networking
|
||||||
|
slug: networking
|
||||||
|
color: "607d8b"
|
||||||
|
description: "Network infrastructure services"
|
||||||
|
- name: inference
|
||||||
|
slug: inference
|
||||||
|
color: "607d8b"
|
||||||
|
description: "AI/ML inference workloads"
|
||||||
|
|
||||||
|
# ── Custom field definitions ──────────────────────────────────────────
|
||||||
|
# object_types use app_label.model format
|
||||||
|
custom_fields:
|
||||||
|
- name: hostnames
|
||||||
|
label: Hostnames
|
||||||
|
type: longtext
|
||||||
|
object_types:
|
||||||
|
- ipam.ipaddress
|
||||||
|
- virtualization.virtualmachine
|
||||||
|
description: "All DNS names that resolve to this service (comma-separated)"
|
||||||
|
ui_visible: always
|
||||||
|
ui_editable: yes
|
||||||
|
|
||||||
|
- name: namespace
|
||||||
|
label: Namespace
|
||||||
|
type: text
|
||||||
|
object_types:
|
||||||
|
- ipam.ipaddress
|
||||||
|
- virtualization.virtualmachine
|
||||||
|
description: "Kubernetes namespace (blank for VM-based services)"
|
||||||
|
ui_visible: always
|
||||||
|
ui_editable: yes
|
||||||
|
|
||||||
|
- name: managed_by
|
||||||
|
label: Managed By
|
||||||
|
type: select
|
||||||
|
object_types:
|
||||||
|
- ipam.ipaddress
|
||||||
|
- virtualization.virtualmachine
|
||||||
|
description: "How this service is managed"
|
||||||
|
choices:
|
||||||
|
- ArgoCD
|
||||||
|
- Ansible
|
||||||
|
- Manual
|
||||||
|
ui_visible: always
|
||||||
|
ui_editable: yes
|
||||||
|
|
||||||
|
- name: thematic_name
|
||||||
|
label: Thematic Name
|
||||||
|
type: text
|
||||||
|
object_types:
|
||||||
|
- ipam.ipaddress
|
||||||
|
- virtualization.virtualmachine
|
||||||
|
description: "Disney/Magic Kingdom thematic hostname for this service"
|
||||||
|
ui_visible: always
|
||||||
|
ui_editable: yes
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
# ── Tags ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
- name: Fetch existing tags
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/tags/?limit=200"
|
||||||
|
method: GET
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
return_content: true
|
||||||
|
status_code: 200
|
||||||
|
register: existing_tags_response
|
||||||
|
|
||||||
|
- name: Set existing tag slugs fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
existing_tag_slugs: "{{ existing_tags_response.json.results | map(attribute='slug') | list }}"
|
||||||
|
|
||||||
|
- name: Create tags
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/tags/"
|
||||||
|
method: POST
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
slug: "{{ item.slug }}"
|
||||||
|
color: "{{ item.color }}"
|
||||||
|
description: "{{ item.description }}"
|
||||||
|
status_code: 201
|
||||||
|
loop: "{{ tags }}"
|
||||||
|
when: item.slug not in existing_tag_slugs
|
||||||
|
register: tag_creation
|
||||||
|
changed_when: tag_creation.status == 201
|
||||||
|
|
||||||
|
# ── Custom Fields ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
- name: Fetch existing custom fields
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/custom-fields/?limit=200"
|
||||||
|
method: GET
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
return_content: true
|
||||||
|
status_code: 200
|
||||||
|
register: existing_cf_response
|
||||||
|
|
||||||
|
- name: Set existing custom field names fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
existing_cf_names: "{{ existing_cf_response.json.results | map(attribute='name') | list }}"
|
||||||
|
|
||||||
|
- name: Create custom field — hostnames
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/custom-fields/"
|
||||||
|
method: POST
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
name: hostnames
|
||||||
|
label: Hostnames
|
||||||
|
type: longtext
|
||||||
|
object_types: "{{ custom_fields[0].object_types }}"
|
||||||
|
description: "{{ custom_fields[0].description }}"
|
||||||
|
ui_visible: always
|
||||||
|
ui_editable: yes
|
||||||
|
status_code: 201
|
||||||
|
when: "'hostnames' not in existing_cf_names"
|
||||||
|
register: cf_hostnames
|
||||||
|
changed_when: cf_hostnames.status == 201
|
||||||
|
|
||||||
|
- name: Create custom field — namespace
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/custom-fields/"
|
||||||
|
method: POST
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
name: namespace
|
||||||
|
label: Namespace
|
||||||
|
type: text
|
||||||
|
object_types: "{{ custom_fields[1].object_types }}"
|
||||||
|
description: "{{ custom_fields[1].description }}"
|
||||||
|
ui_visible: always
|
||||||
|
ui_editable: yes
|
||||||
|
status_code: 201
|
||||||
|
when: "'namespace' not in existing_cf_names"
|
||||||
|
register: cf_namespace
|
||||||
|
changed_when: cf_namespace.status == 201
|
||||||
|
|
||||||
|
- name: Create choice set for managed_by field
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/custom-field-choice-sets/"
|
||||||
|
method: POST
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
name: managed-by-choices
|
||||||
|
extra_choices:
|
||||||
|
- - ArgoCD
|
||||||
|
- ArgoCD
|
||||||
|
- - Ansible
|
||||||
|
- Ansible
|
||||||
|
- - Manual
|
||||||
|
- Manual
|
||||||
|
status_code: [201, 400]
|
||||||
|
register: choice_set
|
||||||
|
changed_when: choice_set.status == 201
|
||||||
|
|
||||||
|
- name: Fetch choice set ID
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/custom-field-choice-sets/?name=managed-by-choices"
|
||||||
|
method: GET
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
return_content: true
|
||||||
|
status_code: 200
|
||||||
|
register: choice_set_response
|
||||||
|
|
||||||
|
- name: Create custom field — managed_by
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/custom-fields/"
|
||||||
|
method: POST
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
name: managed_by
|
||||||
|
label: Managed By
|
||||||
|
type: select
|
||||||
|
object_types: "{{ custom_fields[2].object_types }}"
|
||||||
|
description: "{{ custom_fields[2].description }}"
|
||||||
|
choice_set: "{{ choice_set_response.json.results[0].id }}"
|
||||||
|
ui_visible: always
|
||||||
|
ui_editable: yes
|
||||||
|
status_code: 201
|
||||||
|
when: "'managed_by' not in existing_cf_names"
|
||||||
|
register: cf_managed_by
|
||||||
|
changed_when: cf_managed_by.status == 201
|
||||||
|
|
||||||
|
- name: Create custom field — thematic_name
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/custom-fields/"
|
||||||
|
method: POST
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
name: thematic_name
|
||||||
|
label: Thematic Name
|
||||||
|
type: text
|
||||||
|
object_types: "{{ custom_fields[3].object_types }}"
|
||||||
|
description: "{{ custom_fields[3].description }}"
|
||||||
|
ui_visible: always
|
||||||
|
ui_editable: yes
|
||||||
|
status_code: 201
|
||||||
|
when: "'thematic_name' not in existing_cf_names"
|
||||||
|
register: cf_thematic_name
|
||||||
|
changed_when: cf_thematic_name.status == 201
|
||||||
|
|
||||||
|
# ── Provisioning Pipeline Fields ──────────────────────────────────────
|
||||||
|
# These fields drive the NetBox → n8n → Terraform pipeline.
|
||||||
|
# proxmox_datastore already exists — PATCH it to add utilidor choice.
|
||||||
|
# data_disk_enabled and data_disk_size_gb are new POSTs.
|
||||||
|
|
||||||
|
- name: Fetch proxmox_datastore field ID
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/custom-fields/?name=proxmox_datastore"
|
||||||
|
method: GET
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
return_content: true
|
||||||
|
status_code: 200
|
||||||
|
register: proxmox_datastore_cf_response
|
||||||
|
|
||||||
|
- name: Fetch proxmox-datastore-choices choice set ID
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/custom-field-choice-sets/?name=proxmox-datastore-choices"
|
||||||
|
method: GET
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
return_content: true
|
||||||
|
status_code: 200
|
||||||
|
register: datastore_choice_set_response
|
||||||
|
|
||||||
|
- name: Create proxmox-datastore-choices choice set if missing
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/custom-field-choice-sets/"
|
||||||
|
method: POST
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
name: proxmox-datastore-choices
|
||||||
|
extra_choices:
|
||||||
|
- - liberty-tree
|
||||||
|
- liberty-tree
|
||||||
|
- - utilidor
|
||||||
|
- utilidor
|
||||||
|
status_code: [201, 400]
|
||||||
|
register: datastore_choice_set_create
|
||||||
|
changed_when: datastore_choice_set_create.status == 201
|
||||||
|
when: datastore_choice_set_response.json.count == 0
|
||||||
|
|
||||||
|
- name: Re-fetch proxmox-datastore-choices choice set ID after possible creation
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/custom-field-choice-sets/?name=proxmox-datastore-choices"
|
||||||
|
method: GET
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
return_content: true
|
||||||
|
status_code: 200
|
||||||
|
register: datastore_choice_set_response
|
||||||
|
|
||||||
|
- name: Patch proxmox_datastore field to use choice set with utilidor
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/custom-fields/{{ proxmox_datastore_cf_response.json.results[0].id }}/"
|
||||||
|
method: PATCH
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
choice_set: "{{ datastore_choice_set_response.json.results[0].id }}"
|
||||||
|
status_code: 200
|
||||||
|
when: proxmox_datastore_cf_response.json.count > 0
|
||||||
|
register: cf_datastore_patch
|
||||||
|
changed_when: cf_datastore_patch.status == 200
|
||||||
|
|
||||||
|
- name: Create custom field — data_disk_enabled
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/custom-fields/"
|
||||||
|
method: POST
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
name: data_disk_enabled
|
||||||
|
label: Data Disk Enabled
|
||||||
|
type: boolean
|
||||||
|
object_types:
|
||||||
|
- virtualization.virtualmachine
|
||||||
|
description: "When true, Terraform provisions a second disk for application data storage."
|
||||||
|
default: false
|
||||||
|
ui_visible: always
|
||||||
|
ui_editable: yes
|
||||||
|
status_code: 201
|
||||||
|
when: "'data_disk_enabled' not in existing_cf_names"
|
||||||
|
register: cf_data_disk_enabled
|
||||||
|
changed_when: cf_data_disk_enabled.status == 201
|
||||||
|
|
||||||
|
- name: Create custom field — data_disk_size_gb
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ netbox_api }}/extras/custom-fields/"
|
||||||
|
method: POST
|
||||||
|
headers: "{{ headers }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
name: data_disk_size_gb
|
||||||
|
label: Data Disk Size (GB)
|
||||||
|
type: integer
|
||||||
|
object_types:
|
||||||
|
- virtualization.virtualmachine
|
||||||
|
description: "Size in GB for the optional second data disk. Only used when data_disk_enabled is true."
|
||||||
|
ui_visible: always
|
||||||
|
ui_editable: yes
|
||||||
|
status_code: 201
|
||||||
|
when: "'data_disk_size_gb' not in existing_cf_names"
|
||||||
|
register: cf_data_disk_size
|
||||||
|
changed_when: cf_data_disk_size.status == 201
|
||||||
|
|
||||||
|
# ── Summary ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
- name: Summary
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "Tags created: {{ tag_creation.results | selectattr('status', 'equalto', 201) | list | length }}"
|
||||||
|
- "Tags skipped (already exist): {{ tag_creation.results | selectattr('skipped', 'defined') | list | length }}"
|
||||||
|
- "Custom fields configured: hostnames, namespace, managed_by, thematic_name, proxmox_datastore (patched), data_disk_enabled, data_disk_size_gb"
|
||||||
18
ansible/playbooks/day1_deploy_authentik.yml
Normal file
18
ansible/playbooks/day1_deploy_authentik.yml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/deploy_authentik.yml
|
||||||
|
# DESCRIPTION: Deploys Authentik identity provider on guest-relations.
|
||||||
|
# Installs Docker and configures Authentik with PostgreSQL and Redis.
|
||||||
|
#
|
||||||
|
# USAGE:
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/deploy_authentik.yml
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Deploy Authentik identity provider
|
||||||
|
hosts: authentik_server
|
||||||
|
become: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- common
|
||||||
|
- docker-host
|
||||||
|
- authentik
|
||||||
23
ansible/playbooks/day1_deploy_gitea.yml
Normal file
23
ansible/playbooks/day1_deploy_gitea.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: ansible/playbooks/day1_deploy_gitea.yml
|
||||||
|
# DESCRIPTION: Deploys Gitea + PostgreSQL on mad-tea-party (10.1.71.129)
|
||||||
|
# Role chain: common → docker-host → gitea
|
||||||
|
#
|
||||||
|
# USAGE:
|
||||||
|
# ansible-playbook -i ansible/inventory.yml ansible/playbooks/day1_deploy_gitea.yml
|
||||||
|
#
|
||||||
|
# SECRETS REQUIRED IN VAULT (group_vars/all/vault):
|
||||||
|
# vault_gitea_db_password
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Deploy Gitea on mad-tea-party
|
||||||
|
hosts: gitea_servers
|
||||||
|
become: true
|
||||||
|
|
||||||
|
vars:
|
||||||
|
gitea_db_password: "{{ vault_gitea_db_password }}"
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: docker-host
|
||||||
|
- role: gitea
|
||||||
79
ansible/playbooks/day1_deploy_hermes.yml
Normal file
79
ansible/playbooks/day1_deploy_hermes.yml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
# =============================================================================
|
||||||
|
# day1_deploy_hermes.yml
|
||||||
|
# Deploy Hermes Agent (Nous Research) on carousel-of-progress (10.1.71.131)
|
||||||
|
#
|
||||||
|
# FIRST-RUN WORKFLOW:
|
||||||
|
# 1. Run this playbook:
|
||||||
|
# ansible-playbook playbooks/day1_deploy_hermes.yml
|
||||||
|
#
|
||||||
|
# 2. SSH to the host and run the setup wizard as the hermes user:
|
||||||
|
# ssh wed@carousel-of-progress.local.mk-labs.cloud
|
||||||
|
# sudo -u hermes hermes setup
|
||||||
|
#
|
||||||
|
# 3. Once configured, start and verify the service:
|
||||||
|
# sudo systemctl start hermes
|
||||||
|
# sudo systemctl status hermes
|
||||||
|
# sudo journalctl -u hermes -f
|
||||||
|
#
|
||||||
|
# VARIABLES:
|
||||||
|
# hermes_skip_browser: true — set to skip Playwright/Chromium install
|
||||||
|
# (saves ~300MB if browser automation not needed)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- name: Deploy Hermes Agent on carousel-of-progress
|
||||||
|
hosts: carousel-of-progress
|
||||||
|
gather_facts: true
|
||||||
|
|
||||||
|
pre_tasks:
|
||||||
|
- name: Verify target is carousel-of-progress
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- inventory_hostname == "carousel-of-progress"
|
||||||
|
fail_msg: >
|
||||||
|
This playbook is scoped to carousel-of-progress only.
|
||||||
|
Got: {{ inventory_hostname }}
|
||||||
|
|
||||||
|
- name: Confirm OS is Ubuntu
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- ansible_distribution == "Ubuntu"
|
||||||
|
fail_msg: >
|
||||||
|
This playbook requires Ubuntu. Found: {{ ansible_distribution }}.
|
||||||
|
(If running Fedora, swap apt tasks for dnf and adjust Playwright deps.)
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: hermes
|
||||||
|
vars:
|
||||||
|
hermes_skip_browser: false # set true to skip Chromium install
|
||||||
|
|
||||||
|
post_tasks:
|
||||||
|
- name: Verify hermes binary is accessible system-wide
|
||||||
|
ansible.builtin.command: hermes --version
|
||||||
|
register: hermes_version_check
|
||||||
|
changed_when: false
|
||||||
|
failed_when: hermes_version_check.rc != 0
|
||||||
|
|
||||||
|
- name: Print hermes version
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ hermes_version_check.stdout }}"
|
||||||
|
|
||||||
|
- name: Print post-install instructions
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "============================================================"
|
||||||
|
- "Hermes installed on carousel-of-progress (10.1.71.131)"
|
||||||
|
- "============================================================"
|
||||||
|
- "Next steps:"
|
||||||
|
- " 1. SSH to the host:"
|
||||||
|
- " ssh wed@carousel-of-progress.local.mk-labs.cloud"
|
||||||
|
- " 2. Run the setup wizard as the hermes user:"
|
||||||
|
- " sudo -u hermes hermes setup"
|
||||||
|
- " 3. After config, start the service:"
|
||||||
|
- " sudo systemctl start hermes"
|
||||||
|
- " 4. Verify:"
|
||||||
|
- " sudo systemctl status hermes"
|
||||||
|
- " sudo journalctl -u hermes -f"
|
||||||
|
- "============================================================"
|
||||||
|
- "Service is ENABLED but NOT STARTED — config required first."
|
||||||
|
- "============================================================"
|
||||||
18
ansible/playbooks/day1_deploy_honcho.yml
Normal file
18
ansible/playbooks/day1_deploy_honcho.yml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# day1_deploy_honcho.yml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Deploys Honcho + pgvector PostgreSQL on the `lincoln` host. Assumes day0
|
||||||
|
# host provisioning (linux-baseline + expand_root_lv) is already complete.
|
||||||
|
#
|
||||||
|
# Run via:
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day0_provision.yml -e target=lincoln
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day1_deploy_honcho.yml
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Deploy Honcho on lincoln
|
||||||
|
hosts: honcho_server
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
roles:
|
||||||
|
- honcho
|
||||||
24
ansible/playbooks/day1_deploy_jmri.yml
Normal file
24
ansible/playbooks/day1_deploy_jmri.yml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# day1_deploy_jmri.yml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Deploys JMRI JmriFaceless headless server on main-street-station.
|
||||||
|
# Applies linux-baseline first, then the jmri role.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ansible-playbook playbooks/day1_deploy_jmri.yml
|
||||||
|
# ansible-playbook playbooks/day1_deploy_jmri.yml -e target=main-street-station
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# 1. Host is in inventory under jmri_server group
|
||||||
|
# 2. jmri_profile_id is set in host_vars/main-street-station.yml
|
||||||
|
# 3. SSH access as 'wed' with sudo
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Deploy JMRI headless server
|
||||||
|
hosts: "{{ target | default('jmri_server') }}"
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
roles:
|
||||||
|
- linux-baseline
|
||||||
|
- jmri
|
||||||
25
ansible/playbooks/day1_deploy_llm_inference.yml
Normal file
25
ansible/playbooks/day1_deploy_llm_inference.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/day1_deploy_llm_inference.yml
|
||||||
|
# DESCRIPTION: Day 1 playbook for astro-orbiter LLM inference stack.
|
||||||
|
# Deploys vLLM + Gemma 2 27B on RTX 3090 via OCuLink.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# cd ~/git/homelab/ansible
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day1_deploy_llm_inference.yml
|
||||||
|
#
|
||||||
|
# Phases (added incrementally — safe to re-run):
|
||||||
|
# 1. Foundation — groups, directories, vault assertion
|
||||||
|
# 2. Driver — nvidia-driver-595-open (idempotent; already installed)
|
||||||
|
# 3. vLLM — Python venv + pip install vllm
|
||||||
|
# 4. Model — HF login, Gemma 2 27B snapshot_download
|
||||||
|
# 5. Serve — systemd vllm-serve.service, health check
|
||||||
|
# 6. Integration — Hermes provider config on carousel
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Deploy LLM inference stack on astro-orbiter
|
||||||
|
hosts: astro_orbiter
|
||||||
|
gather_facts: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: llm-inference
|
||||||
35
ansible/playbooks/day1_deploy_llm_inference_multimodel.yml
Normal file
35
ansible/playbooks/day1_deploy_llm_inference_multimodel.yml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/day1_deploy_llm_inference_multimodel.yml
|
||||||
|
# DESCRIPTION: Day 1 playbook for the dual-model (aux + tool-calling) rollout
|
||||||
|
# on astro-orbiter. Builds on roles/llm-inference (CUDA/driver
|
||||||
|
# already done) — does not replace it.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# cd ~/git/homelab/ansible
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day1_deploy_llm_inference_multimodel.yml
|
||||||
|
# # or scope to specific phases:
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day1_deploy_llm_inference_multimodel.yml --tags discover
|
||||||
|
#
|
||||||
|
# EXECUTION CHANNEL (2026-08-12, War Machine): run via the Semaphore template
|
||||||
|
# "llm_inference_multimodel_stage_models" (scoped to --tags models). Do NOT
|
||||||
|
# run this via direct ansible-playbook or ad-hoc ssh/curl/systemctl — all
|
||||||
|
# homelab inference changes go through Ansible roles executed by Semaphore for
|
||||||
|
# audit/visibility. Phase 1 (models) is idempotent: it only downloads/stages a
|
||||||
|
# GGUF when missing or size-mismatched, and only restarts the router when a new
|
||||||
|
# GGUF is detected (normal re-runs that find the files correct touch nothing).
|
||||||
|
#
|
||||||
|
# Phases (see roles/llm-inference-multimodel/README.md for detail):
|
||||||
|
# 0. discover — read-only; confirm existing Gemma service management
|
||||||
|
# 1. models — idempotent GGUF downloads (Phi-4-14B, Mistral-Small-24B)
|
||||||
|
# 2. systemd — deploy both unit files, do NOT auto-start
|
||||||
|
# 3. firewall — scope ports 8000/8001, non-0.0.0.0 bind
|
||||||
|
# 4. verify — start both services, smoke test, VRAM check
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Deploy dual-model LLM inference stack on astro-orbiter
|
||||||
|
hosts: astro_orbiter
|
||||||
|
gather_facts: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: llm-inference-multimodel
|
||||||
106
ansible/playbooks/day1_deploy_llm_router_shadow.yml
Normal file
106
ansible/playbooks/day1_deploy_llm_router_shadow.yml
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/day1_deploy_llm_router_shadow.yml
|
||||||
|
# DESCRIPTION: Deploy llama-server in router mode on a shadow port (8003).
|
||||||
|
#
|
||||||
|
# This playbook deploys and validates the llama.cpp router mode supervisor on
|
||||||
|
# astro-orbiter (10.1.71.130) WITHOUT touching the production endpoint
|
||||||
|
# (llama-server-qwen, port 8002). All 7 dependent Hermes profiles
|
||||||
|
# (bruce-banner, groot, happy, heimdall, rocket-raccoon, war-machine, wong)
|
||||||
|
# remain pointing at port 8002 throughout this run.
|
||||||
|
#
|
||||||
|
# Usage (from ~/git/homelab/ansible):
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day1_deploy_llm_router_shadow.yml
|
||||||
|
#
|
||||||
|
# Tag-scoped runs (if you need to re-run one phase):
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day1_deploy_llm_router_shadow.yml \
|
||||||
|
# --tags router_systemd,router_firewall,router_verify
|
||||||
|
#
|
||||||
|
# Execution path (Ryan-approved 2026-08-12, task t_0cca74a2):
|
||||||
|
# Direct ansible-playbook as documented exception — Semaphore template for
|
||||||
|
# this role does not exist yet. Create template after cutover is confirmed.
|
||||||
|
# This is the same exception pattern used in prior sessions on this box.
|
||||||
|
#
|
||||||
|
# Pre-requisites:
|
||||||
|
# 1. llama-server binary at /opt/llama.cpp/build/bin/llama-server supports
|
||||||
|
# router mode (confirmed 2026-08-12: --models-dir flag present in --help).
|
||||||
|
# 2. /opt/models/ contains ONLY Qwen3.6-35B-A3B-UD-Q4_K_S.gguf
|
||||||
|
# (confirmed 2026-08-12: directory is clean, Phi-4/Mistral already deleted).
|
||||||
|
# 3. Port 8002 is in use by the production llama-server-qwen service —
|
||||||
|
# this playbook does NOT touch it.
|
||||||
|
#
|
||||||
|
# Validation gates this playbook runs (all hard gates EXCEPT Gate 4):
|
||||||
|
# Gate 1: /v1/models reports Qwen with n_ctx >= 64000 (64K Hermes floor)
|
||||||
|
# Gate 2: Tool-calling probe through router returns finish_reason=tool_calls
|
||||||
|
# Gate 2b: Hallucination stress test does NOT trigger spurious tool_calls
|
||||||
|
# Gate 3: nvidia-smi VRAM <= 23,000 MiB (--models-max 1 confirmed effective)
|
||||||
|
# Gate 4: Bundled SvelteKit UI check (nice-to-have, non-blocking)
|
||||||
|
#
|
||||||
|
# What happens after this playbook:
|
||||||
|
# War Machine posts validation gate results to Ryan.
|
||||||
|
# Ryan reviews and signs off on cutover (or requests changes).
|
||||||
|
# War Machine then runs day2_cutover_qwen_to_router.yml (not yet created)
|
||||||
|
# to promote the router to port 8002 and retire the bare llama-server-qwen.
|
||||||
|
#
|
||||||
|
# Reference: proposal at
|
||||||
|
# ~/friday/system/inbox/agents/war-machine/2026-08-12-qwen-router-mode-proposal.md
|
||||||
|
# Task: t_0cca74a2
|
||||||
|
# Author: War Machine (2026-08-12)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Deploy llama-server router (shadow, port 8003) on astro-orbiter
|
||||||
|
hosts: astro_orbiter
|
||||||
|
gather_facts: true
|
||||||
|
become: true
|
||||||
|
|
||||||
|
vars:
|
||||||
|
# Enable the router phase — this is the ONLY var that makes router.yml run.
|
||||||
|
# Default in defaults/main.yml is false (no-op). Flip here for the shadow run.
|
||||||
|
llm_router_enabled: true
|
||||||
|
|
||||||
|
# Qwen model ID as it appears in /v1/models from the router.
|
||||||
|
# llama-server router uses the GGUF filename (without .gguf) as the model id.
|
||||||
|
llm_router_expected_model_id: "Qwen3.6-35B-A3B-UD-Q4_K_S"
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: llm-inference-multimodel
|
||||||
|
|
||||||
|
# No --tags needed here: router.yml is included dynamically from main.yml
|
||||||
|
# whenever llm_router_enabled: true. The full role runs but the
|
||||||
|
# discover/models/systemd/verify phases are gated on their own vars
|
||||||
|
# (llm_qwen_service_enabled etc.) and are idempotent. The stale
|
||||||
|
# models.yml (Phi-4/Mistral download tasks) uses variables no longer
|
||||||
|
# defined — a follow-up cleanup task should update that file.
|
||||||
|
|
||||||
|
- name: "POST-VALIDATION SAFETY NET — ensure production service is running"
|
||||||
|
hosts: astro_orbiter
|
||||||
|
gather_facts: false
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
# Always run this, regardless of whether the validation play succeeded.
|
||||||
|
# If the router.yml play stopped llama-server-qwen for VRAM validation
|
||||||
|
# and then a gate failed (play aborted), this play ensures it comes back up.
|
||||||
|
- name: "Ensure llama-server-qwen (port 8002) is running after validation (always)"
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: llama-server-qwen
|
||||||
|
state: started
|
||||||
|
enabled: true
|
||||||
|
ignore_errors: true # don't fail if the unit doesn't exist
|
||||||
|
|
||||||
|
- name: "Verify production /health after safety-net restart"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://10.1.71.130:8002/health"
|
||||||
|
status_code: 200
|
||||||
|
timeout: 30
|
||||||
|
register: llm_safety_net_health
|
||||||
|
failed_when: false
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: "Report production status (safety-net check)"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: >-
|
||||||
|
Safety-net: llama-server-qwen :8002 health check returned
|
||||||
|
{{ llm_safety_net_health.status | default('UNREACHABLE') }}.
|
||||||
|
{{ 'OK — production is up.' if (llm_safety_net_health.status | default(0) | int == 200)
|
||||||
|
else 'WARNING — production may not be healthy. Check manually.' }}
|
||||||
91
ansible/playbooks/day1_deploy_nextcloud.yml
Normal file
91
ansible/playbooks/day1_deploy_nextcloud.yml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/day1_deploy_nextcloud.yml
|
||||||
|
# DESCRIPTION: Deploys Nextcloud on the-grid
|
||||||
|
# Runs: docker-host → nextcloud
|
||||||
|
#
|
||||||
|
# USAGE:
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day1_deploy_nextcloud.yml
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day1_deploy_nextcloud.yml --limit the-grid
|
||||||
|
#
|
||||||
|
# SECRETS REQUIRED IN VAULT (group_vars/all/vault):
|
||||||
|
# vault_nextcloud_db_root_password
|
||||||
|
# vault_nextcloud_db_password
|
||||||
|
# vault_nextcloud_admin_user
|
||||||
|
# vault_nextcloud_admin_password
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Deploy Nextcloud on the-grid
|
||||||
|
hosts: the-grid
|
||||||
|
become: true
|
||||||
|
|
||||||
|
vars:
|
||||||
|
# NFS prerequisite
|
||||||
|
nfs_packages:
|
||||||
|
- nfs-common
|
||||||
|
|
||||||
|
# Compose stack location
|
||||||
|
nextcloud_base_dir: /opt/docker/nextcloud
|
||||||
|
|
||||||
|
# Secrets from vault
|
||||||
|
nextcloud_db_root_password: "{{ vault_nextcloud_db_root_password }}"
|
||||||
|
nextcloud_db_password: "{{ vault_nextcloud_db_password }}"
|
||||||
|
nextcloud_admin_user: "{{ vault_nextcloud_admin_user }}"
|
||||||
|
nextcloud_admin_password: "{{ vault_nextcloud_admin_password }}"
|
||||||
|
|
||||||
|
pre_tasks:
|
||||||
|
- name: Install NFS client
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: "{{ nfs_packages }}"
|
||||||
|
state: present
|
||||||
|
update_cache: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: docker-host
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Create Nextcloud directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ nextcloud_base_dir }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ ansible_user }}"
|
||||||
|
group: docker
|
||||||
|
mode: "0775"
|
||||||
|
|
||||||
|
- name: Deploy Compose file
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "{{ playbook_dir }}/../../boilerplates/nextcloud/compose.yml"
|
||||||
|
dest: "{{ nextcloud_base_dir }}/compose.yml"
|
||||||
|
owner: "{{ ansible_user }}"
|
||||||
|
group: docker
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Deploy .env from vault
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: |
|
||||||
|
# Managed by Ansible — do not edit manually
|
||||||
|
MYSQL_ROOT_PASSWORD={{ nextcloud_db_root_password }}
|
||||||
|
MYSQL_PASSWORD={{ nextcloud_db_password }}
|
||||||
|
NEXTCLOUD_ADMIN_USER={{ nextcloud_admin_user }}
|
||||||
|
NEXTCLOUD_ADMIN_PASSWORD={{ nextcloud_admin_password }}
|
||||||
|
dest: "{{ nextcloud_base_dir }}/.env"
|
||||||
|
owner: "{{ ansible_user }}"
|
||||||
|
group: docker
|
||||||
|
mode: "0600"
|
||||||
|
|
||||||
|
- name: Start Nextcloud stack
|
||||||
|
community.docker.docker_compose_v2:
|
||||||
|
project_src: "{{ nextcloud_base_dir }}"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Wait for Nextcloud to become ready
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://the-grid.local.mk-labs.cloud/status.php"
|
||||||
|
status_code: 200
|
||||||
|
return_content: true
|
||||||
|
register: nextcloud_status
|
||||||
|
until: >
|
||||||
|
nextcloud_status.status == 200 and
|
||||||
|
(nextcloud_status.content | from_json).installed == true
|
||||||
|
retries: 20
|
||||||
|
delay: 15
|
||||||
35
ansible/playbooks/day1_deploy_ollama.yml
Normal file
35
ansible/playbooks/day1_deploy_ollama.yml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: ansible/playbooks/day1_deploy_ollama.yml
|
||||||
|
# DESCRIPTION: Deploys Ollama with ROCm GPU acceleration on astro-orbiter.
|
||||||
|
# Assumes day0_baseline.yml has already run (common role complete).
|
||||||
|
# PCIe passthrough for the RX 5700 must be configured in Proxmox
|
||||||
|
# and the GPU must be visible to the VM before running this playbook.
|
||||||
|
#
|
||||||
|
# Pre-flight check:
|
||||||
|
# ssh wed@astro-orbiter 'lspci | grep -i amd'
|
||||||
|
# Should show the RX 5700 before proceeding.
|
||||||
|
#
|
||||||
|
# Usage (from ansible/ directory):
|
||||||
|
# ansible-playbook playbooks/day1_deploy_ollama.yml
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Deploy Ollama with ROCm on astro-orbiter
|
||||||
|
hosts: astro-orbiter
|
||||||
|
become: true
|
||||||
|
|
||||||
|
pre_tasks:
|
||||||
|
- name: Verify AMD GPU is visible to the VM
|
||||||
|
command: lspci
|
||||||
|
register: lspci_output
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Fail if no AMD GPU detected
|
||||||
|
fail:
|
||||||
|
msg: >
|
||||||
|
No AMD GPU detected via lspci. Verify PCIe passthrough is configured
|
||||||
|
in Proxmox and the RX 5700 is visible to the VM before proceeding.
|
||||||
|
when: "'AMD' not in lspci_output.stdout and 'Radeon' not in lspci_output.stdout"
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- ollama
|
||||||
17
ansible/playbooks/day1_deploy_semaphore.yml
Normal file
17
ansible/playbooks/day1_deploy_semaphore.yml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# day1_deploy_semaphore.yml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Deploys SemaphoreUI + PostgreSQL on the imagineering host (figment).
|
||||||
|
# Run AFTER day0_linux_baseline.yml has been applied to the target.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day1_deploy_semaphore.yml
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Deploy SemaphoreUI on imagineering
|
||||||
|
hosts: semaphore_server
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
roles:
|
||||||
|
- semaphore
|
||||||
18
ansible/playbooks/day1_deploy_traefik.yml
Normal file
18
ansible/playbooks/day1_deploy_traefik.yml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/deploy_traefik.yml
|
||||||
|
# DESCRIPTION: Deploys Traefik reverse proxy on lightning-lane.
|
||||||
|
# Installs Docker and configures Traefik with Cloudflare DNS-01
|
||||||
|
# certificate resolution for *.local.mk-labs.cloud
|
||||||
|
#
|
||||||
|
# USAGE:
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/deploy_traefik.yml
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Deploy Traefik reverse proxy
|
||||||
|
hosts: load_balancers
|
||||||
|
become: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- docker-host
|
||||||
|
- traefik
|
||||||
18
ansible/playbooks/day1_deploy_vllm.yml
Normal file
18
ansible/playbooks/day1_deploy_vllm.yml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/day1_deploy_vllm.yml
|
||||||
|
# Deploy vLLM to a target host via roles/deploy-vllm.
|
||||||
|
#
|
||||||
|
# Staging run (deploy + validate WITHOUT touching production traffic):
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day1_deploy_vllm.yml --limit astro-orbiter
|
||||||
|
#
|
||||||
|
# Cutover run (once staging is validated and Ryan/JARVIS approve flipping
|
||||||
|
# traffic — starts and enables the systemd unit(s), runs Phase 5 verification):
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day1_deploy_vllm.yml \
|
||||||
|
# --limit astro-orbiter --extra-vars "vllm_service_state=started"
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
- name: Deploy vLLM inference serving stack
|
||||||
|
hosts: astro-orbiter
|
||||||
|
become: false
|
||||||
|
gather_facts: true
|
||||||
|
roles:
|
||||||
|
- deploy-vllm
|
||||||
7
ansible/playbooks/day1_docker.yml
Normal file
7
ansible/playbooks/day1_docker.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
- name: Apply common role
|
||||||
|
hosts: "{{ target | default('all') }}"
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- common
|
||||||
|
- docker-host
|
||||||
259
ansible/playbooks/day2_add_coder_alias.yml
Normal file
259
ansible/playbooks/day2_add_coder_alias.yml
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/day2_add_coder_alias.yml
|
||||||
|
# DESCRIPTION: Add Qwen2.5-Coder-14B-Instruct-Q4_K_M to the llama-server-router
|
||||||
|
# on astro-orbiter (10.1.71.130:8002).
|
||||||
|
#
|
||||||
|
# Context (t_55c164f5, 2026-08-13):
|
||||||
|
# Ryan requested a Qwen2.5-Coder-14B-Instruct-Q4_K_M.gguf be added to the
|
||||||
|
# astro-orbiter router with:
|
||||||
|
# alias = "Qwen2.5-Coder-14B-Instruct-4bit"
|
||||||
|
# n_gpu_layers = 99
|
||||||
|
# ctx_size = 16384
|
||||||
|
# flash_attn = true
|
||||||
|
# Deployed GitOps-style via this role; no hand-editing of the live preset.
|
||||||
|
#
|
||||||
|
# What this playbook does:
|
||||||
|
# 1. Downloads Qwen2.5-Coder-14B-Instruct-Q4_K_M.gguf into /opt/models if
|
||||||
|
# not already present (idempotent: size-check guard, no re-pull on match).
|
||||||
|
# 2. Redeploys the preset INI (adding the [Qwen2.5-Coder-14B-Instruct-Q4_K_M]
|
||||||
|
# section with alias = Qwen2.5-Coder-14B-Instruct-4bit).
|
||||||
|
# 3. Restarts llama-server-router to pick up the new model entry.
|
||||||
|
# 4. Verifies /v1/models returns all 4 models including the new Coder entry.
|
||||||
|
#
|
||||||
|
# VRAM context note (t_55c164f5):
|
||||||
|
# Qwen2.5-Coder-14B Q4_K_M: ~8.4GB weights + ~0.6GB KV @ 16K ctx ≈ 9.0GB
|
||||||
|
# Qwen3.6-35B-A3B: ~21.5GB
|
||||||
|
# Full co-residency is impossible on 24GB. LRU eviction handles this:
|
||||||
|
# when Coder is requested, Qwen3.6-35B is evicted (and vice versa).
|
||||||
|
# Model-switching incurs ~30-60s cold-load latency — expected and acceptable.
|
||||||
|
# Phi (~4.3GB) or Llama (~5.6GB) can co-reside with Coder (total ~14GB).
|
||||||
|
#
|
||||||
|
# Usage (from ~/git/homelab/ansible):
|
||||||
|
# env -u ANSIBLE_VAULT_PASSWORD_FILE ansible-playbook -i inventory.yml \
|
||||||
|
# playbooks/day2_add_coder_alias.yml
|
||||||
|
#
|
||||||
|
# Semaphore note: Semaphore SSH key for jarvis user is not loaded in the
|
||||||
|
# container (known pitfall, homelab-llm-serving skill). Run via CLI with
|
||||||
|
# id_jarvis key; document as exception per Ryan's standing CLI fallback directive.
|
||||||
|
#
|
||||||
|
# Author: War Machine (2026-08-13, t_55c164f5)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: "Add Qwen2.5-Coder-14B-Instruct-4bit alias to astro-orbiter router"
|
||||||
|
hosts: astro_orbiter
|
||||||
|
gather_facts: false
|
||||||
|
become: true
|
||||||
|
|
||||||
|
vars:
|
||||||
|
# Activate preset mode
|
||||||
|
llm_router_preset_enabled: true
|
||||||
|
llm_router_preset_path: /opt/llama-server-router-preset.ini
|
||||||
|
|
||||||
|
# Production port (router is on 8002 since t_cd0d5388)
|
||||||
|
llm_router_port: 8002
|
||||||
|
|
||||||
|
# Per-model ctx-size settings (carried from t_ryan_per_model_ctx; Coder new)
|
||||||
|
llm_router_llama_ctx_size: 8192
|
||||||
|
llm_router_llama_flash_attn: "true"
|
||||||
|
llm_router_phi_ctx_size: 32768
|
||||||
|
llm_router_phi_flash_attn: "true"
|
||||||
|
llm_router_coder_ctx_size: 16384
|
||||||
|
llm_router_coder_flash_attn: "true"
|
||||||
|
|
||||||
|
# All other vars inherit from host_vars + defaults/main.yml.
|
||||||
|
# Explicitly set the ones needed by the unit/template tasks for clarity:
|
||||||
|
llm_router_enabled: true
|
||||||
|
llm_service_user: jarvis
|
||||||
|
llm_binary_path: /opt/llama.cpp/build/bin/llama-server
|
||||||
|
llm_models_dir: /opt/models
|
||||||
|
llm_bind_address: "10.1.71.130"
|
||||||
|
llm_allowed_source_cidr: "10.1.70.0/24"
|
||||||
|
llm_router_service_name: llama-server-router
|
||||||
|
llm_router_bind_address: "10.1.71.130"
|
||||||
|
llm_router_allowed_source_cidr: "10.1.70.0/24"
|
||||||
|
llm_router_models_dir: /opt/models
|
||||||
|
llm_router_models_max: 4 # from host_vars; bumped by t_33acbb2e
|
||||||
|
llm_router_ctx_size: 65536 # Qwen3.6-35B default; per-model overrides above
|
||||||
|
llm_router_parallel: 1
|
||||||
|
llm_router_gpu_layers: 99
|
||||||
|
llm_router_batch_size: 2048
|
||||||
|
llm_router_ubatch_size: 512
|
||||||
|
llm_router_cache_type_k: q4_0
|
||||||
|
llm_router_cache_type_v: q4_0
|
||||||
|
llm_router_flash_attn: "auto"
|
||||||
|
llm_router_expected_model_id: "Qwen3.6-35B-A3B-UD-Q4_K_S"
|
||||||
|
llm_router_vram_max_mib: 23000
|
||||||
|
|
||||||
|
# Coder model staging entry (used below)
|
||||||
|
coder_filename: "Qwen2.5-Coder-14B-Instruct-Q4_K_M.gguf"
|
||||||
|
coder_url: "https://huggingface.co/bartowski/Qwen2.5-Coder-14B-Instruct-GGUF/resolve/main/Qwen2.5-Coder-14B-Instruct-Q4_K_M.gguf"
|
||||||
|
coder_size_bytes: 8988111072
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- name: reload systemd
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
become: true
|
||||||
|
listen: "reload systemd"
|
||||||
|
|
||||||
|
- name: restart router
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: llama-server-router
|
||||||
|
state: restarted
|
||||||
|
become: true
|
||||||
|
listen: "restart router"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# PHASE 1: Download Coder GGUF if not present / size mismatch
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
- name: "[coder] Stat existing GGUF"
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ llm_models_dir }}/{{ coder_filename }}"
|
||||||
|
get_checksum: false
|
||||||
|
register: coder_stat
|
||||||
|
|
||||||
|
- name: "[coder] Download GGUF (skip if present and size matches)"
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "{{ coder_url }}"
|
||||||
|
dest: "{{ llm_models_dir }}/{{ coder_filename }}"
|
||||||
|
owner: "{{ llm_service_user }}"
|
||||||
|
group: "{{ llm_service_user }}"
|
||||||
|
mode: "0644"
|
||||||
|
timeout: 3600
|
||||||
|
when: >
|
||||||
|
not coder_stat.stat.exists or
|
||||||
|
coder_stat.stat.size != coder_size_bytes
|
||||||
|
register: coder_download
|
||||||
|
notify: restart router
|
||||||
|
|
||||||
|
- name: "[coder] Confirm GGUF size post-download"
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ llm_models_dir }}/{{ coder_filename }}"
|
||||||
|
get_checksum: false
|
||||||
|
register: coder_stat_post
|
||||||
|
|
||||||
|
- name: "[coder] FAIL if GGUF size mismatch after download"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
GGUF size mismatch: expected {{ coder_size_bytes }} bytes,
|
||||||
|
got {{ coder_stat_post.stat.size }} bytes.
|
||||||
|
Re-download may be needed.
|
||||||
|
when: coder_stat_post.stat.size != coder_size_bytes
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# PHASE 2: Deploy updated preset INI (adds Coder section)
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
- name: "[coder] Deploy preset INI to {{ llm_router_preset_path }}"
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "../roles/llm-inference-multimodel/templates/llama-server-router-preset.ini.j2"
|
||||||
|
dest: "{{ llm_router_preset_path }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
register: coder_preset_deployed
|
||||||
|
notify: restart router
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# PHASE 3: Redeploy systemd unit (unchanged flags, but ensures unit is fresh)
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
- name: "[coder] Deploy llama-server-router unit"
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "../roles/llm-inference-multimodel/templates/llama-server-router.service.j2"
|
||||||
|
dest: /etc/systemd/system/llama-server-router.service
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
register: coder_unit_deployed
|
||||||
|
notify:
|
||||||
|
- reload systemd
|
||||||
|
- restart router
|
||||||
|
|
||||||
|
- name: "[coder] Flush handlers (daemon-reload + router restart)"
|
||||||
|
ansible.builtin.meta: flush_handlers
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# PHASE 4: Verify router is up and Coder model appears in /v1/models
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
- name: "[coder] Wait for /health (router supervisor)"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/health"
|
||||||
|
status_code: 200
|
||||||
|
timeout: 30
|
||||||
|
retries: 12
|
||||||
|
delay: 5
|
||||||
|
register: coder_health
|
||||||
|
until: coder_health.status == 200
|
||||||
|
|
||||||
|
- name: "[coder] Query /v1/models"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/models"
|
||||||
|
status_code: 200
|
||||||
|
return_content: true
|
||||||
|
timeout: 30
|
||||||
|
register: coder_models
|
||||||
|
|
||||||
|
- name: "[coder] Extract model IDs and aliases"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
coder_model_ids: "{{ coder_models.json.data | map(attribute='id') | list }}"
|
||||||
|
coder_all_aliases: "{{ coder_models.json.data | map(attribute='aliases') | flatten | list }}"
|
||||||
|
|
||||||
|
- name: "[coder] FAIL if Coder primary ID missing"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
'Qwen2.5-Coder-14B-Instruct-Q4_K_M' not in /v1/models.
|
||||||
|
IDs: {{ coder_model_ids }}
|
||||||
|
when: "'Qwen2.5-Coder-14B-Instruct-Q4_K_M' not in coder_model_ids"
|
||||||
|
|
||||||
|
- name: "[coder] FAIL if Coder alias missing"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
'Qwen2.5-Coder-14B-Instruct-4bit' not found as ID or alias in /v1/models.
|
||||||
|
IDs: {{ coder_model_ids }}
|
||||||
|
Aliases: {{ coder_all_aliases }}
|
||||||
|
when:
|
||||||
|
- "'Qwen2.5-Coder-14B-Instruct-4bit' not in coder_model_ids"
|
||||||
|
- "'Qwen2.5-Coder-14B-Instruct-4bit' not in coder_all_aliases"
|
||||||
|
|
||||||
|
- name: "[coder] FAIL if Qwen3.6-35B missing"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: "'Qwen3.6-35B-A3B-UD-Q4_K_S' not in /v1/models. IDs: {{ coder_model_ids }}"
|
||||||
|
when: "'Qwen3.6-35B-A3B-UD-Q4_K_S' not in coder_model_ids"
|
||||||
|
|
||||||
|
- name: "[coder] FAIL if Phi missing"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: "'Phi-3.5-mini-instruct-Q8_0' not in /v1/models. IDs: {{ coder_model_ids }}"
|
||||||
|
when: "'Phi-3.5-mini-instruct-Q8_0' not in coder_model_ids"
|
||||||
|
|
||||||
|
- name: "[coder] FAIL if Llama missing"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: "'Meta-Llama-3.1-8B-Instruct-Q4_K_M' not in /v1/models. IDs: {{ coder_model_ids }}"
|
||||||
|
when: "'Meta-Llama-3.1-8B-Instruct-Q4_K_M' not in coder_model_ids"
|
||||||
|
|
||||||
|
- name: "[coder] PASS — full /v1/models summary"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "========================================================================"
|
||||||
|
- "QWEN2.5-CODER-14B ALIAS DEPLOYMENT — COMPLETE"
|
||||||
|
- ""
|
||||||
|
- " Mode: --models-preset ({{ llm_router_preset_path }})"
|
||||||
|
- " Service: llama-server-router.service (:{{ llm_router_port }})"
|
||||||
|
- ""
|
||||||
|
- " /v1/models IDs: {{ coder_model_ids }}"
|
||||||
|
- " /v1/models aliases: {{ coder_all_aliases }}"
|
||||||
|
- ""
|
||||||
|
- " VERIFY:"
|
||||||
|
- " Qwen3.6-35B-A3B-UD-Q4_K_S: {{ 'PRESENT' if 'Qwen3.6-35B-A3B-UD-Q4_K_S' in coder_model_ids else 'MISSING' }}"
|
||||||
|
- " Phi-3.5-mini-instruct-Q8_0: {{ 'PRESENT' if 'Phi-3.5-mini-instruct-Q8_0' in coder_model_ids else 'MISSING' }}"
|
||||||
|
- " Meta-Llama-3.1-8B-Instruct-Q4_K_M: {{ 'PRESENT' if 'Meta-Llama-3.1-8B-Instruct-Q4_K_M' in coder_model_ids else 'MISSING' }}"
|
||||||
|
- " Qwen2.5-Coder-14B-Instruct-Q4_K_M: {{ 'PRESENT' if 'Qwen2.5-Coder-14B-Instruct-Q4_K_M' in coder_model_ids else 'MISSING' }}"
|
||||||
|
- " Qwen2.5-Coder-14B-Instruct-4bit: {{ 'PRESENT (ID)' if 'Qwen2.5-Coder-14B-Instruct-4bit' in coder_model_ids else ('PRESENT (alias)' if 'Qwen2.5-Coder-14B-Instruct-4bit' in coder_all_aliases else 'MISSING') }}"
|
||||||
|
- ""
|
||||||
|
- " GGUF download: {{ 'NEW DOWNLOAD' if (coder_download is defined and coder_download.changed) else 'ALREADY PRESENT (skipped)' }}"
|
||||||
|
- "========================================================================"
|
||||||
313
ansible/playbooks/day2_add_nomic_embed.yml
Normal file
313
ansible/playbooks/day2_add_nomic_embed.yml
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/day2_add_nomic_embed.yml
|
||||||
|
# DESCRIPTION: Add nomic-embed-text-v1.5-Q4_K_M to the llama-server-router
|
||||||
|
# on astro-orbiter (10.1.71.130:8002).
|
||||||
|
#
|
||||||
|
# Context (t_34b96e83, 2026-08-13, OpenViking Phase 1b):
|
||||||
|
# Ryan approved adding nomic-embed-text-v1.5-Q4_K_M as an embedding model
|
||||||
|
# after Phase 0 follow-up confirmed embedding models fold cleanly into the
|
||||||
|
# existing router preset via embedding=true. Model ID is "nomic-embed-text-v1.5".
|
||||||
|
# No alias needed — peter-parker and Honcho consumers will call it by the section
|
||||||
|
# name directly.
|
||||||
|
#
|
||||||
|
# What this playbook does:
|
||||||
|
# 1. Downloads nomic-embed-text-v1.5-Q4_K_M.gguf into /opt/models if not
|
||||||
|
# already present (idempotent: exact size-check guard, no re-pull on match).
|
||||||
|
# 2. Redeploys the preset INI (adding the [nomic-embed-text-v1.5] section with
|
||||||
|
# embedding=true, n-gpu-layers=99, ctx-size=8192, load-on-startup=true,
|
||||||
|
# sleep-idle-seconds=-1).
|
||||||
|
# 3. Restarts llama-server-router to pick up the new model entry.
|
||||||
|
# 4. Verifies /v1/models returns all 5 models including the new nomic entry.
|
||||||
|
# 5. Runs a /v1/embeddings smoke test to confirm the model actually embeds.
|
||||||
|
#
|
||||||
|
# VRAM context note (t_34b96e83):
|
||||||
|
# nomic-embed-text-v1.5 Q4_K_M: ~84MB weights, embedding model (no KV cache).
|
||||||
|
# VRAM impact is negligible — always pinned via sleep-idle-seconds=-1.
|
||||||
|
# The 4 generative models remain unchanged (OOM analysis unchanged from t_55c164f5).
|
||||||
|
#
|
||||||
|
# Usage (from ~/git/homelab/ansible):
|
||||||
|
# env -u ANSIBLE_VAULT_PASSWORD_FILE ansible-playbook -i inventory.yml \
|
||||||
|
# playbooks/day2_add_nomic_embed.yml
|
||||||
|
#
|
||||||
|
# Semaphore note: Semaphore SSH key for jarvis user is not loaded in the
|
||||||
|
# container (known pitfall, homelab-llm-serving skill). Run via CLI with
|
||||||
|
# id_jarvis key; document as exception per Ryan's standing CLI fallback directive.
|
||||||
|
#
|
||||||
|
# Author: War Machine (2026-08-13, t_34b96e83)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: "Add nomic-embed-text-v1.5 embedding model to astro-orbiter router"
|
||||||
|
hosts: astro_orbiter
|
||||||
|
gather_facts: false
|
||||||
|
become: true
|
||||||
|
|
||||||
|
vars:
|
||||||
|
# Activate preset mode
|
||||||
|
llm_router_preset_enabled: true
|
||||||
|
llm_router_preset_path: /opt/llama-server-router-preset.ini
|
||||||
|
|
||||||
|
# Production port (router is on 8002 since t_cd0d5388)
|
||||||
|
llm_router_port: 8002
|
||||||
|
|
||||||
|
# Per-model ctx-size settings (carried from t_55c164f5; nomic new)
|
||||||
|
llm_router_llama_ctx_size: 8192
|
||||||
|
llm_router_llama_flash_attn: "true"
|
||||||
|
llm_router_phi_ctx_size: 32768
|
||||||
|
llm_router_phi_flash_attn: "true"
|
||||||
|
llm_router_coder_ctx_size: 16384
|
||||||
|
llm_router_coder_flash_attn: "true"
|
||||||
|
llm_router_nomic_ctx_size: 8192
|
||||||
|
# NOTE (2026-08-14, t_openviking_embed_batch): per-model batch-size/
|
||||||
|
# ubatch-size lines in the preset INI are NOT honored by llama-server's
|
||||||
|
# router — only ctx-size is applied per-model; batch-size/ubatch-size for
|
||||||
|
# every spawned child come from the router's own global CLI flags
|
||||||
|
# (confirmed via `ps aux` on astro-orbiter: child process launched with
|
||||||
|
# the router's --batch-size/--ubatch-size regardless of the INI values).
|
||||||
|
# Kept below for documentation/future-proofing but the REAL fix is the
|
||||||
|
# global llm_router_batch_size / llm_router_ubatch_size override further
|
||||||
|
# down, which raises the physical batch for ALL models on this router
|
||||||
|
# (Qwen3.6-35B, Phi, Llama, Coder, nomic).
|
||||||
|
llm_router_nomic_batch_size: 4096
|
||||||
|
llm_router_nomic_ubatch_size: 4096
|
||||||
|
|
||||||
|
# All other vars inherit from host_vars + defaults/main.yml.
|
||||||
|
llm_router_enabled: true
|
||||||
|
llm_service_user: jarvis
|
||||||
|
llm_binary_path: /opt/llama.cpp/build/bin/llama-server
|
||||||
|
llm_models_dir: /opt/models
|
||||||
|
llm_bind_address: "10.1.71.130"
|
||||||
|
llm_allowed_source_cidr: "10.1.70.0/24"
|
||||||
|
llm_router_service_name: llama-server-router
|
||||||
|
llm_router_bind_address: "10.1.71.130"
|
||||||
|
llm_router_allowed_source_cidr: "10.1.70.0/24"
|
||||||
|
llm_router_models_dir: /opt/models
|
||||||
|
llm_router_models_max: 4 # from host_vars; bumped by t_33acbb2e
|
||||||
|
llm_router_ctx_size: 65536 # Qwen3.6-35B default; per-model overrides above
|
||||||
|
llm_router_parallel: 1
|
||||||
|
llm_router_gpu_layers: 99
|
||||||
|
# FIX (2026-08-14, t_openviking_embed_batch): raised from 512 to 4096.
|
||||||
|
# This is a GLOBAL router flag applied to every spawned model process
|
||||||
|
# (per-model INI batch-size/ubatch-size overrides are not honored by
|
||||||
|
# llama-server's router — see note above nomic vars). 512 tokens was too
|
||||||
|
# small for OpenViking's chunked-document embedding inputs (observed
|
||||||
|
# 2000-3400 tokens/chunk), causing hard 500 errors ("input (N tokens) is
|
||||||
|
# too large to process") that tripped OpenViking's circuit breaker into a
|
||||||
|
# permanent fail/re-enqueue loop. 4096 comfortably covers observed chunk
|
||||||
|
# sizes and stays under nomic's ctx-size=8192. VRAM impact of raising
|
||||||
|
# ubatch-size is in compute-buffer scratch space, not KV cache; monitored
|
||||||
|
# post-deploy against the 23000 MiB budget (host_vars/astro-orbiter).
|
||||||
|
llm_router_batch_size: 4096
|
||||||
|
llm_router_ubatch_size: 4096
|
||||||
|
llm_router_cache_type_k: q4_0
|
||||||
|
llm_router_cache_type_v: q4_0
|
||||||
|
llm_router_flash_attn: "auto"
|
||||||
|
llm_router_expected_model_id: "Qwen3.6-35B-A3B-UD-Q4_K_S"
|
||||||
|
llm_router_vram_max_mib: 23000
|
||||||
|
|
||||||
|
# nomic model staging
|
||||||
|
nomic_filename: "nomic-embed-text-v1.5-Q4_K_M.gguf"
|
||||||
|
nomic_url: "https://huggingface.co/nomic-ai/nomic-embed-text-v1.5-GGUF/resolve/main/nomic-embed-text-v1.5.Q4_K_M.gguf"
|
||||||
|
nomic_size_bytes: 84106624
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- name: reload systemd
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
become: true
|
||||||
|
listen: "reload systemd"
|
||||||
|
|
||||||
|
- name: restart router
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: llama-server-router
|
||||||
|
state: restarted
|
||||||
|
become: true
|
||||||
|
listen: "restart router"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# PHASE 1: Download nomic GGUF if not present / size mismatch
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
- name: "[nomic] Stat existing GGUF"
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ llm_models_dir }}/{{ nomic_filename }}"
|
||||||
|
get_checksum: false
|
||||||
|
register: nomic_stat
|
||||||
|
|
||||||
|
- name: "[nomic] Download GGUF (skip if present and size matches)"
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "{{ nomic_url }}"
|
||||||
|
dest: "{{ llm_models_dir }}/{{ nomic_filename }}"
|
||||||
|
owner: "{{ llm_service_user }}"
|
||||||
|
group: "{{ llm_service_user }}"
|
||||||
|
mode: "0644"
|
||||||
|
timeout: 300
|
||||||
|
when: >
|
||||||
|
not nomic_stat.stat.exists or
|
||||||
|
nomic_stat.stat.size != nomic_size_bytes
|
||||||
|
register: nomic_download
|
||||||
|
notify: restart router
|
||||||
|
|
||||||
|
- name: "[nomic] Confirm GGUF size post-download"
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ llm_models_dir }}/{{ nomic_filename }}"
|
||||||
|
get_checksum: false
|
||||||
|
register: nomic_stat_post
|
||||||
|
|
||||||
|
- name: "[nomic] FAIL if GGUF size mismatch after download"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
GGUF size mismatch: expected {{ nomic_size_bytes }} bytes,
|
||||||
|
got {{ nomic_stat_post.stat.size }} bytes.
|
||||||
|
Re-download may be needed.
|
||||||
|
when: nomic_stat_post.stat.size != nomic_size_bytes
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# PHASE 2: Deploy updated preset INI (adds nomic-embed-text-v1.5 section)
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
- name: "[nomic] Deploy preset INI to {{ llm_router_preset_path }}"
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "../roles/llm-inference-multimodel/templates/llama-server-router-preset.ini.j2"
|
||||||
|
dest: "{{ llm_router_preset_path }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
register: nomic_preset_deployed
|
||||||
|
notify: restart router
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# PHASE 3: Redeploy systemd unit (ensures unit is fresh; no flag changes)
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
- name: "[nomic] Deploy llama-server-router unit"
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "../roles/llm-inference-multimodel/templates/llama-server-router.service.j2"
|
||||||
|
dest: /etc/systemd/system/llama-server-router.service
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
register: nomic_unit_deployed
|
||||||
|
notify:
|
||||||
|
- reload systemd
|
||||||
|
- restart router
|
||||||
|
|
||||||
|
- name: "[nomic] Flush handlers (daemon-reload + router restart)"
|
||||||
|
ansible.builtin.meta: flush_handlers
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# PHASE 4: Verify router is up and nomic model appears in /v1/models
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
- name: "[nomic] Wait for /health (router supervisor)"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/health"
|
||||||
|
status_code: 200
|
||||||
|
timeout: 30
|
||||||
|
retries: 12
|
||||||
|
delay: 5
|
||||||
|
register: nomic_health
|
||||||
|
until: nomic_health.status == 200
|
||||||
|
|
||||||
|
- name: "[nomic] Query /v1/models"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/models"
|
||||||
|
status_code: 200
|
||||||
|
return_content: true
|
||||||
|
timeout: 30
|
||||||
|
register: nomic_models
|
||||||
|
|
||||||
|
- name: "[nomic] Extract model IDs and aliases"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
nomic_model_ids: "{{ nomic_models.json.data | map(attribute='id') | list }}"
|
||||||
|
nomic_all_aliases: "{{ nomic_models.json.data | map(attribute='aliases') | flatten | list }}"
|
||||||
|
|
||||||
|
- name: "[nomic] FAIL if nomic primary ID missing"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
'nomic-embed-text-v1.5' not in /v1/models.
|
||||||
|
IDs: {{ nomic_model_ids }}
|
||||||
|
when: "'nomic-embed-text-v1.5' not in nomic_model_ids"
|
||||||
|
|
||||||
|
- name: "[nomic] FAIL if Qwen3.6-35B missing"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: "'Qwen3.6-35B-A3B-UD-Q4_K_S' not in /v1/models. IDs: {{ nomic_model_ids }}"
|
||||||
|
when: "'Qwen3.6-35B-A3B-UD-Q4_K_S' not in nomic_model_ids"
|
||||||
|
|
||||||
|
- name: "[nomic] FAIL if Phi missing"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: "'Phi-3.5-mini-instruct-Q8_0' not in /v1/models. IDs: {{ nomic_model_ids }}"
|
||||||
|
when: "'Phi-3.5-mini-instruct-Q8_0' not in nomic_model_ids"
|
||||||
|
|
||||||
|
- name: "[nomic] FAIL if Llama missing"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: "'Meta-Llama-3.1-8B-Instruct-Q4_K_M' not in /v1/models. IDs: {{ nomic_model_ids }}"
|
||||||
|
when: "'Meta-Llama-3.1-8B-Instruct-Q4_K_M' not in nomic_model_ids"
|
||||||
|
|
||||||
|
- name: "[nomic] FAIL if Coder missing"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: "'Qwen2.5-Coder-14B-Instruct-Q4_K_M' not in /v1/models. IDs: {{ nomic_model_ids }}"
|
||||||
|
when: "'Qwen2.5-Coder-14B-Instruct-Q4_K_M' not in nomic_model_ids"
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# PHASE 5: /v1/embeddings smoke test — confirm model actually embeds
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
- name: "[nomic] POST /v1/embeddings smoke test"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/embeddings"
|
||||||
|
method: POST
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
model: "nomic-embed-text-v1.5"
|
||||||
|
input: "The dog ran across the park."
|
||||||
|
status_code: 200
|
||||||
|
return_content: true
|
||||||
|
timeout: 120
|
||||||
|
register: nomic_embed_result
|
||||||
|
|
||||||
|
- name: "[nomic] Extract embedding vector length"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
nomic_embed_dims: >-
|
||||||
|
{{ (nomic_embed_result.json.data | first).embedding | length }}
|
||||||
|
when:
|
||||||
|
- nomic_embed_result.status == 200
|
||||||
|
- nomic_embed_result.json.data is defined
|
||||||
|
- nomic_embed_result.json.data | length > 0
|
||||||
|
|
||||||
|
- name: "[nomic] FAIL if embedding vector is empty or missing"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
Embedding smoke test returned no vector.
|
||||||
|
Response: {{ nomic_embed_result.json }}
|
||||||
|
when: >-
|
||||||
|
nomic_embed_result.status != 200 or
|
||||||
|
nomic_embed_result.json.data is not defined or
|
||||||
|
nomic_embed_result.json.data | length == 0 or
|
||||||
|
(nomic_embed_result.json.data | first).embedding | length == 0
|
||||||
|
|
||||||
|
- name: "[nomic] PASS — full summary"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "========================================================================"
|
||||||
|
- "NOMIC-EMBED-TEXT-V1.5 DEPLOYMENT — COMPLETE"
|
||||||
|
- ""
|
||||||
|
- " Mode: --models-preset ({{ llm_router_preset_path }})"
|
||||||
|
- " Service: llama-server-router.service (:{{ llm_router_port }})"
|
||||||
|
- ""
|
||||||
|
- " /v1/models IDs: {{ nomic_model_ids }}"
|
||||||
|
- ""
|
||||||
|
- " VERIFY:"
|
||||||
|
- " Qwen3.6-35B-A3B-UD-Q4_K_S: {{ 'PRESENT' if 'Qwen3.6-35B-A3B-UD-Q4_K_S' in nomic_model_ids else 'MISSING' }}"
|
||||||
|
- " Phi-3.5-mini-instruct-Q8_0: {{ 'PRESENT' if 'Phi-3.5-mini-instruct-Q8_0' in nomic_model_ids else 'MISSING' }}"
|
||||||
|
- " Meta-Llama-3.1-8B-Instruct-Q4_K_M: {{ 'PRESENT' if 'Meta-Llama-3.1-8B-Instruct-Q4_K_M' in nomic_model_ids else 'MISSING' }}"
|
||||||
|
- " Qwen2.5-Coder-14B-Instruct-Q4_K_M: {{ 'PRESENT' if 'Qwen2.5-Coder-14B-Instruct-Q4_K_M' in nomic_model_ids else 'MISSING' }}"
|
||||||
|
- " nomic-embed-text-v1.5: {{ 'PRESENT' if 'nomic-embed-text-v1.5' in nomic_model_ids else 'MISSING' }}"
|
||||||
|
- ""
|
||||||
|
- " Embedding smoke test: PASS"
|
||||||
|
- " Vector dimensions: {{ nomic_embed_dims | default('unknown') }}"
|
||||||
|
- ""
|
||||||
|
- " GGUF download: {{ 'NEW DOWNLOAD' if (nomic_download is defined and nomic_download.changed) else 'ALREADY PRESENT (skipped)' }}"
|
||||||
|
- "========================================================================"
|
||||||
203
ansible/playbooks/day2_add_phi_alias.yml
Normal file
203
ansible/playbooks/day2_add_phi_alias.yml
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/day2_add_phi_alias.yml
|
||||||
|
# DESCRIPTION: Add Phi-3.5-mini-instruct-8bit alias to the llama-server-router
|
||||||
|
# by switching from --models-dir to --models-preset INI mode.
|
||||||
|
#
|
||||||
|
# Context (t_9adf0889, 2026-08-12):
|
||||||
|
# Ryan's Hermes config (auxiliary.title_generation.model) points to
|
||||||
|
# "Phi-3.5-mini-instruct-8bit" but the router only exposes the GGUF
|
||||||
|
# filename-derived ID "Phi-3.5-mini-instruct-Q8_0". They are the same file.
|
||||||
|
# This playbook adds the alias so both names work without changing Ryan's
|
||||||
|
# Hermes config.
|
||||||
|
#
|
||||||
|
# What this playbook does:
|
||||||
|
# 1. Deploys the preset INI template (llama-server-router-preset.ini.j2)
|
||||||
|
# to /opt/llama-server-router-preset.ini on astro-orbiter.
|
||||||
|
# 2. Redeploys the systemd unit (llama-server-router.service) with
|
||||||
|
# --models-preset instead of --models-dir.
|
||||||
|
# 3. Restarts llama-server-router to pick up the new flag.
|
||||||
|
# 4. Verifies that /v1/models returns:
|
||||||
|
# - Phi-3.5-mini-instruct-Q8_0 (original ID — must still work)
|
||||||
|
# - Phi-3.5-mini-instruct-8bit (new alias — Ryan's config target)
|
||||||
|
# - Qwen3.6-35B-A3B-UD-Q4_K_S (unchanged)
|
||||||
|
# - Meta-Llama-3.1-8B-Instruct-Q4_K_M (unchanged)
|
||||||
|
#
|
||||||
|
# Known upstream behavior:
|
||||||
|
# GH #22364: --models-preset creates an extra "default" entry in /v1/models.
|
||||||
|
# This is cosmetic only and does not affect model selection by name.
|
||||||
|
#
|
||||||
|
# Usage (from ~/git/homelab/ansible):
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day2_add_phi_alias.yml
|
||||||
|
#
|
||||||
|
# Semaphore note (t_9adf0889): Semaphore SSH key for jarvis user is not loaded
|
||||||
|
# in the container (known pitfall, homelab-llm-serving skill). Run via CLI with
|
||||||
|
# id_jarvis key; document as exception per Ryan's standing CLI fallback directive.
|
||||||
|
#
|
||||||
|
# Author: War Machine (2026-08-12, t_9adf0889)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: "Add Phi-3.5-mini-instruct-8bit alias — switch router to preset mode"
|
||||||
|
hosts: astro_orbiter
|
||||||
|
gather_facts: false
|
||||||
|
become: true
|
||||||
|
|
||||||
|
vars:
|
||||||
|
# Activate preset mode and provide the on-disk INI path
|
||||||
|
llm_router_preset_enabled: true
|
||||||
|
llm_router_preset_path: /opt/llama-server-router-preset.ini
|
||||||
|
|
||||||
|
# Production port (router is already on 8002 since t_cd0d5388)
|
||||||
|
llm_router_port: 8002
|
||||||
|
|
||||||
|
# All other vars inherit from host_vars + defaults/main.yml.
|
||||||
|
# Explicitly set the ones needed by the unit template for clarity:
|
||||||
|
llm_router_enabled: true
|
||||||
|
llm_service_user: jarvis
|
||||||
|
llm_binary_path: /opt/llama.cpp/build/bin/llama-server
|
||||||
|
llm_models_dir: /opt/models
|
||||||
|
llm_bind_address: "10.1.71.130"
|
||||||
|
llm_allowed_source_cidr: "10.1.70.0/24"
|
||||||
|
llm_router_service_name: llama-server-router
|
||||||
|
llm_router_bind_address: "10.1.71.130"
|
||||||
|
llm_router_allowed_source_cidr: "10.1.70.0/24"
|
||||||
|
llm_router_models_dir: /opt/models
|
||||||
|
llm_router_models_max: 4 # from host_vars; bumped by t_33acbb2e
|
||||||
|
llm_router_ctx_size: 65536
|
||||||
|
llm_router_parallel: 1
|
||||||
|
llm_router_gpu_layers: 99
|
||||||
|
llm_router_batch_size: 2048
|
||||||
|
llm_router_ubatch_size: 512
|
||||||
|
llm_router_cache_type_k: q4_0
|
||||||
|
llm_router_cache_type_v: q4_0
|
||||||
|
llm_router_flash_attn: "auto"
|
||||||
|
llm_router_expected_model_id: "Qwen3.6-35B-A3B-UD-Q4_K_S"
|
||||||
|
llm_router_vram_max_mib: 23000
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- name: reload systemd
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
become: true
|
||||||
|
listen: "reload systemd"
|
||||||
|
|
||||||
|
- name: restart router
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: llama-server-router
|
||||||
|
state: restarted
|
||||||
|
become: true
|
||||||
|
listen: "restart router"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# PHASE 1: Deploy the preset INI
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
- name: "[phi-alias] Deploy preset INI to {{ llm_router_preset_path }}"
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "../roles/llm-inference-multimodel/templates/llama-server-router-preset.ini.j2"
|
||||||
|
dest: "{{ llm_router_preset_path }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
register: phi_alias_preset_deployed
|
||||||
|
notify:
|
||||||
|
- restart router
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# PHASE 2: Redeploy systemd unit with --models-preset flag
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
- name: "[phi-alias] Deploy llama-server-router unit (--models-preset mode)"
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "../roles/llm-inference-multimodel/templates/llama-server-router.service.j2"
|
||||||
|
dest: /etc/systemd/system/llama-server-router.service
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
register: phi_alias_unit_deployed
|
||||||
|
notify:
|
||||||
|
- reload systemd
|
||||||
|
- restart router
|
||||||
|
|
||||||
|
- name: "[phi-alias] Flush handlers (daemon-reload + router restart)"
|
||||||
|
ansible.builtin.meta: flush_handlers
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# PHASE 3: Verify alias is present
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
- name: "[phi-alias] Wait for /health (router supervisor, no model needed)"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/health"
|
||||||
|
status_code: 200
|
||||||
|
timeout: 30
|
||||||
|
retries: 12
|
||||||
|
delay: 5
|
||||||
|
register: phi_alias_health
|
||||||
|
until: phi_alias_health.status == 200
|
||||||
|
|
||||||
|
- name: "[phi-alias] Query /v1/models"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/models"
|
||||||
|
status_code: 200
|
||||||
|
return_content: true
|
||||||
|
timeout: 30
|
||||||
|
register: phi_alias_models
|
||||||
|
|
||||||
|
- name: "[phi-alias] Extract model IDs and aliases"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
phi_alias_model_ids: "{{ phi_alias_models.json.data | map(attribute='id') | list }}"
|
||||||
|
phi_alias_all_aliases: "{{ phi_alias_models.json.data | map(attribute='aliases') | flatten | list }}"
|
||||||
|
phi_alias_model_sources: "{{ phi_alias_models.json.data | map(attribute='source') | list }}"
|
||||||
|
|
||||||
|
- name: "[phi-alias] FAIL if Phi original ID missing"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
'Phi-3.5-mini-instruct-Q8_0' not in /v1/models.
|
||||||
|
IDs: {{ phi_alias_model_ids }}
|
||||||
|
when: "'Phi-3.5-mini-instruct-Q8_0' not in phi_alias_model_ids"
|
||||||
|
|
||||||
|
- name: "[phi-alias] FAIL if Phi alias missing"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
'Phi-3.5-mini-instruct-8bit' not found as ID or alias in /v1/models.
|
||||||
|
IDs: {{ phi_alias_model_ids }}
|
||||||
|
Aliases: {{ phi_alias_all_aliases }}
|
||||||
|
when:
|
||||||
|
- "'Phi-3.5-mini-instruct-8bit' not in phi_alias_model_ids"
|
||||||
|
- "'Phi-3.5-mini-instruct-8bit' not in phi_alias_all_aliases"
|
||||||
|
|
||||||
|
- name: "[phi-alias] FAIL if Qwen missing"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: "'Qwen3.6-35B-A3B-UD-Q4_K_S' not in /v1/models. IDs: {{ phi_alias_model_ids }}"
|
||||||
|
when: "'Qwen3.6-35B-A3B-UD-Q4_K_S' not in phi_alias_model_ids"
|
||||||
|
|
||||||
|
- name: "[phi-alias] FAIL if Llama missing"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: "'Meta-Llama-3.1-8B-Instruct-Q4_K_M' not in /v1/models. IDs: {{ phi_alias_model_ids }}"
|
||||||
|
when: "'Meta-Llama-3.1-8B-Instruct-Q4_K_M' not in phi_alias_model_ids"
|
||||||
|
|
||||||
|
- name: "[phi-alias] PASS — full /v1/models summary"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "========================================================================"
|
||||||
|
- "PHI ALIAS DEPLOYMENT — COMPLETE"
|
||||||
|
- ""
|
||||||
|
- " Mode: --models-preset ({{ llm_router_preset_path }})"
|
||||||
|
- " Service: llama-server-router.service (:{{ llm_router_port }})"
|
||||||
|
- ""
|
||||||
|
- " /v1/models IDs: {{ phi_alias_model_ids }}"
|
||||||
|
- " /v1/models aliases: {{ phi_alias_all_aliases }}"
|
||||||
|
- " Sources: {{ phi_alias_model_sources }}"
|
||||||
|
- ""
|
||||||
|
- " VERIFY:"
|
||||||
|
- " Phi-3.5-mini-instruct-Q8_0: {{ 'PRESENT' if 'Phi-3.5-mini-instruct-Q8_0' in phi_alias_model_ids else 'MISSING' }}"
|
||||||
|
- " Phi-3.5-mini-instruct-8bit: {{ 'PRESENT (ID)' if 'Phi-3.5-mini-instruct-8bit' in phi_alias_model_ids else ('PRESENT (alias)' if 'Phi-3.5-mini-instruct-8bit' in phi_alias_all_aliases else 'MISSING') }}"
|
||||||
|
- " Qwen3.6-35B-A3B-UD-Q4_K_S: {{ 'PRESENT' if 'Qwen3.6-35B-A3B-UD-Q4_K_S' in phi_alias_model_ids else 'MISSING' }}"
|
||||||
|
- " Meta-Llama-3.1-8B-Instruct-Q4_K_M: {{ 'PRESENT' if 'Meta-Llama-3.1-8B-Instruct-Q4_K_M' in phi_alias_model_ids else 'MISSING' }}"
|
||||||
|
- ""
|
||||||
|
- " GH #22364: if 'default' appears in IDs above, that is expected"
|
||||||
|
- " in --models-preset mode. Cosmetic only."
|
||||||
|
- "========================================================================"
|
||||||
161
ansible/playbooks/day2_bump_router_models_max.yml
Normal file
161
ansible/playbooks/day2_bump_router_models_max.yml
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/day2_bump_router_models_max.yml
|
||||||
|
# DESCRIPTION: Bump --models-max on the production llama-server-router unit.
|
||||||
|
#
|
||||||
|
# Context: t_33acbb2e (2026-08-12) — Ryan requested --models-max raised from 1
|
||||||
|
# to 4 so the router can keep multiple GGUFs resident on-demand (LRU eviction
|
||||||
|
# when the cap is reached). The actual var change lives in:
|
||||||
|
# host_vars/astro-orbiter/vars.yml (llm_router_models_max: 4)
|
||||||
|
#
|
||||||
|
# This playbook:
|
||||||
|
# 1. Re-renders llama-server-router.service.j2 with the updated var value.
|
||||||
|
# 2. Reloads systemd (daemon-reload handler) if the unit changed.
|
||||||
|
# 3. Restarts llama-server-router so the new --models-max takes effect on the
|
||||||
|
# live process. Router holds no resident model (all-unloaded) so restart
|
||||||
|
# is sub-second and non-disruptive.
|
||||||
|
# 4. Verifies /health returns 200 and /v1/models still lists all three GGUFs.
|
||||||
|
#
|
||||||
|
# VRAM NOTE: --models-max 4 allows up to all 3 current GGUFs to co-reside on
|
||||||
|
# a 24GB card simultaneously. Worst-case combined footprint is ~31GB which
|
||||||
|
# EXCEEDS 24GB — OOM is possible if all 3 are loaded concurrently. In normal
|
||||||
|
# single-user homelab operation this is very unlikely. Full VRAM breakdown
|
||||||
|
# documented in host_vars/astro-orbiter/vars.yml. Ryan approved (t_33acbb2e).
|
||||||
|
#
|
||||||
|
# Execution channel: Semaphore template "llm_router_update_unit" (project mk-labs).
|
||||||
|
# Do NOT run via direct ansible-playbook or ad-hoc ssh/systemctl.
|
||||||
|
#
|
||||||
|
# Author: War Machine (2026-08-12, t_33acbb2e)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: "Bump llama-server-router --models-max to 4 on astro-orbiter"
|
||||||
|
hosts: astro_orbiter
|
||||||
|
gather_facts: true
|
||||||
|
become: true
|
||||||
|
|
||||||
|
vars:
|
||||||
|
# Production vars — router is live on :8002 (post-cutover t_cd0d5388)
|
||||||
|
llm_router_port: 8002
|
||||||
|
llm_router_bind_address: "10.1.71.130"
|
||||||
|
|
||||||
|
# llm_router_models_max is 4 via host_vars/astro-orbiter/vars.yml.
|
||||||
|
# Remaining role vars come from host_vars + defaults/main.yml via the
|
||||||
|
# inventory — we only explicitly set vars this playbook needs for its
|
||||||
|
# own tasks (health/models check URIs).
|
||||||
|
|
||||||
|
# Needed by the template task (mirrors defaults set in role defaults/main.yml)
|
||||||
|
llm_service_user: jarvis
|
||||||
|
llm_binary_path: /opt/llama.cpp/build/bin/llama-server
|
||||||
|
llm_models_dir: /opt/models
|
||||||
|
llm_router_service_name: llama-server-router
|
||||||
|
llm_router_models_dir: /opt/models
|
||||||
|
llm_router_gpu_layers: 99
|
||||||
|
llm_router_ctx_size: 65536
|
||||||
|
llm_router_flash_attn: "auto"
|
||||||
|
llm_router_cache_type_k: q4_0
|
||||||
|
llm_router_cache_type_v: q4_0
|
||||||
|
llm_router_batch_size: 2048
|
||||||
|
llm_router_ubatch_size: 512
|
||||||
|
llm_router_parallel: 1
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# Phase 1: Re-render the router unit file
|
||||||
|
# Template src path is relative to the role's templates/ dir; we reference
|
||||||
|
# it with a relative path that Ansible resolves from the role directory.
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: "Deploy updated llama-server-router unit (--models-max {{ llm_router_models_max }})"
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "{{ playbook_dir }}/../roles/llm-inference-multimodel/templates/llama-server-router.service.j2"
|
||||||
|
dest: "/etc/systemd/system/{{ llm_router_service_name }}.service"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
register: llm_router_unit_updated
|
||||||
|
notify:
|
||||||
|
- reload systemd
|
||||||
|
tags: [always]
|
||||||
|
|
||||||
|
- name: "Flush handlers — ensure daemon-reload lands before restart"
|
||||||
|
ansible.builtin.meta: flush_handlers
|
||||||
|
tags: [always]
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# Phase 2: Restart the router so the new --models-max takes effect.
|
||||||
|
# Always restart (even if unit unchanged) to ensure live process matches.
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: "Restart llama-server-router so --models-max {{ llm_router_models_max }} takes effect"
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ llm_router_service_name }}"
|
||||||
|
state: restarted
|
||||||
|
enabled: true
|
||||||
|
tags: [always]
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# Phase 3: Verify /health returns 200
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: "Wait for /health to return 200 after restart"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/health"
|
||||||
|
status_code: 200
|
||||||
|
timeout: 30
|
||||||
|
register: bump_health_check
|
||||||
|
retries: 10
|
||||||
|
delay: 3
|
||||||
|
until: bump_health_check.status == 200
|
||||||
|
tags: [always]
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# Phase 4: Verify /v1/models lists all three GGUFs
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: "Check /v1/models — all three GGUFs should appear"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/models"
|
||||||
|
status_code: 200
|
||||||
|
timeout: 30
|
||||||
|
return_content: true
|
||||||
|
register: bump_models_check
|
||||||
|
tags: [always]
|
||||||
|
|
||||||
|
- name: "Display /v1/models summary"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "======================================================================"
|
||||||
|
- "--models-max BUMP VERIFICATION (t_33acbb2e)"
|
||||||
|
- ""
|
||||||
|
- " /health: HTTP {{ bump_health_check.status }}"
|
||||||
|
- " /v1/models HTTP: {{ bump_models_check.status }}"
|
||||||
|
- " Models listed: {{ bump_models_check.json.data | map(attribute='id') | list | join(', ') }}"
|
||||||
|
- ""
|
||||||
|
- " --models-max now: {{ llm_router_models_max }}"
|
||||||
|
- " --parallel (unchanged): {{ llm_router_parallel }}"
|
||||||
|
- ""
|
||||||
|
- " VRAM WARNING: worst-case 3-model co-residency ~31GB > 24GB RTX 3090."
|
||||||
|
- " OOM risk if all 3 load concurrently. LRU eviction mitigates in practice."
|
||||||
|
- " Full breakdown: host_vars/astro-orbiter/vars.yml"
|
||||||
|
- "======================================================================"
|
||||||
|
when: bump_models_check is defined
|
||||||
|
tags: [always]
|
||||||
|
|
||||||
|
- name: "GATE: confirm all 3 expected GGUFs appear in /v1/models"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- "'Qwen3.6-35B-A3B-UD-Q4_K_S' in (bump_models_check.json.data | map(attribute='id') | list)"
|
||||||
|
- "'Phi-3.5-mini-instruct-Q8_0' in (bump_models_check.json.data | map(attribute='id') | list)"
|
||||||
|
- "'Meta-Llama-3.1-8B-Instruct-Q4_K_M' in (bump_models_check.json.data | map(attribute='id') | list)"
|
||||||
|
fail_msg: >-
|
||||||
|
/v1/models did not return all 3 expected GGUFs after --models-max bump.
|
||||||
|
Check router logs: journalctl -u llama-server-router -n 50
|
||||||
|
success_msg: "GATE PASSED: all 3 GGUFs listed in /v1/models."
|
||||||
|
when: bump_models_check is defined
|
||||||
|
tags: [always]
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- name: reload systemd
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
listen: "reload systemd"
|
||||||
59
ansible/playbooks/day2_cpu_offload_aux_models.yml
Normal file
59
ansible/playbooks/day2_cpu_offload_aux_models.yml
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Playbook: day2_cpu_offload_aux_models.yml
|
||||||
|
# Purpose: CPU-offload Qwen2.5-Coder-14B and Meta-Llama-3.1-8B on
|
||||||
|
# astro-orbiter's production router (port 8002).
|
||||||
|
#
|
||||||
|
# What this playbook does:
|
||||||
|
# 1. Re-renders llama-server-router-preset.ini (Coder + Llama sections now
|
||||||
|
# use per-model n-gpu-layers vars = 0 -> full CPU inference).
|
||||||
|
# 2. Re-renders the router unit (--models-max now 4 via host_vars, global
|
||||||
|
# --n-gpu-layers removed per t_72646029 unit template fix) and restarts
|
||||||
|
# llama-server-router so both changes take effect.
|
||||||
|
# 3. Verifies per the role's router_preset phase.
|
||||||
|
#
|
||||||
|
# Context (2026-08-17):
|
||||||
|
# - RAM/model-swap audit, TIER 1 (Coder-14B CPU offload) + TIER 2
|
||||||
|
# (Llama-3.1-8B CPU offload) — Ryan approved 1 & 2 on 2026-08-17.
|
||||||
|
# See inbox/ryan/2026-08-17-llm-system-ram-model-swap.md.
|
||||||
|
# - Unit template fix (t_72646029): global --n-gpu-layers removed from
|
||||||
|
# ExecStart in preset mode. Each INI section now sets n-gpu-layers
|
||||||
|
# explicitly (Qwen3.8=99, Phi=99, nomic=99, Coder=0, Llama=0).
|
||||||
|
# - Concurrent residency after change: Qwen3.8-27B (20,302 MiB @ 128K ctx)
|
||||||
|
# + nomic-embed (558 MiB, pinned) + Coder (CPU, ~1,390 MiB CUDA ctx) +
|
||||||
|
# Llama (CPU, ~1,706 MiB CUDA ctx) = ~24,004 MiB. NOTE: llama.cpp 6ea215d
|
||||||
|
# allocates CUDA-context VRAM even at n-gpu-layers=0, so CPU models are not
|
||||||
|
# 0-VRAM; total sits at the 24,576 MiB physical limit (headroom ~572 MiB).
|
||||||
|
# Qwen3.8 is never evicted for a CPU aux model; Phi-3.5-mini (GPU, 8.3GB)
|
||||||
|
# still evicts as before.
|
||||||
|
# - CPU speed (8-core Ryzen 7 5800XT): ~5-10 tok/s (14B), ~10-20 tok/s (8B).
|
||||||
|
# - Semaphore SSH gap for astro-orbiter still applies (t_730f9584 /
|
||||||
|
# t_33acbb2e); running direct CLI Ansible per standing exception.
|
||||||
|
#
|
||||||
|
# Run:
|
||||||
|
# cd /home/hermes/git/homelab/ansible
|
||||||
|
# env -u ANSIBLE_VAULT_PASSWORD_FILE ansible-playbook \
|
||||||
|
# -i inventory.yml \
|
||||||
|
# playbooks/day2_cpu_offload_aux_models.yml
|
||||||
|
#
|
||||||
|
# Rollback:
|
||||||
|
# git checkout -- \
|
||||||
|
# roles/llm-inference-multimodel/templates/llama-server-router.service.j2 \
|
||||||
|
# roles/llm-inference-multimodel/templates/llama-server-router-preset.ini.j2 \
|
||||||
|
# roles/llm-inference-multimodel/defaults/main.yml \
|
||||||
|
# host_vars/astro-orbiter/vars.yml
|
||||||
|
# (restores n-gpu-layers=99 global flag, models-max=2, all GPU)
|
||||||
|
# then re-run this playbook to redeploy rollback state.
|
||||||
|
# Note: playbooks/day2_cpu_offload_aux_models.yml is untracked — left on disk.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
- name: CPU-offload Coder-14B and Llama-3.1-8B on astro-orbiter
|
||||||
|
hosts: astro-orbiter
|
||||||
|
become: true
|
||||||
|
vars:
|
||||||
|
llm_router_preset_enabled: true
|
||||||
|
llm_router_enabled: true
|
||||||
|
llm_router_port: 8002
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: llm-inference-multimodel
|
||||||
|
tags: [always]
|
||||||
511
ansible/playbooks/day2_cutover_qwen_to_router.yml
Normal file
511
ansible/playbooks/day2_cutover_qwen_to_router.yml
Normal file
@@ -0,0 +1,511 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/day2_cutover_qwen_to_router.yml
|
||||||
|
# DESCRIPTION: Promote llama-server-router to production on port 8002.
|
||||||
|
#
|
||||||
|
# Context: Router-mode shadow deployment (t_0cca74a2) validated 2026-08-12:
|
||||||
|
# all 4 hard gates PASSED (n_ctx 65536, tool-calling PASS, hallucination-stress
|
||||||
|
# PASS, VRAM 20410 MiB / 1 process). Ryan approved cutover.
|
||||||
|
#
|
||||||
|
# This playbook makes the router the permanent production endpoint:
|
||||||
|
#
|
||||||
|
# 1. Stop + disable llama-server-qwen (:8002). Unit file is PRESERVED on disk
|
||||||
|
# as the rollback target (same pattern as prior role history).
|
||||||
|
# 2. Redeploy llama-server-router unit file with --port 8002 (production port).
|
||||||
|
# PORT DECISION: we rebind the router to :8002 rather than updating 8
|
||||||
|
# dependent Hermes profiles' base_url. One unit file change beats 8
|
||||||
|
# config.yaml updates — atomic, GitOps-clean, zero profile drift.
|
||||||
|
# 3. Enable + start llama-server-router on :8002.
|
||||||
|
# 4. Re-run validation gates 1-3 against the NOW-production endpoint.
|
||||||
|
# (Same logic as Phase R / router_verify in tasks/router.yml — hard gates.)
|
||||||
|
# 5. Run Gate 4: verify bundled SvelteKit UI is reachable.
|
||||||
|
#
|
||||||
|
# Usage (from ~/git/homelab/ansible):
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day2_cutover_qwen_to_router.yml
|
||||||
|
#
|
||||||
|
# Rollback (if gates fail or any time after):
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day2_cutover_qwen_to_router.yml \
|
||||||
|
# --tags cutover_rollback
|
||||||
|
#
|
||||||
|
# Author: War Machine (2026-08-12, t_cd0d5388)
|
||||||
|
# Approved by: Ryan (cutover authorization, 2026-08-12)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: "CUTOVER — Promote llama-server-router to production (:8002) on astro-orbiter"
|
||||||
|
hosts: astro_orbiter
|
||||||
|
gather_facts: true
|
||||||
|
become: true
|
||||||
|
|
||||||
|
vars:
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
# PORT DECISION:
|
||||||
|
# We rebind the router to :8002 (production port) rather than
|
||||||
|
# updating 8 dependent Hermes profiles' base_url to :8003.
|
||||||
|
# Rationale: one unit file change is atomic and GitOps-clean.
|
||||||
|
# Updating 8 config.yaml files risks drift and requires per-profile
|
||||||
|
# activation tests. The template renders llm_router_port as the
|
||||||
|
# --port argument; we just override it here to 8002.
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
# Router port override: take over production port
|
||||||
|
llm_router_port: 8002
|
||||||
|
|
||||||
|
# All other role defaults needed by the template (mirrors defaults/main.yml)
|
||||||
|
llm_service_user: jarvis
|
||||||
|
llm_binary_path: /opt/llama.cpp/build/bin/llama-server
|
||||||
|
llm_models_dir: /opt/models
|
||||||
|
llm_bind_address: "10.1.71.130"
|
||||||
|
llm_allowed_source_cidr: "10.1.70.0/24"
|
||||||
|
|
||||||
|
llm_router_enabled: true
|
||||||
|
llm_router_service_name: llama-server-router
|
||||||
|
llm_router_models_dir: /opt/models
|
||||||
|
llm_router_models_max: 1 # CRITICAL: RTX 3090 24GB, single model only
|
||||||
|
llm_router_ctx_size: 65536
|
||||||
|
llm_router_parallel: 1
|
||||||
|
llm_router_gpu_layers: 99
|
||||||
|
llm_router_batch_size: 2048
|
||||||
|
llm_router_ubatch_size: 512
|
||||||
|
llm_router_cache_type_k: q4_0
|
||||||
|
llm_router_cache_type_v: q4_0
|
||||||
|
llm_router_flash_attn: "auto"
|
||||||
|
llm_router_bind_address: "10.1.71.130"
|
||||||
|
llm_router_allowed_source_cidr: "10.1.70.0/24"
|
||||||
|
llm_router_expected_model_id: "Qwen3.6-35B-A3B-UD-Q4_K_S"
|
||||||
|
llm_router_vram_max_mib: 23000
|
||||||
|
|
||||||
|
llm_qwen_service_name: llama-server-qwen
|
||||||
|
llm_qwen_port: 8002
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# PHASE 1 — Stop and disable llama-server-qwen (bare single-model)
|
||||||
|
# Preserve unit file on disk — rollback target per existing role pattern.
|
||||||
|
# =======================================================================
|
||||||
|
|
||||||
|
- name: "[cutover] PHASE 1: Confirm llama-server-qwen current state"
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: llama-server-qwen
|
||||||
|
register: cutover_qwen_status
|
||||||
|
tags: [cutover_stop_qwen, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] PHASE 1: Report current llama-server-qwen status"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: >-
|
||||||
|
llama-server-qwen: ActiveState={{ cutover_qwen_status.status.ActiveState | default('unknown') }},
|
||||||
|
UnitFileState={{ cutover_qwen_status.status.UnitFileState | default('unknown') }}.
|
||||||
|
Will stop + disable. Unit file preserved at /etc/systemd/system/llama-server-qwen.service as rollback target.
|
||||||
|
tags: [cutover_stop_qwen, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] PHASE 1: Stop llama-server-qwen (:8002, bare single-model)"
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: llama-server-qwen
|
||||||
|
state: stopped
|
||||||
|
register: cutover_qwen_stopped
|
||||||
|
tags: [cutover_stop_qwen, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] PHASE 1: Disable llama-server-qwen (prevent auto-start on reboot)"
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: llama-server-qwen
|
||||||
|
enabled: false
|
||||||
|
tags: [cutover_stop_qwen, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] PHASE 1: Wait 5s for VRAM to be released"
|
||||||
|
ansible.builtin.pause:
|
||||||
|
seconds: 5
|
||||||
|
when: cutover_qwen_stopped.changed | default(false)
|
||||||
|
tags: [cutover_stop_qwen, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] PHASE 1: Verify port 8002 is now free"
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: ss -ltnp
|
||||||
|
register: cutover_port_check
|
||||||
|
changed_when: false
|
||||||
|
tags: [cutover_stop_qwen, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] PHASE 1: Fail if port 8002 is still bound"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
Port 8002 is still bound after stopping llama-server-qwen.
|
||||||
|
Check 'ss -ltnp | grep :8002' and resolve before the router can bind.
|
||||||
|
when:
|
||||||
|
- "':8002 ' in (cutover_port_check.stdout | default('')) or ':8002:' in (cutover_port_check.stdout | default(''))"
|
||||||
|
tags: [cutover_stop_qwen, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] PHASE 1: Report VRAM state (should be empty)"
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: nvidia-smi --query-compute-apps=pid,name,used_memory --format=csv,noheader
|
||||||
|
register: cutover_vram_free_check
|
||||||
|
changed_when: false
|
||||||
|
tags: [cutover_stop_qwen, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] PHASE 1: Print VRAM state"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: >-
|
||||||
|
VRAM after stopping llama-server-qwen:
|
||||||
|
{{ cutover_vram_free_check.stdout if (cutover_vram_free_check.stdout | length > 0)
|
||||||
|
else '(no GPU processes — VRAM free)' }}
|
||||||
|
tags: [cutover_stop_qwen, cutover]
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# PHASE 2 — Redeploy llama-server-router unit with --port 8002
|
||||||
|
# =======================================================================
|
||||||
|
|
||||||
|
- name: "[cutover] PHASE 2: Deploy llama-server-router unit file (port 8002 — production)"
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "../roles/llm-inference-multimodel/templates/llama-server-router.service.j2"
|
||||||
|
dest: /etc/systemd/system/llama-server-router.service
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
register: cutover_router_unit_deployed
|
||||||
|
notify:
|
||||||
|
- reload systemd
|
||||||
|
tags: [cutover_deploy_unit, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] PHASE 2: Flush handlers (daemon-reload before start)"
|
||||||
|
ansible.builtin.meta: flush_handlers
|
||||||
|
tags: [cutover_deploy_unit, cutover]
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# PHASE 3 — Enable + start llama-server-router on :8002
|
||||||
|
# =======================================================================
|
||||||
|
|
||||||
|
- name: "[cutover] PHASE 3: Enable + start llama-server-router (production, :8002)"
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: llama-server-router
|
||||||
|
state: "{{ 'restarted' if (cutover_router_unit_deployed.changed | default(false)) else 'started' }}"
|
||||||
|
enabled: true
|
||||||
|
daemon_reload: true
|
||||||
|
tags: [cutover_start_router, cutover]
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# PHASE 4 — Validation gates 1-3 (hard gates against now-production :8002)
|
||||||
|
# =======================================================================
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 1a: Wait for router /health on :8002 (up to 5min — cold model load)"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/health"
|
||||||
|
status_code: 200
|
||||||
|
register: cutover_health
|
||||||
|
retries: 30
|
||||||
|
delay: 10
|
||||||
|
until: cutover_health.status == 200
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 1a: Trigger model load (router lazy-loads on first request)"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/chat/completions"
|
||||||
|
method: POST
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
model: "{{ llm_router_expected_model_id }}"
|
||||||
|
messages:
|
||||||
|
- role: user
|
||||||
|
content: "Reply with one word: hello"
|
||||||
|
max_tokens: 5
|
||||||
|
temperature: 0.0
|
||||||
|
status_code: 200
|
||||||
|
return_content: true
|
||||||
|
timeout: 300
|
||||||
|
register: cutover_warmup
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 1a: Report warmup"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "Model loaded. finish_reason={{ cutover_warmup.json.choices[0].finish_reason | default('unknown') }}"
|
||||||
|
- "Response: {{ cutover_warmup.json.choices[0].message.content | default('(empty)') | truncate(100) }}"
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 1b: Query /v1/models on :8002"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/models"
|
||||||
|
status_code: 200
|
||||||
|
return_content: true
|
||||||
|
register: cutover_models
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 1b: Fail if expected model ID not found"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
GATE 1 FAIL: '{{ llm_router_expected_model_id }}' not found in /v1/models.
|
||||||
|
Returned: {{ cutover_models.json.data | map(attribute='id') | list }}
|
||||||
|
when:
|
||||||
|
- cutover_models.json.data | selectattr('id', 'equalto', llm_router_expected_model_id) | list | length == 0
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 1b: Extract ctx-size from router model args"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cutover_qwen_n_ctx: >-
|
||||||
|
{%- set model = cutover_models.json.data | selectattr('id', 'equalto', llm_router_expected_model_id) | first -%}
|
||||||
|
{%- set args = model.status.args -%}
|
||||||
|
{%- set ctx_idx = args.index('--ctx-size') if '--ctx-size' in args else -1 -%}
|
||||||
|
{{ args[ctx_idx + 1] | int if ctx_idx >= 0 else 0 }}
|
||||||
|
when:
|
||||||
|
- cutover_models.json.data | selectattr('id', 'equalto', llm_router_expected_model_id) | list | length > 0
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 1b: Fail if n_ctx < 64000"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: "GATE 1 FAIL: --ctx-size={{ cutover_qwen_n_ctx }} < 64000 (Hermes 64K floor)."
|
||||||
|
when:
|
||||||
|
- cutover_qwen_n_ctx is defined
|
||||||
|
- cutover_qwen_n_ctx | int < 64000
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 1b: PASS — n_ctx >= 64K"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "GATE 1 PASS: --ctx-size={{ cutover_qwen_n_ctx }} >= 64000."
|
||||||
|
when:
|
||||||
|
- cutover_qwen_n_ctx is defined
|
||||||
|
- cutover_qwen_n_ctx | int >= 64000
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
# --- Gate 2: Tool-calling through router proxy ---
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 2: Tool-calling probe"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/chat/completions"
|
||||||
|
method: POST
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
model: "{{ llm_router_expected_model_id }}"
|
||||||
|
messages:
|
||||||
|
- role: user
|
||||||
|
content: "What is the current weather in Chicago? Use the provided tool."
|
||||||
|
tools:
|
||||||
|
- type: function
|
||||||
|
function:
|
||||||
|
name: get_weather
|
||||||
|
description: "Get current weather conditions for a city"
|
||||||
|
parameters:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
city:
|
||||||
|
type: string
|
||||||
|
description: "The city name"
|
||||||
|
required:
|
||||||
|
- city
|
||||||
|
temperature: 0.0
|
||||||
|
status_code: 200
|
||||||
|
return_content: true
|
||||||
|
timeout: 120
|
||||||
|
register: cutover_toolcall_probe
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 2: Fail if not finish_reason=tool_calls"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
GATE 2 FAIL: finish_reason={{ cutover_toolcall_probe.json.choices[0].finish_reason | default('(missing)') }}
|
||||||
|
(expected tool_calls). Response: {{ cutover_toolcall_probe.json | to_json }}
|
||||||
|
when:
|
||||||
|
- cutover_toolcall_probe.json.choices[0].finish_reason | default('') != 'tool_calls'
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 2: PASS"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "GATE 2 PASS: finish_reason=tool_calls"
|
||||||
|
- "function: {{ cutover_toolcall_probe.json.choices[0].message.tool_calls[0].function.name | default('(unknown)') }}"
|
||||||
|
- "arguments: {{ cutover_toolcall_probe.json.choices[0].message.tool_calls[0].function.arguments | default('(none)') }}"
|
||||||
|
when:
|
||||||
|
- cutover_toolcall_probe.json.choices[0].finish_reason | default('') == 'tool_calls'
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
# --- Gate 2b: Hallucination stress ---
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 2b: Hallucination stress probe"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/chat/completions"
|
||||||
|
method: POST
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
model: "{{ llm_router_expected_model_id }}"
|
||||||
|
messages:
|
||||||
|
- role: user
|
||||||
|
content: "Tell me a brief fact about the planet Mars. Do not call any functions."
|
||||||
|
tools:
|
||||||
|
- type: function
|
||||||
|
function:
|
||||||
|
name: get_weather
|
||||||
|
description: "Get current weather conditions for a city"
|
||||||
|
parameters:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
city:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- city
|
||||||
|
temperature: 0.1
|
||||||
|
status_code: 200
|
||||||
|
return_content: true
|
||||||
|
timeout: 120
|
||||||
|
register: cutover_halluc_probe
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 2b: Fail if spurious tool_calls"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
GATE 2b FAIL: finish_reason=tool_calls on unrelated prompt (Mars fact).
|
||||||
|
Over-triggering through router. Response: {{ cutover_halluc_probe.json | to_json }}
|
||||||
|
when:
|
||||||
|
- cutover_halluc_probe.json.choices[0].finish_reason | default('') == 'tool_calls'
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 2b: PASS"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "GATE 2b PASS: finish_reason={{ cutover_halluc_probe.json.choices[0].finish_reason }} — no spurious tool_calls."
|
||||||
|
when:
|
||||||
|
- cutover_halluc_probe.json.choices[0].finish_reason | default('') != 'tool_calls'
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
# --- Gate 3: VRAM guard ---
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 3: Check VRAM usage (--models-max 1 guard)"
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: nvidia-smi --query-gpu=memory.used,memory.total,utilization.gpu --format=csv,noheader
|
||||||
|
register: cutover_vram_post
|
||||||
|
changed_when: false
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 3: Parse VRAM used MiB"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cutover_vram_used_mib: "{{ cutover_vram_post.stdout.split(',')[0].strip().split(' ')[0] | int }}"
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 3: Fail if VRAM exceeds ceiling"
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
GATE 3 FAIL: {{ cutover_vram_used_mib }} MiB > {{ llm_router_vram_max_mib }} MiB ceiling.
|
||||||
|
Full: {{ cutover_vram_post.stdout }}
|
||||||
|
when:
|
||||||
|
- cutover_vram_used_mib | int > llm_router_vram_max_mib | int
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 3: Count GPU processes"
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: nvidia-smi --query-compute-apps=pid,name --format=csv,noheader
|
||||||
|
register: cutover_gpu_procs
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 3: PASS"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "GATE 3 PASS: {{ cutover_vram_used_mib }} MiB / {{ llm_router_vram_max_mib }} MiB ceiling."
|
||||||
|
- "GPU processes: {{ cutover_gpu_procs.stdout_lines | default(['(none)']) }}"
|
||||||
|
- "Full nvidia-smi: {{ cutover_vram_post.stdout }}"
|
||||||
|
when:
|
||||||
|
- cutover_vram_used_mib | int <= llm_router_vram_max_mib | int
|
||||||
|
tags: [cutover_validate, cutover]
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# PHASE 5 — Gate 4: Bundled SvelteKit Web UI (required this time)
|
||||||
|
# =======================================================================
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 4: Check bundled SvelteKit UI at :8002"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/"
|
||||||
|
status_code: [200, 301, 302]
|
||||||
|
return_content: true
|
||||||
|
timeout: 30
|
||||||
|
register: cutover_ui_check
|
||||||
|
failed_when: false
|
||||||
|
tags: [cutover_validate, cutover_ui, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 4: Inspect UI content"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cutover_ui_is_html: "{{ 'html' in (cutover_ui_check.content | default('') | lower) or '<!doctype' in (cutover_ui_check.content | default('') | lower) }}"
|
||||||
|
cutover_ui_has_model_select: "{{ 'select' in (cutover_ui_check.content | default('') | lower) or 'model' in (cutover_ui_check.content | default('') | lower) }}"
|
||||||
|
when: cutover_ui_check is defined
|
||||||
|
tags: [cutover_validate, cutover_ui, cutover]
|
||||||
|
|
||||||
|
- name: "[cutover] GATE 4: Report UI check and bookmark URL"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "======================================================================"
|
||||||
|
- "GATE 4 UI CHECK:"
|
||||||
|
- " HTTP status: {{ cutover_ui_check.status | default('UNREACHABLE') }}"
|
||||||
|
- " Is HTML: {{ cutover_ui_is_html | default(false) }}"
|
||||||
|
- " Contains model/select: {{ cutover_ui_has_model_select | default(false) }}"
|
||||||
|
- " BOOKMARK URL: http://{{ llm_router_bind_address }}:{{ llm_router_port }}/"
|
||||||
|
- " {{ 'GATE 4 PASS — UI serving HTML at :8002.' if (cutover_ui_check.status | default(0) | int in [200, 301, 302]) else 'GATE 4 WARN — UI not reachable (HTTP ' + (cutover_ui_check.status | default('FAIL') | string) + ').' }}"
|
||||||
|
- "======================================================================"
|
||||||
|
when: cutover_ui_check is defined
|
||||||
|
tags: [cutover_validate, cutover_ui, cutover]
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# CUTOVER SUMMARY
|
||||||
|
# =======================================================================
|
||||||
|
|
||||||
|
- name: "[cutover] CUTOVER SUMMARY — production promoted"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "======================================================================"
|
||||||
|
- "CUTOVER COMPLETE: llama-server-router is now production."
|
||||||
|
- ""
|
||||||
|
- " Service: llama-server-router.service (enabled, running)"
|
||||||
|
- " Port: 8002 (unchanged for all 8 Hermes profiles)"
|
||||||
|
- " Model: {{ llm_router_expected_model_id }}"
|
||||||
|
- " Mode: Router/supervisor (--models-dir /opt/models, --models-max 1)"
|
||||||
|
- ""
|
||||||
|
- " Gate 1 (n_ctx >= 64K): PASS ({{ cutover_qwen_n_ctx | default('N/A') }})"
|
||||||
|
- " Gate 2 (tool-calling): PASS (finish_reason=tool_calls)"
|
||||||
|
- " Gate 2b (halluc stress): PASS (no spurious tool_calls)"
|
||||||
|
- " Gate 3 (VRAM <= 23000MiB): PASS ({{ cutover_vram_used_mib | default('N/A') }} MiB)"
|
||||||
|
- " Gate 4 (Web UI): HTTP {{ cutover_ui_check.status | default('N/A') }}"
|
||||||
|
- ""
|
||||||
|
- " ROLLBACK TARGET: /etc/systemd/system/llama-server-qwen.service (unit preserved)"
|
||||||
|
- " ROLLBACK CMD: sudo systemctl enable --now llama-server-qwen"
|
||||||
|
- " sudo systemctl disable --now llama-server-router"
|
||||||
|
- " Or: ansible-playbook -i inventory.yml day2_cutover_qwen_to_router.yml --tags cutover_rollback"
|
||||||
|
- ""
|
||||||
|
- " Web UI bookmark: http://{{ llm_router_bind_address }}:{{ llm_router_port }}/"
|
||||||
|
- "======================================================================"
|
||||||
|
tags: [cutover]
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# ROLLBACK — tag cutover_rollback reverses the cutover
|
||||||
|
# Run: ansible-playbook -i inventory.yml day2_cutover_qwen_to_router.yml --tags cutover_rollback
|
||||||
|
# WARNING: rollback_task has no dependency on cutover tags — safe to run standalone.
|
||||||
|
# =======================================================================
|
||||||
|
|
||||||
|
- name: "[cutover_rollback] Stop + disable llama-server-router"
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: llama-server-router
|
||||||
|
state: stopped
|
||||||
|
enabled: false
|
||||||
|
tags: [cutover_rollback, never] # 'never' = only runs with explicit --tags cutover_rollback
|
||||||
|
|
||||||
|
- name: "[cutover_rollback] Enable + start llama-server-qwen (restore bare :8002)"
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: llama-server-qwen
|
||||||
|
state: started
|
||||||
|
enabled: true
|
||||||
|
tags: [cutover_rollback, never]
|
||||||
|
|
||||||
|
- name: "[cutover_rollback] Verify rollback /health"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_bind_address | default('10.1.71.130') }}:8002/health"
|
||||||
|
status_code: 200
|
||||||
|
timeout: 30
|
||||||
|
register: cutover_rollback_health
|
||||||
|
failed_when: false
|
||||||
|
tags: [cutover_rollback, never]
|
||||||
|
|
||||||
|
- name: "[cutover_rollback] Report rollback result"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: >-
|
||||||
|
ROLLBACK: llama-server-qwen :8002 health returned
|
||||||
|
{{ cutover_rollback_health.status | default('UNREACHABLE') }}.
|
||||||
|
{{ 'OK — production restored to bare qwen.' if (cutover_rollback_health.status | default(0) | int == 200)
|
||||||
|
else 'WARNING — health check failed. Check manually.' }}
|
||||||
|
tags: [cutover_rollback, never]
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- name: reload systemd
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: true
|
||||||
80
ansible/playbooks/day2_install_node_exporter.yml
Normal file
80
ansible/playbooks/day2_install_node_exporter.yml
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: ansible/playbooks/day2_install_node_exporter.yml
|
||||||
|
# DESCRIPTION: Install and configure Prometheus Node Exporter on Linux hosts.
|
||||||
|
#
|
||||||
|
# Targets two groups with different firewall managers:
|
||||||
|
# - proxmox_nodes : Proxmox hypervisors (Debian, firewalld not present,
|
||||||
|
# uses iptables/no firewall — just open the port via UFW
|
||||||
|
# if present, otherwise skip)
|
||||||
|
# - monitored_vms : Ubuntu VMs managed by UFW
|
||||||
|
#
|
||||||
|
# After installing on new hosts, re-runs day1_deploy_monitoring.yml to refresh
|
||||||
|
# the Prometheus scrape config is NOT needed — targets are already statically
|
||||||
|
# defined in prometheus.yaml.j2 for the Proxmox nodes. For new VMs, add the
|
||||||
|
# IP to the prometheus.yaml.j2 proxmox-vms job and re-run day1_deploy_monitoring.yml.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day2_install_node_exporter.yml
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day2_install_node_exporter.yml --limit proxmox_nodes
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day2_install_node_exporter.yml --limit monitored_vms
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ── Play 1: Proxmox hypervisors ───────────────────────────────────────────────
|
||||||
|
# Proxmox runs Debian. The prometheus.prometheus.node_exporter role installs
|
||||||
|
# a binary + systemd service without touching apt, which is what we want on
|
||||||
|
# hypervisors (keep the package footprint clean).
|
||||||
|
# Note: Proxmox does not run UFW. Port 9100 is open by default on VLAN 71.
|
||||||
|
|
||||||
|
- name: Install Node Exporter on Proxmox hypervisors
|
||||||
|
hosts: proxmox_nodes
|
||||||
|
become: true
|
||||||
|
|
||||||
|
vars:
|
||||||
|
node_exporter_version: "1.9.1"
|
||||||
|
node_exporter_web_listen_address: "0.0.0.0:9100"
|
||||||
|
node_exporter_enabled_collectors:
|
||||||
|
- systemd
|
||||||
|
- processes
|
||||||
|
- filesystem
|
||||||
|
- meminfo
|
||||||
|
- cpu
|
||||||
|
- diskstats
|
||||||
|
- netdev
|
||||||
|
- loadavg
|
||||||
|
- uname
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: prometheus.prometheus.node_exporter
|
||||||
|
|
||||||
|
# ── Play 2: Ubuntu VMs ────────────────────────────────────────────────────────
|
||||||
|
# Standard Ubuntu hosts with UFW. Same role, adds UFW allow rule for 9100.
|
||||||
|
|
||||||
|
- name: Install Node Exporter on monitored VMs
|
||||||
|
hosts: monitored_vms
|
||||||
|
become: true
|
||||||
|
|
||||||
|
vars:
|
||||||
|
node_exporter_version: "1.9.1"
|
||||||
|
node_exporter_web_listen_address: "0.0.0.0:9100"
|
||||||
|
node_exporter_enabled_collectors:
|
||||||
|
- systemd
|
||||||
|
- processes
|
||||||
|
- filesystem
|
||||||
|
- meminfo
|
||||||
|
- cpu
|
||||||
|
- diskstats
|
||||||
|
- netdev
|
||||||
|
- loadavg
|
||||||
|
- uname
|
||||||
|
|
||||||
|
pre_tasks:
|
||||||
|
- name: Open Node Exporter port in UFW
|
||||||
|
community.general.ufw:
|
||||||
|
rule: allow
|
||||||
|
port: "9100"
|
||||||
|
proto: tcp
|
||||||
|
comment: "Prometheus Node Exporter"
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: prometheus.prometheus.node_exporter
|
||||||
277
ansible/playbooks/day2_per_model_ctx_size.yml
Normal file
277
ansible/playbooks/day2_per_model_ctx_size.yml
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/day2_per_model_ctx_size.yml
|
||||||
|
# DESCRIPTION: Right-size --ctx-size per model workload on llama-server-router
|
||||||
|
# (already in --models-preset mode since t_9adf0889).
|
||||||
|
#
|
||||||
|
# Context (t_ryan_per_model_ctx, 2026-08-13, requested by Ryan via JARVIS):
|
||||||
|
# All 3 preset models currently launch with a uniform --ctx-size 65536.
|
||||||
|
# This playbook narrows two of them to match actual workload:
|
||||||
|
# - Meta-Llama-3.1-8B-Instruct-Q4_K_M (alias Meta-Llama-3.1-8B-Instruct-4bit):
|
||||||
|
# ctx-size 65536 -> 8192 (tool-routing / micro-tasks: title gen, MCP
|
||||||
|
# tool calls, approval checks)
|
||||||
|
# - Phi-3.5-mini-instruct-Q8_0 (alias Phi-3.5-mini-instruct-8bit):
|
||||||
|
# ctx-size 65536 -> 32768 (long web scrapes / session-log compression)
|
||||||
|
# Both also move flash-attn from "auto" to explicit "true" per Ryan's spec.
|
||||||
|
# Qwen3.6-35B-A3B-UD-Q4_K_S is INTENTIONALLY left untouched at 65536/auto.
|
||||||
|
#
|
||||||
|
# Existing aliases (Meta-Llama-3.1-8B-Instruct-4bit, Phi-3.5-mini-instruct-8bit)
|
||||||
|
# are PRESERVED as-is. Ryan's pasted TOML used different alias strings
|
||||||
|
# ("llama-3.1-8b", "phi-3.5-mini") but renaming aliases was not explicitly
|
||||||
|
# requested and would break live Hermes custom_providers routing — flagged
|
||||||
|
# in the deployment report rather than applied silently.
|
||||||
|
#
|
||||||
|
# IMPORTANT — Hermes side effect: /home/hermes/.hermes/config.yaml declares
|
||||||
|
# context_length: 65536 for both these models under custom_providers. This
|
||||||
|
# playbook does NOT touch that file (out of role/agent scope) but the value
|
||||||
|
# becomes STALE the moment this playbook lands. Flag to JARVIS/Maria Hill.
|
||||||
|
#
|
||||||
|
# Usage (from ~/git/homelab/ansible):
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day2_per_model_ctx_size.yml
|
||||||
|
#
|
||||||
|
# Author: War Machine (2026-08-13, t_ryan_per_model_ctx)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: "Right-size per-model ctx-size on llama-server-router (Llama 8k, Phi 32k)"
|
||||||
|
hosts: astro_orbiter
|
||||||
|
gather_facts: false
|
||||||
|
become: true
|
||||||
|
|
||||||
|
vars:
|
||||||
|
# Preset mode already active in production (t_9adf0889) — keep it on.
|
||||||
|
llm_router_preset_enabled: true
|
||||||
|
llm_router_preset_path: /opt/llama-server-router-preset.ini
|
||||||
|
llm_router_enabled: true
|
||||||
|
|
||||||
|
# Production port
|
||||||
|
llm_router_port: 8002
|
||||||
|
llm_router_bind_address: "10.1.71.130"
|
||||||
|
llm_router_allowed_source_cidr: "10.1.70.0/24"
|
||||||
|
llm_bind_address: "10.1.71.130"
|
||||||
|
llm_allowed_source_cidr: "10.1.70.0/24"
|
||||||
|
|
||||||
|
llm_service_user: jarvis
|
||||||
|
llm_binary_path: /opt/llama.cpp/build/bin/llama-server
|
||||||
|
llm_models_dir: /opt/models
|
||||||
|
llm_router_service_name: llama-server-router
|
||||||
|
llm_router_models_dir: /opt/models
|
||||||
|
llm_router_models_max: 4
|
||||||
|
llm_router_parallel: 1
|
||||||
|
llm_router_gpu_layers: 99
|
||||||
|
llm_router_batch_size: 2048
|
||||||
|
llm_router_ubatch_size: 512
|
||||||
|
llm_router_cache_type_k: q4_0
|
||||||
|
llm_router_cache_type_v: q4_0
|
||||||
|
|
||||||
|
# Qwen — untouched baseline (also used as router-wide fallback default)
|
||||||
|
llm_router_ctx_size: 65536
|
||||||
|
llm_router_flash_attn: "auto"
|
||||||
|
llm_router_expected_model_id: "Qwen3.6-35B-A3B-UD-Q4_K_S"
|
||||||
|
llm_router_vram_max_mib: 23000
|
||||||
|
|
||||||
|
# --- THE CHANGE: per-model overrides ---
|
||||||
|
llm_router_llama_ctx_size: 8192
|
||||||
|
llm_router_llama_flash_attn: "true"
|
||||||
|
llm_router_phi_ctx_size: 32768
|
||||||
|
llm_router_phi_flash_attn: "true"
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- name: reload systemd
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
become: true
|
||||||
|
listen: "reload systemd"
|
||||||
|
|
||||||
|
- name: restart router
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: llama-server-router
|
||||||
|
state: restarted
|
||||||
|
become: true
|
||||||
|
listen: "restart router"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# PHASE 1: Deploy the preset INI with new per-model ctx-size/flash-attn
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
- name: "[ctx-resize] Deploy preset INI to {{ llm_router_preset_path }}"
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "../roles/llm-inference-multimodel/templates/llama-server-router-preset.ini.j2"
|
||||||
|
dest: "{{ llm_router_preset_path }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
register: ctx_resize_preset_deployed
|
||||||
|
notify:
|
||||||
|
- restart router
|
||||||
|
|
||||||
|
- name: "[ctx-resize] Deploy router systemd unit (drop global --ctx-size/--flash-attn in preset mode)"
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "../roles/llm-inference-multimodel/templates/llama-server-router.service.j2"
|
||||||
|
dest: /etc/systemd/system/llama-server-router.service
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
register: ctx_resize_unit_deployed
|
||||||
|
notify:
|
||||||
|
- reload systemd
|
||||||
|
- restart router
|
||||||
|
|
||||||
|
- name: "[ctx-resize] Flush handlers (daemon-reload + router restart if changed)"
|
||||||
|
ansible.builtin.meta: flush_handlers
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# PHASE 2: Verify
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
- name: "[ctx-resize] Wait for /health"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/health"
|
||||||
|
status_code: 200
|
||||||
|
timeout: 30
|
||||||
|
retries: 12
|
||||||
|
delay: 5
|
||||||
|
register: ctx_resize_health
|
||||||
|
until: ctx_resize_health.status == 200
|
||||||
|
|
||||||
|
- name: "[ctx-resize] Query /v1/models"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/models"
|
||||||
|
status_code: 200
|
||||||
|
return_content: true
|
||||||
|
timeout: 30
|
||||||
|
register: ctx_resize_models
|
||||||
|
|
||||||
|
- name: "[ctx-resize] Trigger load — Llama (confirms actual load + captures live args)"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/chat/completions"
|
||||||
|
method: POST
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
model: "Meta-Llama-3.1-8B-Instruct-Q4_K_M"
|
||||||
|
messages:
|
||||||
|
- role: user
|
||||||
|
content: "Reply with one word: hello"
|
||||||
|
max_tokens: 5
|
||||||
|
temperature: 0.0
|
||||||
|
status_code: 200
|
||||||
|
return_content: true
|
||||||
|
timeout: 120
|
||||||
|
register: ctx_resize_llama_warmup
|
||||||
|
|
||||||
|
- name: "[ctx-resize] Trigger load — Phi (confirms actual load + captures live args)"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/chat/completions"
|
||||||
|
method: POST
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
model: "Phi-3.5-mini-instruct-Q8_0"
|
||||||
|
messages:
|
||||||
|
- role: user
|
||||||
|
content: "Reply with one word: hello"
|
||||||
|
max_tokens: 5
|
||||||
|
temperature: 0.0
|
||||||
|
status_code: 200
|
||||||
|
return_content: true
|
||||||
|
timeout: 120
|
||||||
|
register: ctx_resize_phi_warmup
|
||||||
|
|
||||||
|
- name: "[ctx-resize] Re-query /v1/models after warmup (final state)"
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/models"
|
||||||
|
status_code: 200
|
||||||
|
return_content: true
|
||||||
|
timeout: 30
|
||||||
|
register: ctx_resize_models_final
|
||||||
|
|
||||||
|
- name: "[ctx-resize] Extract Llama args"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
ctx_resize_llama_args: >-
|
||||||
|
{{ (ctx_resize_models_final.json.data | selectattr('id', 'equalto', 'Meta-Llama-3.1-8B-Instruct-Q4_K_M') | first).status.args }}
|
||||||
|
ctx_resize_llama_status: >-
|
||||||
|
{{ (ctx_resize_models_final.json.data | selectattr('id', 'equalto', 'Meta-Llama-3.1-8B-Instruct-Q4_K_M') | first).status.value }}
|
||||||
|
|
||||||
|
- name: "[ctx-resize] Extract Phi args"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
ctx_resize_phi_args: >-
|
||||||
|
{{ (ctx_resize_models_final.json.data | selectattr('id', 'equalto', 'Phi-3.5-mini-instruct-Q8_0') | first).status.args }}
|
||||||
|
ctx_resize_phi_status: >-
|
||||||
|
{{ (ctx_resize_models_final.json.data | selectattr('id', 'equalto', 'Phi-3.5-mini-instruct-Q8_0') | first).status.value }}
|
||||||
|
|
||||||
|
- name: "[ctx-resize] Extract Qwen args (must be unchanged)"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
ctx_resize_qwen_args: >-
|
||||||
|
{{ (ctx_resize_models_final.json.data | selectattr('id', 'equalto', 'Qwen3.6-35B-A3B-UD-Q4_K_S') | first).status.args }}
|
||||||
|
|
||||||
|
- name: "[ctx-resize] GATE — Llama ctx-size must be 8192"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- "'8192' in ctx_resize_llama_args"
|
||||||
|
- ctx_resize_llama_args[ctx_resize_llama_args.index('--ctx-size') + 1] == '8192'
|
||||||
|
fail_msg: "Llama ctx-size not 8192. Args: {{ ctx_resize_llama_args }}"
|
||||||
|
success_msg: "Llama ctx-size confirmed 8192."
|
||||||
|
|
||||||
|
- name: "[ctx-resize] GATE — Llama flash-attn must be true"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- ctx_resize_llama_args[ctx_resize_llama_args.index('--flash-attn') + 1] == 'true'
|
||||||
|
fail_msg: "Llama flash-attn not true. Args: {{ ctx_resize_llama_args }}"
|
||||||
|
success_msg: "Llama flash-attn confirmed true."
|
||||||
|
|
||||||
|
- name: "[ctx-resize] GATE — Llama loaded successfully"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- ctx_resize_llama_status == 'loaded'
|
||||||
|
fail_msg: "Llama status is '{{ ctx_resize_llama_status }}', expected 'loaded'."
|
||||||
|
success_msg: "Llama status confirmed 'loaded'."
|
||||||
|
|
||||||
|
- name: "[ctx-resize] GATE — Phi ctx-size must be 32768"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- ctx_resize_phi_args[ctx_resize_phi_args.index('--ctx-size') + 1] == '32768'
|
||||||
|
fail_msg: "Phi ctx-size not 32768. Args: {{ ctx_resize_phi_args }}"
|
||||||
|
success_msg: "Phi ctx-size confirmed 32768."
|
||||||
|
|
||||||
|
- name: "[ctx-resize] GATE — Phi flash-attn must be true"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- ctx_resize_phi_args[ctx_resize_phi_args.index('--flash-attn') + 1] == 'true'
|
||||||
|
fail_msg: "Phi flash-attn not true. Args: {{ ctx_resize_phi_args }}"
|
||||||
|
success_msg: "Phi flash-attn confirmed true."
|
||||||
|
|
||||||
|
- name: "[ctx-resize] GATE — Phi loaded successfully"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- ctx_resize_phi_status == 'loaded'
|
||||||
|
fail_msg: "Phi status is '{{ ctx_resize_phi_status }}', expected 'loaded'."
|
||||||
|
success_msg: "Phi status confirmed 'loaded'."
|
||||||
|
|
||||||
|
- name: "[ctx-resize] GATE — Qwen ctx-size UNCHANGED at 65536"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- ctx_resize_qwen_args[ctx_resize_qwen_args.index('--ctx-size') + 1] == '65536'
|
||||||
|
fail_msg: "Qwen ctx-size changed unexpectedly! Args: {{ ctx_resize_qwen_args }}"
|
||||||
|
success_msg: "Qwen ctx-size confirmed UNCHANGED at 65536."
|
||||||
|
|
||||||
|
- name: "[ctx-resize] PASS — summary"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "================================================================"
|
||||||
|
- "PER-MODEL CTX-SIZE DEPLOYMENT — COMPLETE"
|
||||||
|
- ""
|
||||||
|
- " Llama-3.1-8B (Meta-Llama-3.1-8B-Instruct-Q4_K_M):"
|
||||||
|
- " status: {{ ctx_resize_llama_status }}"
|
||||||
|
- " args: {{ ctx_resize_llama_args }}"
|
||||||
|
- ""
|
||||||
|
- " Phi-3.5-mini (Phi-3.5-mini-instruct-Q8_0):"
|
||||||
|
- " status: {{ ctx_resize_phi_status }}"
|
||||||
|
- " args: {{ ctx_resize_phi_args }}"
|
||||||
|
- ""
|
||||||
|
- " Qwen3.6-35B-A3B-UD-Q4_K_S: UNCHANGED (ctx-size 65536, args: {{ ctx_resize_qwen_args }})"
|
||||||
|
- ""
|
||||||
|
- " ACTION NEEDED: /home/hermes/.hermes/config.yaml custom_providers"
|
||||||
|
- " context_length: 65536 for both Meta-Llama-3.1-8B-Instruct-4bit and"
|
||||||
|
- " Phi-3.5-mini-instruct-8bit is now STALE (actual: 8192 / 32768)."
|
||||||
|
- " Flag to JARVIS/Maria Hill for correction — NOT done by this playbook."
|
||||||
|
- "================================================================"
|
||||||
38
ansible/playbooks/day2_qwen38_ctx128k.yml
Normal file
38
ansible/playbooks/day2_qwen38_ctx128k.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Playbook: day2_qwen38_ctx128k.yml
|
||||||
|
# Purpose: Bump Qwen3.8-27B-Q4_K_M ctx-size from 32768 to 131072 (128K)
|
||||||
|
# on astro-orbiter's production router (port 8002).
|
||||||
|
#
|
||||||
|
# What this playbook does:
|
||||||
|
# 1. Renders the updated llama-server-router-preset.ini.j2 (now with
|
||||||
|
# llm_router_qwen38_ctx_size: 131072) to /opt/llama-server-router-preset.ini.
|
||||||
|
# 2. Restarts llama-server-router.service.
|
||||||
|
# 3. Verifies the router loads Qwen3.8-27B at ctx=131072 in status.args.
|
||||||
|
#
|
||||||
|
# Context:
|
||||||
|
# - Empirical VRAM test (t_4455a44c): 131072 ctx = 20,282 MiB Qwen3.8
|
||||||
|
# + 558 MiB nomic-embed = ~20.8GB total; ~3.2GB headroom on 24GB RTX 3090.
|
||||||
|
# Co-resident with nomic-embed: comfortably fits.
|
||||||
|
# - Ryan approved this deployment.
|
||||||
|
# - Semaphore SSH gap for astro-orbiter still applies (t_730f9584 / t_33acbb2e);
|
||||||
|
# running direct CLI Ansible per standing exception.
|
||||||
|
#
|
||||||
|
# Run:
|
||||||
|
# cd /home/hermes/git/homelab/ansible
|
||||||
|
# env -u ANSIBLE_VAULT_PASSWORD_FILE ansible-playbook \
|
||||||
|
# -i inventory.yml \
|
||||||
|
# playbooks/day2_qwen38_ctx128k.yml
|
||||||
|
#
|
||||||
|
# Task reference: t_441470b9 — War Machine, 2026-08-16
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
- name: Bump Qwen3.8-27B ctx-size to 131072 on astro-orbiter
|
||||||
|
hosts: astro-orbiter
|
||||||
|
become: true
|
||||||
|
vars:
|
||||||
|
llm_router_preset_enabled: true
|
||||||
|
llm_router_qwen38_ctx_size: 131072
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: llm-inference-multimodel
|
||||||
|
tags: [preset, systemd, verify]
|
||||||
65
ansible/playbooks/day2_qwen38_ctx128k_rollback.yml
Normal file
65
ansible/playbooks/day2_qwen38_ctx128k_rollback.yml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Playbook: day2_qwen38_ctx128k_rollback.yml
|
||||||
|
# Purpose: Roll back Qwen3.8-27B-Q4_K_M ctx-size from 131072 back to 65536
|
||||||
|
# on astro-orbiter's production router (port 8002).
|
||||||
|
#
|
||||||
|
# What this playbook does:
|
||||||
|
# 1. Renders the updated llama-server-router-preset.ini.j2 (now with
|
||||||
|
# llm_router_qwen38_ctx_size: 65536) to
|
||||||
|
# /opt/llama-server-router-preset.ini.
|
||||||
|
# 2. Restarts llama-server-router.service.
|
||||||
|
# 3. Verifies the router loads Qwen3.8-27B at ctx=65536 in status.args.
|
||||||
|
#
|
||||||
|
# Context:
|
||||||
|
# - t_441470b9 (2026-08-16): ctx-size bumped 32768 -> 131072. Verified VRAM
|
||||||
|
# at 131072 ctx with only Qwen3.8 + nomic-embed co-resident: ~20,282 MiB
|
||||||
|
# + 558 MiB = ~20.8 GB on 24 GB RTX 3090. Comfortably safe.
|
||||||
|
# - t_72646029 (2026-08-17): Phi-3.5mini moved to GPU (n-gpu-layers=99)
|
||||||
|
# to enable concurrent residency with CPU-offloaded Coder-14B and
|
||||||
|
# Llama-3.1-8B. This added ~2GB CUDA context buffers for Phi + shifted
|
||||||
|
# Phi's model weights onto the GPU (~3.8GB).
|
||||||
|
# - NEW steady-state VRAM: Qwen3.8 @ 131072 ctx (~20,282 MiB) + nomic-embed
|
||||||
|
# (~558 MiB) + Llama CUDA ctx (~1,706 MiB) + Coder CUDA ctx (~1,390 MiB)
|
||||||
|
# = ~24,004 MiB. Adding Phi-3.5 (~3,800 MiB weights + ~1.4 GB CUDA ctx)
|
||||||
|
# pushes total to ~29,000+ MiB — exceeding the 24,576 MiB RTX 3090 limit.
|
||||||
|
# Qwen3.8-27B-131072 now fails to load (HTTP 500, OOM before llama.cpp
|
||||||
|
# reaches the model-loading phase).
|
||||||
|
# - FIX: reduce Qwen3.8 ctx-size 131072 -> 65536. This reduces KV cache
|
||||||
|
# from ~6GB to ~3GB, freeing ~3GB of VRAM. New estimated steady-state:
|
||||||
|
# Qwen3.8 @ 65536 ctx (~17,068 MiB) + nomic (~558) + Llama ctx (~1,706)
|
||||||
|
# + Coder ctx (~1,390) + Phi-3.5 (~3,800 + ~1,400 CUDA ctx) = ~25,922 MiB.
|
||||||
|
# Still over 24,576 — see "Phase 2" below for the secondary fix.
|
||||||
|
#
|
||||||
|
# IMPORTANT: Rolling back ctx-size alone may NOT be sufficient. The
|
||||||
|
# hardware reference (astro-orbiter-hardware.md line 166, t_72646029)
|
||||||
|
# states steady-state ~24,004 MiB WITHOUT Phi on GPU. Adding Phi-3.5 back
|
||||||
|
# to GPU tips it over. This playbook handles the context rollback; if Qwen3.8
|
||||||
|
# still fails to load after Phase R, Wong should escalate to Ryan for a
|
||||||
|
# decision on either (a) offloading Phi-3.5mini to CPU (n-gpu-layers=0),
|
||||||
|
# or (b) adding a second GPU. Document the Phase 2 finding as a separate
|
||||||
|
# follow-up task if needed.
|
||||||
|
#
|
||||||
|
# The 64K floor from the 2026-08-12 cutover validation (t_cd0d5388, Gate 1)
|
||||||
|
# still applies — ctx-size=65536 satisfies it.
|
||||||
|
#
|
||||||
|
# Run:
|
||||||
|
# cd /home/hermes/git/homelab/ansible
|
||||||
|
# env -u ANSIBLE_VAULT_PASSWORD_FILE ansible-playbook \
|
||||||
|
# -i inventory.yml \
|
||||||
|
# playbooks/day2_qwen38_ctx128k_rollback.yml
|
||||||
|
#
|
||||||
|
# Task reference: t_c9fed26c — War Machine benchmark, 2026-08-18
|
||||||
|
# Root cause: t_72646029 CPU-offload deployment added Phi-3.5 to GPU,
|
||||||
|
# shifting total VRAM past the 24,576 MiB ceiling when Qwen3.8 runs at 128K.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
- name: Roll back Qwen3.8-27B ctx-size to 65536 on astro-orbiter
|
||||||
|
hosts: astro-orbiter
|
||||||
|
become: true
|
||||||
|
vars:
|
||||||
|
llm_router_preset_enabled: true
|
||||||
|
llm_router_qwen38_ctx_size: 65536
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: llm-inference-multimodel
|
||||||
|
tags: [preset, systemd, verify]
|
||||||
36
ansible/playbooks/day2_swap_qwen38.yml
Normal file
36
ansible/playbooks/day2_swap_qwen38.yml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Playbook: day2_swap_qwen38.yml
|
||||||
|
# Purpose: Swap the primary production model on astro-orbiter router from
|
||||||
|
# Qwen3.6-35B-A3B-UD-Q4_K_S to Qwen3.8-27B-Q4_K_M.
|
||||||
|
# This is a GitOps-encoded record of the swap performed 2026-08-16
|
||||||
|
# per Ryan's direction (kanban task t_f5f7e9ad).
|
||||||
|
#
|
||||||
|
# What this playbook does:
|
||||||
|
# 1. Renders the updated llama-server-router-preset.ini.j2 to
|
||||||
|
# /opt/llama-server-router-preset.ini on astro-orbiter.
|
||||||
|
# 2. Reloads the llama-server-router service (SIGHUP / restart as needed).
|
||||||
|
# 3. Verifies the new model ID appears in /v1/models.
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# - Qwen3.8-27B-Q4_K_M.gguf must be present in /opt/models on astro-orbiter.
|
||||||
|
# (Downloaded out-of-band via wget during the swap task.)
|
||||||
|
# - roles/llm-inference-multimodel/defaults/main.yml updated to reference
|
||||||
|
# Qwen3.8-27B-Q4_K_M (done in this same commit).
|
||||||
|
#
|
||||||
|
# Run:
|
||||||
|
# env -u ANSIBLE_VAULT_PASSWORD_FILE ansible-playbook \
|
||||||
|
# -i inventory.yml \
|
||||||
|
# playbooks/day2_swap_qwen38.yml
|
||||||
|
#
|
||||||
|
# Task reference: t_f5f7e9ad — War Machine, 2026-08-16
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
- name: Swap primary model to Qwen3.8-27B-Q4_K_M on astro-orbiter
|
||||||
|
hosts: astro-orbiter
|
||||||
|
become: true
|
||||||
|
vars:
|
||||||
|
llm_router_preset_enabled: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: llm-inference-multimodel
|
||||||
|
tags: [preset, systemd, verify]
|
||||||
22
ansible/playbooks/day3_deploy_qwen38_ctx131k.yml
Normal file
22
ansible/playbooks/day3_deploy_qwen38_ctx131k.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
# Playbook: day3_deploy_qwen38_ctx131k.yml
|
||||||
|
# Purpose: Deploy Qwen3.8-27B-Q4_K_M ctx-size 65536 -> 131072 to astro-orbiter
|
||||||
|
# via llama-swap config re-render + restart.
|
||||||
|
#
|
||||||
|
# The git change to defaults/main.yml (line 235: ctx_size: 131072) is already staged.
|
||||||
|
# This playbook renders /etc/llama-swap/config.yaml from the updated defaults
|
||||||
|
# and restarts llama-swap to load the new ctx-size.
|
||||||
|
#
|
||||||
|
# Run:
|
||||||
|
# cd /home/hermes/git/homelab/ansible
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day3_deploy_qwen38_ctx131k.yml
|
||||||
|
#
|
||||||
|
- name: Deploy Qwen3.8 ctx-size 131072 to astro-orbiter
|
||||||
|
hosts: astro-orbiter
|
||||||
|
become: true
|
||||||
|
vars:
|
||||||
|
llm_swapmode_enabled: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: llm-inference-multimodel
|
||||||
|
tags: [swapmode_config, swapmode_systemd, swapmode_verify]
|
||||||
0
infra-config/playbooks/del_dns_entry.yml → ansible/playbooks/del_dns_entry.yml
Normal file → Executable file
0
infra-config/playbooks/del_dns_entry.yml → ansible/playbooks/del_dns_entry.yml
Normal file → Executable file
5
ansible/playbooks/deploy_n8n_server.yml
Executable file
5
ansible/playbooks/deploy_n8n_server.yml
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
- name: 1. Deploy n8n server
|
||||||
|
hosts: n8n_server
|
||||||
|
gather_facts: true
|
||||||
|
roles:
|
||||||
|
- role: n8n
|
||||||
5
ansible/playbooks/deploy_ntp_servers.yml
Executable file
5
ansible/playbooks/deploy_ntp_servers.yml
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
- name: 1. Deploy NTP servers
|
||||||
|
hosts: ntp_servers
|
||||||
|
gather_facts: true
|
||||||
|
roles:
|
||||||
|
- role: ntp-server
|
||||||
29
ansible/playbooks/deploy_step_ca.yml
Normal file
29
ansible/playbooks/deploy_step_ca.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/deploy_step_ca.yml
|
||||||
|
# DESCRIPTION: Deploys Smallstep step-ca SSH Certificate Authority on turnstile.
|
||||||
|
# Installs Docker and configures step-ca with SSH certificate support.
|
||||||
|
#
|
||||||
|
# PREREQUISITES:
|
||||||
|
# - VM provisioned via Terraform
|
||||||
|
# - DNS record for turnstile.local.mk-labs.cloud on monorail
|
||||||
|
# - Authentik OIDC application created (for post-init provisioner setup)
|
||||||
|
#
|
||||||
|
# USAGE:
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/deploy_step_ca.yml
|
||||||
|
#
|
||||||
|
# POST-DEPLOY:
|
||||||
|
# 1. Note the CA fingerprint from the init output
|
||||||
|
# 2. Add the OIDC provisioner (see docs/guides/step-ca-setup.md)
|
||||||
|
# 3. Deploy Traefik route via update_traefik_routes.yml
|
||||||
|
# 4. Bootstrap client workstations with: step ca bootstrap
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Deploy step-ca SSH Certificate Authority
|
||||||
|
hosts: step_ca_server
|
||||||
|
become: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- common
|
||||||
|
- docker-host
|
||||||
|
- step-ca
|
||||||
7
ansible/playbooks/enroll_step_ca_client.yml
Normal file
7
ansible/playbooks/enroll_step_ca_client.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
- name: Enroll host as step-ca SSH client
|
||||||
|
hosts: "{{ target | default('all') }}"
|
||||||
|
become: true
|
||||||
|
tasks:
|
||||||
|
- name: Include step-ca client enrollment
|
||||||
|
ansible.builtin.include_tasks: roles/common/tasks/step_ca_client.yml
|
||||||
67
ansible/playbooks/install_monitoring.yml
Executable file
67
ansible/playbooks/install_monitoring.yml
Executable file
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
- name: Master playbook to install and configure prometheus
|
||||||
|
hosts: prometheus_server
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Install prerequisite software
|
||||||
|
ansible.builtin.dnf:
|
||||||
|
name:
|
||||||
|
- tar
|
||||||
|
- python3-dnf
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Permit prometheus metrics in default zone for dns service
|
||||||
|
ansible.posix.firewalld:
|
||||||
|
port: 9100/tcp
|
||||||
|
permanent: true
|
||||||
|
immediate: true
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
- name: Permit traffic in default zone for dns service
|
||||||
|
ansible.posix.firewalld:
|
||||||
|
port: 9090/tcp
|
||||||
|
permanent: true
|
||||||
|
immediate: true
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
- name: Install prometheus via role
|
||||||
|
ansible.builtin.import_role:
|
||||||
|
name: prometheus.prometheus.prometheus
|
||||||
|
vars:
|
||||||
|
prometheus_targets:
|
||||||
|
node:
|
||||||
|
- targets:
|
||||||
|
- localhost:9100
|
||||||
|
labels:
|
||||||
|
env: mk-labs
|
||||||
|
|
||||||
|
- name: Permit grafana in default zone for dns service
|
||||||
|
ansible.posix.firewalld:
|
||||||
|
port: 3000/tcp
|
||||||
|
permanent: true
|
||||||
|
immediate: true
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
- name: Install grafana via role
|
||||||
|
ansible.builtin.import_role:
|
||||||
|
name: grafana.grafana.grafana
|
||||||
|
|
||||||
|
# - name: Create/Update Data sources
|
||||||
|
# grafana.grafana.datasource:
|
||||||
|
# dataSource: |
|
||||||
|
# {
|
||||||
|
# "name": "Prometheus",
|
||||||
|
# "type": "prometheus",
|
||||||
|
# "access": "proxy",
|
||||||
|
# "url": "http://localhost:9090",
|
||||||
|
# "jsonData": {
|
||||||
|
# "httpMethod": "POST",
|
||||||
|
# "manageAlerts": true,
|
||||||
|
# "prometheusType": "Prometheus",
|
||||||
|
# "cacheLevel": "High"
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# grafana_url: "{{ grafana_url }}"
|
||||||
|
# grafana_api_key: "{{ grafana_api_key }}"
|
||||||
|
# state: present
|
||||||
15
ansible/playbooks/install_talosctl.yml
Normal file
15
ansible/playbooks/install_talosctl.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: playbooks/install_talosctl.yml
|
||||||
|
# DESCRIPTION: Install talosctl on city-hall for managing the fastpass cluster.
|
||||||
|
# USAGE:
|
||||||
|
# cd ansible
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/install_talosctl.yml
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Install talosctl
|
||||||
|
hosts: talos_control
|
||||||
|
become: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- talosctl
|
||||||
0
infra-config/playbooks/monitoring.yml → ansible/playbooks/monitoring.yml
Normal file → Executable file
0
infra-config/playbooks/monitoring.yml → ansible/playbooks/monitoring.yml
Normal file → Executable file
16
ansible/playbooks/node_explorer.yml
Executable file
16
ansible/playbooks/node_explorer.yml
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
- name: Master playbook to install and configure prometheus node explorer
|
||||||
|
hosts: prometheus_server
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Permit prometheus metrics in default zone for dns service
|
||||||
|
ansible.posix.firewalld:
|
||||||
|
port: 9100/tcp
|
||||||
|
permanent: true
|
||||||
|
immediate: true
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
- name: Install prometheus node exporter via role
|
||||||
|
ansible.builtin.import_role:
|
||||||
|
name: prometheus.prometheus.node_exporter
|
||||||
31
ansible/playbooks/populate_cloud_init.yml
Executable file
31
ansible/playbooks/populate_cloud_init.yml
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
- name: Create VM ID from IP address for Proxmox hosts
|
||||||
|
hosts: all
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Modify Proxmox Ubuntu VM
|
||||||
|
delegate_to: "localhost"
|
||||||
|
when: platform is defined and platform == "proxmox"
|
||||||
|
block:
|
||||||
|
- name: Update cloud-init file
|
||||||
|
community.proxmox.proxmox_kvm:
|
||||||
|
api_user: "{{ proxmox_user }}"
|
||||||
|
api_password: "{{ proxmox_password }}"
|
||||||
|
api_host: "{{ proxmox_host }}"
|
||||||
|
node: "{{ proxmox_clone_node }}"
|
||||||
|
vmid: "{{ vm_id }}"
|
||||||
|
ipconfig:
|
||||||
|
ipconfig0: "ip=dhcp"
|
||||||
|
update: true
|
||||||
|
|
||||||
|
# - name: Add VM to HA group
|
||||||
|
# community.proxmox.proxmox_cluster_ha_resources:
|
||||||
|
# api_user: "{{ proxmox_user }}"
|
||||||
|
# api_password: "{{ proxmox_password }}"
|
||||||
|
# api_host: "{{ proxmox_host }}"
|
||||||
|
# name: vm:"{{ vm_id }}"
|
||||||
|
# state: "present"
|
||||||
|
# group: "{{ ha_group }}"
|
||||||
|
# max_relocate: 2
|
||||||
|
# max_restart: 2
|
||||||
143
ansible/playbooks/prometheus_grafana_server.yml
Executable file
143
ansible/playbooks/prometheus_grafana_server.yml
Executable file
@@ -0,0 +1,143 @@
|
|||||||
|
---
|
||||||
|
- name: Install Prometheus and Grafana on control node
|
||||||
|
hosts: prometheus_server
|
||||||
|
become: true
|
||||||
|
# vars_files:
|
||||||
|
# - vars.yml
|
||||||
|
tasks:
|
||||||
|
- name: Install prerequisite software
|
||||||
|
ansible.builtin.dnf:
|
||||||
|
name:
|
||||||
|
- tar
|
||||||
|
- wget
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Download Prometheus
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "{{ prometheus_installer_download_url }}"
|
||||||
|
dest: "/tmp/{{ prometheus_installer_file }}"
|
||||||
|
|
||||||
|
- name: Extract Prometheus
|
||||||
|
ansible.builtin.unarchive:
|
||||||
|
src: "/tmp/{{ prometheus_installer_file }}"
|
||||||
|
dest: "/usr/local/bin/"
|
||||||
|
remote_src: true
|
||||||
|
|
||||||
|
- name: Create Prometheus user
|
||||||
|
user:
|
||||||
|
name: prometheus
|
||||||
|
shell: /bin/false
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Create Prometheus directories
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: directory
|
||||||
|
owner: prometheus
|
||||||
|
group: prometheus
|
||||||
|
with_items:
|
||||||
|
- /etc/prometheus
|
||||||
|
- /var/lib/prometheus
|
||||||
|
|
||||||
|
- name: Move Prometheus binaries
|
||||||
|
ansible.builtin.command: "mv /usr/local/bin/prometheus-{{ prometheus_version }}.linux-amd64/prometheus /usr/local/bin/prometheus"
|
||||||
|
|
||||||
|
- name: Move Prometheus tool
|
||||||
|
ansible.builtin.command: "mv /usr/local/bin/prometheus-{{ prometheus_version }}.linux-amd64/promtool /usr/local/bin/promtool"
|
||||||
|
|
||||||
|
- name: Create Prometheus configuration file
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: "/etc/prometheus/prometheus.yml"
|
||||||
|
content: |
|
||||||
|
global:
|
||||||
|
scrape_interval: 15s
|
||||||
|
evaluation_interval: 15s
|
||||||
|
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: 'prometheus'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['localhost:9090']
|
||||||
|
|
||||||
|
- job_name: 'nodes'
|
||||||
|
static_configs:
|
||||||
|
- targets:
|
||||||
|
{% for ip in prometheus_nodes %}
|
||||||
|
- '{{ ip }}:9100'
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
- name: Create Prometheus service file
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: "/etc/systemd/system/prometheus.service"
|
||||||
|
content: |
|
||||||
|
[Unit]
|
||||||
|
Description=Prometheus
|
||||||
|
Wants=network-online.target
|
||||||
|
After=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=prometheus
|
||||||
|
Group=prometheus
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/local/bin/prometheus \
|
||||||
|
--config.file=/etc/prometheus/prometheus.yml \
|
||||||
|
--storage.tsdb.path=/var/lib/prometheus/ \
|
||||||
|
--web.console.templates=/etc/prometheus/consoles \
|
||||||
|
--web.console.libraries=/etc/prometheus/console_libraries
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
register: prometheus_service_status
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Check if Prometheus service is running
|
||||||
|
ansible.builtin.command: systemctl is-active prometheus
|
||||||
|
register: prometheus_status
|
||||||
|
changed_when: false
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Permit prometheus endpoint in default zone for dns service
|
||||||
|
firewalld:
|
||||||
|
port: 9090/tcp
|
||||||
|
permanent: true
|
||||||
|
immediate: true
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
- name: Permit prometheus metrics in default zone for dns service
|
||||||
|
firewalld:
|
||||||
|
port: 9100/tcp
|
||||||
|
permanent: true
|
||||||
|
immediate: true
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
- name: Permit grafana traffic in default zone for dns service
|
||||||
|
firewalld:
|
||||||
|
port: 3000/tcp
|
||||||
|
permanent: true
|
||||||
|
immediate: true
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
- name: Manage Prometheus service state
|
||||||
|
systemd:
|
||||||
|
name: prometheus
|
||||||
|
state: "{{ 'restarted' if prometheus_status.rc == 0 else 'started' }}"
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
- name: Add repository
|
||||||
|
ansible.builtin.yum_repository:
|
||||||
|
name: grafana
|
||||||
|
description: Grafana OSS repo
|
||||||
|
baseurl: https://rpm.grafana.com
|
||||||
|
gpgkey: https://rpm.grafana.com/gpg.key
|
||||||
|
|
||||||
|
- name: Install Grafana
|
||||||
|
ansible.builtin.dnf:
|
||||||
|
name:
|
||||||
|
- grafana
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Start Grafana service
|
||||||
|
systemd:
|
||||||
|
name: grafana-server
|
||||||
|
state: started
|
||||||
|
enabled: true
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user