cmdbsyncer 4.2.2__tar.gz → 4.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. {cmdbsyncer-4.2.2/cmdbsyncer.egg-info → cmdbsyncer-4.2.4}/PKG-INFO +1 -1
  2. cmdbsyncer-4.2.4/application/buildinfo.txt +1 -0
  3. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/changelog/v4.2.md +26 -13
  4. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/cmk2.py +15 -0
  5. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/cmk_rules.py +31 -0
  6. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/dcd.py +67 -9
  7. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/downtimes.py +5 -1
  8. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/inits.py +22 -3
  9. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/models.py +14 -1
  10. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/syncer.py +3 -20
  11. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tags.py +22 -9
  12. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_cmk_rules.py +85 -0
  13. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_dcd.py +56 -0
  14. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_rule_passwords.py +48 -0
  15. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_tags.py +41 -0
  16. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/views.py +112 -20
  17. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/__init__.py +40 -0
  18. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/devices.py +2 -20
  19. cmdbsyncer-4.2.4/application/plugins/netbox/interfaces.py +263 -0
  20. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/tests/test_import_filter.py +8 -8
  21. cmdbsyncer-4.2.4/application/plugins/netbox/utils.py +31 -0
  22. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/checkmk_rule_project_import.html +4 -3
  23. cmdbsyncer-4.2.4/application/templates/admin/checkmk_rule_project_list.html +25 -0
  24. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/checkmk_rule_project_overview.html +72 -0
  25. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/model/inline_list_base.html +2 -2
  26. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/themes/dracula.css +8 -0
  27. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/themes/gruvbox-dark.css +8 -0
  28. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/themes/gruvbox-light.css +9 -1
  29. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/themes/nord.css +8 -0
  30. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/_form_sections.py +3 -2
  31. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4/cmdbsyncer.egg-info}/PKG-INFO +1 -1
  32. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/cmdbsyncer.egg-info/SOURCES.txt +2 -0
  33. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/pyproject.toml +1 -1
  34. cmdbsyncer-4.2.2/application/buildinfo.txt +0 -1
  35. cmdbsyncer-4.2.2/application/plugins/netbox/interfaces.py +0 -124
  36. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/LICENSE +0 -0
  37. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/README.md +0 -0
  38. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/__init__.py +0 -0
  39. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/_version.py +0 -0
  40. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/api/__init__.py +0 -0
  41. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/api/inventory.py +0 -0
  42. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/api/objects.py +0 -0
  43. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/api/rules.py +0 -0
  44. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/api/syncer.py +0 -0
  45. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/api/views.py +0 -0
  46. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/auth/views.py +0 -0
  47. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/changelog/.pages +0 -0
  48. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/changelog/v3.10.md +0 -0
  49. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/changelog/v3.11.md +0 -0
  50. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/changelog/v3.12.md +0 -0
  51. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/changelog/v3.5.md +0 -0
  52. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/changelog/v3.7.md +0 -0
  53. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/changelog/v3.8.md +0 -0
  54. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/changelog/v3.9.md +0 -0
  55. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/changelog/v4.0.md +0 -0
  56. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/changelog/v4.1.md +0 -0
  57. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/cli.py +0 -0
  58. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/config.py +0 -0
  59. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/docu_links.py +0 -0
  60. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/enterprise.py +0 -0
  61. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/__init__.py +0 -0
  62. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/audit.py +0 -0
  63. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/cron.py +0 -0
  64. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/get_account.py +0 -0
  65. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/inventory.py +0 -0
  66. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/local_config_file.py +0 -0
  67. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/local_config_presets.py +0 -0
  68. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/mongo_keys.py +0 -0
  69. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/notification_channels.py +0 -0
  70. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/notification_dispatch.py +0 -0
  71. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/plugins.py +0 -0
  72. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/sates.py +0 -0
  73. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/sql.py +0 -0
  74. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/syncer_jinja.py +0 -0
  75. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/tablib_formater.py +0 -0
  76. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/tests/__init__.py +0 -0
  77. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/helpers/tests/test_sql.py +0 -0
  78. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/mcp_server.py +0 -0
  79. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/models/__init__.py +0 -0
  80. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/models/account.py +0 -0
  81. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/models/config.py +0 -0
  82. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/models/cron.py +0 -0
  83. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/models/field_approval.py +0 -0
  84. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/models/forms.py +0 -0
  85. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/models/host.py +0 -0
  86. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/models/host_inventory_tree.py +0 -0
  87. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/models/notification_channel.py +0 -0
  88. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/models/notification_rule.py +0 -0
  89. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/models/notification_state.py +0 -0
  90. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/models/saved_search.py +0 -0
  91. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/models/states.py +0 -0
  92. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/models/user.py +0 -0
  93. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/__init__.py +0 -0
  94. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/custom_attributes/models.py +0 -0
  95. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/custom_attributes/rules.py +0 -0
  96. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/custom_attributes/views.py +0 -0
  97. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/debug.py +0 -0
  98. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/email.py +0 -0
  99. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/inventory/__init__.py +0 -0
  100. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/log/log.py +0 -0
  101. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/log/models.py +0 -0
  102. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/log/views.py +0 -0
  103. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/plugin.py +0 -0
  104. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/rule/__init__.py +0 -0
  105. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/rule/filter.py +0 -0
  106. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/rule/match.py +0 -0
  107. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/rule/models.py +0 -0
  108. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/rule/rewrite.py +0 -0
  109. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/rule/rule.py +0 -0
  110. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/rule/views.py +0 -0
  111. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/search_parser.py +0 -0
  112. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/modules/statefile.py +0 -0
  113. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/notices/strict_certificate_verification.txt +0 -0
  114. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/__init__.py +0 -0
  115. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/ansible/__init__.py +0 -0
  116. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/ansible/admin_views.py +0 -0
  117. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/ansible/inventory.py +0 -0
  118. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/ansible/models.py +0 -0
  119. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/ansible/playbook_rules.py +0 -0
  120. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/ansible/plugin.json +0 -0
  121. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/ansible/rules.py +0 -0
  122. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/ansible/runner.py +0 -0
  123. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/ansible/seed.py +0 -0
  124. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/ansible/site_syncer.py +0 -0
  125. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/ansible/views.py +0 -0
  126. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/bmc_remedy/__init__.py +0 -0
  127. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/bmc_remedy/bmc_remedy.py +0 -0
  128. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/bmc_remedy/plugin.json +0 -0
  129. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/__init__.py +0 -0
  130. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/admin_views.py +0 -0
  131. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/bi.py +0 -0
  132. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/groups.py +0 -0
  133. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/helpers.py +0 -0
  134. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/import_v1.py +0 -0
  135. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/import_v2.py +0 -0
  136. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/inventorize.py +0 -0
  137. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/notification_rules.py +0 -0
  138. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/passwords.py +0 -0
  139. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/plugin.json +0 -0
  140. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/poolfolder.py +0 -0
  141. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/rule_passwords.py +0 -0
  142. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/rules.py +0 -0
  143. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/sites.py +0 -0
  144. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/__init__.py +0 -0
  145. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_bi.py +0 -0
  146. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_cmk2.py +0 -0
  147. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_downtimes.py +0 -0
  148. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_groups.py +0 -0
  149. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_helpers.py +0 -0
  150. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_import_v1.py +0 -0
  151. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_import_v2.py +0 -0
  152. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_inventorize.py +0 -0
  153. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_notification_rules.py +0 -0
  154. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_passwords.py +0 -0
  155. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_poolfolder.py +0 -0
  156. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_rules.py +0 -0
  157. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_sites.py +0 -0
  158. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_syncer.py +0 -0
  159. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/tests/test_users.py +0 -0
  160. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/checkmk/users.py +0 -0
  161. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/cisco_dna/__init__.py +0 -0
  162. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/cisco_dna/plugin.json +0 -0
  163. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/cisco_dna/syncer.py +0 -0
  164. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/cron.py +0 -0
  165. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/csv/__init__.py +0 -0
  166. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/csv/csv.py +0 -0
  167. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/csv/plugin.json +0 -0
  168. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/file_requests.py +0 -0
  169. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/idoit/__init__.py +0 -0
  170. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/idoit/admin_views.py +0 -0
  171. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/idoit/models.py +0 -0
  172. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/idoit/plugin.json +0 -0
  173. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/idoit/rules.py +0 -0
  174. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/idoit/syncer.py +0 -0
  175. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/idoit/views.py +0 -0
  176. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jdisc/__init__.py +0 -0
  177. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jdisc/applications.py +0 -0
  178. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jdisc/devices.py +0 -0
  179. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jdisc/executables.py +0 -0
  180. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jdisc/jdisc.py +0 -0
  181. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jdisc/plugin.json +0 -0
  182. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jira/__init__.py +0 -0
  183. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jira/jira.py +0 -0
  184. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jira/plugin.json +0 -0
  185. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jira_cloud/__init__.py +0 -0
  186. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jira_cloud/admin_views.py +0 -0
  187. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jira_cloud/export.py +0 -0
  188. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jira_cloud/jira_cloud.py +0 -0
  189. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jira_cloud/models.py +0 -0
  190. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jira_cloud/plugin.json +0 -0
  191. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jira_cloud/rules.py +0 -0
  192. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jira_cloud/schema_sync.py +0 -0
  193. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/jira_cloud/views.py +0 -0
  194. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/json/__init__.py +0 -0
  195. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/json/json.py +0 -0
  196. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/json/plugin.json +0 -0
  197. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/ldap/__init__.py +0 -0
  198. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/ldap/ldap.py +0 -0
  199. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/ldap/plugin.json +0 -0
  200. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/maintenance/__init__.py +0 -0
  201. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/maintenance/plugin.json +0 -0
  202. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/mssql/__init__.py +0 -0
  203. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/mssql/plugin.json +0 -0
  204. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/mysql/__init__.py +0 -0
  205. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/mysql/mysql.py +0 -0
  206. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/mysql/plugin.json +0 -0
  207. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/admin_views.py +0 -0
  208. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/cluster.py +0 -0
  209. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/contacts.py +0 -0
  210. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/dataflow.py +0 -0
  211. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/ips.py +0 -0
  212. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/models.py +0 -0
  213. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/netbox.py +0 -0
  214. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/networks.py +0 -0
  215. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/plugin.json +0 -0
  216. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/prefixes.py +0 -0
  217. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/rules.py +0 -0
  218. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/tests/__init__.py +0 -0
  219. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/views.py +0 -0
  220. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/netbox/virtualmachines.py +0 -0
  221. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/prtg/__init__.py +0 -0
  222. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/prtg/plugin.json +0 -0
  223. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/prtg/prtg.py +0 -0
  224. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/pyodbc/__init__.py +0 -0
  225. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/pyodbc/plugin.json +0 -0
  226. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/pyodbc/pyodbc.py +0 -0
  227. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/rest/__init__.py +0 -0
  228. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/rest/plugin.json +0 -0
  229. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/rest/rest.py +0 -0
  230. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/rules/__init__.py +0 -0
  231. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/rules/admin_views.py +0 -0
  232. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/rules/autorules.py +0 -0
  233. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/rules/models.py +0 -0
  234. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/rules/plugin.json +0 -0
  235. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/rules/rule_definitions.py +0 -0
  236. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/rules/rule_import_export.py +0 -0
  237. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/rules/views.py +0 -0
  238. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/syncer_shell.py +0 -0
  239. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/vmware/__init__.py +0 -0
  240. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/vmware/admin_views.py +0 -0
  241. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/vmware/custom_attributes.py +0 -0
  242. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/vmware/models.py +0 -0
  243. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/vmware/plugin.json +0 -0
  244. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/vmware/rules.py +0 -0
  245. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/vmware/views.py +0 -0
  246. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/vmware/vmware.py +0 -0
  247. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/yml/__init__.py +0 -0
  248. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/yml/plugin.json +0 -0
  249. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins/yml/yml.py +0 -0
  250. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/plugins_cli.py +0 -0
  251. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/static/cmdbsyncer_mark.png +0 -0
  252. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/static/css/cmdbsyncer.css +0 -0
  253. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/static/favicon.png +0 -0
  254. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/static/js/main.js +0 -0
  255. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/static/logo_white.png +0 -0
  256. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/_brand.html +0 -0
  257. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/_theme.html +0 -0
  258. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/_host_tabs.html +0 -0
  259. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/account_pick_type.html +0 -0
  260. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/ansible_playbook_run.html +0 -0
  261. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/ansible_project_detail.html +0 -0
  262. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/ansible_rule_list.html +0 -0
  263. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/ansible_run_details.html +0 -0
  264. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/bulk_label_form.html +0 -0
  265. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/changelog_archive.html +0 -0
  266. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/checkmk_rule_mngmt_list.html +0 -0
  267. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/checkmk_rule_project_edit.html +0 -0
  268. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/checkmk_rule_project_import_cmk.html +0 -0
  269. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/checkmk_settings_list.html +0 -0
  270. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/checkmk_test_folder_scope.html +0 -0
  271. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/copy_as_new_form.html +0 -0
  272. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/data_quality.html +0 -0
  273. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/file/list.html +0 -0
  274. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/file/modals/form.html +0 -0
  275. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/host_details.html +0 -0
  276. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/host_edit.html +0 -0
  277. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/host_inventory_tree.html +0 -0
  278. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/host_list.html +0 -0
  279. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/host_relations_graph.html +0 -0
  280. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/host_timeline.html +0 -0
  281. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/index.html +0 -0
  282. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/local_config_editor.html +0 -0
  283. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/master.html +0 -0
  284. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/model/clone_confirm.html +0 -0
  285. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/object_list.html +0 -0
  286. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/set_cmk_version_form.html +0 -0
  287. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/admin/set_template_form.html +0 -0
  288. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/base.html +0 -0
  289. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/change_password.html +0 -0
  290. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/debug.html +0 -0
  291. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/debug_host.html +0 -0
  292. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/email/newuser.html +0 -0
  293. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/email/notification.html +0 -0
  294. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/email/resetpassword.html +0 -0
  295. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/formular.html +0 -0
  296. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/license_info.html +0 -0
  297. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/login.html +0 -0
  298. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/logout_confirm.html +0 -0
  299. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/request_password.html +0 -0
  300. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/set_2fa.html +0 -0
  301. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/templates/set_theme.html +0 -0
  302. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/themes_registry.py +0 -0
  303. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/_form_fields.py +0 -0
  304. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/account.py +0 -0
  305. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/config.py +0 -0
  306. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/cron.py +0 -0
  307. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/data_quality.py +0 -0
  308. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/default.py +0 -0
  309. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/field_approval.py +0 -0
  310. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/fileadmin.py +0 -0
  311. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/host.py +0 -0
  312. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/host_filters.py +0 -0
  313. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/host_renderers.py +0 -0
  314. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/host_widgets.py +0 -0
  315. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/license.py +0 -0
  316. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/notification_channel.py +0 -0
  317. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/notification_rule.py +0 -0
  318. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/saved_search.py +0 -0
  319. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/application/views/user.py +0 -0
  320. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/cmdbsyncer.egg-info/dependency_links.txt +0 -0
  321. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/cmdbsyncer.egg-info/entry_points.txt +0 -0
  322. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/cmdbsyncer.egg-info/requires.txt +0 -0
  323. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/cmdbsyncer.egg-info/top_level.txt +0 -0
  324. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/requirements-ansible-windows.txt +0 -0
  325. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/requirements-ansible.txt +0 -0
  326. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/requirements-extras.txt +0 -0
  327. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/requirements.txt +0 -0
  328. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/setup.cfg +0 -0
  329. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/syncerapi/__init__.py +0 -0
  330. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/syncerapi/v1/__init__.py +0 -0
  331. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/syncerapi/v1/core/__init__.py +0 -0
  332. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/syncerapi/v1/inventory/__init__.py +0 -0
  333. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/syncerapi/v1/rest/__init__.py +0 -0
  334. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/tests/test_account_macro.py +0 -0
  335. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/tests/test_api.py +0 -0
  336. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/tests/test_api_csrf_invariant.py +0 -0
  337. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/tests/test_default_view.py +0 -0
  338. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/tests/test_mongo_keys.py +0 -0
  339. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/tests/test_plugin.py +0 -0
  340. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/tests/test_plugin_ssti.py +0 -0
  341. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/tests/test_plugins_cli.py +0 -0
  342. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/tests/test_rule.py +0 -0
  343. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/tests/test_rule_filename_detect.py +0 -0
  344. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/tests/test_rule_match.py +0 -0
  345. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/tests/test_search_parser.py +0 -0
  346. {cmdbsyncer-4.2.2 → cmdbsyncer-4.2.4}/tests/test_webhook_token_hash.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cmdbsyncer
