test_models.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. from django.contrib.contenttypes.models import ContentType
  2. from django.test import TestCase
  3. from dcim.models import Device, DeviceRole, DeviceType, Location, Manufacturer, Platform, Region, Site, SiteGroup
  4. from extras.models import ConfigContext, Tag
  5. from tenancy.models import Tenant, TenantGroup
  6. from utilities.exceptions import AbortRequest
  7. from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
  8. class TagTest(TestCase):
  9. def test_create_tag_unicode(self):
  10. tag = Tag(name='Testing Unicode: 台灣')
  11. tag.save()
  12. self.assertEqual(tag.slug, 'testing-unicode-台灣')
  13. def test_object_type_validation(self):
  14. region = Region.objects.create(name='Region 1', slug='region-1')
  15. sitegroup = SiteGroup.objects.create(name='Site Group 1', slug='site-group-1')
  16. # Create a Tag that can only be applied to Regions
  17. tag = Tag.objects.create(name='Tag 1', slug='tag-1')
  18. tag.object_types.add(ContentType.objects.get_by_natural_key('dcim', 'region'))
  19. # Apply the Tag to a Region
  20. region.tags.add(tag)
  21. self.assertIn(tag, region.tags.all())
  22. # Apply the Tag to a SiteGroup
  23. with self.assertRaises(AbortRequest):
  24. sitegroup.tags.add(tag)
  25. class ConfigContextTest(TestCase):
  26. """
  27. These test cases deal with the weighting, ordering, and deep merge logic of config context data.
  28. It also ensures the various config context querysets are consistent.
  29. """
  30. @classmethod
  31. def setUpTestData(cls):
  32. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  33. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
  34. role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
  35. region = Region.objects.create(name='Region')
  36. sitegroup = SiteGroup.objects.create(name='Site Group')
  37. site = Site.objects.create(name='Site 1', slug='site-1', region=region, group=sitegroup)
  38. location = Location.objects.create(name='Location 1', slug='location-1', site=site)
  39. platform = Platform.objects.create(name='Platform')
  40. tenantgroup = TenantGroup.objects.create(name='Tenant Group')
  41. tenant = Tenant.objects.create(name='Tenant', group=tenantgroup)
  42. tag1 = Tag.objects.create(name='Tag', slug='tag')
  43. tag2 = Tag.objects.create(name='Tag2', slug='tag2')
  44. Device.objects.create(
  45. name='Device 1',
  46. device_type=devicetype,
  47. role=role,
  48. site=site,
  49. location=location
  50. )
  51. def test_higher_weight_wins(self):
  52. device = Device.objects.first()
  53. context1 = ConfigContext(
  54. name="context 1",
  55. weight=101,
  56. data={
  57. "a": 123,
  58. "b": 456,
  59. "c": 777
  60. }
  61. )
  62. context2 = ConfigContext(
  63. name="context 2",
  64. weight=100,
  65. data={
  66. "a": 123,
  67. "b": 456,
  68. "c": 789
  69. }
  70. )
  71. ConfigContext.objects.bulk_create([context1, context2])
  72. expected_data = {
  73. "a": 123,
  74. "b": 456,
  75. "c": 777
  76. }
  77. self.assertEqual(device.get_config_context(), expected_data)
  78. def test_name_ordering_after_weight(self):
  79. device = Device.objects.first()
  80. context1 = ConfigContext(
  81. name="context 1",
  82. weight=100,
  83. data={
  84. "a": 123,
  85. "b": 456,
  86. "c": 777
  87. }
  88. )
  89. context2 = ConfigContext(
  90. name="context 2",
  91. weight=100,
  92. data={
  93. "a": 123,
  94. "b": 456,
  95. "c": 789
  96. }
  97. )
  98. ConfigContext.objects.bulk_create([context1, context2])
  99. expected_data = {
  100. "a": 123,
  101. "b": 456,
  102. "c": 789
  103. }
  104. self.assertEqual(device.get_config_context(), expected_data)
  105. def test_annotation_same_as_get_for_object(self):
  106. """
  107. This test incorporates features from all of the above tests cases to ensure
  108. the annotate_config_context_data() and get_for_object() queryset methods are the same.
  109. """
  110. device = Device.objects.first()
  111. context1 = ConfigContext(
  112. name="context 1",
  113. weight=101,
  114. data={
  115. "a": 123,
  116. "b": 456,
  117. "c": 777
  118. }
  119. )
  120. context2 = ConfigContext(
  121. name="context 2",
  122. weight=100,
  123. data={
  124. "a": 123,
  125. "b": 456,
  126. "c": 789
  127. }
  128. )
  129. context3 = ConfigContext(
  130. name="context 3",
  131. weight=99,
  132. data={
  133. "d": 1
  134. }
  135. )
  136. context4 = ConfigContext(
  137. name="context 4",
  138. weight=99,
  139. data={
  140. "d": 2
  141. }
  142. )
  143. ConfigContext.objects.bulk_create([context1, context2, context3, context4])
  144. annotated_queryset = Device.objects.filter(name=device.name).annotate_config_context_data()
  145. self.assertEqual(device.get_config_context(), annotated_queryset[0].get_config_context())
  146. def test_annotation_same_as_get_for_object_device_relations(self):
  147. region = Region.objects.first()
  148. sitegroup = SiteGroup.objects.first()
  149. site = Site.objects.first()
  150. location = Location.objects.first()
  151. platform = Platform.objects.first()
  152. tenantgroup = TenantGroup.objects.first()
  153. tenant = Tenant.objects.first()
  154. tag = Tag.objects.first()
  155. region_context = ConfigContext.objects.create(
  156. name="region",
  157. weight=100,
  158. data={
  159. "region": 1
  160. }
  161. )
  162. region_context.regions.add(region)
  163. sitegroup_context = ConfigContext.objects.create(
  164. name="sitegroup",
  165. weight=100,
  166. data={
  167. "sitegroup": 1
  168. }
  169. )
  170. sitegroup_context.site_groups.add(sitegroup)
  171. site_context = ConfigContext.objects.create(
  172. name="site",
  173. weight=100,
  174. data={
  175. "site": 1
  176. }
  177. )
  178. site_context.sites.add(site)
  179. location_context = ConfigContext.objects.create(
  180. name="location",
  181. weight=100,
  182. data={
  183. "location": 1
  184. }
  185. )
  186. location_context.locations.add(location)
  187. platform_context = ConfigContext.objects.create(
  188. name="platform",
  189. weight=100,
  190. data={
  191. "platform": 1
  192. }
  193. )
  194. platform_context.platforms.add(platform)
  195. tenant_group_context = ConfigContext.objects.create(
  196. name="tenant group",
  197. weight=100,
  198. data={
  199. "tenant_group": 1
  200. }
  201. )
  202. tenant_group_context.tenant_groups.add(tenantgroup)
  203. tenant_context = ConfigContext.objects.create(
  204. name="tenant",
  205. weight=100,
  206. data={
  207. "tenant": 1
  208. }
  209. )
  210. tenant_context.tenants.add(tenant)
  211. tag_context = ConfigContext.objects.create(
  212. name="tag",
  213. weight=100,
  214. data={
  215. "tag": 1
  216. }
  217. )
  218. tag_context.tags.add(tag)
  219. device = Device.objects.create(
  220. name="Device 2",
  221. site=site,
  222. location=location,
  223. tenant=tenant,
  224. platform=platform,
  225. role=DeviceRole.objects.first(),
  226. device_type=DeviceType.objects.first()
  227. )
  228. device.tags.add(tag)
  229. annotated_queryset = Device.objects.filter(name=device.name).annotate_config_context_data()
  230. self.assertEqual(device.get_config_context(), annotated_queryset[0].get_config_context())
  231. def test_annotation_same_as_get_for_object_virtualmachine_relations(self):
  232. region = Region.objects.first()
  233. sitegroup = SiteGroup.objects.first()
  234. site = Site.objects.first()
  235. platform = Platform.objects.first()
  236. tenantgroup = TenantGroup.objects.first()
  237. tenant = Tenant.objects.first()
  238. tag = Tag.objects.first()
  239. cluster_type = ClusterType.objects.create(name="Cluster Type")
  240. cluster_group = ClusterGroup.objects.create(name="Cluster Group")
  241. cluster = Cluster.objects.create(name="Cluster", group=cluster_group, type=cluster_type)
  242. region_context = ConfigContext.objects.create(
  243. name="region",
  244. weight=100,
  245. data={"region": 1}
  246. )
  247. region_context.regions.add(region)
  248. sitegroup_context = ConfigContext.objects.create(
  249. name="sitegroup",
  250. weight=100,
  251. data={"sitegroup": 1}
  252. )
  253. sitegroup_context.site_groups.add(sitegroup)
  254. site_context = ConfigContext.objects.create(
  255. name="site",
  256. weight=100,
  257. data={"site": 1}
  258. )
  259. site_context.sites.add(site)
  260. platform_context = ConfigContext.objects.create(
  261. name="platform",
  262. weight=100,
  263. data={"platform": 1}
  264. )
  265. platform_context.platforms.add(platform)
  266. tenant_group_context = ConfigContext.objects.create(
  267. name="tenant group",
  268. weight=100,
  269. data={"tenant_group": 1}
  270. )
  271. tenant_group_context.tenant_groups.add(tenantgroup)
  272. tenant_context = ConfigContext.objects.create(
  273. name="tenant",
  274. weight=100,
  275. data={"tenant": 1}
  276. )
  277. tenant_context.tenants.add(tenant)
  278. tag_context = ConfigContext.objects.create(
  279. name="tag",
  280. weight=100,
  281. data={"tag": 1}
  282. )
  283. tag_context.tags.add(tag)
  284. cluster_type_context = ConfigContext.objects.create(
  285. name="cluster type",
  286. weight=100,
  287. data={"cluster_type": 1}
  288. )
  289. cluster_type_context.cluster_types.add(cluster_type)
  290. cluster_group_context = ConfigContext.objects.create(
  291. name="cluster group",
  292. weight=100,
  293. data={"cluster_group": 1}
  294. )
  295. cluster_group_context.cluster_groups.add(cluster_group)
  296. cluster_context = ConfigContext.objects.create(
  297. name="cluster",
  298. weight=100,
  299. data={"cluster": 1}
  300. )
  301. cluster_context.clusters.add(cluster)
  302. virtual_machine = VirtualMachine.objects.create(
  303. name="VM 1",
  304. cluster=cluster,
  305. tenant=tenant,
  306. platform=platform,
  307. role=DeviceRole.objects.first()
  308. )
  309. virtual_machine.tags.add(tag)
  310. annotated_queryset = VirtualMachine.objects.filter(name=virtual_machine.name).annotate_config_context_data()
  311. self.assertEqual(virtual_machine.get_config_context(), annotated_queryset[0].get_config_context())
  312. def test_multiple_tags_return_distinct_objects(self):
  313. """
  314. Tagged items use a generic relationship, which results in duplicate rows being returned when queried.
  315. This is combated by appending distinct() to the config context querysets. This test creates a config
  316. context assigned to two tags and ensures objects related by those same two tags result in only a single
  317. config context record being returned.
  318. See https://github.com/netbox-community/netbox/issues/5314
  319. """
  320. site = Site.objects.first()
  321. platform = Platform.objects.first()
  322. tenant = Tenant.objects.first()
  323. tags = Tag.objects.all()
  324. tag_context = ConfigContext.objects.create(
  325. name="tag",
  326. weight=100,
  327. data={
  328. "tag": 1
  329. }
  330. )
  331. tag_context.tags.set(tags)
  332. device = Device.objects.create(
  333. name="Device 3",
  334. site=site,
  335. tenant=tenant,
  336. platform=platform,
  337. role=DeviceRole.objects.first(),
  338. device_type=DeviceType.objects.first()
  339. )
  340. device.tags.set(tags)
  341. annotated_queryset = Device.objects.filter(name=device.name).annotate_config_context_data()
  342. self.assertEqual(ConfigContext.objects.get_for_object(device).count(), 1)
  343. self.assertEqual(device.get_config_context(), annotated_queryset[0].get_config_context())
  344. def test_multiple_tags_return_distinct_objects_with_seperate_config_contexts(self):
  345. """
  346. Tagged items use a generic relationship, which results in duplicate rows being returned when queried.
  347. This is combatted by by appending distinct() to the config context querysets. This test creates a config
  348. context assigned to two tags and ensures objects related by those same two tags result in only a single
  349. config context record being returned.
  350. This test case is seperate from the above in that it deals with multiple config context objects in play.
  351. See https://github.com/netbox-community/netbox/issues/5387
  352. """
  353. site = Site.objects.first()
  354. platform = Platform.objects.first()
  355. tenant = Tenant.objects.first()
  356. tag1, tag2 = list(Tag.objects.all())
  357. tag_context_1 = ConfigContext.objects.create(
  358. name="tag-1",
  359. weight=100,
  360. data={
  361. "tag": 1
  362. }
  363. )
  364. tag_context_1.tags.add(tag1)
  365. tag_context_2 = ConfigContext.objects.create(
  366. name="tag-2",
  367. weight=100,
  368. data={
  369. "tag": 1
  370. }
  371. )
  372. tag_context_2.tags.add(tag2)
  373. device = Device.objects.create(
  374. name="Device 3",
  375. site=site,
  376. tenant=tenant,
  377. platform=platform,
  378. role=DeviceRole.objects.first(),
  379. device_type=DeviceType.objects.first()
  380. )
  381. device.tags.set([tag1, tag2])
  382. annotated_queryset = Device.objects.filter(name=device.name).annotate_config_context_data()
  383. self.assertEqual(ConfigContext.objects.get_for_object(device).count(), 2)
  384. self.assertEqual(device.get_config_context(), annotated_queryset[0].get_config_context())