test_api.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. from django.urls import reverse
  2. from rest_framework import status
  3. from dcim.choices import InterfaceModeChoices
  4. from dcim.models import Site
  5. from extras.models import ConfigTemplate
  6. from ipam.models import VLAN, VRF
  7. from utilities.testing import APITestCase, APIViewTestCases, create_test_device, create_test_virtualmachine
  8. from virtualization.choices import *
  9. from virtualization.models import *
  10. class AppTest(APITestCase):
  11. def test_root(self):
  12. url = reverse('virtualization-api:api-root')
  13. response = self.client.get('{}?format=api'.format(url), **self.header)
  14. self.assertEqual(response.status_code, 200)
  15. class ClusterTypeTest(APIViewTestCases.APIViewTestCase):
  16. model = ClusterType
  17. brief_fields = ['cluster_count', 'display', 'id', 'name', 'slug', 'url']
  18. create_data = [
  19. {
  20. 'name': 'Cluster Type 4',
  21. 'slug': 'cluster-type-4',
  22. },
  23. {
  24. 'name': 'Cluster Type 5',
  25. 'slug': 'cluster-type-5',
  26. },
  27. {
  28. 'name': 'Cluster Type 6',
  29. 'slug': 'cluster-type-6',
  30. },
  31. ]
  32. bulk_update_data = {
  33. 'description': 'New description',
  34. }
  35. @classmethod
  36. def setUpTestData(cls):
  37. cluster_types = (
  38. ClusterType(name='Cluster Type 1', slug='cluster-type-1'),
  39. ClusterType(name='Cluster Type 2', slug='cluster-type-2'),
  40. ClusterType(name='Cluster Type 3', slug='cluster-type-3'),
  41. )
  42. ClusterType.objects.bulk_create(cluster_types)
  43. class ClusterGroupTest(APIViewTestCases.APIViewTestCase):
  44. model = ClusterGroup
  45. brief_fields = ['cluster_count', 'display', 'id', 'name', 'slug', 'url']
  46. create_data = [
  47. {
  48. 'name': 'Cluster Group 4',
  49. 'slug': 'cluster-type-4',
  50. },
  51. {
  52. 'name': 'Cluster Group 5',
  53. 'slug': 'cluster-type-5',
  54. },
  55. {
  56. 'name': 'Cluster Group 6',
  57. 'slug': 'cluster-type-6',
  58. },
  59. ]
  60. bulk_update_data = {
  61. 'description': 'New description',
  62. }
  63. @classmethod
  64. def setUpTestData(cls):
  65. cluster_Groups = (
  66. ClusterGroup(name='Cluster Group 1', slug='cluster-type-1'),
  67. ClusterGroup(name='Cluster Group 2', slug='cluster-type-2'),
  68. ClusterGroup(name='Cluster Group 3', slug='cluster-type-3'),
  69. )
  70. ClusterGroup.objects.bulk_create(cluster_Groups)
  71. class ClusterTest(APIViewTestCases.APIViewTestCase):
  72. model = Cluster
  73. brief_fields = ['display', 'id', 'name', 'url', 'virtualmachine_count']
  74. bulk_update_data = {
  75. 'status': 'offline',
  76. 'comments': 'New comment',
  77. }
  78. @classmethod
  79. def setUpTestData(cls):
  80. cluster_types = (
  81. ClusterType(name='Cluster Type 1', slug='cluster-type-1'),
  82. ClusterType(name='Cluster Type 2', slug='cluster-type-2'),
  83. )
  84. ClusterType.objects.bulk_create(cluster_types)
  85. cluster_groups = (
  86. ClusterGroup(name='Cluster Group 1', slug='cluster-group-1'),
  87. ClusterGroup(name='Cluster Group 2', slug='cluster-group-2'),
  88. )
  89. ClusterGroup.objects.bulk_create(cluster_groups)
  90. clusters = (
  91. Cluster(name='Cluster 1', type=cluster_types[0], group=cluster_groups[0], status=ClusterStatusChoices.STATUS_PLANNED),
  92. Cluster(name='Cluster 2', type=cluster_types[0], group=cluster_groups[0], status=ClusterStatusChoices.STATUS_PLANNED),
  93. Cluster(name='Cluster 3', type=cluster_types[0], group=cluster_groups[0], status=ClusterStatusChoices.STATUS_PLANNED),
  94. )
  95. Cluster.objects.bulk_create(clusters)
  96. cls.create_data = [
  97. {
  98. 'name': 'Cluster 4',
  99. 'type': cluster_types[1].pk,
  100. 'group': cluster_groups[1].pk,
  101. 'status': ClusterStatusChoices.STATUS_STAGING,
  102. },
  103. {
  104. 'name': 'Cluster 5',
  105. 'type': cluster_types[1].pk,
  106. 'group': cluster_groups[1].pk,
  107. 'status': ClusterStatusChoices.STATUS_STAGING,
  108. },
  109. {
  110. 'name': 'Cluster 6',
  111. 'type': cluster_types[1].pk,
  112. 'group': cluster_groups[1].pk,
  113. 'status': ClusterStatusChoices.STATUS_STAGING,
  114. },
  115. ]
  116. class VirtualMachineTest(APIViewTestCases.APIViewTestCase):
  117. model = VirtualMachine
  118. brief_fields = ['display', 'id', 'name', 'url']
  119. bulk_update_data = {
  120. 'status': 'staged',
  121. }
  122. @classmethod
  123. def setUpTestData(cls):
  124. clustertype = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1')
  125. clustergroup = ClusterGroup.objects.create(name='Cluster Group 1', slug='cluster-group-1')
  126. sites = (
  127. Site(name='Site 1', slug='site-1'),
  128. Site(name='Site 2', slug='site-2'),
  129. Site(name='Site 3', slug='site-3'),
  130. )
  131. Site.objects.bulk_create(sites)
  132. clusters = (
  133. Cluster(name='Cluster 1', type=clustertype, site=sites[0], group=clustergroup),
  134. Cluster(name='Cluster 2', type=clustertype, site=sites[1], group=clustergroup),
  135. Cluster(name='Cluster 3', type=clustertype),
  136. )
  137. Cluster.objects.bulk_create(clusters)
  138. device1 = create_test_device('device1', site=sites[0], cluster=clusters[0])
  139. device2 = create_test_device('device2', site=sites[1], cluster=clusters[1])
  140. virtual_machines = (
  141. VirtualMachine(name='Virtual Machine 1', site=sites[0], cluster=clusters[0], device=device1, local_context_data={'A': 1}),
  142. VirtualMachine(name='Virtual Machine 2', site=sites[0], cluster=clusters[0], local_context_data={'B': 2}),
  143. VirtualMachine(name='Virtual Machine 3', site=sites[0], cluster=clusters[0], local_context_data={'C': 3}),
  144. )
  145. VirtualMachine.objects.bulk_create(virtual_machines)
  146. cls.create_data = [
  147. {
  148. 'name': 'Virtual Machine 4',
  149. 'site': sites[1].pk,
  150. 'cluster': clusters[1].pk,
  151. 'device': device2.pk,
  152. },
  153. {
  154. 'name': 'Virtual Machine 5',
  155. 'site': sites[1].pk,
  156. 'cluster': clusters[1].pk,
  157. },
  158. {
  159. 'name': 'Virtual Machine 6',
  160. 'site': sites[1].pk,
  161. },
  162. {
  163. 'name': 'Virtual Machine 7',
  164. 'cluster': clusters[2].pk,
  165. },
  166. ]
  167. def test_config_context_included_by_default_in_list_view(self):
  168. """
  169. Check that config context data is included by default in the virtual machines list.
  170. """
  171. virtualmachine = VirtualMachine.objects.first()
  172. url = '{}?id={}'.format(reverse('virtualization-api:virtualmachine-list'), virtualmachine.pk)
  173. self.add_permissions('virtualization.view_virtualmachine')
  174. response = self.client.get(url, **self.header)
  175. self.assertEqual(response.data['results'][0].get('config_context', {}).get('A'), 1)
  176. def test_config_context_excluded(self):
  177. """
  178. Check that config context data can be excluded by passing ?exclude=config_context.
  179. """
  180. url = reverse('virtualization-api:virtualmachine-list') + '?exclude=config_context'
  181. self.add_permissions('virtualization.view_virtualmachine')
  182. response = self.client.get(url, **self.header)
  183. self.assertFalse('config_context' in response.data['results'][0])
  184. def test_unique_name_per_cluster_constraint(self):
  185. """
  186. Check that creating a virtual machine with a duplicate name fails.
  187. """
  188. data = {
  189. 'name': 'Virtual Machine 1',
  190. 'cluster': Cluster.objects.first().pk,
  191. }
  192. url = reverse('virtualization-api:virtualmachine-list')
  193. self.add_permissions('virtualization.add_virtualmachine')
  194. response = self.client.post(url, data, format='json', **self.header)
  195. self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
  196. def test_render_config(self):
  197. configtemplate = ConfigTemplate.objects.create(
  198. name='Config Template 1',
  199. template_code='Config for virtual machine {{ virtualmachine.name }}'
  200. )
  201. vm = VirtualMachine.objects.first()
  202. vm.config_template = configtemplate
  203. vm.save()
  204. self.add_permissions('virtualization.add_virtualmachine')
  205. url = reverse('virtualization-api:virtualmachine-detail', kwargs={'pk': vm.pk}) + 'render-config/'
  206. response = self.client.post(url, {}, format='json', **self.header)
  207. self.assertHttpStatus(response, status.HTTP_200_OK)
  208. self.assertEqual(response.data['content'], f'Config for virtual machine {vm.name}')
  209. class VMInterfaceTest(APIViewTestCases.APIViewTestCase):
  210. model = VMInterface
  211. brief_fields = ['display', 'id', 'name', 'url', 'virtual_machine']
  212. bulk_update_data = {
  213. 'description': 'New description',
  214. }
  215. graphql_base_name = 'vm_interface'
  216. @classmethod
  217. def setUpTestData(cls):
  218. virtualmachine = create_test_virtualmachine('Virtual Machine 1')
  219. interfaces = (
  220. VMInterface(virtual_machine=virtualmachine, name='Interface 1'),
  221. VMInterface(virtual_machine=virtualmachine, name='Interface 2'),
  222. VMInterface(virtual_machine=virtualmachine, name='Interface 3'),
  223. )
  224. VMInterface.objects.bulk_create(interfaces)
  225. vlans = (
  226. VLAN(name='VLAN 1', vid=1),
  227. VLAN(name='VLAN 2', vid=2),
  228. VLAN(name='VLAN 3', vid=3),
  229. )
  230. VLAN.objects.bulk_create(vlans)
  231. vrfs = (
  232. VRF(name='VRF 1'),
  233. VRF(name='VRF 2'),
  234. VRF(name='VRF 3'),
  235. )
  236. VRF.objects.bulk_create(vrfs)
  237. cls.create_data = [
  238. {
  239. 'virtual_machine': virtualmachine.pk,
  240. 'name': 'Interface 4',
  241. 'mode': InterfaceModeChoices.MODE_TAGGED,
  242. 'tagged_vlans': [vlans[0].pk, vlans[1].pk],
  243. 'untagged_vlan': vlans[2].pk,
  244. 'vrf': vrfs[0].pk,
  245. },
  246. {
  247. 'virtual_machine': virtualmachine.pk,
  248. 'name': 'Interface 5',
  249. 'mode': InterfaceModeChoices.MODE_TAGGED,
  250. 'bridge': interfaces[0].pk,
  251. 'tagged_vlans': [vlans[0].pk, vlans[1].pk],
  252. 'untagged_vlan': vlans[2].pk,
  253. 'vrf': vrfs[1].pk,
  254. },
  255. {
  256. 'virtual_machine': virtualmachine.pk,
  257. 'name': 'Interface 6',
  258. 'mode': InterfaceModeChoices.MODE_TAGGED,
  259. 'parent': interfaces[1].pk,
  260. 'tagged_vlans': [vlans[0].pk, vlans[1].pk],
  261. 'untagged_vlan': vlans[2].pk,
  262. 'vrf': vrfs[2].pk,
  263. },
  264. ]
  265. def test_bulk_delete_child_interfaces(self):
  266. interface1 = VMInterface.objects.get(name='Interface 1')
  267. virtual_machine = interface1.virtual_machine
  268. self.add_permissions('virtualization.delete_vminterface')
  269. # Create a child interface
  270. child = VMInterface.objects.create(
  271. virtual_machine=virtual_machine,
  272. name='Interface 1A',
  273. parent=interface1
  274. )
  275. self.assertEqual(virtual_machine.interfaces.count(), 4)
  276. # Attempt to delete only the parent interface
  277. url = self._get_detail_url(interface1)
  278. self.client.delete(url, **self.header)
  279. self.assertEqual(virtual_machine.interfaces.count(), 4) # Parent was not deleted
  280. # Attempt to bulk delete parent & child together
  281. data = [
  282. {"id": interface1.pk},
  283. {"id": child.pk},
  284. ]
  285. self.client.delete(self._get_list_url(), data, format='json', **self.header)
  286. self.assertEqual(virtual_machine.interfaces.count(), 2) # Child & parent were both deleted
  287. class VirtualDiskTest(APIViewTestCases.APIViewTestCase):
  288. model = VirtualDisk
  289. brief_fields = ['display', 'id', 'name', 'size', 'url', 'virtual_machine']
  290. bulk_update_data = {
  291. 'size': 888,
  292. }
  293. graphql_base_name = 'virtual_disk'
  294. @classmethod
  295. def setUpTestData(cls):
  296. virtualmachine = create_test_virtualmachine('Virtual Machine 1')
  297. disks = (
  298. VirtualDisk(virtual_machine=virtualmachine, name='Disk 1', size=10),
  299. VirtualDisk(virtual_machine=virtualmachine, name='Disk 2', size=20),
  300. VirtualDisk(virtual_machine=virtualmachine, name='Disk 3', size=30),
  301. )
  302. VirtualDisk.objects.bulk_create(disks)
  303. cls.create_data = [
  304. {
  305. 'virtual_machine': virtualmachine.pk,
  306. 'name': 'Disk 4',
  307. 'size': 10,
  308. },
  309. {
  310. 'virtual_machine': virtualmachine.pk,
  311. 'name': 'Disk 5',
  312. 'size': 20,
  313. },
  314. {
  315. 'virtual_machine': virtualmachine.pk,
  316. 'name': 'Disk 6',
  317. 'size': 30,
  318. },
  319. ]