3
- Version: 4.2.2
3
+ Version: 4.2.4
4
4
  Summary: Rule-based modular synchronizer for Checkmk, Netbox, Ansible, I-DOIT, Jira CMDB and 15+ other CMDB/monitoring systems
5
5
  Author-email: Kuhn & Ruess GmbH <info@kuhn-ruess.de>
6
6
  Maintainer-email: Bastian Kuhn <mail@bastian-kuhn.de>
@@ -0,0 +1 @@
1
+ BUILD_DATE=2026-07-15T17:59:01Z
@@ -1,35 +1,48 @@
1
1
  # Version 4.2
2
2
 
3
- ## Version 4.2.2
3
+ ## Version 4.2.4
4
+
5
+ ### Checkmk
6
+ - FIX: Downtime export now reports the real error — a Checkmk/livestatus timeout or connection problem while reading current downtimes is surfaced (and re-raised with `--debug`) instead of ending in a generic "Checkmk: Export Downtimes" failure with no detail; reading downtimes for a host that returns no data no longer aborts the run
7
+ - FIX: Host export no longer logs "Created Host" when the host creation actually failed — the success line is now only printed when Checkmk accepted the host, so a failed single-host create is reported as an error instead of a false success
8
+ - FIX: Exporting host tags to Checkmk 2.5 no longer fails with "Unknown field" — the tag group id field (renamed from `ident` to `id` in Checkmk 2.4) is now selected per Checkmk version, so 2.3 and older keep working too
9
+ - FEAT: DCD rules can be flagged "static" — a host-independent DCD rule is rendered once and always created instead of being recalculated for every host, which speeds up the DCD export on large inventories (like the Setup rule static flag)
10
+ - FIX: Exporting DCD rules works again on Checkmk 2.4 and 2.5 — the connector settings are now sent nested under a `connector` object as the REST API requires; before, the export failed with many "Unknown field" errors. Checkmk 2.3 keeps its two extra connector fields
11
+ - FEAT: A DCD rule's site can be set to `{{ cmk_site }}`, which resolves to the exporting account's Checkmk site — so one rule targets the right site on a test and a prod instance with different site names; the field now shows this as a placeholder hint
12
+ - FEAT: DCD rules can use `{{ account.<field> }}` to insert any (non-secret) attribute of the exporting Checkmk account — including per-account custom fields — so one rule adapts its values per account (e.g. test vs prod); passwords are never exposed
13
+
14
+ ### Netbox
15
+ - FEAT: Netbox can now import interfaces of devices and virtual machines (analogous to the device and VM import) — each host's Netbox interfaces, including their assigned IP addresses, are stored on the host's inventory; with the account option `import_as_hosts` every interface is instead imported as its own host so it can be exported again
16
+
17
+ ### Interface
18
+ - FIX: Rule forms render fully theme-aware on the dark themes (Gruvbox, Dracula, Nord) — the nested outcome/condition field groups and their "Add …" buttons no longer show a hard-coded white/light background; nested cards, `bg-light`/`bg-white` utilities and the inline "Add" buttons now follow the active theme's surface colours everywhere
19
+
20
+ ## Version 4.2.3
4
21
 
