bulk_import.py 8.9 KB

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