Compare commits
705 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
504800a7db | ||
|
|
97723b1f96 | ||
|
|
5911041777 | ||
|
|
fcd0481b09 | ||
|
|
cc350165dd | ||
|
|
db7e1b8a97 | ||
|
|
188f773081 | ||
|
|
6271f81cff | ||
|
|
4bfc3bf412 | ||
|
|
d5a92104d1 | ||
|
|
ddd4f805a5 | ||
|
|
a1c1b19482 | ||
|
|
426bc15065 | ||
|
|
df5febf6e7 | ||
|
|
9e09e46700 | ||
|
|
ba0e9bb1d2 | ||
|
|
19da92b510 | ||
|
|
beb1f4e172 | ||
|
|
fb3d1ef399 | ||
|
|
d7c37d9dd6 | ||
|
|
24de404fbc | ||
|
|
8565d175f9 | ||
|
|
8d9e151030 | ||
|
|
758c5347fb | ||
|
|
1e54eee631 | ||
|
|
448760a2fe | ||
|
|
e44b22f7d1 | ||
|
|
30379c3f52 | ||
|
|
8729d60c1c | ||
|
|
effcdb8723 | ||
|
|
1354947434 | ||
|
|
864ce0088e | ||
|
|
93ac0b77c9 | ||
|
|
ea327e6b37 | ||
|
|
f7b85ab941 | ||
|
|
ce9933da85 | ||
|
|
0662f0dab4 | ||
|
|
0669fda1fd | ||
|
|
b88fcb6625 | ||
|
|
69be24cd5f | ||
|
|
35273cc87f | ||
|
|
5af73e9bf7 | ||
|
|
128ccb4330 | ||
|
|
07df622b59 | ||
|
|
5d22260589 | ||
|
|
39985ebdd1 | ||
|
|
92ec06c694 | ||
|
|
04137e887e | ||
|
|
e940f00c01 | ||
|
|
1c72a80d9a | ||
|
|
b9f8370097 | ||
|
|
1c636ea127 | ||
|
|
e1169e7ea6 | ||
|
|
5975dbcb07 | ||
|
|
08a419ec7a | ||
|
|
d417168805 | ||
|
|
ccb2966c4c | ||
|
|
b7cdbd3d41 | ||
|
|
ae440c9edf | ||
|
|
b6cd099117 | ||
|
|
98f57f2dba | ||
|
|
735fa4aa31 | ||
|
|
c7108bb3f7 | ||
|
|
98b3fc03b8 | ||
|
|
92da2fe082 | ||
|
|
bfab3a26bc | ||
|
|
b35b33e798 | ||
|
|
db5c2a379e | ||
|
|
3675ad2539 | ||
|
|
27c71b8ec0 | ||
|
|
0058c7749c | ||
|
|
f882dcabf7 | ||
|
|
c8f4a7c742 | ||
|
|
ed366c5ab2 | ||
|
|
2738da2d39 | ||
|
|
9f15ca2d90 | ||
|
|
e4f5407c70 | ||
|
|
951f82b428 | ||
|
|
f8685ad7aa | ||
|
|
c59d527664 | ||
|
|
77423e7bb1 | ||
|
|
ba12675267 | ||
|
|
def3ccfaee | ||
|
|
bbc68f9484 | ||
|
|
93685d92a4 | ||
|
|
b2bf613895 | ||
|
|
80ced6b782 | ||
|
|
47dfb89c52 | ||
|
|
064e3ff605 | ||
|
|
fb27803ab0 | ||
|
|
5e32b39f25 | ||
|
|
b9888d6f86 | ||
|
|
96a796ebde | ||
|
|
996e73d5d8 | ||
|
|
5c969a8caf | ||
|
|
68faab8196 | ||
|
|
b3693099dc | ||
|
|
9bb9ac3dec | ||
|
|
a57378e780 | ||
|
|
41f631b65b | ||
|
|
2db668f5cc | ||
|
|
aacf606999 | ||
|
|
e338f7cfe3 | ||
|
|
758030733c | ||
|
|
ad78f9e075 | ||
|
|
3468e8c8ae | ||
|
|
13d39a28ce | ||
|
|
8809fc949b | ||
|
|
860805ba82 | ||
|
|
1e0b024609 | ||
|
|
8486d47d17 | ||
|
|
407365888a | ||
|
|
2ad1db0c64 | ||
|
|
83a0576ca4 | ||
|
|
0b100b8fc8 | ||
|
|
2b12138c41 | ||
|
|
97aa40f7a8 | ||
|
|
b2f34cec19 | ||
|
|
3bc9586b0c | ||
|
|
4297c65f87 | ||
|
|
62b0f034e7 | ||
|
|
6ffd8aa320 | ||
|
|
d53ddd611b | ||
|
|
080a001118 | ||
|
|
5a77791f9d | ||
|
|
ab9c253310 | ||
|
|
35596ddcbc | ||
|
|
0cacac82ee | ||
|
|
780997a568 | ||
|
|
d2d60c0607 | ||
|
|
d4d8d00d01 | ||
|
|
cb52d9c84e | ||
|
|
db61e57893 | ||
|
|
52cf9086a5 | ||
|
|
db7590df1a | ||
|
|
ee03f3d584 | ||
|
|
2577f3a786 | ||
|
|
826a1714c3 | ||
|
|
d0e0c2ff8b | ||
|
|
fb407e9076 | ||
|
|
85c60670dc | ||
|
|
f2f36c67f6 | ||
|
|
281934cf34 | ||
|
|
00d72f18cf | ||
|
|
b36afdc924 | ||
|
|
4ed45e4031 | ||
|
|
cf0258204f | ||
|
|
3bd560add8 | ||
|
|
9e51a8d9d2 | ||
|
|
f59c6699f6 | ||
|
|
39732fa861 | ||
|
|
80f5eeacdd | ||
|
|
f56e3eb784 | ||
|
|
c3dcd8937f | ||
|
|
b1da374df2 | ||
|
|
dc1da0a738 | ||
|
|
1946e8f053 | ||
|
|
4623858849 | ||
|
|
9c5891f1b6 | ||
|
|
d5538c1ca3 | ||
|
|
90f15b8d55 | ||
|
|
4e27e8d3dd | ||
|
|
150cb772fe | ||
|
|
e494d7bb22 | ||
|
|
9774bb46ce | ||
|
|
84c0c45da9 | ||
|
|
46e3883f19 | ||
|
|
3a89a676cd | ||
|
|
0885333b11 | ||
|
|
c287641363 | ||
|
|
de9646d096 | ||
|
|
dd2520d675 | ||
|
|
3a5914827b | ||
|
|
cf55e96241 | ||
|
|
bd29d15814 | ||
|
|
d3911e2a4c | ||
|
|
eb591731ef | ||
|
|
ae11419045 | ||
|
|
43bbd42d3c | ||
|
|
d4a231585a | ||
|
|
977b79ecee | ||
|
|
5202d0add9 | ||
|
|
ebf555e1fb | ||
|
|
f411c4f439 | ||
|
|
216d8d24b8 | ||
|
|
cb2b256934 | ||
|
|
2f96fdd135 | ||
|
|
e40e2550a6 | ||
|
|
bfda5d9011 | ||
|
|
62a80c46a8 | ||
|
|
ceec1055e0 | ||
|
|
540bba4544 | ||
|
|
44c248e6c2 | ||
|
|
3a62fd49e6 | ||
|
|
a2007a4728 | ||
|
|
316c3808f7 | ||
|
|
928d880f0e | ||
|
|
c6930e3ea8 | ||
|
|
db77e9428f | ||
|
|
80f5c96af3 | ||
|
|
de1b346da0 | ||
|
|
96818cacf0 | ||
|
|
0c0c848597 | ||
|
|
23077821f6 | ||
|
|
87fd09ca8b | ||
|
|
0bcc59a1e9 | ||
|
|
87727c71f7 | ||
|
|
23c0ca456f | ||
|
|
d9d25ff4e7 | ||
|
|
6f8a7fdbe3 | ||
|
|
640fd8045d | ||
|
|
4230162294 | ||
|
|
355678274d | ||
|
|
ecf5304a14 | ||
|
|
94dd07e1e6 | ||
|
|
1b707e07f2 | ||
|
|
564884a774 | ||
|
|
7401fd7050 | ||
|
|
42e9dc0da7 | ||
|
|
a5308ea28e | ||
|
|
27bf7b4a9a | ||
|
|
a57c937aaa | ||
|
|
4a95cfd1c4 | ||
|
|
cd8943144b | ||
|
|
8400509358 | ||
|
|
d971131198 | ||
|
|
5729a06348 | ||
|
|
6eba5d4d96 | ||
|
|
2cc2d2cc37 | ||
|
|
b374351154 | ||
|
|
d59d23e308 | ||
|
|
3d1501e8fd | ||
|
|
c854c29016 | ||
|
|
33d8f8e5e7 | ||
|
|
653acbf62c | ||
|
|
93e7457e0d | ||
|
|
93e241e8f3 | ||
|
|
43da786016 | ||
|
|
ea6d86e6c4 | ||
|
|
fe73e90b7b | ||
|
|
f68c7fb188 | ||
|
|
ad40d42dc4 | ||
|
|
cdeb65e2fb | ||
|
|
271d524687 | ||
|
|
bc6b5bc4be | ||
|
|
e2f5ee661a | ||
|
|
6b2deaeced | ||
|
|
13afc52617 | ||
|
|
e7f54c5867 | ||
|
|
4ebcdd2b8f | ||
|
|
07730ccd33 | ||
|
|
0ad7ae2837 | ||
|
|
8fb91a1f8c | ||
|
|
3b0a84969b | ||
|
|
81c0dce5a3 | ||
|
|
816fedb78d | ||
|
|
484efdaf75 | ||
|
|
ea61a540cd | ||
|
|
0b24d3d892 | ||
|
|
2af8891f70 | ||
|
|
4e39021b6f | ||
|
|
2cd5fce62d | ||
|
|
ade307bc03 | ||
|
|
c8be4ef8e2 | ||
|
|
816214361d | ||
|
|
d1970ca85b | ||
|
|
8001694a4c | ||
|
|
10e258739f | ||
|
|
f3fdf03661 | ||
|
|
44814f759c | ||
|
|
4f5caa5ed2 | ||
|
|
aa7f04bf1b | ||
|
|
aaf1ea52b7 | ||
|
|
7990cfb078 | ||
|
|
675a5f8687 | ||
|
|
a25ee66150 | ||
|
|
13c27b00d3 | ||
|
|
867af61875 | ||
|
|
8f4fa065f9 | ||
|
|
7b5f5ca6bb | ||
|
|
a176e9452f | ||
|
|
cb815ede60 | ||
|
|
d773f4e62a | ||
|
|
dbe66596f9 | ||
|
|
ec4e2a8e16 | ||
|
|
7e7f68923d | ||
|
|
edb5220228 | ||
|
|
ac373d2376 | ||
|
|
16919cc1d9 | ||
|
|
7a155407f6 | ||
|
|
9e8234bb45 | ||
|
|
c4dcd34ce9 | ||
|
|
18332bdbf1 | ||
|
|
f1a7bceef2 | ||
|
|
eac2ace80b | ||
|
|
174ba6cf0f | ||
|
|
658c9347f3 | ||
|
|
7b3ef2ade5 | ||
|
|
2a62b628cf | ||
|
|
d8c07abd68 | ||
|
|
8d486c5838 | ||
|
|
eb91934d70 | ||
|
|
01654765e8 | ||
|
|
91c6bbcd78 | ||
|
|
b2e2e3be35 | ||
|
|
9628dead07 | ||
|
|
4c504870e0 | ||
|
|
3d687a6c2d | ||
|
|
5d56d95fda | ||
|
|
5e5228ff12 | ||
|
|
72ba57052a | ||
|
|
ed2f7f1236 | ||
|
|
893925436d | ||
|
|
96c4696417 | ||
|
|
e7659a5f99 | ||
|
|
53c9c3cf8d | ||
|
|
f60312febf | ||
|
|
bd79a27e4d | ||
|
|
7505baf3a1 | ||
|
|
4f95c5a72c | ||
|
|
85a4b1f881 | ||
|
|
33c6142365 | ||
|
|
10e874039f | ||
|
|
060ee2dd96 | ||
|
|
43d1182b4b | ||
|
|
d53da57f63 | ||
|
|
028b4b7ea7 | ||
|
|
4cb0230878 | ||
|
|
2fe8df3cbb | ||
|
|
64d67e3b00 | ||
|
|
83ff99a130 | ||
|
|
52faeb6f60 | ||
|
|
f797b5ed97 | ||
|
|
e143038df8 | ||
|
|
074082d1f1 | ||
|
|
d647983003 | ||
|
|
1d9808a92a | ||
|
|
a44eed5001 | ||
|
|
f2407afc9f | ||
|
|
99cf1b1671 | ||
|
|
aaf829898b | ||
|
|
8481cf66e3 | ||
|
|
bb150379a2 | ||
|
|
cc811e5a56 | ||
|
|
e8c5a4724a | ||
|
|
9ca4c7315b | ||
|
|
ffce5d968d | ||
|
|
656f0b7d82 | ||
|
|
ef0b455b05 | ||
|
|
679a9e839b | ||
|
|
53c8a48244 | ||
|
|
3f37914b3c | ||
|
|
b0ba9bd83d | ||
|
|
a3dbf4023c | ||
|
|
053c97b7a8 | ||
|
|
1fc8de85a3 | ||
|
|
5e1a0733e4 | ||
|
|
bfe26b46a6 | ||
|
|
4c999daacd | ||
|
|
663652f45e | ||
|
|
10bb8fa10a | ||
|
|
a0bae06ff7 | ||
|
|
0d7851ed9d | ||
|
|
664d5db5eb | ||
|
|
a1cc15a604 | ||
|
|
24ba840be7 | ||
|
|
a9e583a693 | ||
|
|
3a3ff474cb | ||
|
|
cc00789d35 | ||
|
|
689f11a573 | ||
|
|
c481a1b6a2 | ||
|
|
ae90ad1fb7 | ||
|
|
56d9725c39 | ||
|
|
1c69bfaf2c | ||
|
|
5e37f82b2f | ||
|
|
bdefd8ea8c | ||
|
|
eabd405845 | ||
|
|
03946f2ca8 | ||
|
|
fec8d1bc2f | ||
|
|
53f5f46037 | ||
|
|
81d99a0061 | ||
|
|
b227757b9a | ||
|
|
eef5cefb5d | ||
|
|
7712b81ab9 | ||
|
|
7feb86fe55 | ||
|
|
d1efbf6620 | ||
|
|
aabee05a6a | ||
|
|
cf062b5b6a | ||
|
|
0b6a3898fe | ||
|
|
517ebcfbcd | ||
|
|
9ef24d3f43 | ||
|
|
568e0c7ff6 | ||
|
|
a454a3f74e | ||
|
|
f7860138c7 | ||
|
|
0607295081 | ||
|
|
d4a7af8a89 | ||
|
|
5c1417c4c7 | ||
|
|
dc522a0135 | ||
|
|
ac7db3cc88 | ||
|
|
5cc55d1e99 | ||
|
|
10352ff5ad | ||
|
|
97d561ac33 | ||
|
|
204c10c053 | ||
|
|
af8bb0c4b9 | ||
|
|
ada5c58acf | ||
|
|
02ffc2ddee | ||
|
|
62820ea2b8 | ||
|
|
04738587e8 | ||
|
|
cbbfcd0e7b | ||
|
|
309a70df89 | ||
|
|
4cb6984a65 | ||
|
|
d486fa8452 | ||
|
|
c95ad5b208 | ||
|
|
5382ac20b6 | ||
|
|
3fbd514417 | ||
|
|
d0465242a3 | ||
|
|
db90b084cf | ||
|
|
00d2dcda68 | ||
|
|
20e3fdc782 | ||
|
|
3c32c09a5a | ||
|
|
ad6a7086c4 | ||
|
|
e977333177 | ||
|
|
1d4f828b93 | ||
|
|
c349e06346 | ||
|
|
7ff2cb75a8 | ||
|
|
f51415cf2c | ||
|
|
ec6457bcd3 | ||
|
|
2d9852d6f1 | ||
|
|
05542324fc | ||
|
|
669e86f96e | ||
|
|
cbf928f363 | ||
|
|
6016e1b15d | ||
|
|
d08d5620da | ||
|
|
43b18c13e3 | ||
|
|
7deb9fde9e | ||
|
|
b134d2a7b0 | ||
|
|
36f31228ff | ||
|
|
8103ad3b9e | ||
|
|
10cb3c2c3d | ||
|
|
dda193247a | ||
|
|
2463e4efd3 | ||
|
|
5ce805db2e | ||
|
|
a0b17887fd | ||
|
|
3eeb31d577 | ||
|
|
e11d594122 | ||
|
|
600c437af5 | ||
|
|
0280ddcbe9 | ||
|
|
96784640e3 | ||
|
|
b75d12fe05 | ||
|
|
5e389c32ed | ||
|
|
fd89ef04b6 | ||
|
|
abcc10e938 | ||
|
|
3ad337dd15 | ||
|
|
a527767caa | ||
|
|
39129ecedf | ||
|
|
c97d2d4fe9 | ||
|
|
5a018afbc4 | ||
|
|
06dea8ef3f | ||
|
|
cbb3378d10 | ||
|
|
7735634649 | ||
|
|
e074570b8f | ||
|
|
148c6a6c23 | ||
|
|
360172cad0 | ||
|
|
a5421ae170 | ||
|
|
5d07f2c837 | ||
|
|
e95708fe08 | ||
|
|
75c91232b4 | ||
|
|
0190c0225e | ||
|
|
86d366be4d | ||
|
|
71d71a6b1b | ||
|
|
c8671ce8e8 | ||
|
|
ed3d04c7ba | ||
|
|
695ad47fe9 | ||
|
|
25ac1edb48 | ||
|
|
824b4e0923 | ||
|
|
1b62c11db5 | ||
|
|
83a66a672d | ||
|
|
30b9ddc251 | ||
|
|
4a9831bd23 | ||
|
|
59388d89a0 | ||
|
|
1d033bd286 | ||
|
|
1eb0e5d307 | ||
|
|
935f008c16 | ||
|
|
b5d57f3418 | ||
|
|
05b17a0082 | ||
|
|
91fd25a548 | ||
|
|
e05696dfcc | ||
|
|
157a45b627 | ||
|
|
e9a91455e8 | ||
|
|
f97eb99950 | ||
|
|
c4b7ab067a | ||
|
|
7477f6584e | ||
|
|
7b4f5252f1 | ||
|
|
4e03419e85 | ||
|
|
4d97043e26 | ||
|
|
4208dbd514 | ||
|
|
c335b76ec6 | ||
|
|
356ff457be | ||
|
|
833c3fbd39 | ||
|
|
ace66eab61 | ||
|
|
6a9274a95f | ||
|
|
87af94a7d2 | ||
|
|
ea9d2e3f88 | ||
|
|
cd5581aada | ||
|
|
211a1394d3 | ||
|
|
44032ffc11 | ||
|
|
f10460d774 | ||
|
|
57365ef7b9 | ||
|
|
d24f10ce6e | ||
|
|
1daa2ff98d | ||
|
|
0e6a6b784d | ||
|
|
ce6bf9e5c1 | ||
|
|
e3576e2614 | ||
|
|
0899d7aefd | ||
|
|
d078befd33 | ||
|
|
f67cb71dbc | ||
|
|
721cd578bb | ||
|
|
3aac62caa7 | ||
|
|
6b9eb57de7 | ||
|
|
2b2a41edd2 | ||
|
|
cd1ad452da | ||
|
|
77868a9b17 | ||
|
|
59a2a43473 | ||
|
|
385a0f979e | ||
|
|
770cc5a700 | ||
|
|
a03155432e | ||
|
|
27d72746ca | ||
|
|
d51e833bf3 | ||
|
|
c6644ec1ae | ||
|
|
a7562a6aa1 | ||
|
|
6b70436e2b | ||
|
|
3dbc7bdd2c | ||
|
|
b4877e7fac | ||
|
|
f489ffa043 | ||
|
|
211c7641c1 | ||
|
|
51d066a1bc | ||
|
|
b702822857 | ||
|
|
1cbb2320c1 | ||
|
|
1c4ad3f817 | ||
|
|
ef5ec06141 | ||
|
|
0c37236d60 | ||
|
|
def853e8c4 | ||
|
|
7826cfb01f | ||
|
|
ac8f0a7ef2 | ||
|
|
48a907ae45 | ||
|
|
2fe620df70 | ||
|
|
536bd37d05 | ||
|
|
d29d265b0a | ||
|
|
41d653738a | ||
|
|
1955497dbe | ||
|
|
572803d7ac | ||
|
|
25a474f9f7 | ||
|
|
c42f7ab6d3 | ||
|
|
a7717b432e | ||
|
|
2acfda3dc5 | ||
|
|
569525fb68 | ||
|
|
15353b7513 | ||
|
|
4737336b85 | ||
|
|
d85b1c775f | ||
|
|
2ce1a96468 | ||
|
|
239b8d2e7c | ||
|
|
43b3ce9ed1 | ||
|
|
80d5a966db | ||
|
|
b11fa53519 | ||
|
|
ce4d00dc21 | ||
|
|
bb269affe2 | ||
|
|
d818c250b0 | ||
|
|
3d4d880110 | ||
|
|
899b61264f | ||
|
|
4dc059fba3 | ||
|
|
f94792fad8 | ||
|
|
6df2ff7ebf | ||
|
|
dc4ddedca3 | ||
|
|
73b85f9b29 | ||
|
|
ce054dd37d | ||
|
|
3a0e91a688 | ||
|
|
60ca4f29d7 | ||
|
|
301ebe0da3 | ||
|
|
ada745324f | ||
|
|
a69eec5fb9 | ||
|
|
8c35ebbb7e | ||
|
|
5ef2d1d7ad | ||
|
|
815b2d8a2b | ||
|
|
0cfe2d882d | ||
|
|
6019b738a4 | ||
|
|
de17a651e6 | ||
|
|
91dc9f0c9d | ||
|
|
0770aa237a | ||
|
|
d3f2d77961 | ||
|
|
da03b22fe4 | ||
|
|
456647838c | ||
|
|
e192ac34d0 | ||
|
|
6a3ccda12e | ||
|
|
14f79ef85a | ||
|
|
924471ee76 | ||
|
|
cd5844b050 | ||
|
|
0f6a12b595 | ||
|
|
d19c6a6afc | ||
|
|
3257ea00b5 | ||
|
|
f073087379 | ||
|
|
1ba47ae67c | ||
|
|
8ca3dfd8c9 | ||
|
|
cdea30253b | ||
|
|
c8ee01ba0f | ||
|
|
3ad08c75c0 | ||
|
|
c12e545ccd | ||
|
|
6571faad6c | ||
|
|
f98f1647da | ||
|
|
249b8b0363 | ||
|
|
777af35030 | ||
|
|
23fafe1996 | ||
|
|
e0741cc9af | ||
|
|
0e4911a575 | ||
|
|
28bc76695a | ||
|
|
4f7287fec5 | ||
|
|
0b5478ad2d | ||
|
|
cd6911f83c | ||
|
|
173f27cb64 | ||
|
|
152dcbe522 | ||
|
|
2a7bad326d | ||
|
|
cb6d8bf063 | ||
|
|
83db8d2072 | ||
|
|
3da8e4c1bb | ||
|
|
ee111a28d4 | ||
|
|
d00d9cb00f | ||
|
|
74ba5a61bf | ||
|
|
fd81f57e61 | ||
|
|
e75e189933 | ||
|
|
88d2fca2c6 | ||
|
|
bd615ebf65 | ||
|
|
44b9e822d9 | ||
|
|
ed4fe6bd36 | ||
|
|
a416ff6314 | ||
|
|
61ba5817c9 | ||
|
|
7bdbdda7f9 | ||
|
|
a9ddd41729 | ||
|
|
1379b9c9fb | ||
|
|
32615befd5 | ||
|
|
7697779abf | ||
|
|
bb37ebf4ba | ||
|
|
5f37699736 | ||
|
|
9da9a209a5 | ||
|
|
482b4b6e95 | ||
|
|
ec2e8ad184 | ||
|
|
6c686af1b7 | ||
|
|
4132027ada | ||
|
|
f70ef7a585 | ||
|
|
eb2bf3469e | ||
|
|
bfbf97aec9 | ||
|
|
2baf06e012 | ||
|
|
c2c8bd0a76 | ||
|
|
36729fb6ae | ||
|
|
18d5576997 | ||
|
|
6a7c56d919 | ||
|
|
3110765d12 | ||
|
|
deee36651d | ||
|
|
1c46102c4a | ||
|
|
439cf1a308 | ||
|
|
219f2eee29 | ||
|
|
374abe5214 | ||
|
|
8b1a462a60 | ||
|
|
c11ca543e2 | ||
|
|
bf92e3a9dd | ||
|
|
7ba0b420f1 | ||
|
|
f3906dd7c4 | ||
|
|
984d8b8ee6 | ||
|
|
7b4189271c | ||
|
|
63e8faeed9 | ||
|
|
f48aaf1c46 | ||
|
|
41499b189c | ||
|
|
9fddd193b9 | ||
|
|
0c7c61b685 | ||
|
|
6179686c81 | ||
|
|
2bb79e1346 | ||
|
|
71bf5f4697 | ||
|
|
069c2d2fd2 | ||
|
|
f35ff105ab | ||
|
|
25ec624e4e | ||
|
|
a972174706 | ||
|
|
646272f9b3 | ||
|
|
743106e94f | ||
|
|
f5d81f51c4 | ||
|
|
e8f62eb1f9 | ||
|
|
ea1467add7 | ||
|
|
917439725a | ||
|
|
2ef9e2d6fc | ||
|
|
c14a5973c7 | ||
|
|
3a7ea62874 | ||
|
|
0a38c16cc2 | ||
|
|
c65a291698 | ||
|
|
38a8ddcd77 | ||
|
|
43ad8e80b9 | ||
|
|
928dff6b68 | ||
|
|
0bdee1d6d8 | ||
|
|
3f40e15ed5 | ||
|
|
a687aa1de6 | ||
|
|
c811eb069d | ||
|
|
eb3d3dcbc4 | ||
|
|
804c064a7e | ||
|
|
9059c09627 | ||
|
|
279253c486 | ||
|
|
c7d6fe2d62 | ||
|
|
5327857f81 | ||
|
|
3b4dd051f2 | ||
|
|
587a34442a |
3
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -14,7 +14,7 @@ body:
|
||||
attributes:
|
||||
label: NetBox version
|
||||
description: What version of NetBox are you currently running?
|
||||
placeholder: v3.3.0
|
||||
placeholder: v3.4.3
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
@@ -25,6 +25,7 @@ body:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
14
.github/ISSUE_TEMPLATE/documentation_change.yaml
vendored
@@ -19,11 +19,15 @@ body:
|
||||
label: Area
|
||||
description: To what section of the documentation does this change primarily pertain?
|
||||
options:
|
||||
- Installation instructions
|
||||
- Configuration parameters
|
||||
- Functionality/features
|
||||
- REST API
|
||||
- Administration/development
|
||||
- Features
|
||||
- Installation/upgrade
|
||||
- Getting started
|
||||
- Configuration
|
||||
- Customization
|
||||
- Integrations/API
|
||||
- Plugins
|
||||
- Administration
|
||||
- Development
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
@@ -14,7 +14,7 @@ body:
|
||||
attributes:
|
||||
label: NetBox version
|
||||
description: What version of NetBox are you currently running?
|
||||
placeholder: v3.3.0
|
||||
placeholder: v3.4.3
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
||||
20
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,15 +1,17 @@
|
||||
<!--
|
||||
Thank you for your interest in contributing to NetBox! Please note
|
||||
that our contribution policy requires that a feature request or bug
|
||||
report be opened for approval prior to filing a pull request. This
|
||||
helps avoid wasting time and effort on something that we might not
|
||||
be able to accept.
|
||||
Thank you for your interest in contributing to NetBox! Please note that
|
||||
our contribution policy requires that a feature request or bug report be
|
||||
approved and assigned prior to opening a pull request. This helps avoid
|
||||
waste time and effort on a proposed change that we might not be able to
|
||||
accept.
|
||||
|
||||
Please indicate the relevant feature request or bug report below.
|
||||
IF YOUR PULL REQUEST DOES NOT REFERENCE AN ACCEPTED BUG REPORT OR
|
||||
FEATURE REQUEST, IT WILL BE MARKED AS INVALID AND CLOSED.
|
||||
IF YOUR PULL REQUEST DOES NOT REFERENCE AN ISSUE WHICH HAS BEEN ASSIGNED
|
||||
TO YOU, IT WILL BE CLOSED AUTOMATICALLY.
|
||||
|
||||
Please specify your assigned issue number on the line below.
|
||||
-->
|
||||
### Fixes: <ISSUE NUMBER GOES HERE>
|
||||
### Fixes: #1234
|
||||
|
||||
<!--
|
||||
Please include a summary of the proposed changes below.
|
||||
-->
|
||||
|
||||
4
.github/workflows/ci.yml
vendored
@@ -1,5 +1,7 @@
|
||||
name: CI
|
||||
on: [push, pull_request]
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -7,7 +9,7 @@ jobs:
|
||||
NETBOX_CONFIGURATION: netbox.configuration_testing
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.8', '3.9', '3.10']
|
||||
python-version: ['3.8', '3.9', '3.10', '3.11']
|
||||
node-version: ['14.x']
|
||||
services:
|
||||
redis:
|
||||
|
||||
16
.github/workflows/lock.yml
vendored
@@ -4,18 +4,18 @@ name: 'Lock threads'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 3 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v2
|
||||
- uses: dessant/lock-threads@v3
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-lock-inactive-days: '90'
|
||||
issue-exclude-created-before: ''
|
||||
issue-exclude-labels: ''
|
||||
issue-lock-labels: ''
|
||||
issue-lock-comment: ''
|
||||
issue-inactive-days: 90
|
||||
pr-inactive-days: 30
|
||||
issue-lock-reason: 'resolved'
|
||||
process-only: 'issues'
|
||||
|
||||
9
.github/workflows/stale.yml
vendored
@@ -1,14 +1,21 @@
|
||||
# close-stale-issues (https://github.com/marketplace/actions/close-stale-issues)
|
||||
name: 'Close stale issues/PRs'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 4 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
- uses: actions/stale@v6
|
||||
with:
|
||||
close-issue-message: >
|
||||
This issue has been automatically closed due to lack of activity. In an
|
||||
|
||||
226
CONTRIBUTING.md
@@ -1,181 +1,115 @@
|
||||
## Getting Help
|
||||
**Looking for help?** NetBox has a vast, active community of fellow users that may be able to provide assistance. Just [start a discussion](https://github.com/netbox-community/netbox/discussions/new) right here on GitHub! Or if you'd prefer to chat, join us live in the `#netbox` channel on the [NetDev Community Slack](https://netdev.chat/)!
|
||||
|
||||
If you encounter any issues installing or using NetBox, try one of the
|
||||
following resources to get assistance. Please **do not** open a GitHub issue
|
||||
except to report bugs or request features.
|
||||
<div align="center">
|
||||
<h3>
|
||||
:bug: <a href="#bug-reporting-bugs">Report a bug</a> ·
|
||||
:bulb: <a href="#bulb-feature-requests">Suggest a feature</a> ·
|
||||
:arrow_heading_up: <a href="#arrow_heading_up-submitting-pull-requests">Submit a pull request</a>
|
||||
</h3>
|
||||
<h3>
|
||||
:jigsaw: <a href="#jigsaw-creating-plugins">Create a plugin</a> ·
|
||||
:rescue_worker_helmet: <a href="#rescue_worker_helmet-become-a-maintainer">Become a maintainer</a> ·
|
||||
:heart: <a href="#heart-other-ways-to-contribute">Other ideas</a>
|
||||
</h3>
|
||||
</div>
|
||||
<h3></h3>
|
||||
|
||||
### GitHub Discussions
|
||||
Some general tips for engaging here on GitHub:
|
||||
|
||||
GitHub's discussions are the best place to get help or propose rough ideas for
|
||||
new functionality. Their integration with GitHub allows for easily cross-
|
||||
referencing and converting posts to issues as needed. There are several
|
||||
categories for discussions:
|
||||
* Register for a free [GitHub account](https://github.com/signup) if you haven't already.
|
||||
* You can use [GitHub Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) for formatting text and adding images.
|
||||
* To help mitigate notification spam, please avoid "bumping" issues with no activity. (To vote an issue up or down, use a :thumbsup: or :thumbsdown: reaction.)
|
||||
* Please avoid pinging members with `@` unless they've previously expressed interest or involvement with that particular issue.
|
||||
|
||||
* **General** - General community discussion
|
||||
* **Ideas** - Ideas for new functionality that isn't yet ready for a formal
|
||||
feature request
|
||||
* **Q&A** - Request help with installing or using NetBox
|
||||
## :bug: Reporting Bugs
|
||||
|
||||
### Slack
|
||||
* First, ensure that you're running the [latest stable version](https://github.com/netbox-community/netbox/releases) of NetBox. If you're running an older version, it's likely that the bug has already been fixed.
|
||||
|
||||
For real-time chat, you can join the **#netbox** Slack channel on [NetDev Community](https://netdev.chat/).
|
||||
Unfortunately, the Slack channel does not provide long-term retention of chat
|
||||
history, so try to avoid it for any discussions would benefit from being
|
||||
preserved for future reference.
|
||||
* Next, search our [issues list](https://github.com/netbox-community/netbox/issues?q=is%3Aissue) to see if the bug you've found has already been reported. If you come across a bug report that seems to match, please click "add a reaction" in the top right corner of the issue and add a thumbs up (:thumbsup:). This will help draw more attention to it. Any comments you can add to provide additional information or context would also be much appreciated.
|
||||
|
||||
## Reporting Bugs
|
||||
* If you can't find any existing issues (open or closed) that seem to match yours, you're welcome to [submit a new bug report](https://github.com/netbox-community/netbox/issues/new?label=type%3A+bug&template=bug_report.yaml). Be sure to complete the entire report template, including detailed steps that someone triaging your issue can follow to confirm the reported behavior. (If we're not able to replicate the bug based on the information provided, we'll ask for additional detail.)
|
||||
|
||||
* First, ensure that you're running the [latest stable version](https://github.com/netbox-community/netbox/releases)
|
||||
of NetBox. If you're running an older version, it's possible that the bug has
|
||||
already been fixed.
|
||||
|
||||
* Next, check the GitHub [issues list](https://github.com/netbox-community/netbox/issues)
|
||||
to see if the bug you've found has already been reported. If you think you may
|
||||
be experiencing a reported issue that hasn't already been resolved, please
|
||||
click "add a reaction" in the top right corner of the issue and add a thumbs
|
||||
up (+1). You might also want to add a comment describing how it's affecting your
|
||||
installation. This will allow us to prioritize bugs based on how many users are
|
||||
affected.
|
||||
|
||||
* When submitting an issue, please be as descriptive as possible. Be sure to
|
||||
provide all information request in the issue template, including:
|
||||
|
||||
* The environment in which NetBox is running
|
||||
* The exact steps that can be taken to reproduce the issue
|
||||
* Expected and observed behavior
|
||||
* Any error messages generated
|
||||
* Screenshots (if applicable)
|
||||
|
||||
* Please avoid prepending any sort of tag (e.g. "[Bug]") to the issue title.
|
||||
The issue will be reviewed by a maintainer after submission and the appropriate
|
||||
labels will be applied for categorization.
|
||||
|
||||
* Keep in mind that we prioritize bugs based on their severity and how much
|
||||
work is required to resolve them. It may take some time for someone to address
|
||||
your issue.
|
||||
* Some other tips to keep in mind:
|
||||
* Error messages and screenshots are especially helpful.
|
||||
* Don't prepend your issue title with a label like `[Bug]`; the proper label will be assigned automatically.
|
||||
* Ensure that your reproduction instructions don't reference data in our [demo instance](https://demo.netbox.dev/), which gets rebuilt nightly.
|
||||
* Verify that you have GitHub notifications enabled and are subscribed to your issue after submitting.
|
||||
* We appreciate your patience as bugs are prioritized by their severity, impact, and difficulty to resolve.
|
||||
|
||||
* For more information on how bug reports are handled, please see our [issue
|
||||
intake policy](https://github.com/netbox-community/netbox/wiki/Issue-Intake-Policy).
|
||||
|
||||
## Feature Requests
|
||||
## :bulb: Feature Requests
|
||||
|
||||
* First, check the GitHub [issues list](https://github.com/netbox-community/netbox/issues)
|
||||
to see if the feature you're requesting is already listed. (Be sure to search
|
||||
closed issues as well, since some feature requests have been rejected.) If the
|
||||
feature you'd like to see has already been requested and is open, click "add a
|
||||
reaction" in the top right corner of the issue and add a thumbs up (+1). This
|
||||
ensures that the issue has a better chance of receiving attention. Also feel
|
||||
free to add a comment with any additional justification for the feature.
|
||||
(However, note that comments with no substance other than a "+1" will be
|
||||
deleted. Please use GitHub's reactions feature to indicate your support.)
|
||||
* First, check the GitHub [issues list](https://github.com/netbox-community/netbox/issues?q=is%3Aissue) to see if the feature you have in mind has already been proposed. If you happen to find an open feature request that matches your idea, click "add a reaction" in the top right corner of the issue and add a thumbs up (:thumbsup:). This ensures that the issue has a better chance of receiving attention. Also feel free to add a comment with any additional justification for the feature.
|
||||
|
||||
* Before filing a new feature request, consider raising your idea in a
|
||||
[GitHub discussion](https://github.com/netbox-community/netbox/discussions)
|
||||
first. Feedback you receive there will help validate and shape the proposed
|
||||
feature before filing a formal issue.
|
||||
* If you have a rough idea that's not quite ready for formal submission yet, start a [GitHub discussion](https://github.com/netbox-community/netbox/discussions) instead. This is a great way to test the viability and narrow down the scope of a new feature prior to submitting a formal proposal, and can serve to generate interest in your idea from other community members.
|
||||
|
||||
* Good feature requests are very narrowly defined. Be sure to thoroughly
|
||||
describe the functionality and data model(s) being proposed. The more effort
|
||||
you put into writing a feature request, the better its chance is of being
|
||||
implemented. Overly broad feature requests will be closed.
|
||||
* Once you're ready, submit a feature request [using this template](https://github.com/netbox-community/netbox/issues/new?label=type%3A+feature&template=feature_request.yaml). Be sure to provide sufficient context and detail to convey exactly what you're proposing and why. The stronger your use case, the better chance your proposal has of being accepted.
|
||||
|
||||
* When submitting a feature request on GitHub, be sure to include all
|
||||
information requested by the issue template, including:
|
||||
* Some other tips to keep in mind:
|
||||
* Don't prepend your issue title with a label like `[Feature]`; the proper label will be assigned automatically.
|
||||
* Try to anticipate any likely questions about your proposal and provide that information proactively.
|
||||
* Verify that you have GitHub notifications enabled and are subscribed to your issue after submitting.
|
||||
* You're welcome to volunteer to implement your FR, but don't submit a pull request until it has been approved.
|
||||
|
||||
* A detailed description of the proposed functionality
|
||||
* A use case for the feature; who would use it and what value it would add
|
||||
to NetBox
|
||||
* A rough description of changes necessary to the database schema (if
|
||||
applicable)
|
||||
* Any third-party libraries or other resources which would be involved
|
||||
* For more information on how feature requests are handled, please see our [issue intake policy](https://github.com/netbox-community/netbox/wiki/Issue-Intake-Policy).
|
||||
|
||||
* Please avoid prepending any sort of tag (e.g. "[Feature]") to the issue
|
||||
title. The issue will be reviewed by a moderator after submission and the
|
||||
appropriate labels will be applied for categorization.
|
||||
## :arrow_heading_up: Submitting Pull Requests
|
||||
|
||||
* For more information on how feature requests are handled, please see our
|
||||
[issue intake policy](https://github.com/netbox-community/netbox/wiki/Issue-Intake-Policy).
|
||||
* [Pull requests](https://docs.github.com/en/pull-requests) (a feature of GitHub) are used to propose changes to NetBox's code base. Our process generally goes like this:
|
||||
* A user opens a new issue (bug report or feature request)
|
||||
* A maintainer triages the issue and may mark it as needing an owner
|
||||
* The issue's author can volunteer to own it, or someone else can
|
||||
* A maintainer assigns the issue to whomever volunteers
|
||||
* The issue owner submits a pull request that will resolve the issue
|
||||
* A maintainer reviews and merges the pull request, closing the issue
|
||||
|
||||
## Submitting Pull Requests
|
||||
* It's very important that you not submit a pull request until a relevant issue has been opened **and** assigned to you. Otherwise, you risk wasting time on work that may ultimately not be needed.
|
||||
|
||||
* If you're interested in contributing to NetBox, be sure to check out our
|
||||
[getting started](https://docs.netbox.dev/en/stable/development/getting-started/)
|
||||
documentation for tips on setting up your development environment.
|
||||
* New pull requests should generally be based off of the `develop` branch, rather than `master`. The `develop` branch is used for ongoing development, while `master` is used for tracking stable releases. (If you're developing for an upcoming minor release, use `feature` instead.)
|
||||
|
||||
* Be sure to open an issue **before** starting work on a pull request, and
|
||||
discuss your idea with the NetBox maintainers before beginning work. This will
|
||||
help prevent wasting time on something that might we might not be able to
|
||||
implement. When suggesting a new feature, also make sure it won't conflict with
|
||||
any work that's already in progress.
|
||||
* In most cases, it is not necessary to add a changelog entry: A maintainer will take care of this when the PR is merged. (This helps avoid merge conflicts resulting from multiple PRs being submitted simultaneously.)
|
||||
|
||||
* Once you've opened or identified an issue you'd like to work on, ask that it
|
||||
be assigned to you so that others are aware it's being worked on. A maintainer
|
||||
will then mark the issue as "accepted."
|
||||
|
||||
* Any pull request which does _not_ relate to an **accepted** issue will be closed.
|
||||
|
||||
* All new functionality must include relevant tests where applicable.
|
||||
|
||||
* When submitting a pull request, please be sure to work off of the `develop`
|
||||
branch, rather than `master`. The `develop` branch is used for ongoing
|
||||
development, while `master` is used for tagging stable releases.
|
||||
|
||||
* In most cases, it is not necessary to add a changelog entry: A maintainer will
|
||||
take care of this when the PR is merged. (This helps avoid merge conflicts
|
||||
resulting from multiple PRs being submitted simultaneously.)
|
||||
|
||||
* All code submissions should meet the following criteria (CI will enforce
|
||||
these checks):
|
||||
|
||||
* Python syntax is valid
|
||||
* All tests pass when run with `./manage.py test`
|
||||
* PEP 8 compliance is enforced, with the exception that lines may be
|
||||
* All code submissions should meet the following criteria (CI will enforce these checks):
|
||||
* Python syntax is valid
|
||||
* All tests pass when run with `./manage.py test`
|
||||
* PEP 8 compliance is enforced, with the exception that lines may be
|
||||
greater than 80 characters in length
|
||||
|
||||
## Commenting
|
||||
* Some other tips to keep in mind:
|
||||
* If you'd like to volunteer for someone else's issue, please post a comment on that issue letting us know. (This will allow the maintainers to assign it to you.)
|
||||
* Check out our [developer docs](https://docs.netbox.dev/en/stable/development/getting-started/) for tips on setting up your development environment.
|
||||
* All new functionality must include relevant tests where applicable.
|
||||
|
||||
Only comment on an issue if you are sharing a relevant idea or constructive
|
||||
feedback. **Do not** comment on an issue just to show your support (give the
|
||||
top post a :+1: instead) or ask for an ETA. These comments will be deleted to
|
||||
reduce noise in the discussion.
|
||||
## :jigsaw: Creating Plugins
|
||||
|
||||
## Issue Lifecycle
|
||||
Do you have an idea for something you'd like to build in NetBox, but might not be right for the core project? NetBox includes a powerful and extensive [plugins framework](https://docs.netbox.dev/en/stable/plugins/) that enables users to develop their own custom data models and integrations.
|
||||
|
||||
New issues are handled according to our [issue intake policy](https://github.com/netbox-community/netbox/wiki/Issue-Intake-Policy).
|
||||
Maintainers will assign label(s) and/or close new issues as the policy
|
||||
dictates. This helps ensure a productive development environment and avoid
|
||||
accumulating a large backlog of work.
|
||||
Check out our [plugin development tutorial](https://github.com/netbox-community/netbox-plugin-tutorial) to get started!
|
||||
|
||||
The core maintainers group has chosen to make use of GitHub's [Stale bot](https://github.com/apps/stale)
|
||||
to aid in issue management.
|
||||
## :rescue_worker_helmet: Become a Maintainer
|
||||
|
||||
* Issues will be marked as stale after 60 days of no activity.
|
||||
* If the stable label is not removed in the following 30 days, the issue will
|
||||
be closed automatically.
|
||||
* Any issue bearing one of the following labels will be exempt from all Stale
|
||||
bot actions:
|
||||
* `status: accepted`
|
||||
* `status: blocked`
|
||||
* `status: needs milestone`
|
||||
We're always looking for motivated individuals to join the maintainers team and help drive NetBox's long-term development. Some of our most sought-after skills include:
|
||||
|
||||
It is natural that some new issues get more attention than others. The stale
|
||||
bot helps bring renewed attention to potentially valuable issues that may have
|
||||
been overlooked. **Do not** comment on a stale issue merely to "bump" it in an
|
||||
effort to circumvent the bot: This will result in the immediate closure of the
|
||||
issue, and you may be barred from participating in future discussions.
|
||||
* Python development with a strong focus on the [Django](https://www.djangoproject.com/) framework
|
||||
* Expertise working with PostgreSQL databases
|
||||
* Javascript & TypeScript proficiency
|
||||
* A knack for web application design (HTML & CSS)
|
||||
* Familiarity with git and software development best practices
|
||||
* Excellent attention to detail
|
||||
* Working experience in the field of network operations & engineering
|
||||
|
||||
## Maintainer Guidance
|
||||
We generally ask that maintainers dedicate around four hours of work to the project each week on average, which includes both hands-on development and project management tasks such as issue triage. Maintainers are also encouraged (but not required) to attend our bi-weekly Zoom call to catch up on recent items.
|
||||
|
||||
* Maintainers are expected to contribute at least four hours per week to the
|
||||
project on average. This can be employer-sponsored or individual time, with
|
||||
the understanding that all contributions are submitted under the Apache 2.0
|
||||
license and that your employer may not make claim to any contributions.
|
||||
Contributions include code work, issue management, and community support. All
|
||||
development must be in accordance with our [development guidance](https://docs.netbox.dev/en/stable/development/).
|
||||
Many maintainers petition their employer to grant some of their paid time to work on NetBox. In doing so, your employer becomes eligible to be featured as a [NetBox sponsor](https://github.com/netbox-community/netbox/wiki/Sponsorship).
|
||||
|
||||
* Maintainers are expected to attend (where feasible) our biweekly ~30-minute
|
||||
sync to review agenda items. This meeting provides opportunity to present and
|
||||
discuss pressing topics. Meetings are held as virtual audio/video conferences.
|
||||
Interested? You can contact our lead maintainer, Jeremy Stretch, at jeremy@netbox.dev or on the [NetDev Community Slack](https://netdev.chat/). We'd love to have you on the team!
|
||||
|
||||
* Maintainers with no substantial recorded activity in a 60-day period will be
|
||||
removed from the project.
|
||||
## :heart: Other Ways to Contribute
|
||||
|
||||
You don't have to be a developer to contribute to NetBox: There are plenty of other ways you can add value to the community! Below are just a few examples:
|
||||
|
||||
* Help answer questions and provide feedback in our [GitHub discussions](https://github.com/netbox-community/netbox/discussions) and on [Slack](https://netdev.chat/).
|
||||
* Write a blog article or record a YouTube video demonstrating how NetBox is used at your organization.
|
||||
* Help grow our [library of device & module type definitions](https://github.com/netbox-community/devicetype-library).
|
||||
|
||||
35
README.md
@@ -2,14 +2,24 @@
|
||||
<img src="https://raw.githubusercontent.com/netbox-community/netbox/develop/docs/netbox_logo.svg" width="400" alt="NetBox logo" />
|
||||
</div>
|
||||
|
||||
NetBox is the leading solution for modeling and documenting modern networks. By
|
||||
combining the traditional disciplines of IP address management (IPAM) and
|
||||
datacenter infrastructure management (DCIM) with powerful APIs and extensions,
|
||||
NetBox provides the ideal "source of truth" to power network automation.
|
||||
Available as open source software under the Apache 2.0 license, NetBox is
|
||||
employed by thousands of organizations around the world.
|
||||
|
||||

|
||||
|
||||
NetBox is an infrastructure resource modeling (IRM) tool designed to empower
|
||||
network automation, used by thousands of organizations around the world.
|
||||
Initially conceived by the network engineering team at
|
||||
[DigitalOcean](https://www.digitalocean.com/), NetBox was developed specifically
|
||||
to address the needs of network and infrastructure engineers. It is intended to
|
||||
function as a domain-specific source of truth for network operations.
|
||||
[](https://github.com/netbox-community/netbox/commits)
|
||||
[](https://github.com/netbox-community/netbox/issues)
|
||||
[](https://github.com/netbox-community/netbox/pulls)
|
||||
[](https://github.com/netbox-community/netbox/graphs/contributors)
|
||||
<br />Stats via [Repography](https://repography.com)
|
||||
|
||||
## About NetBox
|
||||
|
||||

|
||||
|
||||
Myriad infrastructure components can be modeled in NetBox, including:
|
||||
|
||||
@@ -21,6 +31,7 @@ Myriad infrastructure components can be modeled in NetBox, including:
|
||||
* Virtual machines and clusters
|
||||
* IP prefixes, ranges, and addresses
|
||||
* VRFs and route targets
|
||||
* L2VPN and overlays
|
||||
* FHRP groups (VRRP, HSRP, etc.)
|
||||
* AS numbers
|
||||
* VLANs and scoped VLAN groups
|
||||
@@ -45,14 +56,16 @@ customized and extended through the use of:
|
||||
NetBox also features a complete REST API as well as a GraphQL API for easily
|
||||
integrating with other tools and systems.
|
||||
|
||||
The complete documentation for NetBox can be found at [docs.netbox.dev](https://docs.netbox.dev/).
|
||||
A public demo instance is available at [demo.netbox.dev](https://demo.netbox.dev).
|
||||
|
||||
NetBox runs as a web application atop the [Django](https://www.djangoproject.com/)
|
||||
Python framework with a [PostgreSQL](https://www.postgresql.org/) database. For a
|
||||
complete list of requirements, see `requirements.txt`. The code is available [on GitHub](https://github.com/netbox-community/netbox).
|
||||
|
||||
The complete documentation for NetBox can be found at [docs.netbox.dev](https://docs.netbox.dev/). A public demo instance is available at https://demo.netbox.dev.
|
||||
complete list of requirements, see `requirements.txt`. The code is available
|
||||
[on GitHub](https://github.com/netbox-community/netbox).
|
||||
|
||||
<div align="center">
|
||||
<h4>Thank you to our sponsors!</h4>
|
||||
<h3>Thank you to our sponsors!</h3>
|
||||
|
||||
[](https://try.digitalocean.com/developer-cloud)
|
||||
|
||||
@@ -90,8 +103,6 @@ our [contributing guide](CONTRIBUTING.md) prior to beginning any work.
|
||||
|
||||
### Screenshots
|
||||
|
||||
")
|
||||
|
||||
")
|
||||
|
||||

|
||||
|
||||
@@ -4,7 +4,7 @@ bleach
|
||||
|
||||
# The Python web framework on which NetBox is built
|
||||
# https://github.com/django/django
|
||||
Django<4.1
|
||||
Django<4.2
|
||||
|
||||
# Django middleware which permits cross-domain API requests
|
||||
# https://github.com/OttoYiu/django-cors-headers
|
||||
@@ -80,7 +80,8 @@ Jinja2
|
||||
|
||||
# Simple markup language for rendering HTML
|
||||
# https://github.com/Python-Markdown/markdown
|
||||
Markdown
|
||||
# mkdocs currently requires Markdown v3.3
|
||||
Markdown<3.4
|
||||
|
||||
# File inclusion plugin for Python-Markdown
|
||||
# https://github.com/cmacmackin/markdown-include
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# The IP address (typically localhost) and port that the Netbox WSGI process should listen on
|
||||
# The IP address (typically localhost) and port that the NetBox WSGI process should listen on
|
||||
bind = '127.0.0.1:8001'
|
||||
|
||||
# Number of gunicorn workers to spawn. This should typically be 2n+1, where
|
||||
|
||||
4
docs/_theme/main.html
vendored
@@ -2,8 +2,8 @@
|
||||
|
||||
{% block site_meta %}
|
||||
{{ super() }}
|
||||
{# Disable search indexing unless we're building for ReadTheDocs #}
|
||||
{% if not config.extra.readthedocs %}
|
||||
{# Disable search indexing unless we're building for ReadTheDocs (see #10496) #}
|
||||
{% if page.canonical_url != 'https://docs.netbox.dev/' %}
|
||||
<meta name="robots" content="noindex">
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -58,9 +58,11 @@ The following model fields support configurable choices:
|
||||
* `circuits.Circuit.status`
|
||||
* `dcim.Device.status`
|
||||
* `dcim.Location.status`
|
||||
* `dcim.Module.status`
|
||||
* `dcim.PowerFeed.status`
|
||||
* `dcim.Rack.status`
|
||||
* `dcim.Site.status`
|
||||
* `dcim.VirtualDeviceContext.status`
|
||||
* `extras.JournalEntry.kind`
|
||||
* `ipam.IPAddress.status`
|
||||
* `ipam.IPRange.status`
|
||||
@@ -68,6 +70,7 @@ The following model fields support configurable choices:
|
||||
* `ipam.VLAN.status`
|
||||
* `virtualization.Cluster.status`
|
||||
* `virtualization.VirtualMachine.status`
|
||||
* `wireless.WirelessLAN.status`
|
||||
|
||||
The following colors are supported:
|
||||
|
||||
|
||||
@@ -141,6 +141,22 @@ When determining the primary IP address for a device, IPv6 is preferred over IPv
|
||||
|
||||
---
|
||||
|
||||
## QUEUE_MAPPINGS
|
||||
|
||||
Allows changing which queues are used internally for background tasks.
|
||||
|
||||
```python
|
||||
QUEUE_MAPPINGS = {
|
||||
'webhook': 'low',
|
||||
'report': 'high',
|
||||
'script': 'high',
|
||||
}
|
||||
```
|
||||
|
||||
If no queue is defined the queue named `default` will be used.
|
||||
|
||||
---
|
||||
|
||||
## RELEASE_CHECK_URL
|
||||
|
||||
Default: None (disabled)
|
||||
|
||||
@@ -25,7 +25,7 @@ ALLOWED_HOSTS = ['*']
|
||||
|
||||
## DATABASE
|
||||
|
||||
NetBox requires access to a PostgreSQL 10 or later database service to store data. This service can run locally on the NetBox server or on a remote system. The following parameters must be defined within the `DATABASE` dictionary:
|
||||
NetBox requires access to a PostgreSQL 11 or later database service to store data. This service can run locally on the NetBox server or on a remote system. The following parameters must be defined within the `DATABASE` dictionary:
|
||||
|
||||
* `NAME` - Database name
|
||||
* `USER` - PostgreSQL username
|
||||
@@ -63,6 +63,7 @@ Redis is configured using a configuration setting similar to `DATABASE` and thes
|
||||
|
||||
* `HOST` - Name or IP address of the Redis server (use `localhost` if running locally)
|
||||
* `PORT` - TCP port of the Redis service; leave blank for default port (6379)
|
||||
* `USERNAME` - Redis username (if set)
|
||||
* `PASSWORD` - Redis password (if set)
|
||||
* `DATABASE` - Numeric database ID
|
||||
* `SSL` - Use SSL connection to Redis
|
||||
@@ -75,6 +76,7 @@ REDIS = {
|
||||
'tasks': {
|
||||
'HOST': 'redis.example.com',
|
||||
'PORT': 1234,
|
||||
'USERNAME': 'netbox'
|
||||
'PASSWORD': 'foobar',
|
||||
'DATABASE': 0,
|
||||
'SSL': False,
|
||||
@@ -82,6 +84,7 @@ REDIS = {
|
||||
'caching': {
|
||||
'HOST': 'localhost',
|
||||
'PORT': 6379,
|
||||
'USERNAME': ''
|
||||
'PASSWORD': '',
|
||||
'DATABASE': 1,
|
||||
'SSL': False,
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# Security & Authentication Parameters
|
||||
|
||||
## ALLOW_TOKEN_RETRIEVAL
|
||||
|
||||
Default: True
|
||||
|
||||
If disabled, the values of API tokens will not be displayed after each token's initial creation. A user **must** record the value of a token immediately upon its creation, or it will be lost. Note that this affects _all_ users, regardless of assigned permissions.
|
||||
|
||||
---
|
||||
|
||||
## ALLOWED_URL_SCHEMES
|
||||
|
||||
!!! tip "Dynamic Configuration Parameter"
|
||||
@@ -129,6 +137,14 @@ The lifetime (in seconds) of the authentication cookie issued to a NetBox user u
|
||||
|
||||
---
|
||||
|
||||
## LOGOUT_REDIRECT_URL
|
||||
|
||||
Default: `'home'`
|
||||
|
||||
The view name or URL to which a user is redirected after logging out.
|
||||
|
||||
---
|
||||
|
||||
## SESSION_COOKIE_NAME
|
||||
|
||||
Default: `sessionid`
|
||||
|
||||
@@ -12,6 +12,17 @@ BASE_PATH = 'netbox/'
|
||||
|
||||
---
|
||||
|
||||
## DEFAULT_LANGUAGE
|
||||
|
||||
Default: `en-us` (US English)
|
||||
|
||||
Defines the default preferred language/locale for requests that do not specify one. This is used to alter e.g. the display of dates and numbers to fit the user's locale. See [this list](http://www.i18nguy.com/unicode/language-identifiers.html) of standard language codes. (This parameter maps to Django's [`LANGUAGE_CODE`](https://docs.djangoproject.com/en/stable/ref/settings/#language-code) internal setting.)
|
||||
|
||||
!!! note
|
||||
Altering this parameter will *not* change the language used in NetBox. We hope to provide translation support in a future NetBox release.
|
||||
|
||||
---
|
||||
|
||||
## DOCS_ROOT
|
||||
|
||||
Default: `$INSTALL_ROOT/docs/`
|
||||
@@ -54,11 +65,19 @@ Email is sent from NetBox only for critical events or if configured for [logging
|
||||
|
||||
---
|
||||
|
||||
## ENABLE_LOCALIZATION
|
||||
|
||||
Default: False
|
||||
|
||||
Determines if localization features are enabled or not. This should only be enabled for development or testing purposes as netbox is not yet fully localized. Turning this on will localize numeric and date formats (overriding what is set for DATE_FORMAT) based on the browser locale as well as translate certain strings from third party modules.
|
||||
|
||||
---
|
||||
|
||||
## HTTP_PROXIES
|
||||
|
||||
Default: None
|
||||
|
||||
A dictionary of HTTP proxies to use for outbound requests originating from NetBox (e.g. when sending webhook requests). Proxies should be specified by schema (HTTP and HTTPS) as per the [Python requests library documentation](https://2.python-requests.org/en/master/user/advanced/). For example:
|
||||
A dictionary of HTTP proxies to use for outbound requests originating from NetBox (e.g. when sending webhook requests). Proxies should be specified by schema (HTTP and HTTPS) as per the [Python requests library documentation](https://requests.readthedocs.io/en/latest/user/advanced/#proxies). For example:
|
||||
|
||||
```python
|
||||
HTTP_PROXIES = {
|
||||
@@ -157,6 +176,14 @@ The file path to the location where [custom scripts](../customization/custom-scr
|
||||
|
||||
---
|
||||
|
||||
## SEARCH_BACKEND
|
||||
|
||||
Default: `'netbox.search.backends.CachedValueSearchBackend'`
|
||||
|
||||
The dotted path to the desired search backend class. `CachedValueSearchBackend` is currently the only search backend provided in NetBox, however this setting can be used to enable a custom backend.
|
||||
|
||||
---
|
||||
|
||||
## STORAGE_BACKEND
|
||||
|
||||
Default: None (local storage)
|
||||
|
||||
@@ -13,6 +13,7 @@ Custom fields may be created by navigating to Customization > Custom Fields. Net
|
||||
* Text: Free-form text (intended for single-line use)
|
||||
* Long text: Free-form of any length; supports Markdown rendering
|
||||
* Integer: A whole number (positive or negative)
|
||||
* Decimal: A fixed-precision decimal number (4 decimal places)
|
||||
* Boolean: True or false
|
||||
* Date: A date in ISO 8601 format (YYYY-MM-DD)
|
||||
* URL: This will be presented as a link in the web UI
|
||||
|
||||
@@ -129,6 +129,19 @@ The Script object provides a set of convenient functions for recording messages
|
||||
|
||||
Log messages are returned to the user upon execution of the script. Markdown rendering is supported for log messages.
|
||||
|
||||
## Change Logging
|
||||
|
||||
To generate the correct change log data when editing an existing object, a snapshot of the object must be taken before making any changes to the object.
|
||||
|
||||
```python
|
||||
if obj.pk and hasattr(obj, 'snapshot'):
|
||||
obj.snapshot()
|
||||
|
||||
obj.property = "New Value"
|
||||
obj.full_clean()
|
||||
obj.save()
|
||||
```
|
||||
|
||||
## Variable Reference
|
||||
|
||||
### Default Options
|
||||
@@ -254,7 +267,7 @@ An IPv4 or IPv6 network with a mask. Returns a `netaddr.IPNetwork` object. Two a
|
||||
|
||||
### Via the Web UI
|
||||
|
||||
Custom scripts can be run via the web UI by navigating to the script, completing any required form data, and clicking the "run script" button.
|
||||
Custom scripts can be run via the web UI by navigating to the script, completing any required form data, and clicking the "run script" button. It is possible to schedule a script to be executed at specified time in the future. A scheduled script can be canceled by deleting the associated job result object.
|
||||
|
||||
### Via the API
|
||||
|
||||
@@ -269,6 +282,8 @@ http://netbox/api/extras/scripts/example.MyReport/ \
|
||||
--data '{"data": {"foo": "somevalue", "bar": 123}, "commit": true}'
|
||||
```
|
||||
|
||||
Optionally `schedule_at` can be passed in the form data with a datetime string to schedule a script at the specified date and time.
|
||||
|
||||
### Via the CLI
|
||||
|
||||
Scripts can be run on the CLI by invoking the management command:
|
||||
|
||||
@@ -45,7 +45,7 @@ class DeviceConnectionsReport(Report):
|
||||
# Check that every console port for every active device has a connection defined.
|
||||
active = DeviceStatusChoices.STATUS_ACTIVE
|
||||
for console_port in ConsolePort.objects.prefetch_related('device').filter(device__status=active):
|
||||
if console_port.connected_endpoint is None:
|
||||
if not console_port.connected_endpoints:
|
||||
self.log_failure(
|
||||
console_port.device,
|
||||
"No console connection defined for {}".format(console_port.name)
|
||||
@@ -64,7 +64,7 @@ class DeviceConnectionsReport(Report):
|
||||
for device in Device.objects.filter(status=DeviceStatusChoices.STATUS_ACTIVE):
|
||||
connected_ports = 0
|
||||
for power_port in PowerPort.objects.filter(device=device):
|
||||
if power_port.connected_endpoint is not None:
|
||||
if power_port.connected_endpoints:
|
||||
connected_ports += 1
|
||||
if not power_port.path.is_active:
|
||||
self.log_warning(
|
||||
@@ -136,7 +136,7 @@ Once you have created a report, it will appear in the reports list. Initially, r
|
||||
|
||||
### Via the Web UI
|
||||
|
||||
Reports can be run via the web UI by navigating to the report and clicking the "run report" button at top right. Once a report has been run, its associated results will be included in the report view.
|
||||
Reports can be run via the web UI by navigating to the report and clicking the "run report" button at top right. Once a report has been run, its associated results will be included in the report view. It is possible to schedule a report to be executed at specified time in the future. A scheduled report can be canceled by deleting the associated job result object.
|
||||
|
||||
### Via the API
|
||||
|
||||
@@ -152,6 +152,8 @@ Our example report above would be called as:
|
||||
POST /api/extras/reports/devices.DeviceConnectionsReport/run/
|
||||
```
|
||||
|
||||
Optionally `schedule_at` can be passed in the form data with a datetime string to schedule a script at the specified date and time.
|
||||
|
||||
### Via the CLI
|
||||
|
||||
Reports can be run on the CLI by invoking the management command:
|
||||
|
||||
@@ -60,7 +60,7 @@ Create the HTML template for the object view. (The other views each typically em
|
||||
|
||||
## 10. Add the model to the navigation menu
|
||||
|
||||
Add the relevant navigation menu items in `netbox/netbox/navigation_menu.py`.
|
||||
Add the relevant navigation menu items in `netbox/netbox/navigation/menu.py`.
|
||||
|
||||
## 11. REST API components
|
||||
|
||||
|
||||
@@ -56,11 +56,15 @@ If the new field should be filterable, add it to the `FilterSet` for the model.
|
||||
|
||||
If the new field will be included in the object list view, add a column to the model's table. For simple fields, adding the field name to `Meta.fields` will be sufficient. More complex fields may require declaring a custom column. Also add the field name to `default_columns` if the column should be present in the table by default.
|
||||
|
||||
## 8. Update the UI templates
|
||||
## 8. Update the SearchIndex
|
||||
|
||||
Where applicable, add the new field to the model's SearchIndex for inclusion in global search.
|
||||
|
||||
## 9. Update the UI templates
|
||||
|
||||
Edit the object's view template to display the new field. There may also be a custom add/edit form template that needs to be updated.
|
||||
|
||||
## 9. Create/extend test cases
|
||||
## 10. Create/extend test cases
|
||||
|
||||
Create or extend the relevant test cases to verify that the new field and any accompanying validation logic perform as expected. This is especially important for relational fields. NetBox incorporates various test suites, including:
|
||||
|
||||
@@ -72,6 +76,6 @@ Create or extend the relevant test cases to verify that the new field and any ac
|
||||
|
||||
Be diligent to ensure all of the relevant test suites are adapted or extended as necessary to test any new functionality.
|
||||
|
||||
## 10. Update the model's documentation
|
||||
## 11. Update the model's documentation
|
||||
|
||||
Each model has a dedicated page in the documentation, at `models/<app>/<model>.md`. Update this file to include any relevant information about the new field.
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
|
||||
Getting started with NetBox development is pretty straightforward, and should feel very familiar to anyone with Django development experience. There are a few things you'll need:
|
||||
|
||||
* A Linux system or environment
|
||||
* A Linux system or compatible environment
|
||||
* A PostgreSQL server, which can be installed locally [per the documentation](../installation/1-postgresql.md)
|
||||
* A Redis server, which can also be [installed locally](../installation/2-redis.md)
|
||||
* A supported version of Python
|
||||
* Python 3.8 or later
|
||||
|
||||
### Fork the Repo
|
||||
### 1. Fork the Repo
|
||||
|
||||
Assuming you'll be working on your own fork, your first step will be to fork the [official git repository](https://github.com/netbox-community/netbox). (If you're a maintainer who's going to be working directly with the official repo, skip this step.) Click the "fork" button at top right (be sure that you've logged into GitHub first).
|
||||
|
||||
@@ -21,7 +21,7 @@ Copy the URL provided in the dialog box.
|
||||
|
||||
You can then clone your GitHub fork locally for development:
|
||||
|
||||
```no-highlight
|
||||
```no-highlight hl_lines="1 9"
|
||||
$ git clone https://github.com/$username/netbox.git
|
||||
Cloning into 'netbox'...
|
||||
remote: Enumerating objects: 85949, done.
|
||||
@@ -35,93 +35,114 @@ base_requirements.txt contrib docs mkdocs.yml NOTICE requ
|
||||
CHANGELOG.md CONTRIBUTING.md LICENSE.txt netbox README.md scripts
|
||||
```
|
||||
|
||||
### 2. Create a New Branch
|
||||
|
||||
The NetBox project utilizes three persistent git branches to track work:
|
||||
|
||||
* `master` - Serves as a snapshot of the current stable release
|
||||
* `develop` - All development on the upcoming stable release occurs here
|
||||
* `feature` - Tracks work on an upcoming major release
|
||||
* `develop` - All development on the upcoming stable (patch) release occurs here
|
||||
* `feature` - Tracks work on an upcoming minor release
|
||||
|
||||
Typically, you'll base pull requests off of the `develop` branch, or off of `feature` if you're working on a new major release. **Never** merge pull requests into the `master` branch: This branch only ever merges pull requests from the `develop` branch, to effect a new release.
|
||||
Typically, you'll base pull requests off of the `develop` branch, or off of `feature` if you're working on a new major release. For example, assume that the current NetBox release is v3.3.5. Work applied to the `develop` branch will appear in v3.3.6, and work done under the `feature` branch will be included in the next minor release (v3.4.0).
|
||||
|
||||
For example, assume that the current NetBox release is v3.1.1. Work applied to the `develop` branch will appear in v3.1.2, and work done under the `feature` branch will be included in the next minor release (v3.2.0).
|
||||
!!! warning
|
||||
**Never** merge pull requests into the `master` branch: This branch only ever merges pull requests from the `develop` branch, to effect a new release.
|
||||
|
||||
### Enable Pre-Commit Hooks
|
||||
To create a new branch, first ensure that you've checked out the desired base branch, then run:
|
||||
|
||||
```no-highlight
|
||||
git checkout -B $branchname
|
||||
```
|
||||
|
||||
When naming a new git branch, contributors are strongly encouraged to use the relevant issue number followed by a very brief description of the work:
|
||||
|
||||
```no-highlight
|
||||
$issue-$description
|
||||
```
|
||||
|
||||
The description should be just two or three words to imply the focus of the work being performed. For example, bug #1234 to fix a TypeError exception when creating a device might be named `1234-device-typerror`. This ensures that branches are always follow some logical ordering (e.g. when running `git branch -a`) and helps other developers quickly identify the purpose of each.
|
||||
|
||||
### 3. Enable Pre-Commit Hooks
|
||||
|
||||
NetBox ships with a [git pre-commit hook](https://githooks.com/) script that automatically checks for style compliance and missing database migrations prior to committing changes. This helps avoid erroneous commits that result in CI test failures. You are encouraged to enable it by creating a link to `scripts/git-hooks/pre-commit`:
|
||||
|
||||
```no-highlight
|
||||
$ cd .git/hooks/
|
||||
$ ln -s ../../scripts/git-hooks/pre-commit
|
||||
cd .git/hooks/
|
||||
ln -s ../../scripts/git-hooks/pre-commit
|
||||
```
|
||||
For the pre-commit hooks to work, you will also need to install the pycodestyle package:
|
||||
|
||||
### Create a Python Virtual Environment
|
||||
```no-highlight
|
||||
python -m pip install pycodestyle
|
||||
```
|
||||
...and set up the yarn packages as shown in the [Web UI Development Guide](web-ui.md)
|
||||
|
||||
### 4. Create a Python Virtual Environment
|
||||
|
||||
A [virtual environment](https://docs.python.org/3/tutorial/venv.html) (or "venv" for short) is like a container for a set of Python packages. These allow you to build environments suited to specific projects without interfering with system packages or other projects. When installed per the documentation, NetBox uses a virtual environment in production.
|
||||
|
||||
Create a virtual environment using the `venv` Python module:
|
||||
|
||||
```no-highlight
|
||||
$ mkdir ~/.venv
|
||||
$ python3 -m venv ~/.venv/netbox
|
||||
mkdir ~/.venv
|
||||
python3 -m venv ~/.venv/netbox
|
||||
```
|
||||
|
||||
This will create a directory named `.venv/netbox/` in your home directory, which houses a virtual copy of the Python executable and its related libraries and tooling. When running NetBox for development, it will be run using the Python binary at `~/.venv/netbox/bin/python`.
|
||||
|
||||
!!! info "Where to Create Your Virtual Environments"
|
||||
Keeping virtual environments in `~/.venv/` is a common convention but entirely optional: Virtual environments can be created almost wherever you please. Also consider using [`virtualenvwrapper`](https://virtualenvwrapper.readthedocs.io/en/stable/) to simplify the management of multiple venvs.
|
||||
!!! tip "Virtual Environments"
|
||||
Keeping virtual environments in `~/.venv/` is a common convention but entirely optional: Virtual environments can be created almost wherever you please. Also consider using [`virtualenvwrapper`](https://virtualenvwrapper.readthedocs.io/en/stable/) to simplify the management of multiple environments.
|
||||
|
||||
Once created, activate the virtual environment:
|
||||
|
||||
```no-highlight
|
||||
$ source ~/.venv/netbox/bin/activate
|
||||
(netbox) $
|
||||
source ~/.venv/netbox/bin/activate
|
||||
```
|
||||
|
||||
Notice that the console prompt changes to indicate the active environment. This updates the necessary system environment variables to ensure that any Python scripts are run within the virtual environment.
|
||||
|
||||
### Install Dependencies
|
||||
### 5. Install Required Packages
|
||||
|
||||
With the virtual environment activated, install the project's required Python packages using the `pip` module:
|
||||
With the virtual environment activated, install the project's required Python packages using the `pip` module. Required packages are defined in `requirements.txt`. Each line in this file specifies the name and specific version of a required package.
|
||||
|
||||
```no-highlight
|
||||
(netbox) $ python -m pip install -r requirements.txt
|
||||
Collecting Django==3.1 (from -r requirements.txt (line 1))
|
||||
Cache entry deserialization failed, entry ignored
|
||||
Using cached https://files.pythonhosted.org/packages/2b/5a/4bd5624546912082a1bd2709d0edc0685f5c7827a278d806a20cf6adea28/Django-3.1-py3-none-any.whl
|
||||
...
|
||||
python -m pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Configure NetBox
|
||||
### 6. Configure NetBox
|
||||
|
||||
Within the `netbox/netbox/` directory, copy `configuration_example.py` to `configuration.py` and update the following parameters:
|
||||
|
||||
* `ALLOWED_HOSTS`: This can be set to `['*']` for development purposes
|
||||
* `DATABASE`: PostgreSQL database connection parameters
|
||||
* `REDIS`: Redis configuration, if different from the defaults
|
||||
* `REDIS`: Redis configuration (if different from the defaults)
|
||||
* `SECRET_KEY`: Set to a random string (use `generate_secret_key.py` in the parent directory to generate a suitable key)
|
||||
* `DEBUG`: Set to `True`
|
||||
* `DEVELOPER`: Set to `True` (this enables the creation of new database migrations)
|
||||
|
||||
### Start the Development Server
|
||||
### 7. Start the Development Server
|
||||
|
||||
Django provides a lightweight, auto-updating HTTP/WSGI server for development use. It is started with the `runserver` management command:
|
||||
Django provides a lightweight, auto-updating [HTTP/WSGI server](https://docs.djangoproject.com/en/stable/ref/django-admin/#runserver) for development use. It is started with the `runserver` management command:
|
||||
|
||||
```no-highlight
|
||||
```no-highlight hl_lines="1"
|
||||
$ ./manage.py runserver
|
||||
Watching for file changes with StatReloader
|
||||
Performing system checks...
|
||||
|
||||
System check identified no issues (0 silenced).
|
||||
February 18, 2022 - 20:29:57
|
||||
Django version 4.0.2, using settings 'netbox.settings'
|
||||
August 18, 2022 - 15:17:52
|
||||
Django version 4.0.7, using settings 'netbox.settings'
|
||||
Starting development server at http://127.0.0.1:8000/
|
||||
Quit the server with CONTROL-C.
|
||||
```
|
||||
|
||||
This ensures that your development environment is now complete and operational. Any changes you make to the code base will be automatically adapted by the development server.
|
||||
This ensures that your development environment is now complete and operational. The development server will monitor the development environment and automatically reload in response to any changes made.
|
||||
|
||||
!!! info "IDE Integration"
|
||||
Some IDEs, such as PyCharm, will integrate with Django's development server and allow you to run it directly within the IDE. This is strongly encouraged as it makes for a much more convenient development environment.
|
||||
!!! tip "IDE Integration"
|
||||
Some IDEs, such as the highly-recommended [PyCharm](https://www.jetbrains.com/pycharm/), will integrate with Django's development server and allow you to run it directly within the IDE. This is strongly encouraged as it makes for a much more convenient development environment.
|
||||
|
||||
## UI Development
|
||||
|
||||
For UI development you will need to review the [Web UI Development Guide](web-ui.md)
|
||||
|
||||
## Populating Demo Data
|
||||
|
||||
@@ -131,48 +152,51 @@ The demo data is provided in JSON format and loaded into an empty database using
|
||||
|
||||
## Running Tests
|
||||
|
||||
Prior to committing any substantial changes to the code base, be sure to run NetBox's test suite to catch any potential errors. Tests are run using the `test` management command, which employs Python's [`unittest`](https://docs.python.org/3/library/unittest.html#module-unittest) library. Remember to ensure the Python virtual environment is active before running this command. Also keep in mind that these commands are executed in the `netbox/` directory, not the root directory of the repository.
|
||||
Prior to committing any substantial changes to the code base, be sure to run NetBox's test suite to catch potential errors. Tests are run using the `test` management command, which employs Python's [`unittest`](https://docs.python.org/3/library/unittest.html#module-unittest) library. Remember to ensure that the Python virtual environment is active before running this command. Also keep in mind that these commands are executed in the `netbox/` directory, not the root directory of the repository.
|
||||
|
||||
To avoid potential issues with your local configuration file, set the `NETBOX_CONFIGURATION` to point to the packaged test configuration at `netbox/configuration_testing.py`. This will handle things like ensuring that the dummy plugin is enabled for comprehensive testing.
|
||||
|
||||
```no-highlight
|
||||
$ export NETBOX_CONFIGURATION=netbox.configuration_testing
|
||||
$ cd netbox/
|
||||
$ python manage.py test
|
||||
export NETBOX_CONFIGURATION=netbox.configuration_testing
|
||||
cd netbox/
|
||||
python manage.py test
|
||||
```
|
||||
|
||||
In cases where you haven't made any changes to the database schema (which is typical), you can append the `--keepdb` argument to this command to reuse the test database between runs. This cuts down on the time it takes to run the test suite since the database doesn't have to be rebuilt each time. (Note that this argument will cause errors if you've modified any model fields since the previous test run.)
|
||||
|
||||
```no-highlight
|
||||
$ python manage.py test --keepdb
|
||||
python manage.py test --keepdb
|
||||
```
|
||||
|
||||
You can also reduce testing time by enabling parallel test execution with the `--parallel` flag. (By default, this will run as many parallel tests as you have processors. To avoid sluggishness, it's a good idea to specify a lower number of parallel tests.) This flag can be combined with `--keepdb`, although if you encounter any strange errors, try running the test suite again with parallelization disabled.
|
||||
|
||||
```no-highlight
|
||||
$ python manage.py test --parallel <n>
|
||||
python manage.py test --parallel <n>
|
||||
```
|
||||
|
||||
Finally, it's possible to limit the run to a specific set of tests, specified by their Python path. For example, to run only IPAM and DCIM view tests:
|
||||
|
||||
```no-highlight
|
||||
$ python manage.py test dcim.tests.test_views ipam.tests.test_views
|
||||
python manage.py test dcim.tests.test_views ipam.tests.test_views
|
||||
```
|
||||
|
||||
This is handy for instances where just a few tests are failing and you want to re-run them individually.
|
||||
|
||||
!!! info
|
||||
NetBox uses [django-rich](https://github.com/adamchainz/django-rich) to enhance Django's default `test` management command.
|
||||
|
||||
## Submitting Pull Requests
|
||||
|
||||
Once you're happy with your work and have verified that all tests pass, commit your changes and push it upstream to your fork. Always provide descriptive (but not excessively verbose) commit messages. When working on a specific issue, be sure to prefix your commit message with the word "Fixes" or "Closes" and the issue number (with a hash mark). This tells GitHub to automatically close the referenced issue once the commit has been merged.
|
||||
Once you're happy with your work and have verified that all tests pass, commit your changes and push it upstream to your fork. Always provide descriptive (but not excessively verbose) commit messages. Be sure to prefix your commit message with the word "Fixes" or "Closes" and the relevant issue number (with a hash mark). This tells GitHub to automatically close the referenced issue once the commit has been merged.
|
||||
|
||||
```no-highlight
|
||||
$ git commit -m "Closes #1234: Add IPv5 support"
|
||||
$ git push origin
|
||||
git commit -m "Closes #1234: Add IPv5 support"
|
||||
git push origin
|
||||
```
|
||||
|
||||
Once your fork has the new commit, submit a [pull request](https://github.com/netbox-community/netbox/compare) to the NetBox repo to propose the changes. Be sure to provide a detailed accounting of the changes being made and the reasons for doing so.
|
||||
|
||||
Once submitted, a maintainer will review your pull request and either merge it or request changes. If changes are needed, you can make them via new commits to your fork: The pull request will update automatically.
|
||||
|
||||
!!! note "Remember to Open an Issue First"
|
||||
!!! warning
|
||||
Remember, pull requests are permitted only for **accepted** issues. If an issue you want to work on hasn't been approved by a maintainer yet, it's best to avoid risking your time and effort on a change that might not be accepted. (The one exception to this is trivial changes to the documentation or other non-critical resources.)
|
||||
|
||||
388
docs/development/git-cheat-sheet.md
Normal file
@@ -0,0 +1,388 @@
|
||||
# git Cheat Sheet
|
||||
|
||||
This cheat sheet serves as a convenient reference for NetBox contributors who already somewhat familiar with using git. For a general introduction to the tooling and workflows involved, please see GitHub's guide [Getting started with git](https://docs.github.com/en/get-started/getting-started-with-git/setting-your-username-in-git).
|
||||
|
||||
## Common Operations
|
||||
|
||||
### Clone a Repo
|
||||
|
||||
This copies a remote git repository (e.g. from GitHub) to your local workstation. It will create a new directory bearing the repo's name in the current path.
|
||||
|
||||
``` title="Command"
|
||||
git clone https://github.com/$org-name/$repo-name
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ git clone https://github.com/netbox-community/netbox
|
||||
Cloning into 'netbox'...
|
||||
remote: Enumerating objects: 95112, done.
|
||||
remote: Counting objects: 100% (682/682), done.
|
||||
remote: Compressing objects: 100% (246/246), done.
|
||||
remote: Total 95112 (delta 448), reused 637 (delta 436), pack-reused 94430
|
||||
Receiving objects: 100% (95112/95112), 60.40 MiB | 45.82 MiB/s, done.
|
||||
Resolving deltas: 100% (74979/74979), done.
|
||||
```
|
||||
|
||||
### Pull New Commits
|
||||
|
||||
To update your local branch with any recent upstream commits, run `git pull`.
|
||||
|
||||
``` title="Command"
|
||||
git pull
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ git pull
|
||||
remote: Enumerating objects: 1, done.
|
||||
remote: Counting objects: 100% (1/1), done.
|
||||
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
|
||||
Unpacking objects: 100% (1/1), done.
|
||||
From https://github.com/netbox-community/netbox
|
||||
28bc76695..e0741cc9a develop -> origin/develop
|
||||
Updating 28bc76695..e0741cc9a
|
||||
Fast-forward
|
||||
docs/release-notes/version-3.3.md | 1 +
|
||||
netbox/netbox/settings.py | 1 +
|
||||
2 files changed, 2 insertions(+)
|
||||
```
|
||||
|
||||
### List Branches
|
||||
|
||||
`git branch` lists all local branches. Appending `-a` to this command will list both local (green) and remote (red) branches.
|
||||
|
||||
``` title="Command"
|
||||
git branch -a
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ git branch -a
|
||||
* develop
|
||||
remotes/origin/10170-changelog
|
||||
remotes/origin/HEAD -> origin/develop
|
||||
remotes/origin/develop
|
||||
remotes/origin/feature
|
||||
remotes/origin/master
|
||||
```
|
||||
|
||||
### Switch Branches
|
||||
|
||||
To switch to a different branch, use the `checkout` command.
|
||||
|
||||
``` title="Command"
|
||||
git checkout $branchname
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ git checkout feature
|
||||
Branch 'feature' set up to track remote branch 'feature' from 'origin'.
|
||||
Switched to a new branch 'feature'
|
||||
```
|
||||
|
||||
### Create a New Branch
|
||||
|
||||
Use the `-b` argument with `checkout` to create a new _local_ branch from the current branch.
|
||||
|
||||
``` title="Command"
|
||||
git checkout -b $newbranch
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ git checkout -b 123-fix-foo
|
||||
Switched to a new branch '123-fix-foo'
|
||||
```
|
||||
|
||||
### Rename a Branch
|
||||
|
||||
To rename the current branch, use the `git branch` command with the `-m` argument (for "modify").
|
||||
|
||||
``` title="Command"
|
||||
git branch -m $newname
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ git branch -m jstretch-testing
|
||||
$ git branch
|
||||
develop
|
||||
feature
|
||||
* jstretch-testing
|
||||
```
|
||||
|
||||
### Merge a Branch
|
||||
|
||||
To merge one branch into another, use the `git merge` command. Start by checking out the _destination_ branch, and merge the _source_ branch into it.
|
||||
|
||||
``` title="Command"
|
||||
git merge $sourcebranch
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ git checkout testing
|
||||
Switched to branch 'testing'
|
||||
Your branch is up to date with 'origin/testing'.
|
||||
$ git merge branch2
|
||||
Updating 9a12b5b5f..8ee42390b
|
||||
Fast-forward
|
||||
newfile.py | 0
|
||||
1 file changed, 0 insertions(+), 0 deletions(-)
|
||||
create mode 100644 newfile.py
|
||||
```
|
||||
|
||||
!!! warning "Avoid Merging Remote Branches"
|
||||
You generally want to avoid merging branches that exist on the remote (upstream) repository, such as `develop` and `feature`: Merges into these branches should be done via a pull request on GitHub. Only merge branches when it is necessary to consolidate work you've done locally.
|
||||
|
||||
### Show Pending Changes
|
||||
|
||||
After making changes to files in the repo, `git status` will display a summary of created, modified, and deleted files.
|
||||
|
||||
``` title="Command"
|
||||
git status
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ git status
|
||||
On branch 123-fix-foo
|
||||
Changes not staged for commit:
|
||||
(use "git add <file>..." to update what will be committed)
|
||||
(use "git checkout -- <file>..." to discard changes in working directory)
|
||||
|
||||
modified: README.md
|
||||
|
||||
Untracked files:
|
||||
(use "git add <file>..." to include in what will be committed)
|
||||
|
||||
foo.py
|
||||
|
||||
no changes added to commit (use "git add" and/or "git commit -a")
|
||||
```
|
||||
|
||||
### Stage Changed Files
|
||||
|
||||
Before creating a new commit, modified files must be staged. This is typically done with the `git add` command. You can specify a particular path, or just append `-A` to automatically staged _all_ changed files within the current directory. Run `git status` again to verify what files have been staged.
|
||||
|
||||
``` title="Command"
|
||||
git add -A
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ git add -A
|
||||
$ git status
|
||||
On branch 123-fix-foo
|
||||
Changes to be committed:
|
||||
(use "git reset HEAD <file>..." to unstage)
|
||||
|
||||
modified: README.md
|
||||
new file: foo.py
|
||||
|
||||
```
|
||||
|
||||
### Review Staged Files
|
||||
|
||||
It's a good idea to thoroughly review all staged changes immediately prior to creating a new commit. This can be done using the `git diff` command. Appending the `--staged` argument will show staged changes; omitting it will show changes that have not yet been staged.
|
||||
|
||||
``` title="Command"
|
||||
git diff --staged
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ git diff --staged
|
||||
diff --git a/README.md b/README.md
|
||||
index 93e125079..4344fb514 100644
|
||||
--- a/README.md
|
||||
+++ b/README.md
|
||||
@@ -1,3 +1,8 @@
|
||||
+
|
||||
+Added some lines here
|
||||
+and here
|
||||
+and here too
|
||||
+
|
||||
<div align="center">
|
||||
<img src="https://raw.githubusercontent.com/netbox-community/netbox/develop/docs/netbox_logo.svg" width="400" alt="NetBox logo" />
|
||||
</div>
|
||||
diff --git a/foo.py b/foo.py
|
||||
new file mode 100644
|
||||
index 000000000..e69de29bb
|
||||
```
|
||||
|
||||
### Create a New Commit
|
||||
|
||||
The `git commit` command records your changes to the current branch. Specify a commit message with the `-m` argument. (If omitted, a file editor will be opened to provide a message.
|
||||
|
||||
``` title="Command"
|
||||
git commit -m "Fixes #123: Fixed the thing that was broken"
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ git commit -m "Fixes #123: Fixed the thing that was broken"
|
||||
[123-fix-foo 9a12b5b5f] Fixes #123: Fixed the thing that was broken
|
||||
2 files changed, 5 insertions(+)
|
||||
create mode 100644 foo.py
|
||||
```
|
||||
|
||||
!!! tip "Automatically Closing Issues"
|
||||
GitHub will [automatically close](https://github.blog/2013-01-22-closing-issues-via-commit-messages/) any issues referenced in a commit message by `Fixes:` or `Closes:` when the commit is merged into the repository's default branch. Contributors are strongly encouraged to follow this convention when forming commit messages. (Use "Closes" for feature requests and "Fixes" for bugs.)
|
||||
|
||||
### Push a Commit Upstream
|
||||
|
||||
Once you've made a commit locally, it needs to be pushed upstream to the _remote_ repository (typically called "origin"). This is done with the `git push` command. If this is a new branch that doesn't yet exist on the remote repository, you'll need to set the upstream for it when pushing.
|
||||
|
||||
``` title="Command"
|
||||
git push -u origin $branchname
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ git push -u origin testing
|
||||
Counting objects: 3, done.
|
||||
Delta compression using up to 16 threads.
|
||||
Compressing objects: 100% (3/3), done.
|
||||
Writing objects: 100% (3/3), 377 bytes | 377.00 KiB/s, done.
|
||||
Total 3 (delta 2), reused 0 (delta 0)
|
||||
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
|
||||
remote:
|
||||
remote: Create a pull request for 'testing' on GitHub by visiting:
|
||||
remote: https://github.com/netbox-community/netbox/pull/new/testing
|
||||
remote:
|
||||
To https://github.com/netbox-community/netbox
|
||||
* [new branch] testing -> testing
|
||||
Branch 'testing' set up to track remote branch 'testing' from 'origin'.
|
||||
```
|
||||
|
||||
!!! tip
|
||||
You can apply the following git configuration to automatically set the upstream for all new branches. This obviates the need to specify `-u origin`.
|
||||
|
||||
```
|
||||
git config --global push.default current
|
||||
```
|
||||
|
||||
## The GitHub CLI Client
|
||||
|
||||
GitHub provides a [free CLI client](https://cli.github.com/) to simplify many aspects of interacting with GitHub repositories. Note that this utility is separate from `git`, and must be [installed separately](https://github.com/cli/cli#installation).
|
||||
|
||||
This guide provides some examples of common operations, but be sure to check out the [GitHub CLI manual](https://cli.github.com/manual/) for a complete accounting of available commands.
|
||||
|
||||
### List Open Pull Requests
|
||||
|
||||
``` title="Command"
|
||||
gh pr list
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ gh pr list
|
||||
|
||||
Showing 3 of 3 open pull requests in netbox-community/netbox
|
||||
|
||||
#10223 #7503 API Bulk-Create of Devices does not check Rack-Space 7503-bulkdevice about 17 hours ago
|
||||
#9716 Closes #9599: Add cursor pagination mode lyuyangh:cursor-pagination about 1 month ago
|
||||
#9498 Adds replication and adoption for module import sleepinggenius2:issue_9361 about 2 months ago
|
||||
```
|
||||
|
||||
### Check Out a PR
|
||||
|
||||
This command will automatically check out the remote branch associated with an open pull request.
|
||||
|
||||
``` title="Command"
|
||||
gh pr checkout $number
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ gh pr checkout 10223
|
||||
Branch '7503-bulkdevice' set up to track remote branch '7503-bulkdevice' from 'origin'.
|
||||
Switched to a new branch '7503-bulkdevice'
|
||||
```
|
||||
|
||||
## Fixing Mistakes
|
||||
|
||||
### Modify the Previous Commit
|
||||
|
||||
Sometimes you'll find that you've overlooked a necessary change and need to commit again. If you haven't pushed your most recent commit and just need to make a small tweak or two, you can _amend_ your most recent commit instead of creating a new one.
|
||||
|
||||
First, stage the desired files with `git add` and verify the changes, the issue the `git commit` command with the `--amend` argument. You can also append the `--no-edit` argument if you would like to keep the previous commit message.
|
||||
|
||||
``` title="Command"
|
||||
git commit --amend --no-edit
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ git add -A
|
||||
$ git diff --staged
|
||||
$ git commit --amend --no-edit
|
||||
[testing 239b16921] Added a new file
|
||||
Date: Fri Aug 26 16:30:05 2022 -0400
|
||||
2 files changed, 1 insertion(+)
|
||||
create mode 100644 newfile.py
|
||||
```
|
||||
|
||||
!!! danger "Don't Amend After Pushing"
|
||||
Never amend a commit you've already pushed upstream unless you're **certain** no one else is working on the same branch. Force-pushing will overwrite the change history, which will break any commits from other contributors. When in doubt, create a new commit instead.
|
||||
|
||||
### Undo the Last Commit
|
||||
|
||||
The `git reset` command can be used to undo the most recent commit. (`HEAD~` is equivalent to `HEAD~1` and references the commit prior to the current HEAD.) After making and staging your changes, commit using `-c ORIG_HEAD` to replace the erroneous commit.
|
||||
|
||||
``` title="Command"
|
||||
git reset HEAD~
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ git add -A
|
||||
$ git commit -m "Erroneous commit"
|
||||
[testing 09ce06736] Erroneous commit
|
||||
Date: Mon Aug 29 15:20:04 2022 -0400
|
||||
1 file changed, 1 insertion(+)
|
||||
create mode 100644 BADCHANGE
|
||||
$ git reset HEAD~
|
||||
$ rm BADFILE
|
||||
$ git add -A
|
||||
$ git commit -m "Fixed commit"
|
||||
[testing c585709f3] Fixed commit
|
||||
Date: Mon Aug 29 15:22:38 2022 -0400
|
||||
1 file changed, 65 insertions(+), 20 deletions(-)
|
||||
```
|
||||
|
||||
!!! danger "Don't Reset After Pushing"
|
||||
Resetting only works until you've pushed your local changes upstream. If you've already pushed upstream, use `git revert` instead. This will create a _new_ commit that reverts the erroneous one, but ensures that the git history remains intact.
|
||||
|
||||
### Rebase from Upstream
|
||||
|
||||
If a change has been pushed to the upstream branch since you most recently pulled it, attempting to push a new local commit will fail:
|
||||
|
||||
```
|
||||
$ git push
|
||||
To https://github.com/netbox-community/netbox.git
|
||||
! [rejected] develop -> develop (fetch first)
|
||||
error: failed to push some refs to 'https://github.com/netbox-community/netbox.git'
|
||||
hint: Updates were rejected because the remote contains work that you do
|
||||
hint: not have locally. This is usually caused by another repository pushing
|
||||
hint: to the same ref. You may want to first integrate the remote changes
|
||||
hint: (e.g., 'git pull ...') before pushing again.
|
||||
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
|
||||
```
|
||||
|
||||
To resolve this, first fetch the upstream branch to update your local copy, and then [rebase](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) your local branch to include the new changes. Once the rebase has completed, you can push your local commits upstream.
|
||||
|
||||
``` title="Commands"
|
||||
git fetch
|
||||
git rebase origin/$branchname
|
||||
```
|
||||
|
||||
``` title="Example"
|
||||
$ git fetch
|
||||
remote: Enumerating objects: 1, done.
|
||||
remote: Counting objects: 100% (1/1), done.
|
||||
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
|
||||
Unpacking objects: 100% (1/1), done.
|
||||
From https://github.com/netbox-community/netbox
|
||||
815b2d8a2..8c35ebbb7 develop -> origin/develop
|
||||
$ git rebase origin/develop
|
||||
First, rewinding head to replay your work on top of it...
|
||||
Applying: Further tweaks to the PR template
|
||||
Applying: Changelog for #10176, #10217
|
||||
$ git push
|
||||
Counting objects: 9, done.
|
||||
Delta compression using up to 16 threads.
|
||||
Compressing objects: 100% (9/9), done.
|
||||
Writing objects: 100% (9/9), 1.02 KiB | 1.02 MiB/s, done.
|
||||
Total 9 (delta 6), reused 0 (delta 0)
|
||||
remote: Resolving deltas: 100% (6/6), completed with 5 local objects.
|
||||
To https://github.com/netbox-community/netbox.git
|
||||
8c35ebbb7..ada745324 develop -> develop
|
||||
```
|
||||
@@ -1,22 +1,18 @@
|
||||
# NetBox Development
|
||||
|
||||
NetBox is maintained as a [GitHub project](https://github.com/netbox-community/netbox) under the Apache 2 license. Users are encouraged to submit GitHub issues for feature requests and bug reports, however we are very selective about pull requests. Each pull request must be preceded by an **approved** issue. Please see the `CONTRIBUTING` guide for more direction on contributing to NetBox.
|
||||
Thanks for your interest in contributing to NetBox! This introduction covers a few important things to know before you get started.
|
||||
|
||||
## Communication
|
||||
## The Code
|
||||
|
||||
There are several official forums for communication among the developers and community members:
|
||||
NetBox and many of its related projects are maintained on [GitHub](https://github.com/netbox-community/netbox). GitHub also serves as one of our primary discussion forums. While all the code and discussion is publicly accessible, you'll need register for a [free GitHub account](https://github.com/signup) to engage in participation. Most people begin by [forking](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the NetBox repository under their own GitHub account to begin working on the code.
|
||||
|
||||
* [GitHub issues](https://github.com/netbox-community/netbox/issues) - All feature requests, bug reports, and other substantial changes to the code base **must** be documented in a GitHub issue.
|
||||
* [GitHub discussions](https://github.com/netbox-community/netbox/discussions) - The preferred forum for general discussion and support issues. Ideal for shaping a feature request prior to submitting an issue.
|
||||
* [#netbox on NetDev Community Slack](https://netdev.chat/) - Good for quick chats. Avoid any discussion that might need to be referenced later on, as the chat history is not retained long.
|
||||

|
||||
|
||||
## Governance
|
||||
There are three permanent branches in the repository:
|
||||
|
||||
NetBox follows the [benevolent dictator](http://oss-watch.ac.uk/resources/benevolentdictatorgovernancemodel) model of governance, with [Jeremy Stretch](https://github.com/jeremystretch) ultimately responsible for all changes to the code base. While community contributions are welcomed and encouraged, the lead maintainer's primary role is to ensure the project's long-term maintainability and continued focus on its primary functions.
|
||||
|
||||
## Project Structure
|
||||
|
||||
All development of the current NetBox release occurs in the `develop` branch; releases are packaged from the `master` branch. The `master` branch should _always_ represent the current stable release in its entirety, such that installing NetBox by either downloading a packaged release or cloning the `master` branch provides the same code base. Only pull requests representing new releases should be merged into `master`.
|
||||
* `master` - The current stable release. Individual changes should never be pushed directly to this branch, but rather merged from `develop`.
|
||||
* `develop` - Active development for the upcoming patch release. Pull requests will typically be based on this branch unless they introduce breaking changes that must be deferred until the next minor release.
|
||||
* `feature` - New feature work to be introduced in the next minor release (e.g. from v3.3 to v3.4).
|
||||
|
||||
NetBox components are arranged into Django apps. Each app holds the models, views, and other resources relevant to a particular function:
|
||||
|
||||
@@ -31,3 +27,34 @@ NetBox components are arranged into Django apps. Each app holds the models, view
|
||||
* `wireless`: Wireless links and LANs
|
||||
|
||||
All core functionality is stored within the `netbox/` subdirectory. HTML templates are stored in a common `templates/` directory, with model- and view-specific templates arranged by app. Documentation is kept in the `docs/` root directory.
|
||||
|
||||
## Proposing Changes
|
||||
|
||||
All substantial changes made to the code base are tracked using [GitHub issues](https://docs.github.com/en/issues). Feature requests, bug reports, and similar proposals must all be filed as issues and approved by a maintainer before work begins. This ensures that all changes to the code base are properly documented for future reference.
|
||||
|
||||
To submit a new feature request or bug report for NetBox, select and complete the appropriate [issue template](https://github.com/netbox-community/netbox/issues/new/choose). Once your issue has been approved, you're welcome to submit a [pull request](https://docs.github.com/en/pull-requests) containing your proposed changes.
|
||||
|
||||

|
||||
|
||||
Check out our [issue intake policy](https://github.com/netbox-community/netbox/wiki/Issue-Intake-Policy) for an overview of the issue triage and approval processes.
|
||||
|
||||
!!! tip
|
||||
Avoid starting work on a proposal before it has been accepted. Not all proposed changes will be accepted, and we'd hate for you to waste time working on code that might not make it into the project.
|
||||
|
||||
## Getting Help
|
||||
|
||||
There are two primary forums for getting assistance with NetBox development:
|
||||
|
||||
* [GitHub discussions](https://github.com/netbox-community/netbox/discussions) - The preferred forum for general discussion and support issues. Ideal for shaping a feature requests prior to submitting an issue.
|
||||
* [#netbox on NetDev Community Slack](https://netdev.chat/) - Good for quick chats. Avoid any discussion that might need to be referenced later on, as the chat history is not retained indefinitely.
|
||||
|
||||
!!! note
|
||||
Don't use GitHub issues to ask for help: These are reserved for proposed code changes only.
|
||||
|
||||
## Governance
|
||||
|
||||
NetBox follows the [benevolent dictator](http://oss-watch.ac.uk/resources/benevolentdictatorgovernancemodel) model of governance, with [Jeremy Stretch](https://github.com/jeremystretch) ultimately responsible for all changes to the code base. While community contributions are welcomed and encouraged, the lead maintainer's primary role is to ensure the project's long-term maintainability and continued focus on its primary functions.
|
||||
|
||||
## Licensing
|
||||
|
||||
The entire NetBox project is licensed as open source under the [Apache 2.0 license](https://github.com/netbox-community/netbox/blob/master/LICENSE.txt). This is a very permissive license which allows unlimited redistribution of all code within the project. Note that all submissions to the project are subject to the same license.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Model Types
|
||||
|
||||
A NetBox model represents a discrete object type such as a device or IP address. Each model is defined as a Python class and has its own SQL table. All NetBox data models can be categorized by type.
|
||||
A NetBox model represents a discrete object type such as a device or IP address. Per [Django convention](https://docs.djangoproject.com/en/stable/topics/db/models/), each model is defined as a Python class and has its own SQL table. All NetBox data models can be categorized by type.
|
||||
|
||||
The Django [content types](https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/) framework can be used to reference models within the database. A ContentType instance references a model by its `app_label` and `name`: For example, the Site model is referred to as `dcim.site`. The content type combined with an object's primary key form a globally unique identifier for the object (e.g. `dcim.site:123`).
|
||||
|
||||
@@ -40,6 +40,7 @@ The Django [content types](https://docs.djangoproject.com/en/stable/ref/contrib/
|
||||
* [dcim.RackReservation](../models/dcim/rackreservation.md)
|
||||
* [dcim.Site](../models/dcim/site.md)
|
||||
* [dcim.VirtualChassis](../models/dcim/virtualchassis.md)
|
||||
* [dcim.VirtualDeviceContext](../models/dcim/virtualdevicecontext.md)
|
||||
* [ipam.Aggregate](../models/ipam/aggregate.md)
|
||||
* [ipam.ASN](../models/ipam/asn.md)
|
||||
* [ipam.FHRPGroup](../models/ipam/fhrpgroup.md)
|
||||
|
||||
@@ -1,62 +1,62 @@
|
||||
# Release Checklist
|
||||
|
||||
## Minor Version Bumps
|
||||
This documentation describes the process of packaging and publishing a new NetBox release. There are three types of release:
|
||||
|
||||
### Address Pinned Dependencies
|
||||
* Major release (e.g. v2.11 to v3.0)
|
||||
* Minor release (e.g. v3.2 to v3.3)
|
||||
* Patch release (e.g. v3.3.0 to v3.3.1)
|
||||
|
||||
Check `base_requirements.txt` for any dependencies pinned to a specific version, and upgrade them to their most stable release (where possible).
|
||||
While major releases generally introduce some very substantial change to the application, they are typically treated the same as minor version increments for the purpose of release packaging.
|
||||
|
||||
### Link to the Release Notes Page
|
||||
## Minor Version Releases
|
||||
|
||||
Add the release notes (`/docs/release-notes/X.Y.md`) to the table of contents within `mkdocs.yml`, and add a summary of the major changes to `index.md`.
|
||||
### Address Constrained Dependencies
|
||||
|
||||
### Manually Perform a New Install
|
||||
|
||||
Install `mkdocs` in your local environment, then start the documentation server:
|
||||
|
||||
```no-highlight
|
||||
$ pip install -r docs/requirements.txt
|
||||
$ mkdocs serve
|
||||
```
|
||||
|
||||
Follow these instructions to perform a new installation of NetBox. This process must _not_ be automated: The goal of this step is to catch any errors or omissions in the documentation, and ensure that it is kept up-to-date for each release. Make any necessary changes to the documentation before proceeding with the release.
|
||||
|
||||
### Close the Release Milestone
|
||||
|
||||
Close the release milestone on GitHub after ensuring there are no remaining open issues associated with it.
|
||||
|
||||
### Merge the Release Branch
|
||||
|
||||
Submit a pull request to merge the `feature` branch into the `develop` branch in preparation for its release.
|
||||
|
||||
---
|
||||
|
||||
## All Releases
|
||||
|
||||
### Update Requirements
|
||||
|
||||
Required Python packages are maintained in two files. `base_requirements.txt` contains a list of all the packages required by NetBox. Some of them may be pinned to a specific version of the package due to a known issue. For example:
|
||||
Sometimes it becomes necessary to constrain dependencies to a particular version, e.g. to work around a bug in a newer release or to avoid a breaking change that we have yet to accommodate. (Another common example is to limit the upstream Django release.) For example:
|
||||
|
||||
```
|
||||
# https://github.com/encode/django-rest-framework/issues/6053
|
||||
djangorestframework==3.8.1
|
||||
```
|
||||
|
||||
The other file is `requirements.txt`, which lists each of the required packages pinned to its current stable version. When NetBox is installed, the Python environment is configured to match this file. This helps ensure that a new release of a dependency doesn't break NetBox.
|
||||
These version constraints are added to `base_requirements.txt` to ensure that newer packages are not installed when updating the pinned dependencies in `requirements.txt` (see the [Update Requirements](#update-requirements) section below). Before each new minor version of NetBox is released, all such constraints on dependent packages should be addressed if feasible. This guards against the collection of stale constraints over time.
|
||||
|
||||
Every release should refresh `requirements.txt` so that it lists the most recent stable release of each package. To do this:
|
||||
### Close the Release Milestone
|
||||
|
||||
1. Create a new virtual environment.
|
||||
2. Install the latest version of all required packages `pip install -U -r base_requirements.txt`).
|
||||
3. Run all tests and check that the UI and API function as expected.
|
||||
4. Review each requirement's release notes for any breaking or otherwise noteworthy changes.
|
||||
5. Update the package versions in `requirements.txt` as appropriate.
|
||||
Close the [release milestone](https://github.com/netbox-community/netbox/milestones) on GitHub after ensuring there are no remaining open issues associated with it.
|
||||
|
||||
In cases where upgrading a dependency to its most recent release is breaking, it should be pinned to its current minor version in `base_requirements.txt` (with an explanatory comment) and revisited for the next major NetBox release.
|
||||
### Update the Release Notes
|
||||
|
||||
### Verify CI Build Status
|
||||
Check that a link to the release notes for the new version is present in the navigation menu (defined in `mkdocs.yml`), and that a summary of all major new features has been added to `docs/index.md`.
|
||||
|
||||
Ensure that continuous integration testing on the `develop` branch is completing successfully.
|
||||
### Manually Perform a New Install
|
||||
|
||||
Start the documentation server and navigate to the current version of the installation docs:
|
||||
|
||||
```no-highlight
|
||||
mkdocs serve
|
||||
```
|
||||
|
||||
Follow these instructions to perform a new installation of NetBox in a temporary environment. This process must not be automated: The goal of this step is to catch any errors or omissions in the documentation, and ensure that it is kept up-to-date for each release. Make any necessary changes to the documentation before proceeding with the release.
|
||||
|
||||
### Merge the Release Branch
|
||||
|
||||
Submit a pull request to merge the `feature` branch into the `develop` branch in preparation for its release. Once it has been merged, continue with the section for patch releases below.
|
||||
|
||||
---
|
||||
|
||||
## Patch Releases
|
||||
|
||||
### Update Requirements
|
||||
|
||||
Before each release, update each of NetBox's Python dependencies to its most recent stable version. These are defined in `requirements.txt`, which is updated from `base_requirements.txt` using `pip`. To do this:
|
||||
|
||||
1. Upgrade the installed version of all required packages in your environment (`pip install -U -r base_requirements.txt`).
|
||||
2. Run all tests and check that the UI and API function as expected.
|
||||
3. Review each requirement's release notes for any breaking or otherwise noteworthy changes.
|
||||
4. Update the package versions in `requirements.txt` as appropriate.
|
||||
|
||||
In cases where upgrading a dependency to its most recent release is breaking, it should be constrained to its current minor version in `base_requirements.txt` with an explanatory comment and revisited for the next major NetBox release (see the [Address Constrained Dependencies](#address-constrained-dependencies) section above).
|
||||
|
||||
### Update Version and Changelog
|
||||
|
||||
@@ -64,28 +64,35 @@ Ensure that continuous integration testing on the `develop` branch is completing
|
||||
* Update the example version numbers in the feature request and bug report templates under `.github/ISSUE_TEMPLATES/`.
|
||||
* Replace the "FUTURE" placeholder in the release notes with the current date.
|
||||
|
||||
Commit these changes to the `develop` branch.
|
||||
Commit these changes to the `develop` branch and push upstream.
|
||||
|
||||
### Verify CI Build Status
|
||||
|
||||
Ensure that continuous integration testing on the `develop` branch is completing successfully. If it fails, take action to correct the failure before proceding with the release.
|
||||
|
||||
### Submit a Pull Request
|
||||
|
||||
Submit a pull request title **"Release vX.Y.Z"** to merge the `develop` branch into `master`. Copy the documented release notes into the pull request's body.
|
||||
Submit a pull request titled **"Release vX.Y.Z"** to merge the `develop` branch into `master`. Copy the documented release notes into the pull request's body.
|
||||
|
||||
Once CI has completed on the PR, merge it.
|
||||
Once CI has completed on the PR, merge it. This effects a new release in the `master` branch.
|
||||
|
||||
### Create a New Release
|
||||
|
||||
Draft a [new release](https://github.com/netbox-community/netbox/releases/new) with the following parameters.
|
||||
Create a [new release](https://github.com/netbox-community/netbox/releases/new) on GitHub with the following parameters.
|
||||
|
||||
* **Tag:** Current version (e.g. `v2.9.9`)
|
||||
* **Tag:** Current version (e.g. `v3.3.1`)
|
||||
* **Target:** `master`
|
||||
* **Title:** Version and date (e.g. `v2.9.9 - 2020-11-09`)
|
||||
* **Title:** Version and date (e.g. `v3.3.1 - 2022-08-25`)
|
||||
* **Description:** Copy from the pull request body
|
||||
|
||||
Copy the description from the pull request to the release.
|
||||
Once created, the release will become available for users to install.
|
||||
|
||||
### Update the Development Version
|
||||
|
||||
On the `develop` branch, update `VERSION` in `settings.py` to point to the next release. For example, if you just released v2.9.9, set:
|
||||
On the `develop` branch, update `VERSION` in `settings.py` to point to the next release. For example, if you just released v3.3.1, set:
|
||||
|
||||
```
|
||||
VERSION = 'v2.9.10-dev'
|
||||
VERSION = 'v3.3.2-dev'
|
||||
```
|
||||
|
||||
Commit this change with the comment "PRVB" (for _post-release version bump_) and push the commit upstream.
|
||||
|
||||
37
docs/development/search.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Search
|
||||
|
||||
NetBox v3.4 introduced a new global search mechanism, which employs the `extras.CachedValue` model to store discrete field values from many models in a single table.
|
||||
|
||||
## SearchIndex
|
||||
|
||||
To enable search support for a model, declare and register a subclass of `netbox.search.SearchIndex` for it. Typically, this will be done within an app's `search.py` module.
|
||||
|
||||
```python
|
||||
from netbox.search import SearchIndex, register_search
|
||||
|
||||
@register_search
|
||||
class MyModelIndex(SearchIndex):
|
||||
model = MyModel
|
||||
fields = (
|
||||
('name', 100),
|
||||
('description', 500),
|
||||
('comments', 5000),
|
||||
)
|
||||
```
|
||||
|
||||
A SearchIndex subclass defines both its model and a list of two-tuples specifying which model fields to be indexed and the weight (precedence) associated with each. Guidance on weight assignment for fields is provided below.
|
||||
|
||||
### Field Weight Guidance
|
||||
|
||||
| Weight | Field Role | Examples |
|
||||
|--------|--------------------------------------------------|----------------------------------------------------|
|
||||
| 50 | Unique serialized attribute | Device.asset_tag |
|
||||
| 60 | Unique serialized attribute (per related object) | Device.serial |
|
||||
| 100 | Primary human identifier | Device.name, Circuit.cid, Cable.label |
|
||||
| 110 | Slug | Site.slug |
|
||||
| 200 | Secondary identifier | Provider.account, DeviceType.part_number |
|
||||
| 300 | Highly unique descriptive attribute | CircuitTermination.xconnect_id, IPAddress.dns_name |
|
||||
| 500 | Description | Site.description |
|
||||
| 1000 | Custom field default | - |
|
||||
| 2000 | Other discrete attribute | CircuitTermination.port_speed |
|
||||
| 5000 | Comment field | Site.comments |
|
||||
@@ -1,34 +1,53 @@
|
||||
# Style Guide
|
||||
|
||||
NetBox generally follows the [Django style guide](https://docs.djangoproject.com/en/stable/internals/contributing/writing-code/coding-style/), which is itself based on [PEP 8](https://www.python.org/dev/peps/pep-0008/). [Pycodestyle](https://github.com/pycqa/pycodestyle) is used to validate code formatting, ignoring certain violations. See `scripts/cibuild.sh` for details.
|
||||
NetBox generally follows the [Django style guide](https://docs.djangoproject.com/en/stable/internals/contributing/writing-code/coding-style/), which is itself based on [PEP 8](https://www.python.org/dev/peps/pep-0008/). [Pycodestyle](https://github.com/pycqa/pycodestyle) is used to validate code formatting, ignoring certain violations.
|
||||
|
||||
## PEP 8 Exceptions
|
||||
## Code
|
||||
|
||||
* Wildcard imports (for example, `from .constants import *`) are acceptable under any of the following conditions:
|
||||
* The library being import contains only constant declarations (e.g. `constants.py`)
|
||||
* The library being imported explicitly defines `__all__`
|
||||
### General Guidance
|
||||
|
||||
* Maximum line length is 120 characters (E501)
|
||||
* This does not apply to HTML templates or to automatically generated code (e.g. database migrations).
|
||||
* When in doubt, remain consistent: It is better to be consistently incorrect than inconsistently correct. If you notice in the course of unrelated work a pattern that should be corrected, continue to follow the pattern for now and submit a separate bug report so that the entire code base can be evaluated at a later point.
|
||||
|
||||
* Line breaks are permitted following binary operators (W504)
|
||||
* Prioritize readability over concision. Python is a very flexible language that typically offers several multiple options for expressing a given piece of logic, but some may be more friendly to the reader than others. (List comprehensions are particularly vulnerable to over-optimization.) Always remain considerate of the future reader who may need to interpret your code without the benefit of the context within which you are writing it.
|
||||
|
||||
## Enforcing Code Style
|
||||
* Include a newline at the end of every file.
|
||||
|
||||
The `pycodestyle` utility (previously `pep8`) is used by the CI process to enforce code style. It is strongly recommended to include as part of your commit process. A git commit hook is provided in the source at `scripts/git-hooks/pre-commit`. Linking to this script from `.git/hooks/` will invoke `pycodestyle` prior to every commit attempt and abort if the validation fails.
|
||||
* No easter eggs. While they can be fun, NetBox must be considered as a business-critical tool. The potential, however minor, for introducing a bug caused by unnecessary code is best avoided entirely.
|
||||
|
||||
```
|
||||
$ cd .git/hooks/
|
||||
$ ln -s ../../scripts/git-hooks/pre-commit
|
||||
```
|
||||
* Constants (variables which do not change) should be declared in `constants.py` within each app. Wildcard imports from the file are acceptable.
|
||||
|
||||
To invoke `pycodestyle` manually, run:
|
||||
* Every model must have a [docstring](https://peps.python.org/pep-0257/). Every custom method should include an explanation of its function.
|
||||
|
||||
* Nested API serializers generate minimal representations of an object. These are stored separately from the primary serializers to avoid circular dependencies. Always import nested serializers from other apps directly. For example, from within the DCIM app you would write `from ipam.api.nested_serializers import NestedIPAddressSerializer`.
|
||||
|
||||
### PEP 8 Exceptions
|
||||
|
||||
NetBox ignores certain PEP8 assertions. These are listed below.
|
||||
|
||||
#### Wildcard Imports
|
||||
|
||||
Wildcard imports (for example, `from .constants import *`) are acceptable under any of the following conditions:
|
||||
|
||||
* The library being import contains only constant declarations (e.g. `constants.py`)
|
||||
* The library being imported explicitly defines `__all__`
|
||||
|
||||
#### Maximum Line Length (E501)
|
||||
|
||||
NetBox does not restrict lines to a maximum length of 79 characters. We use a maximum line length of 120 characters, however this is not enforced by CI. The maximum length does not apply to HTML templates or to automatically generated code (e.g. database migrations).
|
||||
|
||||
#### Line Breaks Following Binary Operators (W504)
|
||||
|
||||
Line breaks are permitted following binary operators.
|
||||
|
||||
### Enforcing Code Style
|
||||
|
||||
The [`pycodestyle`](https://pypi.org/project/pycodestyle/) utility (formerly `pep8`) is used by the CI process to enforce code style. A [pre-commit hook](./getting-started.md#2-enable-pre-commit-hooks) which runs this automatically is included with NetBox. To invoke `pycodestyle` manually, run:
|
||||
|
||||
```
|
||||
pycodestyle --ignore=W504,E501 netbox/
|
||||
```
|
||||
|
||||
## Introducing New Dependencies
|
||||
### Introducing New Dependencies
|
||||
|
||||
The introduction of a new dependency is best avoided unless it is absolutely necessary. For small features, it's generally preferable to replicate functionality within the NetBox code base rather than to introduce reliance on an external project. This reduces both the burden of tracking new releases and our exposure to outside bugs and supply chain attacks.
|
||||
|
||||
@@ -39,24 +58,22 @@ If there's a strong case for introducing a new dependency, it must meet the foll
|
||||
* It must be actively maintained, with no longer than one year between releases.
|
||||
* It must be available via the [Python Package Index](https://pypi.org/) (PyPI).
|
||||
|
||||
When adding a new dependency, a short description of the package and the URL of its code repository must be added to `base_requirements.txt`. Additionally, a line specifying the package name pinned to the current stable release must be added to `requirements.txt`. This ensures that NetBox will install only the known-good release and simplify support efforts.
|
||||
When adding a new dependency, a short description of the package and the URL of its code repository must be added to `base_requirements.txt`. Additionally, a line specifying the package name pinned to the current stable release must be added to `requirements.txt`. This ensures that NetBox will install only the known-good release.
|
||||
|
||||
## General Guidance
|
||||
## Written Works
|
||||
|
||||
* When in doubt, remain consistent: It is better to be consistently incorrect than inconsistently correct. If you notice in the course of unrelated work a pattern that should be corrected, continue to follow the pattern for now and submit a separate bug report so that the entire code base can be evaluated at a later point.
|
||||
### General Guidance
|
||||
|
||||
* Prioritize readability over concision. Python is a very flexible language that typically offers several options for expressing a given piece of logic, but some may be more friendly to the reader than others. (List comprehensions are particularly vulnerable to over-optimization.) Always remain considerate of the future reader who may need to interpret your code without the benefit of the context within which you are writing it.
|
||||
* Written material must always meet a reasonable professional standard, with proper grammar, spelling, and punctuation.
|
||||
|
||||
* No easter eggs. While they can be fun, NetBox must be considered as a business-critical tool. The potential, however minor, for introducing a bug caused by unnecessary logic is best avoided entirely.
|
||||
* Use two line breaks between paragraphs.
|
||||
|
||||
* Constants (variables which generally do not change) should be declared in `constants.py` within each app. Wildcard imports from the file are acceptable.
|
||||
* Use only a single space between sentences.
|
||||
|
||||
* Every model should have a docstring. Every custom method should include an explanation of its function.
|
||||
* All documentation is to be written in [Markdown](../reference/markdown.md), with modest amounts of HTML permitted where needed to overcome technical limitations.
|
||||
|
||||
* Nested API serializers generate minimal representations of an object. These are stored separately from the primary serializers to avoid circular dependencies. Always import nested serializers from other apps directly. For example, from within the DCIM app you would write `from ipam.api.nested_serializers import NestedIPAddressSerializer`.
|
||||
### Branding
|
||||
|
||||
## Branding
|
||||
|
||||
* When referring to NetBox in writing, use the proper form "NetBox," with the letters N and B capitalized. The lowercase form "netbox" should be used in code, filenames, etc. But never "Netbox" or any other deviation.
|
||||
* When referring to NetBox in writing, use the proper form "NetBox," with the letters N and B capitalized. The lowercase form "netbox" should be used in code, filenames, etc. but never "Netbox" or any other deviation.
|
||||
|
||||
* There is an SVG form of the NetBox logo at [docs/netbox_logo.svg](../netbox_logo.svg). It is preferred to use this logo for all purposes as it scales to arbitrary sizes without loss of resolution. If a raster image is required, the SVG logo should be converted to a PNG image of the prescribed size.
|
||||
|
||||
@@ -71,13 +71,13 @@ To learn more about this feature, check out the [export template documentation](
|
||||
|
||||
NetBox administrators can install custom Python scripts, known as _reports_, which run within NetBox and can be executed and analyzed within the NetBox UI. Reports are a great way to evaluate NetBox objects against a set of arbitrary rules. For example, you could write a report to check that every router has a loopback interface with an IP address assigned, or that every site has a minimum set of VLANs defined.
|
||||
|
||||
When a report runs, its logs messages pertaining to the operations being performed, and will ultimately result in either a pass or fail. Reports can be executed via the UI, REST API, or CLI (as a management command).
|
||||
When a report runs, its logs messages pertaining to the operations being performed, and will ultimately result in either a pass or fail. Reports can be executed via the UI, REST API, or CLI (as a management command). They can be run immediately or scheduled to run at a future time.
|
||||
|
||||
To learn more about this feature, check out the [documentation for reports](../customization/reports.md).
|
||||
|
||||
## Custom Scripts
|
||||
|
||||
Custom scripts are similar to reports, but more powerful. A custom script can prompt the user for input via a form (or API data), and is built to do much more than just reporting. Custom scripts are generally used to automate tasks, such as the population of new objects in NetBox, or exchanging data with external systems.
|
||||
Custom scripts are similar to reports, but more powerful. A custom script can prompt the user for input via a form (or API data), and is built to do much more than just reporting. Custom scripts are generally used to automate tasks, such as the population of new objects in NetBox, or exchanging data with external systems. As with reports, they can be run via the UI, REST API, or CLI, and be scheduled to execute at a future time.
|
||||
|
||||
The complete Python environment is available to a custom script, including all of NetBox's internal mechanisms: There are no artificial restrictions on what a script can do. As such, custom scripting is considered an advanced feature and requires sufficient familiarity with Python and NetBox's data model.
|
||||
|
||||
|
||||
@@ -65,6 +65,10 @@ Each device can have an operational status, functional role, and software platfo
|
||||
|
||||
Sometimes it is necessary to model a set of physical devices as sharing a single management plane. Perhaps the most common example of such a scenario is stackable switches. These can be modeled as virtual chassis in NetBox, with one device acting as the chassis master and the rest as members. All components of member devices will appear on the master.
|
||||
|
||||
### Virtual Device Contexts
|
||||
|
||||
A virtual device context (VDC) is a logical partition within a device. Each VDC operates autonomously but shares a common pool of resources. Each interface can be assigned to one or more VDCs on its device.
|
||||
|
||||
## Module Types & Modules
|
||||
|
||||
Much like device types and devices, module types can instantiate discrete modules, which are hardware components installed within devices. Modules often have their own child components, which become available to the parent device. For example, when modeling a chassis-based switch with multiple line cards in NetBox, the chassis would be created (from a device type) as a device, and each of its line cards would be instantiated from a module type as a module installed in one of the device's module bays.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Journaling
|
||||
|
||||
All primary objects in NetBox support journaling. A journal is a collection of human-generated notes and comments about an object maintained for historical context. It supplements NetBox's change log to provide additional information about why changes have been made or to convey events which occur outside NetBox. Unlike the change log, in which records typically expire after a configurable period of time, journal entries persist for the life of their associated object.
|
||||
All primary and organizational models in NetBox support journaling. A journal is a collection of human-generated notes and comments about an object maintained for historical context. It supplements NetBox's change log to provide additional information about why changes have been made or to convey events which occur outside NetBox. Unlike the change log, in which records typically expire after a configurable period of time, journal entries persist for the life of their associated object.
|
||||
|
||||
Each journal entry has a selectable kind (info, success, warning, or danger) and a user-populated `comments` field. Each entry automatically records the date, time, and associated user upon being created.
|
||||
|
||||
27
docs/features/search.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Search
|
||||
|
||||
## Global Search
|
||||
|
||||
NetBox includes a powerful global search engine, providing a single convenient interface to search across its complex data model. Relevant fields on each model are indexed according to their precedence, so that the most relevant results are returned first. When objects are created or modified, the search index is updated immediately, ensuring real-time accuracy.
|
||||
|
||||
When entering a search query, the user can choose a specific lookup type: exact match, partial match, etc. When a partial match is found, the matching portion of the applicable field value is included with each result so that the user can easily determine its relevance.
|
||||
|
||||
Custom fields defined by NetBox administrators are also included in search results if configured with a search weight. Additionally, NetBox plugins can register their own custom models for inclusion alongside core models.
|
||||
|
||||
## Saved Filters
|
||||
|
||||
Each type of object in NetBox is accompanied by an extensive set of filters, each tied to a specific attribute, which enable the creation of complex queries. Often you'll find that certain queries are used routinely to apply some set of prescribed conditions to a query. Once a set of filters has been applied, NetBox offers the option to save it for future use.
|
||||
|
||||
For example, suppose you often need to locate all planned devices of a certain type within a region. The applicable filters can be applied and then saved as custom named filter for reuse, such that
|
||||
|
||||
```
|
||||
?status=planned&device_type_id=78®ion_id=12
|
||||
```
|
||||
|
||||
becomes
|
||||
|
||||
```
|
||||
?filter=my-custom-filter
|
||||
```
|
||||
|
||||
These saved filters can be used both within the UI and for API queries.
|
||||
@@ -20,12 +20,14 @@ To create a new object in NetBox, find the object type in the navigation menu an
|
||||
|
||||
## Bulk Import (CSV/YAML)
|
||||
|
||||
NetBox supports the bulk import of new objects using CSV-formatted data. This method can be ideal for importing spreadsheet data, which is very easy to convert to CSV data. CSV data can be imported either as raw text using the form field, or by uploading a properly formatted CSV file.
|
||||
NetBox supports the bulk import of new objects, and updating of existing objects using CSV-formatted data. This method can be ideal for importing spreadsheet data, which is very easy to convert to CSV data. CSV data can be imported either as raw text using the form field, or by uploading a properly formatted CSV file.
|
||||
|
||||
When viewing the CSV import form for an object type, you'll notice that the headers for the required columns have been pre-populated. Each form has a table beneath it titled "CSV Field Options," which lists _all_ supported columns for your reference. (Generally, these map to the fields you see in the corresponding creation form for individual objects.)
|
||||
|
||||
<!-- TODO: Screenshot -->
|
||||
|
||||
If an "id" field is added the data will be used to update existing records instead of importing new objects.
|
||||
|
||||
Note that some models (namely device types and module types) do not support CSV import. Instead, they accept YAML-formatted data to facilitate the import of both the parent object as well as child components.
|
||||
|
||||
## Scripting
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
NetBox is the leading solution for modeling and documenting modern networks. By combining the traditional disciplines of IP address management (IPAM) and datacenter infrastructure management (DCIM) with powerful APIs and extensions, NetBox provides the ideal "source of truth" to power network automation. Read on to discover why thousands of organizations worldwide put NetBox at the heart of their infrastructure.
|
||||
|
||||
[](./media/screenshots/netbox-ui.png)
|
||||
|
||||
## :material-server-network: Built for Networks
|
||||
|
||||
Unlike general-purpose CMDBs, NetBox has curated a data model which caters specifically to the needs of network engineers and operators. It delivers a wide assortment of object types carefully crafted to best serve the needs of infrastructure design and documentation. These cover all facets of network technology, from IP address managements to cabling to overlays and more:
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
This section entails the installation and configuration of a local PostgreSQL database. If you already have a PostgreSQL database service in place, skip to [the next section](2-redis.md).
|
||||
|
||||
!!! warning "PostgreSQL 10 or later required"
|
||||
NetBox requires PostgreSQL 10 or later. Please note that MySQL and other relational databases are **not** supported.
|
||||
!!! warning "PostgreSQL 11 or later required"
|
||||
NetBox requires PostgreSQL 11 or later. Please note that MySQL and other relational databases are **not** supported.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -35,7 +35,7 @@ sudo systemctl start postgresql
|
||||
sudo systemctl enable postgresql
|
||||
```
|
||||
|
||||
Before continuing, verify that you have installed PostgreSQL 10 or later:
|
||||
Before continuing, verify that you have installed PostgreSQL 11 or later:
|
||||
|
||||
```no-highlight
|
||||
psql -V
|
||||
|
||||
@@ -7,7 +7,7 @@ This section of the documentation discusses installing and configuring the NetBo
|
||||
Begin by installing all system packages required by NetBox and its dependencies.
|
||||
|
||||
!!! warning "Python 3.8 or later required"
|
||||
NetBox v3.2 requires Python 3.8, 3.9, or 3.10.
|
||||
NetBox requires Python 3.8, 3.9, 3.10 or 3.11.
|
||||
|
||||
=== "Ubuntu"
|
||||
|
||||
@@ -36,7 +36,7 @@ This documentation provides two options for installing NetBox: from a downloadab
|
||||
Download the [latest stable release](https://github.com/netbox-community/netbox/releases) from GitHub as a tarball or ZIP archive and extract it to your desired path. In this example, we'll use `/opt/netbox` as the NetBox root.
|
||||
|
||||
```no-highlight
|
||||
sudo wget https://github.com/netbox-community/netbox/archive/vX.Y.Z.tar.gz
|
||||
sudo wget https://github.com/netbox-community/netbox/archive/refs/tags/vX.Y.Z.tar.gz
|
||||
sudo tar -xzf vX.Y.Z.tar.gz -C /opt
|
||||
sudo ln -s /opt/netbox-X.Y.Z/ /opt/netbox
|
||||
```
|
||||
@@ -225,6 +225,9 @@ Once NetBox has been configured, we're ready to proceed with the actual installa
|
||||
* Builds the documentation locally (for offline use)
|
||||
* Aggregate static resource files on disk
|
||||
|
||||
!!! warning
|
||||
If you still have a Python virtual environment active from a previous installation step, disable it now by running the `deactivate` command. This will avoid errors on systems where `sudo` has been configured to preserve the user's current environment.
|
||||
|
||||
```no-highlight
|
||||
sudo /opt/netbox/upgrade.sh
|
||||
```
|
||||
|
||||
@@ -46,7 +46,7 @@ Next, create a file in the same directory as `configuration.py` (typically `/opt
|
||||
### General Server Configuration
|
||||
|
||||
!!! info
|
||||
When using Windows Server 2012 you may need to specify a port on `AUTH_LDAP_SERVER_URI`. Use `3269` for secure, or `3268` for non-secure.
|
||||
When using Active Directory you may need to specify a port on `AUTH_LDAP_SERVER_URI` to authenticate users from all domains in the forest. Use `3269` for secure, or `3268` for non-secure access to the GC (Global Catalog).
|
||||
|
||||
```python
|
||||
import ldap
|
||||
@@ -67,6 +67,16 @@ AUTH_LDAP_BIND_PASSWORD = "demo"
|
||||
# Note that this is a NetBox-specific setting which sets:
|
||||
# ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
||||
LDAP_IGNORE_CERT_ERRORS = True
|
||||
|
||||
# Include this setting if you want to validate the LDAP server certificates against a CA certificate directory on your server
|
||||
# Note that this is a NetBox-specific setting which sets:
|
||||
# ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, LDAP_CA_CERT_DIR)
|
||||
LDAP_CA_CERT_DIR = '/etc/ssl/certs'
|
||||
|
||||
# Include this setting if you want to validate the LDAP server certificates against your own CA.
|
||||
# Note that this is a NetBox-specific setting which sets:
|
||||
# ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, LDAP_CA_CERT_FILE)
|
||||
LDAP_CA_CERT_FILE = '/path/to/example-CA.crt'
|
||||
```
|
||||
|
||||
STARTTLS can be configured by setting `AUTH_LDAP_START_TLS = True` and using the `ldap://` URI scheme.
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
The installation instructions provided here have been tested to work on Ubuntu 20.04 and CentOS 8.3. The particular commands needed to install dependencies on other distributions may vary significantly. Unfortunately, this is outside the control of the NetBox maintainers. Please consult your distribution's documentation for assistance with any errors.
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/_y5JRiW_PLM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
The following sections detail how to set up a new instance of NetBox:
|
||||
|
||||
1. [PostgreSQL database](1-postgresql.md)
|
||||
@@ -16,7 +18,7 @@ The following sections detail how to set up a new instance of NetBox:
|
||||
| Dependency | Minimum Version |
|
||||
|------------|-----------------|
|
||||
| Python | 3.8 |
|
||||
| PostgreSQL | 10 |
|
||||
| PostgreSQL | 11 |
|
||||
| Redis | 4.0 |
|
||||
|
||||
Below is a simplified overview of the NetBox application stack for reference:
|
||||
|
||||
@@ -1,23 +1,46 @@
|
||||
# Upgrading to a New NetBox Release
|
||||
|
||||
## Review the Release Notes
|
||||
Upgrading NetBox to a new version is pretty simple, however users are cautioned to always review the release notes and save a backup of their current deployment prior to beginning an upgrade.
|
||||
|
||||
NetBox can generally be upgraded directly to any newer release with no interim steps, with the one exception being incrementing major versions. This can be done only from the most recent _minor_ release of the major version. For example, NetBox v2.11.8 can be upgraded to version 3.3.2 following the steps below. However, a deployment of NetBox v2.10.10 or earlier must first be upgraded to any v2.11 release, and then to any v3.x release. (This is to accommodate the consolidation of database schema migrations effected by a major version change).
|
||||
|
||||
[](../media/installation/upgrade_paths.png)
|
||||
|
||||
!!! warning "Perform a Backup"
|
||||
Always be sure to save a backup of your current NetBox deployment prior to starting the upgrade process.
|
||||
|
||||
## 1. Review the Release Notes
|
||||
|
||||
Prior to upgrading your NetBox instance, be sure to carefully review all [release notes](../release-notes/index.md) that have been published since your current version was released. Although the upgrade process typically does not involve additional work, certain releases may introduce breaking or backward-incompatible changes. These are called out in the release notes under the release in which the change went into effect.
|
||||
|
||||
## Update Dependencies to Required Versions
|
||||
## 2. Update Dependencies to Required Versions
|
||||
|
||||
NetBox v3.0 and later require the following:
|
||||
|
||||
| Dependency | Minimum Version |
|
||||
|------------|-----------------|
|
||||
| Python | 3.8 |
|
||||
| PostgreSQL | 10 |
|
||||
| PostgreSQL | 11 |
|
||||
| Redis | 4.0 |
|
||||
|
||||
## Install the Latest Release
|
||||
## 3. Install the Latest Release
|
||||
|
||||
As with the initial installation, you can upgrade NetBox by either downloading the latest release package or by cloning the `master` branch of the git repository.
|
||||
|
||||
!!! warning
|
||||
Use the same method as you used to install NetBox originally
|
||||
|
||||
If you are not sure how NetBox was installed originally, check with this command:
|
||||
|
||||
```
|
||||
ls -ld /opt/netbox /opt/netbox/.git
|
||||
```
|
||||
|
||||
If NetBox was installed from a release package, then `/opt/netbox` will be a
|
||||
symlink pointing to the current version, and `/opt/netbox/.git` will not
|
||||
exist. If it was installed from git, then `/opt/netbox` and
|
||||
`/opt/netbox/.git` will both exist as normal directories.
|
||||
|
||||
### Option A: Download a Release
|
||||
|
||||
Download the [latest stable release](https://github.com/netbox-community/netbox/releases) from GitHub as a tarball or ZIP archive. Extract it to your desired path. In this example, we'll use `/opt/netbox`.
|
||||
@@ -72,7 +95,7 @@ sudo git pull origin master
|
||||
|
||||
sudo git checkout v2.11.11
|
||||
|
||||
## Run the Upgrade Script
|
||||
## 4. Run the Upgrade Script
|
||||
|
||||
Once the new code is in place, verify that any optional Python packages required by your deployment (e.g. `napalm` or `django-auth-ldap`) are listed in `local_requirements.txt`. Then, run the upgrade script:
|
||||
|
||||
@@ -103,7 +126,7 @@ This script performs the following actions:
|
||||
been made to your local codebase and should be investigated. Never attempt to create new migrations unless you are
|
||||
intentionally modifying the database schema.
|
||||
|
||||
## Restart the NetBox Services
|
||||
## 5. Restart the NetBox Services
|
||||
|
||||
!!! warning
|
||||
If you are upgrading from an installation that does not use a Python virtual environment (any release prior to v2.7.9), you'll need to update the systemd service files to reference the new Python and gunicorn executables before restarting the services. These are located in `/opt/netbox/venv/bin/`. See the example service files in `/opt/netbox/contrib/` for reference.
|
||||
@@ -114,7 +137,7 @@ Finally, restart the gunicorn and RQ services:
|
||||
sudo systemctl restart netbox netbox-rq
|
||||
```
|
||||
|
||||
## Verify Housekeeping Scheduling
|
||||
## 6. Verify Housekeeping Scheduling
|
||||
|
||||
If upgrading from a release prior to NetBox v3.0, check that a cron task (or similar scheduled process) has been configured to run NetBox's nightly housekeeping command. A shell script which invokes this command is included at `contrib/netbox-housekeeping.sh`. It can be linked from your system's daily cron task directory, or included within the crontab directly. (If NetBox has been installed in a nonstandard path, be sure to update the system paths within this script first.)
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ NetBox provides both a singular and plural query field for each object type:
|
||||
|
||||
For example, query `device(id:123)` to fetch a specific device (identified by its unique ID), and query `device_list` (with an optional set of filters) to fetch all devices.
|
||||
|
||||
For more detail on constructing GraphQL queries, see the [Graphene documentation](https://docs.graphene-python.org/en/latest/).
|
||||
For more detail on constructing GraphQL queries, see the [Graphene documentation](https://docs.graphene-python.org/en/latest/) as well as the [GraphQL queries documentation](https://graphql.org/learn/queries/).
|
||||
|
||||
## Filtering
|
||||
|
||||
@@ -56,6 +56,47 @@ The GraphQL API employs the same filtering logic as the UI and REST API. Filters
|
||||
```
|
||||
{"query": "query {site_list(region:\"north-carolina\", status:\"active\") {name}}"}
|
||||
```
|
||||
In addition, filtering can be done on list of related objects as shown in the following query:
|
||||
|
||||
```
|
||||
{
|
||||
device_list {
|
||||
id
|
||||
name
|
||||
interfaces(enabled: true) {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Multiple Return Types
|
||||
|
||||
Certain queries can return multiple types of objects, for example cable terminations can return circuit terminations, console ports and many others. These can be queried using [inline fragments](https://graphql.org/learn/schema/#union-types) as shown below:
|
||||
|
||||
```
|
||||
{
|
||||
cable_list {
|
||||
id
|
||||
a_terminations {
|
||||
... on CircuitTerminationType {
|
||||
id
|
||||
class_type
|
||||
}
|
||||
... on ConsolePortType {
|
||||
id
|
||||
class_type
|
||||
}
|
||||
... on ConsoleServerPortType {
|
||||
id
|
||||
class_type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
The field "class_type" is an easy way to distinguish what type of object it is when viewing the returned data, or when filtering. It contains the class name, for example "CircuitTermination" or "ConsoleServerPort".
|
||||
|
||||
## Authentication
|
||||
|
||||
|
||||
@@ -579,6 +579,9 @@ By default, a token can be used to perform all actions via the API that a user w
|
||||
|
||||
Additionally, a token can be set to expire at a specific time. This can be useful if an external client needs to be granted temporary access to NetBox.
|
||||
|
||||
!!! warning "Restricting Token Retrieval"
|
||||
The ability to retrieve the key value of a previously-created API token can be restricted by disabling the [`ALLOW_TOKEN_RETRIEVAL`](../configuration/security.md#allow_token_retrieval) configuration parameter.
|
||||
|
||||
#### Client IP Restriction
|
||||
|
||||
!!! note
|
||||
|
||||
@@ -74,6 +74,6 @@ NetBox is built on the [Django](https://djangoproject.com/) Python framework and
|
||||
| HTTP service | nginx or Apache |
|
||||
| WSGI service | gunicorn or uWSGI |
|
||||
| Application | Django/Python |
|
||||
| Database | PostgreSQL 10+ |
|
||||
| Database | PostgreSQL 11+ |
|
||||
| Task queuing | Redis/django-rq |
|
||||
| Live device access | NAPALM (optional) |
|
||||
|
||||
BIN
docs/media/development/github.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
docs/media/development/github_new_issue.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
docs/media/installation/upgrade_paths.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 96 KiB |
BIN
docs/media/screenshots/netbox-ui.png
Normal file
|
After Width: | Height: | Size: 173 KiB |
@@ -41,6 +41,10 @@ Indicates whether this is a parent type (capable of housing child devices), a ch
|
||||
|
||||
The default direction in which airflow circulates within the device chassis. This may be configured differently for instantiated devices (e.g. because of different fan modules).
|
||||
|
||||
### Weight
|
||||
|
||||
The numeric weight of the device, including a unit designation (e.g. 10 kilograms or 20 pounds).
|
||||
|
||||
### Front & Rear Images
|
||||
|
||||
Users can upload illustrations of the device's front and rear panels. If present, these will be used to render the device in [rack](./rack.md) elevation diagrams.
|
||||
|
||||
@@ -18,6 +18,13 @@ The [module bay](./modulebay.md) into which the module is installed.
|
||||
|
||||
The [module type](./moduletype.md) which represents the physical make & model of hardware. By default, module components will be instantiated automatically from the module type when creating a new module.
|
||||
|
||||
### Status
|
||||
|
||||
The module's operational status.
|
||||
|
||||
!!! tip
|
||||
Additional statuses may be defined by setting `Module.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.
|
||||
|
||||
### Serial Number
|
||||
|
||||
The unique physical serial number assigned to this module by its manufacturer.
|
||||
|
||||
@@ -35,3 +35,7 @@ The model number assigned to this module type by its manufacturer. Must be uniqu
|
||||
### Part Number
|
||||
|
||||
An alternative part number to uniquely identify the module type.
|
||||
|
||||
### Weight
|
||||
|
||||
The numeric weight of the module, including a unit designation (e.g. 3 kilograms or 1 pound).
|
||||
|
||||
@@ -65,6 +65,18 @@ The height of the rack, measured in units.
|
||||
|
||||
The external width and depth of the rack can be tracked to aid in floorplan calculations. These measurements must be designated in either millimeters or inches.
|
||||
|
||||
### Mounting Depth
|
||||
|
||||
The maximum depth of a mounted device that the rack can accommodate, in millimeters. For four-post frames or cabinets, this is the horizontal distance between the front and rear vertical rails. (Note that this measurement does _not_ include space between the rails and the cabinet doors.)
|
||||
|
||||
### Weight
|
||||
|
||||
The numeric weight of the rack, including a unit designation (e.g. 10 kilograms or 20 pounds).
|
||||
|
||||
### Maximum Weight
|
||||
|
||||
The maximum total weight capacity for all installed devices, inclusive of the rack itself.
|
||||
|
||||
### Descending Units
|
||||
|
||||
If selected, the rack's elevation will display unit 1 at the top of the rack. (Most racks use asceneding numbering, with unit 1 assigned to the bottommost position.)
|
||||
If selected, the rack's elevation will display unit 1 at the top of the rack. (Most racks use ascending numbering, with unit 1 assigned to the bottommost position.)
|
||||
|
||||
@@ -33,7 +33,7 @@ Each site can have multiple [AS numbers](../ipam/asn.md) assigned to it.
|
||||
|
||||
### Time Zone
|
||||
|
||||
The site's local time zone. (Time zones are provided by the [pytz](https://pypi.org/project/pytz/) package.)
|
||||
The site's local time zone. (Time zones are provided by the [zoneinfo](https://docs.python.org/3/library/zoneinfo.html) library.)
|
||||
|
||||
### Physical Address
|
||||
|
||||
|
||||
33
docs/models/dcim/virtualdevicecontext.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Virtual Device Context
|
||||
|
||||
A virtual device context (VDC) represents a logical partition within a physical device, to which interfaces from the parent device can be allocated. Each VDC effectively provides an isolated control plane, but relies on shared resources of the parent device. A VDC is somewhat similar to a virtual machine in that it effects isolation between various components, but stops short of delivering a fully virtualized environment.
|
||||
|
||||
Each VDC must be assigned to a device upon creation, after which interfaces belonging to that device can be assigned to one or more of its VDCs. A VDC can have any number of interfaces assigned to it, and an interface can belong to any number of VDCs.
|
||||
|
||||
!!! info "A VDC by Any Other Name"
|
||||
Network vendors use differing names for this concept. Cisco uses the term VDC, whereas Juniper refers to it as a _Virtual Routing Instance_, and Fortinet uses _Virtual Domain_, for instance. While there may be some nuance among the vendors' unique implementations, the general concept remains the same for each.
|
||||
|
||||
## Fields
|
||||
|
||||
### Device
|
||||
|
||||
The device to which this VDC belongs.
|
||||
|
||||
### Name
|
||||
|
||||
The VDC's configured name. Must be unique to the assigned device.
|
||||
|
||||
### Status
|
||||
|
||||
The operational status of the VDC.
|
||||
|
||||
### Identifier
|
||||
|
||||
A vendor-prescribed unique identifier for the VDC (optional). Must be unique to the assigned device if defined.
|
||||
|
||||
### Primary IPv4 & IPv6 Addresses
|
||||
|
||||
Each VDC may designate one primary IPv4 address and/or one primary IPv6 address for management purposes.
|
||||
|
||||
!!! tip
|
||||
NetBox will prefer IPv6 addresses over IPv4 addresses by default. This can be changed by setting the `PREFER_IPV4` configuration parameter.
|
||||
13
docs/models/extras/branch.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Branches
|
||||
|
||||
A branch is a collection of related [staged changes](./stagedchange.md) that have been prepared for merging into the active database. A branch can be merged by executing its `commit()` method. Deleting a branch will delete all its related changes.
|
||||
|
||||
## Fields
|
||||
|
||||
### Name
|
||||
|
||||
The branch's name.
|
||||
|
||||
### User
|
||||
|
||||
The user to which the branch belongs (optional).
|
||||
47
docs/models/extras/savedfilter.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Saved Filters
|
||||
|
||||
When filtering lists of objects in NetBox, users can save applied filters for future use. This is handy for complex filter strategies involving multiple discrete filters. For example, you might want to find all planned devices within a region that have a specific platform. Once you've applied the desired filters to the object list, simply create a saved filter with name and optional description. This filter can then be applied directly for future queries via both the UI and REST API.
|
||||
|
||||
## Fields
|
||||
|
||||
### Name
|
||||
|
||||
The filter's human-friendly name.
|
||||
|
||||
### Slug
|
||||
|
||||
The unique identifier by which this filter will be referenced during application (e.g. `?filter=my-slug`).
|
||||
|
||||
### User
|
||||
|
||||
The user to which this filter belongs. The current user will be assigned automatically when creating saved filters via the UI, and cannot be changed.
|
||||
|
||||
### Weight
|
||||
|
||||
A numeric weight used to override alphabetic ordering of filters by name. Saved filters with a lower weight will be listed before those with a higher weight.
|
||||
|
||||
### Enabled
|
||||
|
||||
Determines whether this filter can be used. Disabled filters will not appear as options in the UI, however they will be included in API results.
|
||||
|
||||
### Shared
|
||||
|
||||
Determines whether this filter is intended for use by all users or only its owner. Note that disabling this field does **not** hide the filter from other users; it is merely excluded from the list of available filters in UI object list views.
|
||||
|
||||
### Parameters
|
||||
|
||||
The query parameters to apply when the filter is active. These must be specified as JSON data. For example, the URL query string
|
||||
|
||||
```
|
||||
?status=active®ion_id=51&tag=alpha&tag=bravo
|
||||
```
|
||||
|
||||
is represented in JSON as
|
||||
|
||||
```json
|
||||
{
|
||||
"tag": ["alpha", "bravo"],
|
||||
"status": "active",
|
||||
"region_id": 51
|
||||
}
|
||||
```
|
||||
26
docs/models/extras/stagedchange.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Staged Changes
|
||||
|
||||
A staged change represents the creation of a new object or the modification or deletion of an existing object to be performed at some future point. Each change must be assigned to a [branch](./branch.md).
|
||||
|
||||
Changes can be applied individually via the `apply()` method, however it is recommended to apply changes in bulk using the parent branch's `commit()` method.
|
||||
|
||||
## Fields
|
||||
|
||||
!!! warning
|
||||
Staged changes are not typically created or manipulated directly, but rather effected through the use of the [`checkout()`](../../plugins/development/staged-changes.md) context manager.
|
||||
|
||||
### Branch
|
||||
|
||||
The [branch](./branch.md) to which this change belongs.
|
||||
|
||||
### Action
|
||||
|
||||
The type of action this change represents: `create`, `update`, or `delete`.
|
||||
|
||||
### Object
|
||||
|
||||
A generic foreign key referencing the existing object to which this change applies.
|
||||
|
||||
### Data
|
||||
|
||||
JSON representation of the changes being made to the object (not applicable for deletions).
|
||||
@@ -19,6 +19,10 @@ The wire protocol employed by cooperating servers to maintain the virtual [IP ad
|
||||
|
||||
The group's numeric identifier.
|
||||
|
||||
### Name
|
||||
|
||||
An optional name for the FHRP group.
|
||||
|
||||
### Authentication Type
|
||||
|
||||
The type of authentication employed by group nodes, if any.
|
||||
|
||||
@@ -23,7 +23,7 @@ The IPv4 or IPv6 address and mask, in CIDR notation (e.g. `192.0.2.0/24`).
|
||||
The operational status of the IP address.
|
||||
|
||||
!!! tip
|
||||
Additional statuses may be defined by setting `IPAddress.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.
|
||||
Additional statuses may be defined by setting `ipam.IPAddress.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.
|
||||
|
||||
### Role
|
||||
|
||||
|
||||
@@ -12,6 +12,13 @@ The service set identifier (SSID) for the wireless network.
|
||||
|
||||
The [wireless LAN group](./wirelesslangroup.md) to which this wireless LAN is assigned (if any).
|
||||
|
||||
### Status
|
||||
|
||||
The operational status of the wireless network.
|
||||
|
||||
!!! tip
|
||||
Additional statuses may be defined by setting `WirelessLAN.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.
|
||||
|
||||
### VLAN
|
||||
|
||||
Each wireless LAN can optionally be mapped to a [VLAN](../ipam/vlan.md), to model a bridge between wired and wireless segments.
|
||||
|
||||
@@ -34,12 +34,12 @@ To utilize a filter set in a subclass of one of NetBox's generic views (such as
|
||||
```python
|
||||
# views.py
|
||||
from netbox.views.generic import ObjectListView
|
||||
from .filtersets import MyModelFitlerSet
|
||||
from .filtersets import MyModelFilterSet
|
||||
from .models import MyModel
|
||||
|
||||
class MyModelListView(ObjectListView):
|
||||
queryset = MyModel.objects.all()
|
||||
filterset = MyModelFitlerSet
|
||||
filterset = MyModelFilterSet
|
||||
```
|
||||
|
||||
To enable a filter set on a REST API endpoint, set the `filterset_class` attribute on the API view:
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
NetBox provides several base form classes for use by plugins.
|
||||
|
||||
| Form Class | Purpose |
|
||||
|---------------------------|--------------------------------------|
|
||||
| `NetBoxModelForm` | Create/edit individual objects |
|
||||
| `NetBoxModelCSVForm` | Bulk import objects from CSV data |
|
||||
| `NetBoxModelBulkEditForm` | Edit multiple objects simultaneously |
|
||||
| Form Class | Purpose |
|
||||
|----------------------------|--------------------------------------|
|
||||
| `NetBoxModelForm` | Create/edit individual objects |
|
||||
| `NetBoxModelImportForm` | Bulk import objects from CSV data |
|
||||
| `NetBoxModelBulkEditForm` | Edit multiple objects simultaneously |
|
||||
| `NetBoxModelFilterSetForm` | Filter objects within a list view |
|
||||
|
||||
### `NetBoxModelForm`
|
||||
@@ -45,19 +45,20 @@ class MyModelForm(NetBoxModelForm):
|
||||
!!! tip "Comment fields"
|
||||
If your form has a `comments` field, there's no need to list it; this will always appear last on the page.
|
||||
|
||||
### `NetBoxModelCSVForm`
|
||||
### `NetBoxModelImportForm`
|
||||
|
||||
This form facilitates the bulk import of new objects from CSV data. As with model forms, you'll need to declare a `Meta` subclass specifying the associated `model` and `fields`. NetBox also provides several form fields suitable for import various types of CSV data, listed below.
|
||||
This form facilitates the bulk import of new objects from CSV, JSON, or YAML data. As with model forms, you'll need to declare a `Meta` subclass specifying the associated `model` and `fields`. NetBox also provides several form fields suitable for import various types of CSV data, listed below.
|
||||
|
||||
**Example**
|
||||
|
||||
```python
|
||||
from dcim.models import Site
|
||||
from netbox.forms import NetBoxModelCSVForm
|
||||
from netbox.forms import NetBoxModelImportForm
|
||||
from utilities.forms import CSVModelChoiceField
|
||||
from .models import MyModel
|
||||
|
||||
class MyModelCSVForm(NetBoxModelCSVForm):
|
||||
|
||||
class MyModelImportForm(NetBoxModelImportForm):
|
||||
site = CSVModelChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='name',
|
||||
@@ -69,6 +70,9 @@ class MyModelCSVForm(NetBoxModelCSVForm):
|
||||
fields = ('name', 'status', 'site', 'comments')
|
||||
```
|
||||
|
||||
!!! note "Previously NetBoxModelCSVForm"
|
||||
This form class was previously named `NetBoxModelCSVForm`. It was renamed in NetBox v3.4 to convey support for JSON and YAML formats in addition to CSV. The `NetBoxModelCSVForm` class has been retained for backward compatibility and functions exactly the same as `NetBoxModelImportForm`. However, plugin authors should be aware that this backward compatability will be removed in NetBox v3.5.
|
||||
|
||||
### `NetBoxModelBulkEditForm`
|
||||
|
||||
This form facilitates editing multiple objects in bulk. Unlike a model form, this form does not have a child `Meta` class, and must explicitly define each field. All fields in a bulk edit form are generally declared with `required=False`.
|
||||
@@ -84,11 +88,12 @@ This form facilitates editing multiple objects in bulk. Unlike a model form, thi
|
||||
```python
|
||||
from django import forms
|
||||
from dcim.models import Site
|
||||
from netbox.forms import NetBoxModelCSVForm
|
||||
from netbox.forms import NetBoxModelImportForm
|
||||
from utilities.forms import CommentField, DynamicModelChoiceField
|
||||
from .models import MyModel, MyModelStatusChoices
|
||||
|
||||
class MyModelEditForm(NetBoxModelCSVForm):
|
||||
|
||||
class MyModelEditForm(NetBoxModelImportForm):
|
||||
name = forms.CharField(
|
||||
required=False
|
||||
)
|
||||
@@ -144,73 +149,73 @@ class MyModelFilterForm(NetBoxModelFilterSetForm):
|
||||
In addition to the [form fields provided by Django](https://docs.djangoproject.com/en/stable/ref/forms/fields/), NetBox provides several field classes for use within forms to handle specific types of data. These can be imported from `utilities.forms.fields` and are documented below.
|
||||
|
||||
::: utilities.forms.ColorField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: utilities.forms.CommentField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: utilities.forms.JSONField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: utilities.forms.MACAddressField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: utilities.forms.SlugField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
## Choice Fields
|
||||
|
||||
::: utilities.forms.ChoiceField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: utilities.forms.MultipleChoiceField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
## Dynamic Object Fields
|
||||
|
||||
::: utilities.forms.DynamicModelChoiceField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: utilities.forms.DynamicModelMultipleChoiceField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
## Content Type Fields
|
||||
|
||||
::: utilities.forms.ContentTypeChoiceField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: utilities.forms.ContentTypeMultipleChoiceField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
## CSV Import Fields
|
||||
|
||||
::: utilities.forms.CSVChoiceField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: utilities.forms.CSVMultipleChoiceField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: utilities.forms.CSVModelChoiceField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: utilities.forms.CSVContentTypeField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: utilities.forms.CSVMultipleContentTypeField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
@@ -32,11 +32,11 @@ schema = MyQuery
|
||||
NetBox provides two object type classes for use by plugins.
|
||||
|
||||
::: netbox.graphql.types.BaseObjectType
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: netbox.graphql.types.NetBoxObjectType
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
## GraphQL Fields
|
||||
@@ -44,9 +44,9 @@ NetBox provides two object type classes for use by plugins.
|
||||
NetBox provides two field classes for use by plugins.
|
||||
|
||||
::: netbox.graphql.fields.ObjectField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: netbox.graphql.fields.ObjectListField
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
@@ -14,6 +14,7 @@ Plugins can do a lot, including:
|
||||
* Provide their own "pages" (views) in the web user interface
|
||||
* Inject template content and navigation links
|
||||
* Extend NetBox's REST and GraphQL APIs
|
||||
* Load additional Django apps
|
||||
* Add custom request/response middleware
|
||||
|
||||
However, keep in mind that each piece of functionality is entirely optional. For example, if your plugin merely adds a piece of middleware or an API endpoint for existing data, there's no need to define any new models.
|
||||
@@ -82,6 +83,7 @@ class FooBarConfig(PluginConfig):
|
||||
default_settings = {
|
||||
'baz': True
|
||||
}
|
||||
django_apps = ["foo", "bar", "baz"]
|
||||
|
||||
config = FooBarConfig
|
||||
```
|
||||
@@ -101,10 +103,12 @@ NetBox looks for the `config` variable within a plugin's `__init__.py` to load i
|
||||
| `base_url` | Base path to use for plugin URLs (optional). If not specified, the project's `name` will be used. |
|
||||
| `required_settings` | A list of any configuration parameters that **must** be defined by the user |
|
||||
| `default_settings` | A dictionary of configuration parameters and their default values |
|
||||
| `django_apps` | A list of additional Django apps to load alongside the plugin |
|
||||
| `min_version` | Minimum version of NetBox with which the plugin is compatible |
|
||||
| `max_version` | Maximum version of NetBox with which the plugin is compatible |
|
||||
| `middleware` | A list of middleware classes to append after NetBox's build-in middleware |
|
||||
| `queues` | A list of custom background task queues to create |
|
||||
| `search_extensions` | The dotted path to the list of search index classes (default: `search.indexes`) |
|
||||
| `template_extensions` | The dotted path to the list of template extension classes (default: `template_content.template_extensions`) |
|
||||
| `menu_items` | The dotted path to the list of menu items provided by the plugin (default: `navigation.menu_items`) |
|
||||
| `graphql_schema` | The dotted path to the plugin's GraphQL schema class, if any (default: `graphql.schema`) |
|
||||
@@ -112,6 +116,22 @@ NetBox looks for the `config` variable within a plugin's `__init__.py` to load i
|
||||
|
||||
All required settings must be configured by the user. If a configuration parameter is listed in both `required_settings` and `default_settings`, the default setting will be ignored.
|
||||
|
||||
!!! tip "Accessing Config Parameters"
|
||||
Plugin configuration parameters can be accessed using the `get_plugin_config()` function. For example:
|
||||
|
||||
```python
|
||||
from extras.plugins import get_plugin_config
|
||||
get_plugin_config('my_plugin', 'verbose_name')
|
||||
```
|
||||
|
||||
#### Important Notes About `django_apps`
|
||||
|
||||
Loading additional apps may cause more harm than good and could make identifying problems within NetBox itself more difficult. The `django_apps` attribute is intended only for advanced use cases that require a deeper Django integration.
|
||||
|
||||
Apps from this list are inserted *before* the plugin's `PluginConfig` in the order defined. Adding the plugin's `PluginConfig` module to this list changes this behavior and allows for apps to be loaded *after* the plugin.
|
||||
|
||||
Any additional apps must be installed within the same Python environment as NetBox or `ImproperlyConfigured` exceptions will be raised when loading the plugin.
|
||||
|
||||
## Create setup.py
|
||||
|
||||
`setup.py` is the [setup script](https://docs.python.org/3.8/distutils/setupscript.html) used to package and install our plugin once it's finished. The primary function of this script is to call the setuptools library's `setup()` function to create a Python distribution package. We can pass a number of keyword arguments to control the package creation as well as to provide metadata about the plugin. An example `setup.py` is below:
|
||||
|
||||
@@ -49,23 +49,11 @@ class MyModel(NetBoxModel):
|
||||
...
|
||||
```
|
||||
|
||||
### The `clone()` Method
|
||||
### NetBoxModel Properties
|
||||
|
||||
!!! info
|
||||
This method was introduced in NetBox v3.3.
|
||||
#### `docs_url`
|
||||
|
||||
The `NetBoxModel` class includes a `clone()` method to be used for gathering attributes which can be used to create a "cloned" instance. This is used primarily for form initialization, e.g. when using the "clone" button in the NetBox UI. By default, this method will replicate any fields listed in the model's `clone_fields` list, if defined.
|
||||
|
||||
Plugin models can leverage this method by defining `clone_fields` as a list of field names to be replicated, or override this method to replace or extend its content:
|
||||
|
||||
```python
|
||||
class MyModel(NetBoxModel):
|
||||
|
||||
def clone(self):
|
||||
attrs = super().clone()
|
||||
attrs['extra-value'] = 123
|
||||
return attrs
|
||||
```
|
||||
This attribute specifies the URL at which the documentation for this model can be reached. By default, it will return `/static/docs/models/<app_label>/<model_name>/`. Plugin models can override this to return a custom URL. For example, you might direct the user to your plugin's documentation hosted on [ReadTheDocs](https://readthedocs.org/).
|
||||
|
||||
### Enabling Features Individually
|
||||
|
||||
@@ -116,6 +104,8 @@ For more information about database migrations, see the [Django documentation](h
|
||||
|
||||
::: netbox.models.features.ChangeLoggingMixin
|
||||
|
||||
::: netbox.models.features.CloningMixin
|
||||
|
||||
::: netbox.models.features.CustomLinksMixin
|
||||
|
||||
::: netbox.models.features.CustomFieldsMixin
|
||||
|
||||
@@ -1,25 +1,67 @@
|
||||
# Navigation
|
||||
|
||||
## Menus
|
||||
|
||||
!!! note
|
||||
This feature was introduced in NetBox v3.4.
|
||||
|
||||
A plugin can register its own submenu as part of NetBox's navigation menu. This is done by defining a variable named `menu` in `navigation.py`, pointing to an instance of the `PluginMenu` class. Each menu must define a label and grouped menu items (discussed below), and may optionally specify an icon. An example is shown below.
|
||||
|
||||
```python title="navigation.py"
|
||||
from extras.plugins import PluginMenu
|
||||
|
||||
menu = PluginMenu(
|
||||
label='My Plugin',
|
||||
groups=(
|
||||
('Foo', (item1, item2, item3)),
|
||||
('Bar', (item4, item5)),
|
||||
),
|
||||
icon_class='mdi mdi-router'
|
||||
)
|
||||
```
|
||||
|
||||
Note that each group is a two-tuple containing a label and an iterable of menu items. The group's label serves as the section header within the submenu. A group label is required even if you have only one group of items.
|
||||
|
||||
!!! tip
|
||||
The path to the menu class can be modified by setting `menu` in the PluginConfig instance.
|
||||
|
||||
A `PluginMenu` has the following attributes:
|
||||
|
||||
| Attribute | Required | Description |
|
||||
|--------------|----------|---------------------------------------------------|
|
||||
| `label` | Yes | The text displayed as the menu heading |
|
||||
| `groups` | Yes | An iterable of named groups containing menu items |
|
||||
| `icon_class` | - | The CSS name of the icon to use for the heading |
|
||||
|
||||
!!! tip
|
||||
Supported icons can be found at [Material Design Icons](https://materialdesignicons.com/)
|
||||
|
||||
### The Default Menu
|
||||
|
||||
If your plugin has only a small number of menu items, it may be desirable to use NetBox's shared "Plugins" menu rather than creating your own. To do this, simply declare `menu_items` as a list of `PluginMenuItems` in `navigation.py`. The listed items will appear under a heading bearing the name of your plugin in the "Plugins" submenu.
|
||||
|
||||
```python title="navigation.py"
|
||||
menu_items = (item1, item2, item3)
|
||||
```
|
||||
|
||||
!!! tip
|
||||
The path to the menu items list can be modified by setting `menu_items` in the PluginConfig instance.
|
||||
|
||||
## Menu Items
|
||||
|
||||
To make its views easily accessible to users, a plugin can inject items in NetBox's navigation menu under the "Plugins" header. Menu items are added by defining a list of PluginMenuItem instances. By default, this should be a variable named `menu_items` in the file `navigation.py`. An example is shown below.
|
||||
Each menu item represents a link and (optionally) a set of buttons comprising one entry in NetBox's navigation menu. Menu items are defined as PluginMenuItem instances. An example is shown below.
|
||||
|
||||
!!! tip
|
||||
The path to declared menu items can be modified by setting `menu_items` in the PluginConfig instance.
|
||||
|
||||
```python
|
||||
```python filename="navigation.py"
|
||||
from extras.plugins import PluginMenuButton, PluginMenuItem
|
||||
from utilities.choices import ButtonColorChoices
|
||||
|
||||
menu_items = (
|
||||
PluginMenuItem(
|
||||
link='plugins:netbox_animal_sounds:random_animal',
|
||||
link_text='Random sound',
|
||||
buttons=(
|
||||
PluginMenuButton('home', 'Button A', 'fa fa-info', ButtonColorChoices.BLUE),
|
||||
PluginMenuButton('home', 'Button B', 'fa fa-warning', ButtonColorChoices.GREEN),
|
||||
)
|
||||
),
|
||||
item1 = PluginMenuItem(
|
||||
link='plugins:myplugin:myview',
|
||||
link_text='Some text',
|
||||
buttons=(
|
||||
PluginMenuButton('home', 'Button A', 'fa fa-info', ButtonColorChoices.BLUE),
|
||||
PluginMenuButton('home', 'Button B', 'fa fa-warning', ButtonColorChoices.GREEN),
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
@@ -34,17 +76,19 @@ A `PluginMenuItem` has the following attributes:
|
||||
|
||||
## Menu Buttons
|
||||
|
||||
Each menu item can include a set of buttons. These can be handy for providing shortcuts related to the menu item. For instance, most items in NetBox's navigation menu include buttons to create and import new objects.
|
||||
|
||||
A `PluginMenuButton` has the following attributes:
|
||||
|
||||
| Attribute | Required | Description |
|
||||
|---------------|----------|--------------------------------------------------------------------|
|
||||
| `link` | Yes | Name of the URL path to which this button links |
|
||||
| `title` | Yes | The tooltip text (displayed when the mouse hovers over the button) |
|
||||
| `icon_class` | Yes | Button icon CSS class* |
|
||||
| `icon_class` | Yes | Button icon CSS class |
|
||||
| `color` | - | One of the choices provided by `ButtonColorChoices` |
|
||||
| `permissions` | - | A list of permissions required to display this button |
|
||||
|
||||
*NetBox supports [Material Design Icons](https://materialdesignicons.com/).
|
||||
Any buttons associated within a menu item will be shown only if the user has permission to view the link, regardless of what permissions are set on the buttons.
|
||||
|
||||
!!! note
|
||||
Any buttons associated within a menu item will be shown only if the user has permission to view the link, regardless of what permissions are set on the buttons.
|
||||
!!! tip
|
||||
Supported icons can be found at [Material Design Icons](https://materialdesignicons.com/)
|
||||
|
||||
31
docs/plugins/development/search.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Search
|
||||
|
||||
!!! note
|
||||
This feature was introduced in NetBox v3.4.
|
||||
|
||||
Plugins can define and register their own models to extend NetBox's core search functionality. Typically, a plugin will include a file named `search.py`, which holds all search indexes for its models (see the example below).
|
||||
|
||||
```python
|
||||
# search.py
|
||||
from netbox.search import SearchIndex
|
||||
from .models import MyModel
|
||||
|
||||
class MyModelIndex(SearchIndex):
|
||||
model = MyModel
|
||||
fields = (
|
||||
('name', 100),
|
||||
('description', 500),
|
||||
('comments', 5000),
|
||||
)
|
||||
```
|
||||
|
||||
To register one or more indexes with NetBox, define a list named `indexes` at the end of this file:
|
||||
|
||||
```python
|
||||
indexes = [MyModelIndex]
|
||||
```
|
||||
|
||||
!!! tip
|
||||
The path to the list of search indexes can be modified by setting `search_indexes` in the PluginConfig instance.
|
||||
|
||||
::: netbox.search.SearchIndex
|
||||
42
docs/plugins/development/staged-changes.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Staged Changes
|
||||
|
||||
!!! danger "Experimental Feature"
|
||||
This feature is still under active development and considered experimental in nature. Its use in production is strongly discouraged at this time.
|
||||
|
||||
!!! note
|
||||
This feature was introduced in NetBox v3.4.
|
||||
|
||||
NetBox provides a programmatic API to stage the creation, modification, and deletion of objects without actually committing those changes to the active database. This can be useful for performing a "dry run" of bulk operations, or preparing a set of changes for administrative approval, for example.
|
||||
|
||||
To begin staging changes, first create a [branch](../../models/extras/branch.md):
|
||||
|
||||
```python
|
||||
from extras.models import Branch
|
||||
|
||||
branch1 = Branch.objects.create(name='branch1')
|
||||
```
|
||||
|
||||
Then, activate the branch using the `checkout()` context manager and begin making your changes. This initiates a new database transaction.
|
||||
|
||||
```python
|
||||
from extras.models import Branch
|
||||
from netbox.staging import checkout
|
||||
|
||||
branch1 = Branch.objects.get(name='branch1')
|
||||
with checkout(branch1):
|
||||
Site.objects.create(name='New Site', slug='new-site')
|
||||
# ...
|
||||
```
|
||||
|
||||
Upon exiting the context, the database transaction is automatically rolled back and your changes recorded as [staged changes](../../models/extras/stagedchange.md). Re-entering a branch will trigger a new database transaction and automatically apply any staged changes associated with the branch.
|
||||
|
||||
To apply the changes within a branch, call the branch's `commit()` method:
|
||||
|
||||
```python
|
||||
from extras.models import Branch
|
||||
|
||||
branch1 = Branch.objects.get(name='branch1')
|
||||
branch1.commit()
|
||||
```
|
||||
|
||||
Committing a branch is an all-or-none operation: Any exceptions will revert the entire set of changes. After successfully committing a branch, all its associated StagedChange objects are automatically deleted (however the branch itself will remain and can be reused).
|
||||
@@ -52,38 +52,38 @@ This will automatically apply any user-specific preferences for the table. (If u
|
||||
The table column classes listed below are supported for use in plugins. These classes can be imported from `netbox.tables.columns`.
|
||||
|
||||
::: netbox.tables.BooleanColumn
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: netbox.tables.ChoiceFieldColumn
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: netbox.tables.ColorColumn
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: netbox.tables.ColoredLabelColumn
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: netbox.tables.ContentTypeColumn
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: netbox.tables.ContentTypesColumn
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: netbox.tables.MarkdownColumn
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: netbox.tables.TagColumn
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: netbox.tables.TemplateColumn
|
||||
selection:
|
||||
options:
|
||||
members:
|
||||
- __init__
|
||||
|
||||
@@ -82,26 +82,28 @@ class ThingEditView(ObjectEditView):
|
||||
Below are the class definitions for NetBox's object views. These views handle CRUD actions for individual objects. The view, add/edit, and delete views each inherit from `BaseObjectView`, which is not intended to be used directly.
|
||||
|
||||
::: netbox.views.generic.base.BaseObjectView
|
||||
options:
|
||||
members:
|
||||
- get_queryset
|
||||
- get_object
|
||||
- get_extra_context
|
||||
|
||||
::: netbox.views.generic.ObjectView
|
||||
selection:
|
||||
options:
|
||||
members:
|
||||
- get_object
|
||||
- get_template_name
|
||||
|
||||
::: netbox.views.generic.ObjectEditView
|
||||
selection:
|
||||
options:
|
||||
members:
|
||||
- get_object
|
||||
- alter_object
|
||||
|
||||
::: netbox.views.generic.ObjectDeleteView
|
||||
selection:
|
||||
members:
|
||||
- get_object
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: netbox.views.generic.ObjectChildrenView
|
||||
selection:
|
||||
options:
|
||||
members:
|
||||
- get_children
|
||||
- prep_table_data
|
||||
@@ -111,24 +113,29 @@ Below are the class definitions for NetBox's object views. These views handle CR
|
||||
Below are the class definitions for NetBox's multi-object views. These views handle simultaneous actions for sets objects. The list, import, edit, and delete views each inherit from `BaseMultiObjectView`, which is not intended to be used directly.
|
||||
|
||||
::: netbox.views.generic.base.BaseMultiObjectView
|
||||
options:
|
||||
members:
|
||||
- get_queryset
|
||||
- get_extra_context
|
||||
|
||||
::: netbox.views.generic.ObjectListView
|
||||
selection:
|
||||
options:
|
||||
members:
|
||||
- get_table
|
||||
- export_table
|
||||
- export_template
|
||||
|
||||
::: netbox.views.generic.BulkImportView
|
||||
selection:
|
||||
members: false
|
||||
options:
|
||||
members:
|
||||
- save_object
|
||||
|
||||
::: netbox.views.generic.BulkEditView
|
||||
selection:
|
||||
options:
|
||||
members: false
|
||||
|
||||
::: netbox.views.generic.BulkDeleteView
|
||||
selection:
|
||||
options:
|
||||
members:
|
||||
- get_form
|
||||
|
||||
@@ -137,29 +144,62 @@ Below are the class definitions for NetBox's multi-object views. These views han
|
||||
These views are provided to enable or enhance certain NetBox model features, such as change logging or journaling. These typically do not need to be subclassed: They can be used directly e.g. in a URL path.
|
||||
|
||||
::: netbox.views.generic.ObjectChangeLogView
|
||||
selection:
|
||||
options:
|
||||
members:
|
||||
- get_form
|
||||
|
||||
::: netbox.views.generic.ObjectJournalView
|
||||
selection:
|
||||
options:
|
||||
members:
|
||||
- get_form
|
||||
|
||||
## Extending Core Views
|
||||
|
||||
Plugins can inject custom content into certain areas of the detail views of applicable models. This is accomplished by subclassing `PluginTemplateExtension`, designating a particular NetBox model, and defining the desired methods to render custom content. Four methods are available:
|
||||
### Additional Tabs
|
||||
|
||||
* `left_page()` - Inject content on the left side of the page
|
||||
* `right_page()` - Inject content on the right side of the page
|
||||
* `full_width_page()` - Inject content across the entire bottom of the page
|
||||
* `buttons()` - Add buttons to the top of the page
|
||||
!!! note
|
||||
This feature was introduced in NetBox v3.4.
|
||||
|
||||
Plugins can "attach" a custom view to a core NetBox model by registering it with `register_model_view()`. To include a tab for this view within the NetBox UI, declare a TabView instance named `tab`:
|
||||
|
||||
```python
|
||||
from dcim.models import Site
|
||||
from myplugin.models import Stuff
|
||||
from netbox.views import generic
|
||||
from utilities.views import ViewTab, register_model_view
|
||||
|
||||
@register_model_view(Site, name='myview', path='some-other-stuff')
|
||||
class MyView(generic.ObjectView):
|
||||
...
|
||||
tab = ViewTab(
|
||||
label='Other Stuff',
|
||||
badge=lambda obj: Stuff.objects.filter(site=obj).count(),
|
||||
permission='myplugin.view_stuff'
|
||||
)
|
||||
```
|
||||
|
||||
::: utilities.views.register_model_view
|
||||
|
||||
::: utilities.views.ViewTab
|
||||
|
||||
### Extra Template Content
|
||||
|
||||
Plugins can inject custom content into certain areas of core NetBox views. This is accomplished by subclassing `PluginTemplateExtension`, designating a particular NetBox model, and defining the desired method(s) to render custom content. Five methods are available:
|
||||
|
||||
| Method | View | Description |
|
||||
|---------------------|-------------|-----------------------------------------------------|
|
||||
| `left_page()` | Object view | Inject content on the left side of the page |
|
||||
| `right_page()` | Object view | Inject content on the right side of the page |
|
||||
| `full_width_page()` | Object view | Inject content across the entire bottom of the page |
|
||||
| `buttons()` | Object view | Add buttons to the top of the page |
|
||||
| `list_buttons()` | List view | Add buttons to the top of the page |
|
||||
|
||||
Additionally, a `render()` method is available for convenience. This method accepts the name of a template to render, and any additional context data you want to pass. Its use is optional, however.
|
||||
|
||||
When a PluginTemplateExtension is instantiated, context data is assigned to `self.context`. Available data include:
|
||||
|
||||
* `object` - The object being viewed
|
||||
* `object` - The object being viewed (object views only)
|
||||
* `model` - The model of the list view (list views only)
|
||||
* `request` - The current request
|
||||
* `settings` - Global NetBox settings
|
||||
* `config` - Plugin-specific configuration parameters
|
||||
|
||||
@@ -168,7 +168,7 @@ Some text to show that the reference links can follow later.
|
||||
## Images
|
||||
|
||||
```
|
||||
Here's the Netbox logo (hover to see the title text):
|
||||
Here's the NetBox logo (hover to see the title text):
|
||||
|
||||
Inline-style:
|
||||

|
||||
@@ -179,7 +179,7 @@ Reference-style:
|
||||
[logo]: /static/netbox_logo.png "Logo Title Text 2"
|
||||
```
|
||||
|
||||
Here's the Netbox logo (hover to see the title text):
|
||||
Here's the NetBox logo (hover to see the title text):
|
||||
|
||||
Inline-style:
|
||||

|
||||
|
||||
@@ -10,6 +10,16 @@ Minor releases are published in April, August, and December of each calendar yea
|
||||
|
||||
This page contains a history of all major and minor releases since NetBox v2.0. For more detail on a specific patch release, please see the release notes page for that specific minor release.
|
||||
|
||||
#### [Version 3.4](./version-3.4.md) (December 2022)
|
||||
|
||||
* New Global Search ([#10560](https://github.com/netbox-community/netbox/issues/10560))
|
||||
* Virtual Device Contexts ([#7854](https://github.com/netbox-community/netbox/issues/7854))
|
||||
* Saved Filters ([#9623](https://github.com/netbox-community/netbox/issues/9623))
|
||||
* JSON/YAML Bulk Imports ([#4347](https://github.com/netbox-community/netbox/issues/4347))
|
||||
* Update Existing Objects via Bulk Import ([#7961](https://github.com/netbox-community/netbox/issues/7961))
|
||||
* Scheduled Reports & Scripts ([#8366](https://github.com/netbox-community/netbox/issues/8366))
|
||||
* API for Staged Changes ([#10851](https://github.com/netbox-community/netbox/issues/10851))
|
||||
|
||||
#### [Version 3.3](./version-3.3.md) (August 2022)
|
||||
|
||||
* Multi-object Cable Terminations ([#9102](https://github.com/netbox-community/netbox/issues/9102))
|
||||
|
||||
@@ -1,5 +1,263 @@
|
||||
# NetBox v3.3
|
||||
|
||||
## v3.3.10 (2022-12-13)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [#9361](https://github.com/netbox-community/netbox/issues/9361) - Add replication controls for module bulk import
|
||||
* [#10255](https://github.com/netbox-community/netbox/issues/10255) - Introduce `LOGOUT_REDIRECT_URL` config parameter to control redirection of user after logout
|
||||
* [#10447](https://github.com/netbox-community/netbox/issues/10447) - Enable reassigning an inventory item from one device to another
|
||||
* [#10516](https://github.com/netbox-community/netbox/issues/10516) - Add vertical frame & cabinet rack types
|
||||
* [#10748](https://github.com/netbox-community/netbox/issues/10748) - Add provider selection field for provider networks to circuit termination edit view
|
||||
* [#11089](https://github.com/netbox-community/netbox/issues/11089) - Permit whitespace in MAC addresses
|
||||
* [#11119](https://github.com/netbox-community/netbox/issues/11119) - Enable filtering L2VPNs by slug
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#11041](https://github.com/netbox-community/netbox/issues/11041) - Correct power utilization percentage precision
|
||||
* [#11077](https://github.com/netbox-community/netbox/issues/11077) - Honor configured date format when displaying date custom field values in tables
|
||||
* [#11087](https://github.com/netbox-community/netbox/issues/11087) - Fix background color of bottom banner content
|
||||
* [#11101](https://github.com/netbox-community/netbox/issues/11101) - Correct circuits count under site view
|
||||
* [#11109](https://github.com/netbox-community/netbox/issues/11109) - Fix nullification of custom object & multi-object fields via REST API
|
||||
* [#11128](https://github.com/netbox-community/netbox/issues/11128) - Disable ordering changelog table by object to avoid exception
|
||||
* [#11142](https://github.com/netbox-community/netbox/issues/11142) - Correct available choices for status under IP range filter form
|
||||
* [#11168](https://github.com/netbox-community/netbox/issues/11168) - Honor `RQ_DEFAULT_TIMEOUT` config parameter when using Redis Sentinel
|
||||
* [#11173](https://github.com/netbox-community/netbox/issues/11173) - Enable missing tags columns for contact, L2VPN lists
|
||||
|
||||
---
|
||||
|
||||
## v3.3.9 (2022-11-30)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [#10653](https://github.com/netbox-community/netbox/issues/10653) - Ensure logging of failed login attempts
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#6389](https://github.com/netbox-community/netbox/issues/6389) - Call `snapshot()` on object when processing deletions
|
||||
* [#9223](https://github.com/netbox-community/netbox/issues/9223) - Fix serialization of array field values in change log
|
||||
* [#9878](https://github.com/netbox-community/netbox/issues/9878) - Fix spurious error message when rendering REST API docs
|
||||
* [#10236](https://github.com/netbox-community/netbox/issues/10236) - Fix TypeError exception when viewing PDU configured for three-phase power
|
||||
* [#10241](https://github.com/netbox-community/netbox/issues/10241) - Support referencing custom field related objects by attribute in addition to PK
|
||||
* [#10579](https://github.com/netbox-community/netbox/issues/10579) - Mark cable traces terminating to a provider network as complete
|
||||
* [#10721](https://github.com/netbox-community/netbox/issues/10721) - Disable ordering by custom object field columns
|
||||
* [#10929](https://github.com/netbox-community/netbox/issues/10929) - Raise validation error when attempting to create a duplicate cable termination
|
||||
* [#10936](https://github.com/netbox-community/netbox/issues/10936) - Permit demotion of device/VM primary IP via IP address edit form
|
||||
* [#10938](https://github.com/netbox-community/netbox/issues/10938) - `render_field` template tag should respect `label` kwarg
|
||||
* [#10969](https://github.com/netbox-community/netbox/issues/10969) - Update cable paths ending at associated rear port when creating new front ports
|
||||
* [#10996](https://github.com/netbox-community/netbox/issues/10996) - Hide checkboxes on child object lists when no bulk operations are available
|
||||
* [#10997](https://github.com/netbox-community/netbox/issues/10997) - Fix exception when editing NAT IP for VM with no cluster
|
||||
* [#11014](https://github.com/netbox-community/netbox/issues/11014) - Use natural ordering when sorting rack elevations by name
|
||||
* [#11028](https://github.com/netbox-community/netbox/issues/11028) - Enable bulk clearing of color attribute of pass-through ports
|
||||
* [#11047](https://github.com/netbox-community/netbox/issues/11047) - Cloning a rack reservation should replicate rack & user
|
||||
|
||||
---
|
||||
|
||||
## v3.3.8 (2022-11-16)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [#10356](https://github.com/netbox-community/netbox/issues/10356) - Add backplane Ethernet interface types
|
||||
* [#10902](https://github.com/netbox-community/netbox/issues/10902) - Add location selector to power feed form
|
||||
* [#10904](https://github.com/netbox-community/netbox/issues/10904) - Use front/rear port colors in cable trace SVG
|
||||
* [#10914](https://github.com/netbox-community/netbox/issues/10914) - Include "add module type" button on manufacturer view
|
||||
* [#10915](https://github.com/netbox-community/netbox/issues/10915) - Add count of L2VPNs to tenant view
|
||||
* [#10919](https://github.com/netbox-community/netbox/issues/10919) - Include device location under cable view
|
||||
* [#10920](https://github.com/netbox-community/netbox/issues/10920) - Include request cookies when queuing a custom script
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#9439](https://github.com/netbox-community/netbox/issues/9439) - Ensure thread safety of change logging functions
|
||||
* [#10709](https://github.com/netbox-community/netbox/issues/10709) - Correct UI display for `azuread-v2-tenant-oauth2` SSO backend
|
||||
* [#10829](https://github.com/netbox-community/netbox/issues/10829) - Fix bulk edit/delete buttons ad top of object lists
|
||||
* [#10837](https://github.com/netbox-community/netbox/issues/10837) - Correct cookie paths when `BASE_PATH` is set
|
||||
* [#10874](https://github.com/netbox-community/netbox/issues/10874) - Remove erroneous link for contact assignment count
|
||||
* [#10881](https://github.com/netbox-community/netbox/issues/10881) - Fix dark mode coloring for data on device status page
|
||||
* [#10891](https://github.com/netbox-community/netbox/issues/10891) - Populate tag selection list for service filter form
|
||||
* [#10897](https://github.com/netbox-community/netbox/issues/10897) - Fix form widget styling on FHRP group form
|
||||
* [#10910](https://github.com/netbox-community/netbox/issues/10910) - Fix cable creation links on power port view
|
||||
|
||||
---
|
||||
|
||||
## v3.3.7 (2022-11-01)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#10282](https://github.com/netbox-community/netbox/issues/10282) - Enforce advisory locks when allocating available IP addresses to prevent race conditions
|
||||
* [#10770](https://github.com/netbox-community/netbox/issues/10282) - Fix social authentication for new users
|
||||
* [#10791](https://github.com/netbox-community/netbox/issues/10791) - Permit nullifying VLAN group `scope_type` via REST API
|
||||
* [#10803](https://github.com/netbox-community/netbox/issues/10803) - Fix exception when ordering contacts by number of assignments
|
||||
* [#10809](https://github.com/netbox-community/netbox/issues/10809) - Permit nullifying site `time_zone` via REST API
|
||||
|
||||
---
|
||||
|
||||
## v3.3.6 (2022-10-26)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [#9584](https://github.com/netbox-community/netbox/issues/9584) - Enable filtering devices by device type slug
|
||||
* [#9722](https://github.com/netbox-community/netbox/issues/9722) - Add LDAP configuration parameters to specify certificates
|
||||
* [#10580](https://github.com/netbox-community/netbox/issues/10580) - Link "assigned" checkbox in IP address table to assigned interface
|
||||
* [#10639](https://github.com/netbox-community/netbox/issues/10639) - Set cookie paths according to configured `BASE_PATH`
|
||||
* [#10685](https://github.com/netbox-community/netbox/issues/10685) - Position A/Z termination cards above the fold under circuit view
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#9669](https://github.com/netbox-community/netbox/issues/9669) - Strip colons from usernames when using remote authentication
|
||||
* [#10575](https://github.com/netbox-community/netbox/issues/10575) - Include OIDC dependencies for python-social-auth
|
||||
* [#10584](https://github.com/netbox-community/netbox/issues/10584) - Fix service clone link
|
||||
* [#10610](https://github.com/netbox-community/netbox/issues/10610) - Allow assignment of VC member to LAG on non-master peer
|
||||
* [#10643](https://github.com/netbox-community/netbox/issues/10643) - Ensure consistent display of custom fields for all model forms
|
||||
* [#10646](https://github.com/netbox-community/netbox/issues/10646) - Fix filtering of power feed by power panel when connecting a cable
|
||||
* [#10655](https://github.com/netbox-community/netbox/issues/10655) - Correct display of assigned contacts in object tables
|
||||
* [#10666](https://github.com/netbox-community/netbox/issues/10666) - Re-evaluate disabled LDAP user when processing API requests
|
||||
* [#10682](https://github.com/netbox-community/netbox/issues/10682) - Correct home view links to connection lists
|
||||
* [#10712](https://github.com/netbox-community/netbox/issues/10712) - Fix ModuleNotFoundError exception when generating API schema under Python 3.9+
|
||||
* [#10716](https://github.com/netbox-community/netbox/issues/10716) - Add left/right page plugin content embeds for tag view
|
||||
* [#10719](https://github.com/netbox-community/netbox/issues/10719) - Prevent user without sufficient permission from creating an IP address via FHRP group creation
|
||||
* [#10723](https://github.com/netbox-community/netbox/issues/10723) - Distinguish between inside/outside NAT assignments for device/VM primary IPs
|
||||
* [#10745](https://github.com/netbox-community/netbox/issues/10745) - Correct display of status field in clusters list
|
||||
* [#10746](https://github.com/netbox-community/netbox/issues/10746) - Add missing status attribute to cluster view
|
||||
|
||||
---
|
||||
|
||||
## v3.3.5 (2022-10-05)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [#8424](https://github.com/netbox-community/netbox/issues/8424) - Include rack elevation under device view
|
||||
* [#10352](https://github.com/netbox-community/netbox/issues/10352) - Omit extraneous URL query attributes during search
|
||||
* [#10465](https://github.com/netbox-community/netbox/issues/10465) - Improve formatting of device heights and rack positions
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#9497](https://github.com/netbox-community/netbox/issues/9497) - Adjust non-racked device filter on site and location detailed view
|
||||
* [#10408](https://github.com/netbox-community/netbox/issues/10408) - Fix validation when attempting to add redundant contact assignments
|
||||
* [#10423](https://github.com/netbox-community/netbox/issues/10423) - Enforce object type validation when creating journal entries
|
||||
* [#10435](https://github.com/netbox-community/netbox/issues/10435) - Fix exception when filtering VLANs by virtual machine with no cluster assigned
|
||||
* [#10439](https://github.com/netbox-community/netbox/issues/10439) - Fix form widget styling for DeviceType airflow field
|
||||
* [#10445](https://github.com/netbox-community/netbox/issues/10445) - Avoid rounding virtual machine memory values
|
||||
* [#10460](https://github.com/netbox-community/netbox/issues/10460) - Restore missing connection details for device components
|
||||
* [#10461](https://github.com/netbox-community/netbox/issues/10461) - Enable filtering by read-only custom fields in the UI
|
||||
* [#10470](https://github.com/netbox-community/netbox/issues/10470) - Omit read-only custom fields from CSV import forms
|
||||
* [#10480](https://github.com/netbox-community/netbox/issues/10480) - Cable trace SVG links should not force a new window
|
||||
* [#10491](https://github.com/netbox-community/netbox/issues/10491) - Clarify representation of blocking contact assignments during contact deletion
|
||||
* [#10513](https://github.com/netbox-community/netbox/issues/10513) - Disable the reassignment of a module to a new device
|
||||
* [#10517](https://github.com/netbox-community/netbox/issues/10517) - Automatically inherit site assignment from cluster when creating a virtual machine
|
||||
* [#10559](https://github.com/netbox-community/netbox/issues/10559) - Permit the pinning of a VM to a particular device within a cluster which has no site assignment
|
||||
* [#10562](https://github.com/netbox-community/netbox/issues/10562) - Correct URL for contacts table tags column
|
||||
|
||||
---
|
||||
|
||||
## v3.3.4 (2022-09-16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#10383](https://github.com/netbox-community/netbox/issues/10383) - Fix assignment of component templates to module types via web UI
|
||||
* [#10387](https://github.com/netbox-community/netbox/issues/10387) - Fix `MultiValueDictKeyError` exception when editing a device interface
|
||||
|
||||
---
|
||||
|
||||
## v3.3.3 (2022-09-15)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [#8580](https://github.com/netbox-community/netbox/issues/8580) - Add `occupied` filter for cabled objects to filter by cable or `mark_connected`
|
||||
* [#9577](https://github.com/netbox-community/netbox/issues/9577) - Add `has_front_image` and `has_rear_image` filters for device types
|
||||
* [#10268](https://github.com/netbox-community/netbox/issues/10268) - Omit trailing ".0" in device positions within UI
|
||||
* [#10359](https://github.com/netbox-community/netbox/issues/10359) - Add region and site group columns to the devices table
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#9231](https://github.com/netbox-community/netbox/issues/9231) - Fix `empty` lookup expression for string filters
|
||||
* [#10247](https://github.com/netbox-community/netbox/issues/10247) - Allow changing the pre-populated device/VM when creating new components
|
||||
* [#10250](https://github.com/netbox-community/netbox/issues/10250) - Fix exception when CableTermination validation fails during bulk import of cables
|
||||
* [#10258](https://github.com/netbox-community/netbox/issues/10258) - Enable the use of reports & scripts packaged in submodules
|
||||
* [#10259](https://github.com/netbox-community/netbox/issues/10259) - Fix `NoReverseMatch` exception when listing available prefixes with "flat" column displayed
|
||||
* [#10270](https://github.com/netbox-community/netbox/issues/10270) - Fix custom field validation when creating new services
|
||||
* [#10278](https://github.com/netbox-community/netbox/issues/10278) - Fix "create & add another" for image attachments
|
||||
* [#10294](https://github.com/netbox-community/netbox/issues/10294) - Fix spurious changelog diff for interface WWN field
|
||||
* [#10304](https://github.com/netbox-community/netbox/issues/10304) - Enable cloning for custom fields & custom links
|
||||
* [#10305](https://github.com/netbox-community/netbox/issues/10305) - Fix Virtual Chassis master field cannot be null according to the API
|
||||
* [#10307](https://github.com/netbox-community/netbox/issues/10307) - Correct value for "Passive 48V (4-pair)" PoE type selection
|
||||
* [#10333](https://github.com/netbox-community/netbox/issues/10333) - Show available values for `ui_visibility` field of CustomField for CSV import
|
||||
* [#10337](https://github.com/netbox-community/netbox/issues/10337) - Display SSO links when local authentication fails
|
||||
* [#10353](https://github.com/netbox-community/netbox/issues/10353) - Table action buttons should reserve return URL parameters
|
||||
* [#10362](https://github.com/netbox-community/netbox/issues/10362) - Correct display of custom fields when editing an L2VPN termination
|
||||
|
||||
---
|
||||
|
||||
## v3.3.2 (2022-09-02)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [#9477](https://github.com/netbox-community/netbox/issues/9477) - Enable clearing applied table column ordering
|
||||
* [#10034](https://github.com/netbox-community/netbox/issues/10034) - Add L2VPN column to interface and VLAN tables
|
||||
* [#10043](https://github.com/netbox-community/netbox/issues/10043) - Add support for `limit` query parameter to available VLANs API endpoint
|
||||
* [#10060](https://github.com/netbox-community/netbox/issues/10060) - Add journal entries to global search
|
||||
* [#10195](https://github.com/netbox-community/netbox/issues/10195) - Enable filtering of device components by rack
|
||||
* [#10233](https://github.com/netbox-community/netbox/issues/10233) - Enable sorting rack elevations by facility ID
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#9328](https://github.com/netbox-community/netbox/issues/9328) - Hide available IPs when non-default ordering is applied
|
||||
* [#9481](https://github.com/netbox-community/netbox/issues/9481) - Update child device location when parent location changes
|
||||
* [#9832](https://github.com/netbox-community/netbox/issues/9832) - Improve error message when validating rack reservation units
|
||||
* [#9895](https://github.com/netbox-community/netbox/issues/9895) - Various corrections to OpenAPI spec
|
||||
* [#9962](https://github.com/netbox-community/netbox/issues/9962) - SSO login should respect `next` URL query parameter
|
||||
* [#9963](https://github.com/netbox-community/netbox/issues/9963) - Fix support for custom `CSRF_COOKIE_NAME` value
|
||||
* [#10155](https://github.com/netbox-community/netbox/issues/10155) - Fix rear port display when editing front port template for module type
|
||||
* [#10156](https://github.com/netbox-community/netbox/issues/10156) - Avoid forcing SVG image links to open in a new window
|
||||
* [#10161](https://github.com/netbox-community/netbox/issues/10161) - Restore "set null" option for custom fields during bulk edit
|
||||
* [#10176](https://github.com/netbox-community/netbox/issues/10176) - Correct utilization display for empty racks
|
||||
* [#10177](https://github.com/netbox-community/netbox/issues/10177) - Correct display of custom fields when editing VM interfaces
|
||||
* [#10178](https://github.com/netbox-community/netbox/issues/10178) - Display manufacturer name alongside device type under device view
|
||||
* [#10181](https://github.com/netbox-community/netbox/issues/10181) - Restore MultiPartParser (regression from #10031)
|
||||
* [#10184](https://github.com/netbox-community/netbox/issues/10184) - Fix vertical alignment when displaying object attributes with buttons
|
||||
* [#10208](https://github.com/netbox-community/netbox/issues/10208) - Fix permissions evaluation for interface actions dropdown menu
|
||||
* [#10217](https://github.com/netbox-community/netbox/issues/10217) - Handle exception when trace splits to multiple rear ports
|
||||
* [#10220](https://github.com/netbox-community/netbox/issues/10220) - Validate IP version when assigning primary IPs to a virtual machine
|
||||
* [#10231](https://github.com/netbox-community/netbox/issues/10231) - Correct API schema definition for several serializer fields
|
||||
|
||||
---
|
||||
|
||||
## v3.3.1 (2022-08-25)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [#6454](https://github.com/netbox-community/netbox/issues/6454) - Include contextual help when creating first objects in UI
|
||||
* [#9935](https://github.com/netbox-community/netbox/issues/9935) - Add 802.11ay and "other" wireless interface types
|
||||
* [#10031](https://github.com/netbox-community/netbox/issues/10031) - Enforce `application/json` content type for REST API requests
|
||||
* [#10033](https://github.com/netbox-community/netbox/issues/10033) - Disable "add termination" button for point-to-point L2VPNs with two terminations
|
||||
* [#10037](https://github.com/netbox-community/netbox/issues/10037) - Add "child interface" option to actions dropdown in interfaces list
|
||||
* [#10038](https://github.com/netbox-community/netbox/issues/10038) - Add "L2VPN termination" option to actions dropdown in interfaces list
|
||||
* [#10039](https://github.com/netbox-community/netbox/issues/10039) - Add "assign FHRP group" option to actions dropdown in interfaces list
|
||||
* [#10061](https://github.com/netbox-community/netbox/issues/10061) - Replicate type when cloning L2VPN instances
|
||||
* [#10066](https://github.com/netbox-community/netbox/issues/10066) - Use fixed column widths for custom field values in UI
|
||||
* [#10133](https://github.com/netbox-community/netbox/issues/10133) - Enable nullifying device location during bulk edit
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#9663](https://github.com/netbox-community/netbox/issues/9663) - Omit available IP annotations when filtering prefix child IPs list
|
||||
* [#10040](https://github.com/netbox-community/netbox/issues/10040) - Fix exception when ordering prefixes by flat representation
|
||||
* [#10053](https://github.com/netbox-community/netbox/issues/10053) - Custom fields header should not be displayed when editing circuit terminations with no custom fields
|
||||
* [#10055](https://github.com/netbox-community/netbox/issues/10055) - Fix extraneous NAT indicator by device primary IP
|
||||
* [#10057](https://github.com/netbox-community/netbox/issues/10057) - Fix AttributeError exception when global search results include rack reservations
|
||||
* [#10059](https://github.com/netbox-community/netbox/issues/10059) - Add identifier column to L2VPN table
|
||||
* [#10070](https://github.com/netbox-community/netbox/issues/10070) - Add unique constraint for L2VPN slug
|
||||
* [#10087](https://github.com/netbox-community/netbox/issues/10087) - Correct display of far end in console/power/interface connections tables
|
||||
* [#10089](https://github.com/netbox-community/netbox/issues/10089) - `linkify` template filter should escape object representation
|
||||
* [#10094](https://github.com/netbox-community/netbox/issues/10094) - Fix 404 when using "create and add another" to add contact assignments
|
||||
* [#10108](https://github.com/netbox-community/netbox/issues/10108) - Linkify inside NAT IPs for primary device IPs in UI
|
||||
* [#10109](https://github.com/netbox-community/netbox/issues/10109) - Fix available prefixes calculation for container prefixes in the global table
|
||||
* [#10111](https://github.com/netbox-community/netbox/issues/10111) - Fix ValueError exception when searching for L2VPN objects
|
||||
* [#10118](https://github.com/netbox-community/netbox/issues/10118) - Fix display of connected LLDP neighbors for devices
|
||||
* [#10134](https://github.com/netbox-community/netbox/issues/10134) - Custom fields data serializer should return a 400 response for invalid data
|
||||
* [#10135](https://github.com/netbox-community/netbox/issues/10135) - Fix SSO support for SAML2 IDPs
|
||||
* [#10147](https://github.com/netbox-community/netbox/issues/10147) - Permit the creation of 0U device types via REST API
|
||||
|
||||
---
|
||||
|
||||
## v3.3.0 (2022-08-17)
|
||||
|
||||
### Breaking Changes
|
||||
@@ -220,7 +478,7 @@ Custom field UI visibility has no impact on API operation.
|
||||
* The `cluster` field is now optional. A virtual machine must have a site and/or cluster assigned.
|
||||
* Added the optional `device` field
|
||||
* Added the `l2vpn_termination` read-only field
|
||||
wireless.WirelessLAN
|
||||
* wireless.WirelessLAN
|
||||
* Added `tenant` field
|
||||
wireless.WirelessLink
|
||||
* wireless.WirelessLink
|
||||
* Added `tenant` field
|
||||
|
||||
303
docs/release-notes/version-3.4.md
Normal file
@@ -0,0 +1,303 @@
|
||||
# NetBox v3.4
|
||||
|
||||
## v3.4.3 (2023-01-20)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [#9996](https://github.com/netbox-community/netbox/issues/9996) - Introduce `CA_CERT_PATH` parameter to define SSL CA path for Redis servers
|
||||
* [#10486](https://github.com/netbox-community/netbox/issues/10486) - Add a cable edit button for connected components in component lists
|
||||
* [#11118](https://github.com/netbox-community/netbox/issues/11118) - Add L2VPN filters for VLANs and interfaces
|
||||
* [#11150](https://github.com/netbox-community/netbox/issues/11150) - Add primary IPv4/v6 address filters for devices
|
||||
* [#11227](https://github.com/netbox-community/netbox/issues/11227) - Add 800GE interface types
|
||||
* [#11228](https://github.com/netbox-community/netbox/issues/11228) - List both devices & VMs under device role view
|
||||
* [#11245](https://github.com/netbox-community/netbox/issues/11245) - Enable export templates for journal entries
|
||||
* [#11371](https://github.com/netbox-community/netbox/issues/11371) - Introduce additional 100M Ethernet interface types
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#10201](https://github.com/netbox-community/netbox/issues/10201) - Fix AssertionError exception when removing some terminations from an existing cable
|
||||
* [#11210](https://github.com/netbox-community/netbox/issues/11210) - Fix ValueError exception when attempting to bulk import cables attached to occupied terminations
|
||||
* [#11340](https://github.com/netbox-community/netbox/issues/11340) - Avoid flagging cable termination changes erroneously
|
||||
* [#11379](https://github.com/netbox-community/netbox/issues/11379) - Fix TypeError exception when bulk editing custom date fields
|
||||
* [#11384](https://github.com/netbox-community/netbox/issues/11384) - Correct current time display on script & report forms
|
||||
* [#11402](https://github.com/netbox-community/netbox/issues/11402) - Avoid LookupError exception when running scripts with commit disabled
|
||||
* [#11403](https://github.com/netbox-community/netbox/issues/11403) - Fix exception when scheduling a job in the past
|
||||
* [#11416](https://github.com/netbox-community/netbox/issues/11416) - Avoid AttributeError exception when deleting a cabled circuit termination
|
||||
* [#11433](https://github.com/netbox-community/netbox/issues/11433) - Avoid AttributeError exception when generating API schema for views with custom schema
|
||||
* [#11438](https://github.com/netbox-community/netbox/issues/11438) - Fix deletion of scheduled job using non-default queues
|
||||
* [#11444](https://github.com/netbox-community/netbox/issues/11444) - Adding/removing a device from a device bay should record a pre-change snapshot on the device bay
|
||||
* [#11467](https://github.com/netbox-community/netbox/issues/11467) - Correct count on interfaces tab when viewing a VC master device
|
||||
* [#11483](https://github.com/netbox-community/netbox/issues/11483) - Apply configured formatting to custom date fields
|
||||
* [#11488](https://github.com/netbox-community/netbox/issues/11488) - Add missing `description` fields to several REST API serializers
|
||||
* [#11497](https://github.com/netbox-community/netbox/issues/11497) - Enforce `run_script` permission when executing scripts via REST API
|
||||
* [#11516](https://github.com/netbox-community/netbox/issues/11516) - Prevent text highlight utility from interpreting match as regex
|
||||
* [#11522](https://github.com/netbox-community/netbox/issues/11522) - Correct tag links under contact & tenant list views
|
||||
* [#11544](https://github.com/netbox-community/netbox/issues/11544) - Catch ValidationError exception when filtering by invalid MAC address
|
||||
|
||||
---
|
||||
|
||||
## v3.4.2 (2023-01-03)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [#9285](https://github.com/netbox-community/netbox/issues/9285) - Enable specifying assigned component during bulk import of inventory items
|
||||
* [#10700](https://github.com/netbox-community/netbox/issues/10700) - Match device name when using modules quick search
|
||||
* [#11121](https://github.com/netbox-community/netbox/issues/11121) - Add VM resource totals to cluster view
|
||||
* [#11156](https://github.com/netbox-community/netbox/issues/11156) - Enable selecting assigned component when editing inventory item in UI
|
||||
* [#11223](https://github.com/netbox-community/netbox/issues/11223) - `reindex` management command should accept app label without model name
|
||||
* [#11244](https://github.com/netbox-community/netbox/issues/11244) - Add controls for saved filters to rack elevations list
|
||||
* [#11248](https://github.com/netbox-community/netbox/issues/11248) - Fix database migration when plugin with search indexer is enabled
|
||||
* [#11259](https://github.com/netbox-community/netbox/issues/11259) - Add support for Redis username configuration
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#11280](https://github.com/netbox-community/netbox/issues/11280) - Fix errant newlines when exporting interfaces with multiple IP addresses assigned
|
||||
* [#11290](https://github.com/netbox-community/netbox/issues/11290) - Correct reporting of scheduled job duration
|
||||
* [#11232](https://github.com/netbox-community/netbox/issues/11232) - Enable partial & regular expression matching for non-string types in global search
|
||||
* [#11342](https://github.com/netbox-community/netbox/issues/11342) - Correct cable trace URL under "connection" tab for device components
|
||||
* [#11345](https://github.com/netbox-community/netbox/issues/11345) - Fix form validation for bulk import of modules
|
||||
|
||||
---
|
||||
|
||||
## v3.4.1 (2022-12-16)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [#9971](https://github.com/netbox-community/netbox/issues/9971) - Enable ordering of nested group models by name
|
||||
* [#11214](https://github.com/netbox-community/netbox/issues/11214) - Introduce the `DEFAULT_LANGUAGE` configuration parameter
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#11175](https://github.com/netbox-community/netbox/issues/11175) - Fix cloning of fields containing special characters
|
||||
* [#11178](https://github.com/netbox-community/netbox/issues/11178) - Pressing enter in quick search box should not trigger bulk operations
|
||||
* [#11184](https://github.com/netbox-community/netbox/issues/11184) - Correct visualization of cable path which splits across multiple circuit terminations
|
||||
* [#11185](https://github.com/netbox-community/netbox/issues/11185) - Fix TemplateSyntaxError when viewing custom script results
|
||||
* [#11189](https://github.com/netbox-community/netbox/issues/11189) - Fix localization of dates & numbers
|
||||
* [#11205](https://github.com/netbox-community/netbox/issues/11205) - Correct cloning behavior for recursively-nested models
|
||||
* [#11206](https://github.com/netbox-community/netbox/issues/11206) - Avoid clearing assigned groups if `REMOTE_AUTH_DEFAULT_GROUPS` is invalid
|
||||
|
||||
---
|
||||
|
||||
## v3.4.0 (2022-12-14)
|
||||
|
||||
!!! warning "PostgreSQL 11 Required"
|
||||
NetBox v3.4 requires PostgreSQL 11 or later.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* Device and virtual machine names are no longer case-sensitive. Attempting to create e.g. "device1" and "DEVICE1" within the same site will raise a validation error.
|
||||
* The `asn`, `noc_contact`, `admin_contact`, and `portal_url` fields have been removed from the provider model. Please replicate any data remaining in these fields to the ASN and contact models introduced in NetBox v3.1 prior to upgrading.
|
||||
* The `content_type` fields on the CustomLink and ExportTemplate models have been renamed to `content_types` and now support the assignment of multiple content types per object.
|
||||
* Within the Python API, the `cf` property on an object with custom fields now returns deserialized values. For example, a custom field referencing an object will return the object instance rather than its numeric ID. To access the raw serialized values, reference the object's `custom_field_data` attribute instead.
|
||||
* The `NetBoxModelCSVForm` class has been renamed to `NetBoxModelImportForm`. Backward compatability with the previous name has been retained for this release, but will be dropped in NetBox v3.5.
|
||||
|
||||
### New Features
|
||||
|
||||
#### New Global Search ([#10560](https://github.com/netbox-community/netbox/issues/10560))
|
||||
|
||||
NetBox's global search functionality has been completely overhauled and replaced by a new cache-based lookup. This new implementation provides a much faster, more intelligent search capability. Results are returned in order of precedence regardless of object type, and matching field values are highlighted in the results. Additionally, custom field values are now included in global search results (where enabled). Plugins can also register their own models with the new global search engine.
|
||||
|
||||
#### Virtual Device Contexts ([#7854](https://github.com/netbox-community/netbox/issues/7854))
|
||||
|
||||
A new model representing virtual device contexts (VDCs) has been added. VDCs are logical partitions of resources within a device that can be managed independently. A VDC is created within a device and may have device interfaces assigned to it. An interface can be allocated to any number of VDCs on its device.
|
||||
|
||||
#### Saved Filters ([#9623](https://github.com/netbox-community/netbox/issues/9623))
|
||||
|
||||
Object lists can be filtered by a variety of different fields and characteristics. Applied filters can now be saved for reuse. For example, the query string
|
||||
|
||||
```
|
||||
?status=active®ion_id=12&tenant=acme
|
||||
```
|
||||
|
||||
can be saved and applied to future queries as
|
||||
|
||||
```
|
||||
?filter=my-custom-filter
|
||||
```
|
||||
|
||||
Saved filters can be kept private, or shared among NetBox users. They can be applied to both UI and REST API searches.
|
||||
|
||||
#### JSON/YAML Bulk Imports ([#4347](https://github.com/netbox-community/netbox/issues/4347))
|
||||
|
||||
NetBox's bulk import feature, which was previously limited to CSV-formatted data for most types of objects, has been extended to accept data formatted in JSON or YAML as well. This enables users to directly import objects from a variety of sources without needing to first convert data to CSV. NetBox will attempt to automatically determine the format of import data if not specified by the user.
|
||||
|
||||
#### Update Existing Objects via Bulk Import ([#7961](https://github.com/netbox-community/netbox/issues/7961))
|
||||
|
||||
NetBox's CSV-based bulk import functionality has been extended to support also modifying existing objects. When an `id` column is present in the import form, it will be used to infer the object to be modified, rather than a new object being created. All fields (columns) are optional when modifying existing objects.
|
||||
|
||||
#### Scheduled Reports & Scripts ([#8366](https://github.com/netbox-community/netbox/issues/8366))
|
||||
|
||||
Reports and custom scripts can now be scheduled for execution at a desired future time. Background scheduling is handled entirely by the existing RQ workers; there is no need to configure additional tasks to support scheduled jobs. When creating a scheduled job, the user may optionally specify an interval at which the job will run repeatedly (e.g. every 24 hours).
|
||||
|
||||
#### API for Staged Changes ([#10851](https://github.com/netbox-community/netbox/issues/10851))
|
||||
|
||||
This release introduces a new programmatic API that enables plugins and custom scripts to prepare changes in NetBox without actually committing them to the active database. To stage changes, create and activate a branch using the `checkout()` context manager. Any changes made within this context will be captured, recorded, and rolled back for future use. Once ready, a branch can be applied to the active database by calling `merge()`.
|
||||
|
||||
!!! danger "Experimental Feature"
|
||||
This feature is still under active development and considered experimental in nature. Its use in production is strongly discouraged at this time.
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [#815](https://github.com/netbox-community/netbox/issues/815) - Enable specifying terminations when bulk importing circuits
|
||||
* [#6003](https://github.com/netbox-community/netbox/issues/6003) - Enable the inclusion of custom field values in global search
|
||||
* [#7376](https://github.com/netbox-community/netbox/issues/7376) - Enable the assignment of tags during CSV import
|
||||
* [#8245](https://github.com/netbox-community/netbox/issues/8245) - Enable GraphQL filtering of related objects
|
||||
* [#8274](https://github.com/netbox-community/netbox/issues/8274) - Enable associating a custom link with multiple object types
|
||||
* [#8485](https://github.com/netbox-community/netbox/issues/8485) - Enable journaling for all organizational models
|
||||
* [#8853](https://github.com/netbox-community/netbox/issues/8853) - Introduce the `ALLOW_TOKEN_RETRIEVAL` config parameter to restrict the display of API tokens
|
||||
* [#9249](https://github.com/netbox-community/netbox/issues/9249) - Device and virtual machine names are no longer case-sensitive
|
||||
* [#9478](https://github.com/netbox-community/netbox/issues/9478) - Add `link_peers` field to GraphQL types for cabled objects
|
||||
* [#9654](https://github.com/netbox-community/netbox/issues/9654) - Add `weight` field to racks, device types, and module types
|
||||
* [#9817](https://github.com/netbox-community/netbox/issues/9817) - Add `assigned_object` field to GraphQL type for IP addresses and L2VPN terminations
|
||||
* [#9832](https://github.com/netbox-community/netbox/issues/9832) - Add `mounting_depth` field to rack model
|
||||
* [#9892](https://github.com/netbox-community/netbox/issues/9892) - Add optional `name` field for FHRP groups
|
||||
* [#10348](https://github.com/netbox-community/netbox/issues/10348) - Add decimal custom field type
|
||||
* [#10371](https://github.com/netbox-community/netbox/issues/10371) - Add `status` field for modules
|
||||
* [#10545](https://github.com/netbox-community/netbox/issues/10545) - Standardize the use of `description` and `comments` fields on all primary models
|
||||
* [#10556](https://github.com/netbox-community/netbox/issues/10556) - Include a `display` field in all GraphQL object types
|
||||
* [#10595](https://github.com/netbox-community/netbox/issues/10595) - Add GraphQL relationships for additional generic foreign key fields
|
||||
* [#10675](https://github.com/netbox-community/netbox/issues/10675) - Add `max_weight` field to track maximum load capacity for racks
|
||||
* [#10698](https://github.com/netbox-community/netbox/issues/10698) - Omit app label from content type in table columns
|
||||
* [#10710](https://github.com/netbox-community/netbox/issues/10710) - Add `status` field to WirelessLAN
|
||||
* [#10761](https://github.com/netbox-community/netbox/issues/10761) - Enable associating an export template with multiple object types
|
||||
* [#10945](https://github.com/netbox-community/netbox/issues/10945) - Enable recurring execution of scheduled reports & scripts
|
||||
* [#11022](https://github.com/netbox-community/netbox/issues/11022) - Introduce `QUEUE_MAPPINGS` configuration parameter to allow customization of background task prioritization
|
||||
|
||||
### Bug Fixes (from v3.4-beta1)
|
||||
|
||||
* [#10946](https://github.com/netbox-community/netbox/issues/10946) - Fix AttributeError exception when viewing a device with a primary IP and no platform assigned
|
||||
* [#10948](https://github.com/netbox-community/netbox/issues/10948) - Linkify primary IPs for VDCs
|
||||
* [#10950](https://github.com/netbox-community/netbox/issues/10950) - Fix validation of VDC primary IPs
|
||||
* [#10957](https://github.com/netbox-community/netbox/issues/10957) - Add missing VDCs column to interface tables
|
||||
* [#10973](https://github.com/netbox-community/netbox/issues/10973) - Fix device links in VDC table
|
||||
* [#10980](https://github.com/netbox-community/netbox/issues/10980) - Fix view tabs for plugin objects
|
||||
* [#10982](https://github.com/netbox-community/netbox/issues/10982) - Catch `NoReverseMatch` exception when rendering tabs with no registered URL
|
||||
* [#10984](https://github.com/netbox-community/netbox/issues/10984) - Fix navigation menu expansion for plugin menus comprising multiple words
|
||||
* [#11000](https://github.com/netbox-community/netbox/issues/11000) - Improve validation of YAML-formatted import data
|
||||
* [#11046](https://github.com/netbox-community/netbox/issues/11046) - Fix exception when caching very large field values for search
|
||||
* [#11154](https://github.com/netbox-community/netbox/issues/11154) - Index VM interface MAC address and MTU for global search
|
||||
* [#11171](https://github.com/netbox-community/netbox/issues/11171) - Fix querying of related objects under GraphQL API
|
||||
|
||||
### Plugins API
|
||||
|
||||
* [#4751](https://github.com/netbox-community/netbox/issues/4751) - Enable embedding custom content on core list views via `list_buttons()` method
|
||||
* [#8927](https://github.com/netbox-community/netbox/issues/8927) - Enable inclusion of plugin models in global search via `SearchIndex`
|
||||
* [#9071](https://github.com/netbox-community/netbox/issues/9071) - Enable plugins to register top-level navigation menus using PluginMenu
|
||||
* [#9072](https://github.com/netbox-community/netbox/issues/9072) - Enable registration of tabbed plugin views for core NetBox models
|
||||
* [#9880](https://github.com/netbox-community/netbox/issues/9880) - Enable plugins to install and register other Django apps via `django_apps` attribute
|
||||
* [#9887](https://github.com/netbox-community/netbox/issues/9887) - Inspect `docs_url` property to determine link to model documentation
|
||||
* [#10314](https://github.com/netbox-community/netbox/issues/10314) - Move `clone()` method from NetBoxModel to CloningMixin
|
||||
* [#10543](https://github.com/netbox-community/netbox/issues/10543) - Introduce `get_plugin_config()` utility function
|
||||
* [#10739](https://github.com/netbox-community/netbox/issues/10739) - Introduce `get_queryset()` method on generic views
|
||||
|
||||
### Other Changes
|
||||
|
||||
* [#9045](https://github.com/netbox-community/netbox/issues/9045) - Remove legacy ASN field from provider model
|
||||
* [#9046](https://github.com/netbox-community/netbox/issues/9046) - Remove legacy contact fields from provider model
|
||||
* [#10052](https://github.com/netbox-community/netbox/issues/10052) - The `cf` attribute on objects now returns deserialized custom field data
|
||||
* [#10358](https://github.com/netbox-community/netbox/issues/10358) - Raise minimum required PostgreSQL version from 10 to 11
|
||||
* [#10694](https://github.com/netbox-community/netbox/issues/10694) - Emit the `post_save` signal when creating device components in bulk
|
||||
* [#10697](https://github.com/netbox-community/netbox/issues/10697) - Move application registry into core app
|
||||
* [#10699](https://github.com/netbox-community/netbox/issues/10699) - Remove unused custom `import_object()` function
|
||||
* [#10781](https://github.com/netbox-community/netbox/issues/10781) - Add support for Python v3.11
|
||||
* [#10816](https://github.com/netbox-community/netbox/issues/10816) - Pass the current request as context when instantiating a FilterSet within UI views
|
||||
* [#10820](https://github.com/netbox-community/netbox/issues/10820) - Switch timezone library from pytz to zoneinfo
|
||||
* [#10821](https://github.com/netbox-community/netbox/issues/10821) - Enable data localization
|
||||
|
||||
### REST API Changes
|
||||
|
||||
* Added the `/api/dcim/virtual-device-contexts/` endpoint
|
||||
* circuits.provider
|
||||
* Removed the `asn`, `noc_contact`, `admin_contact`, and `portal_url` fields
|
||||
* Added a `description` field
|
||||
* dcim.Cable
|
||||
* Added `description` and `comments` fields
|
||||
* dcim.Device
|
||||
* Added a `description` field
|
||||
* dcim.DeviceType
|
||||
* Added `description`, `weight`, and `weight_unit` fields
|
||||
* dcim.Module
|
||||
* Added a `description` field
|
||||
* dcim.Interface
|
||||
* Added the `vdcs` field
|
||||
* dcim.Module
|
||||
* Added a required `status` field
|
||||
* dcim.ModuleType
|
||||
* Added `description`, `weight`, and `weight_unit` fields
|
||||
* dcim.PowerFeed
|
||||
* Added a `description` field
|
||||
* dcim.PowerPanel
|
||||
* Added `description` and `comments` fields
|
||||
* dcim.Rack
|
||||
* Added `description`, `mounting_depth`, `weight`, `max_weight`, and `weight_unit` fields
|
||||
* dcim.RackReservation
|
||||
* Added a `comments` field
|
||||
* dcim.VirtualChassis
|
||||
* Added `description` and `comments` fields
|
||||
* extras.CustomField
|
||||
* Added a `search_weight` field
|
||||
* extras.CustomLink
|
||||
* Renamed `content_type` field to `content_types`
|
||||
* extras.ExportTemplate
|
||||
* Renamed `content_type` field to `content_types`
|
||||
* extras.JobResult
|
||||
* Added `interval`, `scheduled`, and `started` fields
|
||||
* ipam.Aggregate
|
||||
* Added a `comments` field
|
||||
* ipam.ASN
|
||||
* Added a `comments` field
|
||||
* ipam.FHRPGroup
|
||||
* Added `name` and `comments` fields
|
||||
* ipam.IPAddress
|
||||
* Added a `comments` field
|
||||
* ipam.IPRange
|
||||
* Added a `comments` field
|
||||
* ipam.L2VPN
|
||||
* Added a `comments` field
|
||||
* ipam.Prefix
|
||||
* Added a `comments` field
|
||||
* ipam.RouteTarget
|
||||
* Added a `comments` field
|
||||
* ipam.Service
|
||||
* Added a `comments` field
|
||||
* ipam.ServiceTemplate
|
||||
* Added a `comments` field
|
||||
* ipam.VLAN
|
||||
* Added a `comments` field
|
||||
* ipam.VRF
|
||||
* Added a `comments` field
|
||||
* tenancy.Contact
|
||||
* Added a `description` field
|
||||
* virtualization.Cluster
|
||||
* Added a `description` field
|
||||
* virtualization.VirtualMachine
|
||||
* Added a `description` field
|
||||
* wireless.WirelessLAN
|
||||
* Added a required `status` choice field
|
||||
* Added a `comments` field
|
||||
* wireless.WirelessLink
|
||||
* Added a `comments` field
|
||||
|
||||
### GraphQL API Changes
|
||||
|
||||
* All object types now include a `display` field
|
||||
* All cabled object types now include a `link_peers` field
|
||||
* Add a `contacts` relationship for all relevant models
|
||||
* dcim.Cable
|
||||
* Add A/B terminations fields
|
||||
* dcim.CableTermination
|
||||
* Add `termination` field
|
||||
* dcim.InventoryItem
|
||||
* Add `component` field
|
||||
* dcim.InventoryItemTemplate
|
||||
* Add `component` field
|
||||
* dcim.Rack
|
||||
* Add `mounting_depth` field
|
||||
* ipam.FHRPGroupAssignment
|
||||
* Add `interface` field
|
||||
* ipam.IPAddress
|
||||
* Add `assigned_object` field
|
||||
* ipam.L2VPNTermination
|
||||
* Add `assigned_object` field
|
||||
* ipam.VLANGroupType
|
||||
* Add `scope` field
|
||||
13
mkdocs.yml
@@ -30,7 +30,7 @@ plugins:
|
||||
- os.chdir('netbox/')
|
||||
- os.environ.setdefault("DJANGO_SETTINGS_MODULE", "netbox.settings")
|
||||
- django.setup()
|
||||
rendering:
|
||||
options:
|
||||
heading_level: 3
|
||||
members_order: source
|
||||
show_root_heading: true
|
||||
@@ -38,7 +38,6 @@ plugins:
|
||||
show_root_toc_entry: false
|
||||
show_source: false
|
||||
extra:
|
||||
readthedocs: !ENV READTHEDOCS
|
||||
social:
|
||||
- icon: fontawesome/brands/github
|
||||
link: https://github.com/netbox-community/netbox
|
||||
@@ -73,6 +72,7 @@ nav:
|
||||
- Virtualization: 'features/virtualization.md'
|
||||
- Tenancy: 'features/tenancy.md'
|
||||
- Contacts: 'features/contacts.md'
|
||||
- Search: 'features/search.md'
|
||||
- Context Data: 'features/context-data.md'
|
||||
- Change Logging: 'features/change-logging.md'
|
||||
- Journaling: 'features/journaling.md'
|
||||
@@ -129,9 +129,11 @@ nav:
|
||||
- Tables: 'plugins/development/tables.md'
|
||||
- Forms: 'plugins/development/forms.md'
|
||||
- Filters & Filter Sets: 'plugins/development/filtersets.md'
|
||||
- Search: 'plugins/development/search.md'
|
||||
- REST API: 'plugins/development/rest-api.md'
|
||||
- GraphQL API: 'plugins/development/graphql-api.md'
|
||||
- Background Tasks: 'plugins/development/background-tasks.md'
|
||||
- Staged Changes: 'plugins/development/staged-changes.md'
|
||||
- Exceptions: 'plugins/development/exceptions.md'
|
||||
- Administration:
|
||||
- Authentication:
|
||||
@@ -190,13 +192,17 @@ nav:
|
||||
- Site: 'models/dcim/site.md'
|
||||
- SiteGroup: 'models/dcim/sitegroup.md'
|
||||
- VirtualChassis: 'models/dcim/virtualchassis.md'
|
||||
- VirtualDeviceContext: 'models/dcim/virtualdevicecontext.md'
|
||||
- Extras:
|
||||
- Branch: 'models/extras/branch.md'
|
||||
- ConfigContext: 'models/extras/configcontext.md'
|
||||
- CustomField: 'models/extras/customfield.md'
|
||||
- CustomLink: 'models/extras/customlink.md'
|
||||
- ExportTemplate: 'models/extras/exporttemplate.md'
|
||||
- ImageAttachment: 'models/extras/imageattachment.md'
|
||||
- JournalEntry: 'models/extras/journalentry.md'
|
||||
- SavedFilter: 'models/extras/savedfilter.md'
|
||||
- StagedChange: 'models/extras/stagedchange.md'
|
||||
- Tag: 'models/extras/tag.md'
|
||||
- Webhook: 'models/extras/webhook.md'
|
||||
- IPAM:
|
||||
@@ -245,12 +251,15 @@ nav:
|
||||
- Adding Models: 'development/adding-models.md'
|
||||
- Extending Models: 'development/extending-models.md'
|
||||
- Signals: 'development/signals.md'
|
||||
- Search: 'development/search.md'
|
||||
- Application Registry: 'development/application-registry.md'
|
||||
- User Preferences: 'development/user-preferences.md'
|
||||
- Web UI: 'development/web-ui.md'
|
||||
- Release Checklist: 'development/release-checklist.md'
|
||||
- git Cheat Sheet: 'development/git-cheat-sheet.md'
|
||||
- Release Notes:
|
||||
- Summary: 'release-notes/index.md'
|
||||
- Version 3.4: 'release-notes/version-3.4.md'
|
||||
- Version 3.3: 'release-notes/version-3.3.md'
|
||||
- Version 3.2: 'release-notes/version-3.2.md'
|
||||
- Version 3.1: 'release-notes/version-3.1.md'
|
||||
|
||||
@@ -31,8 +31,8 @@ class ProviderSerializer(NetBoxModelSerializer):
|
||||
class Meta:
|
||||
model = Provider
|
||||
fields = [
|
||||
'id', 'url', 'display', 'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact',
|
||||
'comments', 'asns', 'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count',
|
||||
'id', 'url', 'display', 'name', 'slug', 'account', 'description', 'comments', 'asns', 'tags',
|
||||
'custom_fields', 'created', 'last_updated', 'circuit_count',
|
||||
]
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ class CircuitCircuitTerminationSerializer(WritableNestedSerializer):
|
||||
model = CircuitTermination
|
||||
fields = [
|
||||
'id', 'url', 'display', 'site', 'provider_network', 'port_speed', 'upstream_speed', 'xconnect_id',
|
||||
'description',
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -6,4 +6,4 @@ class CircuitsConfig(AppConfig):
|
||||
verbose_name = "Circuits"
|
||||
|
||||
def ready(self):
|
||||
import circuits.signals
|
||||
from . import signals, search
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import django_filters
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from dcim.filtersets import CabledObjectFilterSet
|
||||
from dcim.models import Region, Site, SiteGroup
|
||||
@@ -24,48 +25,48 @@ class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
|
||||
queryset=Region.objects.all(),
|
||||
field_name='circuits__terminations__site__region',
|
||||
lookup_expr='in',
|
||||
label='Region (ID)',
|
||||
label=_('Region (ID)'),
|
||||
)
|
||||
region = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='circuits__terminations__site__region',
|
||||
lookup_expr='in',
|
||||
to_field_name='slug',
|
||||
label='Region (slug)',
|
||||
label=_('Region (slug)'),
|
||||
)
|
||||
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||
queryset=SiteGroup.objects.all(),
|
||||
field_name='circuits__terminations__site__group',
|
||||
lookup_expr='in',
|
||||
label='Site group (ID)',
|
||||
label=_('Site group (ID)'),
|
||||
)
|
||||
site_group = TreeNodeMultipleChoiceFilter(
|
||||
queryset=SiteGroup.objects.all(),
|
||||
field_name='circuits__terminations__site__group',
|
||||
lookup_expr='in',
|
||||
to_field_name='slug',
|
||||
label='Site group (slug)',
|
||||
label=_('Site group (slug)'),
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='circuits__terminations__site',
|
||||
queryset=Site.objects.all(),
|
||||
label='Site',
|
||||
label=_('Site'),
|
||||
)
|
||||
site = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='circuits__terminations__site__slug',
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Site (slug)',
|
||||
label=_('Site (slug)'),
|
||||
)
|
||||
asn_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='asns',
|
||||
queryset=ASN.objects.all(),
|
||||
label='ASN (ID)',
|
||||
label=_('ASN (ID)'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Provider
|
||||
fields = ['id', 'name', 'slug', 'asn', 'account']
|
||||
fields = ['id', 'name', 'slug', 'account']
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
@@ -73,8 +74,6 @@ class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
|
||||
return queryset.filter(
|
||||
Q(name__icontains=value) |
|
||||
Q(account__icontains=value) |
|
||||
Q(noc_contact__icontains=value) |
|
||||
Q(admin_contact__icontains=value) |
|
||||
Q(comments__icontains=value)
|
||||
)
|
||||
|
||||
@@ -82,13 +81,13 @@ class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
|
||||
class ProviderNetworkFilterSet(NetBoxModelFilterSet):
|
||||
provider_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Provider.objects.all(),
|
||||
label='Provider (ID)',
|
||||
label=_('Provider (ID)'),
|
||||
)
|
||||
provider = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='provider__slug',
|
||||
queryset=Provider.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Provider (slug)',
|
||||
label=_('Provider (slug)'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@@ -116,28 +115,28 @@ class CircuitTypeFilterSet(OrganizationalModelFilterSet):
|
||||
class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
|
||||
provider_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Provider.objects.all(),
|
||||
label='Provider (ID)',
|
||||
label=_('Provider (ID)'),
|
||||
)
|
||||
provider = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='provider__slug',
|
||||
queryset=Provider.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Provider (slug)',
|
||||
label=_('Provider (slug)'),
|
||||
)
|
||||
provider_network_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='terminations__provider_network',
|
||||
queryset=ProviderNetwork.objects.all(),
|
||||
label='ProviderNetwork (ID)',
|
||||
label=_('ProviderNetwork (ID)'),
|
||||
)
|
||||
type_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=CircuitType.objects.all(),
|
||||
label='Circuit type (ID)',
|
||||
label=_('Circuit type (ID)'),
|
||||
)
|
||||
type = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='type__slug',
|
||||
queryset=CircuitType.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Circuit type (slug)',
|
||||
label=_('Circuit type (slug)'),
|
||||
)
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
choices=CircuitStatusChoices,
|
||||
@@ -147,38 +146,38 @@ class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilte
|
||||
queryset=Region.objects.all(),
|
||||
field_name='terminations__site__region',
|
||||
lookup_expr='in',
|
||||
label='Region (ID)',
|
||||
label=_('Region (ID)'),
|
||||
)
|
||||
region = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='terminations__site__region',
|
||||
lookup_expr='in',
|
||||
to_field_name='slug',
|
||||
label='Region (slug)',
|
||||
label=_('Region (slug)'),
|
||||
)
|
||||
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||
queryset=SiteGroup.objects.all(),
|
||||
field_name='terminations__site__group',
|
||||
lookup_expr='in',
|
||||
label='Site group (ID)',
|
||||
label=_('Site group (ID)'),
|
||||
)
|
||||
site_group = TreeNodeMultipleChoiceFilter(
|
||||
queryset=SiteGroup.objects.all(),
|
||||
field_name='terminations__site__group',
|
||||
lookup_expr='in',
|
||||
to_field_name='slug',
|
||||
label='Site group (slug)',
|
||||
label=_('Site group (slug)'),
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='terminations__site',
|
||||
queryset=Site.objects.all(),
|
||||
label='Site (ID)',
|
||||
label=_('Site (ID)'),
|
||||
)
|
||||
site = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='terminations__site__slug',
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Site (slug)',
|
||||
label=_('Site (slug)'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@@ -201,25 +200,25 @@ class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilte
|
||||
class CircuitTerminationFilterSet(NetBoxModelFilterSet, CabledObjectFilterSet):
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
label=_('Search'),
|
||||
)
|
||||
circuit_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Circuit.objects.all(),
|
||||
label='Circuit',
|
||||
label=_('Circuit'),
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Site.objects.all(),
|
||||
label='Site (ID)',
|
||||
label=_('Site (ID)'),
|
||||
)
|
||||
site = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='site__slug',
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Site (slug)',
|
||||
label=_('Site (slug)'),
|
||||
)
|
||||
provider_network_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=ProviderNetwork.objects.all(),
|
||||
label='ProviderNetwork (ID)',
|
||||
label=_('ProviderNetwork (ID)'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from .bulk_edit import *
|
||||
from .bulk_import import *
|
||||
from .filtersets import *
|
||||
from .models import *
|
||||
from .model_forms import *
|
||||
|
||||
@@ -20,10 +20,6 @@ __all__ = (
|
||||
|
||||
|
||||
class ProviderBulkEditForm(NetBoxModelBulkEditForm):
|
||||
asn = forms.IntegerField(
|
||||
required=False,
|
||||
label='ASN (legacy)'
|
||||
)
|
||||
asns = DynamicModelMultipleChoiceField(
|
||||
queryset=ASN.objects.all(),
|
||||
label=_('ASNs'),
|
||||
@@ -32,33 +28,23 @@ class ProviderBulkEditForm(NetBoxModelBulkEditForm):
|
||||
account = forms.CharField(
|
||||
max_length=30,
|
||||
required=False,
|
||||
label='Account number'
|
||||
label=_('Account number')
|
||||
)
|
||||
portal_url = forms.URLField(
|
||||
required=False,
|
||||
label='Portal'
|
||||
)
|
||||
noc_contact = forms.CharField(
|
||||
required=False,
|
||||
widget=SmallTextarea,
|
||||
label='NOC contact'
|
||||
)
|
||||
admin_contact = forms.CharField(
|
||||
required=False,
|
||||
widget=SmallTextarea,
|
||||
label='Admin contact'
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
comments = CommentField(
|
||||
widget=SmallTextarea,
|
||||
label='Comments'
|
||||
label=_('Comments')
|
||||
)
|
||||
|
||||
model = Provider
|
||||
fieldsets = (
|
||||
(None, ('asn', 'asns', 'account', 'portal_url', 'noc_contact', 'admin_contact')),
|
||||
(None, ('asns', 'account', )),
|
||||
)
|
||||
nullable_fields = (
|
||||
'asn', 'asns', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
|
||||
'asns', 'account', 'description', 'comments',
|
||||
)
|
||||
|
||||
|
||||
@@ -70,7 +56,7 @@ class ProviderNetworkBulkEditForm(NetBoxModelBulkEditForm):
|
||||
service_id = forms.CharField(
|
||||
max_length=100,
|
||||
required=False,
|
||||
label='Service ID'
|
||||
label=_('Service ID')
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
@@ -78,7 +64,7 @@ class ProviderNetworkBulkEditForm(NetBoxModelBulkEditForm):
|
||||
)
|
||||
comments = CommentField(
|
||||
widget=SmallTextarea,
|
||||
label='Comments'
|
||||
label=_('Comments')
|
||||
)
|
||||
|
||||
model = ProviderNetwork
|
||||
@@ -132,7 +118,7 @@ class CircuitBulkEditForm(NetBoxModelBulkEditForm):
|
||||
)
|
||||
commit_rate = forms.IntegerField(
|
||||
required=False,
|
||||
label='Commit rate (Kbps)'
|
||||
label=_('Commit rate (Kbps)')
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=100,
|
||||
@@ -140,7 +126,7 @@ class CircuitBulkEditForm(NetBoxModelBulkEditForm):
|
||||
)
|
||||
comments = CommentField(
|
||||
widget=SmallTextarea,
|
||||
label='Comments'
|
||||
label=_('Comments')
|
||||
)
|
||||
|
||||
model = Circuit
|
||||
|
||||
@@ -1,77 +1,102 @@
|
||||
from django import forms
|
||||
|
||||
from circuits.choices import CircuitStatusChoices
|
||||
from circuits.models import *
|
||||
from netbox.forms import NetBoxModelCSVForm
|
||||
from dcim.models import Site
|
||||
from django.utils.translation import gettext as _
|
||||
from netbox.forms import NetBoxModelImportForm
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms import CSVChoiceField, CSVModelChoiceField, SlugField
|
||||
from utilities.forms import BootstrapMixin, CSVChoiceField, CSVModelChoiceField, SlugField
|
||||
|
||||
__all__ = (
|
||||
'CircuitCSVForm',
|
||||
'CircuitTypeCSVForm',
|
||||
'ProviderCSVForm',
|
||||
'ProviderNetworkCSVForm',
|
||||
'CircuitImportForm',
|
||||
'CircuitTerminationImportForm',
|
||||
'CircuitTypeImportForm',
|
||||
'ProviderImportForm',
|
||||
'ProviderNetworkImportForm',
|
||||
)
|
||||
|
||||
|
||||
class ProviderCSVForm(NetBoxModelCSVForm):
|
||||
class ProviderImportForm(NetBoxModelImportForm):
|
||||
slug = SlugField()
|
||||
|
||||
class Meta:
|
||||
model = Provider
|
||||
fields = (
|
||||
'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
|
||||
'name', 'slug', 'account', 'description', 'comments', 'tags',
|
||||
)
|
||||
|
||||
|
||||
class ProviderNetworkCSVForm(NetBoxModelCSVForm):
|
||||
class ProviderNetworkImportForm(NetBoxModelImportForm):
|
||||
provider = CSVModelChoiceField(
|
||||
queryset=Provider.objects.all(),
|
||||
to_field_name='name',
|
||||
help_text='Assigned provider'
|
||||
help_text=_('Assigned provider')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ProviderNetwork
|
||||
fields = [
|
||||
'provider', 'name', 'service_id', 'description', 'comments',
|
||||
'provider', 'name', 'service_id', 'description', 'comments', 'tags'
|
||||
]
|
||||
|
||||
|
||||
class CircuitTypeCSVForm(NetBoxModelCSVForm):
|
||||
class CircuitTypeImportForm(NetBoxModelImportForm):
|
||||
slug = SlugField()
|
||||
|
||||
class Meta:
|
||||
model = CircuitType
|
||||
fields = ('name', 'slug', 'description')
|
||||
fields = ('name', 'slug', 'description', 'tags')
|
||||
help_texts = {
|
||||
'name': 'Name of circuit type',
|
||||
'name': _('Name of circuit type'),
|
||||
}
|
||||
|
||||
|
||||
class CircuitCSVForm(NetBoxModelCSVForm):
|
||||
class CircuitImportForm(NetBoxModelImportForm):
|
||||
provider = CSVModelChoiceField(
|
||||
queryset=Provider.objects.all(),
|
||||
to_field_name='name',
|
||||
help_text='Assigned provider'
|
||||
help_text=_('Assigned provider')
|
||||
)
|
||||
type = CSVModelChoiceField(
|
||||
queryset=CircuitType.objects.all(),
|
||||
to_field_name='name',
|
||||
help_text='Type of circuit'
|
||||
help_text=_('Type of circuit')
|
||||
)
|
||||
status = CSVChoiceField(
|
||||
choices=CircuitStatusChoices,
|
||||
help_text='Operational status'
|
||||
help_text=_('Operational status')
|
||||
)
|
||||
tenant = CSVModelChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
required=False,
|
||||
to_field_name='name',
|
||||
help_text='Assigned tenant'
|
||||
help_text=_('Assigned tenant')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Circuit
|
||||
fields = [
|
||||
'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'termination_date', 'commit_rate',
|
||||
'description', 'comments',
|
||||
'description', 'comments', 'tags'
|
||||
]
|
||||
|
||||
|
||||
class CircuitTerminationImportForm(BootstrapMixin, forms.ModelForm):
|
||||
site = CSVModelChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='name',
|
||||
required=False
|
||||
)
|
||||
provider_network = CSVModelChoiceField(
|
||||
queryset=ProviderNetwork.objects.all(),
|
||||
to_field_name='name',
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = CircuitTermination
|
||||
fields = [
|
||||
'circuit', 'term_side', 'site', 'provider_network', 'port_speed', 'upstream_speed', 'xconnect_id',
|
||||
'pp_info', 'description',
|
||||
]
|
||||
|
||||
@@ -20,7 +20,7 @@ __all__ = (
|
||||
class ProviderFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
|
||||
model = Provider
|
||||
fieldsets = (
|
||||
(None, ('q', 'tag')),
|
||||
(None, ('q', 'filter_id', 'tag')),
|
||||
('Location', ('region_id', 'site_group_id', 'site_id')),
|
||||
('ASN', ('asn',)),
|
||||
('Contacts', ('contact', 'contact_role', 'contact_group')),
|
||||
@@ -59,7 +59,7 @@ class ProviderFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
|
||||
class ProviderNetworkFilterForm(NetBoxModelFilterSetForm):
|
||||
model = ProviderNetwork
|
||||
fieldsets = (
|
||||
(None, ('q', 'tag')),
|
||||
(None, ('q', 'filter_id', 'tag')),
|
||||
('Attributes', ('provider_id', 'service_id')),
|
||||
)
|
||||
provider_id = DynamicModelMultipleChoiceField(
|
||||
@@ -82,7 +82,7 @@ class CircuitTypeFilterForm(NetBoxModelFilterSetForm):
|
||||
class CircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
|
||||
model = Circuit
|
||||
fieldsets = (
|
||||
(None, ('q', 'tag')),
|
||||
(None, ('q', 'filter_id', 'tag')),
|
||||
('Provider', ('provider_id', 'provider_network_id')),
|
||||
('Attributes', ('type_id', 'status', 'install_date', 'termination_date', 'commit_rate')),
|
||||
('Location', ('region_id', 'site_group_id', 'site_id')),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from django import forms
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from circuits.models import *
|
||||
@@ -7,8 +6,8 @@ from ipam.models import ASN
|
||||
from netbox.forms import NetBoxModelForm
|
||||
from tenancy.forms import TenancyForm
|
||||
from utilities.forms import (
|
||||
BootstrapMixin, CommentField, DatePicker, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
|
||||
SelectSpeedWidget, SmallTextarea, SlugField, StaticSelect,
|
||||
CommentField, DatePicker, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SelectSpeedWidget, SlugField,
|
||||
StaticSelect,
|
||||
)
|
||||
|
||||
__all__ = (
|
||||
@@ -30,29 +29,17 @@ class ProviderForm(NetBoxModelForm):
|
||||
comments = CommentField()
|
||||
|
||||
fieldsets = (
|
||||
('Provider', ('name', 'slug', 'asn', 'asns', 'tags')),
|
||||
('Support Info', ('account', 'portal_url', 'noc_contact', 'admin_contact')),
|
||||
('Provider', ('name', 'slug', 'asns', 'description', 'tags')),
|
||||
('Support Info', ('account',)),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Provider
|
||||
fields = [
|
||||
'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'asns', 'comments', 'tags',
|
||||
'name', 'slug', 'account', 'asns', 'description', 'comments', 'tags',
|
||||
]
|
||||
widgets = {
|
||||
'noc_contact': SmallTextarea(
|
||||
attrs={'rows': 5}
|
||||
),
|
||||
'admin_contact': SmallTextarea(
|
||||
attrs={'rows': 5}
|
||||
),
|
||||
}
|
||||
help_texts = {
|
||||
'name': "Full name of the provider",
|
||||
'asn': "BGP autonomous system number (if applicable)",
|
||||
'portal_url': "URL of the provider's customer support portal",
|
||||
'noc_contact': "NOC email address and phone number",
|
||||
'admin_contact': "Administrative contact email address and phone number",
|
||||
'name': _("Full name of the provider"),
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +63,12 @@ class ProviderNetworkForm(NetBoxModelForm):
|
||||
class CircuitTypeForm(NetBoxModelForm):
|
||||
slug = SlugField()
|
||||
|
||||
fieldsets = (
|
||||
('Circuit Type', (
|
||||
'name', 'slug', 'description', 'tags',
|
||||
)),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = CircuitType
|
||||
fields = [
|
||||
@@ -105,8 +98,8 @@ class CircuitForm(TenancyForm, NetBoxModelForm):
|
||||
'tenant_group', 'tenant', 'comments', 'tags',
|
||||
]
|
||||
help_texts = {
|
||||
'cid': "Unique circuit ID",
|
||||
'commit_rate': "Committed rate",
|
||||
'cid': _("Unique circuit ID"),
|
||||
'commit_rate': _("Committed rate"),
|
||||
}
|
||||
widgets = {
|
||||
'status': StaticSelect(),
|
||||
@@ -152,21 +145,33 @@ class CircuitTerminationForm(NetBoxModelForm):
|
||||
},
|
||||
required=False
|
||||
)
|
||||
provider_network_provider = DynamicModelChoiceField(
|
||||
queryset=Provider.objects.all(),
|
||||
required=False,
|
||||
label='Provider',
|
||||
initial_params={
|
||||
'networks': 'provider_network'
|
||||
}
|
||||
)
|
||||
provider_network = DynamicModelChoiceField(
|
||||
queryset=ProviderNetwork.objects.all(),
|
||||
query_params={
|
||||
'provider_id': '$provider_network_provider',
|
||||
},
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = CircuitTermination
|
||||
fields = [
|
||||
'provider', 'circuit', 'term_side', 'region', 'site_group', 'site', 'provider_network', 'mark_connected',
|
||||
'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', 'description', 'tags',
|
||||
'provider', 'circuit', 'term_side', 'region', 'site_group', 'site', 'provider_network_provider',
|
||||
'provider_network', 'mark_connected', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info',
|
||||
'description', 'tags',
|
||||
]
|
||||
help_texts = {
|
||||
'port_speed': "Physical circuit speed",
|
||||
'xconnect_id': "ID of the local cross-connect",
|
||||
'pp_info': "Patch panel ID and port number(s)"
|
||||
'port_speed': _("Physical circuit speed"),
|
||||
'xconnect_id': _("ID of the local cross-connect"),
|
||||
'pp_info': _("Patch panel ID and port number(s)")
|
||||
}
|
||||
widgets = {
|
||||
'term_side': StaticSelect(),
|
||||
@@ -1,6 +1,8 @@
|
||||
import graphene
|
||||
|
||||
from circuits import filtersets, models
|
||||
from dcim.graphql.mixins import CabledObjectMixin
|
||||
from extras.graphql.mixins import CustomFieldsMixin, TagsMixin
|
||||
from extras.graphql.mixins import CustomFieldsMixin, TagsMixin, ContactsMixin
|
||||
from netbox.graphql.types import ObjectType, OrganizationalObjectType, NetBoxObjectType
|
||||
|
||||
__all__ = (
|
||||
@@ -20,8 +22,7 @@ class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, Ob
|
||||
filterset_class = filtersets.CircuitTerminationFilterSet
|
||||
|
||||
|
||||
class CircuitType(NetBoxObjectType):
|
||||
|
||||
class CircuitType(NetBoxObjectType, ContactsMixin):
|
||||
class Meta:
|
||||
model = models.Circuit
|
||||
fields = '__all__'
|
||||
@@ -36,7 +37,7 @@ class CircuitTypeType(OrganizationalObjectType):
|
||||
filterset_class = filtersets.CircuitTypeFilterSet
|
||||
|
||||
|
||||
class ProviderType(NetBoxObjectType):
|
||||
class ProviderType(NetBoxObjectType, ContactsMixin):
|
||||
|
||||
class Meta:
|
||||
model = models.Provider
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import dcim.fields
|
||||
import django.core.serializers.json
|
||||
from utilities.json import CustomFieldJSONEncoder
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
@@ -21,7 +21,7 @@ class Migration(migrations.Migration):
|
||||
fields=[
|
||||
('created', models.DateField(auto_now_add=True, null=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
|
||||
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
|
||||
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||
('cid', models.CharField(max_length=100)),
|
||||
('status', models.CharField(default='active', max_length=50)),
|
||||
@@ -58,14 +58,14 @@ class Migration(migrations.Migration):
|
||||
fields=[
|
||||
('created', models.DateField(auto_now_add=True, null=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
|
||||
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
|
||||
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=100, unique=True)),
|
||||
('slug', models.SlugField(max_length=100, unique=True)),
|
||||
('description', models.CharField(blank=True, max_length=200)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
'ordering': ('name',),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
@@ -73,7 +73,7 @@ class Migration(migrations.Migration):
|
||||
fields=[
|
||||
('created', models.DateField(auto_now_add=True, null=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
|
||||
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
|
||||
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=100, unique=True)),
|
||||
('slug', models.SlugField(max_length=100, unique=True)),
|
||||
@@ -93,7 +93,7 @@ class Migration(migrations.Migration):
|
||||
fields=[
|
||||
('created', models.DateField(auto_now_add=True, null=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
|
||||
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
|
||||
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=100)),
|
||||
('description', models.CharField(blank=True, max_length=200)),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import django.core.serializers.json
|
||||
from utilities.json import CustomFieldJSONEncoder
|
||||
from django.db import migrations, models
|
||||
import taggit.managers
|
||||
|
||||
@@ -18,7 +18,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name='circuittermination',
|
||||
name='custom_field_data',
|
||||
field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
|
||||
field=models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='circuittermination',
|
||||
|
||||
39
netbox/circuits/migrations/0039_unique_constraints.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('circuits', '0038_cabling_cleanup'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveConstraint(
|
||||
model_name='providernetwork',
|
||||
name='circuits_providernetwork_provider_name',
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='circuit',
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='circuittermination',
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='providernetwork',
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='circuit',
|
||||
constraint=models.UniqueConstraint(fields=('provider', 'cid'), name='circuits_circuit_unique_provider_cid'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='circuittermination',
|
||||
constraint=models.UniqueConstraint(fields=('circuit', 'term_side'), name='circuits_circuittermination_unique_circuit_term_side'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='providernetwork',
|
||||
constraint=models.UniqueConstraint(fields=('provider', 'name'), name='circuits_providernetwork_unique_provider_name'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,59 @@
|
||||
import os
|
||||
|
||||
from django.db import migrations
|
||||
from django.db.utils import DataError
|
||||
|
||||
|
||||
def check_legacy_data(apps, schema_editor):
|
||||
"""
|
||||
Abort the migration if any legacy provider fields still contain data.
|
||||
"""
|
||||
Provider = apps.get_model('circuits', 'Provider')
|
||||
|
||||
provider_count = Provider.objects.exclude(asn__isnull=True).count()
|
||||
if provider_count and 'NETBOX_DELETE_LEGACY_DATA' not in os.environ:
|
||||
raise DataError(
|
||||
f"Unable to proceed with deleting asn field from Provider model: Found {provider_count} "
|
||||
f"providers with legacy ASN data. Please ensure all legacy provider ASN data has been "
|
||||
f"migrated to ASN objects before proceeding. Or, set the NETBOX_DELETE_LEGACY_DATA "
|
||||
f"environment variable to bypass this safeguard and delete all legacy provider ASN data."
|
||||
)
|
||||
|
||||
provider_count = Provider.objects.exclude(admin_contact='', noc_contact='', portal_url='').count()
|
||||
if provider_count and 'NETBOX_DELETE_LEGACY_DATA' not in os.environ:
|
||||
raise DataError(
|
||||
f"Unable to proceed with deleting contact fields from Provider model: Found {provider_count} "
|
||||
f"providers with legacy contact data. Please ensure all legacy provider contact data has been "
|
||||
f"migrated to contact objects before proceeding. Or, set the NETBOX_DELETE_LEGACY_DATA "
|
||||
f"environment variable to bypass this safeguard and delete all legacy provider contact data."
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('circuits', '0039_unique_constraints'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=check_legacy_data,
|
||||
reverse_code=migrations.RunPython.noop
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='provider',
|
||||
name='admin_contact',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='provider',
|
||||
name='asn',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='provider',
|
||||
name='noc_contact',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='provider',
|
||||
name='portal_url',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.1.2 on 2022-11-03 18:24
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('circuits', '0040_provider_remove_deprecated_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='provider',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=200),
|
||||
),
|
||||
]
|
||||
@@ -1,12 +1,14 @@
|
||||
from django.apps import apps
|
||||
from django.contrib.contenttypes.fields import GenericRelation
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from circuits.choices import *
|
||||
from dcim.models import CabledObjectModel
|
||||
from netbox.models import (
|
||||
ChangeLoggedModel, CustomFieldsMixin, CustomLinksMixin, OrganizationalModel, NetBoxModel, TagsMixin,
|
||||
ChangeLoggedModel, CustomFieldsMixin, CustomLinksMixin, OrganizationalModel, PrimaryModel, TagsMixin,
|
||||
)
|
||||
from netbox.models.features import WebhooksMixin
|
||||
|
||||
@@ -22,30 +24,11 @@ class CircuitType(OrganizationalModel):
|
||||
Circuits can be organized by their functional role. For example, a user might wish to define CircuitTypes named
|
||||
"Long Haul," "Metro," or "Out-of-Band".
|
||||
"""
|
||||
name = models.CharField(
|
||||
max_length=100,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
max_length=100,
|
||||
unique=True
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=200,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('circuits:circuittype', args=[self.pk])
|
||||
|
||||
|
||||
class Circuit(NetBoxModel):
|
||||
class Circuit(PrimaryModel):
|
||||
"""
|
||||
A communications circuit connects two points. Each Circuit belongs to a Provider; Providers may have multiple
|
||||
circuits. Each circuit is also assigned a CircuitType and a Site. Circuit port speed and commit rate are measured
|
||||
@@ -91,13 +74,6 @@ class Circuit(NetBoxModel):
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='Commit rate (Kbps)')
|
||||
description = models.CharField(
|
||||
max_length=200,
|
||||
blank=True
|
||||
)
|
||||
comments = models.TextField(
|
||||
blank=True
|
||||
)
|
||||
|
||||
# Generic relations
|
||||
contacts = GenericRelation(
|
||||
@@ -128,10 +104,19 @@ class Circuit(NetBoxModel):
|
||||
clone_fields = (
|
||||
'provider', 'type', 'status', 'tenant', 'install_date', 'termination_date', 'commit_rate', 'description',
|
||||
)
|
||||
prerequisite_models = (
|
||||
'circuits.CircuitType',
|
||||
'circuits.Provider',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['provider', 'cid']
|
||||
unique_together = ['provider', 'cid']
|
||||
constraints = (
|
||||
models.UniqueConstraint(
|
||||
fields=('provider', 'cid'),
|
||||
name='%(app_label)s_%(class)s_unique_provider_cid'
|
||||
),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.cid
|
||||
@@ -184,7 +169,7 @@ class CircuitTermination(
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='Upstream speed (Kbps)',
|
||||
help_text='Upstream speed, if different from port speed'
|
||||
help_text=_('Upstream speed, if different from port speed')
|
||||
)
|
||||
xconnect_id = models.CharField(
|
||||
max_length=50,
|
||||
@@ -203,7 +188,12 @@ class CircuitTermination(
|
||||
|
||||
class Meta:
|
||||
ordering = ['circuit', 'term_side']
|
||||
unique_together = ['circuit', 'term_side']
|
||||
constraints = (
|
||||
models.UniqueConstraint(
|
||||
fields=('circuit', 'term_side'),
|
||||
name='%(app_label)s_%(class)s_unique_circuit_term_side'
|
||||
),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f'Termination {self.term_side}: {self.site or self.provider_network}'
|
||||
|
||||
@@ -2,8 +2,7 @@ from django.contrib.contenttypes.fields import GenericRelation
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
|
||||
from dcim.fields import ASNField
|
||||
from netbox.models import NetBoxModel
|
||||
from netbox.models import PrimaryModel
|
||||
|
||||
__all__ = (
|
||||
'ProviderNetwork',
|
||||
@@ -11,7 +10,7 @@ __all__ = (
|
||||
)
|
||||
|
||||
|
||||
class Provider(NetBoxModel):
|
||||
class Provider(PrimaryModel):
|
||||
"""
|
||||
Each Circuit belongs to a Provider. This is usually a telecommunications company or similar organization. This model
|
||||
stores information pertinent to the user's relationship with the Provider.
|
||||
@@ -24,12 +23,6 @@ class Provider(NetBoxModel):
|
||||
max_length=100,
|
||||
unique=True
|
||||
)
|
||||
asn = ASNField(
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='ASN',
|
||||
help_text='32-bit autonomous system number'
|
||||
)
|
||||
asns = models.ManyToManyField(
|
||||
to='ipam.ASN',
|
||||
related_name='providers',
|
||||
@@ -40,21 +33,6 @@ class Provider(NetBoxModel):
|
||||
blank=True,
|
||||
verbose_name='Account number'
|
||||
)
|
||||
portal_url = models.URLField(
|
||||
blank=True,
|
||||
verbose_name='Portal URL'
|
||||
)
|
||||
noc_contact = models.TextField(
|
||||
blank=True,
|
||||
verbose_name='NOC contact'
|
||||
)
|
||||
admin_contact = models.TextField(
|
||||
blank=True,
|
||||
verbose_name='Admin contact'
|
||||
)
|
||||
comments = models.TextField(
|
||||
blank=True
|
||||
)
|
||||
|
||||
# Generic relations
|
||||
contacts = GenericRelation(
|
||||
@@ -62,7 +40,7 @@ class Provider(NetBoxModel):
|
||||
)
|
||||
|
||||
clone_fields = (
|
||||
'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact',
|
||||
'account',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@@ -75,7 +53,7 @@ class Provider(NetBoxModel):
|
||||
return reverse('circuits:provider', args=[self.pk])
|
||||
|
||||
|
||||
class ProviderNetwork(NetBoxModel):
|
||||
class ProviderNetwork(PrimaryModel):
|
||||
"""
|
||||
This represents a provider network which exists outside of NetBox, the details of which are unknown or
|
||||
unimportant to the user.
|
||||
@@ -93,23 +71,15 @@ class ProviderNetwork(NetBoxModel):
|
||||
blank=True,
|
||||
verbose_name='Service ID'
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=200,
|
||||
blank=True
|
||||
)
|
||||
comments = models.TextField(
|
||||
blank=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ('provider', 'name')
|
||||
constraints = (
|
||||
models.UniqueConstraint(
|
||||
fields=('provider', 'name'),
|
||||
name='circuits_providernetwork_provider_name'
|
||||
name='%(app_label)s_%(class)s_unique_provider_name'
|
||||
),
|
||||
)
|
||||
unique_together = ('provider', 'name')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
56
netbox/circuits/search.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from netbox.search import SearchIndex, register_search
|
||||
from . import models
|
||||
|
||||
|
||||
@register_search
|
||||
class CircuitIndex(SearchIndex):
|
||||
model = models.Circuit
|
||||
fields = (
|
||||
('cid', 100),
|
||||
('description', 500),
|
||||
('comments', 5000),
|
||||
)
|
||||
|
||||
|
||||
@register_search
|
||||
class CircuitTerminationIndex(SearchIndex):
|
||||
model = models.CircuitTermination
|
||||
fields = (
|
||||
('xconnect_id', 300),
|
||||
('pp_info', 300),
|
||||
('description', 500),
|
||||
('port_speed', 2000),
|
||||
('upstream_speed', 2000),
|
||||
)
|
||||
|
||||
|
||||
@register_search
|
||||
class CircuitTypeIndex(SearchIndex):
|
||||
model = models.CircuitType
|
||||
fields = (
|
||||
('name', 100),
|
||||
('slug', 110),
|
||||
('description', 500),
|
||||
)
|
||||
|
||||
|
||||
@register_search
|
||||
class ProviderIndex(SearchIndex):
|
||||
model = models.Provider
|
||||
fields = (
|
||||
('name', 100),
|
||||
('account', 200),
|
||||
('description', 500),
|
||||
('comments', 5000),
|
||||
)
|
||||
|
||||
|
||||
@register_search
|
||||
class ProviderNetworkIndex(SearchIndex):
|
||||
model = models.ProviderNetwork
|
||||
fields = (
|
||||
('name', 100),
|
||||
('service_id', 200),
|
||||
('description', 500),
|
||||
('comments', 5000),
|
||||
)
|
||||
@@ -1,8 +1,9 @@
|
||||
import django_tables2 as tables
|
||||
|
||||
from circuits.models import *
|
||||
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
|
||||
|
||||
from netbox.tables import NetBoxTable, columns
|
||||
from tenancy.tables import TenancyColumnsMixin
|
||||
|
||||
from .columns import CommitRateColumn
|
||||
|
||||
__all__ = (
|
||||
@@ -39,7 +40,7 @@ class CircuitTypeTable(NetBoxTable):
|
||||
default_columns = ('pk', 'name', 'circuit_count', 'description', 'slug')
|
||||
|
||||
|
||||
class CircuitTable(TenancyColumnsMixin, NetBoxTable):
|
||||
class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
|
||||
cid = tables.Column(
|
||||
linkify=True,
|
||||
verbose_name='Circuit ID'
|
||||
@@ -58,9 +59,6 @@ class CircuitTable(TenancyColumnsMixin, NetBoxTable):
|
||||
)
|
||||
commit_rate = CommitRateColumn()
|
||||
comments = columns.MarkdownColumn()
|
||||
contacts = columns.ManyToManyColumn(
|
||||
linkify_item=True
|
||||
)
|
||||
tags = columns.TagColumn(
|
||||
url_name='circuits:circuit_list'
|
||||
)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import django_tables2 as tables
|
||||
from django_tables2.utils import Accessor
|
||||
|
||||
from circuits.models import *
|
||||
from django_tables2.utils import Accessor
|
||||
from tenancy.tables import ContactsColumnMixin
|
||||
|
||||
from netbox.tables import NetBoxTable, columns
|
||||
|
||||
__all__ = (
|
||||
@@ -10,7 +11,7 @@ __all__ = (
|
||||
)
|
||||
|
||||
|
||||
class ProviderTable(NetBoxTable):
|
||||
class ProviderTable(ContactsColumnMixin, NetBoxTable):
|
||||
name = tables.Column(
|
||||
linkify=True
|
||||
)
|
||||
@@ -31,9 +32,6 @@ class ProviderTable(NetBoxTable):
|
||||
verbose_name='Circuits'
|
||||
)
|
||||
comments = columns.MarkdownColumn()
|
||||
contacts = columns.ManyToManyColumn(
|
||||
linkify_item=True
|
||||
)
|
||||
tags = columns.TagColumn(
|
||||
url_name='circuits:provider_list'
|
||||
)
|
||||
@@ -41,10 +39,10 @@ class ProviderTable(NetBoxTable):
|
||||
class Meta(NetBoxTable.Meta):
|
||||
model = Provider
|
||||
fields = (
|
||||
'pk', 'id', 'name', 'asn', 'asns', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'asn_count',
|
||||
'circuit_count', 'comments', 'contacts', 'tags', 'created', 'last_updated',
|
||||
'pk', 'id', 'name', 'asns', 'account', 'asn_count', 'circuit_count', 'description', 'comments', 'contacts',
|
||||
'tags', 'created', 'last_updated',
|
||||
)
|
||||
default_columns = ('pk', 'name', 'asn', 'account', 'circuit_count')
|
||||
default_columns = ('pk', 'name', 'account', 'circuit_count')
|
||||
|
||||
|
||||
class ProviderNetworkTable(NetBoxTable):
|
||||
|
||||
@@ -20,7 +20,7 @@ class ProviderTest(APIViewTestCases.APIViewTestCase):
|
||||
model = Provider
|
||||
brief_fields = ['circuit_count', 'display', 'id', 'name', 'slug', 'url']
|
||||
bulk_update_data = {
|
||||
'asn': 1234,
|
||||
'account': '1234',
|
||||
}
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -25,11 +25,11 @@ class ProviderTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
ASN.objects.bulk_create(asns)
|
||||
|
||||
providers = (
|
||||
Provider(name='Provider 1', slug='provider-1', asn=65001, account='1234'),
|
||||
Provider(name='Provider 2', slug='provider-2', asn=65002, account='2345'),
|
||||
Provider(name='Provider 3', slug='provider-3', asn=65003, account='3456'),
|
||||
Provider(name='Provider 4', slug='provider-4', asn=65004, account='4567'),
|
||||
Provider(name='Provider 5', slug='provider-5', asn=65005, account='5678'),
|
||||
Provider(name='Provider 1', slug='provider-1', account='1234'),
|
||||
Provider(name='Provider 2', slug='provider-2', account='2345'),
|
||||
Provider(name='Provider 3', slug='provider-3', account='3456'),
|
||||
Provider(name='Provider 4', slug='provider-4', account='4567'),
|
||||
Provider(name='Provider 5', slug='provider-5', account='5678'),
|
||||
)
|
||||
Provider.objects.bulk_create(providers)
|
||||
providers[0].asns.set([asns[0]])
|
||||
@@ -82,10 +82,6 @@ class ProviderTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
params = {'slug': ['provider-1', 'provider-2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_asn(self): # Legacy field
|
||||
params = {'asn': ['65001', '65002']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_asn_id(self): # ASN object assignment
|
||||
asns = ASN.objects.all()[:2]
|
||||
params = {'asn_id': [asns[0].pk, asns[1].pk]}
|
||||
@@ -344,6 +340,7 @@ class CircuitTerminationTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 4'),
|
||||
Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 5'),
|
||||
Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 6'),
|
||||
Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 7'),
|
||||
)
|
||||
Circuit.objects.bulk_create(circuits)
|
||||
|
||||
@@ -357,6 +354,7 @@ class CircuitTerminationTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
CircuitTermination(circuit=circuits[3], provider_network=provider_networks[0], term_side='A'),
|
||||
CircuitTermination(circuit=circuits[4], provider_network=provider_networks[1], term_side='A'),
|
||||
CircuitTermination(circuit=circuits[5], provider_network=provider_networks[2], term_side='A'),
|
||||
CircuitTermination(circuit=circuits[6], provider_network=provider_networks[0], term_side='A', mark_connected=True),
|
||||
))
|
||||
CircuitTermination.objects.bulk_create(circuit_terminations)
|
||||
|
||||
@@ -364,7 +362,7 @@ class CircuitTerminationTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
|
||||
def test_term_side(self):
|
||||
params = {'term_side': 'A'}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6)
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 7)
|
||||
|
||||
def test_port_speed(self):
|
||||
params = {'port_speed': ['1000', '2000']}
|
||||
@@ -397,11 +395,19 @@ class CircuitTerminationTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
def test_provider_network(self):
|
||||
provider_networks = ProviderNetwork.objects.all()[:2]
|
||||
params = {'provider_network_id': [provider_networks[0].pk, provider_networks[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
def test_cabled(self):
|
||||
params = {'cabled': True}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'cabled': False}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 8)
|
||||
|
||||
def test_occupied(self):
|
||||
params = {'occupied': True}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
params = {'occupied': False}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 7)
|
||||
|
||||
|
||||
class ProviderNetworkTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
|
||||
@@ -23,9 +23,9 @@ class ProviderTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
ASN.objects.bulk_create(asns)
|
||||
|
||||
providers = (
|
||||
Provider(name='Provider 1', slug='provider-1', asn=65001),
|
||||
Provider(name='Provider 2', slug='provider-2', asn=65002),
|
||||
Provider(name='Provider 3', slug='provider-3', asn=65003),
|
||||
Provider(name='Provider 1', slug='provider-1'),
|
||||
Provider(name='Provider 2', slug='provider-2'),
|
||||
Provider(name='Provider 3', slug='provider-3'),
|
||||
)
|
||||
Provider.objects.bulk_create(providers)
|
||||
providers[0].asns.set([asns[0], asns[1]])
|
||||
@@ -37,12 +37,8 @@ class ProviderTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
cls.form_data = {
|
||||
'name': 'Provider X',
|
||||
'slug': 'provider-x',
|
||||
'asn': 65123,
|
||||
'asns': [asns[6].pk, asns[7].pk],
|
||||
'account': '1234',
|
||||
'portal_url': 'http://example.com/portal',
|
||||
'noc_contact': 'noc@example.com',
|
||||
'admin_contact': 'admin@example.com',
|
||||
'comments': 'Another provider',
|
||||
'tags': [t.pk for t in tags],
|
||||
}
|
||||
@@ -54,12 +50,15 @@ class ProviderTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Provider 6,provider-6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,comments",
|
||||
f"{providers[0].pk},Provider 7,New comment7",
|
||||
f"{providers[1].pk},Provider 8,New comment8",
|
||||
f"{providers[2].pk},Provider 9,New comment9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'asn': 65009,
|
||||
'account': '5678',
|
||||
'portal_url': 'http://example.com/portal2',
|
||||
'noc_contact': 'noc2@example.com',
|
||||
'admin_contact': 'admin2@example.com',
|
||||
'comments': 'New comments',
|
||||
}
|
||||
|
||||
@@ -70,11 +69,13 @@ class CircuitTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
CircuitType.objects.bulk_create([
|
||||
circuit_types = (
|
||||
CircuitType(name='Circuit Type 1', slug='circuit-type-1'),
|
||||
CircuitType(name='Circuit Type 2', slug='circuit-type-2'),
|
||||
CircuitType(name='Circuit Type 3', slug='circuit-type-3'),
|
||||
])
|
||||
)
|
||||
|
||||
CircuitType.objects.bulk_create(circuit_types)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -92,6 +93,13 @@ class CircuitTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Circuit Type 6,circuit-type-6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{circuit_types[0].pk},Circuit Type 7,New description7",
|
||||
f"{circuit_types[1].pk},Circuit Type 8,New description8",
|
||||
f"{circuit_types[2].pk},Circuit Type 9,New description9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'Foo',
|
||||
}
|
||||
@@ -100,12 +108,19 @@ class CircuitTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = Circuit
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.add_permissions(
|
||||
'circuits.add_circuittermination',
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
providers = (
|
||||
Provider(name='Provider 1', slug='provider-1', asn=65001),
|
||||
Provider(name='Provider 2', slug='provider-2', asn=65002),
|
||||
Provider(name='Provider 1', slug='provider-1'),
|
||||
Provider(name='Provider 2', slug='provider-2'),
|
||||
)
|
||||
Provider.objects.bulk_create(providers)
|
||||
|
||||
@@ -115,11 +130,13 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
)
|
||||
CircuitType.objects.bulk_create(circuittypes)
|
||||
|
||||
Circuit.objects.bulk_create([
|
||||
circuits = (
|
||||
Circuit(cid='Circuit 1', provider=providers[0], type=circuittypes[0]),
|
||||
Circuit(cid='Circuit 2', provider=providers[0], type=circuittypes[0]),
|
||||
Circuit(cid='Circuit 3', provider=providers[0], type=circuittypes[0]),
|
||||
])
|
||||
)
|
||||
|
||||
Circuit.objects.bulk_create(circuits)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -144,6 +161,13 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Circuit 6,Provider 1,Circuit Type 1,active",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
f"id,cid,description,status",
|
||||
f"{circuits[0].pk},Circuit 7,New description7,{CircuitStatusChoices.STATUS_DECOMMISSIONED}",
|
||||
f"{circuits[1].pk},Circuit 8,New description8,{CircuitStatusChoices.STATUS_DECOMMISSIONED}",
|
||||
f"{circuits[2].pk},Circuit 9,New description9,{CircuitStatusChoices.STATUS_DECOMMISSIONED}",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'provider': providers[1].pk,
|
||||
'type': circuittypes[1].pk,
|
||||
@@ -167,11 +191,13 @@ class ProviderNetworkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
)
|
||||
Provider.objects.bulk_create(providers)
|
||||
|
||||
ProviderNetwork.objects.bulk_create([
|
||||
provider_networks = (
|
||||
ProviderNetwork(name='Provider Network 1', provider=providers[0]),
|
||||
ProviderNetwork(name='Provider Network 2', provider=providers[0]),
|
||||
ProviderNetwork(name='Provider Network 3', provider=providers[0]),
|
||||
])
|
||||
)
|
||||
|
||||
ProviderNetwork.objects.bulk_create(provider_networks)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -190,6 +216,13 @@ class ProviderNetworkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Provider Network 6,Provider 1,Baz",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{provider_networks[0].pk},Provider Network 7,New description7",
|
||||
f"{provider_networks[1].pk},Provider Network 8,New description8",
|
||||
f"{provider_networks[2].pk},Provider Network 9,New description9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'provider': providers[1].pk,
|
||||
'description': 'New description',
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
from django.urls import path
|
||||
from django.urls import include, path
|
||||
|
||||
from dcim.views import PathTraceView
|
||||
from netbox.views.generic import ObjectChangeLogView, ObjectJournalView
|
||||
from utilities.urls import get_model_urls
|
||||
from . import views
|
||||
from .models import *
|
||||
|
||||
app_name = 'circuits'
|
||||
urlpatterns = [
|
||||
@@ -14,11 +12,7 @@ urlpatterns = [
|
||||
path('providers/import/', views.ProviderBulkImportView.as_view(), name='provider_import'),
|
||||
path('providers/edit/', views.ProviderBulkEditView.as_view(), name='provider_bulk_edit'),
|
||||
path('providers/delete/', views.ProviderBulkDeleteView.as_view(), name='provider_bulk_delete'),
|
||||
path('providers/<int:pk>/', views.ProviderView.as_view(), name='provider'),
|
||||
path('providers/<int:pk>/edit/', views.ProviderEditView.as_view(), name='provider_edit'),
|
||||
path('providers/<int:pk>/delete/', views.ProviderDeleteView.as_view(), name='provider_delete'),
|
||||
path('providers/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='provider_changelog', kwargs={'model': Provider}),
|
||||
path('providers/<int:pk>/journal/', ObjectJournalView.as_view(), name='provider_journal', kwargs={'model': Provider}),
|
||||
path('providers/<int:pk>/', include(get_model_urls('circuits', 'provider'))),
|
||||
|
||||
# Provider networks
|
||||
path('provider-networks/', views.ProviderNetworkListView.as_view(), name='providernetwork_list'),
|
||||
@@ -26,11 +20,7 @@ urlpatterns = [
|
||||
path('provider-networks/import/', views.ProviderNetworkBulkImportView.as_view(), name='providernetwork_import'),
|
||||
path('provider-networks/edit/', views.ProviderNetworkBulkEditView.as_view(), name='providernetwork_bulk_edit'),
|
||||
path('provider-networks/delete/', views.ProviderNetworkBulkDeleteView.as_view(), name='providernetwork_bulk_delete'),
|
||||
path('provider-networks/<int:pk>/', views.ProviderNetworkView.as_view(), name='providernetwork'),
|
||||
path('provider-networks/<int:pk>/edit/', views.ProviderNetworkEditView.as_view(), name='providernetwork_edit'),
|
||||
path('provider-networks/<int:pk>/delete/', views.ProviderNetworkDeleteView.as_view(), name='providernetwork_delete'),
|
||||
path('provider-networks/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='providernetwork_changelog', kwargs={'model': ProviderNetwork}),
|
||||
path('provider-networks/<int:pk>/journal/', ObjectJournalView.as_view(), name='providernetwork_journal', kwargs={'model': ProviderNetwork}),
|
||||
path('provider-networks/<int:pk>/', include(get_model_urls('circuits', 'providernetwork'))),
|
||||
|
||||
# Circuit types
|
||||
path('circuit-types/', views.CircuitTypeListView.as_view(), name='circuittype_list'),
|
||||
@@ -38,10 +28,7 @@ urlpatterns = [
|
||||
path('circuit-types/import/', views.CircuitTypeBulkImportView.as_view(), name='circuittype_import'),
|
||||
path('circuit-types/edit/', views.CircuitTypeBulkEditView.as_view(), name='circuittype_bulk_edit'),
|
||||
path('circuit-types/delete/', views.CircuitTypeBulkDeleteView.as_view(), name='circuittype_bulk_delete'),
|
||||
path('circuit-types/<int:pk>/', views.CircuitTypeView.as_view(), name='circuittype'),
|
||||
path('circuit-types/<int:pk>/edit/', views.CircuitTypeEditView.as_view(), name='circuittype_edit'),
|
||||
path('circuit-types/<int:pk>/delete/', views.CircuitTypeDeleteView.as_view(), name='circuittype_delete'),
|
||||
path('circuit-types/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='circuittype_changelog', kwargs={'model': CircuitType}),
|
||||
path('circuit-types/<int:pk>/', include(get_model_urls('circuits', 'circuittype'))),
|
||||
|
||||
# Circuits
|
||||
path('circuits/', views.CircuitListView.as_view(), name='circuit_list'),
|
||||
@@ -49,17 +36,11 @@ urlpatterns = [
|
||||
path('circuits/import/', views.CircuitBulkImportView.as_view(), name='circuit_import'),
|
||||
path('circuits/edit/', views.CircuitBulkEditView.as_view(), name='circuit_bulk_edit'),
|
||||
path('circuits/delete/', views.CircuitBulkDeleteView.as_view(), name='circuit_bulk_delete'),
|
||||
path('circuits/<int:pk>/', views.CircuitView.as_view(), name='circuit'),
|
||||
path('circuits/<int:pk>/edit/', views.CircuitEditView.as_view(), name='circuit_edit'),
|
||||
path('circuits/<int:pk>/delete/', views.CircuitDeleteView.as_view(), name='circuit_delete'),
|
||||
path('circuits/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='circuit_changelog', kwargs={'model': Circuit}),
|
||||
path('circuits/<int:pk>/journal/', ObjectJournalView.as_view(), name='circuit_journal', kwargs={'model': Circuit}),
|
||||
path('circuits/<int:pk>/terminations/swap/', views.CircuitSwapTerminations.as_view(), name='circuit_terminations_swap'),
|
||||
path('circuits/<int:pk>/', include(get_model_urls('circuits', 'circuit'))),
|
||||
|
||||
# Circuit terminations
|
||||
path('circuit-terminations/add/', views.CircuitTerminationEditView.as_view(), name='circuittermination_add'),
|
||||
path('circuit-terminations/<int:pk>/edit/', views.CircuitTerminationEditView.as_view(), name='circuittermination_edit'),
|
||||
path('circuit-terminations/<int:pk>/delete/', views.CircuitTerminationDeleteView.as_view(), name='circuittermination_delete'),
|
||||
path('circuit-terminations/<int:pk>/trace/', PathTraceView.as_view(), name='circuittermination_trace', kwargs={'model': CircuitTermination}),
|
||||
path('circuit-terminations/<int:pk>/', include(get_model_urls('circuits', 'circuittermination'))),
|
||||
|
||||
]
|
||||
|
||||
@@ -3,9 +3,11 @@ from django.db import transaction
|
||||
from django.db.models import Q
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
|
||||
from dcim.views import PathTraceView
|
||||
from netbox.views import generic
|
||||
from utilities.forms import ConfirmationForm
|
||||
from utilities.utils import count_related
|
||||
from utilities.views import register_model_view
|
||||
from . import filtersets, forms, tables
|
||||
from .models import *
|
||||
|
||||
@@ -23,6 +25,7 @@ class ProviderListView(generic.ObjectListView):
|
||||
table = tables.ProviderTable
|
||||
|
||||
|
||||
@register_model_view(Provider)
|
||||
class ProviderView(generic.ObjectView):
|
||||
queryset = Provider.objects.all()
|
||||
|
||||
@@ -41,18 +44,20 @@ class ProviderView(generic.ObjectView):
|
||||
}
|
||||
|
||||
|
||||
@register_model_view(Provider, 'edit')
|
||||
class ProviderEditView(generic.ObjectEditView):
|
||||
queryset = Provider.objects.all()
|
||||
form = forms.ProviderForm
|
||||
|
||||
|
||||
@register_model_view(Provider, 'delete')
|
||||
class ProviderDeleteView(generic.ObjectDeleteView):
|
||||
queryset = Provider.objects.all()
|
||||
|
||||
|
||||
class ProviderBulkImportView(generic.BulkImportView):
|
||||
queryset = Provider.objects.all()
|
||||
model_form = forms.ProviderCSVForm
|
||||
model_form = forms.ProviderImportForm
|
||||
table = tables.ProviderTable
|
||||
|
||||
|
||||
@@ -84,6 +89,7 @@ class ProviderNetworkListView(generic.ObjectListView):
|
||||
table = tables.ProviderNetworkTable
|
||||
|
||||
|
||||
@register_model_view(ProviderNetwork)
|
||||
class ProviderNetworkView(generic.ObjectView):
|
||||
queryset = ProviderNetwork.objects.all()
|
||||
|
||||
@@ -103,18 +109,20 @@ class ProviderNetworkView(generic.ObjectView):
|
||||
}
|
||||
|
||||
|
||||
@register_model_view(ProviderNetwork, 'edit')
|
||||
class ProviderNetworkEditView(generic.ObjectEditView):
|
||||
queryset = ProviderNetwork.objects.all()
|
||||
form = forms.ProviderNetworkForm
|
||||
|
||||
|
||||
@register_model_view(ProviderNetwork, 'delete')
|
||||
class ProviderNetworkDeleteView(generic.ObjectDeleteView):
|
||||
queryset = ProviderNetwork.objects.all()
|
||||
|
||||
|
||||
class ProviderNetworkBulkImportView(generic.BulkImportView):
|
||||
queryset = ProviderNetwork.objects.all()
|
||||
model_form = forms.ProviderNetworkCSVForm
|
||||
model_form = forms.ProviderNetworkImportForm
|
||||
table = tables.ProviderNetworkTable
|
||||
|
||||
|
||||
@@ -144,6 +152,7 @@ class CircuitTypeListView(generic.ObjectListView):
|
||||
table = tables.CircuitTypeTable
|
||||
|
||||
|
||||
@register_model_view(CircuitType)
|
||||
class CircuitTypeView(generic.ObjectView):
|
||||
queryset = CircuitType.objects.all()
|
||||
|
||||
@@ -157,18 +166,20 @@ class CircuitTypeView(generic.ObjectView):
|
||||
}
|
||||
|
||||
|
||||
@register_model_view(CircuitType, 'edit')
|
||||
class CircuitTypeEditView(generic.ObjectEditView):
|
||||
queryset = CircuitType.objects.all()
|
||||
form = forms.CircuitTypeForm
|
||||
|
||||
|
||||
@register_model_view(CircuitType, 'delete')
|
||||
class CircuitTypeDeleteView(generic.ObjectDeleteView):
|
||||
queryset = CircuitType.objects.all()
|
||||
|
||||
|
||||
class CircuitTypeBulkImportView(generic.BulkImportView):
|
||||
queryset = CircuitType.objects.all()
|
||||
model_form = forms.CircuitTypeCSVForm
|
||||
model_form = forms.CircuitTypeImportForm
|
||||
table = tables.CircuitTypeTable
|
||||
|
||||
|
||||
@@ -202,23 +213,36 @@ class CircuitListView(generic.ObjectListView):
|
||||
table = tables.CircuitTable
|
||||
|
||||
|
||||
@register_model_view(Circuit)
|
||||
class CircuitView(generic.ObjectView):
|
||||
queryset = Circuit.objects.all()
|
||||
|
||||
|
||||
@register_model_view(Circuit, 'edit')
|
||||
class CircuitEditView(generic.ObjectEditView):
|
||||
queryset = Circuit.objects.all()
|
||||
form = forms.CircuitForm
|
||||
|
||||
|
||||
@register_model_view(Circuit, 'delete')
|
||||
class CircuitDeleteView(generic.ObjectDeleteView):
|
||||
queryset = Circuit.objects.all()
|
||||
|
||||
|
||||
class CircuitBulkImportView(generic.BulkImportView):
|
||||
queryset = Circuit.objects.all()
|
||||
model_form = forms.CircuitCSVForm
|
||||
model_form = forms.CircuitImportForm
|
||||
table = tables.CircuitTable
|
||||
additional_permissions = [
|
||||
'circuits.add_circuittermination',
|
||||
]
|
||||
related_object_forms = {
|
||||
'terminations': forms.CircuitTerminationImportForm,
|
||||
}
|
||||
|
||||
def prep_related_object_data(self, parent, data):
|
||||
data.update({'circuit': parent})
|
||||
return data
|
||||
|
||||
|
||||
class CircuitBulkEditView(generic.BulkEditView):
|
||||
@@ -318,11 +342,17 @@ class CircuitSwapTerminations(generic.ObjectEditView):
|
||||
# Circuit terminations
|
||||
#
|
||||
|
||||
@register_model_view(CircuitTermination, 'edit')
|
||||
class CircuitTerminationEditView(generic.ObjectEditView):
|
||||
queryset = CircuitTermination.objects.all()
|
||||
form = forms.CircuitTerminationForm
|
||||
template_name = 'circuits/circuittermination_edit.html'
|
||||
|
||||
|
||||
@register_model_view(CircuitTermination, 'delete')
|
||||
class CircuitTerminationDeleteView(generic.ObjectDeleteView):
|
||||
queryset = CircuitTermination.objects.all()
|
||||
|
||||
|
||||
# Trace view
|
||||
register_model_view(CircuitTermination, 'trace', kwargs={'model': CircuitTermination})(PathTraceView)
|
||||
|
||||