5
22
  ### Ansible
6
23
  - FIX: Playbook tasks delegated to localhost (`delegate_to: localhost`) no longer abort with "Failed to create temporary directory" under the web server — the local connection's temp dir now points at a writable per-run path instead of the web user's home
7
24
  - FEAT: Checkmk Settings gain an "installation staging path" — the Checkmk server-management playbook stages the downloaded installer there instead of `/tmp`; leave it empty to keep using `/tmp`. Use it when the executing user can't reliably use `/tmp` (permission or private-tmp isolation)
8
25
  - FIX: The Checkmk site-update playbook no longer fails the "set downtime" step with "401 Unauthorized" when no Checkmk API user/secret is configured — the downtime is skipped instead
26
+ - FIX: Running a playbook from the UI no longer aborts with "cmdbsyncer binary not found" — the local-mode inventory now resolves the `cmdbsyncer` CLI from the venv even when the web server's PATH omits it
27
+
28
+ ### Setup
29
+ - FIX: A fresh `docker compose up` from git now starts — the bundled compose stack was told to reach MongoDB on `127.0.0.1` (the app container itself) instead of the `mongo` service, so the API aborted on boot with `Connection refused` (gh #124)
9
30
 
10
31
  ### Checkmk
32
+ - FIX: Importing Setup Rules from a Checkmk folder now surfaces the real Checkmk error (e.g. wrong credentials) instead of silently reporting "0 rules imported"
33
+ - FEAT: The Checkmk group export now honors an account's (or child account's) object filter for "Checkmk: Export Groups" — set it to include CMDB objects/shadow hosts in the group calculation; without a filter the export stays host-only as before
11
34
  - FEAT: The Checkmk module menu is tidier — "Setup Rule Projects" now sits next to "Manage Checkmk Setup Rules", "Limit Host Export to Folders" moved under the host filters, and the separate "Export Scoping" submenu is gone
