bulk_import.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. from django.utils.safestring import mark_safe
  2. from django.utils.translation import gettext_lazy as _
  3. from dcim.choices import InterfaceModeChoices
  4. from dcim.forms.mixins import ScopedImportForm
  5. from dcim.models import Device, DeviceRole, Platform, Site
  6. from extras.models import ConfigTemplate
  7. from ipam.choices import VLANQinQRoleChoices
  8. from ipam.models import VLAN, VRF, VLANGroup
  9. from netbox.forms import NetBoxModelImportForm
  10. from netbox.forms import OrganizationalModelImportForm, OwnerCSVMixin, PrimaryModelImportForm
  11. from tenancy.models import Tenant
  12. from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, CSVModelMultipleChoiceField
  13. from virtualization.choices import *
  14. from virtualization.models import *
  15. __all__ = (
  16. 'ClusterImportForm',
  17. 'ClusterGroupImportForm',
  18. 'ClusterTypeImportForm',
  19. 'VirtualDiskImportForm',
  20. 'VirtualMachineImportForm',
  21. 'VMInterfaceImportForm',
  22. )
  23. class ClusterTypeImportForm(OrganizationalModelImportForm):
  24. class Meta:
  25. model = ClusterType
  26. fields = ('name', 'slug', 'description', 'owner', 'tags')
  27. class ClusterGroupImportForm(OrganizationalModelImportForm):
  28. class Meta:
  29. model = ClusterGroup
  30. fields = ('name', 'slug', 'description', 'owner', 'tags')
  31. class ClusterImportForm(ScopedImportForm, PrimaryModelImportForm):
  32. type = CSVModelChoiceField(
  33. label=_('Type'),
  34. queryset=ClusterType.objects.all(),
  35. to_field_name='name',
  36. help_text=_('Type of cluster')
  37. )
  38. group = CSVModelChoiceField(
  39. label=_('Group'),
  40. queryset=ClusterGroup.objects.all(),
  41. to_field_name='name',
  42. required=False,
  43. help_text=_('Assigned cluster group')
  44. )
  45. status = CSVChoiceField(
  46. label=_('Status'),
  47. choices=ClusterStatusChoices,
  48. help_text=_('Operational status')
  49. )
  50. site = CSVModelChoiceField(
  51. label=_('Site'),
  52. queryset=Site.objects.all(),
  53. to_field_name='name',
  54. required=False,
  55. help_text=_('Assigned site')
  56. )
  57. tenant = CSVModelChoiceField(
  58. label=_('Tenant'),
  59. queryset=Tenant.objects.all(),
  60. to_field_name='name',
  61. required=False,
  62. help_text=_('Assigned tenant')
  63. )
  64. class Meta:
  65. model = Cluster
  66. fields = (
  67. 'name', 'type', 'group', 'status', 'scope_type', 'scope_id', 'tenant', 'description', 'owner', 'comments',
  68. 'tags',
  69. )
  70. labels = {
  71. 'scope_id': _('Scope ID'),
  72. }
  73. class VirtualMachineImportForm(PrimaryModelImportForm):
  74. status = CSVChoiceField(
  75. label=_('Status'),
  76. choices=VirtualMachineStatusChoices,
  77. help_text=_('Operational status')
  78. )
  79. site = CSVModelChoiceField(
  80. label=_('Site'),
  81. queryset=Site.objects.all(),
  82. to_field_name='name',
  83. required=False,
  84. help_text=_('Assigned site')
  85. )
  86. cluster = CSVModelChoiceField(
  87. label=_('Cluster'),
  88. queryset=Cluster.objects.all(),
  89. to_field_name='name',
  90. required=False,
  91. help_text=_('Assigned cluster')
  92. )
  93. device = CSVModelChoiceField(
  94. label=_('Device'),
  95. queryset=Device.objects.all(),
  96. to_field_name='name',
  97. required=False,
  98. help_text=_('Assigned device within cluster')
  99. )
  100. role = CSVModelChoiceField(
  101. label=_('Role'),
  102. queryset=DeviceRole.objects.filter(
  103. vm_role=True
  104. ),
  105. required=False,
  106. to_field_name='name',
  107. help_text=_('Functional role')
  108. )
  109. tenant = CSVModelChoiceField(
  110. label=_('Tenant'),
  111. queryset=Tenant.objects.all(),
  112. required=False,
  113. to_field_name='name',
  114. help_text=_('Assigned tenant')
  115. )
  116. platform = CSVModelChoiceField(
  117. label=_('Platform'),
  118. queryset=Platform.objects.all(),
  119. required=False,
  120. to_field_name='name',
  121. help_text=_('Assigned platform')
  122. )
  123. config_template = CSVModelChoiceField(
  124. queryset=ConfigTemplate.objects.all(),
  125. to_field_name='name',
  126. required=False,
  127. label=_('Config template'),
  128. help_text=_('Config template')
  129. )
  130. class Meta:
  131. model = VirtualMachine
  132. fields = (
  133. 'name', 'status', 'role', 'site', 'cluster', 'device', 'tenant', 'platform', 'vcpus', 'memory', 'disk',
  134. 'description', 'serial', 'config_template', 'comments', 'owner', 'tags',
  135. )
  136. class VMInterfaceImportForm(OwnerCSVMixin, NetBoxModelImportForm):
  137. virtual_machine = CSVModelChoiceField(
  138. label=_('Virtual machine'),
  139. queryset=VirtualMachine.objects.all(),
  140. to_field_name='name'
  141. )
  142. parent = CSVModelChoiceField(
  143. label=_('Parent'),
  144. queryset=VMInterface.objects.all(),
  145. required=False,
  146. to_field_name='name',
  147. help_text=_('Parent interface'),
  148. )
  149. bridge = CSVModelChoiceField(
  150. label=_('Bridge'),
  151. queryset=VMInterface.objects.all(),
  152. required=False,
  153. to_field_name='name',
  154. help_text=_('Bridged interface'),
  155. )
  156. mode = CSVChoiceField(
  157. label=_('Mode'),
  158. choices=InterfaceModeChoices,
  159. required=False,
  160. help_text=_('IEEE 802.1Q operational mode (for L2 interfaces)'),
  161. )
  162. vlan_group = CSVModelChoiceField(
  163. label=_('VLAN group'),
  164. queryset=VLANGroup.objects.all(),
  165. required=False,
  166. to_field_name='name',
  167. help_text=_('Filter VLANs available for assignment by group'),
  168. )
  169. untagged_vlan = CSVModelChoiceField(
  170. label=_('Untagged VLAN'),
  171. queryset=VLAN.objects.all(),
  172. required=False,
  173. to_field_name='vid',
  174. help_text=_('Assigned untagged VLAN ID (filtered by VLAN group)'),
  175. )
  176. tagged_vlans = CSVModelMultipleChoiceField(
  177. label=_('Tagged VLANs'),
  178. queryset=VLAN.objects.all(),
  179. required=False,
  180. to_field_name='vid',
  181. help_text=mark_safe(
  182. _(
  183. 'Assigned tagged VLAN IDs separated by commas, encased with double quotes '
  184. '(filtered by VLAN group). Example:'
  185. )
  186. + ' <code>"100,200,300"</code>'
  187. ),
  188. )
  189. qinq_svlan = CSVModelChoiceField(
  190. label=_('Q-in-Q Service VLAN'),
  191. queryset=VLAN.objects.filter(qinq_role=VLANQinQRoleChoices.ROLE_SERVICE),
  192. required=False,
  193. to_field_name='vid',
  194. help_text=_('Assigned Q-in-Q Service VLAN ID (filtered by VLAN group)'),
  195. )
  196. vrf = CSVModelChoiceField(
  197. label=_('VRF'),
  198. queryset=VRF.objects.all(),
  199. required=False,
  200. to_field_name='rd',
  201. help_text=_('Assigned VRF')
  202. )
  203. class Meta:
  204. model = VMInterface
  205. fields = (
  206. 'virtual_machine', 'name', 'parent', 'bridge', 'enabled', 'mtu', 'description', 'mode', 'vlan_group',
  207. 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vrf', 'owner', 'tags',
  208. )
  209. def __init__(self, data=None, *args, **kwargs):
  210. super().__init__(data, *args, **kwargs)
  211. if data:
  212. # Limit interface choices for parent & bridge interfaces to the assigned VM
  213. if virtual_machine := data.get('virtual_machine'):
  214. params = {
  215. f"virtual_machine__{self.fields['virtual_machine'].to_field_name}": virtual_machine
  216. }
  217. self.fields['parent'].queryset = self.fields['parent'].queryset.filter(**params)
  218. self.fields['bridge'].queryset = self.fields['bridge'].queryset.filter(**params)
  219. # Limit choices for VLANs to the assigned VLAN group
  220. if vlan_group := data.get('vlan_group'):
  221. params = {f"group__{self.fields['vlan_group'].to_field_name}": vlan_group}
  222. self.fields['untagged_vlan'].queryset = self.fields['untagged_vlan'].queryset.filter(**params)
  223. self.fields['tagged_vlans'].queryset = self.fields['tagged_vlans'].queryset.filter(**params)
  224. self.fields['qinq_svlan'].queryset = self.fields['qinq_svlan'].queryset.filter(**params)
  225. def clean_enabled(self):
  226. # Make sure enabled is True when it's not included in the uploaded data
  227. if 'enabled' not in self.data:
  228. return True
  229. else:
  230. return self.cleaned_data['enabled']
  231. class VirtualDiskImportForm(OwnerCSVMixin, NetBoxModelImportForm):
  232. virtual_machine = CSVModelChoiceField(
  233. label=_('Virtual machine'),
  234. queryset=VirtualMachine.objects.all(),
  235. to_field_name='name'
  236. )
  237. class Meta:
  238. model = VirtualDisk
  239. fields = (
  240. 'virtual_machine', 'name', 'size', 'description', 'owner', 'tags'
  241. )