Coverage for src/meshadmin/server/networks/views/network.py: 82%

135 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-10 16:08 +0200

1from django.conf import settings 

2from django.contrib.auth.mixins import LoginRequiredMixin 

3from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator 

4from django.db import models 

5from django.http import ( 

6 HttpResponse, 

7 HttpResponseBadRequest, 

8 HttpResponseRedirect, 

9 QueryDict, 

10) 

11from django.shortcuts import get_object_or_404, render 

12from django.urls import reverse_lazy 

13from django.utils.timezone import now 

14from django.views.generic import ( 

15 CreateView, 

16 DeleteView, 

17 DetailView, 

18 ListView, 

19 UpdateView, 

20) 

21 

22from meshadmin.server.networks.forms import NetworkForm, NetworkMembershipForm 

23from meshadmin.server.networks.mixins import NetworkPermissionMixin 

24from meshadmin.server.networks.models import Network, NetworkMembership 

25 

26 

27class NetworkListView(LoginRequiredMixin, ListView): 

28 model = Network 

29 template_name = "networks/network/list.html" 

30 

31 def get_queryset(self): 

32 if self.request.user.is_superuser: 

33 return Network.objects.all() 

34 return Network.objects.filter( 

35 memberships__user=self.request.user, 

36 memberships__role__in=[ 

37 NetworkMembership.Role.ADMIN, 

38 NetworkMembership.Role.MEMBER, 

39 ], 

40 ) 

41 

42 

43class NetworkDetailView(LoginRequiredMixin, NetworkPermissionMixin, DetailView): 

44 model = Network 

45 template_name = "networks/network/detail.html" 

46 context_object_name = "network" 

47 

48 def get_context_data(self, **kwargs): 

49 context = super().get_context_data(**kwargs) 

50 context["fields"] = [ 

51 { 

52 "name": field.name, 

53 "verbose_name": field.verbose_name, 

54 "value": getattr(self.object, field.name), 

55 } 

56 for field in self.model._meta.fields 

57 ] 

58 

59 hosts = self.object.host_set.all() 

60 search = self.request.GET.get("search", "") 

61 if search: 

62 hosts = hosts.filter(name__icontains=search) 

63 

64 if self.request.GET.get("stale_config"): 

65 from datetime import timedelta 

66 

67 from django.utils import timezone 

68 

69 stale_threshold = timezone.now() - timedelta(hours=24) 

70 hosts = hosts.filter( 

71 models.Q(last_config_refresh__lt=stale_threshold) 

72 | models.Q(last_config_refresh__isnull=True) 

73 ) 

74 

75 per_page = int(self.request.GET.get("per_page", settings.PAGINATION_PER_PAGE)) 

76 paginator = Paginator(hosts, per_page) 

77 page = self.request.GET.get("hosts_page", 1) 

78 

79 try: 

80 hosts = paginator.page(page) 

81 except (PageNotAnInteger, EmptyPage): 

82 hosts = paginator.page(1) 

83 

84 context.update( 

85 { 

86 "hosts": hosts, 

87 "paginator": paginator, 

88 "page_obj": hosts, 

89 "per_page": per_page, 

90 "per_page_options": [25, 50, 100], 

91 "templates": self.object.template_set.all(), 

92 "groups": self.object.group_set.all(), 

93 "cas": self.object.ca_set.all(), 

94 "rollouts": self.object.configrollout_set.all(), 

95 "now": now(), 

96 "memberships": self.object.memberships.select_related("user").all(), 

97 } 

98 ) 

99 return context 

100 

101 def get_template_names(self): 

102 if self.request.htmx: 

103 return ["networks/network/_hosts_table.html"] 

104 return [self.template_name] 

105 

106 

107class NetworkCreateView(LoginRequiredMixin, CreateView): 

108 model = Network 

109 form_class = NetworkForm 

110 template_name = "networks/shared/form.html" 

111 

112 def get_form_kwargs(self): 

113 kwargs = super().get_form_kwargs() 

114 kwargs["request"] = self.request 

115 return kwargs 

116 

117 def get_success_url(self): 