12
35
  - FIX: "Limit Host Export to Folders" now lists rule folders in lowercase to match Checkmk's folder names, so the scope reliably matches the folders hosts are exported to
13
36
  - FEAT: "Limit Host Export to Folders" is easier to use — clicking a rule folder or typing one adds it to a list of selected folders you can remove individually, instead of editing a comma-separated text field
14
37
  - FEAT: Importing Setup Rules from a Checkmk folder into a project now shows a progress bar and status message while it runs, so you can see it is working
15
38
  - FEAT: Checkmk Setup Rules can reference a syncer Checkmk Password with `{{ cmk_password("name") }}`, which resolves to that entry's Checkmk password-store ident on export — run the password export so the entry exists in the target Checkmk
16
39
  - FEAT: Importing rules from a Checkmk folder now rewrites inline rule passwords (which Checkmk masks as `******` on read) into such password-store references, so imported rules deploy from a test to a production Checkmk without exposing or overwriting the secret — create a Checkmk Password with the reported name and export it
40
+ - FEAT: The Checkmk rule export now respects an account's folder scope ("Limit Host Export to Folders") — a scoped account only receives Setup rules whose target folder is in scope, just like the host export
41
+ - FEAT: DCD rules can be assigned to a Rule Project — they show on the project overview, follow the project's account filter on export (only exported to the accounts the project allows), and are included in the project's JSON export/import; the project list gains an "Import project from JSON" button
17
42
 
18
43
  ### Interface
19
44
  - FIX: Card-style admin form pages (e.g. the "New Account" plugin-type picker) render with consistent padding and a theme-aware header in all four themes, instead of a header hanging off the edge of a flat card
20
45
 
21
- ## Version 4.2.1
22
-
23
- ### Ansible
24
- - FIX: Running a playbook from the UI no longer aborts with "cmdbsyncer binary not found" — the local-mode inventory now resolves the `cmdbsyncer` CLI from the venv even when the web server's PATH omits it
25
-
26
- ### Setup
27
- - FIX: A fresh `docker compose up` from git now starts — the bundled compose stack was told to reach MongoDB on `127.0.0.1` (the app container itself) instead of the `mongo` service, so the API aborted on boot with `Connection refused` (gh #124)
28
-
29
- ### Checkmk
30
- - FIX: Importing Setup Rules from a Checkmk folder now surfaces the real Checkmk error (e.g. wrong credentials) instead of silently reporting "0 rules imported"
31
- - FEAT: The Checkmk group export now honors an account's (or child account's) object filter for "Checkmk: Export Groups" — set it to include CMDB objects/shadow hosts in the group calculation; without a filter the export stays host-only as before
32
-
33
46
  ## Version 4.2.0
34
47
 
35
48
  ### Hosts
@@ -69,6 +69,21 @@ class CMK2(Plugin):
69
69
  self._init_complete = True
70
70
 
71
71
 
72
+ def version_at_least(self, major, minor):
73
+ """
74
+ True when the probed Checkmk version is at least ``major.minor``.
75
+
76
+ Central helper for version switches in the export code (the Checkmk REST
77
+ API changes field shapes between releases). ``checkmk_version`` looks
78
+ like ``2.4.0p19.cee`` or ``2.5.0.pro``; an unreadable value is treated
79
+ as the newest release so new installs get the current behaviour.
80
+ """
81
+ try:
82
+ release = tuple(int(p) for p in str(self.checkmk_version).split('.')[:2])
83
+ except (AttributeError, ValueError, TypeError):
84
+ return True
85
+ return release >= (major, minor)
86
+
72
87
  def _probe_checkmk_version(self):
