|
|
& B! y* u% {5 a* |( _( ]9 I5 A& W7 [非常好 这一步就是把你的 OpenStack Neutron 网络管理模块从「仅网络层」扩展到完整的 网络 + 子网 + 端口管理功能。
: f& v* }2 [$ V1 l" S* f下面我给你完整规划 + 代码框架(基于你现有的 network_list_view 和 edit_network_view 结构,可直接嵌入项目)。
2 T* F0 a4 j& t& x& V+ z7 l0 M( @3 h! c
- e# r% J# R/ F; [ [/ f$ r
一、总体架构设计# [4 H1 E H% P( `, ~
2 r) @2 H4 J+ c# Z+ V# S* |我们按功能模块划分成三个部分:& r* i# m9 W, \$ ^0 q f5 f, F/ V
* c% F( T6 s" H0 {6 U
networks/; L8 ~; K( D- U+ W5 o
├── views/( V k- c" q6 ]6 w' d5 M9 @$ U
│ ├── networks.py # 网络的增删改查
% O/ A# N. g1 I6 Z3 w% a│ ├── subnets.py # 子网管理(新增/编辑/删除): ^8 u" e- i6 v5 S
│ ├── ports.py # 端口管理(创建/删除/绑定安全组/绑定虚机)1 Q. }8 e; b. y
│
6 f/ x% {! I' n# J) Q( h1 h├── templates/* t+ s1 ~7 N& |
│ ├── networks.html
. M4 A/ M) @' b. t! t│ ├── network_edit.html5 _# k' v1 f) s& l) e
│ ├── subnets.html
7 z5 }; b( p+ u# Y! F7 L│ ├── subnet_edit.html
; L! P6 I8 s2 A* `1 Q│ ├── ports.html0 |, D1 @ \' E: d4 m4 y: g
│ ├── port_edit.html, H8 x* }& M7 j& y* |
│
8 v9 T# D' W) ~$ d└── urls.py # 统一注册URL
) K, ^& I6 |* B$ J
" ^0 g- }" `2 O* ] 二、子网管理 Subnets 功能3 q I; h- V N+ k
子网列表6 S* H1 m' k+ o \" K0 b
# views/subnets.py
; B* h) F" a8 |8 l& Ximport requests9 U& H5 V4 F- Z9 g5 Z
from django.shortcuts import render, redirect8 a4 P# m9 c- v# G3 i9 t" J- @. V
from django.contrib import messages( ~' o0 P. p4 A4 A4 r, U1 u
from django.views.decorators.csrf import csrf_exempt% p" m. {/ i* Q( Y2 _# a& d
1 @4 z! M& w" V
@csrf_exempt
/ X" D1 _; Y; r8 V c( V7 Edef subnet_list_view(request, network_id):6 @1 b( Q+ B C& F5 I
"""查看指定网络的所有子网"""
7 Z* q* {. J4 D+ h token = request.session.get("token")- k" F, m4 ]8 ]3 {( i
headers = {"X-Auth-Token": token}
, \# V& ^1 w' s4 D- `# { resp = requests.get(f"{Neutron_Base}/subnets", headers=headers, timeout=10)1 W9 n9 x" y; d6 c4 ?* s4 P$ B
; ^4 Y4 q9 ~" T4 Q3 g! `
if resp.status_code == 200:
5 @( ]' u/ ^# y7 d( Q7 A0 R7 h all_subnets = resp.json().get("subnets", [])) ?/ Z [2 T) s
subnets = [s for s in all_subnets if s["network_id"] == network_id]
# \+ [; [! G/ h' B0 O' w7 b else:' C& X" S- N S6 R' O
messages.error(request, f"无法获取子网列表: {resp.text}")! X1 [3 d/ S1 ]7 s
subnets = []
/ e* Z* ]1 i9 c( q
s" Z, x; V4 q( {6 n' V return render(request, "subnets.html", {"subnets": subnets, "network_id": network_id})
* t+ t$ ?+ {- ~( s1 T d! \/ p: F( ]" t! A( F0 ?' V( I* v
新建子网
( Q' d9 y) Y9 G5 j8 |3 w@csrf_exempt7 F) s4 k8 R* }$ ?6 ?
def subnet_create_view(request, network_id):
5 @2 S+ q$ n k6 W5 ? if request.method == "POST":3 ^- z: p3 z4 G' z: B7 a# V. G" }
token = request.session.get("token")0 e' }: Z; G1 a/ G
headers = {"X-Auth-Token": token, "Content-Type": "application/json"}6 A& |% i6 ]+ P ^5 q
3 l0 V* W5 V D6 C- Z% K1 I# z name = request.POST.get("name")
2 ^) J6 B. K7 j2 q cidr = request.POST.get("cidr")
' L& |! L+ ?, @. [- C/ m! j( q P) N gateway_ip = request.POST.get("gateway_ip")
0 D* M. X3 V9 S6 |% A' O enable_dhcp = request.POST.get("enable_dhcp") == "on"
O" A' B, L( ^" {) i. A1 m) [7 w& l( s
data = {
5 Y% i5 U3 k6 A" F$ H9 J "subnet": {
5 o3 l- G9 X" M6 ?" r" X3 L8 n "network_id": network_id,
" h8 R4 F: J; {. z- @& ~ "ip_version": 4,4 f, M5 [+ Y: ?4 k
"cidr": cidr,
6 j' a* Y1 m7 ~8 ~5 U2 g "gateway_ip": gateway_ip,
6 g. l7 U9 z' t "enable_dhcp": enable_dhcp,+ x0 w/ s! F; K" Y1 A- c. P
"name": name,/ s5 G) L% W9 f3 G+ I4 p
}* p% r- j0 Z) F. I. o l4 q5 i" ~8 K
}
0 u4 A( g2 G& W8 q/ m7 H
$ G% z4 }. m- t5 j resp = requests.post(f"{Neutron_Base}/subnets", headers=headers, json=data, timeout=15)" f. n5 {- W6 @7 n4 h
if resp.status_code in [200, 201]:
5 x- r1 l# x, w* C1 c$ M messages.success(request, "子网创建成功")
: W6 i- J. g2 a* H5 ~ else:
0 W7 s. D+ a9 b. I; [! x: d messages.error(request, f"子网创建失败: {resp.text}")
4 @' W: C, n* t return redirect("subnet_list", network_id=network_id)% f2 J, R; U' X( l
+ X& k( [7 \) u4 X6 B- U! s
return render(request, "subnet_edit.html", {"network_id": network_id})# K: N/ t: X" E0 ]: `& F
7 x$ N, i2 @" S删除子网
8 F! y8 f! S2 i! p8 N9 ydef subnet_delete_view(request, subnet_id, network_id):
8 z. C- _- v; b1 n token = request.session.get("token"): |: j# I' N: }6 F3 k
headers = {"X-Auth-Token": token}" u: U3 n: {7 F# o' u
resp = requests.delete(f"{Neutron_Base}/subnets/{subnet_id}", headers=headers, timeout=10)
' H; Z; o( n5 {) [8 j5 { ?) A- x
if resp.status_code == 204:% M6 ]/ T3 g8 F7 K
messages.success(request, "子网已删除")
" t4 z" g ]' M3 z. l$ D7 q8 a4 ` else:9 |) K/ R& s, d* D: P
messages.error(request, f"删除失败: {resp.text}")$ J! B$ Q T `& P' v' G, D5 f `
return redirect("subnet_list", network_id=network_id)
4 j8 K5 R$ t8 J. X2 O1 I2 ?( D# y# L U$ Q; i3 B) S
三、端口 Ports 管理3 ?- F; E' q) {. ^+ W
端口列表
6 E7 \' R1 B; d/ K6 U' \def port_list_view(request, network_id):
6 F, i# @5 M; c- Y& L t token = request.session.get("token"). ~/ Y7 j* B) T
headers = {"X-Auth-Token": token}
. t7 W: ]1 X, z& l% N6 P, Q resp = requests.get(f"{Neutron_Base}/ports", headers=headers, timeout=10); T! U7 y1 q6 p+ A X! t
/ A& `, I9 z0 }* L if resp.status_code == 200:4 I7 k+ g' m, c+ }; ^' ~ ~" O
ports = [p for p in resp.json().get("ports", []) if p["network_id"] == network_id]
9 Q" S0 k' z6 @* [9 c else:
9 m, n1 F% F( u ports = []
% ~9 n* `# J& O/ P& P3 s messages.error(request, f"无法获取端口: {resp.text}")" m0 D, ^9 ~5 U' C8 r: s) o8 k
5 C" ?6 L& V( G' ]* ^* C return render(request, "ports.html", {"ports": ports, "network_id": network_id})
+ l' b4 G2 w$ ]8 J* J Z a. Q4 u( O+ s
! H: t) g/ X/ i- \新建端口
# h: V1 E1 r# w2 J4 @@csrf_exempt" t! M% i2 }8 V1 H
def port_create_view(request, network_id):( Q+ N8 l- h) ?% W% |
if request.method == "POST":
4 ^. E2 _# U+ J$ r7 D! x token = request.session.get("token")
* ?4 o* E0 P- p0 t! c. l headers = {"X-Auth-Token": token, "Content-Type": "application/json"}
3 e: V( Y f, P/ V: ~/ `. q
: R* P* [ y! c I! f1 d name = request.POST.get("name")
- U2 S: t/ }5 J; g data = {4 L# I, Q, L, }/ h2 b
"port": {
( H. w- p! e3 `5 V" E% u% ` "network_id": network_id,
. J6 ]1 a( ?% T+ l( e6 ~' Z "name": name," e# G% t* ^5 M1 a
"admin_state_up": True
, j' D7 h8 S; ^4 J1 Y }
9 s1 b3 U0 U" I# K, O$ V }
. H# R. w2 p6 l1 j7 d resp = requests.post(f"{Neutron_Base}/ports", headers=headers, json=data, timeout=10)
0 h0 z7 ~8 h: h) h2 _! H; B% d if resp.status_code in [200, 201]:
# K2 P5 W$ w. l# D% W4 y messages.success(request, "端口创建成功")( w& ^2 p& ?( L" R! y7 ^
else:
- `0 T q$ Q1 e messages.error(request, f"创建失败: {resp.text}")
+ k" J5 _/ r7 U, a. @5 C' @5 B9 J return redirect("port_list", network_id=network_id)2 S0 k- W, z r( u1 I1 ?* v
' S+ D9 F7 r, t
return render(request, "port_edit.html", {"network_id": network_id})
, w, y* X4 T. ?* y* {* q7 v- h! R# C7 \
删除端口8 }% ?2 j% ~% P B' H8 o) r1 l9 B! A
def port_delete_view(request, port_id, network_id):; I0 ~& B" B( ~7 P: b6 v# Z
token = request.session.get("token")2 f% @; I% N& }! ?- H5 E9 v5 H
headers = {"X-Auth-Token": token}# U: ^( o8 ~+ _6 @4 L# `
resp = requests.delete(f"{Neutron_Base}/ports/{port_id}", headers=headers, timeout=10)
6 _9 l3 @1 W& p- w/ k Q( T, }# a. x: e
. ^3 ]* u9 ]. h7 @* e$ D P- } if resp.status_code == 204:
& p! N( @, h, f O$ g5 [: Z messages.success(request, "端口已删除")! n6 F! \% y% u" s
else:
! z$ e Q, M+ I messages.error(request, f"删除失败: {resp.text}")
! i9 x9 |& f" O9 C- ` e( e: T! C: x0 K% |; ~
return redirect("port_list", network_id=network_id)* R( P+ V6 t2 S4 Z
$ J! W% E! v5 {. h1 n* |
4️⃣ 更新安全组绑定
: u9 E5 `+ f/ U% E1 e; t@csrf_exempt0 l: W. ? V! e5 f4 `
def port_update_sg_view(request, port_id, network_id):9 Z r7 |8 e) o2 H0 M9 r3 c
if request.method == "POST":1 `1 e+ D( q$ f) a, R' N
token = request.session.get("token")9 F5 X* w" K9 t {# g
headers = {"X-Auth-Token": token, "Content-Type": "application/json"}7 X e5 Q( x( C/ [: N! Z
sg_id = request.POST.get("security_group_id")& C( j4 n1 j' P: L' d8 Y
$ P' D! J* C% A% ]: U4 P data = {"port": {"security_groups": [sg_id]}}4 n, r' V3 K: @ M0 x
resp = requests.put(f"{Neutron_Base}/ports/{port_id}", headers=headers, json=data, timeout=10)" m- e* B' S' d. q
2 e3 X0 N O0 D! W/ o1 A: ~# h: @ if resp.status_code in [200, 202]:
g5 ?' _0 o0 W2 o q" b messages.success(request, "安全组更新成功") ]. L; j0 Z: P# f! I
else:
8 g9 l- d2 U H8 j: { messages.error(request, f"安全组更新失败: {resp.text}")
9 K1 R% J' X+ T/ W
2 s5 M% c0 @% r: B# } return redirect("port_list", network_id=network_id)
0 Q* L. H! `7 W4 C# t
6 R; |9 Z) A6 D f6 T绑定虚机(实例)接口
$ F, G) @& |: m2 e@csrf_exempt
`- L+ M: U- M0 x$ u" Ndef port_bind_instance_view(request, port_id, server_id, network_id):
^, E4 J8 g7 a- R. o token = request.session.get("token")
& ^( O8 L; A4 A d- r7 i$ z8 B headers = {"X-Auth-Token": token, "Content-Type": "application/json"}
' O9 m2 c+ L' l4 J4 [8 i* P( ~3 i
@2 ` Q, Z( M- T, M data = {! b. K% }' K) M% {. \7 f
"port": {) C& h! i) E+ U8 j, z4 y* G
"device_id": server_id, I+ Q6 F. |8 `: g- R' ?
"device_owner": "compute:nova"! v ]0 ?4 R8 e7 R* ^3 c
}4 {6 d+ l" F+ }: O. o
}/ e" K6 ^. [+ { z* w; x9 b3 p
3 T+ T: q2 i% ^5 i0 a
resp = requests.put(f"{Neutron_Base}/ports/{port_id}", headers=headers, json=data, timeout=10)
- o5 J6 v5 X5 L; l% s if resp.status_code in [200, 202]:" ]8 [4 s# c* c; |, ~
messages.success(request, "端口成功绑定到虚机") _& t% T- x( Q3 T" h" a
else:
) s% b4 x @% B. U* {6 k messages.error(request, f"绑定失败: {resp.text}")$ P v- D* _! f0 n' P
return redirect("port_list", network_id=network_id)
# N4 L4 r" h7 X. S& _( u# O
5 N M/ P/ r2 D( N( B2 D2 e 四、URL 路由示例
2 `% I) c8 Q V8 U# A1 Q# urls.py
3 \" @/ V, E, U/ t! Q( v0 S+ ufrom django.urls import path
- |$ {+ f: q. m: q; b3 A pfrom .views import networks, subnets, ports
0 l( W% d2 l! V" r' t9 @4 ~* g' {8 Z; A0 A! e
urlpatterns = [& x2 X; {$ a( j2 z
path("networks/", networks.network_list_view, name="networks"),
! M) A+ L% V" B+ \* D' L path("networks/edit/<str:network_id>/", networks.edit_network_view, name="edit_network"),1 m% j8 e/ k1 T7 s$ T! f* U
/ ?# F8 K: g5 C& w; g path("networks/<str:network_id>/subnets/", subnets.subnet_list_view, name="subnet_list"),
6 c# X+ s+ h, N; K; \8 w6 v path("networks/<str:network_id>/subnets/create/", subnets.subnet_create_view, name="subnet_create"),
8 L1 h/ g5 R2 V# Q% z7 ?6 f path("networks/<str:network_id>/subnets/delete/<str:subnet_id>/", subnets.subnet_delete_view, name="subnet_delete"), L! L; X9 y/ H8 q. s& U
) S/ ?# b4 V( ~# c
path("networks/<str:network_id>/ports/", ports.port_list_view, name="port_list"),
5 ^, I5 }6 u2 c/ @) M" T path("networks/<str:network_id>/ports/create/", ports.port_create_view, name="port_create"),
+ D1 g2 g+ v: o path("networks/<str:network_id>/ports/delete/<str:port_id>/", ports.port_delete_view, name="port_delete"),
4 G4 ^) c* W$ G+ c6 I path("networks/<str:network_id>/ports/update_sg/<str:port_id>/", ports.port_update_sg_view, name="port_update_sg"),
1 v, `) D9 S9 ?6 f- @) \8 m9 \ path("networks/<str:network_id>/ports/bind/<str:port_id>/<str:server_id>/", ports.port_bind_instance_view, name="port_bind_instance"),
. e: y; o- G1 `' d) M- w" O]
( E) m. [. h5 b3 R; |
5 _# w0 z; Z+ W! F, _- z( k6 d& z8 E M |
|