118 return ( 

119 reverse_lazy("networks:network-detail", kwargs={"pk": self.object.pk}) 

120 + "#templates-section" 

121 ) 

122 

123 def get_context_data(self, **kwargs): 

124 context = super().get_context_data(**kwargs) 

125 context["action"] = "Create" 

126 context["model_name"] = self.model._meta.verbose_name 

127 return context 

128 

129 

130class NetworkUpdateView(LoginRequiredMixin, NetworkPermissionMixin, UpdateView): 

131 model = Network 

132 form_class = NetworkForm 

133 template_name = "networks/shared/form.html" 

134 

135 def get_success_url(self): 

136 return reverse_lazy("networks:network-detail", kwargs={"pk": self.object.pk}) 

137 

138 def get_context_data(self, **kwargs): 

139 context = super().get_context_data(**kwargs) 

140 context["action"] = "Update" 

141 context["model_name"] = self.model._meta.verbose_name 

142 return context 

143 

144 

145class NetworkDeleteView(LoginRequiredMixin, NetworkPermissionMixin, DeleteView): 

146 model = Network 

147 template_name = "networks/shared/delete.html" 

148 success_url = reverse_lazy("networks:network-list") 

149 

150 def get_context_data(self, **kwargs): 

151 context = super().get_context_data(**kwargs) 

152 context["model_name"] = self.model._meta.verbose_name 

153 return context 

154 

155 

156class NetworkMemberAddView(LoginRequiredMixin, NetworkPermissionMixin, CreateView): 

157 model = NetworkMembership 

158 form_class = NetworkMembershipForm 

159 template_name = "networks/shared/form.html" 

160 

161 def get_network(self): 

162 return get_object_or_404(Network, id=self.kwargs.get("network_id")) 

163 

164 def get_form_kwargs(self): 

165 kwargs = super().get_form_kwargs() 

166 kwargs["network"] = self.get_network() 

167 return kwargs 

168 

169 def get_success_url(self): 

170 return ( 

171 reverse_lazy( 

172 "networks:network-detail", kwargs={"pk": self.kwargs["network_id"]} 

173 ) 

174 + "#members-section" 

175 ) 

176 

177 def get_context_data(self, **kwargs): 

178 context = super().get_context_data(**kwargs) 

179 context["action"] = "Add Member" 

180 context["model_name"] = self.model._meta.verbose_name 

181 context["network_id"] = self.kwargs["network_id"] 

182 return context 

183 

184 

185class NetworkMemberEditView(LoginRequiredMixin, NetworkPermissionMixin, UpdateView): 

186 model = NetworkMembership 

187 form_class = NetworkMembershipForm 

188 template_name = "networks/shared/form.html" 

189 

190 def get_network(self): 

191 return self.get_object().network 

192 

193 def form_valid(self, form): 

194 self.object = form.save() 

195 if self.request.htmx: 

196 context = self.get_context_data() 

197 return render(self.request, "networks/network/_member_row.html", context) 

198 return super().form_valid(form) 

199 

200 def put(self, request, *args, **kwargs): 

201 membership = self.get_object() 

202 data = QueryDict(request.body) 

203 role = data.get("role") 

204 if role in [NetworkMembership.Role.ADMIN, NetworkMembership.Role.MEMBER]: 

205 membership.role = role 

206 membership.save() 

207 context = {"membership": membership, "network": membership.network} 

208 return render(request, "networks/network/_member_row.html", context) 

209 return HttpResponseBadRequest() 

210 

211 

212class NetworkMemberDeleteView(LoginRequiredMixin, NetworkPermissionMixin, DeleteView): 

213 model = NetworkMembership 

214 template_name = "networks/shared/delete.html" 

215 

216 def get_network(self): 

217 return self.get_object().network 

218 

219 def delete(self, request, *args, **kwargs): 

220 self.object = self.get_object() 

221 self.object.delete() 

222 if request.htmx: 

223 return HttpResponse("") 

224 return HttpResponseRedirect(self.get_success_url()) 

225 

226 def get_success_url(self): 

227 return ( 

228 reverse_lazy( 

229 "networks:network-detail", kwargs={"pk": self.get_object().network.id} 

230 ) 

231 + "#members-section" 

232 )