73
88
  """
74
89
  Ask Checkmk for its version and return the bare version string.
@@ -83,6 +83,31 @@ def folder_in_scope(rule_folder, target_folder, recursive=False):
83
83
  return rule_folder.startswith(target_folder + '/')
84
84
 
85
85
 
86
+ def folder_within_scope(folder, limits):
87
+ """
88
+ True when ``folder`` falls within an account's ``limit_by_folders`` scope.
89
+
90
+ ``limits`` is the raw comma-separated ``limit_by_folders`` value off the
91
+ account. An empty or missing scope means no restriction (always True).
92
+ Folders typed without a leading slash are tolerated, and the match is
93
+ recursive so selecting ``/test`` also covers ``/test/linux``. Shared by the
94
+ host export (which folder a host lands in) and the rule export (which folder
95
+ a Setup rule is placed in) so both honour the same scope.
96
+ """
97
+ allowed = []
98
+ for entry in (limits or '').split(','):
99
+ entry = entry.strip()
100
+ if not entry:
101
+ continue
102
+ if not entry.startswith('/'):
103
+ entry = '/' + entry
104
+ allowed.append(entry)
105
+ if not allowed:
106
+ return True
107
+ folder = folder or '/'
108
+ return any(folder_in_scope(folder, scope, recursive=True) for scope in allowed)
109
+
110
+
86
111
  def iter_rule_folders():
87
112
  """
88
113
  Collect the literal folders that the configured rules can place objects
@@ -758,6 +783,12 @@ class CheckmkRuleSync(CMK2):
758
783
  value = render_jinja(rule_params['value_template'], **context)
759
784
  rule_params['folder'] = normalize_folder(
760
785
  render_jinja(rule_params['folder'], **context))
786
+ # Respect the account's folder scope (limit_by_folders): a scoped
787
+ # account only receives rules whose target folder is in scope, just
788
+ # like the host export only pushes hosts of those folders.
789
+ if not folder_within_scope(
790
+ rule_params['folder'], self.config.get('limit_by_folders')):
791
+ return None
761
792
  rule_params['value'] = value
762
793
  del rule_params['value_template']
763
794
  rule_params['optimize'] = False
@@ -17,6 +17,10 @@ class CheckmkDCDRuleSync(CMK2):
17
17
  """
18
18
  console = None
19
19
 
20
+ # Host-independent DCD rules, rendered once instead of per host. Injected
21
+ # by inits.export_dcd_rules; defaults to empty so other callers still work.
22
+ static_rules = []
23
+
20
24
  def __init__(self, account=False):
21
25
  super().__init__(account)
22
26
  # Per-instance run state. Class-level defaults would accumulate
@@ -24,6 +28,33 @@ class CheckmkDCDRuleSync(CMK2):
24
28
  # into later exports.
25
29
  self.all_rules = []
26
30
 
31
+ # Account config keys withheld from the rule template context — secrets and
32
+ # internal ids that must never end up in a rendered rule.
33
+ _ACCOUNT_HIDDEN_KEYS = frozenset({
34
+ 'password', 'bakery_passphrase', 'ca_cert_chain', 'ca_root_cert',
35
+ '_id', 'id', 'ref',
36
+ })
37
+
38
+ def _account_site(self):
39
+ """
40
+ Checkmk site name of the exporting account — the last path segment of
41
+ its address (which ends in ``/<site>``). Exposed to DCD rules as
42
+ ``{{ cmk_site }}`` so one rule can target the correct site on each
43
+ Checkmk (e.g. a test and a prod instance with different site names).
44
+ """
45
+ address = (self.config.get('address') or '').rstrip('/')
46
+ return address.rsplit('/', 1)[-1] if '/' in address else ''
47
+
48
+ def _account_context(self):
49
+ """
50
+ The exporting account's attributes exposed to DCD rules as
51
+ ``{{ account.<field> }}`` — its standard fields plus any per-account
52
+ custom field (flattened into the account config). Lets one rule use a
53
+ value configured per account (test vs prod). Secrets are withheld.
54
+ """
55
+ return {key: value for key, value in (self.config or {}).items()
56
+ if key not in self._ACCOUNT_HIDDEN_KEYS}
57
+
27
58
  def does_rule_exist(self, rule_id):
28
59
  """
29
60
  Check if Rule is existing
@@ -97,25 +128,34 @@ class CheckmkDCDRuleSync(CMK2):
97
128
  exclude_time_ranges = self.build_timeranges(rule)
98
129
 
99
130
 
