mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-27 23:57:46 -06:00
Compare commits
557 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e13d4ffe60 | ||
|
|
2581a55214 | ||
|
|
aa4b89f751 | ||
|
|
838aaffc4b | ||
|
|
9dfd0e5b40 | ||
|
|
3357c050c4 | ||
|
|
60c5418516 | ||
|
|
48b4695ebe | ||
|
|
737b05d12b | ||
|
|
1d0546b3d1 | ||
|
|
a7a166a9cb | ||
|
|
74e1c08324 | ||
|
|
007de40ada | ||
|
|
e184eb3521 | ||
|
|
e421c15bdd | ||
|
|
469a088874 | ||
|
|
63dbee16cc | ||
|
|
5f3f21215a | ||
|
|
cdd7ed21ee | ||
|
|
255d12309a | ||
|
|
856d14aaa6 | ||
|
|
134cf38a84 | ||
|
|
1a56a5561c | ||
|
|
eb7fbe4b3a | ||
|
|
9d3215e806 | ||
|
|
9e855ac6cd | ||
|
|
a6fde3168b | ||
|
|
939a7bbe50 | ||
|
|
c6d18da2eb | ||
|
|
606f3dacbb | ||
|
|
aa73a7ad02 | ||
|
|
a4687be5e5 | ||
|
|
302f87e108 | ||
|
|
439fa731ba | ||
|
|
c6eb40daa8 | ||
|
|
f15cde0275 | ||
|
|
83427d5585 | ||
|
|
d3f278400e | ||
|
|
295d4f0394 | ||
|
|
8aad11b8d2 | ||
|
|
0a1dd64b94 | ||
|
|
f220b3f128 | ||
|
|
1c0e0fec4c | ||
|
|
5369aef971 | ||
|
|
9f569d4b1b | ||
|
|
604924231a | ||
|
|
ea91e09a1b | ||
|
|
0f1518e4c5 | ||
|
|
4cb1facb6a | ||
|
|
e640f413ab | ||
|
|
fecbb60c36 | ||
|
|
c0f1910493 | ||
|
|
3eb2d45e8d | ||
|
|
4556eac780 | ||
|
|
c955aeebeb | ||
|
|
8bd67b2c17 | ||
|
|
4073dedff8 | ||
|
|
bc696f2e11 | ||
|
|
c28684a8b3 | ||
|
|
215b4d0b3f | ||
|
|
d9437a08f0 | ||
|
|
f81e7d30e2 | ||
|
|
09bee75cb3 | ||
|
|
9c4ab79bea | ||
|
|
f8dad1744c | ||
|
|
b98ac64ac2 | ||
|
|
88267e9d05 | ||
|
|
caf7d02637 | ||
|
|
aefe2b4196 | ||
|
|
fdf8211e9a | ||
|
|
73d1a2df3d | ||
|
|
0893d32665 | ||
|
|
c5ec470a00 | ||
|
|
28350d84f9 | ||
|
|
1055faf734 | ||
|
|
351a6e005e | ||
|
|
e5ebe6cebc | ||
|
|
0053aa2d2e | ||
|
|
dda9a2ee1c | ||
|
|
1ea820a50e | ||
|
|
c202c1325b | ||
|
|
49f027fae7 | ||
|
|
deec10efe7 | ||
|
|
826f4d313d | ||
|
|
685cf50268 | ||
|
|
e0ea5b0e0b | ||
|
|
b538495a29 | ||
|
|
8df53eac91 | ||
|
|
857e04e90b | ||
|
|
3f37cc461d | ||
|
|
823e1280d2 | ||
|
|
a9e1e7fc78 | ||
|
|
f27e06e619 | ||
|
|
c084547dca | ||
|
|
6959785cd1 | ||
|
|
26a257b794 | ||
|
|
2615906526 | ||
|
|
d96f474a5f | ||
|
|
d33e10b4ce | ||
|
|
e536f363f9 | ||
|
|
9d0da0f45a | ||
|
|
7b8e82f321 | ||
|
|
5c047faa1d | ||
|
|
8f636d9636 | ||
|
|
ce0e351d76 | ||
|
|
f170a579de | ||
|
|
0dad9f8901 | ||
|
|
473d67354f | ||
|
|
5d7af0fae9 | ||
|
|
946779000f | ||
|
|
c3c3000a53 | ||
|
|
66daeda85f | ||
|
|
0fd0e76183 | ||
|
|
cf480375f6 | ||
|
|
736cd709d9 | ||
|
|
0f42219b4b | ||
|
|
b64f4b93eb | ||
|
|
32ffc1b54b | ||
|
|
608006ee77 | ||
|
|
3d78a67343 | ||
|
|
2f98f133bb | ||
|
|
fe0fbeab49 | ||
|
|
e3aacb183b | ||
|
|
49fa243b4f | ||
|
|
a2308b9c99 | ||
|
|
422c6bad5b | ||
|
|
db57d3830f | ||
|
|
b7e78028ce | ||
|
|
ca13045515 | ||
|
|
2e9f21e222 | ||
|
|
9f627fd0d3 | ||
|
|
509a115f68 | ||
|
|
69a696a8d6 | ||
|
|
830a51d9f5 | ||
|
|
3c247ac47d | ||
|
|
123a58bf7d | ||
|
|
f20d16f188 | ||
|
|
9399652dd0 | ||
|
|
f4514034b8 | ||
|
|
6bc8f2e50b | ||
|
|
fc1245c49d | ||
|
|
4be7ca0c78 | ||
|
|
cb91c9231d | ||
|
|
03b22594e8 | ||
|
|
a5413a5484 | ||
|
|
71120d9899 | ||
|
|
acb66c7dc0 | ||
|
|
2eba84dad5 | ||
|
|
fe89982d4e | ||
|
|
528b345f57 | ||
|
|
e3807a8937 | ||
|
|
da0ac4ff1e | ||
|
|
49a6a36f4c | ||
|
|
a77fadd114 | ||
|
|
15e1f62919 | ||
|
|
83c0d1ef44 | ||
|
|
97654b7585 | ||
|
|
0767de205e | ||
|
|
847cf9d038 | ||
|
|
0296aa240a | ||
|
|
d88b3456c4 | ||
|
|
789cf827f2 | ||
|
|
6c19c88e99 | ||
|
|
c1ed2b6068 | ||
|
|
790cfd7b5b | ||
|
|
dc5f5efcfe | ||
|
|
4eacc57522 | ||
|
|
0527626709 | ||
|
|
a2ead6af94 | ||
|
|
1e740a70f7 | ||
|
|
94a7d8e493 | ||
|
|
883655ce71 | ||
|
|
1d3651e255 | ||
|
|
fe490d144a | ||
|
|
40fe6666e3 | ||
|
|
2a2bc66841 | ||
|
|
6019260374 | ||
|
|
e5c5a1a101 | ||
|
|
c13b9d8798 | ||
|
|
03b10b6f73 | ||
|
|
67f4d8fab5 | ||
|
|
7e0073d6f5 | ||
|
|
c66dca399b | ||
|
|
9d085ad83a | ||
|
|
ad565e55f1 | ||
|
|
46c712e735 | ||
|
|
989d6f5af3 | ||
|
|
86865b91f8 | ||
|
|
b53480dd6a | ||
|
|
581ed52b24 | ||
|
|
472486acd6 | ||
|
|
92ec4bd22f | ||
|
|
959a0da0ed | ||
|
|
b23eaeca54 | ||
|
|
4f9271e9ff | ||
|
|
094553dbe7 | ||
|
|
60e4812b32 | ||
|
|
40625d1299 | ||
|
|
c9ec8b71e0 | ||
|
|
73e456495f | ||
|
|
54227ca9c7 | ||
|
|
f3b323536e | ||
|
|
6537f35176 | ||
|
|
b36d0ca3fc | ||
|
|
99809109ab | ||
|
|
1cdbfd6d60 | ||
|
|
4030e5ec24 | ||
|
|
4151e52802 | ||
|
|
b1e8145ffb | ||
|
|
c04d8ca5a7 | ||
|
|
e312c30822 | ||
|
|
40c30baffa | ||
|
|
bca7435a5a | ||
|
|
396bb28967 | ||
|
|
eb40275427 | ||
|
|
8519f546a6 | ||
|
|
e9b2ad9f5c | ||
|
|
39fba4f05d | ||
|
|
38c16d71b4 | ||
|
|
8fef6edb27 | ||
|
|
5cac900380 | ||
|
|
f49467bcb5 | ||
|
|
98a66f7fbe | ||
|
|
ce8d470860 | ||
|
|
dc475f4755 | ||
|
|
a7982bb0e1 | ||
|
|
ea05b5b606 | ||
|
|
996d49de67 | ||
|
|
74997a18a5 | ||
|
|
832fd49339 | ||
|
|
acb2f32304 | ||
|
|
32f39e10c9 | ||
|
|
190e683654 | ||
|
|
770f4c962c | ||
|
|
227921e0a0 | ||
|
|
39d0261d8a | ||
|
|
f267a532f6 | ||
|
|
e7ee4486a5 | ||
|
|
6a3cd83efc | ||
|
|
a7ec0c14f7 | ||
|
|
05bfe94d3e | ||
|
|
8d0aaa4ec1 | ||
|
|
4b5c4b7be5 | ||
|
|
18333973aa | ||
|
|
07a1baef13 | ||
|
|
15545b70d6 | ||
|
|
cfb8b3cf56 | ||
|
|
206732eb62 | ||
|
|
258cc4b50e | ||
|
|
d1f81783ef | ||
|
|
9bd2af48a3 | ||
|
|
d4df965f46 | ||
|
|
7dddd4734c | ||
|
|
c45daca5f2 | ||
|
|
067af26892 | ||
|
|
792f38334a | ||
|
|
dba40cd6bc | ||
|
|
4b19073b8b | ||
|
|
28eca9a026 | ||
|
|
3556051d14 | ||
|
|
e1d1f522ff | ||
|
|
04f3e58ab4 | ||
|
|
1f175031bd | ||
|
|
e1c61c5019 | ||
|
|
1c0de0093b | ||
|
|
9d8ab81e3a | ||
|
|
6e49a0ba6e | ||
|
|
fa55571503 | ||
|
|
feb04f0401 | ||
|
|
5c07b6dc1d | ||
|
|
e3b448b7ad | ||
|
|
57f199f899 | ||
|
|
b38bb64c81 | ||
|
|
1d63a30b7a | ||
|
|
c2dc243c7c | ||
|
|
25c3c1b431 | ||
|
|
156fbae7d3 | ||
|
|
a0ae7a227d | ||
|
|
9ef3e68479 | ||
|
|
435d248645 | ||
|
|
53db5090c1 | ||
|
|
caa062c8ba | ||
|
|
240bbc2944 | ||
|
|
a3861ed492 | ||
|
|
82c70302fd | ||
|
|
80d1f80b61 | ||
|
|
d3c6caf8a8 | ||
|
|
a707204f98 | ||
|
|
523a1388db | ||
|
|
970586b07b | ||
|
|
28ae6849b4 | ||
|
|
8e3a371688 | ||
|
|
2a219eff23 | ||
|
|
f81641ae96 | ||
|
|
4f0d3e6b32 | ||
|
|
77d1ac8b07 | ||
|
|
5d0ac02704 | ||
|
|
fc5d07bb13 | ||
|
|
395f23e1d3 | ||
|
|
dd85448451 | ||
|
|
6a7af22dea | ||
|
|
37bc17d3a2 | ||
|
|
ca131e5b2a | ||
|
|
c75795ceda | ||
|
|
7dc0591e3e | ||
|
|
cfa078c929 | ||
|
|
57ea73db46 | ||
|
|
7ad9e8a2fb | ||
|
|
1a57120b78 | ||
|
|
d267aeb621 | ||
|
|
a110b0badb | ||
|
|
242ae9eb91 | ||
|
|
53625e0dea | ||
|
|
aa4f73ffbf | ||
|
|
8c7b0cf670 | ||
|
|
05570ae4ad | ||
|
|
0cf94cff16 | ||
|
|
b5455ed882 | ||
|
|
8a4293a4cc | ||
|
|
f649b9f04f | ||
|
|
5caa04ef2b | ||
|
|
f2c49063f8 | ||
|
|
ea2351e902 | ||
|
|
8effb54c89 | ||
|
|
66c99acb9e | ||
|
|
2e5a326315 | ||
|
|
b5177c608d | ||
|
|
be7d5a2310 | ||
|
|
65444899af | ||
|
|
5230127fde | ||
|
|
405320d8ab | ||
|
|
2f2e193cf9 | ||
|
|
1fc206b9c5 | ||
|
|
44e1a477f2 | ||
|
|
e62302c979 | ||
|
|
51b0fe4596 | ||
|
|
7399aa0c5e | ||
|
|
aa4588f9ba | ||
|
|
4c2e2e0fa3 | ||
|
|
6dea8ddbce | ||
|
|
e6623a6ca8 | ||
|
|
020e485196 | ||
|
|
0c5f535689 | ||
|
|
ae9d0d894a | ||
|
|
14401c30b6 | ||
|
|
fbb93c72d0 | ||
|
|
280f31955a | ||
|
|
5fedcd1f4e | ||
|
|
adeee0bf5c | ||
|
|
84a2b726f5 | ||
|
|
407a60dcc4 | ||
|
|
d31507985b | ||
|
|
0174c9747b | ||
|
|
55b503da5b | ||
|
|
aff4ad0f97 | ||
|
|
70b09446b6 | ||
|
|
3a18d1df8c | ||
|
|
01883f92d9 | ||
|
|
8acd3d0a72 | ||
|
|
85563e21db | ||
|
|
9352867cec | ||
|
|
93837c5b9e | ||
|
|
aaf5523378 | ||
|
|
8a22f83bc0 | ||
|
|
7550d53d02 | ||
|
|
821cd58825 | ||
|
|
526412a1ba | ||
|
|
fae2469dd7 | ||
|
|
27fd351fc2 | ||
|
|
ea5c9df095 | ||
|
|
5aa17bc42a | ||
|
|
7bbd9be389 | ||
|
|
dbf9a2b452 | ||
|
|
e4b8ea8905 | ||
|
|
fd8913f09b | ||
|
|
645383509b | ||
|
|
77a5f6ef81 | ||
|
|
2ec7259a69 | ||
|
|
b6f8c248de | ||
|
|
b16be577e3 | ||
|
|
590bbbce7f | ||
|
|
577b4593de | ||
|
|
44e5a63a2a | ||
|
|
19363add30 | ||
|
|
fd88ba65b2 | ||
|
|
ce4a5a38a3 | ||
|
|
05938d60f0 | ||
|
|
8b189abd58 | ||
|
|
7f788eaa06 | ||
|
|
9f204f72ef | ||
|
|
e02ac133eb | ||
|
|
c09aefd509 | ||
|
|
e0c81fd837 | ||
|
|
9a8c8012be | ||
|
|
f1e75b0fbb | ||
|
|
b4d724b5ae | ||
|
|
f7a1595ce5 | ||
|
|
378883df2b | ||
|
|
dafa2513e3 | ||
|
|
5710f297f1 | ||
|
|
0d8fd45587 | ||
|
|
01b6e73f9b | ||
|
|
b0fb474aa2 | ||
|
|
0787713298 | ||
|
|
6221bc8f43 | ||
|
|
a6904dc5d5 | ||
|
|
3d5fb2491a | ||
|
|
24bdd10b4f | ||
|
|
7845d9b25f | ||
|
|
1f288175e4 | ||
|
|
f0a6c881bc | ||
|
|
9452cebc6a | ||
|
|
12657ebd7c | ||
|
|
d8dd5f00c1 | ||
|
|
1ec191db92 | ||
|
|
20c8abe8da | ||
|
|
086813fc3e | ||
|
|
606a73a547 | ||
|
|
740cc8b601 | ||
|
|
5ac4a3ba79 | ||
|
|
2e37b19e9f | ||
|
|
a8dd060e32 | ||
|
|
b0adce998b | ||
|
|
6fe1b9b37c | ||
|
|
44b042e3fc | ||
|
|
e5b49e25b9 | ||
|
|
b54a05d255 | ||
|
|
66b6f586f1 | ||
|
|
82819d5429 | ||
|
|
bc859c6139 | ||
|
|
e1fdc7773a | ||
|
|
5d5b7e0dd9 | ||
|
|
77890ad775 | ||
|
|
1a9b9f50d8 | ||
|
|
808f5c95e3 | ||
|
|
d11de6d021 | ||
|
|
88c7d95b08 | ||
|
|
2956eff051 | ||
|
|
b05566e96f | ||
|
|
446acbdf82 | ||
|
|
47fefbec07 | ||
|
|
45917f8014 | ||
|
|
dd3e7397a4 | ||
|
|
7518174374 | ||
|
|
02a009b7a7 | ||
|
|
ec9443edb8 | ||
|
|
dcb2c4722c | ||
|
|
b0c0adf6e7 | ||
|
|
17898a4c57 | ||
|
|
edbf562803 | ||
|
|
5d772d7055 | ||
|
|
8cbd2f5c2d | ||
|
|
89e720cb77 | ||
|
|
2583823e5f | ||
|
|
33890e6b97 | ||
|
|
a2b0da2608 | ||
|
|
2bcbcd3458 | ||
|
|
8efde811e9 | ||
|
|
4e1ee270cf | ||
|
|
7a3c725f51 | ||
|
|
fe2c682dc3 | ||
|
|
adb25fd7d7 | ||
|
|
bfea77baa5 | ||
|
|
3ff22bea56 | ||
|
|
ca11b9a2f5 | ||
|
|
4ecbfc4e5e | ||
|
|
213bd1555a | ||
|
|
14a7a33cc2 | ||
|
|
ba8f324b12 | ||
|
|
929c0648d0 | ||
|
|
21fe5902a8 | ||
|
|
8c7f6c62b0 | ||
|
|
2dc07ca30d | ||
|
|
bb8b012397 | ||
|
|
62494f295e | ||
|
|
9872a46583 | ||
|
|
4846557d61 | ||
|
|
79a40e22c9 | ||
|
|
dead5b42be | ||
|
|
bcc34f6099 | ||
|
|
5c95927a43 | ||
|
|
3fa4ceadb0 | ||
|
|
f93cd17fee | ||
|
|
5f5081f719 | ||
|
|
180d3d0029 | ||
|
|
4e2863e4ec | ||
|
|
f3a41df395 | ||
|
|
afd82fd9d3 | ||
|
|
fbd12e1887 | ||
|
|
4f0d82ac16 | ||
|
|
88b8cf3360 | ||
|
|
a8db07e0a8 | ||
|
|
c79c29e769 | ||
|
|
e1e09bff9b | ||
|
|
07aa036fe8 | ||
|
|
8d27b62114 | ||
|
|
62b45494b6 | ||
|
|
e09ad6915f | ||
|
|
a2a6b754be | ||
|
|
d392982fe7 | ||
|
|
8cb7eb0382 | ||
|
|
48dcd2d2b9 | ||
|
|
6ccaa0d9bd | ||
|
|
a6f2d5b414 | ||
|
|
7306d56902 | ||
|
|
b8f1585976 | ||
|
|
cf5c24ee2d | ||
|
|
a63383dc08 | ||
|
|
0ad19081ac | ||
|
|
bb30f64252 | ||
|
|
c4dd76a748 | ||
|
|
56a248e601 | ||
|
|
0f65cf23a5 | ||
|
|
fd3f718a0a | ||
|
|
93d28e6a72 | ||
|
|
1cfb8aea23 | ||
|
|
657a7aef42 | ||
|
|
ea0d232169 | ||
|
|
c6893731ad | ||
|
|
2ffbced47e | ||
|
|
bfce177a07 | ||
|
|
8ca182571c | ||
|
|
801dd384d8 | ||
|
|
adc96702db | ||
|
|
18e0bd8937 | ||
|
|
0d15ac15ae | ||
|
|
b013a60b12 | ||
|
|
d787c353f3 | ||
|
|
553fe0fe97 | ||
|
|
aeeb49a6a7 | ||
|
|
3262805938 | ||
|
|
807d849657 | ||
|
|
6892b79366 | ||
|
|
88d61db384 | ||
|
|
ee4e68b082 | ||
|
|
edc1b52f65 | ||
|
|
36d4f0d259 | ||
|
|
5f3528cf74 | ||
|
|
47f1febfc9 | ||
|
|
93154abb31 | ||
|
|
778e5bed3c | ||
|
|
0615d368f2 | ||
|
|
30ee232654 | ||
|
|
2621f17cde | ||
|
|
15b2a7eab0 | ||
|
|
5049c6c0a1 | ||
|
|
60b70b6c7b | ||
|
|
5266fc67c9 | ||
|
|
56dcadb69b | ||
|
|
f8fdca4968 | ||
|
|
9e258dd31e | ||
|
|
480db83f39 | ||
|
|
cc0f0c4843 | ||
|
|
a6511632ad | ||
|
|
15b55f5e62 | ||
|
|
dccda62f2d | ||
|
|
ef432754ee |
23
.github/lock.yml
vendored
Normal file
23
.github/lock.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# Configuration for Lock (https://github.com/apps/lock)
|
||||
|
||||
# Number of days of inactivity before a closed issue or pull request is locked
|
||||
daysUntilLock: 90
|
||||
|
||||
# Skip issues and pull requests created before a given timestamp. Timestamp must
|
||||
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
|
||||
skipCreatedBefore: false
|
||||
|
||||
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
|
||||
exemptLabels: []
|
||||
|
||||
# Label to add before locking, such as `outdated`. Set to `false` to disable
|
||||
lockLabel: false
|
||||
|
||||
# Comment to post before locking. Set to `false` to disable
|
||||
lockComment: false
|
||||
|
||||
# Assign `resolved` as the reason for locking. Set to `false` to disable
|
||||
setLockReason: true
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
# only: issues
|
||||
7
.github/stale.yml
vendored
7
.github/stale.yml
vendored
@@ -1,20 +1,27 @@
|
||||
# Configuration for Stale (https://github.com/apps/stale)
|
||||
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 14
|
||||
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- "status: accepted"
|
||||
- "status: gathering feedback"
|
||||
- "status: blocked"
|
||||
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: wontfix
|
||||
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. NetBox
|
||||
is governed by a small group of core maintainers which means not all opened
|
||||
issues may receive direct feedback. Please see our [contributing guide](https://github.com/netbox-community/netbox/blob/develop/CONTRIBUTING.md).
|
||||
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: >
|
||||
This issue has been automatically closed due to lack of activity. In an
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -12,5 +12,9 @@
|
||||
fabfile.py
|
||||
*.swp
|
||||
gunicorn_config.py
|
||||
gunicorn.py
|
||||
netbox.log
|
||||
netbox.pid
|
||||
.DS_Store
|
||||
.vscode
|
||||
.coverage
|
||||
|
||||
@@ -10,6 +10,7 @@ python:
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
- pip install pycodestyle
|
||||
- pip install coverage
|
||||
before_script:
|
||||
- psql --version
|
||||
- psql -U postgres -c 'SELECT version();'
|
||||
|
||||
@@ -16,7 +16,7 @@ For real-time discussion, you can join the #netbox Slack channel on [NetworkToCo
|
||||
|
||||
## Reporting Bugs
|
||||
|
||||
* First, ensure that you've installed the [latest stable version](https://github.com/netbox-community/netbox/releases)
|
||||
* 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.
|
||||
|
||||
@@ -28,27 +28,26 @@ 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.
|
||||
|
||||
* If you haven't found an existing issue that describes your suspected bug,
|
||||
please inquire about it on the mailing list. **Do not** file an issue until you
|
||||
have received confirmation that it is in fact a bug. Invalid issues are very
|
||||
distracting and slow the pace at which NetBox is developed.
|
||||
|
||||
* When submitting an issue, please be as descriptive as possible. Be sure to
|
||||
include:
|
||||
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 (if applicable)
|
||||
* 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 moderator after submission and the appropriate
|
||||
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.
|
||||
|
||||
* 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
|
||||
|
||||
* First, check the GitHub [issues list](https://github.com/netbox-community/netbox/issues)
|
||||
@@ -61,10 +60,10 @@ 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.)
|
||||
|
||||
* Due to an excessive backlog of feature requests, we are not currently
|
||||
accepting any proposals which substantially extend NetBox's functionality
|
||||
beyond its current feature set. This includes the introduction of any new views
|
||||
or models which have not already been proposed in an existing feature request.
|
||||
* Due to a large backlog of feature requests, we are not currently accepting
|
||||
any proposals which substantially extend NetBox's functionality beyond its
|
||||
current feature set. This includes the introduction of any new views or models
|
||||
which have not already been proposed in an existing feature request.
|
||||
|
||||
* Before filing a new feature request, consider raising your idea on the
|
||||
mailing list first. Feedback you receive there will help validate and shape the
|
||||
@@ -75,8 +74,8 @@ 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.
|
||||
|
||||
* When submitting a feature request on GitHub, be sure to include the
|
||||
following:
|
||||
* When submitting a feature request on GitHub, be sure to include all
|
||||
information requested by the issue template, including:
|
||||
|
||||
* A detailed description of the proposed functionality
|
||||
* A use case for the feature; who would use it and what value it would add
|
||||
@@ -89,6 +88,9 @@ following:
|
||||
title. The issue will be reviewed by a moderator after submission and the
|
||||
appropriate labels will be applied for categorization.
|
||||
|
||||
* 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).
|
||||
|
||||
## Submitting Pull Requests
|
||||
|
||||
* Be sure to open an issue **before** starting work on a pull request, and
|
||||
@@ -103,7 +105,7 @@ any work that's already in progress.
|
||||
|
||||
* 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 new stable releases.
|
||||
development, while `master` is used for tagging stable releases.
|
||||
|
||||
* All code submissions should meet the following criteria (CI will enforce
|
||||
these checks):
|
||||
@@ -122,27 +124,26 @@ reduce noise in the discussion.
|
||||
|
||||
## Issue Lifecycle
|
||||
|
||||
When a correctly formatted issue is submitted it is evaluated by a moderator
|
||||
who may elect to immediately label the issue as accepted in addition to another
|
||||
issue type label. In other cases, the issue may be labeled as "status: gathering feedback"
|
||||
which will often be accompanied by a comment from a moderator asking for further dialog from the community.
|
||||
If an issue is labeled as "status: revisions needed" a moderator has identified a problem with
|
||||
the issue itself and is asking for the submitter himself to update the original post with
|
||||
the requested information. If the original post is not updated in a reasonable amount of time,
|
||||
the issue will be closed as invalid.
|
||||
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.
|
||||
|
||||
The core maintainers group has chosen to make use of the GitHub Stale bot to aid in issue management.
|
||||
The core maintainers group has chosen to make use of GitHub's [Stale bot](https://github.com/apps/stale)
|
||||
to aid in issue management.
|
||||
|
||||
* Issues will be marked as stale after 14 days of no activity.
|
||||
* Then after 7 more days of inactivity, the issue will be closed.
|
||||
* Any issue bearing one of the following labels will be exempt from all Stale bot actions:
|
||||
* Any issue bearing one of the following labels will be exempt from all Stale
|
||||
bot actions:
|
||||
* `status: accepted`
|
||||
* `status: gathering feedback`
|
||||
* `status: blocked`
|
||||
|
||||
It is natural that some new issues get more attention than others. Often this is a metric of an issues's
|
||||
overall usefulness to the project. In other cases in which issues merely get lost in the shuffle,
|
||||
notifications from Stale bot can bring renewed attention to potentially meaningful issues.
|
||||
It is natural that some new issues get more attention than others. Often this
|
||||
is a metric of an issues's overall value to the project. In other cases in
|
||||
which issues merely get lost in the shuffle, notifications from Stale bot can
|
||||
bring renewed attention to potentially meaningful issues.
|
||||
|
||||
## Maintainer Guidance
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
NetBox is an IP address management (IPAM) and data center infrastructure
|
||||
management (DCIM) tool. Initially conceived by the network engineering team at
|
||||
|
||||
@@ -22,14 +22,14 @@ django-filter
|
||||
# https://github.com/django-mptt/django-mptt
|
||||
django-mptt
|
||||
|
||||
# Django integration for RQ (Reqis queuing)
|
||||
# https://github.com/rq/django-rq
|
||||
django-rq
|
||||
|
||||
# Prometheus metrics library for Django
|
||||
# https://github.com/korfuri/django-prometheus
|
||||
django-prometheus
|
||||
|
||||
# Django integration for RQ (Reqis queuing)
|
||||
# https://github.com/rq/django-rq
|
||||
django-rq
|
||||
|
||||
# Abstraction models for rendering and paginating HTML tables
|
||||
# https://github.com/jieter/django-tables2
|
||||
django-tables2
|
||||
@@ -54,9 +54,9 @@ djangorestframework
|
||||
# https://github.com/axnsan12/drf-yasg
|
||||
drf-yasg[validation]
|
||||
|
||||
# Python interface to the graphviz graph rendering utility
|
||||
# https://github.com/xflr6/graphviz
|
||||
graphviz
|
||||
# Platform-agnostic template rendering engine
|
||||
# https://github.com/pallets/jinja
|
||||
Jinja2
|
||||
|
||||
# Simple markup language for rendering HTML
|
||||
# https://github.com/Python-Markdown/markdown
|
||||
@@ -82,3 +82,15 @@ py-gfm
|
||||
# Extensive cryptographic library (fork of pycrypto)
|
||||
# https://github.com/Legrandin/pycryptodome
|
||||
pycryptodome
|
||||
|
||||
# YAML rendering library
|
||||
# https://github.com/yaml/pyyaml
|
||||
PyYAML
|
||||
|
||||
# In-memory key/value store used for caching and queuing
|
||||
# https://github.com/andymccurdy/redis-py
|
||||
redis
|
||||
|
||||
# SVG image rendering (used for rack elevations)
|
||||
# https://github.com/mozman/svgwrite
|
||||
svgwrite
|
||||
|
||||
16
contrib/gunicorn.py
Normal file
16
contrib/gunicorn.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# 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
|
||||
# n is the number of CPU cores present.
|
||||
workers = 5
|
||||
|
||||
# Number of threads per worker process
|
||||
threads = 3
|
||||
|
||||
# Timeout (in seconds) for a request to complete
|
||||
timeout = 120
|
||||
|
||||
# The maximum number of requests a worker can handle before being respawned
|
||||
max_requests = 5000
|
||||
max_requests_jitter = 500
|
||||
22
contrib/netbox-rq.service
Normal file
22
contrib/netbox-rq.service
Normal file
@@ -0,0 +1,22 @@
|
||||
[Unit]
|
||||
Description=NetBox Request Queue Worker
|
||||
Documentation=https://netbox.readthedocs.io/en/stable/
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
|
||||
User=www-data
|
||||
Group=www-data
|
||||
|
||||
WorkingDirectory=/opt/netbox
|
||||
|
||||
ExecStart=/usr/bin/python3 /opt/netbox/netbox/manage.py rqworker
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=30
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
22
contrib/netbox.service
Normal file
22
contrib/netbox.service
Normal file
@@ -0,0 +1,22 @@
|
||||
[Unit]
|
||||
Description=NetBox WSGI Service
|
||||
Documentation=https://netbox.readthedocs.io/en/stable/
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
|
||||
User=www-data
|
||||
Group=www-data
|
||||
PIDFile=/var/tmp/netbox.pid
|
||||
WorkingDirectory=/opt/netbox
|
||||
|
||||
ExecStart=/usr/local/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/netbox --config /opt/netbox/gunicorn.py netbox.wsgi
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=30
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -71,6 +71,18 @@ The checkbox to commit database changes when executing a script is checked by de
|
||||
commit_default = False
|
||||
```
|
||||
|
||||
## Accessing Request Data
|
||||
|
||||
Details of the current HTTP request (the one being made to execute the script) are available as the instance attribute `self.request`. This can be used to infer, for example, the user executing the script and the client IP address:
|
||||
|
||||
```python
|
||||
username = self.request.user.username
|
||||
ip_address = self.request.META.get('HTTP_X_FORWARDED_FOR') or self.request.META.get('REMOTE_ADDR')
|
||||
self.log_info("Running as user {} (IP: {})...".format(username, ip_address))
|
||||
```
|
||||
|
||||
For a complete list of available request parameters, please see the [Django documentation](https://docs.djangoproject.com/en/stable/ref/request-response/).
|
||||
|
||||
## Reading Data from Files
|
||||
|
||||
The Script class provides two convenience methods for reading data from files:
|
||||
|
||||
@@ -8,6 +8,11 @@ NetBox does not have the ability to generate graphs natively, but this feature a
|
||||
* **Source URL:** The source of the image to be embedded. The associated object will be available as a template variable named `obj`.
|
||||
* **Link URL (optional):** A URL to which the graph will be linked. The associated object will be available as a template variable named `obj`.
|
||||
|
||||
Graph names and links can be rendered using the Django or Jinja2 template languages.
|
||||
|
||||
!!! warning
|
||||
Support for the Django templating language will be removed in NetBox v2.8. Jinja2 is recommended.
|
||||
|
||||
## Examples
|
||||
|
||||
You only need to define one graph object for each graph you want to include when viewing an object. For example, if you want to include a graph of traffic through an interface over the past five minutes, your graph source might looks like this:
|
||||
|
||||
65
docs/additional-features/napalm.md
Normal file
65
docs/additional-features/napalm.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# NAPALM
|
||||
|
||||
NetBox supports integration with the [NAPALM automation](https://napalm-automation.net/) library. NAPALM allows NetBox to fetch live data from devices and return it to a requester via its REST API.
|
||||
|
||||
!!! info
|
||||
To enable the integration, the NAPALM library must be installed. See [installation steps](../../installation/2-netbox/#napalm-automation-optional) for more information.
|
||||
|
||||
```
|
||||
GET /api/dcim/devices/1/napalm/?method=get_environment
|
||||
|
||||
{
|
||||
"get_environment": {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
By default, the [`NAPALM_USERNAME`](../../configuration/optional-settings/#napalm_username) and [`NAPALM_PASSWORD`](../../configuration/optional-settings/#napalm_password) are used for NAPALM authentication. They can be overridden for an individual API call through the `X-NAPALM-Username` and `X-NAPALM-Password` headers.
|
||||
|
||||
```
|
||||
$ curl "http://localhost/api/dcim/devices/1/napalm/?method=get_environment" \
|
||||
-H "Authorization: Token f4b378553dacfcfd44c5a0b9ae49b57e29c552b5" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json; indent=4" \
|
||||
-H "X-NAPALM-Username: foo" \
|
||||
-H "X-NAPALM-Password: bar"
|
||||
```
|
||||
|
||||
## Method Support
|
||||
|
||||
The list of supported NAPALM methods depends on the [NAPALM driver](https://napalm.readthedocs.io/en/latest/support/index.html#general-support-matrix) configured for the platform of a device. NetBox only supports [get](https://napalm.readthedocs.io/en/latest/support/index.html#getters-support-matrix) methods.
|
||||
|
||||
## Multiple Methods
|
||||
|
||||
More than one method in an API call can be invoked by adding multiple `method` parameters. For example:
|
||||
|
||||
```
|
||||
GET /api/dcim/devices/1/napalm/?method=get_ntp_servers&method=get_ntp_peers
|
||||
|
||||
{
|
||||
"get_ntp_servers": {
|
||||
...
|
||||
},
|
||||
"get_ntp_peers": {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Optional Arguments
|
||||
|
||||
The behavior of NAPALM drivers can be adjusted according to the [optional arguments](https://napalm.readthedocs.io/en/latest/support/index.html#optional-arguments). NetBox exposes those arguments using headers prefixed with `X-NAPALM-`.
|
||||
|
||||
|
||||
For instance, the SSH port is changed to 2222 in this API call:
|
||||
|
||||
```
|
||||
$ curl "http://localhost/api/dcim/devices/1/napalm/?method=get_environment" \
|
||||
-H "Authorization: Token f4b378553dacfcfd44c5a0b9ae49b57e29c552b5" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json; indent=4" \
|
||||
-H "X-NAPALM-port: 2222"
|
||||
```
|
||||
@@ -1,17 +0,0 @@
|
||||
# Topology Maps
|
||||
|
||||
NetBox can generate simple topology maps from the physical network connections recorded in its database. First, you'll need to create a topology map definition under the admin UI at Extras > Topology Maps.
|
||||
|
||||
Each topology map is associated with a site. A site can have multiple topology maps, which might each illustrate a different aspect of its infrastructure (for example, production versus backend infrastructure).
|
||||
|
||||
To define the scope of a topology map, decide which devices you want to include. The map will only include interface connections with both points terminated on an included device. Specify the devices to include in the **device patterns** field by entering a list of [regular expressions](https://en.wikipedia.org/wiki/Regular_expression) matching device names. For example, if you wanted to include "mgmt-switch1" through "mgmt-switch99", you might use the regex `mgmt-switch\d+`.
|
||||
|
||||
Each line of the **device patterns** field represents a hierarchical layer within the topology map. For example, you might map a traditional network with core, distribution, and access tiers like this:
|
||||
|
||||
```
|
||||
core-switch-[abcd]
|
||||
dist-switch\d
|
||||
access-switch\d+;oob-switch\d+
|
||||
```
|
||||
|
||||
Note that you can combine multiple regexes onto one line using semicolons. The order in which regexes are listed on a line is significant: devices matching the first regex will be rendered first, and subsequent groups will be rendered to the right of those.
|
||||
@@ -293,6 +293,26 @@ Session data is used to track authenticated users when they access NetBox. By de
|
||||
|
||||
---
|
||||
|
||||
## STORAGE_BACKEND
|
||||
|
||||
Default: None (local storage)
|
||||
|
||||
The backend storage engine for handling uploaded files (e.g. image attachments). NetBox supports integration with the [`django-storages`](https://django-storages.readthedocs.io/en/stable/) package, which provides backends for several popular file storage services. If not configured, local filesystem storage will be used.
|
||||
|
||||
The configuration parameters for the specified storage backend are defined under the `STORAGE_CONFIG` setting.
|
||||
|
||||
---
|
||||
|
||||
## STORAGE_CONFIG
|
||||
|
||||
Default: Empty
|
||||
|
||||
A dictionary of configuration parameters for the storage backend configured as `STORAGE_BACKEND`. The specific parameters to be used here are specific to each backend; see the [`django-storages` documentation](https://django-storages.readthedocs.io/en/stable/) for more detail.
|
||||
|
||||
If `STORAGE_BACKEND` is not defined, this setting will be ignored.
|
||||
|
||||
---
|
||||
|
||||
## TIME_ZONE
|
||||
|
||||
Default: UTC
|
||||
@@ -301,14 +321,6 @@ The time zone NetBox will use when dealing with dates and times. It is recommend
|
||||
|
||||
---
|
||||
|
||||
## WEBHOOKS_ENABLED
|
||||
|
||||
Default: False
|
||||
|
||||
Enable this option to run the webhook backend. See the docs section on the webhook backend [here](../../additional-features/webhooks/) for more information on setup and use.
|
||||
|
||||
---
|
||||
|
||||
## Date and Time Formatting
|
||||
|
||||
You may define custom formatting for date and times. For detailed instructions on writing format strings, please see [the Django documentation](https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date).
|
||||
|
||||
@@ -25,7 +25,7 @@ NetBox requires access to a PostgreSQL database service to store data. This serv
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
```python
|
||||
DATABASE = {
|
||||
'NAME': 'netbox', # Database name
|
||||
'USER': 'netbox', # PostgreSQL username
|
||||
@@ -42,40 +42,48 @@ DATABASE = {
|
||||
|
||||
[Redis](https://redis.io/) is an in-memory data store similar to memcached. While Redis has been an optional component of
|
||||
NetBox since the introduction of webhooks in version 2.4, it is required starting in 2.6 to support NetBox's caching
|
||||
functionality (as well as other planned features).
|
||||
functionality (as well as other planned features). In 2.7, the connection settings were broken down into two sections for
|
||||
webhooks and caching, allowing the user to connect to different Redis instances/databases per feature.
|
||||
|
||||
Redis is configured using a configuration setting similar to `DATABASE`:
|
||||
Redis is configured using a configuration setting similar to `DATABASE` and these settings are the same for both of the `webhooks` and `caching` subsections:
|
||||
|
||||
* `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)
|
||||
* `PASSWORD` - Redis password (if set)
|
||||
* `DATABASE` - Numeric database ID for webhooks
|
||||
* `CACHE_DATABASE` - Numeric database ID for caching
|
||||
* `DATABASE` - Numeric database ID
|
||||
* `DEFAULT_TIMEOUT` - Connection timeout in seconds
|
||||
* `SSL` - Use SSL connection to Redis
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
```python
|
||||
REDIS = {
|
||||
'HOST': 'localhost',
|
||||
'PORT': 6379,
|
||||
'PASSWORD': '',
|
||||
'DATABASE': 0,
|
||||
'CACHE_DATABASE': 1,
|
||||
'DEFAULT_TIMEOUT': 300,
|
||||
'SSL': False,
|
||||
'webhooks': {
|
||||
'HOST': 'redis.example.com',
|
||||
'PORT': 1234,
|
||||
'PASSWORD': 'foobar',
|
||||
'DATABASE': 0,
|
||||
'DEFAULT_TIMEOUT': 300,
|
||||
'SSL': False,
|
||||
},
|
||||
'caching': {
|
||||
'HOST': 'localhost',
|
||||
'PORT': 6379,
|
||||
'PASSWORD': '',
|
||||
'DATABASE': 1,
|
||||
'DEFAULT_TIMEOUT': 300,
|
||||
'SSL': False,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
!!! note:
|
||||
If you were using these settings in a prior release with webhooks, the `DATABASE` setting remains the same but
|
||||
an additional `CACHE_DATABASE` setting has been added with a default value of 1 to support the caching backend. The
|
||||
`DATABASE` setting will be renamed in a future release of NetBox to better relay the meaning of the setting.
|
||||
If you are upgrading from a version prior to v2.7, please note that the Redis connection configuration settings have
|
||||
changed. Manual modification to bring the `REDIS` section inline with the above specification is necessary
|
||||
|
||||
!!! warning:
|
||||
It is highly recommended to keep the webhook and cache databases seperate. Using the same database number for both may result in webhook
|
||||
processing data being lost in cache flushing events.
|
||||
It is highly recommended to keep the webhook and cache databases separate. Using the same database number on the
|
||||
same Redis instance for both may result in webhook processing data being lost during cache flushing events.
|
||||
|
||||
---
|
||||
|
||||
|
||||
58
docs/core-functionality/power.md
Normal file
58
docs/core-functionality/power.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Power Panel
|
||||
|
||||
A power panel represents the distribution board where power circuits – and their circuit breakers – terminate on. If you have multiple power panels in your data center, you should model them as such in NetBox to assist you in determining the redundancy of your power allocation.
|
||||
|
||||
# Power Feed
|
||||
|
||||
A power feed identifies the power outlet/drop that goes to a rack and is terminated to a power panel. Power feeds have a supply type (AC/DC), voltage, amperage, and phase type (single/three).
|
||||
|
||||
Power feeds are optionally assigned to a rack. In addition, a power port – and only one – can connect to a power feed; in the context of a PDU, the power feed is analogous to the power outlet that a PDU's power port/inlet connects to.
|
||||
|
||||
!!! info
|
||||
The power usage of a rack is calculated when a power feed (or multiple) is assigned to that rack and connected to a power port.
|
||||
|
||||
# Power Outlet
|
||||
|
||||
Power outlets represent the ports on a PDU that supply power to other devices. Power outlets are downstream-facing towards power ports. A power outlet can be associated with a power port on the same device and a feed leg (i.e. in a case of a three-phase supply). This indicates which power port supplies power to a power outlet.
|
||||
|
||||
# Power Port
|
||||
|
||||
A power port is the inlet of a device where it draws its power. Power ports are upstream-facing towards power outlets. Alternatively, a power port can connect to a power feed – as mentioned in the power feed section – to indicate the power source of a PDU's inlet.
|
||||
|
||||
!!! info
|
||||
If the draw of a power port is left empty, it will be dynamically calculated based on the power outlets associated with that power port. This is usually the case on the power ports of devices that supply power, like a PDU.
|
||||
|
||||
|
||||
# Example
|
||||
|
||||
Below is a simple diagram demonstrating how power is modelled in NetBox.
|
||||
|
||||
!!! note
|
||||
The power feeds are connected to the same power panel for illustrative purposes; usually, you would have such feeds diversely connected to panels to avoid the single point of failure.
|
||||
|
||||
```
|
||||
+---------------+
|
||||
| Power panel 1 |
|
||||
+---------------+
|
||||
| |
|
||||
| |
|
||||
+--------------+ +--------------+
|
||||
| Power feed 1 | | Power feed 2 |
|
||||
+--------------+ +--------------+
|
||||
| |
|
||||
| |
|
||||
| | <-- Power ports
|
||||
+---------+ +---------+
|
||||
| PDU 1 | | PDU 2 |
|
||||
+---------+ +---------+
|
||||
| \ / | <-- Power outlets
|
||||
| \ / |
|
||||
| \ / |
|
||||
| X |
|
||||
| / \ |
|
||||
| / \ |
|
||||
| / \ | <-- Power ports
|
||||
+--------+ +--------+
|
||||
| Server | | Router |
|
||||
+--------+ +--------+
|
||||
```
|
||||
@@ -24,6 +24,20 @@ Each user within NetBox can associate his or her account with an RSA public key.
|
||||
|
||||
User keys may be created by users individually, however they are of no use until they have been activated by a user who already possesses an active user key.
|
||||
|
||||
## Supported Key Format
|
||||
|
||||
Public key formats supported
|
||||
|
||||
- PKCS#1 RSAPublicKey* (PEM header: BEGIN RSA PUBLIC KEY)
|
||||
- X.509 SubjectPublicKeyInfo** (PEM header: BEGIN PUBLIC KEY)
|
||||
- **OpenSSH line format is not supported.**
|
||||
|
||||
Private key formats supported (unencrypted)
|
||||
|
||||
- PKCS#1 RSAPrivateKey** (PEM header: BEGIN RSA PRIVATE KEY)
|
||||
- PKCS#8 PrivateKeyInfo* (PEM header: BEGIN PRIVATE KEY)
|
||||
|
||||
|
||||
## Creating the First User Key
|
||||
|
||||
When NetBox is first installed, it contains no encryption keys. Before it can store secrets, a user (typically the superuser) must create a user key. This can be done by navigating to Profile > User Key.
|
||||
|
||||
@@ -40,6 +40,8 @@ Racks can be arranged into groups. As with sites, how you choose to designate ra
|
||||
|
||||
Each rack group must be assigned to a parent site. Hierarchical recursion of rack groups is not currently supported.
|
||||
|
||||
The name and facility ID of each rack within a group must be unique. (Racks not assigned to the same rack group may have identical names and/or facility IDs.)
|
||||
|
||||
## Rack Roles
|
||||
|
||||
Each rack can optionally be assigned a functional role. For example, you might designate a rack for compute or storage resources, or to house colocated customer devices. Rack roles are fully customizable.
|
||||
|
||||
@@ -69,6 +69,14 @@ If the new field will be included in the object list view, add a column to the m
|
||||
|
||||
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.
|
||||
|
||||
### 11. Adjust API and model tests
|
||||
### 11. Create/extend test cases
|
||||
|
||||
Extend the model and/or API tests to verify that the new field and any accompanying validation logic perform as expected. This is especially important for relational fields.
|
||||
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:
|
||||
|
||||
* API serializer/view tests
|
||||
* Filter tests
|
||||
* Form tests
|
||||
* Model tests
|
||||
* View tests
|
||||
|
||||
Be diligent to ensure all of the relevant test suites are adapted or extended as necessary to test any new functionality.
|
||||
|
||||
@@ -33,6 +33,10 @@ Update the following static libraries to their most recent stable release:
|
||||
* jQuery
|
||||
* jQuery UI
|
||||
|
||||
## Squash Schema Migrations
|
||||
|
||||
Database schema migrations should be squashed for each new minor release. See the [squashing guide](squashing-migrations.md) for the detailed process.
|
||||
|
||||
## Create a new Release Notes Page
|
||||
|
||||
Create a file at `/docs/release-notes/X.Y.md` to establish the release notes for the new release. Add the file to the table of contents within `mkdocs.yml`.
|
||||
|
||||
168
docs/development/squashing-migrations.md
Normal file
168
docs/development/squashing-migrations.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# Squashing Database Schema Migrations
|
||||
|
||||
## What are Squashed Migrations?
|
||||
|
||||
The Django framework on which NetBox is built utilizes [migration files](https://docs.djangoproject.com/en/stable/topics/migrations/) to keep track of changes to the PostgreSQL database schema. Each time a model is altered, the resulting schema change is captured in a migration file, which can then be applied to effect the new schema.
|
||||
|
||||
As changes are made over time, more and more migration files are created. Although not necessarily problematic, it can be beneficial to merge and compress these files occasionally to reduce the total number of migrations that need to be applied upon installation of NetBox. This merging process is called _squashing_ in Django vernacular, and results in two parallel migration paths: individual and squashed.
|
||||
|
||||
Below is an example showing both individual and squashed migration files within an app:
|
||||
|
||||
| Individual | Squashed |
|
||||
|------------|----------|
|
||||
| 0001_initial | 0001_initial_squashed_0004_add_field |
|
||||
| 0002_alter_field | . |
|
||||
| 0003_remove_field | . |
|
||||
| 0004_add_field | . |
|
||||
| 0005_another_field | 0005_another_field |
|
||||
|
||||
In the example above, a new installation can leverage the squashed migrations to apply only two migrations:
|
||||
|
||||
* `0001_initial_squashed_0004_add_field`
|
||||
* `0005_another_field`
|
||||
|
||||
This is because the squash file contains all of the operations performed by files `0001` through `0004`.
|
||||
|
||||
However, an existing installation that has already applied some of the individual migrations contained within the squash file must continue applying individual migrations. For instance, an installation which currently has up to `0002_alter_field` applied must apply the following migrations to become current:
|
||||
|
||||
* `0003_remove_field`
|
||||
* `0004_add_field`
|
||||
* `0005_another_field`
|
||||
|
||||
Squashed migrations are opportunistic: They are used only if applicable to the current environment. Django will fall back to using individual migrations if the squashed migrations do not agree with the current database schema at any point.
|
||||
|
||||
## Squashing Migrations
|
||||
|
||||
During every minor (i.e. 2.x) release, migrations should be squashed to help simplify the migration process for new installations. The process below describes how to squash migrations efficiently and with minimal room for error.
|
||||
|
||||
### 1. Create a New Branch
|
||||
|
||||
Create a new branch off of the `develop-2.x` branch. (Migrations should be squashed _only_ in preparation for a new minor release.)
|
||||
|
||||
```
|
||||
git checkout -B squash-migrations
|
||||
```
|
||||
|
||||
### 2. Delete Existing Squash Files
|
||||
|
||||
Delete the most recent squash file within each NetBox app. This allows us to extend squash files where the opportunity exists. For example, we might be able to replace `0005_to_0008` with `0005_to_0011`.
|
||||
|
||||
### 3. Generate the Current Migration Plan
|
||||
|
||||
Use Django's `showmigrations` utility to display the order in which all migrations would be applied for a new installation.
|
||||
|
||||
```
|
||||
manage.py showmigrations --plan
|
||||
```
|
||||
|
||||
From the resulting output, delete all lines which reference an external migration. Any migrations imposed by Django itself on an external package are not relevant.
|
||||
|
||||
### 4. Create Squash Files
|
||||
|
||||
Begin iterating through the migration plan, looking for successive sets of migrations within an app. These are candidates for squashing. For example:
|
||||
|
||||
```
|
||||
[X] extras.0014_configcontexts
|
||||
[X] extras.0015_remove_useraction
|
||||
[X] extras.0016_exporttemplate_add_cable
|
||||
[X] extras.0017_exporttemplate_mime_type_length
|
||||
[ ] extras.0018_exporttemplate_add_jinja2
|
||||
[ ] extras.0019_tag_taggeditem
|
||||
[X] dcim.0062_interface_mtu
|
||||
[X] dcim.0063_device_local_context_data
|
||||
[X] dcim.0064_remove_platform_rpc_client
|
||||
[ ] dcim.0065_front_rear_ports
|
||||
[X] circuits.0001_initial_squashed_0010_circuit_status
|
||||
[ ] dcim.0066_cables
|
||||
...
|
||||
```
|
||||
|
||||
Migrations `0014` through `0019` in `extras` can be squashed, as can migrations `0062` through `0065` in `dcim`. Migration `0066` cannot be included in the same squash file, because the `circuits` migration must be applied before it. (Note that whether or not each migration is currently applied to the database does not matter.)
|
||||
|
||||
Squash files are created using Django's `squashmigrations` utility:
|
||||
|
||||
```
|
||||
manage.py squashmigrations <app> <start> <end>
|
||||
```
|
||||
|
||||
For example, our first step in the example would be to run `manage.py squashmigrations extras 0014 0019`.
|
||||
|
||||
!!! note
|
||||
Specifying a migration file's numeric index is enough to uniquely identify it within an app. There is no need to specify the full filename.
|
||||
|
||||
This will create a new squash file within the app's `migrations` directory, named as a concatenation of its beginning and ending migration. Some manual editing is necessary for each new squash file for housekeeping purposes:
|
||||
|
||||
* Remove the "automatically generated" comment at top (to indicate that a human has reviewed the file).
|
||||
* Reorder `import` statements as necessary per PEP8.
|
||||
* It may be necessary to copy over custom functions from the original migration files (this will be indicated by a comment near the top of the squash file). It is safe to remove any functions that exist solely to accomodate reverse migrations (which we no longer support).
|
||||
|
||||
Repeat this process for each candidate set of migrations until you reach the end of the migration plan.
|
||||
|
||||
### 5. Check for Missing Migrations
|
||||
|
||||
If everything went well, at this point we should have a completed squashed path. Perform a dry run to check for any missing migrations:
|
||||
|
||||
```
|
||||
manage.py migrate --dry-run
|
||||
```
|
||||
|
||||
### 5. Run Migrations
|
||||
|
||||
Next, we'll apply the entire migration path to an empty database. Begin by dropping and creating your development database.
|
||||
|
||||
!!! warning
|
||||
Obviously, first back up any data you don't want to lose.
|
||||
|
||||
```
|
||||
sudo -u postgres psql -c 'drop database netbox'
|
||||
sudo -u postgres psql -c 'create database netbox'
|
||||
```
|
||||
|
||||
Apply the migrations with the `migrate` management command. It is not necessary to specify a particular migration path; Django will detect and use the squashed migrations automatically. You can verify the exact migrations being applied by enabling verboes output with `-v 2`.
|
||||
|
||||
```
|
||||
manage.py migrate -v 2
|
||||
```
|
||||
|
||||
### 6. Commit the New Migrations
|
||||
|
||||
If everything is successful to this point, commit your changes to the `squash-migrations` branch.
|
||||
|
||||
### 7. Validate Resulting Schema
|
||||
|
||||
To ensure our new squashed migrations do not result in a deviation from the original schema, we'll compare the two. With the new migration file safely commit, check out the `develop-2.x` branch, which still contains only the individual migrations.
|
||||
|
||||
```
|
||||
git checkout develop-2.x
|
||||
```
|
||||
|
||||
Temporarily install the [django-extensions](https://django-extensions.readthedocs.io/) package, which provides the `sqldiff utility`:
|
||||
|
||||
```
|
||||
pip install django-extensions
|
||||
```
|
||||
|
||||
Also add `django_extensions` to `INSTALLED_APPS` in `netbox/netbox/settings.py`.
|
||||
|
||||
At this point, our database schema has been defined by using the squashed migrations. We can run `sqldiff` to see if it differs any from what the current (non-squashed) migrations would generate. `sqldiff` accepts a list of apps against which to run:
|
||||
|
||||
```
|
||||
manage.py sqldiff circuits dcim extras ipam secrets tenancy users virtualization
|
||||
```
|
||||
|
||||
It is safe to ignore errors indicating an "unknown database type" for the following fields:
|
||||
|
||||
* `dcim_interface.mac_address`
|
||||
* `ipam_aggregate.prefix`
|
||||
* `ipam_prefix.prefix`
|
||||
|
||||
It is also safe to ignore the message "Table missing: extras_script".
|
||||
|
||||
Resolve any differences by correcting migration files in the `squash-migrations` branch.
|
||||
|
||||
!!! warning
|
||||
Don't forget to remove `django_extension` from `INSTALLED_APPS` before committing your changes.
|
||||
|
||||
### 8. Merge the Squashed Migrations
|
||||
|
||||
Once all squashed migrations have been validated and all tests run successfully, merge the `squash-migrations` branch into `develop-2.x`. This completes the squashing process.
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# What is NetBox?
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ NetBox requires a PostgreSQL database to store data. This can be hosted locally
|
||||
The installation instructions provided here have been tested to work on Ubuntu 18.04 and CentOS 7.5. 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.
|
||||
|
||||
!!! warning
|
||||
NetBox v2.2 and later requires PostgreSQL 9.4 or higher.
|
||||
NetBox requires PostgreSQL 9.4 or higher.
|
||||
|
||||
# Installation
|
||||
|
||||
|
||||
@@ -5,16 +5,16 @@ This section of the documentation discusses installing and configuring the NetBo
|
||||
**Ubuntu**
|
||||
|
||||
```no-highlight
|
||||
# apt-get install -y python3 python3-pip python3-dev build-essential libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev libssl-dev redis-server zlib1g-dev
|
||||
# apt-get install -y python3 python3-pip python3-dev build-essential libxml2-dev libxslt1-dev libffi-dev libpq-dev libssl-dev redis-server zlib1g-dev
|
||||
```
|
||||
|
||||
**CentOS**
|
||||
|
||||
```no-highlight
|
||||
# yum install -y epel-release
|
||||
# yum install -y gcc python36 python36-devel python36-setuptools libxml2-devel libxslt-devel libffi-devel graphviz openssl-devel redhat-rpm-config redis
|
||||
# yum install -y gcc python36 python36-devel python36-setuptools libxml2-devel libxslt-devel libffi-devel openssl-devel redhat-rpm-config redis
|
||||
# easy_install-3.6 pip
|
||||
# ln -s /usr/bin/python36 /usr/bin/python3
|
||||
# ln -s /usr/bin/python3.6 /usr/bin/python3
|
||||
```
|
||||
|
||||
You may opt to install NetBox either from a numbered release or by cloning the master branch of its repository on GitHub.
|
||||
@@ -90,6 +90,14 @@ NetBox supports integration with the [NAPALM automation](https://napalm-automati
|
||||
# pip3 install napalm
|
||||
```
|
||||
|
||||
## Remote File Storage (Optional)
|
||||
|
||||
By default, NetBox will use the local filesystem to storage uploaded files. To use a remote filesystem, install the [`django-storages`](https://django-storages.readthedocs.io/en/stable/) library and configure your [desired backend](../../configuration/optional-settings/#storage_backend) in `configuration.py`.
|
||||
|
||||
```no-highlight
|
||||
# pip3 install django-storages
|
||||
```
|
||||
|
||||
# Configuration
|
||||
|
||||
Move into the NetBox configuration directory and make a copy of `configuration.example.py` named `configuration.py`.
|
||||
@@ -139,13 +147,22 @@ Redis is a in-memory key-value store required as part of the NetBox installation
|
||||
|
||||
```python
|
||||
REDIS = {
|
||||
'HOST': 'localhost',
|
||||
'PORT': 6379,
|
||||
'PASSWORD': '',
|
||||
'DATABASE': 0,
|
||||
'CACHE_DATABASE': 1,
|
||||
'DEFAULT_TIMEOUT': 300,
|
||||
'SSL': False,
|
||||
'webhooks': {
|
||||
'HOST': 'redis.example.com',
|
||||
'PORT': 1234,
|
||||
'PASSWORD': 'foobar',
|
||||
'DATABASE': 0,
|
||||
'DEFAULT_TIMEOUT': 300,
|
||||
'SSL': False,
|
||||
},
|
||||
'caching': {
|
||||
'HOST': 'localhost',
|
||||
'PORT': 6379,
|
||||
'PASSWORD': '',
|
||||
'DATABASE': 1,
|
||||
'DEFAULT_TIMEOUT': 300,
|
||||
'SSL': False,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -195,27 +212,7 @@ Superuser created successfully.
|
||||
```no-highlight
|
||||
# python3 manage.py collectstatic --no-input
|
||||
|
||||
You have requested to collect static files at the destination
|
||||
location as specified in your settings:
|
||||
|
||||
/opt/netbox/netbox/static
|
||||
|
||||
This will overwrite existing files!
|
||||
Are you sure you want to do this?
|
||||
|
||||
Type 'yes' to continue, or 'no' to cancel: yes
|
||||
```
|
||||
|
||||
# Load Initial Data (Optional)
|
||||
|
||||
NetBox ships with some initial data to help you get started: RIR definitions, common devices roles, etc. You can delete any seed data that you don't want to keep.
|
||||
|
||||
!!! note
|
||||
This step is optional. It's perfectly fine to start using NetBox without using this initial data if you'd rather create everything from scratch.
|
||||
|
||||
```no-highlight
|
||||
# python3 manage.py loaddata initial_data
|
||||
Installed 43 object(s) from 4 fixture(s)
|
||||
959 static files copied to '/opt/netbox/netbox/static'.
|
||||
```
|
||||
|
||||
# Test the Application
|
||||
@@ -237,3 +234,11 @@ Next, connect to the name or IP of the server (as defined in `ALLOWED_HOSTS`) on
|
||||
|
||||
!!! warning
|
||||
If the test service does not run, or you cannot reach the NetBox home page, something has gone wrong. Do not proceed with the rest of this guide until the installation has been corrected.
|
||||
|
||||
Note that the initial UI will be locked down for non-authenticated users.
|
||||
|
||||

|
||||
|
||||
After logging in as the superuser you created earlier, all areas of the UI will be available.
|
||||
|
||||

|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
We'll set up a simple WSGI front end using [gunicorn](http://gunicorn.org/) for the purposes of this guide. For web servers, we provide example configurations for both [nginx](https://www.nginx.com/resources/wiki/) and [Apache](http://httpd.apache.org/docs/2.4). (You are of course free to use whichever combination of HTTP and WSGI services you'd like.) We'll also use [supervisord](http://supervisord.org/) to enable service persistence.
|
||||
We'll set up a simple WSGI front end using [gunicorn](http://gunicorn.org/) for the purposes of this guide. For web servers, we provide example configurations for both [nginx](https://www.nginx.com/resources/wiki/) and [Apache](http://httpd.apache.org/docs/2.4). (You are of course free to use whichever combination of HTTP and WSGI services you'd like.) We'll use systemd to enable service persistence.
|
||||
|
||||
!!! info
|
||||
For the sake of brevity, only Ubuntu 18.04 instructions are provided here, but this sort of web server and WSGI configuration is not unique to NetBox. Please consult your distribution's documentation for assistance if needed.
|
||||
@@ -107,47 +107,53 @@ Install gunicorn:
|
||||
# pip3 install gunicorn
|
||||
```
|
||||
|
||||
Save the following configuration in the root netbox installation path as `gunicorn_config.py` (e.g. `/opt/netbox/gunicorn_config.py` per our example installation). Be sure to verify the location of the gunicorn executable on your server (e.g. `which gunicorn`) and to update the `pythonpath` variable if needed. If using CentOS/RHEL, change the username from `www-data` to `nginx` or `apache`. More info on `max_requests` can be found in the [gunicorn docs](https://docs.gunicorn.org/en/stable/settings.html#max-requests).
|
||||
Copy `contrib/gunicorn.py` to `/opt/netbox/gunicorn.py`. We make a copy of this file to ensure that any changes to it do not get overwritten by a future upgrade.
|
||||
|
||||
```no-highlight
|
||||
command = '/usr/bin/gunicorn'
|
||||
pythonpath = '/opt/netbox/netbox'
|
||||
bind = '127.0.0.1:8001'
|
||||
workers = 3
|
||||
user = 'www-data'
|
||||
max_requests = 5000
|
||||
max_requests_jitter = 500
|
||||
# cp contrib/gunicorn.py /opt/netbox/gunicorn.py
|
||||
```
|
||||
|
||||
# supervisord Installation
|
||||
You may wish to edit this file to change the bound IP address or port number, or to make performance-related adjustments.
|
||||
|
||||
Install supervisor:
|
||||
# systemd configuration
|
||||
|
||||
We'll use systemd to control the daemonization of NetBox services. First, copy `contrib/netbox.service` and `contrib/netbox-rq.service` to the `/etc/systemd/system/` directory:
|
||||
|
||||
```no-highlight
|
||||
# apt-get install -y supervisor
|
||||
# cp contrib/*.service /etc/systemd/system/
|
||||
```
|
||||
|
||||
Save the following as `/etc/supervisor/conf.d/netbox.conf`. Update the `command` and `directory` paths as needed. If using CentOS/RHEL, change the username from `www-data` to `nginx` or `apache`.
|
||||
!!! note
|
||||
These service files assume that gunicorn is installed at `/usr/local/bin/gunicorn`. If the output of `which gunicorn` indicates a different path, you'll need to correct the `ExecStart` path in both files.
|
||||
|
||||
Then, start the `netbox` and `netbox-rq` services and enable them to initiate at boot time:
|
||||
|
||||
```no-highlight
|
||||
[program:netbox]
|
||||
command = gunicorn -c /opt/netbox/gunicorn_config.py netbox.wsgi
|
||||
directory = /opt/netbox/netbox/
|
||||
user = www-data
|
||||
|
||||
[program:netbox-rqworker]
|
||||
command = python3 /opt/netbox/netbox/manage.py rqworker
|
||||
directory = /opt/netbox/netbox/
|
||||
user = www-data
|
||||
# systemctl daemon-reload
|
||||
# systemctl start netbox.service
|
||||
# systemctl start netbox-rq.service
|
||||
# systemctl enable netbox.service
|
||||
# systemctl enable netbox-rq.service
|
||||
```
|
||||
|
||||
Then, restart the supervisor service to detect and run the gunicorn service:
|
||||
You can use the command `systemctl status netbox` to verify that the WSGI service is running:
|
||||
|
||||
```no-highlight
|
||||
# service supervisor restart
|
||||
```
|
||||
# systemctl status netbox.service
|
||||
● netbox.service - NetBox WSGI Service
|
||||
Loaded: loaded (/etc/systemd/system/netbox.service; enabled; vendor preset: enabled)
|
||||
Active: active (running) since Thu 2019-12-12 19:23:40 UTC; 25s ago
|
||||
Docs: https://netbox.readthedocs.io/en/stable/
|
||||
Main PID: 11993 (gunicorn)
|
||||
Tasks: 6 (limit: 2362)
|
||||
CGroup: /system.slice/netbox.service
|
||||
├─11993 /usr/bin/python3 /usr/local/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/...
|
||||
├─12015 /usr/bin/python3 /usr/local/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/...
|
||||
├─12016 /usr/bin/python3 /usr/local/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/...
|
||||
...
|
||||
```
|
||||
|
||||
At this point, you should be able to connect to the nginx HTTP service at the server name or IP address you provided. If you are unable to connect, check that the nginx service is running and properly configured. If you receive a 502 (bad gateway) error, this indicates that gunicorn is misconfigured or not running.
|
||||
At this point, you should be able to connect to the HTTP service at the server name or IP address you provided. If you are unable to connect, check that the nginx service is running and properly configured. If you receive a 502 (bad gateway) error, this indicates that gunicorn is misconfigured or not running.
|
||||
|
||||
!!! info
|
||||
Please keep in mind that the configurations provided here are bare minimums required to get NetBox up and running. You will almost certainly want to make some changes to better suit your production environment.
|
||||
Please keep in mind that the configurations provided here are bare minimums required to get NetBox up and running. You may want to make adjustments to better suit your production environment.
|
||||
|
||||
@@ -80,6 +80,7 @@ AUTH_LDAP_USER_ATTR_MAP = {
|
||||
```
|
||||
|
||||
# User Groups for Permissions
|
||||
|
||||
!!! info
|
||||
When using Microsoft Active Directory, support for nested groups can be activated by using `NestedGroupOfNamesType()` instead of `GroupOfNamesType()` for `AUTH_LDAP_GROUP_TYPE`. You will also need to modify the import line to use `NestedGroupOfNamesType` instead of `GroupOfNamesType` .
|
||||
|
||||
@@ -117,6 +118,9 @@ AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600
|
||||
* `is_staff` - Users mapped to this group are enabled for access to the administration tools; this is the equivalent of checking the "staff status" box on a manually created user. This doesn't grant any specific permissions.
|
||||
* `is_superuser` - Users mapped to this group will be granted superuser status. Superusers are implicitly granted all permissions.
|
||||
|
||||
!!! warning
|
||||
Authentication will fail if the groups (the distinguished names) do not exist in the LDAP directory.
|
||||
|
||||
# Troubleshooting LDAP
|
||||
|
||||
`supervisorctl restart netbox` restarts the Netbox service, and initiates any changes made to `ldap_config.py`. If there are syntax errors present, the NetBox process will not spawn an instance, and errors should be logged to `/var/log/supervisor/`.
|
||||
|
||||
@@ -12,3 +12,5 @@ The following sections detail how to set up a new instance of NetBox:
|
||||
If you are upgrading from an existing installation, please consult the [upgrading guide](upgrading.md).
|
||||
|
||||
NetBox v2.5 and later requires Python 3.5 or higher. Please see the instructions for [migrating to Python 3](migrating-to-python3.md) if you are still using Python 2.
|
||||
|
||||
Netbox v2.5.9 and later moved to using systemd instead of supervisord. Please see the instructions for [migrating to systemd](migrating-to-systemd.md) if you are still using supervisord.
|
||||
|
||||
100
docs/installation/migrating-to-systemd.md
Normal file
100
docs/installation/migrating-to-systemd.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Migration
|
||||
|
||||
Migration is not required, as supervisord will still continue to function.
|
||||
|
||||
## Ubuntu
|
||||
|
||||
### Remove supervisord:
|
||||
|
||||
```no-highlight
|
||||
# apt-get remove -y supervisord
|
||||
```
|
||||
|
||||
### systemd configuration:
|
||||
|
||||
Copy or link contrib/netbox.service and contrib/netbox-rq.service to /etc/systemd/system/netbox.service and /etc/systemd/system/netbox-rq.service
|
||||
|
||||
```no-highlight
|
||||
# cp contrib/netbox.service /etc/systemd/system/netbox.service
|
||||
# cp contrib/netbox-rq.service /etc/systemd/system/netbox-rq.service
|
||||
```
|
||||
|
||||
Edit /etc/systemd/system/netbox.service and /etc/systemd/system/netbox-rq.service. Be sure to verify the location of the gunicorn executable on your server (e.g. `which gunicorn`). If using CentOS/RHEL. Change the username from `www-data` to `nginx` or `apache`:
|
||||
|
||||
```no-highlight
|
||||
/usr/local/bin/gunicorn --pid ${PidPath} --pythonpath ${WorkingDirectory}/netbox --config ${ConfigPath} netbox.wsgi
|
||||
```
|
||||
|
||||
```no-highlight
|
||||
User=www-data
|
||||
Group=www-data
|
||||
```
|
||||
|
||||
Copy contrib/netbox.env to /etc/sysconfig/netbox.env
|
||||
|
||||
```no-highlight
|
||||
# cp contrib/netbox.env /etc/sysconfig/netbox.env
|
||||
```
|
||||
|
||||
Edit /etc/sysconfig/netbox.env and change the settings as required. Update the `WorkingDirectory` variable if needed.
|
||||
|
||||
```no-highlight
|
||||
# Name is the Process Name
|
||||
#
|
||||
Name = 'Netbox'
|
||||
|
||||
# ConfigPath is the path to the gunicorn config file.
|
||||
#
|
||||
ConfigPath=/opt/netbox/gunicorn.conf
|
||||
|
||||
# WorkingDirectory is the Working Directory for Netbox.
|
||||
#
|
||||
WorkingDirectory=/opt/netbox/
|
||||
|
||||
# PidPath is the path to the pid for the netbox WSGI
|
||||
#
|
||||
PidPath=/var/run/netbox.pid
|
||||
```
|
||||
|
||||
Copy contrib/gunicorn.conf to gunicorn.conf
|
||||
|
||||
```no-highlight
|
||||
# cp contrib/gunicorn.conf to gunicorn.conf
|
||||
```
|
||||
|
||||
Edit gunicorn.conf and change the settings as required.
|
||||
|
||||
```
|
||||
# Bind is the ip and port that the Netbox WSGI should bind to
|
||||
#
|
||||
bind='127.0.0.1:8001'
|
||||
|
||||
# Workers is the number of workers that GUnicorn should spawn.
|
||||
# Workers should be: cores * 2 + 1. So if you have 8 cores, it would be 17.
|
||||
#
|
||||
workers=3
|
||||
|
||||
# Threads
|
||||
# The number of threads for handling requests
|
||||
#
|
||||
threads=3
|
||||
|
||||
# Timeout is the timeout between gunicorn receiving a request and returning a response (or failing with a 500 error)
|
||||
#
|
||||
timeout=120
|
||||
|
||||
# ErrorLog
|
||||
# ErrorLog is the logfile for the ErrorLog
|
||||
#
|
||||
errorlog='/opt/netbox/netbox.log'
|
||||
```
|
||||
|
||||
Finally, start the `netbox` and `netbox-rq` services and enable them to initiate at boot time:
|
||||
|
||||
```no-highlight
|
||||
# systemctl daemon-reload
|
||||
# systemctl start netbox.service
|
||||
# systemctl start netbox-rq.service
|
||||
# systemctl enable netbox.service
|
||||
# systemctl enable netbox-rq.service
|
||||
```
|
||||
@@ -84,14 +84,12 @@ This script:
|
||||
|
||||
# Restart the WSGI Service
|
||||
|
||||
Finally, restart the WSGI service to run the new code. If you followed this guide for the initial installation, this is done using `supervisorctl`:
|
||||
Finally, restart the WSGI services to run the new code. If you followed this guide for the initial installation, this is done using `systemctl:
|
||||
|
||||
```no-highlight
|
||||
# sudo supervisorctl restart netbox
|
||||
# sudo systemctl restart netbox
|
||||
# sudo systemctl restart netbox-rqworker
|
||||
```
|
||||
|
||||
If using webhooks, also restart the Redis worker:
|
||||
|
||||
```no-highlight
|
||||
# sudo supervisorctl restart netbox-rqworker
|
||||
```
|
||||
!!! note
|
||||
It's possible you are still using supervisord instead of the linux native systemd. If you are still using supervisord you can restart the services by either restarting supervisord or by using supervisorctl to restart netbox.
|
||||
|
||||
BIN
docs/media/installation/netbox_ui_admin.png
Normal file
BIN
docs/media/installation/netbox_ui_admin.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
BIN
docs/media/installation/netbox_ui_guest.png
Normal file
BIN
docs/media/installation/netbox_ui_guest.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
21
docs/netbox_logo.svg
Normal file
21
docs/netbox_logo.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1100 320">
|
||||
<g fill="#9cc8f8" stroke="#9cc8f8">
|
||||
<circle cx="37" cy="284" r="23"/>
|
||||
<circle cx="101" cy="37" r="23"/>
|
||||
<circle cx="101" cy="220" r="23"/>
|
||||
<circle cx="284" cy="220" r="23"/>
|
||||
<rect x="93" y="37" width="16" height="180"/>
|
||||
<rect x="101" y="212" width="180" height="16"/>
|
||||
<rect x="93" y="212" width="16" height="90" transform="rotate(45 101 220)"/>
|
||||
</g>
|
||||
<g fill="#1685fc" stroke="#1685fc">
|
||||
<circle cx="284" cy="37" r="23"/>
|
||||
<circle cx="37" cy="101" r="23"/>
|
||||
<circle cx="220" cy="101" r="23"/>
|
||||
<circle cx="220" cy="284" r="23"/>
|
||||
<rect x="37" y="93" width="180" height="16"/>
|
||||
<rect x="212" y="101" width="16" height="180"/>
|
||||
<rect x="212" y="93" width="16" height="90" transform="rotate(225 220 101)"/>
|
||||
<path transform="translate(380, 8)" d="M13.60 200L13.60 104L36.40 104L36.40 119.40L36.80 119.40Q40.20 112.20 47.20 106.90Q54.20 101.60 66.20 101.60L66.20 101.60Q75.80 101.60 82.50 104.80Q89.20 108 93.40 113.20Q97.60 118.40 99.40 125.20Q101.20 132 101.20 139.40L101.20 139.40L101.20 200L77.20 200L77.20 151.40Q77.20 147.40 76.80 142.50Q76.40 137.60 74.70 133.30Q73 129 69.40 126.10Q65.80 123.20 59.60 123.20L59.60 123.20Q53.60 123.20 49.50 125.20Q45.40 127.20 42.70 130.60Q40 134 38.80 138.40Q37.60 142.80 37.60 147.60L37.60 147.60L37.60 200L13.60 200ZM224.80 160.40L151.60 160.40Q152.80 171.20 160 177.20Q167.20 183.20 177.40 183.20L177.40 183.20Q186.40 183.20 192.50 179.50Q198.60 175.80 203.20 170.20L203.20 170.20L220.40 183.20Q212 193.60 201.60 198Q191.20 202.40 179.80 202.40L179.80 202.40Q169 202.40 159.40 198.80Q149.80 195.20 142.80 188.60Q135.80 182 131.70 172.70Q127.60 163.40 127.60 152L127.60 152Q127.60 140.60 131.70 131.30Q135.80 122 142.80 115.40Q149.80 108.80 159.40 105.20Q169 101.60 179.80 101.60L179.80 101.60Q189.80 101.60 198.10 105.10Q206.40 108.60 212.30 115.20Q218.20 121.80 221.50 131.50Q224.80 141.20 224.80 153.80L224.80 153.80L224.80 160.40ZM151.60 142.40L200.80 142.40Q200.60 131.80 194.20 125.70Q187.80 119.60 176.40 119.60L176.40 119.60Q165.60 119.60 159.30 125.80Q153 132 151.60 142.40L151.60 142.40ZM259.80 124.40L240.00 124.40L240.00 104L259.80 104L259.80 76.20L283.80 76.20L283.80 104L310.20 104L310.20 124.40L283.80 124.40L283.80 166.40Q283.80 173.60 286.50 177.80Q289.20 182 297.20 182L297.20 182Q300.40 182 304.20 181.30Q308 180.60 310.20 179L310.20 179L310.20 199.20Q306.40 201 300.90 201.70Q295.40 202.40 291.20 202.40L291.20 202.40Q281.60 202.40 275.50 200.30Q269.40 198.20 265.90 193.90Q262.40 189.60 261.10 183.20Q259.80 176.80 259.80 168.40L259.80 168.40L259.80 124.40ZM333.20 200L333.20 48.80L357.20 48.80L357.20 116.20L357.80 116.20Q359.60 113.80 362.40 111.30Q365.20 108.80 369.20 106.60Q373.20 104.40 378.40 103Q383.60 101.60 390.40 101.60L390.40 101.60Q400.60 101.60 409.20 105.50Q417.80 109.40 423.90 116.20Q430 123 433.40 132.20Q436.80 141.40 436.80 152L436.80 152Q436.80 162.60 433.60 171.80Q430.40 181 424.20 187.80Q418 194.60 409.20 198.50Q400.40 202.40 389.40 202.40L389.40 202.40Q379.20 202.40 370.40 198.40Q361.60 194.40 356.40 185.60L356.40 185.60L356 185.60L356 200L333.20 200ZM412.80 152L412.80 152Q412.80 146.40 410.90 141.20Q409 136 405.30 132Q401.60 128 396.40 125.60Q391.20 123.20 384.60 123.20L384.60 123.20Q378 123.20 372.80 125.60Q367.60 128 363.90 132Q360.20 136 358.30 141.20Q356.40 146.40 356.40 152L356.40 152Q356.40 157.60 358.30 162.80Q360.20 168 363.90 172Q367.60 176 372.80 178.40Q378 180.80 384.60 180.80L384.60 180.80Q391.20 180.80 396.40 178.40Q401.60 176 405.30 172Q409 168 410.90 162.80Q412.80 157.60 412.80 152ZM458.40 152L458.40 152Q458.40 140.60 462.50 131.30Q466.60 122 473.60 115.40Q480.60 108.80 490.20 105.20Q499.80 101.60 510.60 101.60L510.60 101.60Q521.40 101.60 531 105.20Q540.60 108.80 547.60 115.40Q554.60 122 558.70 131.30Q562.80 140.60 562.80 152L562.80 152Q562.80 163.40 558.70 172.70Q554.60 182 547.60 188.60Q540.60 195.20 531 198.80Q521.40 202.40 510.60 202.40L510.60 202.40Q499.80 202.40 490.20 198.80Q480.60 195.20 473.60 188.60Q466.60 182 462.50 172.70Q458.40 163.40 458.40 152ZM482.40 152L482.40 152Q482.40 157.60 484.30 162.80Q486.20 168 489.90 172Q493.60 176 498.80 178.40Q504 180.80 510.60 180.80L510.60 180.80Q517.20 180.80 522.40 178.40Q527.60 176 531.30 172Q535 168 536.90 162.80Q538.80 157.60 538.80 152L538.80 152Q538.80 146.40 536.90 141.20Q535 136 531.30 132Q527.60 128 522.40 125.60Q517.20 123.20 510.60 123.20L510.60 123.20Q504 123.20 498.80 125.60Q493.60 128 489.90 132Q486.20 136 484.30 141.20Q482.40 146.40 482.40 152ZM575.40 200L614 148.40L580.80 104L610 104L629.20 132.80L650 104L677.40 104L644.60 148.40L683.20 200L654 200L629 165.60L603.80 200L575.40 200Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.6 KiB |
@@ -1 +1 @@
|
||||
version-2.6.md
|
||||
version-2.7.md
|
||||
@@ -1,3 +1,75 @@
|
||||
# v2.6.12 (2020-01-13)
|
||||
|
||||
## Enhancements
|
||||
|
||||
* [#1982](https://github.com/netbox-community/netbox/issues/1982) - Improved NAPALM method documentation in Swagger (OpenAPI)
|
||||
* [#2050](https://github.com/netbox-community/netbox/issues/2050) - Preview image attachments when hovering over the link
|
||||
* [#2113](https://github.com/netbox-community/netbox/issues/2113) - Allow NAPALM driver settings to be changed with request headers
|
||||
* [#2598](https://github.com/netbox-community/netbox/issues/2598) - Toggle the display of child prefixes/IP addresses
|
||||
* [#3009](https://github.com/netbox-community/netbox/issues/3009) - Search by description when assigning IP address to interfaces
|
||||
* [#3021](https://github.com/netbox-community/netbox/issues/3021) - Add `tenant` filter field for cables
|
||||
* [#3090](https://github.com/netbox-community/netbox/issues/3090) - Enable filtering of interfaces by name on the device view
|
||||
* [#3187](https://github.com/netbox-community/netbox/issues/3187) - Add rack selection field to rack elevations view
|
||||
* [#3393](https://github.com/netbox-community/netbox/issues/3393) - Paginate assigned circuits at the provider details view
|
||||
* [#3440](https://github.com/netbox-community/netbox/issues/3440) - Add total path length to cable trace
|
||||
* [#3491](https://github.com/netbox-community/netbox/issues/3491) - Include content of response on webhook error
|
||||
* [#3623](https://github.com/netbox-community/netbox/issues/3623) - Enable word expansion during interface creation
|
||||
* [#3668](https://github.com/netbox-community/netbox/issues/3668) - Enable searching by DNS name when assigning IP address
|
||||
* [#3851](https://github.com/netbox-community/netbox/issues/3851) - Allow passing initial data to custom script forms
|
||||
* [#3891](https://github.com/netbox-community/netbox/issues/3891) - Add `local_context_data` filter for virtual machines
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* [#3589](https://github.com/netbox-community/netbox/issues/3589) - Fix validation on tagged VLANs of an interface
|
||||
* [#3849](https://github.com/netbox-community/netbox/issues/3849) - Fix ordering of models when dumping data to JSON
|
||||
* [#3853](https://github.com/netbox-community/netbox/issues/3853) - Fix device role link on config context view
|
||||
* [#3856](https://github.com/netbox-community/netbox/issues/3856) - Allow filtering VM interfaces by multiple MAC addresses
|
||||
* [#3857](https://github.com/netbox-community/netbox/issues/3857) - Fix rendering of grouped custom links
|
||||
* [#3862](https://github.com/netbox-community/netbox/issues/3862) - Allow filtering device components by multiple device names
|
||||
* [#3864](https://github.com/netbox-community/netbox/issues/3864) - Disallow /0 masks for prefixes and IP addresses
|
||||
* [#3872](https://github.com/netbox-community/netbox/issues/3872) - Paginate related IPs on the IP address view
|
||||
* [#3876](https://github.com/netbox-community/netbox/issues/3876) - Fix minimum/maximum value rendering for site ASN field
|
||||
* [#3882](https://github.com/netbox-community/netbox/issues/3882) - Fix filtering of devices by rack group
|
||||
* [#3898](https://github.com/netbox-community/netbox/issues/3898) - Fix references to deleted cables without a label
|
||||
* [#3905](https://github.com/netbox-community/netbox/issues/3905) - Fix divide-by-zero on power feeds with low power values
|
||||
|
||||
---
|
||||
|
||||
# v2.6.11 (2020-01-03)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* [#3831](https://github.com/netbox-community/netbox/issues/3831) - Fix API-driven filter field rendering (#3812 regression)
|
||||
* [#3833](https://github.com/netbox-community/netbox/issues/3833) - Add missing region filters for multiple objects
|
||||
|
||||
---
|
||||
|
||||
# v2.6.10 (2020-01-02)
|
||||
|
||||
## Enhancements
|
||||
|
||||
* [#2233](https://github.com/netbox-community/netbox/issues/2233) - Add ability to move inventory items between devices
|
||||
* [#2892](https://github.com/netbox-community/netbox/issues/2892) - Extend admin UI to allow deleting old report results
|
||||
* [#3062](https://github.com/netbox-community/netbox/issues/3062) - Add `assigned_to_interface` filter for IP addresses
|
||||
* [#3461](https://github.com/netbox-community/netbox/issues/3461) - Fail gracefully on custom link rendering exception
|
||||
* [#3705](https://github.com/netbox-community/netbox/issues/3705) - Provide request context when executing custom scripts
|
||||
* [#3762](https://github.com/netbox-community/netbox/issues/3762) - Add date/time picker widgets
|
||||
* [#3788](https://github.com/netbox-community/netbox/issues/3788) - Enable partial search for inventory items
|
||||
* [#3812](https://github.com/netbox-community/netbox/issues/3812) - Optimize size of pages containing a dynamic selection field
|
||||
* [#3827](https://github.com/netbox-community/netbox/issues/3827) - Allow filtering console/power/interface connections by device ID
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* [#3106](https://github.com/netbox-community/netbox/issues/3106) - Restrict queryset of chained fields when form validation fails
|
||||
* [#3695](https://github.com/netbox-community/netbox/issues/3695) - Include A/Z termination sites for circuits in global search
|
||||
* [#3712](https://github.com/netbox-community/netbox/issues/3712) - Scrolling to target (hash) did not account for the header size
|
||||
* [#3780](https://github.com/netbox-community/netbox/issues/3780) - Fix AttributeError exception in API docs
|
||||
* [#3809](https://github.com/netbox-community/netbox/issues/3809) - Filter platform by manufacturer when editing devices
|
||||
* [#3811](https://github.com/netbox-community/netbox/issues/3811) - Fix filtering of racks by group on device list
|
||||
* [#3822](https://github.com/netbox-community/netbox/issues/3822) - Fix exception when editing a device bay (regression from #3596)
|
||||
|
||||
---
|
||||
|
||||
# v2.6.9 (2019-12-16)
|
||||
|
||||
## Enhancements
|
||||
@@ -13,6 +85,8 @@
|
||||
* [#3749](https://github.com/netbox-community/netbox/issues/3749) - Fix exception on password change page for local users
|
||||
* [#3757](https://github.com/netbox-community/netbox/issues/3757) - Fix unable to assign IP to interface
|
||||
|
||||
---
|
||||
|
||||
# v2.6.8 (2019-12-10)
|
||||
|
||||
## Enhancements
|
||||
@@ -35,6 +109,8 @@
|
||||
* [#3724](https://github.com/netbox-community/netbox/issues/3724) - Fix API filtering of interfaces by more than one device name
|
||||
* [#3725](https://github.com/netbox-community/netbox/issues/3725) - Enforce client validation for minimum service port number
|
||||
|
||||
---
|
||||
|
||||
# v2.6.7 (2019-11-01)
|
||||
|
||||
## Enhancements
|
||||
|
||||
315
docs/release-notes/version-2.7.md
Normal file
315
docs/release-notes/version-2.7.md
Normal file
@@ -0,0 +1,315 @@
|
||||
# v2.7.2 (2020-01-21)
|
||||
|
||||
## Enhancements
|
||||
|
||||
* [#3135](https://github.com/netbox-community/netbox/issues/3135) - Documented power modelling
|
||||
* [#3842](https://github.com/netbox-community/netbox/issues/3842) - Add 802.11ax interface type
|
||||
* [#3954](https://github.com/netbox-community/netbox/issues/3954) - Add `device_bays` filter for devices and device types
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* [#3721](https://github.com/netbox-community/netbox/issues/3721) - Allow Unicode characters in tag slugs
|
||||
* [#3923](https://github.com/netbox-community/netbox/issues/3923) - Indicate validation failure when using SSH-style RSA keys
|
||||
* [#3951](https://github.com/netbox-community/netbox/issues/3951) - Fix exception in webhook worker due to missing constant
|
||||
* [#3953](https://github.com/netbox-community/netbox/issues/3953) - Fix validation error when creating child devices
|
||||
* [#3960](https://github.com/netbox-community/netbox/issues/3960) - Fix legacy device status choice
|
||||
* [#3962](https://github.com/netbox-community/netbox/issues/3962) - Fix display of unnamed devices in rack elevations
|
||||
* [#3963](https://github.com/netbox-community/netbox/issues/3963) - Restore tooltip for devices in rack elevations
|
||||
* [#3964](https://github.com/netbox-community/netbox/issues/3964) - Show borders around devices in rack elevations
|
||||
* [#3965](https://github.com/netbox-community/netbox/issues/3965) - Indicate the presence of "background" devices in rack elevations
|
||||
* [#3966](https://github.com/netbox-community/netbox/issues/3966) - Fix filtering of device components by region/site
|
||||
* [#3967](https://github.com/netbox-community/netbox/issues/3967) - Resolve migration of "other" interface type
|
||||
|
||||
---
|
||||
|
||||
# v2.7.1 (2020-01-16)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* [#3941](https://github.com/netbox-community/netbox/issues/3941) - Fixed exception when attempting to assign IP to interface
|
||||
* [#3943](https://github.com/netbox-community/netbox/issues/3943) - Prevent rack elevation links from opening new tabs/windows
|
||||
* [#3944](https://github.com/netbox-community/netbox/issues/3944) - Fix AttributeError exception when viewing prefixes list
|
||||
|
||||
---
|
||||
|
||||
# v2.7.0 (2020-01-16)
|
||||
|
||||
**Note:** This release completely removes the topology map feature ([#2745](https://github.com/netbox-community/netbox/issues/2745)).
|
||||
|
||||
**Note:** NetBox v2.7 is the last major release that will support Python 3.5. Beginning with NetBox v2.8, Python 3.6 or
|
||||
higher will be required.
|
||||
|
||||
## New Features
|
||||
|
||||
### Enhanced Device Type Import ([#451](https://github.com/netbox-community/netbox/issues/451))
|
||||
|
||||
NetBox now supports the import of device types and related component templates using definitions written in YAML or
|
||||
JSON. For example, the following will create a new device type with four network interfaces, two power ports, and a
|
||||
console port:
|
||||
|
||||
```yaml
|
||||
manufacturer: Acme
|
||||
model: Packet Shooter 9000
|
||||
slug: packet-shooter-9000
|
||||
u_height: 1
|
||||
interfaces:
|
||||
- name: ge-0/0/0
|
||||
type: 1000base-t
|
||||
- name: ge-0/0/1
|
||||
type: 1000base-t
|
||||
- name: ge-0/0/2
|
||||
type: 1000base-t
|
||||
- name: ge-0/0/3
|
||||
type: 1000base-t
|
||||
power-ports:
|
||||
- name: PSU0
|
||||
- name: PSU1
|
||||
console-ports:
|
||||
- name: Console
|
||||
```
|
||||
|
||||
This new functionality replaces the old CSV-based import form, which did not allow for bulk import of component
|
||||
templates.
|
||||
|
||||
### Bulk Import of Device Components ([#822](https://github.com/netbox-community/netbox/issues/822))
|
||||
|
||||
Device components such as console ports, power ports, and interfaces can now be imported in bulk to multiple devices in
|
||||
CSV format. Here's an example showing the bulk import of interfaces to several devices:
|
||||
|
||||
```
|
||||
device,name,type
|
||||
Switch1,Vlan100,Virtual
|
||||
Switch1,Vlan200,Virtual
|
||||
Switch2,Vlan100,Virtual
|
||||
Switch2,Vlan200,Virtual
|
||||
```
|
||||
|
||||
The import form for each type of device component is available under the "Devices" item in the navigation menu.
|
||||
|
||||
### External File Storage ([#1814](https://github.com/netbox-community/netbox/issues/1814))
|
||||
|
||||
In prior releases, the only option for storing uploaded files (e.g. image attachments) was to save them to the local
|
||||
filesystem on the NetBox server. This release introduces support for several remote storage backends provided by the
|
||||
[`django-storages`](https://django-storages.readthedocs.io/en/stable/) library. These include:
|
||||
|
||||
* Amazon S3
|
||||
* ApacheLibcloud
|
||||
* Azure Storage
|
||||
* netbox-community Spaces
|
||||
* Dropbox
|
||||
* FTP
|
||||
* Google Cloud Storage
|
||||
* SFTP
|
||||
|
||||
To enable remote file storage, first install the `django-storages` package:
|
||||
|
||||
```
|
||||
pip install django-storages
|
||||
```
|
||||
|
||||
Then, set the appropriate storage backend and its configuration in `configuration.py`. Here's an example using Amazon
|
||||
S3:
|
||||
|
||||
```python
|
||||
STORAGE_BACKEND = 'storages.backends.s3boto3.S3Boto3Storage'
|
||||
STORAGE_CONFIG = {
|
||||
'AWS_ACCESS_KEY_ID': '<Key>',
|
||||
'AWS_SECRET_ACCESS_KEY': '<Secret>',
|
||||
'AWS_STORAGE_BUCKET_NAME': 'netbox',
|
||||
'AWS_S3_REGION_NAME': 'eu-west-1',
|
||||
}
|
||||
```
|
||||
|
||||
Thanks to [@steffann](https://github.com/steffann) for contributing this work!
|
||||
|
||||
### Rack Elevations Rendered via SVG ([#2248](https://github.com/netbox-community/netbox/issues/2248))
|
||||
|
||||
NetBox v2.7 introduces a new method of rendering rack elevations as an
|
||||
[SVG image](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics) via a REST API endpoint. This replaces the prior
|
||||
method of rendering elevations using pure HTML and CSS, which was cumbersome and had several shortcomings. Rendering
|
||||
rack elevations as SVG images via the REST API allows users to retrieve and make use of the drawings in their own
|
||||
tooling. This also opens the door to other feature requests related to rack elevations in the NetBox backlog.
|
||||
|
||||
This feature implements a new REST API endpoint:
|
||||
|
||||
```
|
||||
/api/dcim/racks/<id>/elevation/
|
||||
```
|
||||
|
||||
By default, this endpoint returns a paginated JSON response representing each rack unit in the given elevation. This is
|
||||
the same response returned by the existing rack units detail endpoint at `/api/dcim/racks/<id>/units/`, which will be
|
||||
removed in v2.8 (see [#3753](https://github.com/netbox-community/netbox/issues/3753)).
|
||||
|
||||
To render the elevation as an SVG image, include the `render=svg` query parameter in the request. You may also control
|
||||
the width and height of the elevation drawing (in pixels) by passing the `unit_width` and `unit_height` parameters. (The
|
||||
default values for these parameters are 230 and 20, respectively.) Additionally, the `face` parameter may be used to
|
||||
request either the `front` or `rear` of the elevation. Below is in example request:
|
||||
|
||||
```
|
||||
/api/dcim/racks/<id>/elevation/?render=svg&face=rear&unit_width=300&unit_height=35
|
||||
```
|
||||
|
||||
Thanks to [@hellerve](https://github.com/hellerve) for doing the heavy lifting on this!
|
||||
|
||||
## Changes
|
||||
|
||||
### Topology Maps Removed ([#2745](https://github.com/netbox-community/netbox/issues/2745))
|
||||
|
||||
The topology maps feature has been removed to help focus NetBox development efforts. Please replicate any required data
|
||||
to another source before upgrading NetBox to v2.7, as any existing topology maps will be deleted.
|
||||
|
||||
### Supervisor Replaced with systemd ([#2902](https://github.com/netbox-community/netbox/issues/2902))
|
||||
|
||||
The NetBox [installation documentation](https://netbox.readthedocs.io/en/stable/installation/) has been updated to
|
||||
provide instructions for managing the WSGI and RQ services using systemd instead of supervisor. This removes the need to
|
||||
install supervisor and simplifies administration of the processes.
|
||||
|
||||
### Redis Configuration ([#3282](https://github.com/netbox-community/netbox/issues/3282))
|
||||
|
||||
NetBox v2.6 introduced request caching and added the `CACHE_DATABASE` option to the existing `REDIS` database
|
||||
configuration parameter. This did not, however, allow for using two different Redis connections for the separate caching
|
||||
and webhook queuing functions. This release modifies the `REDIS` parameter to accept two discrete subsections named
|
||||
`webhooks` and `caching`. This requires modification of the `REDIS` parameter in `configuration.py` as follows:
|
||||
|
||||
Old Redis configuration:
|
||||
|
||||
```python
|
||||
REDIS = {
|
||||
'HOST': 'localhost',
|
||||
'PORT': 6379,
|
||||
'PASSWORD': '',
|
||||
'DATABASE': 0,
|
||||
'CACHE_DATABASE': 1,
|
||||
'DEFAULT_TIMEOUT': 300,
|
||||
'SSL': False,
|
||||
}
|
||||
```
|
||||
|
||||
New Redis configuration:
|
||||
|
||||
```python
|
||||
REDIS = {
|
||||
'webhooks': {
|
||||
'HOST': 'redis.example.com',
|
||||
'PORT': 1234,
|
||||
'PASSWORD': 'foobar',
|
||||
'DATABASE': 0,
|
||||
'DEFAULT_TIMEOUT': 300,
|
||||
'SSL': False,
|
||||
},
|
||||
'caching': {
|
||||
'HOST': 'localhost',
|
||||
'PORT': 6379,
|
||||
'PASSWORD': '',
|
||||
'DATABASE': 1,
|
||||
'DEFAULT_TIMEOUT': 300,
|
||||
'SSL': False,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that the `CACHE_DATABASE` parameter has been removed and the connection settings have been duplicated for both
|
||||
`webhooks` and `caching`. This allows the user to make use of separate Redis instances if desired. It is fine to use the
|
||||
same Redis service for both functions, although the database identifiers should be different.
|
||||
|
||||
### WEBHOOKS_ENABLED Configuration Setting Removed ([#3408](https://github.com/netbox-community/netbox/issues/3408))
|
||||
|
||||
As `django-rq` is now a required library, NetBox assumes that the RQ worker process is running. The installation and
|
||||
upgrade documentation has been updated to reflect this, and the `WEBHOOKS_ENABLED` configuration parameter is no longer
|
||||
used. Please ensure that both the NetBox WSGI service and the RQ worker process are running on all production
|
||||
installations.
|
||||
|
||||
### API Choice Fields Now Use String Values ([#3569](https://github.com/netbox-community/netbox/issues/3569))
|
||||
|
||||
NetBox's REST API presents fields which reference a particular choice as a dictionary with two keys: `value` and
|
||||
`label`. In previous versions, `value` was an integer which represented a particular choice in the database. This has
|
||||
been changed to a more human-friendly "slug" string, which is essentially a simplified version of the choice's `label`.
|
||||
|
||||
For example, The site model's `status` field was previously represented as:
|
||||
|
||||
```json
|
||||
"status": {
|
||||
"value": 1,
|
||||
"label": "Active"
|
||||
},
|
||||
```
|
||||
|
||||
In NetBox v2.7, it now looks like this:
|
||||
|
||||
```json
|
||||
"status": {
|
||||
"value": "active",
|
||||
"label": "Active",
|
||||
"id": 1
|
||||
},
|
||||
```
|
||||
|
||||
This change allows for much more intuitive representation and manipulation of values, and removes the need for API
|
||||
consumers to maintain local mappings of static integer values.
|
||||
|
||||
Note that that all v2.7 releases will continue to accept the legacy integer values in write requests (`POST`, `PUT`, and
|
||||
`PATCH`) to maintain backward compatibility. Additionally, the legacy numeric identifier is conveyed in the `id` field
|
||||
for convenient reference as consumers adopt to the new string values. This behavior will be discontinued in NetBox v2.8.
|
||||
|
||||
## Enhancements
|
||||
|
||||
* [#33](https://github.com/netbox-community/netbox/issues/33) - Add ability to clone objects (pre-populate form fields)
|
||||
* [#648](https://github.com/netbox-community/netbox/issues/648) - Pre-populate form fields when selecting "create and
|
||||
add another"
|
||||
* [#792](https://github.com/netbox-community/netbox/issues/792) - Add power port and power outlet types
|
||||
* [#1865](https://github.com/netbox-community/netbox/issues/1865) - Add console port and console server port types
|
||||
* [#2669](https://github.com/netbox-community/netbox/issues/2669) - Relax uniqueness constraint on device and VM names
|
||||
* [#2902](https://github.com/netbox-community/netbox/issues/2902) - Replace `supervisord` with `systemd`
|
||||
* [#3455](https://github.com/netbox-community/netbox/issues/3455) - Add tenant assignment to virtual machine clusters
|
||||
* [#3520](https://github.com/netbox-community/netbox/issues/3520) - Add Jinja2 template support for graphs
|
||||
* [#3525](https://github.com/netbox-community/netbox/issues/3525) - Enable IP address filtering using multiple address
|
||||
parameters
|
||||
* [#3564](https://github.com/netbox-community/netbox/issues/3564) - Add list views for all device components
|
||||
* [#3538](https://github.com/netbox-community/netbox/issues/3538) - Introduce a REST API endpoint for executing custom
|
||||
scripts
|
||||
* [#3655](https://github.com/netbox-community/netbox/issues/3655) - Add `description` field to organizational models
|
||||
* [#3664](https://github.com/netbox-community/netbox/issues/3664) - Enable applying configuration contexts by tags
|
||||
* [#3706](https://github.com/netbox-community/netbox/issues/3706) - Increase `available_power` maximum value on
|
||||
PowerFeed
|
||||
* [#3731](https://github.com/netbox-community/netbox/issues/3731) - Change Graph.type to a ContentType foreign key field
|
||||
* [#3801](https://github.com/netbox-community/netbox/issues/3801) - Use YAML for export of device types
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* [#3830](https://github.com/netbox-community/netbox/issues/3830) - Ensure deterministic ordering for all models
|
||||
* [#3900](https://github.com/netbox-community/netbox/issues/3900) - Fix exception when deleting device types
|
||||
* [#3914](https://github.com/netbox-community/netbox/issues/3914) - Fix interface filter field when unauthenticated
|
||||
* [#3919](https://github.com/netbox-community/netbox/issues/3919) - Fix utilization graph extending out of bounds when
|
||||
utilization > 100%
|
||||
* [#3927](https://github.com/netbox-community/netbox/issues/3927) - Fix exception when deleting devices with secrets
|
||||
assigned
|
||||
* [#3930](https://github.com/netbox-community/netbox/issues/3930) - Fix API rendering of the `family` field for
|
||||
aggregates
|
||||
|
||||
## Bug Fixes (From Beta)
|
||||
|
||||
* [#3868](https://github.com/netbox-community/netbox/issues/3868) - Fix creation of interfaces for virtual machines
|
||||
* [#3878](https://github.com/netbox-community/netbox/issues/3878) - Fix database migration for cable status field
|
||||
|
||||
## API Changes
|
||||
|
||||
* Choice fields now use human-friendly strings for their values instead of integers (see
|
||||
[#3569](https://github.com/netbox-community/netbox/issues/3569)).
|
||||
* Introduced the `/api/extras/scripts/` endpoint for retrieving and executing custom scripts
|
||||
* circuits.CircuitType: Added field `description`
|
||||
* dcim.ConsolePort: Added field `type`
|
||||
* dcim.ConsolePortTemplate: Added field `type`
|
||||
* dcim.ConsoleServerPort: Added field `type`
|
||||
* dcim.ConsoleServerPortTemplate: Added field `type`
|
||||
* dcim.DeviceRole: Added field `description`
|
||||
* dcim.PowerPort: Added field `type`
|
||||
* dcim.PowerPortTemplate: Added field `type`
|
||||
* dcim.PowerOutlet: Added field `type`
|
||||
* dcim.PowerOutletTemplate: Added field `type`
|
||||
* dcim.RackRole: Added field `description`
|
||||
* extras.Graph: Added field `template_language` (to indicate `django` or `jinja2`)
|
||||
* extras.Graph: The `type` field has been changed to a content type foreign key. Models are specified as
|
||||
`<app>.<model>`; e.g. `dcim.site`.
|
||||
* ipam.Role: Added field `description`
|
||||
* secrets.SecretRole: Added field `description`
|
||||
* virtualization.Cluster: Added field `tenant`
|
||||
@@ -12,6 +12,7 @@ pages:
|
||||
- 4. LDAP (Optional): 'installation/4-ldap.md'
|
||||
- Upgrading NetBox: 'installation/upgrading.md'
|
||||
- Migrating to Python3: 'installation/migrating-to-python3.md'
|
||||
- Migrating to systemd: 'installation/migrating-to-systemd.md'
|
||||
- Configuration:
|
||||
- Configuring NetBox: 'configuration/index.md'
|
||||
- Required Settings: 'configuration/required-settings.md'
|
||||
@@ -24,6 +25,7 @@ pages:
|
||||
- Virtual Machines: 'core-functionality/virtual-machines.md'
|
||||
- Services: 'core-functionality/services.md'
|
||||
- Circuits: 'core-functionality/circuits.md'
|
||||
- Power: 'core-functionality/power.md'
|
||||
- Secrets: 'core-functionality/secrets.md'
|
||||
- Tenancy: 'core-functionality/tenancy.md'
|
||||
- Additional Features:
|
||||
@@ -35,6 +37,7 @@ pages:
|
||||
- Custom Scripts: 'additional-features/custom-scripts.md'
|
||||
- Export Templates: 'additional-features/export-templates.md'
|
||||
- Graphs: 'additional-features/graphs.md'
|
||||
- NAPALM: 'additional-features/napalm.md'
|
||||
- Prometheus Metrics: 'additional-features/prometheus-metrics.md'
|
||||
- Reports: 'additional-features/reports.md'
|
||||
- Tags: 'additional-features/tags.md'
|
||||
@@ -54,7 +57,9 @@ pages:
|
||||
- Utility Views: 'development/utility-views.md'
|
||||
- Extending Models: 'development/extending-models.md'
|
||||
- Release Checklist: 'development/release-checklist.md'
|
||||
- Squashing Migrations: 'development/squashing-migrations.md'
|
||||
- Release Notes:
|
||||
- Version 2.7: 'release-notes/version-2.7.md'
|
||||
- Version 2.6: 'release-notes/version-2.6.md'
|
||||
- Version 2.5: 'release-notes/version-2.5.md'
|
||||
- Version 2.4: 'release-notes/version-2.4.md'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from rest_framework import serializers
|
||||
from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
|
||||
|
||||
from circuits.constants import CIRCUIT_STATUS_CHOICES
|
||||
from circuits.choices import CircuitStatusChoices
|
||||
from circuits.models import Provider, Circuit, CircuitTermination, CircuitType
|
||||
from dcim.api.nested_serializers import NestedCableSerializer, NestedSiteSerializer
|
||||
from dcim.api.serializers import ConnectedEndpointSerializer
|
||||
@@ -36,12 +36,12 @@ class CircuitTypeSerializer(ValidatedModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = CircuitType
|
||||
fields = ['id', 'name', 'slug', 'circuit_count']
|
||||
fields = ['id', 'name', 'slug', 'description', 'circuit_count']
|
||||
|
||||
|
||||
class CircuitSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
provider = NestedProviderSerializer()
|
||||
status = ChoiceField(choices=CIRCUIT_STATUS_CHOICES, required=False)
|
||||
status = ChoiceField(choices=CircuitStatusChoices, required=False)
|
||||
type = NestedCircuitTypeSerializer()
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
@@ -7,7 +7,7 @@ from circuits import filters
|
||||
from circuits.models import Provider, CircuitTermination, CircuitType, Circuit
|
||||
from extras.api.serializers import RenderedGraphSerializer
|
||||
from extras.api.views import CustomFieldModelViewSet
|
||||
from extras.models import Graph, GRAPH_TYPE_PROVIDER
|
||||
from extras.models import Graph
|
||||
from utilities.api import FieldChoicesViewSet, ModelViewSet
|
||||
from . import serializers
|
||||
|
||||
@@ -18,8 +18,8 @@ from . import serializers
|
||||
|
||||
class CircuitsFieldChoicesViewSet(FieldChoicesViewSet):
|
||||
fields = (
|
||||
(Circuit, ['status']),
|
||||
(CircuitTermination, ['term_side']),
|
||||
(serializers.CircuitSerializer, ['status']),
|
||||
(serializers.CircuitTerminationSerializer, ['term_side']),
|
||||
)
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class ProviderViewSet(CustomFieldModelViewSet):
|
||||
circuit_count=Count('circuits')
|
||||
)
|
||||
serializer_class = serializers.ProviderSerializer
|
||||
filterset_class = filters.ProviderFilter
|
||||
filterset_class = filters.ProviderFilterSet
|
||||
|
||||
@action(detail=True)
|
||||
def graphs(self, request, pk):
|
||||
@@ -40,7 +40,7 @@ class ProviderViewSet(CustomFieldModelViewSet):
|
||||
A convenience method for rendering graphs for a particular provider.
|
||||
"""
|
||||
provider = get_object_or_404(Provider, pk=pk)
|
||||
queryset = Graph.objects.filter(type=GRAPH_TYPE_PROVIDER)
|
||||
queryset = Graph.objects.filter(type__model='provider')
|
||||
serializer = RenderedGraphSerializer(queryset, many=True, context={'graphed_object': provider})
|
||||
return Response(serializer.data)
|
||||
|
||||
@@ -54,7 +54,7 @@ class CircuitTypeViewSet(ModelViewSet):
|
||||
circuit_count=Count('circuits')
|
||||
)
|
||||
serializer_class = serializers.CircuitTypeSerializer
|
||||
filterset_class = filters.CircuitTypeFilter
|
||||
filterset_class = filters.CircuitTypeFilterSet
|
||||
|
||||
|
||||
#
|
||||
@@ -64,7 +64,7 @@ class CircuitTypeViewSet(ModelViewSet):
|
||||
class CircuitViewSet(CustomFieldModelViewSet):
|
||||
queryset = Circuit.objects.prefetch_related('type', 'tenant', 'provider').prefetch_related('tags')
|
||||
serializer_class = serializers.CircuitSerializer
|
||||
filterset_class = filters.CircuitFilter
|
||||
filterset_class = filters.CircuitFilterSet
|
||||
|
||||
|
||||
#
|
||||
@@ -76,4 +76,4 @@ class CircuitTerminationViewSet(ModelViewSet):
|
||||
'circuit', 'site', 'connected_endpoint__device', 'cable'
|
||||
)
|
||||
serializer_class = serializers.CircuitTerminationSerializer
|
||||
filterset_class = filters.CircuitTerminationFilter
|
||||
filterset_class = filters.CircuitTerminationFilterSet
|
||||
|
||||
48
netbox/circuits/choices.py
Normal file
48
netbox/circuits/choices.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from utilities.choices import ChoiceSet
|
||||
|
||||
|
||||
#
|
||||
# Circuits
|
||||
#
|
||||
|
||||
class CircuitStatusChoices(ChoiceSet):
|
||||
|
||||
STATUS_DEPROVISIONING = 'deprovisioning'
|
||||
STATUS_ACTIVE = 'active'
|
||||
STATUS_PLANNED = 'planned'
|
||||
STATUS_PROVISIONING = 'provisioning'
|
||||
STATUS_OFFLINE = 'offline'
|
||||
STATUS_DECOMMISSIONED = 'decommissioned'
|
||||
|
||||
CHOICES = (
|
||||
(STATUS_PLANNED, 'Planned'),
|
||||
(STATUS_PROVISIONING, 'Provisioning'),
|
||||
(STATUS_ACTIVE, 'Active'),
|
||||
(STATUS_OFFLINE, 'Offline'),
|
||||
(STATUS_DEPROVISIONING, 'Deprovisioning'),
|
||||
(STATUS_DECOMMISSIONED, 'Decommissioned'),
|
||||
)
|
||||
|
||||
LEGACY_MAP = {
|
||||
STATUS_DEPROVISIONING: 0,
|
||||
STATUS_ACTIVE: 1,
|
||||
STATUS_PLANNED: 2,
|
||||
STATUS_PROVISIONING: 3,
|
||||
STATUS_OFFLINE: 4,
|
||||
STATUS_DECOMMISSIONED: 5,
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# CircuitTerminations
|
||||
#
|
||||
|
||||
class CircuitTerminationSideChoices(ChoiceSet):
|
||||
|
||||
SIDE_A = 'A'
|
||||
SIDE_Z = 'Z'
|
||||
|
||||
CHOICES = (
|
||||
(SIDE_A, 'A'),
|
||||
(SIDE_Z, 'Z')
|
||||
)
|
||||
@@ -1,23 +0,0 @@
|
||||
# Circuit statuses
|
||||
CIRCUIT_STATUS_DEPROVISIONING = 0
|
||||
CIRCUIT_STATUS_ACTIVE = 1
|
||||
CIRCUIT_STATUS_PLANNED = 2
|
||||
CIRCUIT_STATUS_PROVISIONING = 3
|
||||
CIRCUIT_STATUS_OFFLINE = 4
|
||||
CIRCUIT_STATUS_DECOMMISSIONED = 5
|
||||
CIRCUIT_STATUS_CHOICES = [
|
||||
[CIRCUIT_STATUS_PLANNED, 'Planned'],
|
||||
[CIRCUIT_STATUS_PROVISIONING, 'Provisioning'],
|
||||
[CIRCUIT_STATUS_ACTIVE, 'Active'],
|
||||
[CIRCUIT_STATUS_OFFLINE, 'Offline'],
|
||||
[CIRCUIT_STATUS_DEPROVISIONING, 'Deprovisioning'],
|
||||
[CIRCUIT_STATUS_DECOMMISSIONED, 'Decommissioned'],
|
||||
]
|
||||
|
||||
# CircuitTermination sides
|
||||
TERM_SIDE_A = 'A'
|
||||
TERM_SIDE_Z = 'Z'
|
||||
TERM_SIDE_CHOICES = (
|
||||
(TERM_SIDE_A, 'A'),
|
||||
(TERM_SIDE_Z, 'Z'),
|
||||
)
|
||||
@@ -3,13 +3,20 @@ from django.db.models import Q
|
||||
|
||||
from dcim.models import Region, Site
|
||||
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
|
||||
from tenancy.filtersets import TenancyFilterSet
|
||||
from tenancy.filters import TenancyFilterSet
|
||||
from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter, TreeNodeMultipleChoiceFilter
|
||||
from .constants import *
|
||||
from .choices import *
|
||||
from .models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
|
||||
__all__ = (
|
||||
'CircuitFilterSet',
|
||||
'CircuitTerminationFilterSet',
|
||||
'CircuitTypeFilterSet',
|
||||
'ProviderFilterSet',
|
||||
)
|
||||
|
||||
class ProviderFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
|
||||
class ProviderFilterSet(CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@@ -18,6 +25,17 @@ class ProviderFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
region_id = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='circuits__terminations__site__region__in',
|
||||
label='Region (ID)',
|
||||
)
|
||||
region = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='circuits__terminations__site__region__in',
|
||||
to_field_name='slug',
|
||||
label='Region (slug)',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='circuits__terminations__site',
|
||||
queryset=Site.objects.all(),
|
||||
@@ -47,14 +65,14 @@ class ProviderFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
)
|
||||
|
||||
|
||||
class CircuitTypeFilter(NameSlugSearchFilterSet):
|
||||
class CircuitTypeFilterSet(NameSlugSearchFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = CircuitType
|
||||
fields = ['id', 'name', 'slug']
|
||||
|
||||
|
||||
class CircuitFilter(CustomFieldFilterSet, TenancyFilterSet, CreatedUpdatedFilterSet):
|
||||
class CircuitFilterSet(CustomFieldFilterSet, TenancyFilterSet, CreatedUpdatedFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@@ -84,7 +102,7 @@ class CircuitFilter(CustomFieldFilterSet, TenancyFilterSet, CreatedUpdatedFilter
|
||||
label='Circuit type (slug)',
|
||||
)
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
choices=CIRCUIT_STATUS_CHOICES,
|
||||
choices=CircuitStatusChoices,
|
||||
null_value=None
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
@@ -128,7 +146,7 @@ class CircuitFilter(CustomFieldFilterSet, TenancyFilterSet, CreatedUpdatedFilter
|
||||
).distinct()
|
||||
|
||||
|
||||
class CircuitTerminationFilter(django_filters.FilterSet):
|
||||
class CircuitTerminationFilterSet(django_filters.FilterSet):
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
[
|
||||
{
|
||||
"model": "circuits.circuittype",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"name": "Internet",
|
||||
"slug": "internet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "circuits.circuittype",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"name": "Private WAN",
|
||||
"slug": "private-wan"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "circuits.circuittype",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"name": "Out-of-Band",
|
||||
"slug": "out-of-band"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -7,9 +7,9 @@ from tenancy.forms import TenancyFilterForm, TenancyForm
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms import (
|
||||
APISelect, APISelectMultiple, add_blank_choice, BootstrapMixin, CommentField, CSVChoiceField,
|
||||
FilterChoiceField, SmallTextarea, SlugField, StaticSelect2, StaticSelect2Multiple
|
||||
DatePicker, FilterChoiceField, SmallTextarea, SlugField, StaticSelect2, StaticSelect2Multiple
|
||||
)
|
||||
from .constants import *
|
||||
from .choices import CircuitStatusChoices
|
||||
from .models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
|
||||
|
||||
@@ -104,6 +104,18 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
required=False,
|
||||
label='Search'
|
||||
)
|
||||
region = FilterChoiceField(
|
||||
queryset=Region.objects.all(),
|
||||
to_field_name='slug',
|
||||
required=False,
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/dcim/regions/",
|
||||
value_field="slug",
|
||||
filter_for={
|
||||
'site': 'region'
|
||||
}
|
||||
)
|
||||
)
|
||||
site = FilterChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
@@ -128,7 +140,7 @@ class CircuitTypeForm(BootstrapMixin, forms.ModelForm):
|
||||
class Meta:
|
||||
model = CircuitType
|
||||
fields = [
|
||||
'name', 'slug',
|
||||
'name', 'slug', 'description',
|
||||
]
|
||||
|
||||
|
||||
@@ -161,7 +173,6 @@ class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldForm):
|
||||
]
|
||||
help_texts = {
|
||||
'cid': "Unique circuit ID",
|
||||
'install_date': "Format: YYYY-MM-DD",
|
||||
'commit_rate': "Committed rate",
|
||||
}
|
||||
widgets = {
|
||||
@@ -172,7 +183,7 @@ class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldForm):
|
||||
api_url="/api/circuits/circuit-types/"
|
||||
),
|
||||
'status': StaticSelect2(),
|
||||
|
||||
'install_date': DatePicker(),
|
||||
}
|
||||
|
||||
|
||||
@@ -194,7 +205,7 @@ class CircuitCSVForm(forms.ModelForm):
|
||||
}
|
||||
)
|
||||
status = CSVChoiceField(
|
||||
choices=CIRCUIT_STATUS_CHOICES,
|
||||
choices=CircuitStatusChoices,
|
||||
required=False,
|
||||
help_text='Operational status'
|
||||
)
|
||||
@@ -235,7 +246,7 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
|
||||
)
|
||||
)
|
||||
status = forms.ChoiceField(
|
||||
choices=add_blank_choice(CIRCUIT_STATUS_CHOICES),
|
||||
choices=add_blank_choice(CircuitStatusChoices),
|
||||
required=False,
|
||||
initial='',
|
||||
widget=StaticSelect2()
|
||||
@@ -292,7 +303,7 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm
|
||||
)
|
||||
)
|
||||
status = forms.MultipleChoiceField(
|
||||
choices=CIRCUIT_STATUS_CHOICES,
|
||||
choices=CircuitStatusChoices,
|
||||
required=False,
|
||||
widget=StaticSelect2Multiple()
|
||||
)
|
||||
@@ -303,6 +314,9 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/dcim/regions/",
|
||||
value_field="slug",
|
||||
filter_for={
|
||||
'site': 'region'
|
||||
}
|
||||
)
|
||||
)
|
||||
site = FilterChoiceField(
|
||||
|
||||
@@ -1,40 +1,36 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.14 on 2018-07-31 02:25
|
||||
import dcim.fields
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
import dcim.fields
|
||||
|
||||
|
||||
def circuits_to_terms(apps, schema_editor):
|
||||
Circuit = apps.get_model('circuits', 'Circuit')
|
||||
CircuitTermination = apps.get_model('circuits', 'CircuitTermination')
|
||||
for c in Circuit.objects.all():
|
||||
CircuitTermination(
|
||||
circuit=c,
|
||||
term_side=b'A',
|
||||
site=c.site,
|
||||
interface=c.interface,
|
||||
port_speed=c.port_speed,
|
||||
upstream_speed=c.upstream_speed,
|
||||
xconnect_id=c.xconnect_id,
|
||||
pp_info=c.pp_info,
|
||||
).save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [('circuits', '0001_initial'), ('circuits', '0002_auto_20160622_1821'), ('circuits', '0003_provider_32bit_asn_support'), ('circuits', '0004_circuit_add_tenant'), ('circuits', '0005_circuit_add_upstream_speed'), ('circuits', '0006_terminations'), ('circuits', '0007_circuit_add_description'), ('circuits', '0008_circuittermination_interface_protect_on_delete'), ('circuits', '0009_unicode_literals'), ('circuits', '0010_circuit_status')]
|
||||
replaces = [('circuits', '0001_initial'), ('circuits', '0002_auto_20160622_1821'), ('circuits', '0003_provider_32bit_asn_support'), ('circuits', '0004_circuit_add_tenant'), ('circuits', '0005_circuit_add_upstream_speed'), ('circuits', '0006_terminations')]
|
||||
|
||||
dependencies = [
|
||||
('tenancy', '0001_initial'),
|
||||
('dcim', '0001_initial'),
|
||||
('dcim', '0022_color_names_to_rgb'),
|
||||
('tenancy', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Provider',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateField(auto_now_add=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True)),
|
||||
('name', models.CharField(max_length=50, unique=True)),
|
||||
('slug', models.SlugField(unique=True)),
|
||||
('asn', dcim.fields.ASNField(blank=True, null=True, verbose_name='ASN')),
|
||||
('account', models.CharField(blank=True, max_length=30, verbose_name='Account number')),
|
||||
('portal_url', models.URLField(blank=True, verbose_name='Portal')),
|
||||
('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)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CircuitType',
|
||||
fields=[
|
||||
@@ -46,49 +42,93 @@ class Migration(migrations.Migration):
|
||||
'ordering': ['name'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Provider',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateField(auto_now_add=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True)),
|
||||
('name', models.CharField(max_length=50, unique=True)),
|
||||
('slug', models.SlugField(unique=True)),
|
||||
('asn', dcim.fields.ASNField(blank=True, null=True, verbose_name=b'ASN')),
|
||||
('account', models.CharField(blank=True, max_length=30, verbose_name=b'Account number')),
|
||||
('portal_url', models.URLField(blank=True, verbose_name=b'Portal')),
|
||||
('noc_contact', models.TextField(blank=True, verbose_name=b'NOC contact')),
|
||||
('admin_contact', models.TextField(blank=True, verbose_name=b'Admin contact')),
|
||||
('comments', models.TextField(blank=True)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Circuit',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateField(auto_now_add=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True)),
|
||||
('cid', models.CharField(max_length=50, verbose_name='Circuit ID')),
|
||||
('install_date', models.DateField(blank=True, null=True, verbose_name='Date installed')),
|
||||
('commit_rate', models.PositiveIntegerField(blank=True, null=True, verbose_name='Commit rate (Kbps)')),
|
||||
('cid', models.CharField(max_length=50, verbose_name=b'Circuit ID')),
|
||||
('install_date', models.DateField(blank=True, null=True, verbose_name=b'Date installed')),
|
||||
('port_speed', models.PositiveIntegerField(verbose_name=b'Port speed (Kbps)')),
|
||||
('commit_rate', models.PositiveIntegerField(blank=True, null=True, verbose_name=b'Commit rate (Kbps)')),
|
||||
('xconnect_id', models.CharField(blank=True, max_length=50, verbose_name=b'Cross-connect ID')),
|
||||
('pp_info', models.CharField(blank=True, max_length=100, verbose_name=b'Patch panel/port(s)')),
|
||||
('comments', models.TextField(blank=True)),
|
||||
('interface', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='circuit', to='dcim.Interface')),
|
||||
('provider', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.Provider')),
|
||||
('site', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='dcim.Site')),
|
||||
('type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.CircuitType')),
|
||||
('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='tenancy.Tenant')),
|
||||
('description', models.CharField(blank=True, max_length=100)),
|
||||
('status', models.PositiveSmallIntegerField(choices=[[2, 'Planned'], [3, 'Provisioning'], [1, 'Active'], [4, 'Offline'], [0, 'Deprovisioning'], [5, 'Decommissioned']], default=1))
|
||||
('upstream_speed', models.PositiveIntegerField(blank=True, help_text=b'Upstream speed, if different from port speed', null=True, verbose_name=b'Upstream speed (Kbps)')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['provider', 'cid'],
|
||||
'unique_together': {('provider', 'cid')},
|
||||
},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='circuit',
|
||||
unique_together=set([('provider', 'cid')]),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CircuitTermination',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('term_side', models.CharField(choices=[('A', 'A'), ('Z', 'Z')], max_length=1, verbose_name='Termination')),
|
||||
('port_speed', models.PositiveIntegerField(verbose_name='Port speed (Kbps)')),
|
||||
('upstream_speed', models.PositiveIntegerField(blank=True, help_text='Upstream speed, if different from port speed', null=True, verbose_name='Upstream speed (Kbps)')),
|
||||
('xconnect_id', models.CharField(blank=True, max_length=50, verbose_name='Cross-connect ID')),
|
||||
('pp_info', models.CharField(blank=True, max_length=100, verbose_name='Patch panel/port(s)')),
|
||||
('term_side', models.CharField(choices=[(b'A', b'A'), (b'Z', b'Z')], max_length=1, verbose_name='Termination')),
|
||||
('port_speed', models.PositiveIntegerField(verbose_name=b'Port speed (Kbps)')),
|
||||
('upstream_speed', models.PositiveIntegerField(blank=True, help_text=b'Upstream speed, if different from port speed', null=True, verbose_name=b'Upstream speed (Kbps)')),
|
||||
('xconnect_id', models.CharField(blank=True, max_length=50, verbose_name=b'Cross-connect ID')),
|
||||
('pp_info', models.CharField(blank=True, max_length=100, verbose_name=b'Patch panel/port(s)')),
|
||||
('circuit', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='circuits.Circuit')),
|
||||
('interface', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuit_termination', to='dcim.Interface')),
|
||||
('interface', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='circuit_termination', to='dcim.Interface')),
|
||||
('site', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='circuit_terminations', to='dcim.Site')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['circuit', 'term_side'],
|
||||
'unique_together': {('circuit', 'term_side')},
|
||||
},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='circuittermination',
|
||||
unique_together=set([('circuit', 'term_side')]),
|
||||
migrations.RunPython(
|
||||
code=circuits_to_terms,
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='circuit',
|
||||
name='interface',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='circuit',
|
||||
name='port_speed',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='circuit',
|
||||
name='pp_info',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='circuit',
|
||||
name='site',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='circuit',
|
||||
name='upstream_speed',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='circuit',
|
||||
name='xconnect_id',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,254 @@
|
||||
import sys
|
||||
|
||||
import django.db.models.deletion
|
||||
import taggit.managers
|
||||
from django.db import migrations, models
|
||||
|
||||
import dcim.fields
|
||||
|
||||
CONNECTION_STATUS_CONNECTED = True
|
||||
|
||||
CIRCUIT_STATUS_CHOICES = (
|
||||
(0, 'deprovisioning'),
|
||||
(1, 'active'),
|
||||
(2, 'planned'),
|
||||
(3, 'provisioning'),
|
||||
(4, 'offline'),
|
||||
(5, 'decommissioned')
|
||||
)
|
||||
|
||||
|
||||
def circuit_terminations_to_cables(apps, schema_editor):
|
||||
"""
|
||||
Copy all existing CircuitTermination Interface associations as Cables
|
||||
"""
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
CircuitTermination = apps.get_model('circuits', 'CircuitTermination')
|
||||
Interface = apps.get_model('dcim', 'Interface')
|
||||
Cable = apps.get_model('dcim', 'Cable')
|
||||
|
||||
# Load content types
|
||||
circuittermination_type = ContentType.objects.get_for_model(CircuitTermination)
|
||||
interface_type = ContentType.objects.get_for_model(Interface)
|
||||
|
||||
# Create a new Cable instance from each console connection
|
||||
if 'test' not in sys.argv:
|
||||
print("\n Adding circuit terminations... ", end='', flush=True)
|
||||
for circuittermination in CircuitTermination.objects.filter(interface__isnull=False):
|
||||
|
||||
# Create the new Cable
|
||||
cable = Cable.objects.create(
|
||||
termination_a_type=circuittermination_type,
|
||||
termination_a_id=circuittermination.id,
|
||||
termination_b_type=interface_type,
|
||||
termination_b_id=circuittermination.interface_id,
|
||||
status=CONNECTION_STATUS_CONNECTED
|
||||
)
|
||||
|
||||
# Cache the Cable on its two termination points
|
||||
CircuitTermination.objects.filter(pk=circuittermination.pk).update(
|
||||
cable=cable,
|
||||
connected_endpoint=circuittermination.interface,
|
||||
connection_status=CONNECTION_STATUS_CONNECTED
|
||||
)
|
||||
# Cache the connected Cable on the Interface
|
||||
Interface.objects.filter(pk=circuittermination.interface_id).update(
|
||||
cable=cable,
|
||||
_connected_circuittermination=circuittermination,
|
||||
connection_status=CONNECTION_STATUS_CONNECTED
|
||||
)
|
||||
|
||||
cable_count = Cable.objects.filter(termination_a_type=circuittermination_type).count()
|
||||
if 'test' not in sys.argv:
|
||||
print("{} cables created".format(cable_count))
|
||||
|
||||
|
||||
def circuit_status_to_slug(apps, schema_editor):
|
||||
Circuit = apps.get_model('circuits', 'Circuit')
|
||||
for id, slug in CIRCUIT_STATUS_CHOICES:
|
||||
Circuit.objects.filter(status=str(id)).update(status=slug)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [('circuits', '0007_circuit_add_description'), ('circuits', '0008_circuittermination_interface_protect_on_delete'), ('circuits', '0009_unicode_literals'), ('circuits', '0010_circuit_status'), ('circuits', '0011_tags'), ('circuits', '0012_change_logging'), ('circuits', '0013_cables'), ('circuits', '0014_circuittermination_description'), ('circuits', '0015_custom_tag_models'), ('circuits', '0016_3569_circuit_fields'), ('circuits', '0017_circuittype_description')]
|
||||
|
||||
dependencies = [
|
||||
('circuits', '0006_terminations'),
|
||||
('extras', '0019_tag_taggeditem'),
|
||||
('taggit', '0002_auto_20150616_2121'),
|
||||
('dcim', '0066_cables'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='circuit',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuittermination',
|
||||
name='interface',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuit_termination', to='dcim.Interface'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuit',
|
||||
name='cid',
|
||||
field=models.CharField(max_length=50, verbose_name='Circuit ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuit',
|
||||
name='commit_rate',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Commit rate (Kbps)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuit',
|
||||
name='install_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='Date installed'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuittermination',
|
||||
name='port_speed',
|
||||
field=models.PositiveIntegerField(verbose_name='Port speed (Kbps)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuittermination',
|
||||
name='pp_info',
|
||||
field=models.CharField(blank=True, max_length=100, verbose_name='Patch panel/port(s)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuittermination',
|
||||
name='term_side',
|
||||
field=models.CharField(choices=[('A', 'A'), ('Z', 'Z')], max_length=1, verbose_name='Termination'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuittermination',
|
||||
name='upstream_speed',
|
||||
field=models.PositiveIntegerField(blank=True, help_text='Upstream speed, if different from port speed', null=True, verbose_name='Upstream speed (Kbps)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuittermination',
|
||||
name='xconnect_id',
|
||||
field=models.CharField(blank=True, max_length=50, verbose_name='Cross-connect ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='provider',
|
||||
name='account',
|
||||
field=models.CharField(blank=True, max_length=30, verbose_name='Account number'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='provider',
|
||||
name='admin_contact',
|
||||
field=models.TextField(blank=True, verbose_name='Admin contact'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='provider',
|
||||
name='asn',
|
||||
field=dcim.fields.ASNField(blank=True, null=True, verbose_name='ASN'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='provider',
|
||||
name='noc_contact',
|
||||
field=models.TextField(blank=True, verbose_name='NOC contact'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='provider',
|
||||
name='portal_url',
|
||||
field=models.URLField(blank=True, verbose_name='Portal'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='circuit',
|
||||
name='status',
|
||||
field=models.PositiveSmallIntegerField(choices=[[2, 'Planned'], [3, 'Provisioning'], [1, 'Active'], [4, 'Offline'], [0, 'Deprovisioning'], [5, 'Decommissioned']], default=1),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='circuit',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='provider',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='circuittype',
|
||||
name='created',
|
||||
field=models.DateField(auto_now_add=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='circuittype',
|
||||
name='last_updated',
|
||||
field=models.DateTimeField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuit',
|
||||
name='created',
|
||||
field=models.DateField(auto_now_add=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuit',
|
||||
name='last_updated',
|
||||
field=models.DateTimeField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='provider',
|
||||
name='created',
|
||||
field=models.DateField(auto_now_add=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='provider',
|
||||
name='last_updated',
|
||||
field=models.DateTimeField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='circuittermination',
|
||||
name='connected_endpoint',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.Interface'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='circuittermination',
|
||||
name='connection_status',
|
||||
field=models.NullBooleanField(),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='circuittermination',
|
||||
name='cable',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.Cable'),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=circuit_terminations_to_cables,
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='circuittermination',
|
||||
name='interface',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='circuittermination',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuit',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='provider',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuit',
|
||||
name='status',
|
||||
field=models.CharField(default='active', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=circuit_status_to_slug,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='circuittype',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
]
|
||||
@@ -3,7 +3,7 @@ import sys
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
from dcim.constants import CONNECTION_STATUS_CONNECTED
|
||||
CONNECTION_STATUS_CONNECTED = True
|
||||
|
||||
|
||||
def circuit_terminations_to_cables(apps, schema_editor):
|
||||
|
||||
39
netbox/circuits/migrations/0016_3569_circuit_fields.py
Normal file
39
netbox/circuits/migrations/0016_3569_circuit_fields.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
CIRCUIT_STATUS_CHOICES = (
|
||||
(0, 'deprovisioning'),
|
||||
(1, 'active'),
|
||||
(2, 'planned'),
|
||||
(3, 'provisioning'),
|
||||
(4, 'offline'),
|
||||
(5, 'decommissioned')
|
||||
)
|
||||
|
||||
|
||||
def circuit_status_to_slug(apps, schema_editor):
|
||||
Circuit = apps.get_model('circuits', 'Circuit')
|
||||
for id, slug in CIRCUIT_STATUS_CHOICES:
|
||||
Circuit.objects.filter(status=str(id)).update(status=slug)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
dependencies = [
|
||||
('circuits', '0015_custom_tag_models'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
||||
# Circuit.status
|
||||
migrations.AlterField(
|
||||
model_name='circuit',
|
||||
name='status',
|
||||
field=models.CharField(default='active', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=circuit_status_to_slug
|
||||
),
|
||||
|
||||
]
|
||||
18
netbox/circuits/migrations/0017_circuittype_description.py
Normal file
18
netbox/circuits/migrations/0017_circuittype_description.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.2.6 on 2019-12-10 18:19
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('circuits', '0016_3569_circuit_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='circuittype',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
]
|
||||
@@ -3,13 +3,21 @@ from django.db import models
|
||||
from django.urls import reverse
|
||||
from taggit.managers import TaggableManager
|
||||
|
||||
from dcim.constants import CONNECTION_STATUS_CHOICES, STATUS_CLASSES
|
||||
from dcim.constants import CONNECTION_STATUS_CHOICES
|
||||
from dcim.fields import ASNField
|
||||
from dcim.models import CableTermination
|
||||
from extras.models import CustomFieldModel, ObjectChange, TaggedItem
|
||||
from utilities.models import ChangeLoggedModel
|
||||
from utilities.utils import serialize_object
|
||||
from .constants import *
|
||||
from .choices import *
|
||||
|
||||
|
||||
__all__ = (
|
||||
'Circuit',
|
||||
'CircuitTermination',
|
||||
'CircuitType',
|
||||
'Provider',
|
||||
)
|
||||
|
||||
|
||||
class Provider(ChangeLoggedModel, CustomFieldModel):
|
||||
@@ -57,7 +65,12 @@ class Provider(ChangeLoggedModel, CustomFieldModel):
|
||||
|
||||
tags = TaggableManager(through=TaggedItem)
|
||||
|
||||
csv_headers = ['name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments']
|
||||
csv_headers = [
|
||||
'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
|
||||
]
|
||||
clone_fields = [
|
||||
'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact',
|
||||
]
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
@@ -93,8 +106,12 @@ class CircuitType(ChangeLoggedModel):
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=100,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
csv_headers = ['name', 'slug']
|
||||
csv_headers = ['name', 'slug', 'description']
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
@@ -109,6 +126,7 @@ class CircuitType(ChangeLoggedModel):
|
||||
return (
|
||||
self.name,
|
||||
self.slug,
|
||||
self.description,
|
||||
)
|
||||
|
||||
|
||||
@@ -132,9 +150,10 @@ class Circuit(ChangeLoggedModel, CustomFieldModel):
|
||||
on_delete=models.PROTECT,
|
||||
related_name='circuits'
|
||||
)
|
||||
status = models.PositiveSmallIntegerField(
|
||||
choices=CIRCUIT_STATUS_CHOICES,
|
||||
default=CIRCUIT_STATUS_ACTIVE
|
||||
status = models.CharField(
|
||||
max_length=50,
|
||||
choices=CircuitStatusChoices,
|
||||
default=CircuitStatusChoices.STATUS_ACTIVE
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
@@ -170,6 +189,18 @@ class Circuit(ChangeLoggedModel, CustomFieldModel):
|
||||
csv_headers = [
|
||||
'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
|
||||
]
|
||||
clone_fields = [
|
||||
'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description',
|
||||
]
|
||||
|
||||
STATUS_CLASS_MAP = {
|
||||
CircuitStatusChoices.STATUS_DEPROVISIONING: 'warning',
|
||||
CircuitStatusChoices.STATUS_ACTIVE: 'success',
|
||||
CircuitStatusChoices.STATUS_PLANNED: 'info',
|
||||
CircuitStatusChoices.STATUS_PROVISIONING: 'primary',
|
||||
CircuitStatusChoices.STATUS_OFFLINE: 'danger',
|
||||
CircuitStatusChoices.STATUS_DECOMMISSIONED: 'default',
|
||||
}
|
||||
|
||||
class Meta:
|
||||
ordering = ['provider', 'cid']
|
||||
@@ -195,7 +226,7 @@ class Circuit(ChangeLoggedModel, CustomFieldModel):
|
||||
)
|
||||
|
||||
def get_status_class(self):
|
||||
return STATUS_CLASSES[self.status]
|
||||
return self.STATUS_CLASS_MAP.get(self.status)
|
||||
|
||||
def _get_termination(self, side):
|
||||
for ct in self.terminations.all():
|
||||
@@ -220,7 +251,7 @@ class CircuitTermination(CableTermination):
|
||||
)
|
||||
term_side = models.CharField(
|
||||
max_length=1,
|
||||
choices=TERM_SIDE_CHOICES,
|
||||
choices=CircuitTerminationSideChoices,
|
||||
verbose_name='Termination'
|
||||
)
|
||||
site = models.ForeignKey(
|
||||
|
||||
@@ -50,12 +50,14 @@ class CircuitTypeTable(BaseTable):
|
||||
name = tables.LinkColumn()
|
||||
circuit_count = tables.Column(verbose_name='Circuits')
|
||||
actions = tables.TemplateColumn(
|
||||
template_code=CIRCUITTYPE_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name=''
|
||||
template_code=CIRCUITTYPE_ACTIONS,
|
||||
attrs={'td': {'class': 'text-right noprint'}},
|
||||
verbose_name=''
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = CircuitType
|
||||
fields = ('pk', 'name', 'circuit_count', 'slug', 'actions')
|
||||
fields = ('pk', 'name', 'circuit_count', 'description', 'slug', 'actions')
|
||||
|
||||
|
||||
#
|
||||
|
||||
@@ -1,12 +1,35 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.urls import reverse
|
||||
from rest_framework import status
|
||||
|
||||
from circuits.constants import CIRCUIT_STATUS_ACTIVE, TERM_SIDE_A, TERM_SIDE_Z
|
||||
from circuits.choices import *
|
||||
from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
from dcim.models import Device, DeviceRole, DeviceType, Interface, Manufacturer, Site
|
||||
from extras.constants import GRAPH_TYPE_PROVIDER
|
||||
from dcim.models import Site
|
||||
from extras.models import Graph
|
||||
from utilities.testing import APITestCase
|
||||
from utilities.testing import APITestCase, choices_to_dict
|
||||
|
||||
|
||||
class AppTest(APITestCase):
|
||||
|
||||
def test_root(self):
|
||||
|
||||
url = reverse('circuits-api:api-root')
|
||||
response = self.client.get('{}?format=api'.format(url), **self.header)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_choices(self):
|
||||
|
||||
url = reverse('circuits-api:field-choice-list')
|
||||
response = self.client.get(url, **self.header)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Circuit
|
||||
self.assertEqual(choices_to_dict(response.data.get('circuit:status')), CircuitStatusChoices.as_dict())
|
||||
|
||||
# CircuitTermination
|
||||
self.assertEqual(choices_to_dict(response.data.get('circuit-termination:term_side')), CircuitTerminationSideChoices.as_dict())
|
||||
|
||||
|
||||
class ProviderTest(APITestCase):
|
||||
@@ -28,16 +51,20 @@ class ProviderTest(APITestCase):
|
||||
|
||||
def test_get_provider_graphs(self):
|
||||
|
||||
provider_ct = ContentType.objects.get(app_label='circuits', model='provider')
|
||||
self.graph1 = Graph.objects.create(
|
||||
type=GRAPH_TYPE_PROVIDER, name='Test Graph 1',
|
||||
type=provider_ct,
|
||||
name='Test Graph 1',
|
||||
source='http://example.com/graphs.py?provider={{ obj.slug }}&foo=1'
|
||||
)
|
||||
self.graph2 = Graph.objects.create(
|
||||
type=GRAPH_TYPE_PROVIDER, name='Test Graph 2',
|
||||
type=provider_ct,
|
||||
name='Test Graph 2',
|
||||
source='http://example.com/graphs.py?provider={{ obj.slug }}&foo=2'
|
||||
)
|
||||
self.graph3 = Graph.objects.create(
|
||||
type=GRAPH_TYPE_PROVIDER, name='Test Graph 3',
|
||||
type=provider_ct,
|
||||
name='Test Graph 3',
|
||||
source='http://example.com/graphs.py?provider={{ obj.slug }}&foo=3'
|
||||
)
|
||||
|
||||
@@ -250,7 +277,7 @@ class CircuitTest(APITestCase):
|
||||
'cid': 'TEST0004',
|
||||
'provider': self.provider1.pk,
|
||||
'type': self.circuittype1.pk,
|
||||
'status': CIRCUIT_STATUS_ACTIVE,
|
||||
'status': CircuitStatusChoices.STATUS_ACTIVE,
|
||||
}
|
||||
|
||||
url = reverse('circuits-api:circuit-list')
|
||||
@@ -270,19 +297,19 @@ class CircuitTest(APITestCase):
|
||||
'cid': 'TEST0004',
|
||||
'provider': self.provider1.pk,
|
||||
'type': self.circuittype1.pk,
|
||||
'status': CIRCUIT_STATUS_ACTIVE,
|
||||
'status': CircuitStatusChoices.STATUS_ACTIVE,
|
||||
},
|
||||
{
|
||||
'cid': 'TEST0005',
|
||||
'provider': self.provider1.pk,
|
||||
'type': self.circuittype1.pk,
|
||||
'status': CIRCUIT_STATUS_ACTIVE,
|
||||
'status': CircuitStatusChoices.STATUS_ACTIVE,
|
||||
},
|
||||
{
|
||||
'cid': 'TEST0006',
|
||||
'provider': self.provider1.pk,
|
||||
'type': self.circuittype1.pk,
|
||||
'status': CIRCUIT_STATUS_ACTIVE,
|
||||
'status': CircuitStatusChoices.STATUS_ACTIVE,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -336,16 +363,28 @@ class CircuitTerminationTest(APITestCase):
|
||||
self.circuit2 = Circuit.objects.create(cid='TEST0002', provider=provider, type=circuittype)
|
||||
self.circuit3 = Circuit.objects.create(cid='TEST0003', provider=provider, type=circuittype)
|
||||
self.circuittermination1 = CircuitTermination.objects.create(
|
||||
circuit=self.circuit1, term_side=TERM_SIDE_A, site=self.site1, port_speed=1000000
|
||||
circuit=self.circuit1,
|
||||
term_side=CircuitTerminationSideChoices.SIDE_A,
|
||||
site=self.site1,
|
||||
port_speed=1000000
|
||||
)
|
||||
self.circuittermination2 = CircuitTermination.objects.create(
|
||||
circuit=self.circuit1, term_side=TERM_SIDE_Z, site=self.site2, port_speed=1000000
|
||||
circuit=self.circuit1,
|
||||
term_side=CircuitTerminationSideChoices.SIDE_Z,
|
||||
site=self.site2,
|
||||
port_speed=1000000
|
||||
)
|
||||
self.circuittermination3 = CircuitTermination.objects.create(
|
||||
circuit=self.circuit2, term_side=TERM_SIDE_A, site=self.site1, port_speed=1000000
|
||||
circuit=self.circuit2,
|
||||
term_side=CircuitTerminationSideChoices.SIDE_A,
|
||||
site=self.site1,
|
||||
port_speed=1000000
|
||||
)
|
||||
self.circuittermination4 = CircuitTermination.objects.create(
|
||||
circuit=self.circuit2, term_side=TERM_SIDE_Z, site=self.site2, port_speed=1000000
|
||||
circuit=self.circuit2,
|
||||
term_side=CircuitTerminationSideChoices.SIDE_Z,
|
||||
site=self.site2,
|
||||
port_speed=1000000
|
||||
)
|
||||
|
||||
def test_get_circuittermination(self):
|
||||
@@ -366,7 +405,7 @@ class CircuitTerminationTest(APITestCase):
|
||||
|
||||
data = {
|
||||
'circuit': self.circuit3.pk,
|
||||
'term_side': TERM_SIDE_A,
|
||||
'term_side': CircuitTerminationSideChoices.SIDE_A,
|
||||
'site': self.site1.pk,
|
||||
'port_speed': 1000000,
|
||||
}
|
||||
@@ -385,12 +424,15 @@ class CircuitTerminationTest(APITestCase):
|
||||
def test_update_circuittermination(self):
|
||||
|
||||
circuittermination5 = CircuitTermination.objects.create(
|
||||
circuit=self.circuit3, term_side=TERM_SIDE_A, site=self.site1, port_speed=1000000
|
||||
circuit=self.circuit3,
|
||||
term_side=CircuitTerminationSideChoices.SIDE_A,
|
||||
site=self.site1,
|
||||
port_speed=1000000
|
||||
)
|
||||
|
||||
data = {
|
||||
'circuit': self.circuit3.pk,
|
||||
'term_side': TERM_SIDE_Z,
|
||||
'term_side': CircuitTerminationSideChoices.SIDE_Z,
|
||||
'site': self.site2.pk,
|
||||
'port_speed': 1000000,
|
||||
}
|
||||
|
||||
287
netbox/circuits/tests/test_filters.py
Normal file
287
netbox/circuits/tests/test_filters.py
Normal file
@@ -0,0 +1,287 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from circuits.choices import *
|
||||
from circuits.filters import *
|
||||
from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
from dcim.models import Region, Site
|
||||
|
||||
|
||||
class ProviderTestCase(TestCase):
|
||||
queryset = Provider.objects.all()
|
||||
filterset = ProviderFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
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.objects.bulk_create(providers)
|
||||
|
||||
regions = (
|
||||
Region(name='Test Region 1', slug='test-region-1'),
|
||||
Region(name='Test Region 2', slug='test-region-2'),
|
||||
)
|
||||
# Can't use bulk_create for models with MPTT fields
|
||||
for r in regions:
|
||||
r.save()
|
||||
|
||||
sites = (
|
||||
Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
|
||||
Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
|
||||
)
|
||||
Site.objects.bulk_create(sites)
|
||||
|
||||
circuit_types = (
|
||||
CircuitType(name='Test Circuit Type 1', slug='test-circuit-type-1'),
|
||||
CircuitType(name='Test Circuit Type 2', slug='test-circuit-type-2'),
|
||||
)
|
||||
CircuitType.objects.bulk_create(circuit_types)
|
||||
|
||||
circuits = (
|
||||
Circuit(provider=providers[0], type=circuit_types[0], cid='Test Circuit 1'),
|
||||
Circuit(provider=providers[1], type=circuit_types[1], cid='Test Circuit 1'),
|
||||
)
|
||||
Circuit.objects.bulk_create(circuits)
|
||||
|
||||
CircuitTermination.objects.bulk_create((
|
||||
CircuitTermination(circuit=circuits[0], site=sites[0], term_side='A', port_speed=1000),
|
||||
CircuitTermination(circuit=circuits[1], site=sites[0], term_side='A', port_speed=1000),
|
||||
))
|
||||
|
||||
def test_name(self):
|
||||
params = {'name': ['Provider 1', 'Provider 2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_slug(self):
|
||||
params = {'slug': ['provider-1', 'provider-2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_asn(self):
|
||||
params = {'asn': ['65001', '65002']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_account(self):
|
||||
params = {'account': ['1234', '2345']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_id__in(self):
|
||||
id_list = self.queryset.values_list('id', flat=True)[:3]
|
||||
params = {'id__in': ','.join([str(id) for id in id_list])}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
def test_site(self):
|
||||
sites = Site.objects.all()[:2]
|
||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'site': [sites[0].slug, sites[1].slug]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_region(self):
|
||||
regions = Region.objects.all()[:2]
|
||||
params = {'region_id': [regions[0].pk, regions[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
|
||||
class CircuitTypeTestCase(TestCase):
|
||||
queryset = CircuitType.objects.all()
|
||||
filterset = CircuitTypeFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
CircuitType.objects.bulk_create((
|
||||
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'),
|
||||
))
|
||||
|
||||
def test_id(self):
|
||||
params = {'id': [self.queryset.first().pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||
|
||||
def test_name(self):
|
||||
params = {'name': ['Circuit Type 1']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||
|
||||
def test_slug(self):
|
||||
params = {'slug': ['circuit-type-1']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||
|
||||
|
||||
class CircuitTestCase(TestCase):
|
||||
queryset = Circuit.objects.all()
|
||||
filterset = CircuitFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
regions = (
|
||||
Region(name='Test Region 1', slug='test-region-1'),
|
||||
Region(name='Test Region 2', slug='test-region-2'),
|
||||
Region(name='Test Region 3', slug='test-region-3'),
|
||||
)
|
||||
# Can't use bulk_create for models with MPTT fields
|
||||
for r in regions:
|
||||
r.save()
|
||||
|
||||
sites = (
|
||||
Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
|
||||
Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
|
||||
Site(name='Test Site 3', slug='test-site-3', region=regions[2]),
|
||||
)
|
||||
Site.objects.bulk_create(sites)
|
||||
|
||||
circuit_types = (
|
||||
CircuitType(name='Test Circuit Type 1', slug='test-circuit-type-1'),
|
||||
CircuitType(name='Test Circuit Type 2', slug='test-circuit-type-2'),
|
||||
)
|
||||
CircuitType.objects.bulk_create(circuit_types)
|
||||
|
||||
providers = (
|
||||
Provider(name='Provider 1', slug='provider-1'),
|
||||
Provider(name='Provider 2', slug='provider-2'),
|
||||
)
|
||||
Provider.objects.bulk_create(providers)
|
||||
|
||||
circuits = (
|
||||
Circuit(provider=providers[0], type=circuit_types[0], cid='Test Circuit 1', install_date='2020-01-01', commit_rate=1000, status=CircuitStatusChoices.STATUS_ACTIVE),
|
||||
Circuit(provider=providers[0], type=circuit_types[0], cid='Test Circuit 2', install_date='2020-01-02', commit_rate=2000, status=CircuitStatusChoices.STATUS_ACTIVE),
|
||||
Circuit(provider=providers[0], type=circuit_types[0], cid='Test Circuit 3', install_date='2020-01-03', commit_rate=3000, status=CircuitStatusChoices.STATUS_PLANNED),
|
||||
Circuit(provider=providers[1], type=circuit_types[1], cid='Test Circuit 4', install_date='2020-01-04', commit_rate=4000, status=CircuitStatusChoices.STATUS_PLANNED),
|
||||
Circuit(provider=providers[1], type=circuit_types[1], cid='Test Circuit 5', install_date='2020-01-05', commit_rate=5000, status=CircuitStatusChoices.STATUS_OFFLINE),
|
||||
Circuit(provider=providers[1], type=circuit_types[1], cid='Test Circuit 6', install_date='2020-01-06', commit_rate=6000, status=CircuitStatusChoices.STATUS_OFFLINE),
|
||||
)
|
||||
Circuit.objects.bulk_create(circuits)
|
||||
|
||||
circuit_terminations = ((
|
||||
CircuitTermination(circuit=circuits[0], site=sites[0], term_side='A', port_speed=1000),
|
||||
CircuitTermination(circuit=circuits[1], site=sites[1], term_side='A', port_speed=1000),
|
||||
CircuitTermination(circuit=circuits[2], site=sites[2], term_side='A', port_speed=1000),
|
||||
))
|
||||
CircuitTermination.objects.bulk_create(circuit_terminations)
|
||||
|
||||
def test_cid(self):
|
||||
params = {'cid': ['Test Circuit 1', 'Test Circuit 2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_install_date(self):
|
||||
params = {'install_date': ['2020-01-01', '2020-01-02']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_commit_rate(self):
|
||||
params = {'commit_rate': ['1000', '2000']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_id__in(self):
|
||||
id_list = self.queryset.values_list('id', flat=True)[:3]
|
||||
params = {'id__in': ','.join([str(id) for id in id_list])}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
def test_provider(self):
|
||||
provider = Provider.objects.first()
|
||||
params = {'provider_id': [provider.pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
params = {'provider': [provider.slug]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
def test_type(self):
|
||||
circuit_type = CircuitType.objects.first()
|
||||
params = {'type_id': [circuit_type.pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
params = {'type': [circuit_type.slug]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
def test_status(self):
|
||||
params = {'status': [CircuitStatusChoices.STATUS_ACTIVE, CircuitStatusChoices.STATUS_PLANNED]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
|
||||
def test_region(self):
|
||||
regions = Region.objects.all()[:2]
|
||||
params = {'region_id': [regions[0].pk, regions[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_site(self):
|
||||
sites = Site.objects.all()[:2]
|
||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'site': [sites[0].slug, sites[1].slug]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
|
||||
class CircuitTerminationTestCase(TestCase):
|
||||
queryset = CircuitTermination.objects.all()
|
||||
filterset = CircuitTerminationFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
sites = (
|
||||
Site(name='Test Site 1', slug='test-site-1'),
|
||||
Site(name='Test Site 2', slug='test-site-2'),
|
||||
Site(name='Test Site 3', slug='test-site-3'),
|
||||
)
|
||||
Site.objects.bulk_create(sites)
|
||||
|
||||
circuit_types = (
|
||||
CircuitType(name='Test Circuit Type 1', slug='test-circuit-type-1'),
|
||||
)
|
||||
CircuitType.objects.bulk_create(circuit_types)
|
||||
|
||||
providers = (
|
||||
Provider(name='Provider 1', slug='provider-1'),
|
||||
)
|
||||
Provider.objects.bulk_create(providers)
|
||||
|
||||
circuits = (
|
||||
Circuit(provider=providers[0], type=circuit_types[0], cid='Test Circuit 1'),
|
||||
Circuit(provider=providers[0], type=circuit_types[0], cid='Test Circuit 2'),
|
||||
Circuit(provider=providers[0], type=circuit_types[0], cid='Test Circuit 3'),
|
||||
)
|
||||
Circuit.objects.bulk_create(circuits)
|
||||
|
||||
circuit_terminations = ((
|
||||
CircuitTermination(circuit=circuits[0], site=sites[0], term_side='A', port_speed=1000, upstream_speed=1000, xconnect_id='ABC'),
|
||||
CircuitTermination(circuit=circuits[0], site=sites[1], term_side='Z', port_speed=1000, upstream_speed=1000, xconnect_id='DEF'),
|
||||
CircuitTermination(circuit=circuits[1], site=sites[1], term_side='A', port_speed=2000, upstream_speed=2000, xconnect_id='GHI'),
|
||||
CircuitTermination(circuit=circuits[1], site=sites[2], term_side='Z', port_speed=2000, upstream_speed=2000, xconnect_id='JKL'),
|
||||
CircuitTermination(circuit=circuits[2], site=sites[2], term_side='A', port_speed=3000, upstream_speed=3000, xconnect_id='MNO'),
|
||||
CircuitTermination(circuit=circuits[2], site=sites[0], term_side='Z', port_speed=3000, upstream_speed=3000, xconnect_id='PQR'),
|
||||
))
|
||||
CircuitTermination.objects.bulk_create(circuit_terminations)
|
||||
|
||||
def test_term_side(self):
|
||||
params = {'term_side': 'A'}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
def test_port_speed(self):
|
||||
params = {'port_speed': ['1000', '2000']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
|
||||
def test_upstream_speed(self):
|
||||
params = {'upstream_speed': ['1000', '2000']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
|
||||
def test_xconnect_id(self):
|
||||
params = {'xconnect_id': ['ABC', 'DEF']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_circuit_id(self):
|
||||
circuits = Circuit.objects.all()[:2]
|
||||
params = {'circuit_id': [circuits[0].pk, circuits[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
|
||||
def test_site(self):
|
||||
sites = Site.objects.all()[:2]
|
||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
params = {'site': [sites[0].slug, sites[1].slug]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
@@ -10,7 +10,12 @@ from utilities.testing import create_test_user
|
||||
class ProviderTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
user = create_test_user(permissions=['circuits.view_provider'])
|
||||
user = create_test_user(
|
||||
permissions=[
|
||||
'circuits.view_provider',
|
||||
'circuits.add_provider',
|
||||
]
|
||||
)
|
||||
self.client = Client()
|
||||
self.client.force_login(user)
|
||||
|
||||
@@ -36,11 +41,30 @@ class ProviderTestCase(TestCase):
|
||||
response = self.client.get(provider.get_absolute_url())
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_provider_import(self):
|
||||
|
||||
csv_data = (
|
||||
"name,slug",
|
||||
"Provider 4,provider-4",
|
||||
"Provider 5,provider-5",
|
||||
"Provider 6,provider-6",
|
||||
)
|
||||
|
||||
response = self.client.post(reverse('circuits:provider_import'), {'csv': '\n'.join(csv_data)})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Provider.objects.count(), 6)
|
||||
|
||||
|
||||
class CircuitTypeTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
user = create_test_user(permissions=['circuits.view_circuittype'])
|
||||
user = create_test_user(
|
||||
permissions=[
|
||||
'circuits.view_circuittype',
|
||||
'circuits.add_circuittype',
|
||||
]
|
||||
)
|
||||
self.client = Client()
|
||||
self.client.force_login(user)
|
||||
|
||||
@@ -57,11 +81,30 @@ class CircuitTypeTestCase(TestCase):
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_circuittype_import(self):
|
||||
|
||||
csv_data = (
|
||||
"name,slug",
|
||||
"Circuit Type 4,circuit-type-4",
|
||||
"Circuit Type 5,circuit-type-5",
|
||||
"Circuit Type 6,circuit-type-6",
|
||||
)
|
||||
|
||||
response = self.client.post(reverse('circuits:circuittype_import'), {'csv': '\n'.join(csv_data)})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(CircuitType.objects.count(), 6)
|
||||
|
||||
|
||||
class CircuitTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
user = create_test_user(permissions=['circuits.view_circuit'])
|
||||
user = create_test_user(
|
||||
permissions=[
|
||||
'circuits.view_circuit',
|
||||
'circuits.add_circuit',
|
||||
]
|
||||
)
|
||||
self.client = Client()
|
||||
self.client.force_login(user)
|
||||
|
||||
@@ -93,3 +136,17 @@ class CircuitTestCase(TestCase):
|
||||
circuit = Circuit.objects.first()
|
||||
response = self.client.get(circuit.get_absolute_url())
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_circuit_import(self):
|
||||
|
||||
csv_data = (
|
||||
"cid,provider,type",
|
||||
"Circuit 4,Provider 1,Circuit Type 1",
|
||||
"Circuit 5,Provider 1,Circuit Type 1",
|
||||
"Circuit 6,Provider 1,Circuit Type 1",
|
||||
)
|
||||
|
||||
response = self.client.post(reverse('circuits:circuit_import'), {'csv': '\n'.join(csv_data)})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Circuit.objects.count(), 6)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
@@ -5,14 +6,16 @@ from django.db import transaction
|
||||
from django.db.models import Count, OuterRef, Subquery
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.views.generic import View
|
||||
from django_tables2 import RequestConfig
|
||||
|
||||
from extras.models import Graph, GRAPH_TYPE_PROVIDER
|
||||
from extras.models import Graph
|
||||
from utilities.forms import ConfirmationForm
|
||||
from utilities.paginator import EnhancedPaginator
|
||||
from utilities.views import (
|
||||
BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
|
||||
)
|
||||
from . import filters, forms, tables
|
||||
from .constants import TERM_SIDE_A, TERM_SIDE_Z
|
||||
from .choices import CircuitTerminationSideChoices
|
||||
from .models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
|
||||
|
||||
@@ -23,8 +26,8 @@ from .models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
class ProviderListView(PermissionRequiredMixin, ObjectListView):
|
||||
permission_required = 'circuits.view_provider'
|
||||
queryset = Provider.objects.annotate(count_circuits=Count('circuits'))
|
||||
filter = filters.ProviderFilter
|
||||
filter_form = forms.ProviderFilterForm
|
||||
filterset = filters.ProviderFilterSet
|
||||
filterset_form = forms.ProviderFilterForm
|
||||
table = tables.ProviderDetailTable
|
||||
template_name = 'circuits/provider_list.html'
|
||||
|
||||
@@ -36,11 +39,20 @@ class ProviderView(PermissionRequiredMixin, View):
|
||||
|
||||
provider = get_object_or_404(Provider, slug=slug)
|
||||
circuits = Circuit.objects.filter(provider=provider).prefetch_related('type', 'tenant', 'terminations__site')
|
||||
show_graphs = Graph.objects.filter(type=GRAPH_TYPE_PROVIDER).exists()
|
||||
show_graphs = Graph.objects.filter(type__model='provider').exists()
|
||||
|
||||
circuits_table = tables.CircuitTable(circuits, orderable=False)
|
||||
circuits_table.columns.hide('provider')
|
||||
|
||||
paginate = {
|
||||
'paginator_class': EnhancedPaginator,
|
||||
'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
|
||||
}
|
||||
RequestConfig(request, paginate).configure(circuits_table)
|
||||
|
||||
return render(request, 'circuits/provider.html', {
|
||||
'provider': provider,
|
||||
'circuits': circuits,
|
||||
'circuits_table': circuits_table,
|
||||
'show_graphs': show_graphs,
|
||||
})
|
||||
|
||||
@@ -73,7 +85,7 @@ class ProviderBulkImportView(PermissionRequiredMixin, BulkImportView):
|
||||
class ProviderBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
permission_required = 'circuits.change_provider'
|
||||
queryset = Provider.objects.all()
|
||||
filter = filters.ProviderFilter
|
||||
filterset = filters.ProviderFilterSet
|
||||
table = tables.ProviderTable
|
||||
form = forms.ProviderBulkEditForm
|
||||
default_return_url = 'circuits:provider_list'
|
||||
@@ -82,7 +94,7 @@ class ProviderBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
class ProviderBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
permission_required = 'circuits.delete_provider'
|
||||
queryset = Provider.objects.all()
|
||||
filter = filters.ProviderFilter
|
||||
filterset = filters.ProviderFilterSet
|
||||
table = tables.ProviderTable
|
||||
default_return_url = 'circuits:provider_list'
|
||||
|
||||
@@ -136,8 +148,8 @@ class CircuitListView(PermissionRequiredMixin, ObjectListView):
|
||||
a_side=Subquery(_terminations.filter(term_side='A').values('site__name')[:1]),
|
||||
z_side=Subquery(_terminations.filter(term_side='Z').values('site__name')[:1]),
|
||||
)
|
||||
filter = filters.CircuitFilter
|
||||
filter_form = forms.CircuitFilterForm
|
||||
filterset = filters.CircuitFilterSet
|
||||
filterset_form = forms.CircuitFilterForm
|
||||
table = tables.CircuitTable
|
||||
template_name = 'circuits/circuit_list.html'
|
||||
|
||||
@@ -151,12 +163,12 @@ class CircuitView(PermissionRequiredMixin, View):
|
||||
termination_a = CircuitTermination.objects.prefetch_related(
|
||||
'site__region', 'connected_endpoint__device'
|
||||
).filter(
|
||||
circuit=circuit, term_side=TERM_SIDE_A
|
||||
circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_A
|
||||
).first()
|
||||
termination_z = CircuitTermination.objects.prefetch_related(
|
||||
'site__region', 'connected_endpoint__device'
|
||||
).filter(
|
||||
circuit=circuit, term_side=TERM_SIDE_Z
|
||||
circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_Z
|
||||
).first()
|
||||
|
||||
return render(request, 'circuits/circuit.html', {
|
||||
@@ -194,7 +206,7 @@ class CircuitBulkImportView(PermissionRequiredMixin, BulkImportView):
|
||||
class CircuitBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
permission_required = 'circuits.change_circuit'
|
||||
queryset = Circuit.objects.prefetch_related('provider', 'type', 'tenant').prefetch_related('terminations__site')
|
||||
filter = filters.CircuitFilter
|
||||
filterset = filters.CircuitFilterSet
|
||||
table = tables.CircuitTable
|
||||
form = forms.CircuitBulkEditForm
|
||||
default_return_url = 'circuits:circuit_list'
|
||||
@@ -203,7 +215,7 @@ class CircuitBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
class CircuitBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
permission_required = 'circuits.delete_circuit'
|
||||
queryset = Circuit.objects.prefetch_related('provider', 'type', 'tenant').prefetch_related('terminations__site')
|
||||
filter = filters.CircuitFilter
|
||||
filterset = filters.CircuitFilterSet
|
||||
table = tables.CircuitTable
|
||||
default_return_url = 'circuits:circuit_list'
|
||||
|
||||
@@ -212,8 +224,12 @@ class CircuitBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
def circuit_terminations_swap(request, pk):
|
||||
|
||||
circuit = get_object_or_404(Circuit, pk=pk)
|
||||
termination_a = CircuitTermination.objects.filter(circuit=circuit, term_side=TERM_SIDE_A).first()
|
||||
termination_z = CircuitTermination.objects.filter(circuit=circuit, term_side=TERM_SIDE_Z).first()
|
||||
termination_a = CircuitTermination.objects.filter(
|
||||
circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_A
|
||||
).first()
|
||||
termination_z = CircuitTermination.objects.filter(
|
||||
circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_Z
|
||||
).first()
|
||||
if not termination_a and not termination_z:
|
||||
messages.error(request, "No terminations have been defined for circuit {}.".format(circuit))
|
||||
return redirect('circuits:circuit', pk=circuit.pk)
|
||||
|
||||
@@ -4,6 +4,7 @@ from rest_framework import serializers
|
||||
from rest_framework.validators import UniqueTogetherValidator
|
||||
from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
|
||||
|
||||
from dcim.choices import *
|
||||
from dcim.constants import *
|
||||
from dcim.models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
@@ -67,7 +68,7 @@ class RegionSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
class SiteSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
status = ChoiceField(choices=SITE_STATUS_CHOICES, required=False)
|
||||
status = ChoiceField(choices=SiteStatusChoices, required=False)
|
||||
region = NestedRegionSerializer(required=False, allow_null=True)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
time_zone = TimeZoneField(required=False)
|
||||
@@ -107,18 +108,18 @@ class RackRoleSerializer(ValidatedModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = RackRole
|
||||
fields = ['id', 'name', 'slug', 'color', 'rack_count']
|
||||
fields = ['id', 'name', 'slug', 'color', 'description', 'rack_count']
|
||||
|
||||
|
||||
class RackSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
site = NestedSiteSerializer()
|
||||
group = NestedRackGroupSerializer(required=False, allow_null=True, default=None)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
status = ChoiceField(choices=RACK_STATUS_CHOICES, required=False)
|
||||
status = ChoiceField(choices=RackStatusChoices, required=False)
|
||||
role = NestedRackRoleSerializer(required=False, allow_null=True)
|
||||
type = ChoiceField(choices=RACK_TYPE_CHOICES, required=False, allow_null=True)
|
||||
width = ChoiceField(choices=RACK_WIDTH_CHOICES, required=False)
|
||||
outer_unit = ChoiceField(choices=RACK_DIMENSION_UNIT_CHOICES, required=False)
|
||||
type = ChoiceField(choices=RackTypeChoices, required=False, allow_null=True)
|
||||
width = ChoiceField(choices=RackWidthChoices, required=False)
|
||||
outer_unit = ChoiceField(choices=RackDimensionUnitChoices, required=False)
|
||||
tags = TagListSerializerField(required=False)
|
||||
device_count = serializers.IntegerField(read_only=True)
|
||||
powerfeed_count = serializers.IntegerField(read_only=True)
|
||||
@@ -156,7 +157,7 @@ class RackUnitSerializer(serializers.Serializer):
|
||||
"""
|
||||
id = serializers.IntegerField(read_only=True)
|
||||
name = serializers.CharField(read_only=True)
|
||||
face = serializers.IntegerField(read_only=True)
|
||||
face = ChoiceField(choices=DeviceFaceChoices, read_only=True)
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
|
||||
|
||||
@@ -170,6 +171,31 @@ class RackReservationSerializer(ValidatedModelSerializer):
|
||||
fields = ['id', 'rack', 'units', 'created', 'user', 'tenant', 'description']
|
||||
|
||||
|
||||
class RackElevationDetailFilterSerializer(serializers.Serializer):
|
||||
face = serializers.ChoiceField(
|
||||
choices=DeviceFaceChoices,
|
||||
default=DeviceFaceChoices.FACE_FRONT
|
||||
)
|
||||
render = serializers.ChoiceField(
|
||||
choices=RackElevationDetailRenderChoices,
|
||||
default=RackElevationDetailRenderChoices.RENDER_JSON
|
||||
)
|
||||
unit_width = serializers.IntegerField(
|
||||
default=RACK_ELEVATION_UNIT_WIDTH_DEFAULT
|
||||
)
|
||||
unit_height = serializers.IntegerField(
|
||||
default=RACK_ELEVATION_UNIT_HEIGHT_DEFAULT
|
||||
)
|
||||
exclude = serializers.IntegerField(
|
||||
required=False,
|
||||
default=None
|
||||
)
|
||||
expand_devices = serializers.BooleanField(
|
||||
required=False,
|
||||
default=True
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Device types
|
||||
#
|
||||
@@ -186,7 +212,7 @@ class ManufacturerSerializer(ValidatedModelSerializer):
|
||||
|
||||
class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
manufacturer = NestedManufacturerSerializer()
|
||||
subdevice_role = ChoiceField(choices=SUBDEVICE_ROLE_CHOICES, required=False, allow_null=True)
|
||||
subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, required=False, allow_null=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
device_count = serializers.IntegerField(read_only=True)
|
||||
|
||||
@@ -200,58 +226,72 @@ class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
|
||||
class ConsolePortTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
type = ChoiceField(
|
||||
choices=ConsolePortTypeChoices,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ConsolePortTemplate
|
||||
fields = ['id', 'device_type', 'name']
|
||||
fields = ['id', 'device_type', 'name', 'type']
|
||||
|
||||
|
||||
class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
type = ChoiceField(
|
||||
choices=ConsolePortTypeChoices,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ConsoleServerPortTemplate
|
||||
fields = ['id', 'device_type', 'name']
|
||||
fields = ['id', 'device_type', 'name', 'type']
|
||||
|
||||
|
||||
class PowerPortTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
type = ChoiceField(
|
||||
choices=PowerPortTypeChoices,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerPortTemplate
|
||||
fields = ['id', 'device_type', 'name', 'maximum_draw', 'allocated_draw']
|
||||
fields = ['id', 'device_type', 'name', 'type', 'maximum_draw', 'allocated_draw']
|
||||
|
||||
|
||||
class PowerOutletTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
type = ChoiceField(
|
||||
choices=PowerOutletTypeChoices,
|
||||
required=False
|
||||
)
|
||||
power_port = PowerPortTemplateSerializer(
|
||||
required=False
|
||||
)
|
||||
feed_leg = ChoiceField(
|
||||
choices=POWERFEED_LEG_CHOICES,
|
||||
choices=PowerOutletFeedLegChoices,
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerOutletTemplate
|
||||
fields = ['id', 'device_type', 'name', 'power_port', 'feed_leg']
|
||||
fields = ['id', 'device_type', 'name', 'type', 'power_port', 'feed_leg']
|
||||
|
||||
|
||||
class InterfaceTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
type = ChoiceField(choices=IFACE_TYPE_CHOICES, required=False)
|
||||
# TODO: Remove in v2.7 (backward-compatibility for form_factor)
|
||||
form_factor = ChoiceField(choices=IFACE_TYPE_CHOICES, required=False)
|
||||
type = ChoiceField(choices=InterfaceTypeChoices, required=False)
|
||||
|
||||
class Meta:
|
||||
model = InterfaceTemplate
|
||||
fields = ['id', 'device_type', 'name', 'type', 'form_factor', 'mgmt_only']
|
||||
fields = ['id', 'device_type', 'name', 'type', 'mgmt_only']
|
||||
|
||||
|
||||
class RearPortTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
type = ChoiceField(choices=PORT_TYPE_CHOICES)
|
||||
type = ChoiceField(choices=PortTypeChoices)
|
||||
|
||||
class Meta:
|
||||
model = RearPortTemplate
|
||||
@@ -260,7 +300,7 @@ class RearPortTemplateSerializer(ValidatedModelSerializer):
|
||||
|
||||
class FrontPortTemplateSerializer(ValidatedModelSerializer):
|
||||
device_type = NestedDeviceTypeSerializer()
|
||||
type = ChoiceField(choices=PORT_TYPE_CHOICES)
|
||||
type = ChoiceField(choices=PortTypeChoices)
|
||||
rear_port = NestedRearPortTemplateSerializer()
|
||||
|
||||
class Meta:
|
||||
@@ -286,7 +326,9 @@ class DeviceRoleSerializer(ValidatedModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = DeviceRole
|
||||
fields = ['id', 'name', 'slug', 'color', 'vm_role', 'device_count', 'virtualmachine_count']
|
||||
fields = [
|
||||
'id', 'name', 'slug', 'color', 'vm_role', 'description', 'device_count', 'virtualmachine_count',
|
||||
]
|
||||
|
||||
|
||||
class PlatformSerializer(ValidatedModelSerializer):
|
||||
@@ -309,8 +351,8 @@ class DeviceSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
platform = NestedPlatformSerializer(required=False, allow_null=True)
|
||||
site = NestedSiteSerializer()
|
||||
rack = NestedRackSerializer(required=False, allow_null=True)
|
||||
face = ChoiceField(choices=RACK_FACE_CHOICES, required=False, allow_null=True)
|
||||
status = ChoiceField(choices=DEVICE_STATUS_CHOICES, required=False)
|
||||
face = ChoiceField(choices=DeviceFaceChoices, required=False, allow_null=True)
|
||||
status = ChoiceField(choices=DeviceStatusChoices, required=False)
|
||||
primary_ip = NestedIPAddressSerializer(read_only=True)
|
||||
primary_ip4 = NestedIPAddressSerializer(required=False, allow_null=True)
|
||||
primary_ip6 = NestedIPAddressSerializer(required=False, allow_null=True)
|
||||
@@ -370,39 +412,55 @@ class DeviceWithConfigContextSerializer(DeviceSerializer):
|
||||
return obj.get_config_context()
|
||||
|
||||
|
||||
class DeviceNAPALMSerializer(serializers.Serializer):
|
||||
method = serializers.DictField()
|
||||
|
||||
|
||||
class ConsoleServerPortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
type = ChoiceField(
|
||||
choices=ConsolePortTypeChoices,
|
||||
required=False
|
||||
)
|
||||
cable = NestedCableSerializer(read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = ConsoleServerPort
|
||||
fields = [
|
||||
'id', 'device', 'name', 'description', 'connected_endpoint_type', 'connected_endpoint', 'connection_status',
|
||||
'cable', 'tags',
|
||||
'id', 'device', 'name', 'type', 'description', 'connected_endpoint_type', 'connected_endpoint',
|
||||
'connection_status', 'cable', 'tags',
|
||||
]
|
||||
|
||||
|
||||
class ConsolePortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
type = ChoiceField(
|
||||
choices=ConsolePortTypeChoices,
|
||||
required=False
|
||||
)
|
||||
cable = NestedCableSerializer(read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = ConsolePort
|
||||
fields = [
|
||||
'id', 'device', 'name', 'description', 'connected_endpoint_type', 'connected_endpoint', 'connection_status',
|
||||
'cable', 'tags',
|
||||
'id', 'device', 'name', 'type', 'description', 'connected_endpoint_type', 'connected_endpoint',
|
||||
'connection_status', 'cable', 'tags',
|
||||
]
|
||||
|
||||
|
||||
class PowerOutletSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
type = ChoiceField(
|
||||
choices=PowerOutletTypeChoices,
|
||||
required=False
|
||||
)
|
||||
power_port = NestedPowerPortSerializer(
|
||||
required=False
|
||||
)
|
||||
feed_leg = ChoiceField(
|
||||
choices=POWERFEED_LEG_CHOICES,
|
||||
choices=PowerOutletFeedLegChoices,
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
@@ -416,31 +474,33 @@ class PowerOutletSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
||||
class Meta:
|
||||
model = PowerOutlet
|
||||
fields = [
|
||||
'id', 'device', 'name', 'power_port', 'feed_leg', 'description', 'connected_endpoint_type',
|
||||
'id', 'device', 'name', 'type', 'power_port', 'feed_leg', 'description', 'connected_endpoint_type',
|
||||
'connected_endpoint', 'connection_status', 'cable', 'tags',
|
||||
]
|
||||
|
||||
|
||||
class PowerPortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
type = ChoiceField(
|
||||
choices=PowerPortTypeChoices,
|
||||
required=False
|
||||
)
|
||||
cable = NestedCableSerializer(read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = PowerPort
|
||||
fields = [
|
||||
'id', 'device', 'name', 'maximum_draw', 'allocated_draw', 'description', 'connected_endpoint_type',
|
||||
'id', 'device', 'name', 'type', 'maximum_draw', 'allocated_draw', 'description', 'connected_endpoint_type',
|
||||
'connected_endpoint', 'connection_status', 'cable', 'tags',
|
||||
]
|
||||
|
||||
|
||||
class InterfaceSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
type = ChoiceField(choices=IFACE_TYPE_CHOICES, required=False)
|
||||
# TODO: Remove in v2.7 (backward-compatibility for form_factor)
|
||||
form_factor = ChoiceField(choices=IFACE_TYPE_CHOICES, required=False)
|
||||
type = ChoiceField(choices=InterfaceTypeChoices, required=False)
|
||||
lag = NestedInterfaceSerializer(required=False, allow_null=True)
|
||||
mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False, allow_null=True)
|
||||
mode = ChoiceField(choices=InterfaceModeChoices, required=False, allow_null=True)
|
||||
untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
|
||||
tagged_vlans = SerializedPKRelatedField(
|
||||
queryset=VLAN.objects.all(),
|
||||
@@ -454,9 +514,9 @@ class InterfaceSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
||||
class Meta:
|
||||
model = Interface
|
||||
fields = [
|
||||
'id', 'device', 'name', 'type', 'form_factor', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only',
|
||||
'description', 'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable', 'mode',
|
||||
'untagged_vlan', 'tagged_vlans', 'tags', 'count_ipaddresses',
|
||||
'id', 'device', 'name', 'type', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only', 'description',
|
||||
'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable', 'mode', 'untagged_vlan',
|
||||
'tagged_vlans', 'tags', 'count_ipaddresses',
|
||||
]
|
||||
|
||||
# TODO: This validation should be handled by Interface.clean()
|
||||
@@ -482,7 +542,7 @@ class InterfaceSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
||||
|
||||
class RearPortSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
type = ChoiceField(choices=PORT_TYPE_CHOICES)
|
||||
type = ChoiceField(choices=PortTypeChoices)
|
||||
cable = NestedCableSerializer(read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
@@ -504,7 +564,7 @@ class FrontPortRearPortSerializer(WritableNestedSerializer):
|
||||
|
||||
class FrontPortSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
type = ChoiceField(choices=PORT_TYPE_CHOICES)
|
||||
type = ChoiceField(choices=PortTypeChoices)
|
||||
rear_port = FrontPortRearPortSerializer()
|
||||
cable = NestedCableSerializer(read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
@@ -549,15 +609,15 @@ class InventoryItemSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
|
||||
class CableSerializer(ValidatedModelSerializer):
|
||||
termination_a_type = ContentTypeField(
|
||||
queryset=ContentType.objects.filter(model__in=CABLE_TERMINATION_TYPES)
|
||||
queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
|
||||
)
|
||||
termination_b_type = ContentTypeField(
|
||||
queryset=ContentType.objects.filter(model__in=CABLE_TERMINATION_TYPES)
|
||||
queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
|
||||
)
|
||||
termination_a = serializers.SerializerMethodField(read_only=True)
|
||||
termination_b = serializers.SerializerMethodField(read_only=True)
|
||||
status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, required=False)
|
||||
length_unit = ChoiceField(choices=CABLE_LENGTH_UNIT_CHOICES, required=False, allow_null=True)
|
||||
status = ChoiceField(choices=CableStatusChoices, required=False)
|
||||
length_unit = ChoiceField(choices=CableLengthUnitChoices, required=False, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = Cable
|
||||
@@ -662,20 +722,20 @@ class PowerFeedSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
default=None
|
||||
)
|
||||
type = ChoiceField(
|
||||
choices=POWERFEED_TYPE_CHOICES,
|
||||
default=POWERFEED_TYPE_PRIMARY
|
||||
choices=PowerFeedTypeChoices,
|
||||
default=PowerFeedTypeChoices.TYPE_PRIMARY
|
||||
)
|
||||
status = ChoiceField(
|
||||
choices=POWERFEED_STATUS_CHOICES,
|
||||
default=POWERFEED_STATUS_ACTIVE
|
||||
choices=PowerFeedStatusChoices,
|
||||
default=PowerFeedStatusChoices.STATUS_ACTIVE
|
||||
)
|
||||
supply = ChoiceField(
|
||||
choices=POWERFEED_SUPPLY_CHOICES,
|
||||
default=POWERFEED_SUPPLY_AC
|
||||
choices=PowerFeedSupplyChoices,
|
||||
default=PowerFeedSupplyChoices.SUPPLY_AC
|
||||
)
|
||||
phase = ChoiceField(
|
||||
choices=POWERFEED_PHASE_CHOICES,
|
||||
default=POWERFEED_PHASE_SINGLE
|
||||
choices=PowerFeedPhaseChoices,
|
||||
default=PowerFeedPhaseChoices.PHASE_SINGLE
|
||||
)
|
||||
tags = TagListSerializerField(
|
||||
required=False
|
||||
|
||||
@@ -2,7 +2,7 @@ from collections import OrderedDict
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models import Count, F
|
||||
from django.http import HttpResponseForbidden
|
||||
from django.http import HttpResponseForbidden, HttpResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.openapi import Parameter
|
||||
@@ -23,7 +23,6 @@ from dcim.models import (
|
||||
)
|
||||
from extras.api.serializers import RenderedGraphSerializer
|
||||
from extras.api.views import CustomFieldModelViewSet
|
||||
from extras.constants import GRAPH_TYPE_DEVICE, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
|
||||
from extras.models import Graph
|
||||
from ipam.models import Prefix, VLAN
|
||||
from utilities.api import (
|
||||
@@ -41,21 +40,26 @@ from .exceptions import MissingFilterException
|
||||
|
||||
class DCIMFieldChoicesViewSet(FieldChoicesViewSet):
|
||||
fields = (
|
||||
(Cable, ['length_unit', 'status', 'termination_a_type', 'termination_b_type', 'type']),
|
||||
(ConsolePort, ['connection_status']),
|
||||
(Device, ['face', 'status']),
|
||||
(DeviceType, ['subdevice_role']),
|
||||
(FrontPort, ['type']),
|
||||
(FrontPortTemplate, ['type']),
|
||||
(Interface, ['type', 'mode']),
|
||||
(InterfaceTemplate, ['type']),
|
||||
(PowerOutlet, ['feed_leg']),
|
||||
(PowerOutletTemplate, ['feed_leg']),
|
||||
(PowerPort, ['connection_status']),
|
||||
(Rack, ['outer_unit', 'status', 'type', 'width']),
|
||||
(RearPort, ['type']),
|
||||
(RearPortTemplate, ['type']),
|
||||
(Site, ['status']),
|
||||
(serializers.CableSerializer, ['length_unit', 'status', 'termination_a_type', 'termination_b_type', 'type']),
|
||||
(serializers.ConsolePortSerializer, ['type', 'connection_status']),
|
||||
(serializers.ConsolePortTemplateSerializer, ['type']),
|
||||
(serializers.ConsoleServerPortSerializer, ['type']),
|
||||
(serializers.ConsoleServerPortTemplateSerializer, ['type']),
|
||||
(serializers.DeviceSerializer, ['face', 'status']),
|
||||
(serializers.DeviceTypeSerializer, ['subdevice_role']),
|
||||
(serializers.FrontPortSerializer, ['type']),
|
||||
(serializers.FrontPortTemplateSerializer, ['type']),
|
||||
(serializers.InterfaceSerializer, ['type', 'mode']),
|
||||
(serializers.InterfaceTemplateSerializer, ['type']),
|
||||
(serializers.PowerFeedSerializer, ['phase', 'status', 'supply', 'type']),
|
||||
(serializers.PowerOutletSerializer, ['type', 'feed_leg']),
|
||||
(serializers.PowerOutletTemplateSerializer, ['type', 'feed_leg']),
|
||||
(serializers.PowerPortSerializer, ['type', 'connection_status']),
|
||||
(serializers.PowerPortTemplateSerializer, ['type']),
|
||||
(serializers.RackSerializer, ['outer_unit', 'status', 'type', 'width']),
|
||||
(serializers.RearPortSerializer, ['type']),
|
||||
(serializers.RearPortTemplateSerializer, ['type']),
|
||||
(serializers.SiteSerializer, ['status']),
|
||||
)
|
||||
|
||||
|
||||
@@ -102,7 +106,7 @@ class RegionViewSet(ModelViewSet):
|
||||
site_count=Count('sites')
|
||||
)
|
||||
serializer_class = serializers.RegionSerializer
|
||||
filterset_class = filters.RegionFilter
|
||||
filterset_class = filters.RegionFilterSet
|
||||
|
||||
|
||||
#
|
||||
@@ -121,7 +125,7 @@ class SiteViewSet(CustomFieldModelViewSet):
|
||||
virtualmachine_count=get_subquery(VirtualMachine, 'cluster__site'),
|
||||
)
|
||||
serializer_class = serializers.SiteSerializer
|
||||
filterset_class = filters.SiteFilter
|
||||
filterset_class = filters.SiteFilterSet
|
||||
|
||||
@action(detail=True)
|
||||
def graphs(self, request, pk):
|
||||
@@ -129,7 +133,7 @@ class SiteViewSet(CustomFieldModelViewSet):
|
||||
A convenience method for rendering graphs for a particular site.
|
||||
"""
|
||||
site = get_object_or_404(Site, pk=pk)
|
||||
queryset = Graph.objects.filter(type=GRAPH_TYPE_SITE)
|
||||
queryset = Graph.objects.filter(type__model='site')
|
||||
serializer = RenderedGraphSerializer(queryset, many=True, context={'graphed_object': site})
|
||||
return Response(serializer.data)
|
||||
|
||||
@@ -143,7 +147,7 @@ class RackGroupViewSet(ModelViewSet):
|
||||
rack_count=Count('racks')
|
||||
)
|
||||
serializer_class = serializers.RackGroupSerializer
|
||||
filterset_class = filters.RackGroupFilter
|
||||
filterset_class = filters.RackGroupFilterSet
|
||||
|
||||
|
||||
#
|
||||
@@ -155,7 +159,7 @@ class RackRoleViewSet(ModelViewSet):
|
||||
rack_count=Count('racks')
|
||||
)
|
||||
serializer_class = serializers.RackRoleSerializer
|
||||
filterset_class = filters.RackRoleFilter
|
||||
filterset_class = filters.RackRoleFilterSet
|
||||
|
||||
|
||||
#
|
||||
@@ -170,15 +174,17 @@ class RackViewSet(CustomFieldModelViewSet):
|
||||
powerfeed_count=get_subquery(PowerFeed, 'rack')
|
||||
)
|
||||
serializer_class = serializers.RackSerializer
|
||||
filterset_class = filters.RackFilter
|
||||
filterset_class = filters.RackFilterSet
|
||||
|
||||
@swagger_auto_schema(deprecated=True)
|
||||
@action(detail=True)
|
||||
def units(self, request, pk=None):
|
||||
"""
|
||||
List rack units (by rack)
|
||||
"""
|
||||
# TODO: Remove this action detail route in v2.8
|
||||
rack = get_object_or_404(Rack, pk=pk)
|
||||
face = request.GET.get('face', 0)
|
||||
face = request.GET.get('face', 'front')
|
||||
exclude_pk = request.GET.get('exclude', None)
|
||||
if exclude_pk is not None:
|
||||
try:
|
||||
@@ -197,6 +203,39 @@ class RackViewSet(CustomFieldModelViewSet):
|
||||
rack_units = serializers.RackUnitSerializer(page, many=True, context={'request': request})
|
||||
return self.get_paginated_response(rack_units.data)
|
||||
|
||||
@swagger_auto_schema(
|
||||
responses={200: serializers.RackUnitSerializer(many=True)},
|
||||
query_serializer=serializers.RackElevationDetailFilterSerializer
|
||||
)
|
||||
@action(detail=True)
|
||||
def elevation(self, request, pk=None):
|
||||
"""
|
||||
Rack elevation representing the list of rack units. Also supports rendering the elevation as an SVG.
|
||||
"""
|
||||
rack = get_object_or_404(Rack, pk=pk)
|
||||
serializer = serializers.RackElevationDetailFilterSerializer(data=request.GET)
|
||||
if not serializer.is_valid():
|
||||
return Response(serializer.errors, 400)
|
||||
data = serializer.validated_data
|
||||
|
||||
if data['render'] == 'svg':
|
||||
# Render and return the elevation as an SVG drawing with the correct content type
|
||||
drawing = rack.get_elevation_svg(data['face'], data['unit_width'], data['unit_height'])
|
||||
return HttpResponse(drawing.tostring(), content_type='image/svg+xml')
|
||||
|
||||
else:
|
||||
# Return a JSON representation of the rack units in the elevation
|
||||
elevation = rack.get_rack_units(
|
||||
face=data['face'],
|
||||
exclude=data['exclude'],
|
||||
expand_devices=data['expand_devices']
|
||||
)
|
||||
|
||||
page = self.paginate_queryset(elevation)
|
||||
if page is not None:
|
||||
rack_units = serializers.RackUnitSerializer(page, many=True, context={'request': request})
|
||||
return self.get_paginated_response(rack_units.data)
|
||||
|
||||
|
||||
#
|
||||
# Rack reservations
|
||||
@@ -205,7 +244,7 @@ class RackViewSet(CustomFieldModelViewSet):
|
||||
class RackReservationViewSet(ModelViewSet):
|
||||
queryset = RackReservation.objects.prefetch_related('rack', 'user', 'tenant')
|
||||
serializer_class = serializers.RackReservationSerializer
|
||||
filterset_class = filters.RackReservationFilter
|
||||
filterset_class = filters.RackReservationFilterSet
|
||||
|
||||
# Assign user from request
|
||||
def perform_create(self, serializer):
|
||||
@@ -223,7 +262,7 @@ class ManufacturerViewSet(ModelViewSet):
|
||||
platform_count=get_subquery(Platform, 'manufacturer')
|
||||
)
|
||||
serializer_class = serializers.ManufacturerSerializer
|
||||
filterset_class = filters.ManufacturerFilter
|
||||
filterset_class = filters.ManufacturerFilterSet
|
||||
|
||||
|
||||
#
|
||||
@@ -235,7 +274,7 @@ class DeviceTypeViewSet(CustomFieldModelViewSet):
|
||||
device_count=Count('instances')
|
||||
)
|
||||
serializer_class = serializers.DeviceTypeSerializer
|
||||
filterset_class = filters.DeviceTypeFilter
|
||||
filterset_class = filters.DeviceTypeFilterSet
|
||||
|
||||
|
||||
#
|
||||
@@ -245,49 +284,49 @@ class DeviceTypeViewSet(CustomFieldModelViewSet):
|
||||
class ConsolePortTemplateViewSet(ModelViewSet):
|
||||
queryset = ConsolePortTemplate.objects.prefetch_related('device_type__manufacturer')
|
||||
serializer_class = serializers.ConsolePortTemplateSerializer
|
||||
filterset_class = filters.ConsolePortTemplateFilter
|
||||
filterset_class = filters.ConsolePortTemplateFilterSet
|
||||
|
||||
|
||||
class ConsoleServerPortTemplateViewSet(ModelViewSet):
|
||||
queryset = ConsoleServerPortTemplate.objects.prefetch_related('device_type__manufacturer')
|
||||
serializer_class = serializers.ConsoleServerPortTemplateSerializer
|
||||
filterset_class = filters.ConsoleServerPortTemplateFilter
|
||||
filterset_class = filters.ConsoleServerPortTemplateFilterSet
|
||||
|
||||
|
||||
class PowerPortTemplateViewSet(ModelViewSet):
|
||||
queryset = PowerPortTemplate.objects.prefetch_related('device_type__manufacturer')
|
||||
serializer_class = serializers.PowerPortTemplateSerializer
|
||||
filterset_class = filters.PowerPortTemplateFilter
|
||||
filterset_class = filters.PowerPortTemplateFilterSet
|
||||
|
||||
|
||||
class PowerOutletTemplateViewSet(ModelViewSet):
|
||||
queryset = PowerOutletTemplate.objects.prefetch_related('device_type__manufacturer')
|
||||
serializer_class = serializers.PowerOutletTemplateSerializer
|
||||
filterset_class = filters.PowerOutletTemplateFilter
|
||||
filterset_class = filters.PowerOutletTemplateFilterSet
|
||||
|
||||
|
||||
class InterfaceTemplateViewSet(ModelViewSet):
|
||||
queryset = InterfaceTemplate.objects.prefetch_related('device_type__manufacturer')
|
||||
serializer_class = serializers.InterfaceTemplateSerializer
|
||||
filterset_class = filters.InterfaceTemplateFilter
|
||||
filterset_class = filters.InterfaceTemplateFilterSet
|
||||
|
||||
|
||||
class FrontPortTemplateViewSet(ModelViewSet):
|
||||
queryset = FrontPortTemplate.objects.prefetch_related('device_type__manufacturer')
|
||||
serializer_class = serializers.FrontPortTemplateSerializer
|
||||
filterset_class = filters.FrontPortTemplateFilter
|
||||
filterset_class = filters.FrontPortTemplateFilterSet
|
||||
|
||||
|
||||
class RearPortTemplateViewSet(ModelViewSet):
|
||||
queryset = RearPortTemplate.objects.prefetch_related('device_type__manufacturer')
|
||||
serializer_class = serializers.RearPortTemplateSerializer
|
||||
filterset_class = filters.RearPortTemplateFilter
|
||||
filterset_class = filters.RearPortTemplateFilterSet
|
||||
|
||||
|
||||
class DeviceBayTemplateViewSet(ModelViewSet):
|
||||
queryset = DeviceBayTemplate.objects.prefetch_related('device_type__manufacturer')
|
||||
serializer_class = serializers.DeviceBayTemplateSerializer
|
||||
filterset_class = filters.DeviceBayTemplateFilter
|
||||
filterset_class = filters.DeviceBayTemplateFilterSet
|
||||
|
||||
|
||||
#
|
||||
@@ -300,7 +339,7 @@ class DeviceRoleViewSet(ModelViewSet):
|
||||
virtualmachine_count=get_subquery(VirtualMachine, 'role')
|
||||
)
|
||||
serializer_class = serializers.DeviceRoleSerializer
|
||||
filterset_class = filters.DeviceRoleFilter
|
||||
filterset_class = filters.DeviceRoleFilterSet
|
||||
|
||||
|
||||
#
|
||||
@@ -313,7 +352,7 @@ class PlatformViewSet(ModelViewSet):
|
||||
virtualmachine_count=get_subquery(VirtualMachine, 'platform')
|
||||
)
|
||||
serializer_class = serializers.PlatformSerializer
|
||||
filterset_class = filters.PlatformFilter
|
||||
filterset_class = filters.PlatformFilterSet
|
||||
|
||||
|
||||
#
|
||||
@@ -325,7 +364,7 @@ class DeviceViewSet(CustomFieldModelViewSet):
|
||||
'device_type__manufacturer', 'device_role', 'tenant', 'platform', 'site', 'rack', 'parent_bay',
|
||||
'virtual_chassis__master', 'primary_ip4__nat_outside', 'primary_ip6__nat_outside', 'tags',
|
||||
)
|
||||
filterset_class = filters.DeviceFilter
|
||||
filterset_class = filters.DeviceFilterSet
|
||||
|
||||
def get_serializer_class(self):
|
||||
"""
|
||||
@@ -353,11 +392,22 @@ class DeviceViewSet(CustomFieldModelViewSet):
|
||||
A convenience method for rendering graphs for a particular Device.
|
||||
"""
|
||||
device = get_object_or_404(Device, pk=pk)
|
||||
queryset = Graph.objects.filter(type=GRAPH_TYPE_DEVICE)
|
||||
queryset = Graph.objects.filter(type__model='device')
|
||||
serializer = RenderedGraphSerializer(queryset, many=True, context={'graphed_object': device})
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
@swagger_auto_schema(
|
||||
manual_parameters=[
|
||||
Parameter(
|
||||
name='method',
|
||||
in_='query',
|
||||
required=True,
|
||||
type=openapi.TYPE_STRING
|
||||
)
|
||||
],
|
||||
responses={'200': serializers.DeviceNAPALMSerializer}
|
||||
)
|
||||
@action(detail=True, url_path='napalm')
|
||||
def napalm(self, request, pk):
|
||||
"""
|
||||
@@ -396,13 +446,29 @@ class DeviceViewSet(CustomFieldModelViewSet):
|
||||
napalm_methods = request.GET.getlist('method')
|
||||
response = OrderedDict([(m, None) for m in napalm_methods])
|
||||
ip_address = str(device.primary_ip.address.ip)
|
||||
username = settings.NAPALM_USERNAME
|
||||
password = settings.NAPALM_PASSWORD
|
||||
optional_args = settings.NAPALM_ARGS.copy()
|
||||
if device.platform.napalm_args is not None:
|
||||
optional_args.update(device.platform.napalm_args)
|
||||
|
||||
# Update NAPALM parameters according to the request headers
|
||||
for header in request.headers:
|
||||
if header[:9].lower() != 'x-napalm-':
|
||||
continue
|
||||
|
||||
key = header[9:]
|
||||
if key.lower() == 'username':
|
||||
username = request.headers[header]
|
||||
elif key.lower() == 'password':
|
||||
password = request.headers[header]
|
||||
elif key:
|
||||
optional_args[key.lower()] = request.headers[header]
|
||||
|
||||
d = driver(
|
||||
hostname=ip_address,
|
||||
username=settings.NAPALM_USERNAME,
|
||||
password=settings.NAPALM_PASSWORD,
|
||||
username=username,
|
||||
password=password,
|
||||
timeout=settings.NAPALM_TIMEOUT,
|
||||
optional_args=optional_args
|
||||
)
|
||||
@@ -437,13 +503,13 @@ class DeviceViewSet(CustomFieldModelViewSet):
|
||||
class ConsolePortViewSet(CableTraceMixin, ModelViewSet):
|
||||
queryset = ConsolePort.objects.prefetch_related('device', 'connected_endpoint__device', 'cable', 'tags')
|
||||
serializer_class = serializers.ConsolePortSerializer
|
||||
filterset_class = filters.ConsolePortFilter
|
||||
filterset_class = filters.ConsolePortFilterSet
|
||||
|
||||
|
||||
class ConsoleServerPortViewSet(CableTraceMixin, ModelViewSet):
|
||||
queryset = ConsoleServerPort.objects.prefetch_related('device', 'connected_endpoint__device', 'cable', 'tags')
|
||||
serializer_class = serializers.ConsoleServerPortSerializer
|
||||
filterset_class = filters.ConsoleServerPortFilter
|
||||
filterset_class = filters.ConsoleServerPortFilterSet
|
||||
|
||||
|
||||
class PowerPortViewSet(CableTraceMixin, ModelViewSet):
|
||||
@@ -451,13 +517,13 @@ class PowerPortViewSet(CableTraceMixin, ModelViewSet):
|
||||
'device', '_connected_poweroutlet__device', '_connected_powerfeed', 'cable', 'tags'
|
||||
)
|
||||
serializer_class = serializers.PowerPortSerializer
|
||||
filterset_class = filters.PowerPortFilter
|
||||
filterset_class = filters.PowerPortFilterSet
|
||||
|
||||
|
||||
class PowerOutletViewSet(CableTraceMixin, ModelViewSet):
|
||||
queryset = PowerOutlet.objects.prefetch_related('device', 'connected_endpoint__device', 'cable', 'tags')
|
||||
serializer_class = serializers.PowerOutletSerializer
|
||||
filterset_class = filters.PowerOutletFilter
|
||||
filterset_class = filters.PowerOutletFilterSet
|
||||
|
||||
|
||||
class InterfaceViewSet(CableTraceMixin, ModelViewSet):
|
||||
@@ -467,7 +533,7 @@ class InterfaceViewSet(CableTraceMixin, ModelViewSet):
|
||||
device__isnull=False
|
||||
)
|
||||
serializer_class = serializers.InterfaceSerializer
|
||||
filterset_class = filters.InterfaceFilter
|
||||
filterset_class = filters.InterfaceFilterSet
|
||||
|
||||
@action(detail=True)
|
||||
def graphs(self, request, pk):
|
||||
@@ -475,7 +541,7 @@ class InterfaceViewSet(CableTraceMixin, ModelViewSet):
|
||||
A convenience method for rendering graphs for a particular interface.
|
||||
"""
|
||||
interface = get_object_or_404(Interface, pk=pk)
|
||||
queryset = Graph.objects.filter(type=GRAPH_TYPE_INTERFACE)
|
||||
queryset = Graph.objects.filter(type__model='interface')
|
||||
serializer = RenderedGraphSerializer(queryset, many=True, context={'graphed_object': interface})
|
||||
return Response(serializer.data)
|
||||
|
||||
@@ -483,25 +549,25 @@ class InterfaceViewSet(CableTraceMixin, ModelViewSet):
|
||||
class FrontPortViewSet(ModelViewSet):
|
||||
queryset = FrontPort.objects.prefetch_related('device__device_type__manufacturer', 'rear_port', 'cable', 'tags')
|
||||
serializer_class = serializers.FrontPortSerializer
|
||||
filterset_class = filters.FrontPortFilter
|
||||
filterset_class = filters.FrontPortFilterSet
|
||||
|
||||
|
||||
class RearPortViewSet(ModelViewSet):
|
||||
queryset = RearPort.objects.prefetch_related('device__device_type__manufacturer', 'cable', 'tags')
|
||||
serializer_class = serializers.RearPortSerializer
|
||||
filterset_class = filters.RearPortFilter
|
||||
filterset_class = filters.RearPortFilterSet
|
||||
|
||||
|
||||
class DeviceBayViewSet(ModelViewSet):
|
||||
queryset = DeviceBay.objects.prefetch_related('installed_device').prefetch_related('tags')
|
||||
serializer_class = serializers.DeviceBaySerializer
|
||||
filterset_class = filters.DeviceBayFilter
|
||||
filterset_class = filters.DeviceBayFilterSet
|
||||
|
||||
|
||||
class InventoryItemViewSet(ModelViewSet):
|
||||
queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer').prefetch_related('tags')
|
||||
serializer_class = serializers.InventoryItemSerializer
|
||||
filterset_class = filters.InventoryItemFilter
|
||||
filterset_class = filters.InventoryItemFilterSet
|
||||
|
||||
|
||||
#
|
||||
@@ -515,7 +581,7 @@ class ConsoleConnectionViewSet(ListModelMixin, GenericViewSet):
|
||||
connected_endpoint__isnull=False
|
||||
)
|
||||
serializer_class = serializers.ConsolePortSerializer
|
||||
filterset_class = filters.ConsoleConnectionFilter
|
||||
filterset_class = filters.ConsoleConnectionFilterSet
|
||||
|
||||
|
||||
class PowerConnectionViewSet(ListModelMixin, GenericViewSet):
|
||||
@@ -525,7 +591,7 @@ class PowerConnectionViewSet(ListModelMixin, GenericViewSet):
|
||||
_connected_poweroutlet__isnull=False
|
||||
)
|
||||
serializer_class = serializers.PowerPortSerializer
|
||||
filterset_class = filters.PowerConnectionFilter
|
||||
filterset_class = filters.PowerConnectionFilterSet
|
||||
|
||||
|
||||
class InterfaceConnectionViewSet(ListModelMixin, GenericViewSet):
|
||||
@@ -537,7 +603,7 @@ class InterfaceConnectionViewSet(ListModelMixin, GenericViewSet):
|
||||
pk__lt=F('_connected_interface')
|
||||
)
|
||||
serializer_class = serializers.InterfaceConnectionSerializer
|
||||
filterset_class = filters.InterfaceConnectionFilter
|
||||
filterset_class = filters.InterfaceConnectionFilterSet
|
||||
|
||||
|
||||
#
|
||||
@@ -549,7 +615,7 @@ class CableViewSet(ModelViewSet):
|
||||
'termination_a', 'termination_b'
|
||||
)
|
||||
serializer_class = serializers.CableSerializer
|
||||
filterset_class = filters.CableFilter
|
||||
filterset_class = filters.CableFilterSet
|
||||
|
||||
|
||||
#
|
||||
@@ -561,7 +627,7 @@ class VirtualChassisViewSet(ModelViewSet):
|
||||
member_count=Count('members')
|
||||
)
|
||||
serializer_class = serializers.VirtualChassisSerializer
|
||||
filterset_class = filters.VirtualChassisFilter
|
||||
filterset_class = filters.VirtualChassisFilterSet
|
||||
|
||||
|
||||
#
|
||||
@@ -575,7 +641,7 @@ class PowerPanelViewSet(ModelViewSet):
|
||||
powerfeed_count=Count('powerfeeds')
|
||||
)
|
||||
serializer_class = serializers.PowerPanelSerializer
|
||||
filterset_class = filters.PowerPanelFilter
|
||||
filterset_class = filters.PowerPanelFilterSet
|
||||
|
||||
|
||||
#
|
||||
@@ -585,7 +651,7 @@ class PowerPanelViewSet(ModelViewSet):
|
||||
class PowerFeedViewSet(CustomFieldModelViewSet):
|
||||
queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack', 'tags')
|
||||
serializer_class = serializers.PowerFeedSerializer
|
||||
filterset_class = filters.PowerFeedFilter
|
||||
filterset_class = filters.PowerFeedFilterSet
|
||||
|
||||
|
||||
#
|
||||
|
||||
1079
netbox/dcim/choices.py
Normal file
1079
netbox/dcim/choices.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,379 +1,41 @@
|
||||
from django.db.models import Q
|
||||
|
||||
# Rack types
|
||||
RACK_TYPE_2POST = 100
|
||||
RACK_TYPE_4POST = 200
|
||||
RACK_TYPE_CABINET = 300
|
||||
RACK_TYPE_WALLFRAME = 1000
|
||||
RACK_TYPE_WALLCABINET = 1100
|
||||
RACK_TYPE_CHOICES = (
|
||||
(RACK_TYPE_2POST, '2-post frame'),
|
||||
(RACK_TYPE_4POST, '4-post frame'),
|
||||
(RACK_TYPE_CABINET, '4-post cabinet'),
|
||||
(RACK_TYPE_WALLFRAME, 'Wall-mounted frame'),
|
||||
(RACK_TYPE_WALLCABINET, 'Wall-mounted cabinet'),
|
||||
)
|
||||
from .choices import InterfaceTypeChoices
|
||||
|
||||
# Rack widths
|
||||
RACK_WIDTH_19IN = 19
|
||||
RACK_WIDTH_23IN = 23
|
||||
RACK_WIDTH_CHOICES = (
|
||||
(RACK_WIDTH_19IN, '19 inches'),
|
||||
(RACK_WIDTH_23IN, '23 inches'),
|
||||
)
|
||||
|
||||
# Rack faces
|
||||
RACK_FACE_FRONT = 0
|
||||
RACK_FACE_REAR = 1
|
||||
RACK_FACE_CHOICES = [
|
||||
[RACK_FACE_FRONT, 'Front'],
|
||||
[RACK_FACE_REAR, 'Rear'],
|
||||
]
|
||||
#
|
||||
# Rack elevation rendering
|
||||
#
|
||||
|
||||
# Rack statuses
|
||||
RACK_STATUS_RESERVED = 0
|
||||
RACK_STATUS_AVAILABLE = 1
|
||||
RACK_STATUS_PLANNED = 2
|
||||
RACK_STATUS_ACTIVE = 3
|
||||
RACK_STATUS_DEPRECATED = 4
|
||||
RACK_STATUS_CHOICES = [
|
||||
[RACK_STATUS_ACTIVE, 'Active'],
|
||||
[RACK_STATUS_PLANNED, 'Planned'],
|
||||
[RACK_STATUS_RESERVED, 'Reserved'],
|
||||
[RACK_STATUS_AVAILABLE, 'Available'],
|
||||
[RACK_STATUS_DEPRECATED, 'Deprecated'],
|
||||
]
|
||||
RACK_ELEVATION_UNIT_WIDTH_DEFAULT = 230
|
||||
RACK_ELEVATION_UNIT_HEIGHT_DEFAULT = 20
|
||||
|
||||
# Device rack position
|
||||
DEVICE_POSITION_CHOICES = [
|
||||
# Rack.u_height is limited to 100
|
||||
(i, 'Unit {}'.format(i)) for i in range(1, 101)
|
||||
]
|
||||
|
||||
# Parent/child device roles
|
||||
SUBDEVICE_ROLE_PARENT = True
|
||||
SUBDEVICE_ROLE_CHILD = False
|
||||
SUBDEVICE_ROLE_CHOICES = (
|
||||
(None, 'None'),
|
||||
(SUBDEVICE_ROLE_PARENT, 'Parent'),
|
||||
(SUBDEVICE_ROLE_CHILD, 'Child'),
|
||||
)
|
||||
|
||||
# Interface types
|
||||
# Virtual
|
||||
IFACE_TYPE_VIRTUAL = 0
|
||||
IFACE_TYPE_LAG = 200
|
||||
# Ethernet
|
||||
IFACE_TYPE_100ME_FIXED = 800
|
||||
IFACE_TYPE_1GE_FIXED = 1000
|
||||
IFACE_TYPE_1GE_GBIC = 1050
|
||||
IFACE_TYPE_1GE_SFP = 1100
|
||||
IFACE_TYPE_2GE_FIXED = 1120
|
||||
IFACE_TYPE_5GE_FIXED = 1130
|
||||
IFACE_TYPE_10GE_FIXED = 1150
|
||||
IFACE_TYPE_10GE_CX4 = 1170
|
||||
IFACE_TYPE_10GE_SFP_PLUS = 1200
|
||||
IFACE_TYPE_10GE_XFP = 1300
|
||||
IFACE_TYPE_10GE_XENPAK = 1310
|
||||
IFACE_TYPE_10GE_X2 = 1320
|
||||
IFACE_TYPE_25GE_SFP28 = 1350
|
||||
IFACE_TYPE_40GE_QSFP_PLUS = 1400
|
||||
IFACE_TYPE_50GE_QSFP28 = 1420
|
||||
IFACE_TYPE_100GE_CFP = 1500
|
||||
IFACE_TYPE_100GE_CFP2 = 1510
|
||||
IFACE_TYPE_100GE_CFP4 = 1520
|
||||
IFACE_TYPE_100GE_CPAK = 1550
|
||||
IFACE_TYPE_100GE_QSFP28 = 1600
|
||||
IFACE_TYPE_200GE_CFP2 = 1650
|
||||
IFACE_TYPE_200GE_QSFP56 = 1700
|
||||
IFACE_TYPE_400GE_QSFP_DD = 1750
|
||||
IFACE_TYPE_400GE_OSFP = 1800
|
||||
# Wireless
|
||||
IFACE_TYPE_80211A = 2600
|
||||
IFACE_TYPE_80211G = 2610
|
||||
IFACE_TYPE_80211N = 2620
|
||||
IFACE_TYPE_80211AC = 2630
|
||||
IFACE_TYPE_80211AD = 2640
|
||||
# Cellular
|
||||
IFACE_TYPE_GSM = 2810
|
||||
IFACE_TYPE_CDMA = 2820
|
||||
IFACE_TYPE_LTE = 2830
|
||||
# SONET
|
||||
IFACE_TYPE_SONET_OC3 = 6100
|
||||
IFACE_TYPE_SONET_OC12 = 6200
|
||||
IFACE_TYPE_SONET_OC48 = 6300
|
||||
IFACE_TYPE_SONET_OC192 = 6400
|
||||
IFACE_TYPE_SONET_OC768 = 6500
|
||||
IFACE_TYPE_SONET_OC1920 = 6600
|
||||
IFACE_TYPE_SONET_OC3840 = 6700
|
||||
# Fibrechannel
|
||||
IFACE_TYPE_1GFC_SFP = 3010
|
||||
IFACE_TYPE_2GFC_SFP = 3020
|
||||
IFACE_TYPE_4GFC_SFP = 3040
|
||||
IFACE_TYPE_8GFC_SFP_PLUS = 3080
|
||||
IFACE_TYPE_16GFC_SFP_PLUS = 3160
|
||||
IFACE_TYPE_32GFC_SFP28 = 3320
|
||||
IFACE_TYPE_128GFC_QSFP28 = 3400
|
||||
# InfiniBand
|
||||
IFACE_FF_INFINIBAND_SDR = 7010
|
||||
IFACE_FF_INFINIBAND_DDR = 7020
|
||||
IFACE_FF_INFINIBAND_QDR = 7030
|
||||
IFACE_FF_INFINIBAND_FDR10 = 7040
|
||||
IFACE_FF_INFINIBAND_FDR = 7050
|
||||
IFACE_FF_INFINIBAND_EDR = 7060
|
||||
IFACE_FF_INFINIBAND_HDR = 7070
|
||||
IFACE_FF_INFINIBAND_NDR = 7080
|
||||
IFACE_FF_INFINIBAND_XDR = 7090
|
||||
# Serial
|
||||
IFACE_TYPE_T1 = 4000
|
||||
IFACE_TYPE_E1 = 4010
|
||||
IFACE_TYPE_T3 = 4040
|
||||
IFACE_TYPE_E3 = 4050
|
||||
# Stacking
|
||||
IFACE_TYPE_STACKWISE = 5000
|
||||
IFACE_TYPE_STACKWISE_PLUS = 5050
|
||||
IFACE_TYPE_FLEXSTACK = 5100
|
||||
IFACE_TYPE_FLEXSTACK_PLUS = 5150
|
||||
IFACE_TYPE_JUNIPER_VCP = 5200
|
||||
IFACE_TYPE_SUMMITSTACK = 5300
|
||||
IFACE_TYPE_SUMMITSTACK128 = 5310
|
||||
IFACE_TYPE_SUMMITSTACK256 = 5320
|
||||
IFACE_TYPE_SUMMITSTACK512 = 5330
|
||||
|
||||
# Other
|
||||
IFACE_TYPE_OTHER = 32767
|
||||
|
||||
IFACE_TYPE_CHOICES = [
|
||||
[
|
||||
'Virtual interfaces',
|
||||
[
|
||||
[IFACE_TYPE_VIRTUAL, 'Virtual'],
|
||||
[IFACE_TYPE_LAG, 'Link Aggregation Group (LAG)'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'Ethernet (fixed)',
|
||||
[
|
||||
[IFACE_TYPE_100ME_FIXED, '100BASE-TX (10/100ME)'],
|
||||
[IFACE_TYPE_1GE_FIXED, '1000BASE-T (1GE)'],
|
||||
[IFACE_TYPE_2GE_FIXED, '2.5GBASE-T (2.5GE)'],
|
||||
[IFACE_TYPE_5GE_FIXED, '5GBASE-T (5GE)'],
|
||||
[IFACE_TYPE_10GE_FIXED, '10GBASE-T (10GE)'],
|
||||
[IFACE_TYPE_10GE_CX4, '10GBASE-CX4 (10GE)'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'Ethernet (modular)',
|
||||
[
|
||||
[IFACE_TYPE_1GE_GBIC, 'GBIC (1GE)'],
|
||||
[IFACE_TYPE_1GE_SFP, 'SFP (1GE)'],
|
||||
[IFACE_TYPE_10GE_SFP_PLUS, 'SFP+ (10GE)'],
|
||||
[IFACE_TYPE_10GE_XFP, 'XFP (10GE)'],
|
||||
[IFACE_TYPE_10GE_XENPAK, 'XENPAK (10GE)'],
|
||||
[IFACE_TYPE_10GE_X2, 'X2 (10GE)'],
|
||||
[IFACE_TYPE_25GE_SFP28, 'SFP28 (25GE)'],
|
||||
[IFACE_TYPE_40GE_QSFP_PLUS, 'QSFP+ (40GE)'],
|
||||
[IFACE_TYPE_50GE_QSFP28, 'QSFP28 (50GE)'],
|
||||
[IFACE_TYPE_100GE_CFP, 'CFP (100GE)'],
|
||||
[IFACE_TYPE_100GE_CFP2, 'CFP2 (100GE)'],
|
||||
[IFACE_TYPE_200GE_CFP2, 'CFP2 (200GE)'],
|
||||
[IFACE_TYPE_100GE_CFP4, 'CFP4 (100GE)'],
|
||||
[IFACE_TYPE_100GE_CPAK, 'Cisco CPAK (100GE)'],
|
||||
[IFACE_TYPE_100GE_QSFP28, 'QSFP28 (100GE)'],
|
||||
[IFACE_TYPE_200GE_QSFP56, 'QSFP56 (200GE)'],
|
||||
[IFACE_TYPE_400GE_QSFP_DD, 'QSFP-DD (400GE)'],
|
||||
[IFACE_TYPE_400GE_OSFP, 'OSFP (400GE)'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'Wireless',
|
||||
[
|
||||
[IFACE_TYPE_80211A, 'IEEE 802.11a'],
|
||||
[IFACE_TYPE_80211G, 'IEEE 802.11b/g'],
|
||||
[IFACE_TYPE_80211N, 'IEEE 802.11n'],
|
||||
[IFACE_TYPE_80211AC, 'IEEE 802.11ac'],
|
||||
[IFACE_TYPE_80211AD, 'IEEE 802.11ad'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'Cellular',
|
||||
[
|
||||
[IFACE_TYPE_GSM, 'GSM'],
|
||||
[IFACE_TYPE_CDMA, 'CDMA'],
|
||||
[IFACE_TYPE_LTE, 'LTE'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'SONET',
|
||||
[
|
||||
[IFACE_TYPE_SONET_OC3, 'OC-3/STM-1'],
|
||||
[IFACE_TYPE_SONET_OC12, 'OC-12/STM-4'],
|
||||
[IFACE_TYPE_SONET_OC48, 'OC-48/STM-16'],
|
||||
[IFACE_TYPE_SONET_OC192, 'OC-192/STM-64'],
|
||||
[IFACE_TYPE_SONET_OC768, 'OC-768/STM-256'],
|
||||
[IFACE_TYPE_SONET_OC1920, 'OC-1920/STM-640'],
|
||||
[IFACE_TYPE_SONET_OC3840, 'OC-3840/STM-1234'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'FibreChannel',
|
||||
[
|
||||
[IFACE_TYPE_1GFC_SFP, 'SFP (1GFC)'],
|
||||
[IFACE_TYPE_2GFC_SFP, 'SFP (2GFC)'],
|
||||
[IFACE_TYPE_4GFC_SFP, 'SFP (4GFC)'],
|
||||
[IFACE_TYPE_8GFC_SFP_PLUS, 'SFP+ (8GFC)'],
|
||||
[IFACE_TYPE_16GFC_SFP_PLUS, 'SFP+ (16GFC)'],
|
||||
[IFACE_TYPE_32GFC_SFP28, 'SFP28 (32GFC)'],
|
||||
[IFACE_TYPE_128GFC_QSFP28, 'QSFP28 (128GFC)'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'InfiniBand',
|
||||
[
|
||||
[IFACE_FF_INFINIBAND_SDR, 'SDR (2 Gbps)'],
|
||||
[IFACE_FF_INFINIBAND_DDR, 'DDR (4 Gbps)'],
|
||||
[IFACE_FF_INFINIBAND_QDR, 'QDR (8 Gbps)'],
|
||||
[IFACE_FF_INFINIBAND_FDR10, 'FDR10 (10 Gbps)'],
|
||||
[IFACE_FF_INFINIBAND_FDR, 'FDR (13.5 Gbps)'],
|
||||
[IFACE_FF_INFINIBAND_EDR, 'EDR (25 Gbps)'],
|
||||
[IFACE_FF_INFINIBAND_HDR, 'HDR (50 Gbps)'],
|
||||
[IFACE_FF_INFINIBAND_NDR, 'NDR (100 Gbps)'],
|
||||
[IFACE_FF_INFINIBAND_XDR, 'XDR (250 Gbps)'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'Serial',
|
||||
[
|
||||
[IFACE_TYPE_T1, 'T1 (1.544 Mbps)'],
|
||||
[IFACE_TYPE_E1, 'E1 (2.048 Mbps)'],
|
||||
[IFACE_TYPE_T3, 'T3 (45 Mbps)'],
|
||||
[IFACE_TYPE_E3, 'E3 (34 Mbps)'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'Stacking',
|
||||
[
|
||||
[IFACE_TYPE_STACKWISE, 'Cisco StackWise'],
|
||||
[IFACE_TYPE_STACKWISE_PLUS, 'Cisco StackWise Plus'],
|
||||
[IFACE_TYPE_FLEXSTACK, 'Cisco FlexStack'],
|
||||
[IFACE_TYPE_FLEXSTACK_PLUS, 'Cisco FlexStack Plus'],
|
||||
[IFACE_TYPE_JUNIPER_VCP, 'Juniper VCP'],
|
||||
[IFACE_TYPE_SUMMITSTACK, 'Extreme SummitStack'],
|
||||
[IFACE_TYPE_SUMMITSTACK128, 'Extreme SummitStack-128'],
|
||||
[IFACE_TYPE_SUMMITSTACK256, 'Extreme SummitStack-256'],
|
||||
[IFACE_TYPE_SUMMITSTACK512, 'Extreme SummitStack-512'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'Other',
|
||||
[
|
||||
[IFACE_TYPE_OTHER, 'Other'],
|
||||
]
|
||||
],
|
||||
]
|
||||
#
|
||||
# Interface type groups
|
||||
#
|
||||
|
||||
VIRTUAL_IFACE_TYPES = [
|
||||
IFACE_TYPE_VIRTUAL,
|
||||
IFACE_TYPE_LAG,
|
||||
InterfaceTypeChoices.TYPE_VIRTUAL,
|
||||
InterfaceTypeChoices.TYPE_LAG,
|
||||
]
|
||||
|
||||
WIRELESS_IFACE_TYPES = [
|
||||
IFACE_TYPE_80211A,
|
||||
IFACE_TYPE_80211G,
|
||||
IFACE_TYPE_80211N,
|
||||
IFACE_TYPE_80211AC,
|
||||
IFACE_TYPE_80211AD,
|
||||
InterfaceTypeChoices.TYPE_80211A,
|
||||
InterfaceTypeChoices.TYPE_80211G,
|
||||
InterfaceTypeChoices.TYPE_80211N,
|
||||
InterfaceTypeChoices.TYPE_80211AC,
|
||||
InterfaceTypeChoices.TYPE_80211AD,
|
||||
]
|
||||
|
||||
NONCONNECTABLE_IFACE_TYPES = VIRTUAL_IFACE_TYPES + WIRELESS_IFACE_TYPES
|
||||
|
||||
IFACE_MODE_ACCESS = 100
|
||||
IFACE_MODE_TAGGED = 200
|
||||
IFACE_MODE_TAGGED_ALL = 300
|
||||
IFACE_MODE_CHOICES = [
|
||||
[IFACE_MODE_ACCESS, 'Access'],
|
||||
[IFACE_MODE_TAGGED, 'Tagged'],
|
||||
[IFACE_MODE_TAGGED_ALL, 'Tagged All'],
|
||||
]
|
||||
|
||||
# Pass-through port types
|
||||
PORT_TYPE_8P8C = 1000
|
||||
PORT_TYPE_110_PUNCH = 1100
|
||||
PORT_TYPE_BNC = 1200
|
||||
PORT_TYPE_ST = 2000
|
||||
PORT_TYPE_SC = 2100
|
||||
PORT_TYPE_SC_APC = 2110
|
||||
PORT_TYPE_FC = 2200
|
||||
PORT_TYPE_LC = 2300
|
||||
PORT_TYPE_LC_APC = 2310
|
||||
PORT_TYPE_MTRJ = 2400
|
||||
PORT_TYPE_MPO = 2500
|
||||
PORT_TYPE_LSH = 2600
|
||||
PORT_TYPE_LSH_APC = 2610
|
||||
PORT_TYPE_CHOICES = [
|
||||
[
|
||||
'Copper',
|
||||
[
|
||||
[PORT_TYPE_8P8C, '8P8C'],
|
||||
[PORT_TYPE_110_PUNCH, '110 Punch'],
|
||||
[PORT_TYPE_BNC, 'BNC'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'Fiber Optic',
|
||||
[
|
||||
[PORT_TYPE_FC, 'FC'],
|
||||
[PORT_TYPE_LC, 'LC'],
|
||||
[PORT_TYPE_LC_APC, 'LC/APC'],
|
||||
[PORT_TYPE_LSH, 'LSH'],
|
||||
[PORT_TYPE_LSH_APC, 'LSH/APC'],
|
||||
[PORT_TYPE_MPO, 'MPO'],
|
||||
[PORT_TYPE_MTRJ, 'MTRJ'],
|
||||
[PORT_TYPE_SC, 'SC'],
|
||||
[PORT_TYPE_SC_APC, 'SC/APC'],
|
||||
[PORT_TYPE_ST, 'ST'],
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
# Device statuses
|
||||
DEVICE_STATUS_OFFLINE = 0
|
||||
DEVICE_STATUS_ACTIVE = 1
|
||||
DEVICE_STATUS_PLANNED = 2
|
||||
DEVICE_STATUS_STAGED = 3
|
||||
DEVICE_STATUS_FAILED = 4
|
||||
DEVICE_STATUS_INVENTORY = 5
|
||||
DEVICE_STATUS_DECOMMISSIONING = 6
|
||||
DEVICE_STATUS_CHOICES = [
|
||||
[DEVICE_STATUS_ACTIVE, 'Active'],
|
||||
[DEVICE_STATUS_OFFLINE, 'Offline'],
|
||||
[DEVICE_STATUS_PLANNED, 'Planned'],
|
||||
[DEVICE_STATUS_STAGED, 'Staged'],
|
||||
[DEVICE_STATUS_FAILED, 'Failed'],
|
||||
[DEVICE_STATUS_INVENTORY, 'Inventory'],
|
||||
[DEVICE_STATUS_DECOMMISSIONING, 'Decommissioning'],
|
||||
]
|
||||
|
||||
# Site statuses
|
||||
SITE_STATUS_ACTIVE = 1
|
||||
SITE_STATUS_PLANNED = 2
|
||||
SITE_STATUS_RETIRED = 4
|
||||
SITE_STATUS_CHOICES = [
|
||||
[SITE_STATUS_ACTIVE, 'Active'],
|
||||
[SITE_STATUS_PLANNED, 'Planned'],
|
||||
[SITE_STATUS_RETIRED, 'Retired'],
|
||||
]
|
||||
|
||||
# Bootstrap CSS classes for device/rack statuses
|
||||
STATUS_CLASSES = {
|
||||
0: 'warning',
|
||||
1: 'success',
|
||||
2: 'info',
|
||||
3: 'primary',
|
||||
4: 'danger',
|
||||
5: 'default',
|
||||
6: 'warning',
|
||||
}
|
||||
#
|
||||
# Cabling and connections
|
||||
#
|
||||
|
||||
# TODO: Replace with CableStatusChoices?
|
||||
# Console/power/interface connection statuses
|
||||
CONNECTION_STATUS_PLANNED = False
|
||||
CONNECTION_STATUS_CONNECTED = True
|
||||
@@ -383,72 +45,22 @@ CONNECTION_STATUS_CHOICES = [
|
||||
]
|
||||
|
||||
# Cable endpoint types
|
||||
CABLE_TERMINATION_TYPES = [
|
||||
'consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport',
|
||||
'circuittermination', 'powerfeed',
|
||||
]
|
||||
|
||||
# Cable types
|
||||
CABLE_TYPE_CAT3 = 1300
|
||||
CABLE_TYPE_CAT5 = 1500
|
||||
CABLE_TYPE_CAT5E = 1510
|
||||
CABLE_TYPE_CAT6 = 1600
|
||||
CABLE_TYPE_CAT6A = 1610
|
||||
CABLE_TYPE_CAT7 = 1700
|
||||
CABLE_TYPE_DAC_ACTIVE = 1800
|
||||
CABLE_TYPE_DAC_PASSIVE = 1810
|
||||
CABLE_TYPE_COAXIAL = 1900
|
||||
CABLE_TYPE_MMF = 3000
|
||||
CABLE_TYPE_MMF_OM1 = 3010
|
||||
CABLE_TYPE_MMF_OM2 = 3020
|
||||
CABLE_TYPE_MMF_OM3 = 3030
|
||||
CABLE_TYPE_MMF_OM4 = 3040
|
||||
CABLE_TYPE_SMF = 3500
|
||||
CABLE_TYPE_SMF_OS1 = 3510
|
||||
CABLE_TYPE_SMF_OS2 = 3520
|
||||
CABLE_TYPE_AOC = 3800
|
||||
CABLE_TYPE_POWER = 5000
|
||||
CABLE_TYPE_CHOICES = (
|
||||
(
|
||||
'Copper', (
|
||||
(CABLE_TYPE_CAT3, 'CAT3'),
|
||||
(CABLE_TYPE_CAT5, 'CAT5'),
|
||||
(CABLE_TYPE_CAT5E, 'CAT5e'),
|
||||
(CABLE_TYPE_CAT6, 'CAT6'),
|
||||
(CABLE_TYPE_CAT6A, 'CAT6a'),
|
||||
(CABLE_TYPE_CAT7, 'CAT7'),
|
||||
(CABLE_TYPE_DAC_ACTIVE, 'Direct Attach Copper (Active)'),
|
||||
(CABLE_TYPE_DAC_PASSIVE, 'Direct Attach Copper (Passive)'),
|
||||
(CABLE_TYPE_COAXIAL, 'Coaxial'),
|
||||
),
|
||||
),
|
||||
(
|
||||
'Fiber', (
|
||||
(CABLE_TYPE_MMF, 'Multimode Fiber'),
|
||||
(CABLE_TYPE_MMF_OM1, 'Multimode Fiber (OM1)'),
|
||||
(CABLE_TYPE_MMF_OM2, 'Multimode Fiber (OM2)'),
|
||||
(CABLE_TYPE_MMF_OM3, 'Multimode Fiber (OM3)'),
|
||||
(CABLE_TYPE_MMF_OM4, 'Multimode Fiber (OM4)'),
|
||||
(CABLE_TYPE_SMF, 'Singlemode Fiber'),
|
||||
(CABLE_TYPE_SMF_OS1, 'Singlemode Fiber (OS1)'),
|
||||
(CABLE_TYPE_SMF_OS2, 'Singlemode Fiber (OS2)'),
|
||||
(CABLE_TYPE_AOC, 'Active Optical Cabling (AOC)'),
|
||||
),
|
||||
),
|
||||
(CABLE_TYPE_POWER, 'Power'),
|
||||
CABLE_TERMINATION_MODELS = Q(
|
||||
Q(app_label='circuits', model__in=(
|
||||
'circuittermination',
|
||||
)) |
|
||||
Q(app_label='dcim', model__in=(
|
||||
'consoleport',
|
||||
'consoleserverport',
|
||||
'frontport',
|
||||
'interface',
|
||||
'powerfeed',
|
||||
'poweroutlet',
|
||||
'powerport',
|
||||
'rearport',
|
||||
))
|
||||
)
|
||||
|
||||
CABLE_TERMINATION_TYPE_CHOICES = {
|
||||
# (API endpoint, human-friendly name)
|
||||
'consoleport': ('console-ports', 'Console port'),
|
||||
'consoleserverport': ('console-server-ports', 'Console server port'),
|
||||
'powerport': ('power-ports', 'Power port'),
|
||||
'poweroutlet': ('power-outlets', 'Power outlet'),
|
||||
'interface': ('interfaces', 'Interface'),
|
||||
'frontport': ('front-ports', 'Front panel port'),
|
||||
'rearport': ('rear-ports', 'Rear panel port'),
|
||||
}
|
||||
|
||||
COMPATIBLE_TERMINATION_TYPES = {
|
||||
'consoleport': ['consoleserverport', 'frontport', 'rearport'],
|
||||
'consoleserverport': ['consoleport', 'frontport', 'rearport'],
|
||||
@@ -459,57 +71,3 @@ COMPATIBLE_TERMINATION_TYPES = {
|
||||
'rearport': ['consoleport', 'consoleserverport', 'interface', 'frontport', 'rearport', 'circuittermination'],
|
||||
'circuittermination': ['interface', 'frontport', 'rearport'],
|
||||
}
|
||||
|
||||
LENGTH_UNIT_METER = 1200
|
||||
LENGTH_UNIT_CENTIMETER = 1100
|
||||
LENGTH_UNIT_MILLIMETER = 1000
|
||||
LENGTH_UNIT_FOOT = 2100
|
||||
LENGTH_UNIT_INCH = 2000
|
||||
CABLE_LENGTH_UNIT_CHOICES = (
|
||||
(LENGTH_UNIT_METER, 'Meters'),
|
||||
(LENGTH_UNIT_CENTIMETER, 'Centimeters'),
|
||||
(LENGTH_UNIT_FOOT, 'Feet'),
|
||||
(LENGTH_UNIT_INCH, 'Inches'),
|
||||
)
|
||||
RACK_DIMENSION_UNIT_CHOICES = (
|
||||
(LENGTH_UNIT_MILLIMETER, 'Millimeters'),
|
||||
(LENGTH_UNIT_INCH, 'Inches'),
|
||||
)
|
||||
|
||||
# Power feeds
|
||||
POWERFEED_TYPE_PRIMARY = 1
|
||||
POWERFEED_TYPE_REDUNDANT = 2
|
||||
POWERFEED_TYPE_CHOICES = (
|
||||
(POWERFEED_TYPE_PRIMARY, 'Primary'),
|
||||
(POWERFEED_TYPE_REDUNDANT, 'Redundant'),
|
||||
)
|
||||
POWERFEED_SUPPLY_AC = 1
|
||||
POWERFEED_SUPPLY_DC = 2
|
||||
POWERFEED_SUPPLY_CHOICES = (
|
||||
(POWERFEED_SUPPLY_AC, 'AC'),
|
||||
(POWERFEED_SUPPLY_DC, 'DC'),
|
||||
)
|
||||
POWERFEED_PHASE_SINGLE = 1
|
||||
POWERFEED_PHASE_3PHASE = 3
|
||||
POWERFEED_PHASE_CHOICES = (
|
||||
(POWERFEED_PHASE_SINGLE, 'Single phase'),
|
||||
(POWERFEED_PHASE_3PHASE, 'Three-phase'),
|
||||
)
|
||||
POWERFEED_STATUS_OFFLINE = 0
|
||||
POWERFEED_STATUS_ACTIVE = 1
|
||||
POWERFEED_STATUS_PLANNED = 2
|
||||
POWERFEED_STATUS_FAILED = 4
|
||||
POWERFEED_STATUS_CHOICES = (
|
||||
(POWERFEED_STATUS_ACTIVE, 'Active'),
|
||||
(POWERFEED_STATUS_OFFLINE, 'Offline'),
|
||||
(POWERFEED_STATUS_PLANNED, 'Planned'),
|
||||
(POWERFEED_STATUS_FAILED, 'Failed'),
|
||||
)
|
||||
POWERFEED_LEG_A = 1
|
||||
POWERFEED_LEG_B = 2
|
||||
POWERFEED_LEG_C = 3
|
||||
POWERFEED_LEG_CHOICES = (
|
||||
(POWERFEED_LEG_A, 'A'),
|
||||
(POWERFEED_LEG_B, 'B'),
|
||||
(POWERFEED_LEG_C, 'C'),
|
||||
)
|
||||
|
||||
@@ -3,14 +3,24 @@ from django.core.validators import MinValueValidator, MaxValueValidator
|
||||
from django.db import models
|
||||
from netaddr import AddrFormatError, EUI, mac_unix_expanded
|
||||
|
||||
from ipam.constants import BGP_ASN_MAX, BGP_ASN_MIN
|
||||
|
||||
|
||||
class ASNField(models.BigIntegerField):
|
||||
description = "32-bit ASN field"
|
||||
default_validators = [
|
||||
MinValueValidator(1),
|
||||
MaxValueValidator(4294967295),
|
||||
MinValueValidator(BGP_ASN_MIN),
|
||||
MaxValueValidator(BGP_ASN_MAX),
|
||||
]
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {
|
||||
'min_value': BGP_ASN_MIN,
|
||||
'max_value': BGP_ASN_MAX,
|
||||
}
|
||||
defaults.update(**kwargs)
|
||||
return super().formfield(**defaults)
|
||||
|
||||
|
||||
class mac_unix_expanded_uppercase(mac_unix_expanded):
|
||||
word_fmt = '%.2X'
|
||||
@@ -22,7 +32,7 @@ class MACAddressField(models.Field):
|
||||
def python_type(self):
|
||||
return EUI
|
||||
|
||||
def from_db_value(self, value, expression, connection, context):
|
||||
def from_db_value(self, value, expression, connection):
|
||||
return self.to_python(value)
|
||||
|
||||
def to_python(self, value):
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import django_filters
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models import Q
|
||||
|
||||
from extras.filters import CustomFieldFilterSet, LocalConfigContextFilter, CreatedUpdatedFilterSet
|
||||
from tenancy.filtersets import TenancyFilterSet
|
||||
from extras.filters import CustomFieldFilterSet, LocalConfigContextFilterSet, CreatedUpdatedFilterSet
|
||||
from tenancy.filters import TenancyFilterSet
|
||||
from tenancy.models import Tenant
|
||||
from utilities.constants import COLOR_CHOICES
|
||||
from utilities.filters import (
|
||||
@@ -11,6 +10,7 @@ from utilities.filters import (
|
||||
TagFilter, TreeNodeMultipleChoiceFilter,
|
||||
)
|
||||
from virtualization.models import Cluster
|
||||
from .choices import *
|
||||
from .constants import *
|
||||
from .models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
@@ -21,7 +21,46 @@ from .models import (
|
||||
)
|
||||
|
||||
|
||||
class RegionFilter(NameSlugSearchFilterSet):
|
||||
__all__ = (
|
||||
'CableFilterSet',
|
||||
'ConsoleConnectionFilterSet',
|
||||
'ConsolePortFilterSet',
|
||||
'ConsolePortTemplateFilterSet',
|
||||
'ConsoleServerPortFilterSet',
|
||||
'ConsoleServerPortTemplateFilterSet',
|
||||
'DeviceBayFilterSet',
|
||||
'DeviceBayTemplateFilterSet',
|
||||
'DeviceFilterSet',
|
||||
'DeviceRoleFilterSet',
|
||||
'DeviceTypeFilterSet',
|
||||
'FrontPortFilterSet',
|
||||
'FrontPortTemplateFilterSet',
|
||||
'InterfaceConnectionFilterSet',
|
||||
'InterfaceFilterSet',
|
||||
'InterfaceTemplateFilterSet',
|
||||
'InventoryItemFilterSet',
|
||||
'ManufacturerFilterSet',
|
||||
'PlatformFilterSet',
|
||||
'PowerConnectionFilterSet',
|
||||
'PowerFeedFilterSet',
|
||||
'PowerOutletFilterSet',
|
||||
'PowerOutletTemplateFilterSet',
|
||||
'PowerPanelFilterSet',
|
||||
'PowerPortFilterSet',
|
||||
'PowerPortTemplateFilterSet',
|
||||
'RackFilterSet',
|
||||
'RackGroupFilterSet',
|
||||
'RackReservationFilterSet',
|
||||
'RackRoleFilterSet',
|
||||
'RearPortFilterSet',
|
||||
'RearPortTemplateFilterSet',
|
||||
'RegionFilterSet',
|
||||
'SiteFilterSet',
|
||||
'VirtualChassisFilterSet',
|
||||
)
|
||||
|
||||
|
||||
class RegionFilterSet(NameSlugSearchFilterSet):
|
||||
parent_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
label='Parent region (ID)',
|
||||
@@ -38,7 +77,7 @@ class RegionFilter(NameSlugSearchFilterSet):
|
||||
fields = ['id', 'name', 'slug']
|
||||
|
||||
|
||||
class SiteFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
class SiteFilterSet(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@@ -48,7 +87,7 @@ class SiteFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet
|
||||
label='Search',
|
||||
)
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
choices=SITE_STATUS_CHOICES,
|
||||
choices=SiteStatusChoices,
|
||||
null_value=None
|
||||
)
|
||||
region_id = TreeNodeMultipleChoiceFilter(
|
||||
@@ -92,7 +131,18 @@ class SiteFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet
|
||||
return queryset.filter(qs_filter)
|
||||
|
||||
|
||||
class RackGroupFilter(NameSlugSearchFilterSet):
|
||||
class RackGroupFilterSet(NameSlugSearchFilterSet):
|
||||
region_id = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='site__region__in',
|
||||
label='Region (ID)',
|
||||
)
|
||||
region = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='site__region__in',
|
||||
to_field_name='slug',
|
||||
label='Region (slug)',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Site.objects.all(),
|
||||
label='Site (ID)',
|
||||
@@ -109,14 +159,14 @@ class RackGroupFilter(NameSlugSearchFilterSet):
|
||||
fields = ['id', 'name', 'slug']
|
||||
|
||||
|
||||
class RackRoleFilter(NameSlugSearchFilterSet):
|
||||
class RackRoleFilterSet(NameSlugSearchFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = RackRole
|
||||
fields = ['id', 'name', 'slug', 'color']
|
||||
|
||||
|
||||
class RackFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
class RackFilterSet(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@@ -125,6 +175,17 @@ class RackFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
region_id = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='site__region__in',
|
||||
label='Region (ID)',
|
||||
)
|
||||
region = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='site__region__in',
|
||||
to_field_name='slug',
|
||||
label='Region (slug)',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Site.objects.all(),
|
||||
label='Site (ID)',
|
||||
@@ -146,7 +207,7 @@ class RackFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet
|
||||
label='Group',
|
||||
)
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
choices=RACK_STATUS_CHOICES,
|
||||
choices=RackStatusChoices,
|
||||
null_value=None
|
||||
)
|
||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||
@@ -183,7 +244,7 @@ class RackFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet
|
||||
)
|
||||
|
||||
|
||||
class RackReservationFilter(TenancyFilterSet):
|
||||
class RackReservationFilterSet(TenancyFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@@ -244,14 +305,14 @@ class RackReservationFilter(TenancyFilterSet):
|
||||
)
|
||||
|
||||
|
||||
class ManufacturerFilter(NameSlugSearchFilterSet):
|
||||
class ManufacturerFilterSet(NameSlugSearchFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = Manufacturer
|
||||
fields = ['id', 'name', 'slug']
|
||||
|
||||
|
||||
class DeviceTypeFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
class DeviceTypeFilterSet(CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@@ -294,6 +355,10 @@ class DeviceTypeFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
method='_pass_through_ports',
|
||||
label='Has pass-through ports',
|
||||
)
|
||||
device_bays = django_filters.BooleanFilter(
|
||||
method='_device_bays',
|
||||
label='Has device bays',
|
||||
)
|
||||
tag = TagFilter()
|
||||
|
||||
class Meta:
|
||||
@@ -333,6 +398,9 @@ class DeviceTypeFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
rearport_templates__isnull=value
|
||||
)
|
||||
|
||||
def _device_bays(self, queryset, name, value):
|
||||
return queryset.exclude(device_bay_templates__isnull=value)
|
||||
|
||||
|
||||
class DeviceTypeComponentFilterSet(NameSlugSearchFilterSet):
|
||||
devicetype_id = django_filters.ModelMultipleChoiceFilter(
|
||||
@@ -342,70 +410,70 @@ class DeviceTypeComponentFilterSet(NameSlugSearchFilterSet):
|
||||
)
|
||||
|
||||
|
||||
class ConsolePortTemplateFilter(DeviceTypeComponentFilterSet):
|
||||
class ConsolePortTemplateFilterSet(DeviceTypeComponentFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = ConsolePortTemplate
|
||||
fields = ['id', 'name']
|
||||
fields = ['id', 'name', 'type']
|
||||
|
||||
|
||||
class ConsoleServerPortTemplateFilter(DeviceTypeComponentFilterSet):
|
||||
class ConsoleServerPortTemplateFilterSet(DeviceTypeComponentFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = ConsoleServerPortTemplate
|
||||
fields = ['id', 'name']
|
||||
fields = ['id', 'name', 'type']
|
||||
|
||||
|
||||
class PowerPortTemplateFilter(DeviceTypeComponentFilterSet):
|
||||
class PowerPortTemplateFilterSet(DeviceTypeComponentFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = PowerPortTemplate
|
||||
fields = ['id', 'name', 'maximum_draw', 'allocated_draw']
|
||||
fields = ['id', 'name', 'type', 'maximum_draw', 'allocated_draw']
|
||||
|
||||
|
||||
class PowerOutletTemplateFilter(DeviceTypeComponentFilterSet):
|
||||
class PowerOutletTemplateFilterSet(DeviceTypeComponentFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = PowerOutletTemplate
|
||||
fields = ['id', 'name', 'feed_leg']
|
||||
fields = ['id', 'name', 'type', 'feed_leg']
|
||||
|
||||
|
||||
class InterfaceTemplateFilter(DeviceTypeComponentFilterSet):
|
||||
class InterfaceTemplateFilterSet(DeviceTypeComponentFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = InterfaceTemplate
|
||||
fields = ['id', 'name', 'type', 'mgmt_only']
|
||||
|
||||
|
||||
class FrontPortTemplateFilter(DeviceTypeComponentFilterSet):
|
||||
class FrontPortTemplateFilterSet(DeviceTypeComponentFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = FrontPortTemplate
|
||||
fields = ['id', 'name', 'type']
|
||||
|
||||
|
||||
class RearPortTemplateFilter(DeviceTypeComponentFilterSet):
|
||||
class RearPortTemplateFilterSet(DeviceTypeComponentFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = RearPortTemplate
|
||||
fields = ['id', 'name', 'type', 'positions']
|
||||
|
||||
|
||||
class DeviceBayTemplateFilter(DeviceTypeComponentFilterSet):
|
||||
class DeviceBayTemplateFilterSet(DeviceTypeComponentFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = DeviceBayTemplate
|
||||
fields = ['id', 'name']
|
||||
|
||||
|
||||
class DeviceRoleFilter(NameSlugSearchFilterSet):
|
||||
class DeviceRoleFilterSet(NameSlugSearchFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = DeviceRole
|
||||
fields = ['id', 'name', 'slug', 'color', 'vm_role']
|
||||
|
||||
|
||||
class PlatformFilter(NameSlugSearchFilterSet):
|
||||
class PlatformFilterSet(NameSlugSearchFilterSet):
|
||||
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='manufacturer',
|
||||
queryset=Manufacturer.objects.all(),
|
||||
@@ -423,7 +491,7 @@ class PlatformFilter(NameSlugSearchFilterSet):
|
||||
fields = ['id', 'name', 'slug', 'napalm_driver']
|
||||
|
||||
|
||||
class DeviceFilter(LocalConfigContextFilter, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
class DeviceFilterSet(LocalConfigContextFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@@ -510,7 +578,7 @@ class DeviceFilter(LocalConfigContextFilter, TenancyFilterSet, CustomFieldFilter
|
||||
label='Device model (slug)',
|
||||
)
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
choices=DEVICE_STATUS_CHOICES,
|
||||
choices=DeviceStatusChoices,
|
||||
null_value=None
|
||||
)
|
||||
is_full_depth = django_filters.BooleanFilter(
|
||||
@@ -561,6 +629,10 @@ class DeviceFilter(LocalConfigContextFilter, TenancyFilterSet, CustomFieldFilter
|
||||
method='_pass_through_ports',
|
||||
label='Has pass-through ports',
|
||||
)
|
||||
device_bays = django_filters.BooleanFilter(
|
||||
method='_device_bays',
|
||||
label='Has device bays',
|
||||
)
|
||||
tag = TagFilter()
|
||||
|
||||
class Meta:
|
||||
@@ -614,17 +686,43 @@ class DeviceFilter(LocalConfigContextFilter, TenancyFilterSet, CustomFieldFilter
|
||||
rearports__isnull=value
|
||||
)
|
||||
|
||||
def _device_bays(self, queryset, name, value):
|
||||
return queryset.exclude(device_bays__isnull=value)
|
||||
|
||||
|
||||
class DeviceComponentFilterSet(django_filters.FilterSet):
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
region_id = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='device__site__region__in',
|
||||
label='Region (ID)',
|
||||
)
|
||||
region = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='device__site__region__in',
|
||||
to_field_name='slug',
|
||||
label='Region (slug)',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='device__site',
|
||||
queryset=Site.objects.all(),
|
||||
label='Site (ID)',
|
||||
)
|
||||
site = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='device__site__slug',
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Site name (slug)',
|
||||
)
|
||||
device_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Device.objects.all(),
|
||||
label='Device (ID)',
|
||||
)
|
||||
device = django_filters.ModelChoiceFilter(
|
||||
device = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='device__name',
|
||||
queryset=Device.objects.all(),
|
||||
to_field_name='name',
|
||||
label='Device (name)',
|
||||
@@ -640,7 +738,11 @@ class DeviceComponentFilterSet(django_filters.FilterSet):
|
||||
)
|
||||
|
||||
|
||||
class ConsolePortFilter(DeviceComponentFilterSet):
|
||||
class ConsolePortFilterSet(DeviceComponentFilterSet):
|
||||
type = django_filters.MultipleChoiceFilter(
|
||||
choices=ConsolePortTypeChoices,
|
||||
null_value=None
|
||||
)
|
||||
cabled = django_filters.BooleanFilter(
|
||||
field_name='cable',
|
||||
lookup_expr='isnull',
|
||||
@@ -652,7 +754,11 @@ class ConsolePortFilter(DeviceComponentFilterSet):
|
||||
fields = ['id', 'name', 'description', 'connection_status']
|
||||
|
||||
|
||||
class ConsoleServerPortFilter(DeviceComponentFilterSet):
|
||||
class ConsoleServerPortFilterSet(DeviceComponentFilterSet):
|
||||
type = django_filters.MultipleChoiceFilter(
|
||||
choices=ConsolePortTypeChoices,
|
||||
null_value=None
|
||||
)
|
||||
cabled = django_filters.BooleanFilter(
|
||||
field_name='cable',
|
||||
lookup_expr='isnull',
|
||||
@@ -664,7 +770,11 @@ class ConsoleServerPortFilter(DeviceComponentFilterSet):
|
||||
fields = ['id', 'name', 'description', 'connection_status']
|
||||
|
||||
|
||||
class PowerPortFilter(DeviceComponentFilterSet):
|
||||
class PowerPortFilterSet(DeviceComponentFilterSet):
|
||||
type = django_filters.MultipleChoiceFilter(
|
||||
choices=PowerPortTypeChoices,
|
||||
null_value=None
|
||||
)
|
||||
cabled = django_filters.BooleanFilter(
|
||||
field_name='cable',
|
||||
lookup_expr='isnull',
|
||||
@@ -676,7 +786,11 @@ class PowerPortFilter(DeviceComponentFilterSet):
|
||||
fields = ['id', 'name', 'maximum_draw', 'allocated_draw', 'description', 'connection_status']
|
||||
|
||||
|
||||
class PowerOutletFilter(DeviceComponentFilterSet):
|
||||
class PowerOutletFilterSet(DeviceComponentFilterSet):
|
||||
type = django_filters.MultipleChoiceFilter(
|
||||
choices=PowerOutletTypeChoices,
|
||||
null_value=None
|
||||
)
|
||||
cabled = django_filters.BooleanFilter(
|
||||
field_name='cable',
|
||||
lookup_expr='isnull',
|
||||
@@ -688,14 +802,13 @@ class PowerOutletFilter(DeviceComponentFilterSet):
|
||||
fields = ['id', 'name', 'feed_leg', 'description', 'connection_status']
|
||||
|
||||
|
||||
class InterfaceFilter(django_filters.FilterSet):
|
||||
"""
|
||||
Not using DeviceComponentFilterSet for Interfaces because we need to check for VirtualChassis membership.
|
||||
"""
|
||||
class InterfaceFilterSet(DeviceComponentFilterSet):
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
# Override device and device_id filters from DeviceComponentFilterSet to match against any peer virtual chassis
|
||||
# members
|
||||
device = MultiValueCharFilter(
|
||||
method='filter_device',
|
||||
field_name='name',
|
||||
@@ -731,7 +844,7 @@ class InterfaceFilter(django_filters.FilterSet):
|
||||
label='Assigned VID'
|
||||
)
|
||||
type = django_filters.MultipleChoiceFilter(
|
||||
choices=IFACE_TYPE_CHOICES,
|
||||
choices=InterfaceTypeChoices,
|
||||
null_value=None
|
||||
)
|
||||
|
||||
@@ -739,14 +852,6 @@ class InterfaceFilter(django_filters.FilterSet):
|
||||
model = Interface
|
||||
fields = ['id', 'name', 'connection_status', 'type', 'enabled', 'mtu', 'mgmt_only', 'mode', 'description']
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(name__icontains=value) |
|
||||
Q(description__icontains=value)
|
||||
).distinct()
|
||||
|
||||
def filter_device(self, queryset, name, value):
|
||||
try:
|
||||
devices = Device.objects.filter(**{'{}__in'.format(name): value})
|
||||
@@ -795,7 +900,7 @@ class InterfaceFilter(django_filters.FilterSet):
|
||||
}.get(value, queryset.none())
|
||||
|
||||
|
||||
class FrontPortFilter(DeviceComponentFilterSet):
|
||||
class FrontPortFilterSet(DeviceComponentFilterSet):
|
||||
cabled = django_filters.BooleanFilter(
|
||||
field_name='cable',
|
||||
lookup_expr='isnull',
|
||||
@@ -807,7 +912,7 @@ class FrontPortFilter(DeviceComponentFilterSet):
|
||||
fields = ['id', 'name', 'type', 'description']
|
||||
|
||||
|
||||
class RearPortFilter(DeviceComponentFilterSet):
|
||||
class RearPortFilterSet(DeviceComponentFilterSet):
|
||||
cabled = django_filters.BooleanFilter(
|
||||
field_name='cable',
|
||||
lookup_expr='isnull',
|
||||
@@ -819,18 +924,40 @@ class RearPortFilter(DeviceComponentFilterSet):
|
||||
fields = ['id', 'name', 'type', 'positions', 'description']
|
||||
|
||||
|
||||
class DeviceBayFilter(DeviceComponentFilterSet):
|
||||
class DeviceBayFilterSet(DeviceComponentFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = DeviceBay
|
||||
fields = ['id', 'name', 'description']
|
||||
|
||||
|
||||
class InventoryItemFilter(DeviceComponentFilterSet):
|
||||
class InventoryItemFilterSet(DeviceComponentFilterSet):
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
region_id = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='device__site__region__in',
|
||||
label='Region (ID)',
|
||||
)
|
||||
region = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='device__site__region__in',
|
||||
to_field_name='slug',
|
||||
label='Region (slug)',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='device__site',
|
||||
queryset=Site.objects.all(),
|
||||
label='Site (ID)',
|
||||
)
|
||||
site = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='device__site__slug',
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Site name (slug)',
|
||||
)
|
||||
device_id = django_filters.ModelChoiceFilter(
|
||||
queryset=Device.objects.all(),
|
||||
label='Device (ID)',
|
||||
@@ -868,18 +995,29 @@ class InventoryItemFilter(DeviceComponentFilterSet):
|
||||
qs_filter = (
|
||||
Q(name__icontains=value) |
|
||||
Q(part_id__icontains=value) |
|
||||
Q(serial__iexact=value) |
|
||||
Q(asset_tag__iexact=value) |
|
||||
Q(serial__icontains=value) |
|
||||
Q(asset_tag__icontains=value) |
|
||||
Q(description__icontains=value)
|
||||
)
|
||||
return queryset.filter(qs_filter)
|
||||
|
||||
|
||||
class VirtualChassisFilter(django_filters.FilterSet):
|
||||
class VirtualChassisFilterSet(django_filters.FilterSet):
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
region_id = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='master__site__region__in',
|
||||
label='Region (ID)',
|
||||
)
|
||||
region = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='master__site__region__in',
|
||||
to_field_name='slug',
|
||||
label='Region (slug)',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='master__site',
|
||||
queryset=Site.objects.all(),
|
||||
@@ -918,16 +1056,16 @@ class VirtualChassisFilter(django_filters.FilterSet):
|
||||
return queryset.filter(qs_filter)
|
||||
|
||||
|
||||
class CableFilter(django_filters.FilterSet):
|
||||
class CableFilterSet(django_filters.FilterSet):
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
type = django_filters.MultipleChoiceFilter(
|
||||
choices=CABLE_TYPE_CHOICES
|
||||
choices=CableTypeChoices
|
||||
)
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
choices=CONNECTION_STATUS_CHOICES
|
||||
choices=CableStatusChoices
|
||||
)
|
||||
color = django_filters.MultipleChoiceFilter(
|
||||
choices=COLOR_CHOICES
|
||||
@@ -935,7 +1073,7 @@ class CableFilter(django_filters.FilterSet):
|
||||
device_id = MultiValueNumberFilter(
|
||||
method='filter_device'
|
||||
)
|
||||
device = MultiValueNumberFilter(
|
||||
device = MultiValueCharFilter(
|
||||
method='filter_device',
|
||||
field_name='device__name'
|
||||
)
|
||||
@@ -955,6 +1093,14 @@ class CableFilter(django_filters.FilterSet):
|
||||
method='filter_device',
|
||||
field_name='device__site__slug'
|
||||
)
|
||||
tenant_id = MultiValueNumberFilter(
|
||||
method='filter_device',
|
||||
field_name='device__tenant_id'
|
||||
)
|
||||
tenant = MultiValueNumberFilter(
|
||||
method='filter_device',
|
||||
field_name='device__tenant__slug'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Cable
|
||||
@@ -973,14 +1119,17 @@ class CableFilter(django_filters.FilterSet):
|
||||
return queryset
|
||||
|
||||
|
||||
class ConsoleConnectionFilter(django_filters.FilterSet):
|
||||
class ConsoleConnectionFilterSet(django_filters.FilterSet):
|
||||
site = django_filters.CharFilter(
|
||||
method='filter_site',
|
||||
label='Site (slug)',
|
||||
)
|
||||
device = django_filters.CharFilter(
|
||||
device_id = MultiValueNumberFilter(
|
||||
method='filter_device'
|
||||
)
|
||||
device = MultiValueCharFilter(
|
||||
method='filter_device',
|
||||
label='Device',
|
||||
field_name='device__name'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@@ -993,22 +1142,25 @@ class ConsoleConnectionFilter(django_filters.FilterSet):
|
||||
return queryset.filter(connected_endpoint__device__site__slug=value)
|
||||
|
||||
def filter_device(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
if not value:
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(device__name__icontains=value) |
|
||||
Q(connected_endpoint__device__name__icontains=value)
|
||||
Q(**{'{}__in'.format(name): value}) |
|
||||
Q(**{'connected_endpoint__{}__in'.format(name): value})
|
||||
)
|
||||
|
||||
|
||||
class PowerConnectionFilter(django_filters.FilterSet):
|
||||
class PowerConnectionFilterSet(django_filters.FilterSet):
|
||||
site = django_filters.CharFilter(
|
||||
method='filter_site',
|
||||
label='Site (slug)',
|
||||
)
|
||||
device = django_filters.CharFilter(
|
||||
device_id = MultiValueNumberFilter(
|
||||
method='filter_device'
|
||||
)
|
||||
device = MultiValueCharFilter(
|
||||
method='filter_device',
|
||||
label='Device',
|
||||
field_name='device__name'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@@ -1021,22 +1173,25 @@ class PowerConnectionFilter(django_filters.FilterSet):
|
||||
return queryset.filter(_connected_poweroutlet__device__site__slug=value)
|
||||
|
||||
def filter_device(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
if not value:
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(device__name__icontains=value) |
|
||||
Q(_connected_poweroutlet__device__name__icontains=value)
|
||||
Q(**{'{}__in'.format(name): value}) |
|
||||
Q(**{'_connected_poweroutlet__{}__in'.format(name): value})
|
||||
)
|
||||
|
||||
|
||||
class InterfaceConnectionFilter(django_filters.FilterSet):
|
||||
class InterfaceConnectionFilterSet(django_filters.FilterSet):
|
||||
site = django_filters.CharFilter(
|
||||
method='filter_site',
|
||||
label='Site (slug)',
|
||||
)
|
||||
device = django_filters.CharFilter(
|
||||
device_id = MultiValueNumberFilter(
|
||||
method='filter_device'
|
||||
)
|
||||
device = MultiValueCharFilter(
|
||||
method='filter_device',
|
||||
label='Device',
|
||||
field_name='device__name'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@@ -1052,15 +1207,15 @@ class InterfaceConnectionFilter(django_filters.FilterSet):
|
||||
)
|
||||
|
||||
def filter_device(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
if not value:
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(device__name__icontains=value) |
|
||||
Q(_connected_interface__device__name__icontains=value)
|
||||
Q(**{'{}__in'.format(name): value}) |
|
||||
Q(**{'_connected_interface__{}__in'.format(name): value})
|
||||
)
|
||||
|
||||
|
||||
class PowerPanelFilter(django_filters.FilterSet):
|
||||
class PowerPanelFilterSet(django_filters.FilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@@ -1069,6 +1224,17 @@ class PowerPanelFilter(django_filters.FilterSet):
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
region_id = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='site__region__in',
|
||||
label='Region (ID)',
|
||||
)
|
||||
region = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='site__region__in',
|
||||
to_field_name='slug',
|
||||
label='Region (slug)',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Site.objects.all(),
|
||||
label='Site (ID)',
|
||||
@@ -1098,7 +1264,7 @@ class PowerPanelFilter(django_filters.FilterSet):
|
||||
return queryset.filter(qs_filter)
|
||||
|
||||
|
||||
class PowerFeedFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
class PowerFeedFilterSet(CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@@ -1107,6 +1273,17 @@ class PowerFeedFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
region_id = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='power_panel__site__region__in',
|
||||
label='Region (ID)',
|
||||
)
|
||||
region = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='power_panel__site__region__in',
|
||||
to_field_name='slug',
|
||||
label='Region (slug)',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='power_panel__site',
|
||||
queryset=Site.objects.all(),
|
||||
|
||||
@@ -1910,7 +1910,7 @@
|
||||
"site": 1,
|
||||
"rack": 1,
|
||||
"position": 1,
|
||||
"face": 0,
|
||||
"face": "front",
|
||||
"status": true,
|
||||
"primary_ip4": 1,
|
||||
"primary_ip6": null,
|
||||
@@ -1931,7 +1931,7 @@
|
||||
"site": 1,
|
||||
"rack": 1,
|
||||
"position": 17,
|
||||
"face": 0,
|
||||
"face": "rear",
|
||||
"status": true,
|
||||
"primary_ip4": 5,
|
||||
"primary_ip6": null,
|
||||
@@ -1952,7 +1952,7 @@
|
||||
"site": 1,
|
||||
"rack": 1,
|
||||
"position": 33,
|
||||
"face": 0,
|
||||
"face": "rear",
|
||||
"status": true,
|
||||
"primary_ip4": null,
|
||||
"primary_ip6": null,
|
||||
@@ -1973,7 +1973,7 @@
|
||||
"site": 1,
|
||||
"rack": 1,
|
||||
"position": 34,
|
||||
"face": 0,
|
||||
"face": "rear",
|
||||
"status": true,
|
||||
"primary_ip4": null,
|
||||
"primary_ip6": null,
|
||||
@@ -1994,7 +1994,7 @@
|
||||
"site": 1,
|
||||
"rack": 2,
|
||||
"position": 34,
|
||||
"face": 0,
|
||||
"face": "rear",
|
||||
"status": true,
|
||||
"primary_ip4": null,
|
||||
"primary_ip6": null,
|
||||
@@ -2015,7 +2015,7 @@
|
||||
"site": 1,
|
||||
"rack": 2,
|
||||
"position": 33,
|
||||
"face": 0,
|
||||
"face": "rear",
|
||||
"status": true,
|
||||
"primary_ip4": null,
|
||||
"primary_ip6": null,
|
||||
@@ -2036,7 +2036,7 @@
|
||||
"site": 1,
|
||||
"rack": 2,
|
||||
"position": 1,
|
||||
"face": 0,
|
||||
"face": "rear",
|
||||
"status": true,
|
||||
"primary_ip4": 3,
|
||||
"primary_ip6": null,
|
||||
@@ -2057,7 +2057,7 @@
|
||||
"site": 1,
|
||||
"rack": 2,
|
||||
"position": 17,
|
||||
"face": 0,
|
||||
"face": "rear",
|
||||
"status": true,
|
||||
"primary_ip4": 19,
|
||||
"primary_ip6": null,
|
||||
@@ -2078,7 +2078,7 @@
|
||||
"site": 1,
|
||||
"rack": 1,
|
||||
"position": 42,
|
||||
"face": 0,
|
||||
"face": "rear",
|
||||
"status": true,
|
||||
"primary_ip4": null,
|
||||
"primary_ip6": null,
|
||||
@@ -2099,7 +2099,7 @@
|
||||
"site": 1,
|
||||
"rack": 1,
|
||||
"position": null,
|
||||
"face": null,
|
||||
"face": "",
|
||||
"status": true,
|
||||
"primary_ip4": null,
|
||||
"primary_ip6": null,
|
||||
@@ -2120,7 +2120,7 @@
|
||||
"site": 1,
|
||||
"rack": 2,
|
||||
"position": null,
|
||||
"face": null,
|
||||
"face": "",
|
||||
"status": true,
|
||||
"primary_ip4": null,
|
||||
"primary_ip6": null,
|
||||
|
||||
@@ -1,195 +0,0 @@
|
||||
[
|
||||
{
|
||||
"model": "dcim.devicerole",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"name": "Console Server",
|
||||
"slug": "console-server",
|
||||
"color": "009688"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.devicerole",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"name": "Core Switch",
|
||||
"slug": "core-switch",
|
||||
"color": "2196f3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.devicerole",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"name": "Distribution Switch",
|
||||
"slug": "distribution-switch",
|
||||
"color": "2196f3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.devicerole",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"name": "Access Switch",
|
||||
"slug": "access-switch",
|
||||
"color": "2196f3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.devicerole",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"name": "Management Switch",
|
||||
"slug": "management-switch",
|
||||
"color": "ff9800"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.devicerole",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"name": "Firewall",
|
||||
"slug": "firewall",
|
||||
"color": "f44336"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.devicerole",
|
||||
"pk": 7,
|
||||
"fields": {
|
||||
"name": "Router",
|
||||
"slug": "router",
|
||||
"color": "9c27b0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.devicerole",
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"name": "Server",
|
||||
"slug": "server",
|
||||
"color": "9e9e9e"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.devicerole",
|
||||
"pk": 9,
|
||||
"fields": {
|
||||
"name": "PDU",
|
||||
"slug": "pdu",
|
||||
"color": "607d8b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.manufacturer",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"name": "APC",
|
||||
"slug": "apc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.manufacturer",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"name": "Cisco",
|
||||
"slug": "cisco"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.manufacturer",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"name": "Dell",
|
||||
"slug": "dell"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.manufacturer",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"name": "HP",
|
||||
"slug": "hp"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.manufacturer",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"name": "Juniper",
|
||||
"slug": "juniper"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.manufacturer",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"name": "Arista",
|
||||
"slug": "arista"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.manufacturer",
|
||||
"pk": 7,
|
||||
"fields": {
|
||||
"name": "Opengear",
|
||||
"slug": "opengear"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.manufacturer",
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"name": "Super Micro",
|
||||
"slug": "super-micro"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.platform",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"name": "Cisco IOS",
|
||||
"slug": "cisco-ios"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.platform",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"name": "Cisco NX-OS",
|
||||
"slug": "cisco-nx-os"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.platform",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"name": "Juniper Junos",
|
||||
"slug": "juniper-junos"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.platform",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"name": "Arista EOS",
|
||||
"slug": "arista-eos"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.platform",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"name": "Linux",
|
||||
"slug": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "dcim.platform",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"name": "Opengear",
|
||||
"slug": "opengear"
|
||||
}
|
||||
}
|
||||
]
|
||||
1047
netbox/dcim/forms.py
1047
netbox/dcim/forms.py
File diff suppressed because it is too large
Load Diff
@@ -1,259 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.14 on 2018-07-31 02:06
|
||||
import dcim.fields
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import utilities.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [('dcim', '0002_auto_20160622_1821'), ('dcim', '0003_auto_20160628_1721'), ('dcim', '0004_auto_20160701_2049'), ('dcim', '0005_auto_20160706_1722'), ('dcim', '0006_add_device_primary_ip4_ip6'), ('dcim', '0007_device_copy_primary_ip'), ('dcim', '0008_device_remove_primary_ip'), ('dcim', '0009_site_32bit_asn_support'), ('dcim', '0010_devicebay_installed_device_set_null'), ('dcim', '0011_devicetype_part_number'), ('dcim', '0012_site_rack_device_add_tenant'), ('dcim', '0013_add_interface_form_factors'), ('dcim', '0014_rack_add_type_width'), ('dcim', '0015_rack_add_u_height_validator'), ('dcim', '0016_module_add_manufacturer'), ('dcim', '0017_rack_add_role'), ('dcim', '0018_device_add_asset_tag'), ('dcim', '0019_new_iface_form_factors'), ('dcim', '0020_rack_desc_units'), ('dcim', '0021_add_ff_flexstack'), ('dcim', '0022_color_names_to_rgb')]
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0001_initial'),
|
||||
('ipam', '0001_initial'),
|
||||
('tenancy', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='rack',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.Rack'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleserverporttemplate',
|
||||
name='device_type',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cs_port_templates', to='dcim.DeviceType'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleserverport',
|
||||
name='device',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cs_ports', to='dcim.Device'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleporttemplate',
|
||||
name='device_type',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='console_port_templates', to='dcim.DeviceType'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleport',
|
||||
name='cs_port',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='connected_console', to='dcim.ConsoleServerPort', verbose_name=b'Console server port'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleport',
|
||||
name='device',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='console_ports', to='dcim.Device'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='rackgroup',
|
||||
unique_together=set([('site', 'name'), ('site', 'slug')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='rack',
|
||||
unique_together=set([('site', 'facility_id'), ('site', 'name')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='powerporttemplate',
|
||||
unique_together=set([('device_type', 'name')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='powerport',
|
||||
unique_together=set([('device', 'name')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='poweroutlettemplate',
|
||||
unique_together=set([('device_type', 'name')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='poweroutlet',
|
||||
unique_together=set([('device', 'name')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='module',
|
||||
unique_together=set([('device', 'parent', 'name')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='interfacetemplate',
|
||||
unique_together=set([('device_type', 'name')]),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='mac_address',
|
||||
field=dcim.fields.MACAddressField(blank=True, null=True, verbose_name=b'MAC Address'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='interface',
|
||||
unique_together=set([('device', 'name')]),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='devicetype',
|
||||
name='subdevice_role',
|
||||
field=models.NullBooleanField(choices=[(None, b'None'), (True, b'Parent'), (False, b'Child')], default=None, help_text=b'Parent devices house child devices in device bays. Select "None" if this device type is neither a parent nor a child.', verbose_name=b'Parent/child status'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='devicetype',
|
||||
unique_together=set([('manufacturer', 'slug'), ('manufacturer', 'model')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='device',
|
||||
unique_together=set([('rack', 'position', 'face')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='consoleserverporttemplate',
|
||||
unique_together=set([('device_type', 'name')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='consoleserverport',
|
||||
unique_together=set([('device', 'name')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='consoleporttemplate',
|
||||
unique_together=set([('device_type', 'name')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='consoleport',
|
||||
unique_together=set([('device', 'name')]),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DeviceBay',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, verbose_name=b'Name')),
|
||||
('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='device_bays', to='dcim.Device')),
|
||||
('installed_device', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='parent_bay', to='dcim.Device')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['device', 'name'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DeviceBayTemplate',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=30)),
|
||||
('device_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='device_bay_templates', to='dcim.DeviceType')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['device_type', 'name'],
|
||||
},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='devicebaytemplate',
|
||||
unique_together=set([('device_type', 'name')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='devicebay',
|
||||
unique_together=set([('device', 'name')]),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='primary_ip4',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='primary_ip4_for', to='ipam.IPAddress', verbose_name=b'Primary IPv4'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='primary_ip6',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='primary_ip6_for', to='ipam.IPAddress', verbose_name=b'Primary IPv6'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='site',
|
||||
name='asn',
|
||||
field=dcim.fields.ASNField(blank=True, null=True, verbose_name=b'ASN'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicebay',
|
||||
name='installed_device',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parent_bay', to='dcim.Device'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='devicetype',
|
||||
name='part_number',
|
||||
field=models.CharField(blank=True, help_text=b'Discrete part number (optional)', max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='tenant',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='tenancy.Tenant'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='tenant',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='racks', to='tenancy.Tenant'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='site',
|
||||
name='tenant',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='sites', to='tenancy.Tenant'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='type',
|
||||
field=models.PositiveSmallIntegerField(blank=True, choices=[(100, b'2-post frame'), (200, b'4-post frame'), (300, b'4-post cabinet'), (1000, b'Wall-mounted frame'), (1100, b'Wall-mounted cabinet')], null=True, verbose_name=b'Type'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='width',
|
||||
field=models.PositiveSmallIntegerField(choices=[(19, b'19 inches'), (23, b'23 inches')], default=19, help_text=b'Rail-to-rail width', verbose_name=b'Width'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='u_height',
|
||||
field=models.PositiveSmallIntegerField(default=42, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)], verbose_name=b'Height (U)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='module',
|
||||
name='manufacturer',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='modules', to='dcim.Manufacturer'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='RackRole',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, unique=True)),
|
||||
('slug', models.SlugField(unique=True)),
|
||||
('color', utilities.fields.ColorField(max_length=6)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='role',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='racks', to='dcim.RackRole'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='asset_tag',
|
||||
field=utilities.fields.NullableCharField(blank=True, help_text=b'A unique tag used to identify this device', max_length=50, null=True, unique=True, verbose_name=b'Asset tag'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='desc_units',
|
||||
field=models.BooleanField(default=False, help_text=b'Units are numbered top-to-bottom', verbose_name=b'Descending units'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='position',
|
||||
field=models.PositiveSmallIntegerField(blank=True, help_text=b'The lowest-numbered unit occupied by the device', null=True, validators=[django.core.validators.MinValueValidator(1)], verbose_name=b'Position (U)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[[b'Virtual interfaces', [[0, b'Virtual']]], [b'Ethernet (fixed)', [[800, b'100BASE-TX (10/100ME)'], [1000, b'1000BASE-T (1GE)'], [1150, b'10GBASE-T (10GE)']]], [b'Ethernet (modular)', [[1050, b'GBIC (1GE)'], [1100, b'SFP (1GE)'], [1200, b'SFP+ (10GE)'], [1300, b'XFP (10GE)'], [1310, b'XENPAK (10GE)'], [1320, b'X2 (10GE)'], [1350, b'SFP28 (25GE)'], [1400, b'QSFP+ (40GE)'], [1500, b'CFP (100GE)'], [1600, b'QSFP28 (100GE)']]], [b'FibreChannel', [[3010, b'SFP (1GFC)'], [3020, b'SFP (2GFC)'], [3040, b'SFP (4GFC)'], [3080, b'SFP+ (8GFC)'], [3160, b'SFP+ (16GFC)']]], [b'Serial', [[4000, b'T1 (1.544 Mbps)'], [4010, b'E1 (2.048 Mbps)'], [4040, b'T3 (45 Mbps)'], [4050, b'E3 (34 Mbps)']]], [b'Stacking', [[5000, b'Cisco StackWise'], [5050, b'Cisco StackWise Plus'], [5100, b'Cisco FlexStack'], [5150, b'Cisco FlexStack Plus']]], [b'Other', [[32767, b'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[[b'Virtual interfaces', [[0, b'Virtual']]], [b'Ethernet (fixed)', [[800, b'100BASE-TX (10/100ME)'], [1000, b'1000BASE-T (1GE)'], [1150, b'10GBASE-T (10GE)']]], [b'Ethernet (modular)', [[1050, b'GBIC (1GE)'], [1100, b'SFP (1GE)'], [1200, b'SFP+ (10GE)'], [1300, b'XFP (10GE)'], [1310, b'XENPAK (10GE)'], [1320, b'X2 (10GE)'], [1350, b'SFP28 (25GE)'], [1400, b'QSFP+ (40GE)'], [1500, b'CFP (100GE)'], [1600, b'QSFP28 (100GE)']]], [b'FibreChannel', [[3010, b'SFP (1GFC)'], [3020, b'SFP (2GFC)'], [3040, b'SFP (4GFC)'], [3080, b'SFP+ (8GFC)'], [3160, b'SFP+ (16GFC)']]], [b'Serial', [[4000, b'T1 (1.544 Mbps)'], [4010, b'E1 (2.048 Mbps)'], [4040, b'T3 (45 Mbps)'], [4050, b'E3 (34 Mbps)']]], [b'Stacking', [[5000, b'Cisco StackWise'], [5050, b'Cisco StackWise Plus'], [5100, b'Cisco FlexStack'], [5150, b'Cisco FlexStack Plus']]], [b'Other', [[32767, b'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicerole',
|
||||
name='color',
|
||||
field=utilities.fields.ColorField(max_length=6),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,101 @@
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
import dcim.fields
|
||||
|
||||
|
||||
def copy_primary_ip(apps, schema_editor):
|
||||
Device = apps.get_model('dcim', 'Device')
|
||||
for d in Device.objects.select_related('primary_ip'):
|
||||
if not d.primary_ip:
|
||||
continue
|
||||
if d.primary_ip.family == 4:
|
||||
d.primary_ip4 = d.primary_ip
|
||||
elif d.primary_ip.family == 6:
|
||||
d.primary_ip6 = d.primary_ip
|
||||
d.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [('dcim', '0003_auto_20160628_1721'), ('dcim', '0004_auto_20160701_2049'), ('dcim', '0005_auto_20160706_1722'), ('dcim', '0006_add_device_primary_ip4_ip6'), ('dcim', '0007_device_copy_primary_ip'), ('dcim', '0008_device_remove_primary_ip'), ('dcim', '0009_site_32bit_asn_support'), ('dcim', '0010_devicebay_installed_device_set_null')]
|
||||
|
||||
dependencies = [
|
||||
('ipam', '0001_initial'),
|
||||
('dcim', '0002_auto_20160622_1821'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[[0, b'Virtual'], [800, b'10/100M (100BASE-TX)'], [1000, b'1GE (1000BASE-T)'], [1100, b'1GE (SFP)'], [1150, b'10GE (10GBASE-T)'], [1200, b'10GE (SFP+)'], [1300, b'10GE (XFP)'], [1400, b'40GE (QSFP+)']], default=1200),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[[0, b'Virtual'], [800, b'10/100M (100BASE-TX)'], [1000, b'1GE (1000BASE-T)'], [1100, b'1GE (SFP)'], [1150, b'10GE (10GBASE-T)'], [1200, b'10GE (SFP+)'], [1300, b'10GE (XFP)'], [1400, b'40GE (QSFP+)']], default=1200),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='devicetype',
|
||||
name='subdevice_role',
|
||||
field=models.NullBooleanField(choices=[(None, b'None'), (True, b'Parent'), (False, b'Child')], default=None, help_text=b'Parent devices house child devices in device bays. Select "None" if this device type is neither a parent nor a child.', verbose_name=b'Parent/child status'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DeviceBayTemplate',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=30)),
|
||||
('device_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='device_bay_templates', to='dcim.DeviceType')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['device_type', 'name'],
|
||||
'unique_together': {('device_type', 'name')},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DeviceBay',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, verbose_name=b'Name')),
|
||||
('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='device_bays', to='dcim.Device')),
|
||||
('installed_device', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='parent_bay', to='dcim.Device')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['device', 'name'],
|
||||
'unique_together': {('device', 'name')},
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='mac_address',
|
||||
field=dcim.fields.MACAddressField(blank=True, null=True, verbose_name=b'MAC Address'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='primary_ip4',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='primary_ip4_for', to='ipam.IPAddress', verbose_name=b'Primary IPv4'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='primary_ip6',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='primary_ip6_for', to='ipam.IPAddress', verbose_name=b'Primary IPv6'),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=copy_primary_ip,
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='device',
|
||||
name='primary_ip',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='site',
|
||||
name='asn',
|
||||
field=dcim.fields.ASNField(blank=True, null=True, verbose_name=b'ASN'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicebay',
|
||||
name='installed_device',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parent_bay', to='dcim.Device'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,154 @@
|
||||
import django.core.validators
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
import utilities.fields
|
||||
|
||||
COLOR_CONVERSION = {
|
||||
'teal': '009688',
|
||||
'green': '4caf50',
|
||||
'blue': '2196f3',
|
||||
'purple': '9c27b0',
|
||||
'yellow': 'ffeb3b',
|
||||
'orange': 'ff9800',
|
||||
'red': 'f44336',
|
||||
'light_gray': 'c0c0c0',
|
||||
'medium_gray': '9e9e9e',
|
||||
'dark_gray': '607d8b',
|
||||
}
|
||||
|
||||
|
||||
def color_names_to_rgb(apps, schema_editor):
|
||||
RackRole = apps.get_model('dcim', 'RackRole')
|
||||
DeviceRole = apps.get_model('dcim', 'DeviceRole')
|
||||
for color_name, color_rgb in COLOR_CONVERSION.items():
|
||||
RackRole.objects.filter(color=color_name).update(color=color_rgb)
|
||||
DeviceRole.objects.filter(color=color_name).update(color=color_rgb)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [('dcim', '0011_devicetype_part_number'), ('dcim', '0012_site_rack_device_add_tenant'), ('dcim', '0013_add_interface_form_factors'), ('dcim', '0014_rack_add_type_width'), ('dcim', '0015_rack_add_u_height_validator'), ('dcim', '0016_module_add_manufacturer'), ('dcim', '0017_rack_add_role'), ('dcim', '0018_device_add_asset_tag'), ('dcim', '0019_new_iface_form_factors'), ('dcim', '0020_rack_desc_units'), ('dcim', '0021_add_ff_flexstack'), ('dcim', '0022_color_names_to_rgb')]
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0010_devicebay_installed_device_set_null'),
|
||||
('tenancy', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='devicetype',
|
||||
name='part_number',
|
||||
field=models.CharField(blank=True, help_text=b'Discrete part number (optional)', max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='tenant',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='tenancy.Tenant'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='tenant',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='racks', to='tenancy.Tenant'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='site',
|
||||
name='tenant',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='sites', to='tenancy.Tenant'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[[b'Virtual interfaces', [[0, b'Virtual']]], [b'Ethernet', [[800, b'100BASE-TX (10/100M)'], [1000, b'1000BASE-T (1GE)'], [1150, b'10GBASE-T (10GE)']]], [b'Modular', [[1050, b'GBIC (1GE)'], [1100, b'SFP (1GE)'], [1300, b'XFP (10GE)'], [1200, b'SFP+ (10GE)'], [1400, b'QSFP+ (40GE)'], [1500, b'CFP (100GE)'], [1600, b'QSFP28 (100GE)']]], [b'Serial', [[4000, b'T1 (1.544 Mbps)'], [4010, b'E1 (2.048 Mbps)'], [4040, b'T3 (45 Mbps)'], [4050, b'E3 (34 Mbps)']]], [b'Stacking', [[5000, b'Cisco StackWise'], [5050, b'Cisco StackWise Plus']]]], default=1200),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[[b'Virtual interfaces', [[0, b'Virtual']]], [b'Ethernet', [[800, b'100BASE-TX (10/100M)'], [1000, b'1000BASE-T (1GE)'], [1150, b'10GBASE-T (10GE)']]], [b'Modular', [[1050, b'GBIC (1GE)'], [1100, b'SFP (1GE)'], [1300, b'XFP (10GE)'], [1200, b'SFP+ (10GE)'], [1400, b'QSFP+ (40GE)'], [1500, b'CFP (100GE)'], [1600, b'QSFP28 (100GE)']]], [b'Serial', [[4000, b'T1 (1.544 Mbps)'], [4010, b'E1 (2.048 Mbps)'], [4040, b'T3 (45 Mbps)'], [4050, b'E3 (34 Mbps)']]], [b'Stacking', [[5000, b'Cisco StackWise'], [5050, b'Cisco StackWise Plus']]]], default=1200),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='type',
|
||||
field=models.PositiveSmallIntegerField(blank=True, choices=[(100, b'2-post frame'), (200, b'4-post frame'), (300, b'4-post cabinet'), (1000, b'Wall-mounted frame'), (1100, b'Wall-mounted cabinet')], null=True, verbose_name=b'Type'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='width',
|
||||
field=models.PositiveSmallIntegerField(choices=[(19, b'19 inches'), (23, b'23 inches')], default=19, help_text=b'Rail-to-rail width', verbose_name=b'Width'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='u_height',
|
||||
field=models.PositiveSmallIntegerField(default=42, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)], verbose_name=b'Height (U)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='module',
|
||||
name='manufacturer',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='modules', to='dcim.Manufacturer'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='RackRole',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, unique=True)),
|
||||
('slug', models.SlugField(unique=True)),
|
||||
('color', models.CharField(choices=[[b'teal', b'Teal'], [b'green', b'Green'], [b'blue', b'Blue'], [b'purple', b'Purple'], [b'yellow', b'Yellow'], [b'orange', b'Orange'], [b'red', b'Red'], [b'light_gray', b'Light Gray'], [b'medium_gray', b'Medium Gray'], [b'dark_gray', b'Dark Gray']], max_length=30)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='role',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='racks', to='dcim.RackRole'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='asset_tag',
|
||||
field=utilities.fields.NullableCharField(blank=True, help_text=b'A unique tag used to identify this device', max_length=50, null=True, unique=True, verbose_name=b'Asset tag'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[[b'Virtual interfaces', [[0, b'Virtual']]], [b'Ethernet (fixed)', [[800, b'100BASE-TX (10/100ME)'], [1000, b'1000BASE-T (1GE)'], [1150, b'10GBASE-T (10GE)']]], [b'Ethernet (modular)', [[1050, b'GBIC (1GE)'], [1100, b'SFP (1GE)'], [1200, b'SFP+ (10GE)'], [1300, b'XFP (10GE)'], [1310, b'XENPAK (10GE)'], [1320, b'X2 (10GE)'], [1350, b'SFP28 (25GE)'], [1400, b'QSFP+ (40GE)'], [1500, b'CFP (100GE)'], [1600, b'QSFP28 (100GE)']]], [b'FibreChannel', [[3010, b'SFP (1GFC)'], [3020, b'SFP (2GFC)'], [3040, b'SFP (4GFC)'], [3080, b'SFP+ (8GFC)'], [3160, b'SFP+ (16GFC)']]], [b'Serial', [[4000, b'T1 (1.544 Mbps)'], [4010, b'E1 (2.048 Mbps)'], [4040, b'T3 (45 Mbps)'], [4050, b'E3 (34 Mbps)']]], [b'Stacking', [[5000, b'Cisco StackWise'], [5050, b'Cisco StackWise Plus']]], [b'Other', [[32767, b'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[[b'Virtual interfaces', [[0, b'Virtual']]], [b'Ethernet (fixed)', [[800, b'100BASE-TX (10/100ME)'], [1000, b'1000BASE-T (1GE)'], [1150, b'10GBASE-T (10GE)']]], [b'Ethernet (modular)', [[1050, b'GBIC (1GE)'], [1100, b'SFP (1GE)'], [1200, b'SFP+ (10GE)'], [1300, b'XFP (10GE)'], [1310, b'XENPAK (10GE)'], [1320, b'X2 (10GE)'], [1350, b'SFP28 (25GE)'], [1400, b'QSFP+ (40GE)'], [1500, b'CFP (100GE)'], [1600, b'QSFP28 (100GE)']]], [b'FibreChannel', [[3010, b'SFP (1GFC)'], [3020, b'SFP (2GFC)'], [3040, b'SFP (4GFC)'], [3080, b'SFP+ (8GFC)'], [3160, b'SFP+ (16GFC)']]], [b'Serial', [[4000, b'T1 (1.544 Mbps)'], [4010, b'E1 (2.048 Mbps)'], [4040, b'T3 (45 Mbps)'], [4050, b'E3 (34 Mbps)']]], [b'Stacking', [[5000, b'Cisco StackWise'], [5050, b'Cisco StackWise Plus']]], [b'Other', [[32767, b'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='desc_units',
|
||||
field=models.BooleanField(default=False, help_text=b'Units are numbered top-to-bottom', verbose_name=b'Descending units'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='position',
|
||||
field=models.PositiveSmallIntegerField(blank=True, help_text=b'The lowest-numbered unit occupied by the device', null=True, validators=[django.core.validators.MinValueValidator(1)], verbose_name=b'Position (U)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[[b'Virtual interfaces', [[0, b'Virtual']]], [b'Ethernet (fixed)', [[800, b'100BASE-TX (10/100ME)'], [1000, b'1000BASE-T (1GE)'], [1150, b'10GBASE-T (10GE)']]], [b'Ethernet (modular)', [[1050, b'GBIC (1GE)'], [1100, b'SFP (1GE)'], [1200, b'SFP+ (10GE)'], [1300, b'XFP (10GE)'], [1310, b'XENPAK (10GE)'], [1320, b'X2 (10GE)'], [1350, b'SFP28 (25GE)'], [1400, b'QSFP+ (40GE)'], [1500, b'CFP (100GE)'], [1600, b'QSFP28 (100GE)']]], [b'FibreChannel', [[3010, b'SFP (1GFC)'], [3020, b'SFP (2GFC)'], [3040, b'SFP (4GFC)'], [3080, b'SFP+ (8GFC)'], [3160, b'SFP+ (16GFC)']]], [b'Serial', [[4000, b'T1 (1.544 Mbps)'], [4010, b'E1 (2.048 Mbps)'], [4040, b'T3 (45 Mbps)'], [4050, b'E3 (34 Mbps)']]], [b'Stacking', [[5000, b'Cisco StackWise'], [5050, b'Cisco StackWise Plus'], [5100, b'Cisco FlexStack'], [5150, b'Cisco FlexStack Plus']]], [b'Other', [[32767, b'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[[b'Virtual interfaces', [[0, b'Virtual']]], [b'Ethernet (fixed)', [[800, b'100BASE-TX (10/100ME)'], [1000, b'1000BASE-T (1GE)'], [1150, b'10GBASE-T (10GE)']]], [b'Ethernet (modular)', [[1050, b'GBIC (1GE)'], [1100, b'SFP (1GE)'], [1200, b'SFP+ (10GE)'], [1300, b'XFP (10GE)'], [1310, b'XENPAK (10GE)'], [1320, b'X2 (10GE)'], [1350, b'SFP28 (25GE)'], [1400, b'QSFP+ (40GE)'], [1500, b'CFP (100GE)'], [1600, b'QSFP28 (100GE)']]], [b'FibreChannel', [[3010, b'SFP (1GFC)'], [3020, b'SFP (2GFC)'], [3040, b'SFP (4GFC)'], [3080, b'SFP+ (8GFC)'], [3160, b'SFP+ (16GFC)']]], [b'Serial', [[4000, b'T1 (1.544 Mbps)'], [4010, b'E1 (2.048 Mbps)'], [4040, b'T3 (45 Mbps)'], [4050, b'E3 (34 Mbps)']]], [b'Stacking', [[5000, b'Cisco StackWise'], [5050, b'Cisco StackWise Plus'], [5100, b'Cisco FlexStack'], [5150, b'Cisco FlexStack Plus']]], [b'Other', [[32767, b'Other']]]], default=1200),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=color_names_to_rgb,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicerole',
|
||||
name='color',
|
||||
field=utilities.fields.ColorField(max_length=6),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rackrole',
|
||||
name='color',
|
||||
field=utilities.fields.ColorField(max_length=6),
|
||||
),
|
||||
]
|
||||
@@ -1,12 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.14 on 2018-07-31 02:13
|
||||
import dcim.fields
|
||||
from django.conf import settings
|
||||
import django.contrib.postgres.fields
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import mptt.fields
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
import dcim.fields
|
||||
import utilities.fields
|
||||
|
||||
|
||||
@@ -32,8 +31,8 @@ class Migration(migrations.Migration):
|
||||
replaces = [('dcim', '0023_devicetype_comments'), ('dcim', '0024_site_add_contact_fields'), ('dcim', '0025_devicetype_add_interface_ordering'), ('dcim', '0026_add_rack_reservations'), ('dcim', '0027_device_add_site'), ('dcim', '0028_device_copy_rack_to_site'), ('dcim', '0029_allow_rackless_devices'), ('dcim', '0030_interface_add_lag'), ('dcim', '0031_regions'), ('dcim', '0032_device_increase_name_length'), ('dcim', '0033_rackreservation_rack_editable'), ('dcim', '0034_rename_module_to_inventoryitem'), ('dcim', '0035_device_expand_status_choices'), ('dcim', '0036_add_ff_juniper_vcp'), ('dcim', '0037_unicode_literals'), ('dcim', '0038_wireless_interfaces'), ('dcim', '0039_interface_add_enabled_mtu'), ('dcim', '0040_inventoryitem_add_asset_tag_description'), ('dcim', '0041_napalm_integration'), ('dcim', '0042_interface_ff_10ge_cx4'), ('dcim', '0043_device_component_name_lengths')]
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('dcim', '0022_color_names_to_rgb'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
@@ -94,10 +93,15 @@ class Migration(migrations.Migration):
|
||||
name='site',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.Site'),
|
||||
),
|
||||
migrations.AddField(
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='lag',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_interfaces', to='dcim.Interface', verbose_name='Parent LAG'),
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[[b'Virtual interfaces', [[0, b'Virtual'], [200, b'Link Aggregation Group (LAG)']]], [b'Ethernet (fixed)', [[800, b'100BASE-TX (10/100ME)'], [1000, b'1000BASE-T (1GE)'], [1150, b'10GBASE-T (10GE)']]], [b'Ethernet (modular)', [[1050, b'GBIC (1GE)'], [1100, b'SFP (1GE)'], [1200, b'SFP+ (10GE)'], [1300, b'XFP (10GE)'], [1310, b'XENPAK (10GE)'], [1320, b'X2 (10GE)'], [1350, b'SFP28 (25GE)'], [1400, b'QSFP+ (40GE)'], [1500, b'CFP (100GE)'], [1600, b'QSFP28 (100GE)']]], [b'FibreChannel', [[3010, b'SFP (1GFC)'], [3020, b'SFP (2GFC)'], [3040, b'SFP (4GFC)'], [3080, b'SFP+ (8GFC)'], [3160, b'SFP+ (16GFC)']]], [b'Serial', [[4000, b'T1 (1.544 Mbps)'], [4010, b'E1 (2.048 Mbps)'], [4040, b'T3 (45 Mbps)'], [4050, b'E3 (34 Mbps)'], [4050, b'E3 (34 Mbps)']]], [b'Stacking', [[5000, b'Cisco StackWise'], [5050, b'Cisco StackWise Plus'], [5100, b'Cisco FlexStack'], [5150, b'Cisco FlexStack Plus']]], [b'Other', [[32767, b'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[[b'Virtual interfaces', [[0, b'Virtual'], [200, b'Link Aggregation Group (LAG)']]], [b'Ethernet (fixed)', [[800, b'100BASE-TX (10/100ME)'], [1000, b'1000BASE-T (1GE)'], [1150, b'10GBASE-T (10GE)']]], [b'Ethernet (modular)', [[1050, b'GBIC (1GE)'], [1100, b'SFP (1GE)'], [1200, b'SFP+ (10GE)'], [1300, b'XFP (10GE)'], [1310, b'XENPAK (10GE)'], [1320, b'X2 (10GE)'], [1350, b'SFP28 (25GE)'], [1400, b'QSFP+ (40GE)'], [1500, b'CFP (100GE)'], [1600, b'QSFP28 (100GE)']]], [b'FibreChannel', [[3010, b'SFP (1GFC)'], [3020, b'SFP (2GFC)'], [3040, b'SFP (4GFC)'], [3080, b'SFP+ (8GFC)'], [3160, b'SFP+ (16GFC)']]], [b'Serial', [[4000, b'T1 (1.544 Mbps)'], [4010, b'E1 (2.048 Mbps)'], [4040, b'T3 (45 Mbps)'], [4050, b'E3 (34 Mbps)'], [4050, b'E3 (34 Mbps)']]], [b'Stacking', [[5000, b'Cisco StackWise'], [5050, b'Cisco StackWise Plus'], [5100, b'Cisco FlexStack'], [5150, b'Cisco FlexStack Plus']]], [b'Other', [[32767, b'Other']]]], default=1200),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Region',
|
||||
@@ -157,7 +161,17 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='status',
|
||||
field=models.PositiveSmallIntegerField(choices=[[1, 'Active'], [0, 'Offline'], [2, 'Planned'], [3, 'Staged'], [4, 'Failed'], [5, 'Inventory']], default=1, verbose_name='Status'),
|
||||
field=models.PositiveSmallIntegerField(choices=[[1, b'Active'], [0, b'Offline'], [2, b'Planned'], [3, b'Staged'], [4, b'Failed'], [5, b'Inventory']], default=1, verbose_name=b'Status'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[[b'Virtual interfaces', [[0, b'Virtual'], [200, b'Link Aggregation Group (LAG)']]], [b'Ethernet (fixed)', [[800, b'100BASE-TX (10/100ME)'], [1000, b'1000BASE-T (1GE)'], [1150, b'10GBASE-T (10GE)']]], [b'Ethernet (modular)', [[1050, b'GBIC (1GE)'], [1100, b'SFP (1GE)'], [1200, b'SFP+ (10GE)'], [1300, b'XFP (10GE)'], [1310, b'XENPAK (10GE)'], [1320, b'X2 (10GE)'], [1350, b'SFP28 (25GE)'], [1400, b'QSFP+ (40GE)'], [1500, b'CFP (100GE)'], [1600, b'QSFP28 (100GE)']]], [b'FibreChannel', [[3010, b'SFP (1GFC)'], [3020, b'SFP (2GFC)'], [3040, b'SFP (4GFC)'], [3080, b'SFP+ (8GFC)'], [3160, b'SFP+ (16GFC)']]], [b'Serial', [[4000, b'T1 (1.544 Mbps)'], [4010, b'E1 (2.048 Mbps)'], [4040, b'T3 (45 Mbps)'], [4050, b'E3 (34 Mbps)'], [4050, b'E3 (34 Mbps)']]], [b'Stacking', [[5000, b'Cisco StackWise'], [5050, b'Cisco StackWise Plus'], [5100, b'Cisco FlexStack'], [5150, b'Cisco FlexStack Plus'], [5200, b'Juniper VCP']]], [b'Other', [[32767, b'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[[b'Virtual interfaces', [[0, b'Virtual'], [200, b'Link Aggregation Group (LAG)']]], [b'Ethernet (fixed)', [[800, b'100BASE-TX (10/100ME)'], [1000, b'1000BASE-T (1GE)'], [1150, b'10GBASE-T (10GE)']]], [b'Ethernet (modular)', [[1050, b'GBIC (1GE)'], [1100, b'SFP (1GE)'], [1200, b'SFP+ (10GE)'], [1300, b'XFP (10GE)'], [1310, b'XENPAK (10GE)'], [1320, b'X2 (10GE)'], [1350, b'SFP28 (25GE)'], [1400, b'QSFP+ (40GE)'], [1500, b'CFP (100GE)'], [1600, b'QSFP28 (100GE)']]], [b'FibreChannel', [[3010, b'SFP (1GFC)'], [3020, b'SFP (2GFC)'], [3040, b'SFP (4GFC)'], [3080, b'SFP+ (8GFC)'], [3160, b'SFP+ (16GFC)']]], [b'Serial', [[4000, b'T1 (1.544 Mbps)'], [4010, b'E1 (2.048 Mbps)'], [4040, b'T3 (45 Mbps)'], [4050, b'E3 (34 Mbps)'], [4050, b'E3 (34 Mbps)']]], [b'Stacking', [[5000, b'Cisco StackWise'], [5050, b'Cisco StackWise Plus'], [5100, b'Cisco FlexStack'], [5150, b'Cisco FlexStack Plus'], [5200, b'Juniper VCP']]], [b'Other', [[32767, b'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='consoleport',
|
||||
@@ -199,6 +213,11 @@ class Migration(migrations.Migration):
|
||||
name='serial',
|
||||
field=models.CharField(blank=True, max_length=50, verbose_name='Serial number'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='status',
|
||||
field=models.PositiveSmallIntegerField(choices=[[1, 'Active'], [0, 'Offline'], [2, 'Planned'], [3, 'Staged'], [4, 'Failed'], [5, 'Inventory']], default=1, verbose_name='Status'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicebay',
|
||||
name='name',
|
||||
@@ -244,6 +263,16 @@ class Migration(migrations.Migration):
|
||||
name='u_height',
|
||||
field=models.PositiveSmallIntegerField(default=1, verbose_name='Height (U)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1600, 'QSFP28 (100GE)']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP']]], ['Other', [[32767, 'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='lag',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_interfaces', to='dcim.Interface', verbose_name='Parent LAG'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='mac_address',
|
||||
@@ -259,6 +288,11 @@ class Migration(migrations.Migration):
|
||||
name='connection_status',
|
||||
field=models.BooleanField(choices=[[False, 'Planned'], [True, 'Connected']], default=True, verbose_name='Status'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1600, 'QSFP28 (100GE)']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP']]], ['Other', [[32767, 'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='mgmt_only',
|
||||
@@ -329,6 +363,16 @@ class Migration(migrations.Migration):
|
||||
name='contact_email',
|
||||
field=models.EmailField(blank=True, max_length=254, verbose_name='Contact E-mail'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP']]], ['Other', [[32767, 'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP']]], ['Other', [[32767, 'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='enabled',
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.14 on 2018-07-31 02:17
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import timezone_field.fields
|
||||
import utilities.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [('dcim', '0044_virtualization'), ('dcim', '0045_devicerole_vm_role'), ('dcim', '0046_rack_lengthen_facility_id'), ('dcim', '0047_more_100ge_form_factors'), ('dcim', '0048_rack_serial'), ('dcim', '0049_rackreservation_change_user'), ('dcim', '0050_interface_vlan_tagging'), ('dcim', '0051_rackreservation_tenant'), ('dcim', '0052_virtual_chassis'), ('dcim', '0053_platform_manufacturer'), ('dcim', '0054_site_status_timezone_description'), ('dcim', '0055_virtualchassis_ordering')]
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0043_device_component_name_lengths'),
|
||||
('ipam', '0020_ipaddress_add_role_carp'),
|
||||
('virtualization', '0001_virtualization'),
|
||||
('tenancy', '0003_unicode_literals'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='cluster',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='devices', to='virtualization.Cluster'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='virtual_machine',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='interfaces', to='virtualization.VirtualMachine'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='device',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='interfaces', to='dcim.Device'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='devicerole',
|
||||
name='vm_role',
|
||||
field=models.BooleanField(default=True, help_text='Virtual machines may be assigned to this role', verbose_name='VM Role'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='facility_id',
|
||||
field=utilities.fields.NullableCharField(blank=True, max_length=50, null=True, verbose_name='Facility ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP']]], ['Other', [[32767, 'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP']]], ['Other', [[32767, 'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='serial',
|
||||
field=models.CharField(blank=True, max_length=50, verbose_name='Serial number'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rackreservation',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='mode',
|
||||
field=models.PositiveSmallIntegerField(blank=True, choices=[[100, 'Access'], [200, 'Tagged'], [300, 'Tagged All']], null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='tagged_vlans',
|
||||
field=models.ManyToManyField(blank=True, related_name='interfaces_as_tagged', to='ipam.VLAN', verbose_name='Tagged VLANs'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='untagged_vlan',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='interfaces_as_untagged', to='ipam.VLAN', verbose_name='Untagged VLAN'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rackreservation',
|
||||
name='tenant',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='rackreservations', to='tenancy.Tenant'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='VirtualChassis',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('domain', models.CharField(blank=True, max_length=30)),
|
||||
('master', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='vc_master_for', to='dcim.Device')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'virtual chassis',
|
||||
'ordering': ['master'],
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='virtual_chassis',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='dcim.VirtualChassis'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='vc_position',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(255)]),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='vc_priority',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(255)]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='device',
|
||||
unique_together=set([('rack', 'position', 'face'), ('virtual_chassis', 'vc_position')]),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='platform',
|
||||
name='manufacturer',
|
||||
field=models.ForeignKey(blank=True, help_text='Optionally limit this platform to devices of a certain manufacturer', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='platforms', to='dcim.Manufacturer'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='platform',
|
||||
name='napalm_driver',
|
||||
field=models.CharField(blank=True, help_text='The name of the NAPALM driver to use when interacting with devices', max_length=50, verbose_name='NAPALM driver'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='site',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='site',
|
||||
name='status',
|
||||
field=models.PositiveSmallIntegerField(choices=[[1, 'Active'], [2, 'Planned'], [4, 'Retired']], default=1),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='site',
|
||||
name='time_zone',
|
||||
field=timezone_field.fields.TimeZoneField(blank=True),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,354 @@
|
||||
import django.contrib.postgres.fields.jsonb
|
||||
import django.core.validators
|
||||
import django.db.models.deletion
|
||||
import taggit.managers
|
||||
import timezone_field.fields
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
import utilities.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [('dcim', '0044_virtualization'), ('dcim', '0045_devicerole_vm_role'), ('dcim', '0046_rack_lengthen_facility_id'), ('dcim', '0047_more_100ge_form_factors'), ('dcim', '0048_rack_serial'), ('dcim', '0049_rackreservation_change_user'), ('dcim', '0050_interface_vlan_tagging'), ('dcim', '0051_rackreservation_tenant'), ('dcim', '0052_virtual_chassis'), ('dcim', '0053_platform_manufacturer'), ('dcim', '0054_site_status_timezone_description'), ('dcim', '0055_virtualchassis_ordering'), ('dcim', '0056_django2'), ('dcim', '0057_tags'), ('dcim', '0058_relax_rack_naming_constraints'), ('dcim', '0059_site_latitude_longitude'), ('dcim', '0060_change_logging'), ('dcim', '0061_platform_napalm_args')]
|
||||
|
||||
dependencies = [
|
||||
('virtualization', '0001_virtualization'),
|
||||
('tenancy', '0003_unicode_literals'),
|
||||
('ipam', '0020_ipaddress_add_role_carp'),
|
||||
('dcim', '0043_device_component_name_lengths'),
|
||||
('taggit', '0002_auto_20150616_2121'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='cluster',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='devices', to='virtualization.Cluster'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='virtual_machine',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='interfaces', to='virtualization.VirtualMachine'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='device',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='interfaces', to='dcim.Device'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='devicerole',
|
||||
name='vm_role',
|
||||
field=models.BooleanField(default=True, help_text='Virtual machines may be assigned to this role', verbose_name='VM Role'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='facility_id',
|
||||
field=utilities.fields.NullableCharField(blank=True, max_length=50, null=True, verbose_name='Facility ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP']]], ['Other', [[32767, 'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP']]], ['Other', [[32767, 'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='serial',
|
||||
field=models.CharField(blank=True, max_length=50, verbose_name='Serial number'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rackreservation',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='mode',
|
||||
field=models.PositiveSmallIntegerField(blank=True, choices=[[100, 'Access'], [200, 'Tagged'], [300, 'Tagged All']], null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='tagged_vlans',
|
||||
field=models.ManyToManyField(blank=True, related_name='interfaces_as_tagged', to='ipam.VLAN', verbose_name='Tagged VLANs'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rackreservation',
|
||||
name='tenant',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='rackreservations', to='tenancy.Tenant'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='VirtualChassis',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('domain', models.CharField(blank=True, max_length=30)),
|
||||
('master', models.OneToOneField(default=1, on_delete=django.db.models.deletion.PROTECT, related_name='vc_master_for', to='dcim.Device')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['master'],
|
||||
'verbose_name_plural': 'virtual chassis',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='virtual_chassis',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='dcim.VirtualChassis'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='vc_position',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(255)]),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='vc_priority',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(255)]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='device',
|
||||
unique_together={('rack', 'position', 'face'), ('virtual_chassis', 'vc_position')},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='platform',
|
||||
name='napalm_driver',
|
||||
field=models.CharField(blank=True, help_text='The name of the NAPALM driver to use when interacting with devices', max_length=50, verbose_name='NAPALM driver'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='site',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='site',
|
||||
name='status',
|
||||
field=models.PositiveSmallIntegerField(choices=[[1, 'Active'], [2, 'Planned'], [4, 'Retired']], default=1),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='site',
|
||||
name='time_zone',
|
||||
field=timezone_field.fields.TimeZoneField(blank=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='virtualchassis',
|
||||
name='master',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='vc_master_for', to='dcim.Device'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='untagged_vlan',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='interfaces_as_untagged', to='ipam.VLAN', verbose_name='Untagged VLAN'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='platform',
|
||||
name='manufacturer',
|
||||
field=models.ForeignKey(blank=True, help_text='Optionally limit this platform to devices of a certain manufacturer', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='platforms', to='dcim.Manufacturer'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='devicetype',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='site',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleport',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleserverport',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='devicebay',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='inventoryitem',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poweroutlet',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerport',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='virtualchassis',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='rack',
|
||||
options={'ordering': ['site', 'group', 'name']},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='rack',
|
||||
unique_together={('group', 'name'), ('group', 'facility_id')},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='site',
|
||||
name='latitude',
|
||||
field=models.DecimalField(blank=True, decimal_places=6, max_digits=8, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='site',
|
||||
name='longitude',
|
||||
field=models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='devicerole',
|
||||
name='created',
|
||||
field=models.DateField(auto_now_add=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='devicerole',
|
||||
name='last_updated',
|
||||
field=models.DateTimeField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='devicetype',
|
||||
name='created',
|
||||
field=models.DateField(auto_now_add=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='devicetype',
|
||||
name='last_updated',
|
||||
field=models.DateTimeField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='manufacturer',
|
||||
name='created',
|
||||
field=models.DateField(auto_now_add=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='manufacturer',
|
||||
name='last_updated',
|
||||
field=models.DateTimeField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='platform',
|
||||
name='created',
|
||||
field=models.DateField(auto_now_add=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='platform',
|
||||
name='last_updated',
|
||||
field=models.DateTimeField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rackgroup',
|
||||
name='created',
|
||||
field=models.DateField(auto_now_add=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rackgroup',
|
||||
name='last_updated',
|
||||
field=models.DateTimeField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rackreservation',
|
||||
name='last_updated',
|
||||
field=models.DateTimeField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rackrole',
|
||||
name='created',
|
||||
field=models.DateField(auto_now_add=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rackrole',
|
||||
name='last_updated',
|
||||
field=models.DateTimeField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='region',
|
||||
name='created',
|
||||
field=models.DateField(auto_now_add=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='region',
|
||||
name='last_updated',
|
||||
field=models.DateTimeField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='virtualchassis',
|
||||
name='created',
|
||||
field=models.DateField(auto_now_add=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='virtualchassis',
|
||||
name='last_updated',
|
||||
field=models.DateTimeField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='created',
|
||||
field=models.DateField(auto_now_add=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='last_updated',
|
||||
field=models.DateTimeField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='created',
|
||||
field=models.DateField(auto_now_add=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='last_updated',
|
||||
field=models.DateTimeField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rackreservation',
|
||||
name='created',
|
||||
field=models.DateField(auto_now_add=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='site',
|
||||
name='created',
|
||||
field=models.DateField(auto_now_add=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='site',
|
||||
name='last_updated',
|
||||
field=models.DateTimeField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='platform',
|
||||
name='napalm_args',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, help_text='Additional arguments to pass when initiating the NAPALM driver (JSON format)', null=True, verbose_name='NAPALM arguments'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,124 @@
|
||||
import django.contrib.postgres.fields.jsonb
|
||||
import django.core.validators
|
||||
import django.db.models.deletion
|
||||
import taggit.managers
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [('dcim', '0062_interface_mtu'), ('dcim', '0063_device_local_context_data'), ('dcim', '0064_remove_platform_rpc_client'), ('dcim', '0065_front_rear_ports')]
|
||||
|
||||
dependencies = [
|
||||
('taggit', '0002_auto_20150616_2121'),
|
||||
('dcim', '0061_platform_napalm_args'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='mtu',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(65536)], verbose_name='MTU'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['SONET', [[6100, 'OC-3/STM-1'], [6200, 'OC-12/STM-4'], [6300, 'OC-48/STM-16'], [6400, 'OC-192/STM-64'], [6500, 'OC-768/STM-256'], [6600, 'OC-1920/STM-640'], [6700, 'OC-3840/STM-1234']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)'], [3320, 'SFP28 (32GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP'], [5300, 'Extreme SummitStack'], [5310, 'Extreme SummitStack-128'], [5320, 'Extreme SummitStack-256'], [5330, 'Extreme SummitStack-512']]], ['Other', [[32767, 'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='form_factor',
|
||||
field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['SONET', [[6100, 'OC-3/STM-1'], [6200, 'OC-12/STM-4'], [6300, 'OC-48/STM-16'], [6400, 'OC-192/STM-64'], [6500, 'OC-768/STM-256'], [6600, 'OC-1920/STM-640'], [6700, 'OC-3840/STM-1234']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)'], [3320, 'SFP28 (32GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP'], [5300, 'Extreme SummitStack'], [5310, 'Extreme SummitStack-128'], [5320, 'Extreme SummitStack-256'], [5330, 'Extreme SummitStack-512']]], ['Other', [[32767, 'Other']]]], default=1200),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='local_context_data',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='platform',
|
||||
name='rpc_client',
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='RearPort',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=64)),
|
||||
('type', models.PositiveSmallIntegerField()),
|
||||
('positions', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(64)])),
|
||||
('description', models.CharField(blank=True, max_length=100)),
|
||||
('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rearports', to='dcim.Device')),
|
||||
('tags', taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['device', 'name'],
|
||||
'unique_together': {('device', 'name')},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='RearPortTemplate',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=64)),
|
||||
('type', models.PositiveSmallIntegerField()),
|
||||
('positions', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(64)])),
|
||||
('device_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rearport_templates', to='dcim.DeviceType')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['device_type', 'name'],
|
||||
'unique_together': {('device_type', 'name')},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='FrontPortTemplate',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=64)),
|
||||
('type', models.PositiveSmallIntegerField()),
|
||||
('rear_port_position', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(64)])),
|
||||
('device_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='frontport_templates', to='dcim.DeviceType')),
|
||||
('rear_port', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='frontport_templates', to='dcim.RearPortTemplate')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['device_type', 'name'],
|
||||
'unique_together': {('rear_port', 'rear_port_position'), ('device_type', 'name')},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='FrontPort',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=64)),
|
||||
('type', models.PositiveSmallIntegerField()),
|
||||
('rear_port_position', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(64)])),
|
||||
('description', models.CharField(blank=True, max_length=100)),
|
||||
('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='frontports', to='dcim.Device')),
|
||||
('rear_port', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='frontports', to='dcim.RearPort')),
|
||||
('tags', taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['device', 'name'],
|
||||
'unique_together': {('device', 'name'), ('rear_port', 'rear_port_position')},
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='consoleporttemplate',
|
||||
name='device_type',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='consoleport_templates', to='dcim.DeviceType'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='consoleserverporttemplate',
|
||||
name='device_type',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='consoleserverport_templates', to='dcim.DeviceType'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='poweroutlettemplate',
|
||||
name='device_type',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='poweroutlet_templates', to='dcim.DeviceType'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='powerporttemplate',
|
||||
name='device_type',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='powerport_templates', to='dcim.DeviceType'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,146 @@
|
||||
import taggit.managers
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [('dcim', '0067_device_type_remove_qualifiers'), ('dcim', '0068_rack_new_fields'), ('dcim', '0069_deprecate_nullablecharfield'), ('dcim', '0070_custom_tag_models')]
|
||||
|
||||
dependencies = [
|
||||
('extras', '0019_tag_taggeditem'),
|
||||
('dcim', '0066_cables'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='devicetype',
|
||||
name='is_console_server',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='devicetype',
|
||||
name='is_network_device',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='devicetype',
|
||||
name='is_pdu',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='devicetype',
|
||||
name='interface_ordering',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='status',
|
||||
field=models.PositiveSmallIntegerField(default=3),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='outer_depth',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='outer_unit',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='outer_width',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='asset_tag',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='name',
|
||||
field=models.CharField(blank=True, max_length=64, null=True, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='inventoryitem',
|
||||
name='asset_tag',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, unique=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='asset_tag',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='facility_id',
|
||||
field=models.CharField(blank=True, max_length=50, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='consoleport',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='consoleserverport',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicebay',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicetype',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='frontport',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='inventoryitem',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='poweroutlet',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='powerport',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rearport',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='site',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='virtualchassis',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,839 @@
|
||||
import sys
|
||||
|
||||
import django.core.validators
|
||||
import django.db.models.deletion
|
||||
import taggit.managers
|
||||
from django.db import migrations, models
|
||||
|
||||
SITE_STATUS_CHOICES = (
|
||||
(1, 'active'),
|
||||
(2, 'planned'),
|
||||
(4, 'retired'),
|
||||
)
|
||||
|
||||
RACK_TYPE_CHOICES = (
|
||||
(100, '2-post-frame'),
|
||||
(200, '4-post-frame'),
|
||||
(300, '4-post-cabinet'),
|
||||
(1000, 'wall-frame'),
|
||||
(1100, 'wall-cabinet'),
|
||||
)
|
||||
|
||||
RACK_STATUS_CHOICES = (
|
||||
(0, 'reserved'),
|
||||
(1, 'available'),
|
||||
(2, 'planned'),
|
||||
(3, 'active'),
|
||||
(4, 'deprecated'),
|
||||
)
|
||||
|
||||
RACK_DIMENSION_CHOICES = (
|
||||
(1000, 'mm'),
|
||||
(2000, 'in'),
|
||||
)
|
||||
|
||||
SUBDEVICE_ROLE_CHOICES = (
|
||||
('true', 'parent'),
|
||||
('false', 'child'),
|
||||
)
|
||||
|
||||
DEVICE_FACE_CHOICES = (
|
||||
(0, 'front'),
|
||||
(1, 'rear'),
|
||||
)
|
||||
|
||||
DEVICE_STATUS_CHOICES = (
|
||||
(0, 'offline'),
|
||||
(1, 'active'),
|
||||
(2, 'planned'),
|
||||
(3, 'staged'),
|
||||
(4, 'failed'),
|
||||
(5, 'inventory'),
|
||||
(6, 'decommissioning'),
|
||||
)
|
||||
|
||||
INTERFACE_TYPE_CHOICES = (
|
||||
(0, 'virtual'),
|
||||
(200, 'lag'),
|
||||
(800, '100base-tx'),
|
||||
(1000, '1000base-t'),
|
||||
(1050, '1000base-x-gbic'),
|
||||
(1100, '1000base-x-sfp'),
|
||||
(1120, '2.5gbase-t'),
|
||||
(1130, '5gbase-t'),
|
||||
(1150, '10gbase-t'),
|
||||
(1170, '10gbase-cx4'),
|
||||
(1200, '10gbase-x-sfpp'),
|
||||
(1300, '10gbase-x-xfp'),
|
||||
(1310, '10gbase-x-xenpak'),
|
||||
(1320, '10gbase-x-x2'),
|
||||
(1350, '25gbase-x-sfp28'),
|
||||
(1400, '40gbase-x-qsfpp'),
|
||||
(1420, '50gbase-x-sfp28'),
|
||||
(1500, '100gbase-x-cfp'),
|
||||
(1510, '100gbase-x-cfp2'),
|
||||
(1520, '100gbase-x-cfp4'),
|
||||
(1550, '100gbase-x-cpak'),
|
||||
(1600, '100gbase-x-qsfp28'),
|
||||
(1650, '200gbase-x-cfp2'),
|
||||
(1700, '200gbase-x-qsfp56'),
|
||||
(1750, '400gbase-x-qsfpdd'),
|
||||
(1800, '400gbase-x-osfp'),
|
||||
(2600, 'ieee802.11a'),
|
||||
(2610, 'ieee802.11g'),
|
||||
(2620, 'ieee802.11n'),
|
||||
(2630, 'ieee802.11ac'),
|
||||
(2640, 'ieee802.11ad'),
|
||||
(2810, 'gsm'),
|
||||
(2820, 'cdma'),
|
||||
(2830, 'lte'),
|
||||
(6100, 'sonet-oc3'),
|
||||
(6200, 'sonet-oc12'),
|
||||
(6300, 'sonet-oc48'),
|
||||
(6400, 'sonet-oc192'),
|
||||
(6500, 'sonet-oc768'),
|
||||
(6600, 'sonet-oc1920'),
|
||||
(6700, 'sonet-oc3840'),
|
||||
(3010, '1gfc-sfp'),
|
||||
(3020, '2gfc-sfp'),
|
||||
(3040, '4gfc-sfp'),
|
||||
(3080, '8gfc-sfpp'),
|
||||
(3160, '16gfc-sfpp'),
|
||||
(3320, '32gfc-sfp28'),
|
||||
(3400, '128gfc-sfp28'),
|
||||
(7010, 'inifiband-sdr'),
|
||||
(7020, 'inifiband-ddr'),
|
||||
(7030, 'inifiband-qdr'),
|
||||
(7040, 'inifiband-fdr10'),
|
||||
(7050, 'inifiband-fdr'),
|
||||
(7060, 'inifiband-edr'),
|
||||
(7070, 'inifiband-hdr'),
|
||||
(7080, 'inifiband-ndr'),
|
||||
(7090, 'inifiband-xdr'),
|
||||
(4000, 't1'),
|
||||
(4010, 'e1'),
|
||||
(4040, 't3'),
|
||||
(4050, 'e3'),
|
||||
(5000, 'cisco-stackwise'),
|
||||
(5050, 'cisco-stackwise-plus'),
|
||||
(5100, 'cisco-flexstack'),
|
||||
(5150, 'cisco-flexstack-plus'),
|
||||
(5200, 'juniper-vcp'),
|
||||
(5300, 'extreme-summitstack'),
|
||||
(5310, 'extreme-summitstack-128'),
|
||||
(5320, 'extreme-summitstack-256'),
|
||||
(5330, 'extreme-summitstack-512'),
|
||||
)
|
||||
|
||||
INTERFACE_MODE_CHOICES = (
|
||||
(100, 'access'),
|
||||
(200, 'tagged'),
|
||||
(300, 'tagged-all'),
|
||||
)
|
||||
|
||||
PORT_TYPE_CHOICES = (
|
||||
(1000, '8p8c'),
|
||||
(1100, '110-punch'),
|
||||
(1200, 'bnc'),
|
||||
(2000, 'st'),
|
||||
(2100, 'sc'),
|
||||
(2110, 'sc-apc'),
|
||||
(2200, 'fc'),
|
||||
(2300, 'lc'),
|
||||
(2310, 'lc-apc'),
|
||||
(2400, 'mtrj'),
|
||||
(2500, 'mpo'),
|
||||
(2600, 'lsh'),
|
||||
(2610, 'lsh-apc'),
|
||||
)
|
||||
|
||||
CABLE_TYPE_CHOICES = (
|
||||
(1300, 'cat3'),
|
||||
(1500, 'cat5'),
|
||||
(1510, 'cat5e'),
|
||||
(1600, 'cat6'),
|
||||
(1610, 'cat6a'),
|
||||
(1700, 'cat7'),
|
||||
(1800, 'dac-active'),
|
||||
(1810, 'dac-passive'),
|
||||
(1900, 'coaxial'),
|
||||
(3000, 'mmf'),
|
||||
(3010, 'mmf-om1'),
|
||||
(3020, 'mmf-om2'),
|
||||
(3030, 'mmf-om3'),
|
||||
(3040, 'mmf-om4'),
|
||||
(3500, 'smf'),
|
||||
(3510, 'smf-os1'),
|
||||
(3520, 'smf-os2'),
|
||||
(3800, 'aoc'),
|
||||
(5000, 'power'),
|
||||
)
|
||||
|
||||
CABLE_STATUS_CHOICES = (
|
||||
('true', 'connected'),
|
||||
('false', 'planned'),
|
||||
)
|
||||
|
||||
CABLE_LENGTH_UNIT_CHOICES = (
|
||||
(1200, 'm'),
|
||||
(1100, 'cm'),
|
||||
(2100, 'ft'),
|
||||
(2000, 'in'),
|
||||
)
|
||||
|
||||
POWERFEED_STATUS_CHOICES = (
|
||||
(0, 'offline'),
|
||||
(1, 'active'),
|
||||
(2, 'planned'),
|
||||
(4, 'failed'),
|
||||
)
|
||||
|
||||
POWERFEED_TYPE_CHOICES = (
|
||||
(1, 'primary'),
|
||||
(2, 'redundant'),
|
||||
)
|
||||
|
||||
POWERFEED_SUPPLY_CHOICES = (
|
||||
(1, 'ac'),
|
||||
(2, 'dc'),
|
||||
)
|
||||
|
||||
POWERFEED_PHASE_CHOICES = (
|
||||
(1, 'single-phase'),
|
||||
(3, 'three-phase'),
|
||||
)
|
||||
|
||||
POWEROUTLET_FEED_LEG_CHOICES_CHOICES = (
|
||||
(1, 'A'),
|
||||
(2, 'B'),
|
||||
(3, 'C'),
|
||||
)
|
||||
|
||||
|
||||
def cache_cable_devices(apps, schema_editor):
|
||||
Cable = apps.get_model('dcim', 'Cable')
|
||||
|
||||
if 'test' not in sys.argv:
|
||||
print("\nUpdating cable device terminations...")
|
||||
cable_count = Cable.objects.count()
|
||||
|
||||
# Cache A/B termination devices on all existing Cables. Note that the custom save() method on Cable is not
|
||||
# available during a migration, so we replicate its logic here.
|
||||
for i, cable in enumerate(Cable.objects.all(), start=1):
|
||||
|
||||
if not i % 1000 and 'test' not in sys.argv:
|
||||
print("[{}/{}]".format(i, cable_count))
|
||||
|
||||
termination_a_model = apps.get_model(cable.termination_a_type.app_label, cable.termination_a_type.model)
|
||||
termination_a_device = None
|
||||
if hasattr(termination_a_model, 'device'):
|
||||
termination_a = termination_a_model.objects.get(pk=cable.termination_a_id)
|
||||
termination_a_device = termination_a.device
|
||||
|
||||
termination_b_model = apps.get_model(cable.termination_b_type.app_label, cable.termination_b_type.model)
|
||||
termination_b_device = None
|
||||
if hasattr(termination_b_model, 'device'):
|
||||
termination_b = termination_b_model.objects.get(pk=cable.termination_b_id)
|
||||
termination_b_device = termination_b.device
|
||||
|
||||
Cable.objects.filter(pk=cable.pk).update(
|
||||
_termination_a_device=termination_a_device,
|
||||
_termination_b_device=termination_b_device
|
||||
)
|
||||
|
||||
|
||||
def site_status_to_slug(apps, schema_editor):
|
||||
Site = apps.get_model('dcim', 'Site')
|
||||
for id, slug in SITE_STATUS_CHOICES:
|
||||
Site.objects.filter(status=str(id)).update(status=slug)
|
||||
|
||||
|
||||
def rack_type_to_slug(apps, schema_editor):
|
||||
Rack = apps.get_model('dcim', 'Rack')
|
||||
for id, slug in RACK_TYPE_CHOICES:
|
||||
Rack.objects.filter(type=str(id)).update(type=slug)
|
||||
|
||||
|
||||
def rack_status_to_slug(apps, schema_editor):
|
||||
Rack = apps.get_model('dcim', 'Rack')
|
||||
for id, slug in RACK_STATUS_CHOICES:
|
||||
Rack.objects.filter(status=str(id)).update(status=slug)
|
||||
|
||||
|
||||
def rack_outer_unit_to_slug(apps, schema_editor):
|
||||
Rack = apps.get_model('dcim', 'Rack')
|
||||
for id, slug in RACK_DIMENSION_CHOICES:
|
||||
Rack.objects.filter(status=str(id)).update(status=slug)
|
||||
|
||||
|
||||
def devicetype_subdevicerole_to_slug(apps, schema_editor):
|
||||
DeviceType = apps.get_model('dcim', 'DeviceType')
|
||||
for boolean, slug in SUBDEVICE_ROLE_CHOICES:
|
||||
DeviceType.objects.filter(subdevice_role=boolean).update(subdevice_role=slug)
|
||||
|
||||
|
||||
def device_face_to_slug(apps, schema_editor):
|
||||
Device = apps.get_model('dcim', 'Device')
|
||||
for id, slug in DEVICE_FACE_CHOICES:
|
||||
Device.objects.filter(face=str(id)).update(face=slug)
|
||||
|
||||
|
||||
def device_status_to_slug(apps, schema_editor):
|
||||
Device = apps.get_model('dcim', 'Device')
|
||||
for id, slug in DEVICE_STATUS_CHOICES:
|
||||
Device.objects.filter(status=str(id)).update(status=slug)
|
||||
|
||||
|
||||
def interfacetemplate_type_to_slug(apps, schema_editor):
|
||||
InterfaceTemplate = apps.get_model('dcim', 'InterfaceTemplate')
|
||||
for id, slug in INTERFACE_TYPE_CHOICES:
|
||||
InterfaceTemplate.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
def interface_type_to_slug(apps, schema_editor):
|
||||
Interface = apps.get_model('dcim', 'Interface')
|
||||
for id, slug in INTERFACE_TYPE_CHOICES:
|
||||
Interface.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
def interface_mode_to_slug(apps, schema_editor):
|
||||
Interface = apps.get_model('dcim', 'Interface')
|
||||
for id, slug in INTERFACE_MODE_CHOICES:
|
||||
Interface.objects.filter(mode=id).update(mode=slug)
|
||||
|
||||
|
||||
def frontporttemplate_type_to_slug(apps, schema_editor):
|
||||
FrontPortTemplate = apps.get_model('dcim', 'FrontPortTemplate')
|
||||
for id, slug in PORT_TYPE_CHOICES:
|
||||
FrontPortTemplate.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
def rearporttemplate_type_to_slug(apps, schema_editor):
|
||||
RearPortTemplate = apps.get_model('dcim', 'RearPortTemplate')
|
||||
for id, slug in PORT_TYPE_CHOICES:
|
||||
RearPortTemplate.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
def frontport_type_to_slug(apps, schema_editor):
|
||||
FrontPort = apps.get_model('dcim', 'FrontPort')
|
||||
for id, slug in PORT_TYPE_CHOICES:
|
||||
FrontPort.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
def rearport_type_to_slug(apps, schema_editor):
|
||||
RearPort = apps.get_model('dcim', 'RearPort')
|
||||
for id, slug in PORT_TYPE_CHOICES:
|
||||
RearPort.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
def cable_type_to_slug(apps, schema_editor):
|
||||
Cable = apps.get_model('dcim', 'Cable')
|
||||
for id, slug in CABLE_TYPE_CHOICES:
|
||||
Cable.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
def cable_status_to_slug(apps, schema_editor):
|
||||
Cable = apps.get_model('dcim', 'Cable')
|
||||
for bool_str, slug in CABLE_STATUS_CHOICES:
|
||||
Cable.objects.filter(status=bool_str).update(status=slug)
|
||||
|
||||
|
||||
def cable_length_unit_to_slug(apps, schema_editor):
|
||||
Cable = apps.get_model('dcim', 'Cable')
|
||||
for id, slug in CABLE_LENGTH_UNIT_CHOICES:
|
||||
Cable.objects.filter(length_unit=id).update(length_unit=slug)
|
||||
|
||||
|
||||
def powerfeed_status_to_slug(apps, schema_editor):
|
||||
PowerFeed = apps.get_model('dcim', 'PowerFeed')
|
||||
for id, slug in POWERFEED_STATUS_CHOICES:
|
||||
PowerFeed.objects.filter(status=id).update(status=slug)
|
||||
|
||||
|
||||
def powerfeed_type_to_slug(apps, schema_editor):
|
||||
PowerFeed = apps.get_model('dcim', 'PowerFeed')
|
||||
for id, slug in POWERFEED_TYPE_CHOICES:
|
||||
PowerFeed.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
def powerfeed_supply_to_slug(apps, schema_editor):
|
||||
PowerFeed = apps.get_model('dcim', 'PowerFeed')
|
||||
for id, slug in POWERFEED_SUPPLY_CHOICES:
|
||||
PowerFeed.objects.filter(supply=id).update(supply=slug)
|
||||
|
||||
|
||||
def powerfeed_phase_to_slug(apps, schema_editor):
|
||||
PowerFeed = apps.get_model('dcim', 'PowerFeed')
|
||||
for id, slug in POWERFEED_PHASE_CHOICES:
|
||||
PowerFeed.objects.filter(phase=id).update(phase=slug)
|
||||
|
||||
|
||||
def poweroutlettemplate_feed_leg_to_slug(apps, schema_editor):
|
||||
PowerOutletTemplate = apps.get_model('dcim', 'PowerOutletTemplate')
|
||||
for id, slug in POWEROUTLET_FEED_LEG_CHOICES_CHOICES:
|
||||
PowerOutletTemplate.objects.filter(feed_leg=id).update(feed_leg=slug)
|
||||
|
||||
|
||||
def poweroutlet_feed_leg_to_slug(apps, schema_editor):
|
||||
PowerOutlet = apps.get_model('dcim', 'PowerOutlet')
|
||||
for id, slug in POWEROUTLET_FEED_LEG_CHOICES_CHOICES:
|
||||
PowerOutlet.objects.filter(feed_leg=id).update(feed_leg=slug)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [('dcim', '0071_device_components_add_description'), ('dcim', '0072_powerfeeds'), ('dcim', '0073_interface_form_factor_to_type'), ('dcim', '0074_increase_field_length_platform_name_slug'), ('dcim', '0075_cable_devices'), ('dcim', '0076_console_port_types'), ('dcim', '0077_power_types'), ('dcim', '0078_3569_site_fields'), ('dcim', '0079_3569_rack_fields'), ('dcim', '0080_3569_devicetype_fields'), ('dcim', '0081_3569_device_fields'), ('dcim', '0082_3569_interface_fields'), ('dcim', '0082_3569_port_fields'), ('dcim', '0083_3569_cable_fields'), ('dcim', '0084_3569_powerfeed_fields'), ('dcim', '0085_3569_poweroutlet_fields'), ('dcim', '0086_device_name_nonunique'), ('dcim', '0087_role_descriptions'), ('dcim', '0088_powerfeed_available_power')]
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0070_custom_tag_models'),
|
||||
('extras', '0021_add_color_comments_changelog_to_tag'),
|
||||
('tenancy', '0006_custom_tag_models'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='consoleport',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleserverport',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='devicebay',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poweroutlet',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerport',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PowerPanel',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||
('created', models.DateField(auto_now_add=True, null=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||
('name', models.CharField(max_length=50)),
|
||||
('rack_group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.RackGroup')),
|
||||
('site', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='dcim.Site')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['site', 'name'],
|
||||
'unique_together': {('site', 'name')},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PowerFeed',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||
('created', models.DateField(auto_now_add=True, null=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||
('name', models.CharField(max_length=50)),
|
||||
('status', models.PositiveSmallIntegerField(default=1)),
|
||||
('type', models.PositiveSmallIntegerField(default=1)),
|
||||
('supply', models.PositiveSmallIntegerField(default=1)),
|
||||
('phase', models.PositiveSmallIntegerField(default=1)),
|
||||
('voltage', models.PositiveSmallIntegerField(default=120, validators=[django.core.validators.MinValueValidator(1)])),
|
||||
('amperage', models.PositiveSmallIntegerField(default=20, validators=[django.core.validators.MinValueValidator(1)])),
|
||||
('max_utilization', models.PositiveSmallIntegerField(default=80, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)])),
|
||||
('available_power', models.PositiveSmallIntegerField(default=0, editable=False)),
|
||||
('comments', models.TextField(blank=True)),
|
||||
('cable', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.Cable')),
|
||||
('power_panel', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='powerfeeds', to='dcim.PowerPanel')),
|
||||
('rack', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.Rack')),
|
||||
('tags', taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='extras.TaggedItem', to='extras.Tag', verbose_name='Tags')),
|
||||
('connected_endpoint', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.PowerPort')),
|
||||
('connection_status', models.NullBooleanField()),
|
||||
],
|
||||
options={
|
||||
'ordering': ['power_panel', 'name'],
|
||||
'unique_together': {('power_panel', 'name')},
|
||||
},
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='powerport',
|
||||
old_name='connected_endpoint',
|
||||
new_name='_connected_poweroutlet',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerport',
|
||||
name='_connected_powerfeed',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.PowerFeed'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerport',
|
||||
name='allocated_draw',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerport',
|
||||
name='maximum_draw',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerporttemplate',
|
||||
name='allocated_draw',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerporttemplate',
|
||||
name='maximum_draw',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poweroutlet',
|
||||
name='feed_leg',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poweroutlet',
|
||||
name='power_port',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='poweroutlets', to='dcim.PowerPort'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poweroutlettemplate',
|
||||
name='feed_leg',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poweroutlettemplate',
|
||||
name='power_port',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='poweroutlet_templates', to='dcim.PowerPortTemplate'),
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='interface',
|
||||
old_name='form_factor',
|
||||
new_name='type',
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='interfacetemplate',
|
||||
old_name='form_factor',
|
||||
new_name='type',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='platform',
|
||||
name='name',
|
||||
field=models.CharField(max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='platform',
|
||||
name='slug',
|
||||
field=models.SlugField(max_length=100, unique=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cable',
|
||||
name='_termination_a_device',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.Device'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cable',
|
||||
name='_termination_b_device',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.Device'),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=cache_cable_devices,
|
||||
reverse_code=django.db.migrations.operations.special.RunPython.noop,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleport',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleporttemplate',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleserverport',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleserverporttemplate',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poweroutlet',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poweroutlettemplate',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerport',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerporttemplate',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='site',
|
||||
name='status',
|
||||
field=models.CharField(default='active', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=site_status_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=rack_type_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='status',
|
||||
field=models.CharField(default='active', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=rack_status_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='outer_unit',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=rack_outer_unit_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='outer_unit',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicetype',
|
||||
name='subdevice_role',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=devicetype_subdevicerole_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicetype',
|
||||
name='subdevice_role',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='face',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=device_face_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='face',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='status',
|
||||
field=models.CharField(default='active', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=device_status_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='type',
|
||||
field=models.CharField(max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=interfacetemplate_type_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='type',
|
||||
field=models.CharField(max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=interface_type_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='mode',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=interface_mode_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='mode',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='frontporttemplate',
|
||||
name='type',
|
||||
field=models.CharField(max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=frontporttemplate_type_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rearporttemplate',
|
||||
name='type',
|
||||
field=models.CharField(max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=rearporttemplate_type_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='frontport',
|
||||
name='type',
|
||||
field=models.CharField(max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=frontport_type_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rearport',
|
||||
name='type',
|
||||
field=models.CharField(max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=rearport_type_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cable',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=cable_type_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cable',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cable',
|
||||
name='status',
|
||||
field=models.CharField(default='connected', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=cable_status_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cable',
|
||||
name='length_unit',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=cable_length_unit_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cable',
|
||||
name='length_unit',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='powerfeed',
|
||||
name='status',
|
||||
field=models.CharField(default='active', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=powerfeed_status_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='powerfeed',
|
||||
name='type',
|
||||
field=models.CharField(default='primary', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=powerfeed_type_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='powerfeed',
|
||||
name='supply',
|
||||
field=models.CharField(default='ac', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=powerfeed_supply_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='powerfeed',
|
||||
name='phase',
|
||||
field=models.CharField(default='single-phase', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=powerfeed_phase_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='poweroutlettemplate',
|
||||
name='feed_leg',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=poweroutlettemplate_feed_leg_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='poweroutlettemplate',
|
||||
name='feed_leg',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='poweroutlet',
|
||||
name='feed_leg',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=poweroutlet_feed_leg_to_slug,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='poweroutlet',
|
||||
name='feed_leg',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='name',
|
||||
field=models.CharField(blank=True, max_length=64, null=True),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='device',
|
||||
unique_together={('rack', 'position', 'face'), ('site', 'tenant', 'name'), ('virtual_chassis', 'vc_position')},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='devicerole',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rackrole',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='powerfeed',
|
||||
name='available_power',
|
||||
field=models.PositiveIntegerField(default=0, editable=False),
|
||||
),
|
||||
]
|
||||
33
netbox/dcim/migrations/0076_console_port_types.py
Normal file
33
netbox/dcim/migrations/0076_console_port_types.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 2.2.6 on 2019-10-30 17:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0075_cable_devices'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='consoleport',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleporttemplate',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleserverport',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleserverporttemplate',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
]
|
||||
33
netbox/dcim/migrations/0077_power_types.py
Normal file
33
netbox/dcim/migrations/0077_power_types.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 2.2.6 on 2019-11-06 19:48
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0076_console_port_types'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='poweroutlet',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poweroutlettemplate',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerport',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerporttemplate',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
]
|
||||
35
netbox/dcim/migrations/0078_3569_site_fields.py
Normal file
35
netbox/dcim/migrations/0078_3569_site_fields.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
SITE_STATUS_CHOICES = (
|
||||
(1, 'active'),
|
||||
(2, 'planned'),
|
||||
(4, 'retired'),
|
||||
)
|
||||
|
||||
|
||||
def site_status_to_slug(apps, schema_editor):
|
||||
Site = apps.get_model('dcim', 'Site')
|
||||
for id, slug in SITE_STATUS_CHOICES:
|
||||
Site.objects.filter(status=str(id)).update(status=slug)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0077_power_types'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
||||
# Site.status
|
||||
migrations.AlterField(
|
||||
model_name='site',
|
||||
name='status',
|
||||
field=models.CharField(default='active', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=site_status_to_slug
|
||||
),
|
||||
|
||||
]
|
||||
92
netbox/dcim/migrations/0079_3569_rack_fields.py
Normal file
92
netbox/dcim/migrations/0079_3569_rack_fields.py
Normal file
@@ -0,0 +1,92 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
RACK_TYPE_CHOICES = (
|
||||
(100, '2-post-frame'),
|
||||
(200, '4-post-frame'),
|
||||
(300, '4-post-cabinet'),
|
||||
(1000, 'wall-frame'),
|
||||
(1100, 'wall-cabinet'),
|
||||
)
|
||||
|
||||
RACK_STATUS_CHOICES = (
|
||||
(0, 'reserved'),
|
||||
(1, 'available'),
|
||||
(2, 'planned'),
|
||||
(3, 'active'),
|
||||
(4, 'deprecated'),
|
||||
)
|
||||
|
||||
RACK_DIMENSION_CHOICES = (
|
||||
(1000, 'mm'),
|
||||
(2000, 'in'),
|
||||
)
|
||||
|
||||
|
||||
def rack_type_to_slug(apps, schema_editor):
|
||||
Rack = apps.get_model('dcim', 'Rack')
|
||||
for id, slug in RACK_TYPE_CHOICES:
|
||||
Rack.objects.filter(type=str(id)).update(type=slug)
|
||||
|
||||
|
||||
def rack_status_to_slug(apps, schema_editor):
|
||||
Rack = apps.get_model('dcim', 'Rack')
|
||||
for id, slug in RACK_STATUS_CHOICES:
|
||||
Rack.objects.filter(status=str(id)).update(status=slug)
|
||||
|
||||
|
||||
def rack_outer_unit_to_slug(apps, schema_editor):
|
||||
Rack = apps.get_model('dcim', 'Rack')
|
||||
for id, slug in RACK_DIMENSION_CHOICES:
|
||||
Rack.objects.filter(status=str(id)).update(status=slug)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0078_3569_site_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
||||
# Rack.type
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=rack_type_to_slug
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
|
||||
# Rack.status
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='status',
|
||||
field=models.CharField(default='active', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=rack_status_to_slug
|
||||
),
|
||||
|
||||
# Rack.outer_unit
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='outer_unit',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=rack_outer_unit_to_slug
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='outer_unit',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
|
||||
]
|
||||
39
netbox/dcim/migrations/0080_3569_devicetype_fields.py
Normal file
39
netbox/dcim/migrations/0080_3569_devicetype_fields.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
SUBDEVICE_ROLE_CHOICES = (
|
||||
('true', 'parent'),
|
||||
('false', 'child'),
|
||||
)
|
||||
|
||||
|
||||
def devicetype_subdevicerole_to_slug(apps, schema_editor):
|
||||
DeviceType = apps.get_model('dcim', 'DeviceType')
|
||||
for boolean, slug in SUBDEVICE_ROLE_CHOICES:
|
||||
DeviceType.objects.filter(subdevice_role=boolean).update(subdevice_role=slug)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0079_3569_rack_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
||||
# DeviceType.subdevice_role
|
||||
migrations.AlterField(
|
||||
model_name='devicetype',
|
||||
name='subdevice_role',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=devicetype_subdevicerole_to_slug
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicetype',
|
||||
name='subdevice_role',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
|
||||
]
|
||||
65
netbox/dcim/migrations/0081_3569_device_fields.py
Normal file
65
netbox/dcim/migrations/0081_3569_device_fields.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
DEVICE_FACE_CHOICES = (
|
||||
(0, 'front'),
|
||||
(1, 'rear'),
|
||||
)
|
||||
|
||||
DEVICE_STATUS_CHOICES = (
|
||||
(0, 'offline'),
|
||||
(1, 'active'),
|
||||
(2, 'planned'),
|
||||
(3, 'staged'),
|
||||
(4, 'failed'),
|
||||
(5, 'inventory'),
|
||||
(6, 'decommissioning'),
|
||||
)
|
||||
|
||||
|
||||
def device_face_to_slug(apps, schema_editor):
|
||||
Device = apps.get_model('dcim', 'Device')
|
||||
for id, slug in DEVICE_FACE_CHOICES:
|
||||
Device.objects.filter(face=str(id)).update(face=slug)
|
||||
|
||||
|
||||
def device_status_to_slug(apps, schema_editor):
|
||||
Device = apps.get_model('dcim', 'Device')
|
||||
for id, slug in DEVICE_STATUS_CHOICES:
|
||||
Device.objects.filter(status=str(id)).update(status=slug)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0080_3569_devicetype_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
||||
# Device.face
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='face',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=device_face_to_slug
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='face',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
|
||||
# Device.status
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='status',
|
||||
field=models.CharField(default='active', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=device_status_to_slug
|
||||
),
|
||||
|
||||
]
|
||||
147
netbox/dcim/migrations/0082_3569_interface_fields.py
Normal file
147
netbox/dcim/migrations/0082_3569_interface_fields.py
Normal file
@@ -0,0 +1,147 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
INTERFACE_TYPE_CHOICES = (
|
||||
(0, 'virtual'),
|
||||
(200, 'lag'),
|
||||
(800, '100base-tx'),
|
||||
(1000, '1000base-t'),
|
||||
(1050, '1000base-x-gbic'),
|
||||
(1100, '1000base-x-sfp'),
|
||||
(1120, '2.5gbase-t'),
|
||||
(1130, '5gbase-t'),
|
||||
(1150, '10gbase-t'),
|
||||
(1170, '10gbase-cx4'),
|
||||
(1200, '10gbase-x-sfpp'),
|
||||
(1300, '10gbase-x-xfp'),
|
||||
(1310, '10gbase-x-xenpak'),
|
||||
(1320, '10gbase-x-x2'),
|
||||
(1350, '25gbase-x-sfp28'),
|
||||
(1400, '40gbase-x-qsfpp'),
|
||||
(1420, '50gbase-x-sfp28'),
|
||||
(1500, '100gbase-x-cfp'),
|
||||
(1510, '100gbase-x-cfp2'),
|
||||
(1520, '100gbase-x-cfp4'),
|
||||
(1550, '100gbase-x-cpak'),
|
||||
(1600, '100gbase-x-qsfp28'),
|
||||
(1650, '200gbase-x-cfp2'),
|
||||
(1700, '200gbase-x-qsfp56'),
|
||||
(1750, '400gbase-x-qsfpdd'),
|
||||
(1800, '400gbase-x-osfp'),
|
||||
(2600, 'ieee802.11a'),
|
||||
(2610, 'ieee802.11g'),
|
||||
(2620, 'ieee802.11n'),
|
||||
(2630, 'ieee802.11ac'),
|
||||
(2640, 'ieee802.11ad'),
|
||||
(2810, 'gsm'),
|
||||
(2820, 'cdma'),
|
||||
(2830, 'lte'),
|
||||
(6100, 'sonet-oc3'),
|
||||
(6200, 'sonet-oc12'),
|
||||
(6300, 'sonet-oc48'),
|
||||
(6400, 'sonet-oc192'),
|
||||
(6500, 'sonet-oc768'),
|
||||
(6600, 'sonet-oc1920'),
|
||||
(6700, 'sonet-oc3840'),
|
||||
(3010, '1gfc-sfp'),
|
||||
(3020, '2gfc-sfp'),
|
||||
(3040, '4gfc-sfp'),
|
||||
(3080, '8gfc-sfpp'),
|
||||
(3160, '16gfc-sfpp'),
|
||||
(3320, '32gfc-sfp28'),
|
||||
(3400, '128gfc-sfp28'),
|
||||
(7010, 'inifiband-sdr'),
|
||||
(7020, 'inifiband-ddr'),
|
||||
(7030, 'inifiband-qdr'),
|
||||
(7040, 'inifiband-fdr10'),
|
||||
(7050, 'inifiband-fdr'),
|
||||
(7060, 'inifiband-edr'),
|
||||
(7070, 'inifiband-hdr'),
|
||||
(7080, 'inifiband-ndr'),
|
||||
(7090, 'inifiband-xdr'),
|
||||
(4000, 't1'),
|
||||
(4010, 'e1'),
|
||||
(4040, 't3'),
|
||||
(4050, 'e3'),
|
||||
(5000, 'cisco-stackwise'),
|
||||
(5050, 'cisco-stackwise-plus'),
|
||||
(5100, 'cisco-flexstack'),
|
||||
(5150, 'cisco-flexstack-plus'),
|
||||
(5200, 'juniper-vcp'),
|
||||
(5300, 'extreme-summitstack'),
|
||||
(5310, 'extreme-summitstack-128'),
|
||||
(5320, 'extreme-summitstack-256'),
|
||||
(5330, 'extreme-summitstack-512'),
|
||||
)
|
||||
|
||||
|
||||
INTERFACE_MODE_CHOICES = (
|
||||
(100, 'access'),
|
||||
(200, 'tagged'),
|
||||
(300, 'tagged-all'),
|
||||
)
|
||||
|
||||
|
||||
def interfacetemplate_type_to_slug(apps, schema_editor):
|
||||
InterfaceTemplate = apps.get_model('dcim', 'InterfaceTemplate')
|
||||
for id, slug in INTERFACE_TYPE_CHOICES:
|
||||
InterfaceTemplate.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
def interface_type_to_slug(apps, schema_editor):
|
||||
Interface = apps.get_model('dcim', 'Interface')
|
||||
for id, slug in INTERFACE_TYPE_CHOICES:
|
||||
Interface.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
def interface_mode_to_slug(apps, schema_editor):
|
||||
Interface = apps.get_model('dcim', 'Interface')
|
||||
for id, slug in INTERFACE_MODE_CHOICES:
|
||||
Interface.objects.filter(mode=id).update(mode=slug)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0081_3569_device_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
||||
# InterfaceTemplate.type
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='type',
|
||||
field=models.CharField(max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=interfacetemplate_type_to_slug
|
||||
),
|
||||
|
||||
# Interface.type
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='type',
|
||||
field=models.CharField(max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=interface_type_to_slug
|
||||
),
|
||||
|
||||
# Interface.mode
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='mode',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=interface_mode_to_slug
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='mode',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
|
||||
]
|
||||
93
netbox/dcim/migrations/0082_3569_port_fields.py
Normal file
93
netbox/dcim/migrations/0082_3569_port_fields.py
Normal file
@@ -0,0 +1,93 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
PORT_TYPE_CHOICES = (
|
||||
(1000, '8p8c'),
|
||||
(1100, '110-punch'),
|
||||
(1200, 'bnc'),
|
||||
(2000, 'st'),
|
||||
(2100, 'sc'),
|
||||
(2110, 'sc-apc'),
|
||||
(2200, 'fc'),
|
||||
(2300, 'lc'),
|
||||
(2310, 'lc-apc'),
|
||||
(2400, 'mtrj'),
|
||||
(2500, 'mpo'),
|
||||
(2600, 'lsh'),
|
||||
(2610, 'lsh-apc'),
|
||||
)
|
||||
|
||||
|
||||
def frontporttemplate_type_to_slug(apps, schema_editor):
|
||||
FrontPortTemplate = apps.get_model('dcim', 'FrontPortTemplate')
|
||||
for id, slug in PORT_TYPE_CHOICES:
|
||||
FrontPortTemplate.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
def rearporttemplate_type_to_slug(apps, schema_editor):
|
||||
RearPortTemplate = apps.get_model('dcim', 'RearPortTemplate')
|
||||
for id, slug in PORT_TYPE_CHOICES:
|
||||
RearPortTemplate.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
def frontport_type_to_slug(apps, schema_editor):
|
||||
FrontPort = apps.get_model('dcim', 'FrontPort')
|
||||
for id, slug in PORT_TYPE_CHOICES:
|
||||
FrontPort.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
def rearport_type_to_slug(apps, schema_editor):
|
||||
RearPort = apps.get_model('dcim', 'RearPort')
|
||||
for id, slug in PORT_TYPE_CHOICES:
|
||||
RearPort.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0082_3569_interface_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
||||
# FrontPortTemplate.type
|
||||
migrations.AlterField(
|
||||
model_name='frontporttemplate',
|
||||
name='type',
|
||||
field=models.CharField(max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=frontporttemplate_type_to_slug
|
||||
),
|
||||
|
||||
# RearPortTemplate.type
|
||||
migrations.AlterField(
|
||||
model_name='rearporttemplate',
|
||||
name='type',
|
||||
field=models.CharField(max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=rearporttemplate_type_to_slug
|
||||
),
|
||||
|
||||
# FrontPort.type
|
||||
migrations.AlterField(
|
||||
model_name='frontport',
|
||||
name='type',
|
||||
field=models.CharField(max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=frontport_type_to_slug
|
||||
),
|
||||
|
||||
# RearPort.type
|
||||
migrations.AlterField(
|
||||
model_name='rearport',
|
||||
name='type',
|
||||
field=models.CharField(max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=rearport_type_to_slug
|
||||
),
|
||||
]
|
||||
106
netbox/dcim/migrations/0083_3569_cable_fields.py
Normal file
106
netbox/dcim/migrations/0083_3569_cable_fields.py
Normal file
@@ -0,0 +1,106 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
CABLE_TYPE_CHOICES = (
|
||||
(1300, 'cat3'),
|
||||
(1500, 'cat5'),
|
||||
(1510, 'cat5e'),
|
||||
(1600, 'cat6'),
|
||||
(1610, 'cat6a'),
|
||||
(1700, 'cat7'),
|
||||
(1800, 'dac-active'),
|
||||
(1810, 'dac-passive'),
|
||||
(1900, 'coaxial'),
|
||||
(3000, 'mmf'),
|
||||
(3010, 'mmf-om1'),
|
||||
(3020, 'mmf-om2'),
|
||||
(3030, 'mmf-om3'),
|
||||
(3040, 'mmf-om4'),
|
||||
(3500, 'smf'),
|
||||
(3510, 'smf-os1'),
|
||||
(3520, 'smf-os2'),
|
||||
(3800, 'aoc'),
|
||||
(5000, 'power'),
|
||||
)
|
||||
|
||||
CABLE_STATUS_CHOICES = (
|
||||
('true', 'connected'),
|
||||
('false', 'planned'),
|
||||
)
|
||||
|
||||
CABLE_LENGTH_UNIT_CHOICES = (
|
||||
(1200, 'm'),
|
||||
(1100, 'cm'),
|
||||
(2100, 'ft'),
|
||||
(2000, 'in'),
|
||||
)
|
||||
|
||||
|
||||
def cable_type_to_slug(apps, schema_editor):
|
||||
Cable = apps.get_model('dcim', 'Cable')
|
||||
for id, slug in CABLE_TYPE_CHOICES:
|
||||
Cable.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
def cable_status_to_slug(apps, schema_editor):
|
||||
Cable = apps.get_model('dcim', 'Cable')
|
||||
for bool_str, slug in CABLE_STATUS_CHOICES:
|
||||
Cable.objects.filter(status=bool_str).update(status=slug)
|
||||
|
||||
|
||||
def cable_length_unit_to_slug(apps, schema_editor):
|
||||
Cable = apps.get_model('dcim', 'Cable')
|
||||
for id, slug in CABLE_LENGTH_UNIT_CHOICES:
|
||||
Cable.objects.filter(length_unit=id).update(length_unit=slug)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0082_3569_port_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
||||
# Cable.type
|
||||
migrations.AlterField(
|
||||
model_name='cable',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=cable_type_to_slug
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cable',
|
||||
name='type',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
|
||||
# Cable.status
|
||||
migrations.AlterField(
|
||||
model_name='cable',
|
||||
name='status',
|
||||
field=models.CharField(default='connected', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=cable_status_to_slug
|
||||
),
|
||||
|
||||
# Cable.length_unit
|
||||
migrations.AlterField(
|
||||
model_name='cable',
|
||||
name='length_unit',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=cable_length_unit_to_slug
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cable',
|
||||
name='length_unit',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
|
||||
]
|
||||
100
netbox/dcim/migrations/0084_3569_powerfeed_fields.py
Normal file
100
netbox/dcim/migrations/0084_3569_powerfeed_fields.py
Normal file
@@ -0,0 +1,100 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
POWERFEED_STATUS_CHOICES = (
|
||||
(0, 'offline'),
|
||||
(1, 'active'),
|
||||
(2, 'planned'),
|
||||
(4, 'failed'),
|
||||
)
|
||||
|
||||
POWERFEED_TYPE_CHOICES = (
|
||||
(1, 'primary'),
|
||||
(2, 'redundant'),
|
||||
)
|
||||
|
||||
POWERFEED_SUPPLY_CHOICES = (
|
||||
(1, 'ac'),
|
||||
(2, 'dc'),
|
||||
)
|
||||
|
||||
POWERFEED_PHASE_CHOICES = (
|
||||
(1, 'single-phase'),
|
||||
(3, 'three-phase'),
|
||||
)
|
||||
|
||||
|
||||
def powerfeed_status_to_slug(apps, schema_editor):
|
||||
PowerFeed = apps.get_model('dcim', 'PowerFeed')
|
||||
for id, slug in POWERFEED_STATUS_CHOICES:
|
||||
PowerFeed.objects.filter(status=id).update(status=slug)
|
||||
|
||||
|
||||
def powerfeed_type_to_slug(apps, schema_editor):
|
||||
PowerFeed = apps.get_model('dcim', 'PowerFeed')
|
||||
for id, slug in POWERFEED_TYPE_CHOICES:
|
||||
PowerFeed.objects.filter(type=id).update(type=slug)
|
||||
|
||||
|
||||
def powerfeed_supply_to_slug(apps, schema_editor):
|
||||
PowerFeed = apps.get_model('dcim', 'PowerFeed')
|
||||
for id, slug in POWERFEED_SUPPLY_CHOICES:
|
||||
PowerFeed.objects.filter(supply=id).update(supply=slug)
|
||||
|
||||
|
||||
def powerfeed_phase_to_slug(apps, schema_editor):
|
||||
PowerFeed = apps.get_model('dcim', 'PowerFeed')
|
||||
for id, slug in POWERFEED_PHASE_CHOICES:
|
||||
PowerFeed.objects.filter(phase=id).update(phase=slug)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0083_3569_cable_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
||||
# PowerFeed.status
|
||||
migrations.AlterField(
|
||||
model_name='powerfeed',
|
||||
name='status',
|
||||
field=models.CharField(default='active', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=powerfeed_status_to_slug
|
||||
),
|
||||
|
||||
# PowerFeed.type
|
||||
migrations.AlterField(
|
||||
model_name='powerfeed',
|
||||
name='type',
|
||||
field=models.CharField(default='primary', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=powerfeed_type_to_slug
|
||||
),
|
||||
|
||||
# PowerFeed.supply
|
||||
migrations.AlterField(
|
||||
model_name='powerfeed',
|
||||
name='supply',
|
||||
field=models.CharField(default='ac', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=powerfeed_supply_to_slug
|
||||
),
|
||||
|
||||
# PowerFeed.phase
|
||||
migrations.AlterField(
|
||||
model_name='powerfeed',
|
||||
name='phase',
|
||||
field=models.CharField(default='single-phase', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=powerfeed_phase_to_slug
|
||||
),
|
||||
|
||||
]
|
||||
62
netbox/dcim/migrations/0085_3569_poweroutlet_fields.py
Normal file
62
netbox/dcim/migrations/0085_3569_poweroutlet_fields.py
Normal file
@@ -0,0 +1,62 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
POWEROUTLET_FEED_LEG_CHOICES_CHOICES = (
|
||||
(1, 'A'),
|
||||
(2, 'B'),
|
||||
(3, 'C'),
|
||||
)
|
||||
|
||||
|
||||
def poweroutlettemplate_feed_leg_to_slug(apps, schema_editor):
|
||||
PowerOutletTemplate = apps.get_model('dcim', 'PowerOutletTemplate')
|
||||
for id, slug in POWEROUTLET_FEED_LEG_CHOICES_CHOICES:
|
||||
PowerOutletTemplate.objects.filter(feed_leg=id).update(feed_leg=slug)
|
||||
|
||||
|
||||
def poweroutlet_feed_leg_to_slug(apps, schema_editor):
|
||||
PowerOutlet = apps.get_model('dcim', 'PowerOutlet')
|
||||
for id, slug in POWEROUTLET_FEED_LEG_CHOICES_CHOICES:
|
||||
PowerOutlet.objects.filter(feed_leg=id).update(feed_leg=slug)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0084_3569_powerfeed_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
||||
# PowerOutletTemplate.feed_leg
|
||||
migrations.AlterField(
|
||||
model_name='poweroutlettemplate',
|
||||
name='feed_leg',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=poweroutlettemplate_feed_leg_to_slug
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='poweroutlettemplate',
|
||||
name='feed_leg',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
|
||||
# PowerOutlet.feed_leg
|
||||
migrations.AlterField(
|
||||
model_name='poweroutlet',
|
||||
name='feed_leg',
|
||||
field=models.CharField(blank=True, default='', max_length=50),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=poweroutlet_feed_leg_to_slug
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='poweroutlet',
|
||||
name='feed_leg',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
|
||||
]
|
||||
23
netbox/dcim/migrations/0086_device_name_nonunique.py
Normal file
23
netbox/dcim/migrations/0086_device_name_nonunique.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 2.2.6 on 2019-12-09 15:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tenancy', '0006_custom_tag_models'),
|
||||
('dcim', '0085_3569_poweroutlet_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='name',
|
||||
field=models.CharField(blank=True, max_length=64, null=True),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='device',
|
||||
unique_together={('rack', 'position', 'face'), ('virtual_chassis', 'vc_position'), ('site', 'tenant', 'name')},
|
||||
),
|
||||
]
|
||||
23
netbox/dcim/migrations/0087_role_descriptions.py
Normal file
23
netbox/dcim/migrations/0087_role_descriptions.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 2.2.6 on 2019-12-10 17:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0086_device_name_nonunique'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='devicerole',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rackrole',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=100),
|
||||
),
|
||||
]
|
||||
18
netbox/dcim/migrations/0088_powerfeed_available_power.py
Normal file
18
netbox/dcim/migrations/0088_powerfeed_available_power.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.2.8 on 2019-12-12 02:09
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0087_role_descriptions'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='powerfeed',
|
||||
name='available_power',
|
||||
field=models.PositiveIntegerField(default=0, editable=False),
|
||||
),
|
||||
]
|
||||
21
netbox/dcim/migrations/0089_deterministic_ordering.py
Normal file
21
netbox/dcim/migrations/0089_deterministic_ordering.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# Generated by Django 2.2.8 on 2020-01-15 18:10
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0088_powerfeed_available_power'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='device',
|
||||
options={'ordering': ('name', 'pk'), 'permissions': (('napalm_read', 'Read-only access to devices via NAPALM'), ('napalm_write', 'Read/write access to devices via NAPALM'))},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='rack',
|
||||
options={'ordering': ('site', 'group', 'name', 'pk')},
|
||||
),
|
||||
]
|
||||
24
netbox/dcim/migrations/0090_cable_termination_models.py
Normal file
24
netbox/dcim/migrations/0090_cable_termination_models.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# Generated by Django 2.2.8 on 2020-01-15 20:51
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0089_deterministic_ordering'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='cable',
|
||||
name='termination_a_type',
|
||||
field=models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'powerfeed', 'poweroutlet', 'powerport', 'rearport'))), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cable',
|
||||
name='termination_b_type',
|
||||
field=models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'powerfeed', 'poweroutlet', 'powerport', 'rearport'))), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType'),
|
||||
),
|
||||
]
|
||||
20
netbox/dcim/migrations/0091_interface_type_other.py
Normal file
20
netbox/dcim/migrations/0091_interface_type_other.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def interface_type_to_slug(apps, schema_editor):
|
||||
Interface = apps.get_model('dcim', 'Interface')
|
||||
Interface.objects.filter(type=32767).update(type='other')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0090_cable_termination_models'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
# Missed type "other" in the initial migration (see #3967)
|
||||
migrations.RunPython(
|
||||
code=interface_type_to_slug
|
||||
),
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
400
netbox/dcim/models/device_component_templates.py
Normal file
400
netbox/dcim/models/device_component_templates.py
Normal file
@@ -0,0 +1,400 @@
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import models
|
||||
|
||||
from dcim.choices import *
|
||||
from dcim.constants import *
|
||||
from dcim.managers import InterfaceManager
|
||||
from extras.models import ObjectChange
|
||||
from utilities.managers import NaturalOrderingManager
|
||||
from utilities.utils import serialize_object
|
||||
from .device_components import (
|
||||
ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, PowerOutlet, PowerPort, RearPort,
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
'ConsolePortTemplate',
|
||||
'ConsoleServerPortTemplate',
|
||||
'DeviceBayTemplate',
|
||||
'FrontPortTemplate',
|
||||
'InterfaceTemplate',
|
||||
'PowerOutletTemplate',
|
||||
'PowerPortTemplate',
|
||||
'RearPortTemplate',
|
||||
)
|
||||
|
||||
|
||||
class ComponentTemplateModel(models.Model):
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def instantiate(self, device):
|
||||
"""
|
||||
Instantiate a new component on the specified Device.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def to_objectchange(self, action):
|
||||
return ObjectChange(
|
||||
changed_object=self,
|
||||
object_repr=str(self),
|
||||
action=action,
|
||||
related_object=self.device_type,
|
||||
object_data=serialize_object(self)
|
||||
)
|
||||
|
||||
|
||||
class ConsolePortTemplate(ComponentTemplateModel):
|
||||
"""
|
||||
A template for a ConsolePort to be created for a new Device.
|
||||
"""
|
||||
device_type = models.ForeignKey(
|
||||
to='dcim.DeviceType',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='consoleport_templates'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
type = models.CharField(
|
||||
max_length=50,
|
||||
choices=ConsolePortTypeChoices,
|
||||
blank=True
|
||||
)
|
||||
|
||||
objects = NaturalOrderingManager()
|
||||
|
||||
class Meta:
|
||||
ordering = ['device_type', 'name']
|
||||
unique_together = ['device_type', 'name']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def instantiate(self, device):
|
||||
return ConsolePort(
|
||||
device=device,
|
||||
name=self.name,
|
||||
type=self.type
|
||||
)
|
||||
|
||||
|
||||
class ConsoleServerPortTemplate(ComponentTemplateModel):
|
||||
"""
|
||||
A template for a ConsoleServerPort to be created for a new Device.
|
||||
"""
|
||||
device_type = models.ForeignKey(
|
||||
to='dcim.DeviceType',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='consoleserverport_templates'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
type = models.CharField(
|
||||
max_length=50,
|
||||
choices=ConsolePortTypeChoices,
|
||||
blank=True
|
||||
)
|
||||
|
||||
objects = NaturalOrderingManager()
|
||||
|
||||
class Meta:
|
||||
ordering = ['device_type', 'name']
|
||||
unique_together = ['device_type', 'name']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def instantiate(self, device):
|
||||
return ConsoleServerPort(
|
||||
device=device,
|
||||
name=self.name,
|
||||
type=self.type
|
||||
)
|
||||
|
||||
|
||||
class PowerPortTemplate(ComponentTemplateModel):
|
||||
"""
|
||||
A template for a PowerPort to be created for a new Device.
|
||||
"""
|
||||
device_type = models.ForeignKey(
|
||||
to='dcim.DeviceType',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='powerport_templates'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
type = models.CharField(
|
||||
max_length=50,
|
||||
choices=PowerPortTypeChoices,
|
||||
blank=True
|
||||
)
|
||||
maximum_draw = models.PositiveSmallIntegerField(
|
||||
blank=True,
|
||||
null=True,
|
||||
validators=[MinValueValidator(1)],
|
||||
help_text="Maximum power draw (watts)"
|
||||
)
|
||||
allocated_draw = models.PositiveSmallIntegerField(
|
||||
blank=True,
|
||||
null=True,
|
||||
validators=[MinValueValidator(1)],
|
||||
help_text="Allocated power draw (watts)"
|
||||
)
|
||||
|
||||
objects = NaturalOrderingManager()
|
||||
|
||||
class Meta:
|
||||
ordering = ['device_type', 'name']
|
||||
unique_together = ['device_type', 'name']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def instantiate(self, device):
|
||||
return PowerPort(
|
||||
device=device,
|
||||
name=self.name,
|
||||
maximum_draw=self.maximum_draw,
|
||||
allocated_draw=self.allocated_draw
|
||||
)
|
||||
|
||||
|
||||
class PowerOutletTemplate(ComponentTemplateModel):
|
||||
"""
|
||||
A template for a PowerOutlet to be created for a new Device.
|
||||
"""
|
||||
device_type = models.ForeignKey(
|
||||
to='dcim.DeviceType',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='poweroutlet_templates'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
type = models.CharField(
|
||||
max_length=50,
|
||||
choices=PowerOutletTypeChoices,
|
||||
blank=True
|
||||
)
|
||||
power_port = models.ForeignKey(
|
||||
to='dcim.PowerPortTemplate',
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True,
|
||||
null=True,
|
||||
related_name='poweroutlet_templates'
|
||||
)
|
||||
feed_leg = models.CharField(
|
||||
max_length=50,
|
||||
choices=PowerOutletFeedLegChoices,
|
||||
blank=True,
|
||||
help_text="Phase (for three-phase feeds)"
|
||||
)
|
||||
|
||||
objects = NaturalOrderingManager()
|
||||
|
||||
class Meta:
|
||||
ordering = ['device_type', 'name']
|
||||
unique_together = ['device_type', 'name']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def clean(self):
|
||||
|
||||
# Validate power port assignment
|
||||
if self.power_port and self.power_port.device_type != self.device_type:
|
||||
raise ValidationError(
|
||||
"Parent power port ({}) must belong to the same device type".format(self.power_port)
|
||||
)
|
||||
|
||||
def instantiate(self, device):
|
||||
if self.power_port:
|
||||
power_port = PowerPort.objects.get(device=device, name=self.power_port.name)
|
||||
else:
|
||||
power_port = None
|
||||
return PowerOutlet(
|
||||
device=device,
|
||||
name=self.name,
|
||||
power_port=power_port,
|
||||
feed_leg=self.feed_leg
|
||||
)
|
||||
|
||||
|
||||
class InterfaceTemplate(ComponentTemplateModel):
|
||||
"""
|
||||
A template for a physical data interface on a new Device.
|
||||
"""
|
||||
device_type = models.ForeignKey(
|
||||
to='dcim.DeviceType',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='interface_templates'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=64
|
||||
)
|
||||
type = models.CharField(
|
||||
max_length=50,
|
||||
choices=InterfaceTypeChoices
|
||||
)
|
||||
mgmt_only = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name='Management only'
|
||||
)
|
||||
|
||||
objects = InterfaceManager()
|
||||
|
||||
class Meta:
|
||||
ordering = ['device_type', 'name']
|
||||
unique_together = ['device_type', 'name']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def instantiate(self, device):
|
||||
return Interface(
|
||||
device=device,
|
||||
name=self.name,
|
||||
type=self.type,
|
||||
mgmt_only=self.mgmt_only
|
||||
)
|
||||
|
||||
|
||||
class FrontPortTemplate(ComponentTemplateModel):
|
||||
"""
|
||||
Template for a pass-through port on the front of a new Device.
|
||||
"""
|
||||
device_type = models.ForeignKey(
|
||||
to='dcim.DeviceType',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='frontport_templates'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=64
|
||||
)
|
||||
type = models.CharField(
|
||||
max_length=50,
|
||||
choices=PortTypeChoices
|
||||
)
|
||||
rear_port = models.ForeignKey(
|
||||
to='dcim.RearPortTemplate',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='frontport_templates'
|
||||
)
|
||||
rear_port_position = models.PositiveSmallIntegerField(
|
||||
default=1,
|
||||
validators=[MinValueValidator(1), MaxValueValidator(64)]
|
||||
)
|
||||
|
||||
objects = NaturalOrderingManager()
|
||||
|
||||
class Meta:
|
||||
ordering = ['device_type', 'name']
|
||||
unique_together = [
|
||||
['device_type', 'name'],
|
||||
['rear_port', 'rear_port_position'],
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def clean(self):
|
||||
|
||||
# Validate rear port assignment
|
||||
if self.rear_port.device_type != self.device_type:
|
||||
raise ValidationError(
|
||||
"Rear port ({}) must belong to the same device type".format(self.rear_port)
|
||||
)
|
||||
|
||||
# Validate rear port position assignment
|
||||
if self.rear_port_position > self.rear_port.positions:
|
||||
raise ValidationError(
|
||||
"Invalid rear port position ({}); rear port {} has only {} positions".format(
|
||||
self.rear_port_position, self.rear_port.name, self.rear_port.positions
|
||||
)
|
||||
)
|
||||
|
||||
def instantiate(self, device):
|
||||
if self.rear_port:
|
||||
rear_port = RearPort.objects.get(device=device, name=self.rear_port.name)
|
||||
else:
|
||||
rear_port = None
|
||||
return FrontPort(
|
||||
device=device,
|
||||
name=self.name,
|
||||
type=self.type,
|
||||
rear_port=rear_port,
|
||||
rear_port_position=self.rear_port_position
|
||||
)
|
||||
|
||||
|
||||
class RearPortTemplate(ComponentTemplateModel):
|
||||
"""
|
||||
Template for a pass-through port on the rear of a new Device.
|
||||
"""
|
||||
device_type = models.ForeignKey(
|
||||
to='dcim.DeviceType',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='rearport_templates'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=64
|
||||
)
|
||||
type = models.CharField(
|
||||
max_length=50,
|
||||
choices=PortTypeChoices
|
||||
)
|
||||
positions = models.PositiveSmallIntegerField(
|
||||
default=1,
|
||||
validators=[MinValueValidator(1), MaxValueValidator(64)]
|
||||
)
|
||||
|
||||
objects = NaturalOrderingManager()
|
||||
|
||||
class Meta:
|
||||
ordering = ['device_type', 'name']
|
||||
unique_together = ['device_type', 'name']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def instantiate(self, device):
|
||||
return RearPort(
|
||||
device=device,
|
||||
name=self.name,
|
||||
type=self.type,
|
||||
positions=self.positions
|
||||
)
|
||||
|
||||
|
||||
class DeviceBayTemplate(ComponentTemplateModel):
|
||||
"""
|
||||
A template for a DeviceBay to be created for a new parent Device.
|
||||
"""
|
||||
device_type = models.ForeignKey(
|
||||
to='dcim.DeviceType',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='device_bay_templates'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
|
||||
objects = NaturalOrderingManager()
|
||||
|
||||
class Meta:
|
||||
ordering = ['device_type', 'name']
|
||||
unique_together = ['device_type', 'name']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def instantiate(self, device):
|
||||
return DeviceBay(
|
||||
device=device,
|
||||
name=self.name
|
||||
)
|
||||
1019
netbox/dcim/models/device_components.py
Normal file
1019
netbox/dcim/models/device_components.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -156,10 +156,6 @@ DEVICE_PRIMARY_IP = """
|
||||
{{ record.primary_ip4.address.ip|default:"" }}
|
||||
"""
|
||||
|
||||
SUBDEVICE_ROLE_TEMPLATE = """
|
||||
{% if record.subdevice_role == True %}Parent{% elif record.subdevice_role == False %}Child{% else %}—{% endif %}
|
||||
"""
|
||||
|
||||
DEVICETYPE_INSTANCES_TEMPLATE = """
|
||||
<a href="{% url 'dcim:device_list' %}?manufacturer_id={{ record.manufacturer_id }}&device_type_id={{ record.pk }}">{{ record.instance_count }}</a>
|
||||
"""
|
||||
@@ -276,16 +272,17 @@ class RackGroupTable(BaseTable):
|
||||
|
||||
class RackRoleTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = tables.LinkColumn(verbose_name='Name')
|
||||
rack_count = tables.Column(verbose_name='Racks')
|
||||
color = tables.TemplateColumn(COLOR_LABEL, verbose_name='Color')
|
||||
slug = tables.Column(verbose_name='Slug')
|
||||
actions = tables.TemplateColumn(template_code=RACKROLE_ACTIONS, attrs={'td': {'class': 'text-right noprint'}},
|
||||
verbose_name='')
|
||||
color = tables.TemplateColumn(COLOR_LABEL)
|
||||
actions = tables.TemplateColumn(
|
||||
template_code=RACKROLE_ACTIONS,
|
||||
attrs={'td': {'class': 'text-right noprint'}},
|
||||
verbose_name=''
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = RackRole
|
||||
fields = ('pk', 'name', 'rack_count', 'color', 'slug', 'actions')
|
||||
fields = ('pk', 'name', 'rack_count', 'color', 'description', 'slug', 'actions')
|
||||
|
||||
|
||||
#
|
||||
@@ -393,10 +390,6 @@ class DeviceTypeTable(BaseTable):
|
||||
verbose_name='Device Type'
|
||||
)
|
||||
is_full_depth = BooleanColumn(verbose_name='Full Depth')
|
||||
subdevice_role = tables.TemplateColumn(
|
||||
template_code=SUBDEVICE_ROLE_TEMPLATE,
|
||||
verbose_name='Subdevice Role'
|
||||
)
|
||||
instance_count = tables.TemplateColumn(
|
||||
template_code=DEVICETYPE_INSTANCES_TEMPLATE,
|
||||
verbose_name='Instances'
|
||||
@@ -424,10 +417,19 @@ class ConsolePortTemplateTable(BaseTable):
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = ConsolePortTemplate
|
||||
fields = ('pk', 'name', 'actions')
|
||||
fields = ('pk', 'name', 'type', 'actions')
|
||||
empty_text = "None"
|
||||
|
||||
|
||||
class ConsolePortImportTable(BaseTable):
|
||||
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device')
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = ConsolePort
|
||||
fields = ('device', 'name', 'description')
|
||||
empty_text = False
|
||||
|
||||
|
||||
class ConsoleServerPortTemplateTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
actions = tables.TemplateColumn(
|
||||
@@ -442,6 +444,15 @@ class ConsoleServerPortTemplateTable(BaseTable):
|
||||
empty_text = "None"
|
||||
|
||||
|
||||
class ConsoleServerPortImportTable(BaseTable):
|
||||
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device')
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = ConsoleServerPort
|
||||
fields = ('device', 'name', 'description')
|
||||
empty_text = False
|
||||
|
||||
|
||||
class PowerPortTemplateTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
actions = tables.TemplateColumn(
|
||||
@@ -452,10 +463,19 @@ class PowerPortTemplateTable(BaseTable):
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = PowerPortTemplate
|
||||
fields = ('pk', 'name', 'maximum_draw', 'allocated_draw', 'actions')
|
||||
fields = ('pk', 'name', 'type', 'maximum_draw', 'allocated_draw', 'actions')
|
||||
empty_text = "None"
|
||||
|
||||
|
||||
class PowerPortImportTable(BaseTable):
|
||||
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device')
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = PowerPort
|
||||
fields = ('device', 'name', 'description', 'maximum_draw', 'allocated_draw')
|
||||
empty_text = False
|
||||
|
||||
|
||||
class PowerOutletTemplateTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
actions = tables.TemplateColumn(
|
||||
@@ -466,10 +486,19 @@ class PowerOutletTemplateTable(BaseTable):
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = PowerOutletTemplate
|
||||
fields = ('pk', 'name', 'power_port', 'feed_leg', 'actions')
|
||||
fields = ('pk', 'name', 'type', 'power_port', 'feed_leg', 'actions')
|
||||
empty_text = "None"
|
||||
|
||||
|
||||
class PowerOutletImportTable(BaseTable):
|
||||
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device')
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = PowerOutlet
|
||||
fields = ('device', 'name', 'description', 'power_port', 'feed_leg')
|
||||
empty_text = False
|
||||
|
||||
|
||||
class InterfaceTemplateTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
mgmt_only = tables.TemplateColumn("{% if value %}OOB Management{% endif %}")
|
||||
@@ -485,6 +514,16 @@ class InterfaceTemplateTable(BaseTable):
|
||||
empty_text = "None"
|
||||
|
||||
|
||||
class InterfaceImportTable(BaseTable):
|
||||
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device')
|
||||
virtual_machine = tables.LinkColumn('virtualization:virtualmachine', args=[Accessor('virtual_machine.pk')], verbose_name='Virtual Machine')
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = Interface
|
||||
fields = ('device', 'virtual_machine', 'name', 'description', 'lag', 'type', 'enabled', 'mac_address', 'mtu', 'mgmt_only', 'mode')
|
||||
empty_text = False
|
||||
|
||||
|
||||
class FrontPortTemplateTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
rear_port_position = tables.Column(
|
||||
@@ -502,6 +541,15 @@ class FrontPortTemplateTable(BaseTable):
|
||||
empty_text = "None"
|
||||
|
||||
|
||||
class FrontPortImportTable(BaseTable):
|
||||
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device')
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = FrontPort
|
||||
fields = ('device', 'name', 'description', 'type', 'rear_port', 'rear_port_position')
|
||||
empty_text = False
|
||||
|
||||
|
||||
class RearPortTemplateTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
actions = tables.TemplateColumn(
|
||||
@@ -516,6 +564,15 @@ class RearPortTemplateTable(BaseTable):
|
||||
empty_text = "None"
|
||||
|
||||
|
||||
class RearPortImportTable(BaseTable):
|
||||
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device')
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = RearPort
|
||||
fields = ('device', 'name', 'description', 'type', 'position')
|
||||
empty_text = False
|
||||
|
||||
|
||||
class DeviceBayTemplateTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
actions = tables.TemplateColumn(
|
||||
@@ -558,7 +615,7 @@ class DeviceRoleTable(BaseTable):
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = DeviceRole
|
||||
fields = ('pk', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'slug', 'actions')
|
||||
fields = ('pk', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'description', 'slug', 'actions')
|
||||
|
||||
|
||||
#
|
||||
@@ -645,11 +702,28 @@ class DeviceImportTable(BaseTable):
|
||||
# Device components
|
||||
#
|
||||
|
||||
class DeviceComponentDetailTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
cable = tables.LinkColumn()
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
order_by = ('device', 'name')
|
||||
fields = ('pk', 'device', 'name', 'type', 'description', 'cable')
|
||||
sequence = ('pk', 'device', 'name', 'type', 'description', 'cable')
|
||||
|
||||
|
||||
class ConsolePortTable(BaseTable):
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = ConsolePort
|
||||
fields = ('name',)
|
||||
fields = ('name', 'type')
|
||||
|
||||
|
||||
class ConsolePortDetailTable(DeviceComponentDetailTable):
|
||||
device = tables.LinkColumn()
|
||||
|
||||
class Meta(DeviceComponentDetailTable.Meta, ConsolePortTable.Meta):
|
||||
pass
|
||||
|
||||
|
||||
class ConsoleServerPortTable(BaseTable):
|
||||
@@ -659,18 +733,39 @@ class ConsoleServerPortTable(BaseTable):
|
||||
fields = ('name', 'description')
|
||||
|
||||
|
||||
class ConsoleServerPortDetailTable(DeviceComponentDetailTable):
|
||||
device = tables.LinkColumn()
|
||||
|
||||
class Meta(DeviceComponentDetailTable.Meta, ConsoleServerPortTable.Meta):
|
||||
pass
|
||||
|
||||
|
||||
class PowerPortTable(BaseTable):
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = PowerPort
|
||||
fields = ('name',)
|
||||
fields = ('name', 'type')
|
||||
|
||||
|
||||
class PowerPortDetailTable(DeviceComponentDetailTable):
|
||||
device = tables.LinkColumn()
|
||||
|
||||
class Meta(DeviceComponentDetailTable.Meta, PowerPortTable.Meta):
|
||||
pass
|
||||
|
||||
|
||||
class PowerOutletTable(BaseTable):
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = PowerOutlet
|
||||
fields = ('name', 'description')
|
||||
fields = ('name', 'type', 'description')
|
||||
|
||||
|
||||
class PowerOutletDetailTable(DeviceComponentDetailTable):
|
||||
device = tables.LinkColumn()
|
||||
|
||||
class Meta(DeviceComponentDetailTable.Meta, PowerOutletTable.Meta):
|
||||
pass
|
||||
|
||||
|
||||
class InterfaceTable(BaseTable):
|
||||
@@ -680,6 +775,15 @@ class InterfaceTable(BaseTable):
|
||||
fields = ('name', 'type', 'lag', 'enabled', 'mgmt_only', 'description')
|
||||
|
||||
|
||||
class InterfaceDetailTable(DeviceComponentDetailTable):
|
||||
parent = tables.LinkColumn(order_by=('device', 'virtual_machine'))
|
||||
|
||||
class Meta(InterfaceTable.Meta):
|
||||
order_by = ('parent', 'name')
|
||||
fields = ('pk', 'parent', 'name', 'type', 'description', 'cable')
|
||||
sequence = ('pk', 'parent', 'name', 'type', 'description', 'cable')
|
||||
|
||||
|
||||
class FrontPortTable(BaseTable):
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
@@ -688,6 +792,13 @@ class FrontPortTable(BaseTable):
|
||||
empty_text = "None"
|
||||
|
||||
|
||||
class FrontPortDetailTable(DeviceComponentDetailTable):
|
||||
device = tables.LinkColumn()
|
||||
|
||||
class Meta(DeviceComponentDetailTable.Meta, FrontPortTable.Meta):
|
||||
pass
|
||||
|
||||
|
||||
class RearPortTable(BaseTable):
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
@@ -696,6 +807,13 @@ class RearPortTable(BaseTable):
|
||||
empty_text = "None"
|
||||
|
||||
|
||||
class RearPortDetailTable(DeviceComponentDetailTable):
|
||||
device = tables.LinkColumn()
|
||||
|
||||
class Meta(DeviceComponentDetailTable.Meta, RearPortTable.Meta):
|
||||
pass
|
||||
|
||||
|
||||
class DeviceBayTable(BaseTable):
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
@@ -703,6 +821,26 @@ class DeviceBayTable(BaseTable):
|
||||
fields = ('name',)
|
||||
|
||||
|
||||
class DeviceBayDetailTable(DeviceComponentDetailTable):
|
||||
device = tables.LinkColumn()
|
||||
installed_device = tables.LinkColumn()
|
||||
|
||||
class Meta(DeviceBayTable.Meta):
|
||||
fields = ('pk', 'name', 'device', 'installed_device')
|
||||
sequence = ('pk', 'name', 'device', 'installed_device')
|
||||
exclude = ('cable',)
|
||||
|
||||
|
||||
class DeviceBayImportTable(BaseTable):
|
||||
device = tables.LinkColumn('dcim:device', args=[Accessor('device.pk')], verbose_name='Device')
|
||||
installed_device = tables.LinkColumn('dcim:device', args=[Accessor('installed_device.pk')], verbose_name='Installed Device')
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = DeviceBay
|
||||
fields = ('device', 'name', 'installed_device', 'description')
|
||||
empty_text = False
|
||||
|
||||
|
||||
#
|
||||
# Cables
|
||||
#
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.urls import reverse
|
||||
from netaddr import IPNetwork
|
||||
from rest_framework import status
|
||||
|
||||
from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
from dcim.choices import *
|
||||
from dcim.constants import *
|
||||
from dcim.models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
@@ -11,11 +13,94 @@ from dcim.models import (
|
||||
Rack, RackGroup, RackReservation, RackRole, RearPort, Region, Site, VirtualChassis,
|
||||
)
|
||||
from ipam.models import IPAddress, VLAN
|
||||
from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
|
||||
from utilities.testing import APITestCase
|
||||
from extras.models import Graph
|
||||
from utilities.testing import APITestCase, choices_to_dict
|
||||
from virtualization.models import Cluster, ClusterType
|
||||
|
||||
|
||||
class AppTest(APITestCase):
|
||||
|
||||
def test_root(self):
|
||||
|
||||
url = reverse('dcim-api:api-root')
|
||||
response = self.client.get('{}?format=api'.format(url), **self.header)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_choices(self):
|
||||
|
||||
url = reverse('dcim-api:field-choice-list')
|
||||
response = self.client.get(url, **self.header)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Cable
|
||||
self.assertEqual(choices_to_dict(response.data.get('cable:length_unit')), CableLengthUnitChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('cable:status')), CableStatusChoices.as_dict())
|
||||
content_types = ContentType.objects.filter(CABLE_TERMINATION_MODELS)
|
||||
cable_termination_choices = {
|
||||
"{}.{}".format(ct.app_label, ct.model): ct.name for ct in content_types
|
||||
}
|
||||
self.assertEqual(choices_to_dict(response.data.get('cable:termination_a_type')), cable_termination_choices)
|
||||
self.assertEqual(choices_to_dict(response.data.get('cable:termination_b_type')), cable_termination_choices)
|
||||
self.assertEqual(choices_to_dict(response.data.get('cable:type')), CableTypeChoices.as_dict())
|
||||
|
||||
# Console ports
|
||||
self.assertEqual(choices_to_dict(response.data.get('console-port:type')), ConsolePortTypeChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('console-port:connection_status')), dict(CONNECTION_STATUS_CHOICES))
|
||||
self.assertEqual(choices_to_dict(response.data.get('console-port-template:type')), ConsolePortTypeChoices.as_dict())
|
||||
|
||||
# Console server ports
|
||||
self.assertEqual(choices_to_dict(response.data.get('console-server-port:type')), ConsolePortTypeChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('console-server-port-template:type')), ConsolePortTypeChoices.as_dict())
|
||||
|
||||
# Device
|
||||
self.assertEqual(choices_to_dict(response.data.get('device:face')), DeviceFaceChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('device:status')), DeviceStatusChoices.as_dict())
|
||||
|
||||
# Device type
|
||||
self.assertEqual(choices_to_dict(response.data.get('device-type:subdevice_role')), SubdeviceRoleChoices.as_dict())
|
||||
|
||||
# Front ports
|
||||
self.assertEqual(choices_to_dict(response.data.get('front-port:type')), PortTypeChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('front-port-template:type')), PortTypeChoices.as_dict())
|
||||
|
||||
# Interfaces
|
||||
self.assertEqual(choices_to_dict(response.data.get('interface:type')), InterfaceTypeChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('interface:mode')), InterfaceModeChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('interface-template:type')), InterfaceTypeChoices.as_dict())
|
||||
|
||||
# Power feed
|
||||
self.assertEqual(choices_to_dict(response.data.get('power-feed:phase')), PowerFeedPhaseChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('power-feed:status')), PowerFeedStatusChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('power-feed:supply')), PowerFeedSupplyChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('power-feed:type')), PowerFeedTypeChoices.as_dict())
|
||||
|
||||
# Power outlets
|
||||
self.assertEqual(choices_to_dict(response.data.get('power-outlet:type')), PowerOutletTypeChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('power-outlet:feed_leg')), PowerOutletFeedLegChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('power-outlet-template:type')), PowerOutletTypeChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('power-outlet-template:feed_leg')), PowerOutletFeedLegChoices.as_dict())
|
||||
|
||||
# Power ports
|
||||
self.assertEqual(choices_to_dict(response.data.get('power-port:type')), PowerPortTypeChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('power-port:connection_status')), dict(CONNECTION_STATUS_CHOICES))
|
||||
self.assertEqual(choices_to_dict(response.data.get('power-port-template:type')), PowerPortTypeChoices.as_dict())
|
||||
|
||||
# Rack
|
||||
self.assertEqual(choices_to_dict(response.data.get('rack:type')), RackTypeChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('rack:width')), RackWidthChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('rack:status')), RackStatusChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('rack:outer_unit')), RackDimensionUnitChoices.as_dict())
|
||||
|
||||
# Rear ports
|
||||
self.assertEqual(choices_to_dict(response.data.get('rear-port:type')), PortTypeChoices.as_dict())
|
||||
self.assertEqual(choices_to_dict(response.data.get('rear-port-template:type')), PortTypeChoices.as_dict())
|
||||
|
||||
# Site
|
||||
self.assertEqual(choices_to_dict(response.data.get('site:status')), SiteStatusChoices.as_dict())
|
||||
|
||||
|
||||
class RegionTest(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
@@ -138,16 +223,20 @@ class SiteTest(APITestCase):
|
||||
|
||||
def test_get_site_graphs(self):
|
||||
|
||||
site_ct = ContentType.objects.get_for_model(Site)
|
||||
self.graph1 = Graph.objects.create(
|
||||
type=GRAPH_TYPE_SITE, name='Test Graph 1',
|
||||
type=site_ct,
|
||||
name='Test Graph 1',
|
||||
source='http://example.com/graphs.py?site={{ obj.slug }}&foo=1'
|
||||
)
|
||||
self.graph2 = Graph.objects.create(
|
||||
type=GRAPH_TYPE_SITE, name='Test Graph 2',
|
||||
type=site_ct,
|
||||
name='Test Graph 2',
|
||||
source='http://example.com/graphs.py?site={{ obj.slug }}&foo=2'
|
||||
)
|
||||
self.graph3 = Graph.objects.create(
|
||||
type=GRAPH_TYPE_SITE, name='Test Graph 3',
|
||||
type=site_ct,
|
||||
name='Test Graph 3',
|
||||
source='http://example.com/graphs.py?site={{ obj.slug }}&foo=3'
|
||||
)
|
||||
|
||||
@@ -180,7 +269,7 @@ class SiteTest(APITestCase):
|
||||
'name': 'Test Site 4',
|
||||
'slug': 'test-site-4',
|
||||
'region': self.region1.pk,
|
||||
'status': SITE_STATUS_ACTIVE,
|
||||
'status': SiteStatusChoices.STATUS_ACTIVE,
|
||||
}
|
||||
|
||||
url = reverse('dcim-api:site-list')
|
||||
@@ -200,19 +289,19 @@ class SiteTest(APITestCase):
|
||||
'name': 'Test Site 4',
|
||||
'slug': 'test-site-4',
|
||||
'region': self.region1.pk,
|
||||
'status': SITE_STATUS_ACTIVE,
|
||||
'status': SiteStatusChoices.STATUS_ACTIVE,
|
||||
},
|
||||
{
|
||||
'name': 'Test Site 5',
|
||||
'slug': 'test-site-5',
|
||||
'region': self.region1.pk,
|
||||
'status': SITE_STATUS_ACTIVE,
|
||||
'status': SiteStatusChoices.STATUS_ACTIVE,
|
||||
},
|
||||
{
|
||||
'name': 'Test Site 6',
|
||||
'slug': 'test-site-6',
|
||||
'region': self.region1.pk,
|
||||
'status': SITE_STATUS_ACTIVE,
|
||||
'status': SiteStatusChoices.STATUS_ACTIVE,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -2416,16 +2505,20 @@ class InterfaceTest(APITestCase):
|
||||
|
||||
def test_get_interface_graphs(self):
|
||||
|
||||
interface_ct = ContentType.objects.get_for_model(Interface)
|
||||
self.graph1 = Graph.objects.create(
|
||||
type=GRAPH_TYPE_INTERFACE, name='Test Graph 1',
|
||||
type=interface_ct,
|
||||
name='Test Graph 1',
|
||||
source='http://example.com/graphs.py?interface={{ obj.name }}&foo=1'
|
||||
)
|
||||
self.graph2 = Graph.objects.create(
|
||||
type=GRAPH_TYPE_INTERFACE, name='Test Graph 2',
|
||||
type=interface_ct,
|
||||
name='Test Graph 2',
|
||||
source='http://example.com/graphs.py?interface={{ obj.name }}&foo=2'
|
||||
)
|
||||
self.graph3 = Graph.objects.create(
|
||||
type=GRAPH_TYPE_INTERFACE, name='Test Graph 3',
|
||||
type=interface_ct,
|
||||
name='Test Graph 3',
|
||||
source='http://example.com/graphs.py?interface={{ obj.name }}&foo=3'
|
||||
)
|
||||
|
||||
@@ -2473,7 +2566,7 @@ class InterfaceTest(APITestCase):
|
||||
data = {
|
||||
'device': self.device.pk,
|
||||
'name': 'Test Interface 4',
|
||||
'mode': IFACE_MODE_TAGGED,
|
||||
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||
'untagged_vlan': self.vlan3.id,
|
||||
'tagged_vlans': [self.vlan1.id, self.vlan2.id],
|
||||
}
|
||||
@@ -2520,21 +2613,21 @@ class InterfaceTest(APITestCase):
|
||||
{
|
||||
'device': self.device.pk,
|
||||
'name': 'Test Interface 4',
|
||||
'mode': IFACE_MODE_TAGGED,
|
||||
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||
'untagged_vlan': self.vlan2.id,
|
||||
'tagged_vlans': [self.vlan1.id],
|
||||
},
|
||||
{
|
||||
'device': self.device.pk,
|
||||
'name': 'Test Interface 5',
|
||||
'mode': IFACE_MODE_TAGGED,
|
||||
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||
'untagged_vlan': self.vlan2.id,
|
||||
'tagged_vlans': [self.vlan1.id],
|
||||
},
|
||||
{
|
||||
'device': self.device.pk,
|
||||
'name': 'Test Interface 6',
|
||||
'mode': IFACE_MODE_TAGGED,
|
||||
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||
'untagged_vlan': self.vlan2.id,
|
||||
'tagged_vlans': [self.vlan1.id],
|
||||
},
|
||||
@@ -2553,7 +2646,7 @@ class InterfaceTest(APITestCase):
|
||||
def test_update_interface(self):
|
||||
|
||||
lag_interface = Interface.objects.create(
|
||||
device=self.device, name='Test LAG Interface', type=IFACE_TYPE_LAG
|
||||
device=self.device, name='Test LAG Interface', type=InterfaceTypeChoices.TYPE_LAG
|
||||
)
|
||||
|
||||
data = {
|
||||
@@ -2590,11 +2683,11 @@ class DeviceBayTest(APITestCase):
|
||||
manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
|
||||
self.devicetype1 = DeviceType.objects.create(
|
||||
manufacturer=manufacturer, model='Parent Device Type', slug='parent-device-type',
|
||||
subdevice_role=SUBDEVICE_ROLE_PARENT
|
||||
subdevice_role=SubdeviceRoleChoices.ROLE_PARENT
|
||||
)
|
||||
self.devicetype2 = DeviceType.objects.create(
|
||||
manufacturer=manufacturer, model='Child Device Type', slug='child-device-type',
|
||||
subdevice_role=SUBDEVICE_ROLE_CHILD
|
||||
subdevice_role=SubdeviceRoleChoices.ROLE_CHILD
|
||||
)
|
||||
devicerole = DeviceRole.objects.create(
|
||||
name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
|
||||
@@ -2841,7 +2934,7 @@ class CableTest(APITestCase):
|
||||
)
|
||||
for device in [self.device1, self.device2]:
|
||||
for i in range(0, 10):
|
||||
Interface(device=device, type=IFACE_TYPE_1GE_FIXED, name='eth{}'.format(i)).save()
|
||||
Interface(device=device, type=InterfaceTypeChoices.TYPE_1GE_FIXED, name='eth{}'.format(i)).save()
|
||||
|
||||
self.cable1 = Cable(
|
||||
termination_a=self.device1.interfaces.get(name='eth0'),
|
||||
@@ -2885,7 +2978,7 @@ class CableTest(APITestCase):
|
||||
'termination_a_id': interface_a.pk,
|
||||
'termination_b_type': 'dcim.interface',
|
||||
'termination_b_id': interface_b.pk,
|
||||
'status': CONNECTION_STATUS_PLANNED,
|
||||
'status': CableStatusChoices.STATUS_PLANNED,
|
||||
'label': 'Test Cable 4',
|
||||
}
|
||||
|
||||
@@ -2939,7 +3032,7 @@ class CableTest(APITestCase):
|
||||
|
||||
data = {
|
||||
'label': 'Test Cable X',
|
||||
'status': CONNECTION_STATUS_CONNECTED,
|
||||
'status': CableStatusChoices.STATUS_CONNECTED,
|
||||
}
|
||||
|
||||
url = reverse('dcim-api:cable-detail', kwargs={'pk': self.cable1.pk})
|
||||
@@ -3033,16 +3126,16 @@ class ConnectionTest(APITestCase):
|
||||
device=self.device2, name='Test Console Server Port 1'
|
||||
)
|
||||
rearport1 = RearPort.objects.create(
|
||||
device=self.panel1, name='Test Rear Port 1', type=PORT_TYPE_8P8C
|
||||
device=self.panel1, name='Test Rear Port 1', type=PortTypeChoices.TYPE_8P8C
|
||||
)
|
||||
frontport1 = FrontPort.objects.create(
|
||||
device=self.panel1, name='Test Front Port 1', type=PORT_TYPE_8P8C, rear_port=rearport1
|
||||
device=self.panel1, name='Test Front Port 1', type=PortTypeChoices.TYPE_8P8C, rear_port=rearport1
|
||||
)
|
||||
rearport2 = RearPort.objects.create(
|
||||
device=self.panel2, name='Test Rear Port 2', type=PORT_TYPE_8P8C
|
||||
device=self.panel2, name='Test Rear Port 2', type=PortTypeChoices.TYPE_8P8C
|
||||
)
|
||||
frontport2 = FrontPort.objects.create(
|
||||
device=self.panel2, name='Test Front Port 2', type=PORT_TYPE_8P8C, rear_port=rearport2
|
||||
device=self.panel2, name='Test Front Port 2', type=PortTypeChoices.TYPE_8P8C, rear_port=rearport2
|
||||
)
|
||||
|
||||
url = reverse('dcim-api:cable-list')
|
||||
@@ -3161,16 +3254,16 @@ class ConnectionTest(APITestCase):
|
||||
device=self.device2, name='Test Interface 2'
|
||||
)
|
||||
rearport1 = RearPort.objects.create(
|
||||
device=self.panel1, name='Test Rear Port 1', type=PORT_TYPE_8P8C
|
||||
device=self.panel1, name='Test Rear Port 1', type=PortTypeChoices.TYPE_8P8C
|
||||
)
|
||||
frontport1 = FrontPort.objects.create(
|
||||
device=self.panel1, name='Test Front Port 1', type=PORT_TYPE_8P8C, rear_port=rearport1
|
||||
device=self.panel1, name='Test Front Port 1', type=PortTypeChoices.TYPE_8P8C, rear_port=rearport1
|
||||
)
|
||||
rearport2 = RearPort.objects.create(
|
||||
device=self.panel2, name='Test Rear Port 2', type=PORT_TYPE_8P8C
|
||||
device=self.panel2, name='Test Rear Port 2', type=PortTypeChoices.TYPE_8P8C
|
||||
)
|
||||
frontport2 = FrontPort.objects.create(
|
||||
device=self.panel2, name='Test Front Port 2', type=PORT_TYPE_8P8C, rear_port=rearport2
|
||||
device=self.panel2, name='Test Front Port 2', type=PortTypeChoices.TYPE_8P8C, rear_port=rearport2
|
||||
)
|
||||
|
||||
url = reverse('dcim-api:cable-list')
|
||||
@@ -3272,16 +3365,16 @@ class ConnectionTest(APITestCase):
|
||||
circuit=circuit, term_side='A', site=self.site, port_speed=10000
|
||||
)
|
||||
rearport1 = RearPort.objects.create(
|
||||
device=self.panel1, name='Test Rear Port 1', type=PORT_TYPE_8P8C
|
||||
device=self.panel1, name='Test Rear Port 1', type=PortTypeChoices.TYPE_8P8C
|
||||
)
|
||||
frontport1 = FrontPort.objects.create(
|
||||
device=self.panel1, name='Test Front Port 1', type=PORT_TYPE_8P8C, rear_port=rearport1
|
||||
device=self.panel1, name='Test Front Port 1', type=PortTypeChoices.TYPE_8P8C, rear_port=rearport1
|
||||
)
|
||||
rearport2 = RearPort.objects.create(
|
||||
device=self.panel2, name='Test Rear Port 2', type=PORT_TYPE_8P8C
|
||||
device=self.panel2, name='Test Rear Port 2', type=PortTypeChoices.TYPE_8P8C
|
||||
)
|
||||
frontport2 = FrontPort.objects.create(
|
||||
device=self.panel2, name='Test Front Port 2', type=PORT_TYPE_8P8C, rear_port=rearport2
|
||||
device=self.panel2, name='Test Front Port 2', type=PortTypeChoices.TYPE_8P8C, rear_port=rearport2
|
||||
)
|
||||
|
||||
url = reverse('dcim-api:cable-list')
|
||||
@@ -3410,23 +3503,23 @@ class VirtualChassisTest(APITestCase):
|
||||
device_type=device_type, device_role=device_role, name='StackSwitch9', site=site
|
||||
)
|
||||
for i in range(0, 13):
|
||||
Interface.objects.create(device=self.device1, name='1/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
|
||||
Interface.objects.create(device=self.device1, name='1/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
|
||||
for i in range(0, 13):
|
||||
Interface.objects.create(device=self.device2, name='2/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
|
||||
Interface.objects.create(device=self.device2, name='2/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
|
||||
for i in range(0, 13):
|
||||
Interface.objects.create(device=self.device3, name='3/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
|
||||
Interface.objects.create(device=self.device3, name='3/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
|
||||
for i in range(0, 13):
|
||||
Interface.objects.create(device=self.device4, name='1/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
|
||||
Interface.objects.create(device=self.device4, name='1/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
|
||||
for i in range(0, 13):
|
||||
Interface.objects.create(device=self.device5, name='2/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
|
||||
Interface.objects.create(device=self.device5, name='2/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
|
||||
for i in range(0, 13):
|
||||
Interface.objects.create(device=self.device6, name='3/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
|
||||
Interface.objects.create(device=self.device6, name='3/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
|
||||
for i in range(0, 13):
|
||||
Interface.objects.create(device=self.device7, name='1/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
|
||||
Interface.objects.create(device=self.device7, name='1/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
|
||||
for i in range(0, 13):
|
||||
Interface.objects.create(device=self.device8, name='2/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
|
||||
Interface.objects.create(device=self.device8, name='2/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
|
||||
for i in range(0, 13):
|
||||
Interface.objects.create(device=self.device9, name='3/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
|
||||
Interface.objects.create(device=self.device9, name='3/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
|
||||
|
||||
# Create two VirtualChassis with three members each
|
||||
self.vc1 = VirtualChassis.objects.create(master=self.device1, domain='test-domain-1')
|
||||
@@ -3678,22 +3771,22 @@ class PowerFeedTest(APITestCase):
|
||||
site=self.site1, rack_group=self.rackgroup1, name='Test Power Panel 2'
|
||||
)
|
||||
self.powerfeed1 = PowerFeed.objects.create(
|
||||
power_panel=self.powerpanel1, rack=self.rack1, name='Test Power Feed 1A', type=POWERFEED_TYPE_PRIMARY
|
||||
power_panel=self.powerpanel1, rack=self.rack1, name='Test Power Feed 1A', type=PowerFeedTypeChoices.TYPE_PRIMARY
|
||||
)
|
||||
self.powerfeed2 = PowerFeed.objects.create(
|
||||
power_panel=self.powerpanel2, rack=self.rack1, name='Test Power Feed 1B', type=POWERFEED_TYPE_REDUNDANT
|
||||
power_panel=self.powerpanel2, rack=self.rack1, name='Test Power Feed 1B', type=PowerFeedTypeChoices.TYPE_REDUNDANT
|
||||
)
|
||||
self.powerfeed3 = PowerFeed.objects.create(
|
||||
power_panel=self.powerpanel1, rack=self.rack2, name='Test Power Feed 2A', type=POWERFEED_TYPE_PRIMARY
|
||||
power_panel=self.powerpanel1, rack=self.rack2, name='Test Power Feed 2A', type=PowerFeedTypeChoices.TYPE_PRIMARY
|
||||
)
|
||||
self.powerfeed4 = PowerFeed.objects.create(
|
||||
power_panel=self.powerpanel2, rack=self.rack2, name='Test Power Feed 2B', type=POWERFEED_TYPE_REDUNDANT
|
||||
power_panel=self.powerpanel2, rack=self.rack2, name='Test Power Feed 2B', type=PowerFeedTypeChoices.TYPE_REDUNDANT
|
||||
)
|
||||
self.powerfeed5 = PowerFeed.objects.create(
|
||||
power_panel=self.powerpanel1, rack=self.rack3, name='Test Power Feed 3A', type=POWERFEED_TYPE_PRIMARY
|
||||
power_panel=self.powerpanel1, rack=self.rack3, name='Test Power Feed 3A', type=PowerFeedTypeChoices.TYPE_PRIMARY
|
||||
)
|
||||
self.powerfeed6 = PowerFeed.objects.create(
|
||||
power_panel=self.powerpanel2, rack=self.rack3, name='Test Power Feed 3B', type=POWERFEED_TYPE_REDUNDANT
|
||||
power_panel=self.powerpanel2, rack=self.rack3, name='Test Power Feed 3B', type=PowerFeedTypeChoices.TYPE_REDUNDANT
|
||||
)
|
||||
|
||||
def test_get_powerfeed(self):
|
||||
@@ -3726,7 +3819,7 @@ class PowerFeedTest(APITestCase):
|
||||
'name': 'Test Power Feed 4A',
|
||||
'power_panel': self.powerpanel1.pk,
|
||||
'rack': self.rack4.pk,
|
||||
'type': POWERFEED_TYPE_PRIMARY,
|
||||
'type': PowerFeedTypeChoices.TYPE_PRIMARY,
|
||||
}
|
||||
|
||||
url = reverse('dcim-api:powerfeed-list')
|
||||
@@ -3746,13 +3839,13 @@ class PowerFeedTest(APITestCase):
|
||||
'name': 'Test Power Feed 4A',
|
||||
'power_panel': self.powerpanel1.pk,
|
||||
'rack': self.rack4.pk,
|
||||
'type': POWERFEED_TYPE_PRIMARY,
|
||||
'type': PowerFeedTypeChoices.TYPE_PRIMARY,
|
||||
},
|
||||
{
|
||||
'name': 'Test Power Feed 4B',
|
||||
'power_panel': self.powerpanel1.pk,
|
||||
'rack': self.rack4.pk,
|
||||
'type': POWERFEED_TYPE_REDUNDANT,
|
||||
'type': PowerFeedTypeChoices.TYPE_REDUNDANT,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -3769,7 +3862,7 @@ class PowerFeedTest(APITestCase):
|
||||
data = {
|
||||
'name': 'Test Power Feed X',
|
||||
'rack': self.rack4.pk,
|
||||
'type': POWERFEED_TYPE_REDUNDANT,
|
||||
'type': PowerFeedTypeChoices.TYPE_REDUNDANT,
|
||||
}
|
||||
|
||||
url = reverse('dcim-api:powerfeed-detail', kwargs={'pk': self.powerfeed1.pk})
|
||||
|
||||
2609
netbox/dcim/tests/test_filters.py
Normal file
2609
netbox/dcim/tests/test_filters.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -21,10 +21,10 @@ class DeviceTestCase(TestCase):
|
||||
'device_type': get_id(DeviceType, 'qfx5100-48s'),
|
||||
'site': get_id(Site, 'test1'),
|
||||
'rack': '1',
|
||||
'face': RACK_FACE_FRONT,
|
||||
'face': DeviceFaceChoices.FACE_FRONT,
|
||||
'position': 41,
|
||||
'platform': get_id(Platform, 'juniper-junos'),
|
||||
'status': DEVICE_STATUS_ACTIVE,
|
||||
'status': DeviceStatusChoices.STATUS_ACTIVE,
|
||||
})
|
||||
self.assertTrue(test.is_valid(), test.fields['position'].choices)
|
||||
self.assertTrue(test.save())
|
||||
@@ -38,10 +38,10 @@ class DeviceTestCase(TestCase):
|
||||
'device_type': get_id(DeviceType, 'qfx5100-48s'),
|
||||
'site': get_id(Site, 'test1'),
|
||||
'rack': '1',
|
||||
'face': RACK_FACE_FRONT,
|
||||
'face': DeviceFaceChoices.FACE_FRONT,
|
||||
'position': 1,
|
||||
'platform': get_id(Platform, 'juniper-junos'),
|
||||
'status': DEVICE_STATUS_ACTIVE,
|
||||
'status': DeviceStatusChoices.STATUS_ACTIVE,
|
||||
})
|
||||
self.assertFalse(test.is_valid())
|
||||
|
||||
@@ -54,10 +54,10 @@ class DeviceTestCase(TestCase):
|
||||
'device_type': get_id(DeviceType, 'cwg-24vym415c9'),
|
||||
'site': get_id(Site, 'test1'),
|
||||
'rack': '1',
|
||||
'face': None,
|
||||
'face': '',
|
||||
'position': None,
|
||||
'platform': None,
|
||||
'status': DEVICE_STATUS_ACTIVE,
|
||||
'status': DeviceStatusChoices.STATUS_ACTIVE,
|
||||
})
|
||||
self.assertTrue(test.is_valid())
|
||||
self.assertTrue(test.save())
|
||||
@@ -71,10 +71,10 @@ class DeviceTestCase(TestCase):
|
||||
'device_type': get_id(DeviceType, 'cwg-24vym415c9'),
|
||||
'site': get_id(Site, 'test1'),
|
||||
'rack': '1',
|
||||
'face': RACK_FACE_REAR,
|
||||
'face': DeviceFaceChoices.FACE_REAR,
|
||||
'position': None,
|
||||
'platform': None,
|
||||
'status': DEVICE_STATUS_ACTIVE,
|
||||
'status': DeviceStatusChoices.STATUS_ACTIVE,
|
||||
})
|
||||
self.assertTrue(test.is_valid())
|
||||
self.assertTrue(test.save())
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.test import TestCase
|
||||
|
||||
from dcim.choices import *
|
||||
from dcim.constants import CONNECTION_STATUS_CONNECTED, CONNECTION_STATUS_PLANNED
|
||||
from dcim.models import *
|
||||
from tenancy.models import Tenant
|
||||
|
||||
|
||||
class RackTestCase(TestCase):
|
||||
@@ -87,7 +91,7 @@ class RackTestCase(TestCase):
|
||||
site=self.site1,
|
||||
rack=rack1,
|
||||
position=43,
|
||||
face=RACK_FACE_FRONT,
|
||||
face=DeviceFaceChoices.FACE_FRONT,
|
||||
)
|
||||
device1.save()
|
||||
|
||||
@@ -117,7 +121,7 @@ class RackTestCase(TestCase):
|
||||
site=self.site1,
|
||||
rack=self.rack,
|
||||
position=10,
|
||||
face=RACK_FACE_REAR,
|
||||
face=DeviceFaceChoices.FACE_REAR,
|
||||
)
|
||||
device1.save()
|
||||
|
||||
@@ -125,14 +129,14 @@ class RackTestCase(TestCase):
|
||||
self.assertEqual(list(self.rack.units), list(reversed(range(1, 43))))
|
||||
|
||||
# Validate inventory (front face)
|
||||
rack1_inventory_front = self.rack.get_front_elevation()
|
||||
rack1_inventory_front = self.rack.get_rack_units(face=DeviceFaceChoices.FACE_FRONT)
|
||||
self.assertEqual(rack1_inventory_front[-10]['device'], device1)
|
||||
del(rack1_inventory_front[-10])
|
||||
for u in rack1_inventory_front:
|
||||
self.assertIsNone(u['device'])
|
||||
|
||||
# Validate inventory (rear face)
|
||||
rack1_inventory_rear = self.rack.get_rear_elevation()
|
||||
rack1_inventory_rear = self.rack.get_rack_units(face=DeviceFaceChoices.FACE_REAR)
|
||||
self.assertEqual(rack1_inventory_rear[-10]['device'], device1)
|
||||
del(rack1_inventory_rear[-10])
|
||||
for u in rack1_inventory_rear:
|
||||
@@ -146,7 +150,7 @@ class RackTestCase(TestCase):
|
||||
site=self.site1,
|
||||
rack=self.rack,
|
||||
position=None,
|
||||
face=None,
|
||||
face='',
|
||||
)
|
||||
self.assertTrue(pdu)
|
||||
|
||||
@@ -187,20 +191,20 @@ class DeviceTestCase(TestCase):
|
||||
device_type=self.device_type,
|
||||
name='Power Outlet 1',
|
||||
power_port=ppt,
|
||||
feed_leg=POWERFEED_LEG_A
|
||||
feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A
|
||||
).save()
|
||||
|
||||
InterfaceTemplate(
|
||||
device_type=self.device_type,
|
||||
name='Interface 1',
|
||||
type=IFACE_TYPE_1GE_FIXED,
|
||||
type=InterfaceTypeChoices.TYPE_1GE_FIXED,
|
||||
mgmt_only=True
|
||||
).save()
|
||||
|
||||
rpt = RearPortTemplate(
|
||||
device_type=self.device_type,
|
||||
name='Rear Port 1',
|
||||
type=PORT_TYPE_8P8C,
|
||||
type=PortTypeChoices.TYPE_8P8C,
|
||||
positions=8
|
||||
)
|
||||
rpt.save()
|
||||
@@ -208,7 +212,7 @@ class DeviceTestCase(TestCase):
|
||||
FrontPortTemplate(
|
||||
device_type=self.device_type,
|
||||
name='Front Port 1',
|
||||
type=PORT_TYPE_8P8C,
|
||||
type=PortTypeChoices.TYPE_8P8C,
|
||||
rear_port=rpt,
|
||||
rear_port_position=2
|
||||
).save()
|
||||
@@ -251,27 +255,27 @@ class DeviceTestCase(TestCase):
|
||||
device=d,
|
||||
name='Power Outlet 1',
|
||||
power_port=pp,
|
||||
feed_leg=POWERFEED_LEG_A
|
||||
feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A
|
||||
)
|
||||
|
||||
Interface.objects.get(
|
||||
device=d,
|
||||
name='Interface 1',
|
||||
type=IFACE_TYPE_1GE_FIXED,
|
||||
type=InterfaceTypeChoices.TYPE_1GE_FIXED,
|
||||
mgmt_only=True
|
||||
)
|
||||
|
||||
rp = RearPort.objects.get(
|
||||
device=d,
|
||||
name='Rear Port 1',
|
||||
type=PORT_TYPE_8P8C,
|
||||
type=PortTypeChoices.TYPE_8P8C,
|
||||
positions=8
|
||||
)
|
||||
|
||||
FrontPort.objects.get(
|
||||
device=d,
|
||||
name='Front Port 1',
|
||||
type=PORT_TYPE_8P8C,
|
||||
type=PortTypeChoices.TYPE_8P8C,
|
||||
rear_port=rp,
|
||||
rear_port_position=2
|
||||
)
|
||||
@@ -281,6 +285,42 @@ class DeviceTestCase(TestCase):
|
||||
name='Device Bay 1'
|
||||
)
|
||||
|
||||
def test_device_duplicate_name_per_site(self):
|
||||
|
||||
device1 = Device(
|
||||
site=self.site,
|
||||
device_type=self.device_type,
|
||||
device_role=self.device_role,
|
||||
name='Test Device 1'
|
||||
)
|
||||
device1.save()
|
||||
|
||||
device2 = Device(
|
||||
site=device1.site,
|
||||
device_type=device1.device_type,
|
||||
device_role=device1.device_role,
|
||||
name=device1.name
|
||||
)
|
||||
|
||||
# Two devices assigned to the same Site and no Tenant should fail validation
|
||||
with self.assertRaises(ValidationError):
|
||||
device2.full_clean()
|
||||
|
||||
tenant = Tenant.objects.create(name='Test Tenant 1', slug='test-tenant-1')
|
||||
device1.tenant = tenant
|
||||
device1.save()
|
||||
device2.tenant = tenant
|
||||
|
||||
# Two devices assigned to the same Site and the same Tenant should fail validation
|
||||
with self.assertRaises(ValidationError):
|
||||
device2.full_clean()
|
||||
|
||||
device2.tenant = None
|
||||
|
||||
# Two devices assigned to the same Site and different Tenants should pass validation
|
||||
device2.full_clean()
|
||||
device2.save()
|
||||
|
||||
|
||||
class CableTestCase(TestCase):
|
||||
|
||||
@@ -325,9 +365,12 @@ class CableTestCase(TestCase):
|
||||
|
||||
def test_cable_deletion(self):
|
||||
"""
|
||||
When a Cable is deleted, the `cable` field on its termination points must be nullified.
|
||||
When a Cable is deleted, the `cable` field on its termination points must be nullified. The str() method
|
||||
should still return the PK of the string even after being nullified.
|
||||
"""
|
||||
self.cable.delete()
|
||||
self.assertIsNone(self.cable.pk)
|
||||
self.assertNotEqual(str(self.cable), '#None')
|
||||
interface1 = Interface.objects.get(pk=self.interface1.pk)
|
||||
self.assertIsNone(interface1.cable)
|
||||
interface2 = Interface.objects.get(pk=self.interface2.pk)
|
||||
@@ -379,7 +422,7 @@ class CableTestCase(TestCase):
|
||||
"""
|
||||
A cable cannot terminate to a virtual interface
|
||||
"""
|
||||
virtual_interface = Interface(device=self.device1, name="V1", type=IFACE_TYPE_VIRTUAL)
|
||||
virtual_interface = Interface(device=self.device1, name="V1", type=InterfaceTypeChoices.TYPE_VIRTUAL)
|
||||
cable = Cable(termination_a=self.interface2, termination_b=virtual_interface)
|
||||
with self.assertRaises(ValidationError):
|
||||
cable.clean()
|
||||
@@ -388,7 +431,7 @@ class CableTestCase(TestCase):
|
||||
"""
|
||||
A cable cannot terminate to a wireless interface
|
||||
"""
|
||||
wireless_interface = Interface(device=self.device1, name="W1", type=IFACE_TYPE_80211A)
|
||||
wireless_interface = Interface(device=self.device1, name="W1", type=InterfaceTypeChoices.TYPE_80211A)
|
||||
cable = Cable(termination_a=self.interface2, termination_b=wireless_interface)
|
||||
with self.assertRaises(ValidationError):
|
||||
cable.clean()
|
||||
@@ -421,16 +464,16 @@ class CablePathTestCase(TestCase):
|
||||
device_type=devicetype, device_role=devicerole, name='Test Panel 2', site=site
|
||||
)
|
||||
self.rear_port1 = RearPort.objects.create(
|
||||
device=self.panel1, name='Rear Port 1', type=PORT_TYPE_8P8C
|
||||
device=self.panel1, name='Rear Port 1', type=PortTypeChoices.TYPE_8P8C
|
||||
)
|
||||
self.front_port1 = FrontPort.objects.create(
|
||||
device=self.panel1, name='Front Port 1', type=PORT_TYPE_8P8C, rear_port=self.rear_port1
|
||||
device=self.panel1, name='Front Port 1', type=PortTypeChoices.TYPE_8P8C, rear_port=self.rear_port1
|
||||
)
|
||||
self.rear_port2 = RearPort.objects.create(
|
||||
device=self.panel2, name='Rear Port 2', type=PORT_TYPE_8P8C
|
||||
device=self.panel2, name='Rear Port 2', type=PortTypeChoices.TYPE_8P8C
|
||||
)
|
||||
self.front_port2 = FrontPort.objects.create(
|
||||
device=self.panel2, name='Front Port 2', type=PORT_TYPE_8P8C, rear_port=self.rear_port2
|
||||
device=self.panel2, name='Front Port 2', type=PortTypeChoices.TYPE_8P8C, rear_port=self.rear_port2
|
||||
)
|
||||
|
||||
def test_path_completion(self):
|
||||
@@ -450,14 +493,18 @@ class CablePathTestCase(TestCase):
|
||||
self.assertIsNone(interface1.connection_status)
|
||||
|
||||
# Third segment
|
||||
cable3 = Cable(termination_a=self.front_port2, termination_b=self.interface2, status=CONNECTION_STATUS_PLANNED)
|
||||
cable3 = Cable(
|
||||
termination_a=self.front_port2,
|
||||
termination_b=self.interface2,
|
||||
status=CableStatusChoices.STATUS_PLANNED
|
||||
)
|
||||
cable3.save()
|
||||
interface1 = Interface.objects.get(pk=self.interface1.pk)
|
||||
self.assertEqual(interface1.connected_endpoint, self.interface2)
|
||||
self.assertEqual(interface1.connection_status, CONNECTION_STATUS_PLANNED)
|
||||
|
||||
# Switch third segment from planned to connected
|
||||
cable3.status = CONNECTION_STATUS_CONNECTED
|
||||
cable3.status = CableStatusChoices.STATUS_CONNECTED
|
||||
cable3.save()
|
||||
interface1 = Interface.objects.get(pk=self.interface1.pk)
|
||||
self.assertEqual(interface1.connected_endpoint, self.interface2)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -82,7 +82,7 @@ urlpatterns = [
|
||||
# Device types
|
||||
path(r'device-types/', views.DeviceTypeListView.as_view(), name='devicetype_list'),
|
||||
path(r'device-types/add/', views.DeviceTypeCreateView.as_view(), name='devicetype_add'),
|
||||
path(r'device-types/import/', views.DeviceTypeBulkImportView.as_view(), name='devicetype_import'),
|
||||
path(r'device-types/import/', views.DeviceTypeImportView.as_view(), name='devicetype_import'),
|
||||
path(r'device-types/edit/', views.DeviceTypeBulkEditView.as_view(), name='devicetype_bulk_edit'),
|
||||
path(r'device-types/delete/', views.DeviceTypeBulkDeleteView.as_view(), name='devicetype_bulk_delete'),
|
||||
path(r'device-types/<int:pk>/', views.DeviceTypeView.as_view(), name='devicetype'),
|
||||
@@ -171,49 +171,58 @@ urlpatterns = [
|
||||
path(r'devices/console-ports/add/', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'),
|
||||
path(r'devices/<int:pk>/console-ports/add/', views.ConsolePortCreateView.as_view(), name='consoleport_add'),
|
||||
path(r'devices/<int:pk>/console-ports/delete/', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'),
|
||||
path(r'console-ports/', views.ConsolePortListView.as_view(), name='consoleport_list'),
|
||||
path(r'console-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='consoleport_connect', kwargs={'termination_a_type': ConsolePort}),
|
||||
path(r'console-ports/<int:pk>/edit/', views.ConsolePortEditView.as_view(), name='consoleport_edit'),
|
||||
path(r'console-ports/<int:pk>/delete/', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'),
|
||||
path(r'console-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='consoleport_trace', kwargs={'model': ConsolePort}),
|
||||
path(r'console-ports/import/', views.ConsolePortBulkImportView.as_view(), name='consoleport_import'),
|
||||
|
||||
# Console server ports
|
||||
path(r'devices/console-server-ports/add/', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'),
|
||||
path(r'devices/<int:pk>/console-server-ports/add/', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'),
|
||||
path(r'devices/<int:pk>/console-server-ports/edit/', views.ConsoleServerPortBulkEditView.as_view(), name='consoleserverport_bulk_edit'),
|
||||
path(r'devices/<int:pk>/console-server-ports/delete/', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'),
|
||||
path(r'console-server-ports/', views.ConsoleServerPortListView.as_view(), name='consoleserverport_list'),
|
||||
path(r'console-server-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'termination_a_type': ConsoleServerPort}),
|
||||
path(r'console-server-ports/<int:pk>/edit/', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'),
|
||||
path(r'console-server-ports/<int:pk>/delete/', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'),
|
||||
path(r'console-server-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='consoleserverport_trace', kwargs={'model': ConsoleServerPort}),
|
||||
path(r'console-server-ports/rename/', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'),
|
||||
path(r'console-server-ports/disconnect/', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'),
|
||||
path(r'console-server-ports/import/', views.ConsoleServerPortBulkImportView.as_view(), name='consoleserverport_import'),
|
||||
|
||||
# Power ports
|
||||
path(r'devices/power-ports/add/', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'),
|
||||
path(r'devices/<int:pk>/power-ports/add/', views.PowerPortCreateView.as_view(), name='powerport_add'),
|
||||
path(r'devices/<int:pk>/power-ports/delete/', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'),
|
||||
path(r'power-ports/', views.PowerPortListView.as_view(), name='powerport_list'),
|
||||
path(r'power-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='powerport_connect', kwargs={'termination_a_type': PowerPort}),
|
||||
path(r'power-ports/<int:pk>/edit/', views.PowerPortEditView.as_view(), name='powerport_edit'),
|
||||
path(r'power-ports/<int:pk>/delete/', views.PowerPortDeleteView.as_view(), name='powerport_delete'),
|
||||
path(r'power-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='powerport_trace', kwargs={'model': PowerPort}),
|
||||
path(r'power-ports/import/', views.PowerPortBulkImportView.as_view(), name='powerport_import'),
|
||||
|
||||
# Power outlets
|
||||
path(r'devices/power-outlets/add/', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'),
|
||||
path(r'devices/<int:pk>/power-outlets/add/', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'),
|
||||
path(r'devices/<int:pk>/power-outlets/edit/', views.PowerOutletBulkEditView.as_view(), name='poweroutlet_bulk_edit'),
|
||||
path(r'devices/<int:pk>/power-outlets/delete/', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'),
|
||||
path(r'power-outlets/', views.PowerOutletListView.as_view(), name='poweroutlet_list'),
|
||||
path(r'power-outlets/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'termination_a_type': PowerOutlet}),
|
||||
path(r'power-outlets/<int:pk>/edit/', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'),
|
||||
path(r'power-outlets/<int:pk>/delete/', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'),
|
||||
path(r'power-outlets/<int:pk>/trace/', views.CableTraceView.as_view(), name='poweroutlet_trace', kwargs={'model': PowerOutlet}),
|
||||
path(r'power-outlets/rename/', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'),
|
||||
path(r'power-outlets/disconnect/', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'),
|
||||
path(r'power-outlets/import/', views.PowerOutletBulkImportView.as_view(), name='poweroutlet_import'),
|
||||
|
||||
# Interfaces
|
||||
path(r'devices/interfaces/add/', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'),
|
||||
path(r'devices/<int:pk>/interfaces/add/', views.InterfaceCreateView.as_view(), name='interface_add'),
|
||||
path(r'devices/<int:pk>/interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'),
|
||||
path(r'devices/<int:pk>/interfaces/delete/', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
|
||||
path(r'interfaces/', views.InterfaceListView.as_view(), name='interface_list'),
|
||||
path(r'interfaces/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='interface_connect', kwargs={'termination_a_type': Interface}),
|
||||
path(r'interfaces/<int:pk>/', views.InterfaceView.as_view(), name='interface'),
|
||||
path(r'interfaces/<int:pk>/edit/', views.InterfaceEditView.as_view(), name='interface_edit'),
|
||||
@@ -222,40 +231,47 @@ urlpatterns = [
|
||||
path(r'interfaces/<int:pk>/trace/', views.CableTraceView.as_view(), name='interface_trace', kwargs={'model': Interface}),
|
||||
path(r'interfaces/rename/', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'),
|
||||
path(r'interfaces/disconnect/', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'),
|
||||
path(r'interfaces/import/', views.InterfaceBulkImportView.as_view(), name='interface_import'),
|
||||
|
||||
# Front ports
|
||||
# path(r'devices/front-ports/add/', views.DeviceBulkAddFrontPortView.as_view(), name='device_bulk_add_frontport'),
|
||||
path(r'devices/<int:pk>/front-ports/add/', views.FrontPortCreateView.as_view(), name='frontport_add'),
|
||||
path(r'devices/<int:pk>/front-ports/edit/', views.FrontPortBulkEditView.as_view(), name='frontport_bulk_edit'),
|
||||
path(r'devices/<int:pk>/front-ports/delete/', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'),
|
||||
path(r'front-ports/', views.FrontPortListView.as_view(), name='frontport_list'),
|
||||
path(r'front-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='frontport_connect', kwargs={'termination_a_type': FrontPort}),
|
||||
path(r'front-ports/<int:pk>/edit/', views.FrontPortEditView.as_view(), name='frontport_edit'),
|
||||
path(r'front-ports/<int:pk>/delete/', views.FrontPortDeleteView.as_view(), name='frontport_delete'),
|
||||
path(r'front-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='frontport_trace', kwargs={'model': FrontPort}),
|
||||
path(r'front-ports/rename/', views.FrontPortBulkRenameView.as_view(), name='frontport_bulk_rename'),
|
||||
path(r'front-ports/disconnect/', views.FrontPortBulkDisconnectView.as_view(), name='frontport_bulk_disconnect'),
|
||||
path(r'front-ports/import/', views.FrontPortBulkImportView.as_view(), name='frontport_import'),
|
||||
|
||||
# Rear ports
|
||||
# path(r'devices/rear-ports/add/', views.DeviceBulkAddRearPortView.as_view(), name='device_bulk_add_rearport'),
|
||||
path(r'devices/<int:pk>/rear-ports/add/', views.RearPortCreateView.as_view(), name='rearport_add'),
|
||||
path(r'devices/<int:pk>/rear-ports/edit/', views.RearPortBulkEditView.as_view(), name='rearport_bulk_edit'),
|
||||
path(r'devices/<int:pk>/rear-ports/delete/', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'),
|
||||
path(r'rear-ports/', views.RearPortListView.as_view(), name='rearport_list'),
|
||||
path(r'rear-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='rearport_connect', kwargs={'termination_a_type': RearPort}),
|
||||
path(r'rear-ports/<int:pk>/edit/', views.RearPortEditView.as_view(), name='rearport_edit'),
|
||||
path(r'rear-ports/<int:pk>/delete/', views.RearPortDeleteView.as_view(), name='rearport_delete'),
|
||||
path(r'rear-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='rearport_trace', kwargs={'model': RearPort}),
|
||||
path(r'rear-ports/rename/', views.RearPortBulkRenameView.as_view(), name='rearport_bulk_rename'),
|
||||
path(r'rear-ports/disconnect/', views.RearPortBulkDisconnectView.as_view(), name='rearport_bulk_disconnect'),
|
||||
path(r'rear-ports/import/', views.RearPortBulkImportView.as_view(), name='rearport_import'),
|
||||
|
||||
# Device bays
|
||||
path(r'devices/device-bays/add/', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'),
|
||||
path(r'devices/<int:pk>/bays/add/', views.DeviceBayCreateView.as_view(), name='devicebay_add'),
|
||||
path(r'devices/<int:pk>/bays/delete/', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'),
|
||||
path(r'device-bays/', views.DeviceBayListView.as_view(), name='devicebay_list'),
|
||||
path(r'device-bays/<int:pk>/edit/', views.DeviceBayEditView.as_view(), name='devicebay_edit'),
|
||||
path(r'device-bays/<int:pk>/delete/', views.DeviceBayDeleteView.as_view(), name='devicebay_delete'),
|
||||
path(r'device-bays/<int:pk>/populate/', views.DeviceBayPopulateView.as_view(), name='devicebay_populate'),
|
||||
path(r'device-bays/<int:pk>/depopulate/', views.DeviceBayDepopulateView.as_view(), name='devicebay_depopulate'),
|
||||
path(r'device-bays/rename/', views.DeviceBayBulkRenameView.as_view(), name='devicebay_bulk_rename'),
|
||||
path(r'device-bays/import/', views.DeviceBayBulkImportView.as_view(), name='devicebay_import'),
|
||||
|
||||
# Inventory items
|
||||
path(r'inventory-items/', views.InventoryItemListView.as_view(), name='inventoryitem_list'),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user