bulk_import.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. from django.core.exceptions import ValidationError
  2. from django.utils.translation import gettext_lazy as _
  3. from dcim.models import Device, Interface
  4. from ipam.models import VLAN, VRF, IPAddress
  5. from netbox.forms import NetBoxModelImportForm, OrganizationalModelImportForm, PrimaryModelImportForm
  6. from tenancy.models import Tenant
  7. from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, CSVModelMultipleChoiceField
  8. from virtualization.models import VirtualMachine, VMInterface
  9. from vpn.choices import *
  10. from vpn.models import *
  11. __all__ = (
  12. 'IKEPolicyImportForm',
  13. 'IKEProposalImportForm',
  14. 'IPSecPolicyImportForm',
  15. 'IPSecProfileImportForm',
  16. 'IPSecProposalImportForm',
  17. 'L2VPNImportForm',
  18. 'L2VPNTerminationImportForm',
  19. 'TunnelGroupImportForm',
  20. 'TunnelImportForm',
  21. 'TunnelTerminationImportForm',
  22. )
  23. class TunnelGroupImportForm(OrganizationalModelImportForm):
  24. class Meta:
  25. model = TunnelGroup
  26. fields = ('name', 'slug', 'description', 'owner', 'comments', 'tags')
  27. class TunnelImportForm(PrimaryModelImportForm):
  28. status = CSVChoiceField(
  29. label=_('Status'),
  30. choices=TunnelStatusChoices,
  31. help_text=_('Operational status')
  32. )
  33. group = CSVModelChoiceField(
  34. label=_('Tunnel group'),
  35. queryset=TunnelGroup.objects.all(),
  36. required=False,
  37. to_field_name='name'
  38. )
  39. encapsulation = CSVChoiceField(
  40. label=_('Encapsulation'),
  41. choices=TunnelEncapsulationChoices,
  42. help_text=_('Tunnel encapsulation')
  43. )
  44. ipsec_profile = CSVModelChoiceField(
  45. label=_('IPSec profile'),
  46. queryset=IPSecProfile.objects.all(),
  47. required=False,
  48. to_field_name='name'
  49. )
  50. tenant = CSVModelChoiceField(
  51. label=_('Tenant'),
  52. queryset=Tenant.objects.all(),
  53. required=False,
  54. to_field_name='name',
  55. help_text=_('Assigned tenant')
  56. )
  57. class Meta:
  58. model = Tunnel
  59. fields = (
  60. 'name', 'status', 'group', 'encapsulation', 'ipsec_profile', 'tenant', 'tunnel_id', 'description',
  61. 'owner', 'comments', 'tags',
  62. )
  63. class TunnelTerminationImportForm(NetBoxModelImportForm):
  64. tunnel = CSVModelChoiceField(
  65. label=_('Tunnel'),
  66. queryset=Tunnel.objects.all(),
  67. to_field_name='name'
  68. )
  69. role = CSVChoiceField(
  70. label=_('Role'),
  71. choices=TunnelTerminationRoleChoices,
  72. help_text=_('Operational role')
  73. )
  74. device = CSVModelChoiceField(
  75. label=_('Device'),
  76. queryset=Device.objects.all(),
  77. required=False,
  78. to_field_name='name',
  79. help_text=_('Parent device of assigned interface')
  80. )
  81. virtual_machine = CSVModelChoiceField(
  82. label=_('Virtual machine'),
  83. queryset=VirtualMachine.objects.all(),
  84. required=False,
  85. to_field_name='name',
  86. help_text=_('Parent VM of assigned interface')
  87. )
  88. termination = CSVModelChoiceField(
  89. label=_('Termination'),
  90. queryset=Interface.objects.none(), # Can also refer to VMInterface
  91. required=False,
  92. to_field_name='name',
  93. help_text=_('Device or virtual machine interface')
  94. )
  95. outside_ip = CSVModelChoiceField(
  96. label=_('Outside IP'),
  97. queryset=IPAddress.objects.all(),
  98. required=False,
  99. to_field_name='address'
  100. )
  101. class Meta:
  102. model = TunnelTermination
  103. fields = (
  104. 'tunnel', 'role', 'outside_ip', 'tags',
  105. )
  106. def __init__(self, data=None, *args, **kwargs):
  107. super().__init__(data, *args, **kwargs)
  108. if data:
  109. # Limit termination queryset by assigned device/VM
  110. if data.get('device'):
  111. self.fields['termination'].queryset = Interface.objects.filter(
  112. **{f"device__{self.fields['device'].to_field_name}": data['device']}
  113. )
  114. elif data.get('virtual_machine'):
  115. self.fields['termination'].queryset = VMInterface.objects.filter(
  116. **{f"virtual_machine__{self.fields['virtual_machine'].to_field_name}": data['virtual_machine']}
  117. )
  118. def save(self, *args, **kwargs):
  119. # Assign termination object
  120. if self.cleaned_data.get('termination'):
  121. self.instance.termination = self.cleaned_data['termination']
  122. return super().save(*args, **kwargs)
  123. class IKEProposalImportForm(PrimaryModelImportForm):
  124. authentication_method = CSVChoiceField(
  125. label=_('Authentication method'),
  126. choices=AuthenticationMethodChoices
  127. )
  128. encryption_algorithm = CSVChoiceField(
  129. label=_('Encryption algorithm'),
  130. choices=EncryptionAlgorithmChoices
  131. )
  132. authentication_algorithm = CSVChoiceField(
  133. label=_('Authentication algorithm'),
  134. choices=AuthenticationAlgorithmChoices,
  135. required=False
  136. )
  137. group = CSVChoiceField(
  138. label=_('Group'),
  139. choices=DHGroupChoices
  140. )
  141. class Meta:
  142. model = IKEProposal
  143. fields = (
  144. 'name', 'description', 'authentication_method', 'encryption_algorithm', 'authentication_algorithm',
  145. 'group', 'sa_lifetime', 'owner', 'comments', 'tags',
  146. )
  147. class IKEPolicyImportForm(PrimaryModelImportForm):
  148. version = CSVChoiceField(
  149. label=_('Version'),
  150. choices=IKEVersionChoices
  151. )
  152. mode = CSVChoiceField(
  153. label=_('Mode'),
  154. choices=IKEModeChoices,
  155. required=False
  156. )
  157. proposals = CSVModelMultipleChoiceField(
  158. queryset=IKEProposal.objects.all(),
  159. to_field_name='name',
  160. help_text=_('IKE proposal(s)'),
  161. )
  162. class Meta:
  163. model = IKEPolicy
  164. fields = (
  165. 'name', 'description', 'version', 'mode', 'proposals', 'preshared_key', 'owner', 'comments', 'tags',
  166. )
  167. class IPSecProposalImportForm(PrimaryModelImportForm):
  168. encryption_algorithm = CSVChoiceField(
  169. label=_('Encryption algorithm'),
  170. choices=EncryptionAlgorithmChoices,
  171. required=False
  172. )
  173. authentication_algorithm = CSVChoiceField(
  174. label=_('Authentication algorithm'),
  175. choices=AuthenticationAlgorithmChoices,
  176. required=False
  177. )
  178. class Meta:
  179. model = IPSecProposal
  180. fields = (
  181. 'name', 'description', 'encryption_algorithm', 'authentication_algorithm', 'sa_lifetime_seconds',
  182. 'sa_lifetime_data', 'owner', 'comments', 'tags',
  183. )
  184. class IPSecPolicyImportForm(PrimaryModelImportForm):
  185. pfs_group = CSVChoiceField(
  186. label=_('Diffie-Hellman group for Perfect Forward Secrecy'),
  187. choices=DHGroupChoices,
  188. required=False
  189. )
  190. proposals = CSVModelMultipleChoiceField(
  191. queryset=IPSecProposal.objects.all(),
  192. to_field_name='name',
  193. help_text=_('IPSec proposal(s)'),
  194. )
  195. class Meta:
  196. model = IPSecPolicy
  197. fields = (
  198. 'name', 'description', 'proposals', 'pfs_group', 'owner', 'comments', 'tags',
  199. )
  200. class IPSecProfileImportForm(PrimaryModelImportForm):
  201. mode = CSVChoiceField(
  202. label=_('Mode'),
  203. choices=IPSecModeChoices,
  204. help_text=_('IPSec protocol')
  205. )
  206. ike_policy = CSVModelChoiceField(
  207. label=_('IKE policy'),
  208. queryset=IKEPolicy.objects.all(),
  209. to_field_name='name'
  210. )
  211. ipsec_policy = CSVModelChoiceField(
  212. label=_('IPSec policy'),
  213. queryset=IPSecPolicy.objects.all(),
  214. to_field_name='name'
  215. )
  216. class Meta:
  217. model = IPSecProfile
  218. fields = (
  219. 'name', 'mode', 'ike_policy', 'ipsec_policy', 'description', 'owner', 'comments', 'tags',
  220. )
  221. class L2VPNImportForm(PrimaryModelImportForm):
  222. tenant = CSVModelChoiceField(
  223. label=_('Tenant'),
  224. queryset=Tenant.objects.all(),
  225. required=False,
  226. to_field_name='name',
  227. )
  228. status = CSVChoiceField(
  229. label=_('Status'),
  230. choices=L2VPNStatusChoices,
  231. help_text=_('Operational status')
  232. )
  233. type = CSVChoiceField(
  234. label=_('Type'),
  235. choices=L2VPNTypeChoices,
  236. help_text=_('L2VPN type')
  237. )
  238. class Meta:
  239. model = L2VPN
  240. fields = (
  241. 'identifier', 'name', 'slug', 'tenant', 'type', 'description', 'owner', 'comments', 'tags',
  242. )
  243. class L2VPNTerminationImportForm(NetBoxModelImportForm):
  244. l2vpn = CSVModelChoiceField(
  245. queryset=L2VPN.objects.all(),
  246. required=True,
  247. to_field_name='name',
  248. label=_('L2VPN'),
  249. )
  250. device = CSVModelChoiceField(
  251. label=_('Device'),
  252. queryset=Device.objects.all(),
  253. required=False,
  254. to_field_name='name',
  255. help_text=_('Parent device (for interface or IP address)')
  256. )
  257. virtual_machine = CSVModelChoiceField(
  258. label=_('Virtual machine'),
  259. queryset=VirtualMachine.objects.all(),
  260. required=False,
  261. to_field_name='name',
  262. help_text=_('Parent virtual machine (for interface or IP address)')
  263. )
  264. interface = CSVModelChoiceField(
  265. label=_('Interface'),
  266. queryset=Interface.objects.none(), # Can also refer to VMInterface
  267. required=False,
  268. to_field_name='name',
  269. help_text=_('Assigned interface (device or VM)')
  270. )
  271. vlan = CSVModelChoiceField(
  272. label=_('VLAN'),
  273. queryset=VLAN.objects.all(),
  274. required=False,
  275. to_field_name='name',
  276. help_text=_('Assigned VLAN')
  277. )
  278. ipaddress = CSVModelChoiceField(
  279. label=_('IP address'),
  280. queryset=IPAddress.objects.all(),
  281. required=False,
  282. to_field_name='address',
  283. help_text=_('Assigned IP address, with mask (e.g. the far-end PE of a pseudowire)')
  284. )
  285. ipaddress_vrf = CSVModelChoiceField(
  286. label=_('IP address VRF'),
  287. queryset=VRF.objects.all(),
  288. required=False,
  289. to_field_name='name',
  290. help_text=_('VRF of the assigned IP address')
  291. )
  292. class Meta:
  293. model = L2VPNTermination
  294. fields = ('l2vpn', 'device', 'virtual_machine', 'interface', 'vlan', 'ipaddress', 'ipaddress_vrf', 'tags')
  295. def __init__(self, data=None, *args, **kwargs):
  296. super().__init__(data, *args, **kwargs)
  297. if data:
  298. # Limit interface and IP address querysets by device or VM
  299. if data.get('device'):
  300. self.fields['interface'].queryset = Interface.objects.filter(
  301. **{f"device__{self.fields['device'].to_field_name}": data['device']}
  302. )
  303. self.fields['ipaddress'].queryset = IPAddress.objects.filter(
  304. **{f"interface__device__{self.fields['device'].to_field_name}": data['device']}
  305. )
  306. elif data.get('virtual_machine'):
  307. self.fields['interface'].queryset = VMInterface.objects.filter(
  308. **{f"virtual_machine__{self.fields['virtual_machine'].to_field_name}": data['virtual_machine']}
  309. )
  310. self.fields['ipaddress'].queryset = IPAddress.objects.filter(
  311. **{
  312. f"vminterface__virtual_machine__{self.fields['virtual_machine'].to_field_name}":
  313. data['virtual_machine']
  314. }
  315. )
  316. # Limit IP address queryset by VRF
  317. if data.get('ipaddress_vrf'):
  318. self.fields['ipaddress'].queryset = self.fields['ipaddress'].queryset.filter(
  319. **{f"vrf__{self.fields['ipaddress_vrf'].to_field_name}": data['ipaddress_vrf']}
  320. )
  321. def clean(self):
  322. super().clean()
  323. if self.cleaned_data.get('device') and self.cleaned_data.get('virtual_machine'):
  324. raise ValidationError(_('Cannot import device and VM interface terminations simultaneously.'))
  325. assigned_objects = [
  326. obj for obj in (
  327. self.cleaned_data.get('interface'),
  328. self.cleaned_data.get('vlan'),
  329. self.cleaned_data.get('ipaddress'),
  330. ) if obj
  331. ]
  332. if not self.instance.pk and not assigned_objects:
  333. raise ValidationError(_('Each termination must specify an interface, a VLAN, or an IP address.'))
  334. if len(assigned_objects) > 1:
  335. raise ValidationError(_('Cannot assign more than one of an interface, a VLAN, and an IP address.'))
  336. # if this is an update we might not have an assigned object in the form data
  337. if assigned_objects:
  338. self.instance.assigned_object = assigned_objects[0]