1 from django.conf import settings

2 from django.contrib.auth.mixins import LoginRequiredMixin

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

4 from django.db import models

5 from django.http import (

6 HttpResponse,

7 HttpResponseBadRequest,

8 HttpResponseRedirect,

9 QueryDict,

10)

11 from django.shortcuts import get_object_or_404, render

12 from django.urls import reverse_lazy

13 from django.utils.timezone import now

14 from django.views.generic import (

15 CreateView,

16 DeleteView,

17 DetailView,

18 ListView,

19 UpdateView,

20)

21

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

23 from meshadmin.server.networks.mixins import NetworkPermissionMixin

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

25

26

27 class 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

43 class 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

107 class 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

130 class 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

145 class 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

156 class 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

185 class 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

212 class 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 )