100
- payload = {
101
- "dcd_id": render_jinja(rule['dcd_id'], mode='raise', **attributes),
102
- "title": render_jinja(rule['title'], mode='raise', **attributes),
103
- "comment": render_jinja(rule['comment'], mode='raise',**attributes),
104
- "disabled": rule['disabled'],
105
- "site": render_jinja(rule['site'], mode='raise',**attributes),
131
+ # Checkmk's DCD REST API nests the connector settings under a
132
+ # "connector" object (all three supported versions). Checkmk 2.4
133
+ # dropped the "activate_changes_interval" and "exclude_time_ranges"
134
+ # connector fields — only 2.3 and older still accept them.
135
+ connector = {
106
136
  "connector_type": render_jinja(rule['connector_type'], mode='raise',**attributes),
107
137
  "restrict_source_hosts": restricted_hosts,
108
138
  "interval": render_jinja(rule['interval'], mode='raise', **attributes),
109
139
  "creation_rules": creation_rules,
110
- "activate_changes_interval": render_jinja(rule['activate_changes_interval'], \
111
- **attributes),
112
140
  "discover_on_creation": rule['discover_on_creation'],
113
- "exclude_time_ranges": exclude_time_ranges,
114
141
  "no_deletion_time_after_init": render_jinja(rule['no_deletion_time_after_init'],\
115
142
  **attributes),
116
143
  "max_cache_age": render_jinja(rule['max_cache_age'], **attributes),
117
144
  "validity_period": render_jinja(rule['validity_period'], **attributes),
118
145
  }
146
+ if not self.version_at_least(2, 4):
147
+ connector["activate_changes_interval"] = \
148
+ render_jinja(rule['activate_changes_interval'], **attributes)
149
+ connector["exclude_time_ranges"] = exclude_time_ranges
150
+
151
+ payload = {
152
+ "dcd_id": render_jinja(rule['dcd_id'], mode='raise', **attributes),
153
+ "title": render_jinja(rule['title'], mode='raise', **attributes),
154
+ "comment": render_jinja(rule['comment'], mode='raise',**attributes),
155
+ "disabled": rule['disabled'],
156
+ "site": render_jinja(rule['site'], mode='raise',**attributes),
157
+ "connector": connector,
158
+ }
119
159
  if rule['documentation_url']:
120
160
  payload['documentation_url']= rule['documentation_url']
121
161
  logger.debug(payload)
@@ -151,6 +191,11 @@ class CheckmkDCDRuleSync(CMK2):
151
191
  Export DCD Rules
152
192
  """
153
193
 
194
+ # Exposed to every DCD rule: {{ cmk_site }} = the account's Checkmk site,
195
+ # and {{ account.<field> }} = any (non-secret) account attribute, so one
196
+ # rule can use values configured per account (test vs prod).
197
+ site = self._account_site()
198
+ account_ctx = self._account_context()
154
199
  db_objects = Host.active_non_template()
155
200
  total = db_objects.count()
156
201
  with Progress(SpinnerColumn(),
@@ -168,12 +213,25 @@ class CheckmkDCDRuleSync(CMK2):
168
213
  logger.debug(' -- Skiped no attributes')
169
214
  progress.advance(task1)
170
215
  continue
216
+ attributes['all']['cmk_site'] = site
217
+ attributes['all']['account'] = account_ctx
171
218
  # pylint: disable-next=no-member
172
219
  host_actions = self.actions.get_outcomes(db_host, attributes['all'])
173
220
  if host_actions:
174
221
  logger.debug(' -- Got Actions')
175
222
  self.calculate_rules_of_host(db_host.hostname, host_actions, attributes['all'])
176
223
  progress.advance(task1)
224
+
225
+ # Static rules carry no host data: render each once against an empty
226
+ # host context instead of per host (their match conditions are
227
+ # ignored — a static rule is always emitted).
228
+ for rule in self.static_rules:
229
+ host_actions = {'dcd': [dict(outcome.to_mongo())
230
+ for outcome in rule.outcomes]}
231
+ self.calculate_rules_of_host(
232
+ f"static:{rule.name}", host_actions,
233
+ {'cmk_site': site, 'account': account_ctx})
234
+
177
235
  task2 = progress.add_task("Send Rules to Checkmk", total=len(self.all_rules))
178
236
  count_new = 0
179
237
  count_existing = 0
@@ -316,7 +316,11 @@ class CheckmkDowntimeSync(CMK2):
316
316
  url = f"domain-types/downtime/collections/all?"\
317
317
  f"host_name={hostname}&downtime_type=host"
318
318
  response = self.request(url, method="GET")
319
- downtimes = response[0]['value']
319
+ # request() returns an empty ``{}`` body on error responses
320
+ # (404, non-200, no content). Guard the lookup so a failed
321
+ # downtime read yields "no current downtimes" instead of a bare
322
+ # KeyError that would escape as a detail-less run failure.
323
+ downtimes = response[0].get('value', []) if response[0] else []
320
324
  for downtime in downtimes:
321
325
  yield {
322
326
  "start" : datetime.datetime.fromisoformat(
@@ -516,10 +516,16 @@ def export_downtimes(account, debug=False, debug_rules=False):
516
516
  syncer.actions = actions
517
517
  syncer.debug_rules(debug_rules, "Checkmk")
518
518
 
519
- except CmkException as error_obj:
519
+ except Exception as error_obj: # pylint: disable=broad-exception-caught
520
+ # Catch every error, not just CmkException: the downtime export
521
+ # talks to Checkmk's livestatus (e.g. reading current downtimes),
522
+ # which can raise a plain requests timeout/connection error. With
523
+ # the old CmkException-only catch those escaped uncaught, so the
524
+ # run left only a detail-less "Checkmk: Export Downtimes" log entry
525
+ # and `--debug` never surfaced the real cause.
520
526
  if debug:
521
527
  raise
522
- print(f'C{ColorCodes.FAIL}MK Connection Error: {error_obj} {ColorCodes.ENDC}')
528
+ print(f'{ColorCodes.FAIL}Error: {error_obj} {ColorCodes.ENDC}')
523
529
  if syncer is not None:
524
530
  syncer.record_exception(error_obj)
525
531
  else:
@@ -540,12 +546,24 @@ def export_dcd_rules(account, debug=False, debug_rules=False):
540
546
  Name overwrite
541
547
  """
542
548
  actions = ExportDCD(account)
543
- actions.rules = CheckmkDCDRule.objects(enabled=True)
549
+ # Honour each project's account filter (limit_by_accounts): a DCD rule
550
+ # assigned to a project is only exported to the accounts that project
551
+ # allows. Rules without a project stay global (exported everywhere),
552
+ # matching the Setup-rule export.
553
+ allowed_projects = [None, ''] + projects_for_account(account)
554
+ # Static rules carry no host data — they are rendered once (see
555
+ # CheckmkDCDRuleSync.export_rules) instead of per host, so they are kept
556
+ # out of the per-host engine here.
557
+ actions.rules = CheckmkDCDRule.objects(
558
+ enabled=True, static_rule__ne=True, project__in=allowed_projects)
559
+ static_rules = CheckmkDCDRule.objects(
560
+ enabled=True, static_rule=True, project__in=allowed_projects)
544
561
 
545
562
  if not debug_rules:
546
563
  syncer = CheckmkDCDRuleSync(account)
547
564
  syncer.debug = debug
548
565
  syncer.actions = actions
566
+ syncer.static_rules = static_rules
549
567
  syncer.rewrite = rules['rewrite']
550
568
  syncer.filter = rules['filter']
551
569
  syncer.name = 'Checkmk: Export DCD Rules'
@@ -554,6 +572,7 @@ def export_dcd_rules(account, debug=False, debug_rules=False):
554
572
  else:
555
573
  syncer = CheckmkDCDRuleSync(False)
556
574
  syncer.actions = actions
575
+ syncer.static_rules = static_rules
557
576
  syncer.rewrite = rules['rewrite']
558
577
  syncer.filter = rules['filter']
559
578
  syncer.debug_rules(debug_rules, "Checkmk")
@@ -690,12 +690,25 @@ class CheckmkDCDRule(db.Document):
690
690
  render_cmk_dcd_rule = db.StringField()
691
691
  last_match = db.BooleanField(default=False)
692
692
 
693
+ # Host-independent DCD rule: a DCD connection rarely depends on host data, so
694
+ # a static rule is rendered once (against an empty host context, ignoring the
695
+ # match conditions) instead of once per host and de-duplicated — the same
696
+ # optimisation CheckmkRuleMngmt.static_rule provides for Setup rules.
697
+ static_rule = db.BooleanField(default=False)
698
+
699
+ # Name of the CheckmkRuleProject this DCD rule belongs to (or empty).
700
+ # Referenced by name to match CheckmkRuleMngmt.project. A DCD rule assigned
701
+ # to a project follows that project's account filter (limit_by_accounts) on
702
+ # export, just like a Setup rule — it is only exported to the accounts the
703
+ # project allows. A rule without a project stays global.
704
+ project = db.StringField()
693
705
 
694
706
  enabled = db.BooleanField()
695
707
  sort_field = db.IntField(default=0)
696
708
 
697
709
  meta = {
698
- 'strict': False
710
+ 'strict': False,
711
+ 'indexes': ['project'],
699
712
  }
700
713
 
701
714
  #.
@@ -9,7 +9,7 @@ from rich.progress import Progress, SpinnerColumn, TimeElapsedColumn, MofNComple
9
9
  from application import app, logger, log, init_db
10
10
  from application.models.host import Host
11
11
  from application.plugins.checkmk.cmk2 import CMK2, CmkException
12
- from application.plugins.checkmk.cmk_rules import folder_in_scope
12
+ from application.plugins.checkmk.cmk_rules import folder_within_scope
13
13
  from application.modules.debug import ColorCodes as CC
14
14
 
15
15
 
@@ -349,25 +349,8 @@ class SyncCMK2(CMK2):
349
349
  Returns:
350
350
  bool: True if the host should be exported to this account.
351
351
  """
352
- limits = self.config.get('limit_by_folders')
353
- if not limits:
354
- return True
355
- # A folder entered without a leading slash (e.g. "test/linux" instead of
356
- # "/test/linux") would otherwise never match the slash-prefixed folder a
357
- # rule produces — normalise it so the scope works regardless of how it
358
- # was typed, on this page or directly in the account's custom fields.
359
- allowed = []
360
- for entry in limits.split(','):
361
- entry = entry.strip()
362
- if not entry:
363
- continue
364
- if not entry.startswith('/'):
365
- entry = '/' + entry
366
- allowed.append(entry)
367
- if not allowed:
368
- return True
369
352
  folder = next_actions.get('move_folder') or '/'
370
- return any(folder_in_scope(folder, scope, recursive=True) for scope in allowed)
353
+ return folder_within_scope(folder, self.config.get('limit_by_folders'))
371
354
 
372
355
  def handle_clusters(self):
373
356
  """
@@ -1039,10 +1022,10 @@ class SyncCMK2(CMK2):
1039
1022
  try:
1040
1023
  self.request(url, method="POST", data=body)
1041
1024
  self.num_created += 1
1025
+ self.console(f" * Created Host {hostname}")
1042
1026
  except CmkException as error:
1043
1027
  self.log_details.append(('error', f"Host Create Error: {error}"))
1044
1028
  self.console(f" * CMK API ERROR {error}")
1045
- self.console(f" * Created Host {hostname}")
1046
1029
 
1047
1030
 
1048
1031
 
@@ -310,6 +310,7 @@ class CheckmkTagSync(CMK2):
310
310
  config_tags.sort(key=lambda tup: tup[1])
311
311
  if len(config_tags) > 1:
312
312
  config_tags.insert(0, (None, "Not set"))
313
+ id_field = self._tag_id_field()
313
314
  found_ids = []
314
315
  tags = []
315
316
  for x, y in config_tags:
@@ -319,7 +320,7 @@ class CheckmkTagSync(CMK2):
319
320
  if not y:
320
321
  continue
321
322
  if x not in found_ids:
322
- tags.append({'ident':x, 'title': y})
323
+ tags.append({id_field: x, 'title': y})
323
324
  found_ids.append(x)
324
325
 
325
326
  if not tags or len(tags) == 0:
@@ -327,6 +328,14 @@ class CheckmkTagSync(CMK2):
327
328
  return False
328
329
  return tags
329
330
 
331
+ def _tag_id_field(self):
332
+ """
333
+ Field name for a host tag group id and its tag choices. Checkmk renamed
334
+ it from ``ident`` to ``id`` in 2.4, so 2.4+ use ``id`` and 2.3 and older
335
+ use ``ident`` (verified against 2.3.0p48, 2.4.0p19 and 2.5.0p8).
336
+ """
337
+ return 'id' if self.version_at_least(2, 4) else 'ident'
338
+
330
339
  def sync_to_checkmk(self, groups, tag_group_list):
331
340
  """
332
341
  Use generated configuration to Sync
@@ -341,12 +350,17 @@ class CheckmkTagSync(CMK2):
341
350
  *Progress.get_default_columns(),
342
351
  TimeElapsedColumn()) as progress:
343
352
  task1 = progress.add_task("Sending to Checkmk", total=len(groups))
353
+ # Checkmk renamed the tag group id field 'ident' -> 'id' in 2.4.
354
+ id_field = self._tag_id_field()
344
355
  for syncer_group_id, syncer_group_data in groups.items():
345
356
  payload = syncer_group_data.copy()
346
357
  for what in ['object_filter', 'rw_id', 'is_template',
347
358
  'rw_title', 'multiply_list']:
348
359
  if what in payload:
349
360
  del payload[what]
361
+ # The group is built with an 'ident' key internally; send it
362
+ # under the field name this Checkmk version expects.
363
+ payload[id_field] = payload.pop('ident')
350
364
 
351
365
  tag_list = [(x[1], x[2]) for x in tag_group_list if x[0] == syncer_group_id]
352
366
  if tags := self.prepare_tags_for_checkmk(tag_list):
@@ -362,7 +376,7 @@ class CheckmkTagSync(CMK2):
362
376
  # Check if we need to update it
363
377
  checkmk_tags = checkmk_ids[syncer_group_id]
364
378
  checkmk_tags_flat = \
365
- [{'ident': x['id'], 'title': x['title']} \
379
+ [{id_field: x['id'], 'title': x['title']} \
366
380
  for x in checkmk_tags if x['title'].strip()]
367
381
 
368
382
  if app.config['CMK_DONT_DELETE_TAGS']:
@@ -370,10 +384,10 @@ class CheckmkTagSync(CMK2):
370
384
  found = set()
371
385
  new_tags = []
372
386
  for tag in payload['tags'] + checkmk_tags_flat:
373
- if tag['ident'] not in found:
374
- found.add(tag['ident'])
387
+ if tag[id_field] not in found:
388
+ found.add(tag[id_field])
375
389
  new_tags.append(tag)
376
- payload['tags'] = sorted(new_tags, key=lambda x: str(x['ident']))
390
+ payload['tags'] = sorted(new_tags, key=lambda x: str(x[id_field]))
377
391
  checkmk_tags_frozen = {frozenset(d.items()) for d in checkmk_tags_flat}
378
392
  syncer_tags_frozen = {frozenset(d.items()) for d in payload['tags']}
379
393
  if checkmk_tags_frozen == syncer_tags_frozen:
@@ -386,12 +400,11 @@ class CheckmkTagSync(CMK2):
386
400
  update_headers = {
387
401
  'if-match': etag
388
402
  }
389
- del payload['ident']
403
+ del payload[id_field]
390
404
  payload['repair'] = False
391
405
  try:
392
- self.request(url,
393
- method="PUT",
394
- data=payload,
406
+ self.request(
407
+ url, method="PUT", data=payload,
395
408
  additional_header=update_headers)
396
409
  except Exception as error:
397
410
  progress.console.print(f" ! Group {syncer_group_id} can't be updated.")
@@ -16,6 +16,7 @@ from application.plugins.checkmk.cmk_rules import (
16
16
  render_jinja_in_value,
17
17
  normalize_cmk_folder,
18
18
  folder_in_scope,
19
+ folder_within_scope,
19
20
  cmk_conditions_to_outcome,
20
21
  cmk_rule_to_outcome,
21
22
  CheckmkRuleSync,
@@ -210,6 +211,43 @@ class TestCheckmkRuleSync(unittest.TestCase):
210
211
  self.assertIn('host_label_groups', result['condition'])
211
212
  self.assertNotIn('service_labels', result['condition'])
212
213
 
214
+ @patch('application.plugins.checkmk.cmk_rules.render_jinja')
215
+ def test_folder_scope_skips_out_of_scope_rule(self, mock_render):
216
+ # A scoped account (limit_by_folders) drops a rule whose folder is not
217
+ # in scope — build returns None so the caller skips it.
218
+ mock_render.side_effect = lambda tpl, **kw: tpl
219
+ self.sync.config = {'limit_by_folders': '/test'}
220
+ rule_params = {
221
+ 'value_template': "{'k': 'v'}", 'folder': '/prod', 'comment': 'c',
222
+ }
223
+ result = self.sync.build_condition_and_update_rule_params(
224
+ rule_params, {'all': {'HOSTNAME': 'h'}})
225
+ self.assertIsNone(result)
226
+
227
+ @patch('application.plugins.checkmk.cmk_rules.render_jinja')
228
+ def test_folder_scope_keeps_in_scope_rule(self, mock_render):
229
+ mock_render.side_effect = lambda tpl, **kw: tpl
230
+ self.sync.config = {'limit_by_folders': '/test'}
231
+ rule_params = {
232
+ 'value_template': "{'k': 'v'}", 'folder': '/test/linux',
233
+ 'comment': 'c',
234
+ }
235
+ result = self.sync.build_condition_and_update_rule_params(
236
+ rule_params, {'all': {'HOSTNAME': 'h'}})
237
+ self.assertIsNotNone(result)
238
+ self.assertEqual(result['folder'], '/test/linux')
239
+
240
+ @patch('application.plugins.checkmk.cmk_rules.render_jinja')
241
+ def test_no_folder_scope_keeps_rule(self, mock_render):
242
+ mock_render.side_effect = lambda tpl, **kw: tpl
243
+ # config without limit_by_folders (the setUp default) — no restriction.
244
+ rule_params = {
245
+ 'value_template': "{'k': 'v'}", 'folder': '/prod', 'comment': 'c',
246
+ }
247
+ result = self.sync.build_condition_and_update_rule_params(
248
+ rule_params, {'all': {'HOSTNAME': 'h'}})
249
+ self.assertIsNotNone(result)
250
+
213
251
  @patch('application.plugins.checkmk.cmk_rules.render_jinja')
214
252
  def test_build_condition_v22(self, mock_render):
215
253
  mock_render.side_effect = lambda tpl, **kw: tpl
@@ -624,6 +662,19 @@ class TestCmkFolderHelpers(unittest.TestCase):
624
662
  self.assertFalse(
625
663
  folder_in_scope('/server-old', '/server', recursive=True))
626
664
 
665
+ def test_within_scope_no_limit_allows_all(self):
666
+ self.assertTrue(folder_within_scope('/anything', ''))
667
+ self.assertTrue(folder_within_scope('/anything', None))
668
+
669
+ def test_within_scope_recursive_and_leading_slash_tolerant(self):
670
+ # scope typed without a leading slash still matches, recursively.
671
+ self.assertTrue(folder_within_scope('/test/linux', 'test'))
672
+ self.assertTrue(folder_within_scope('/test', '/test,/other'))
673
+
674
+ def test_within_scope_out_of_scope_folder(self):
675
+ self.assertFalse(folder_within_scope('/prod', '/test'))
676
+ self.assertFalse(folder_within_scope('/', '/test'))
677
+
627
678
 
628
679
  class TestCmkConditionReverse(unittest.TestCase):
629
680
  """cmk_conditions_to_outcome + cmk_rule_to_outcome (reverse of export)."""
@@ -898,5 +949,39 @@ class TestProjectsForAccount(unittest.TestCase):
898
949
  self.assertEqual(for_b, ['all', 'a_and_b', 'only_b'])
899
950
 
900
951
 
952
+ class TestExportDcdRulesProjectFilter(unittest.TestCase):
953
+ """export_dcd_rules restricts DCD rules by their project's account filter."""
954
+
955
+ def test_dcd_export_filters_by_project(self):
956
+ # DefaultRule stand-in (must be subclassable)
957
+ class _StubRule: # pylint: disable=too-few-public-methods
958
+ def __init__(self, account=None):
959
+ self.rules = None
960
+
961
+ # CheckmkDCDRuleSync stand-in
962
+ class _StubSync: # pylint: disable=too-few-public-methods
963
+ def __init__(self, account=False):
964
+ pass
965
+
966
+ def export_rules(self):
967
+ pass
968
+
969
+ with patch.object(inits, '_load_rules',
970
+ return_value={'rewrite': [], 'filter': []}), \
971
+ patch.object(inits, 'projects_for_account',
972
+ return_value=['proj_a']), \
973
+ patch.object(inits, 'DefaultRule', _StubRule), \
974
+ patch.object(inits, 'CheckmkDCDRuleSync', _StubSync), \
975
+ patch.object(inits, 'CheckmkDCDRule') as mock_dcd:
976
+ mock_dcd.objects.return_value = ['rule']
977
+ inits.export_dcd_rules('acc_a')
978
+ # Global (no project) rules plus the projects this account is allowed,
979
+ # split into the per-host (non-static) and static-rule querysets.
980
+ mock_dcd.objects.assert_any_call(
981
+ enabled=True, static_rule__ne=True, project__in=[None, '', 'proj_a'])
982
+ mock_dcd.objects.assert_any_call(
983
+ enabled=True, static_rule=True, project__in=[None, '', 'proj_a'])
984
+
985
+
901
986
  if __name__ == '__main__':
902
987
  unittest.main(verbosity=2)