cmdbsyncer 4.2.4__tar.gz → 4.2.6__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 (348) hide show
  1. {cmdbsyncer-4.2.4/cmdbsyncer.egg-info → cmdbsyncer-4.2.6}/PKG-INFO +1 -1
  2. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/__init__.py +6 -0
  3. cmdbsyncer-4.2.6/application/buildinfo.txt +1 -0
  4. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/changelog/v4.2.md +34 -0
  5. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/models/host.py +6 -0
  6. cmdbsyncer-4.2.6/application/models/project.py +34 -0
  7. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/plugin.py +29 -14
  8. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/rule/rule.py +50 -9
  9. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/rule/views.py +32 -7
  10. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/__init__.py +12 -8
  11. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/admin_views.py +0 -11
  12. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/cmk_rules.py +4 -4
  13. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/downtimes.py +52 -19
  14. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/helpers.py +20 -0
  15. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/inits.py +21 -11
  16. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/models.py +2 -21
  17. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/syncer.py +33 -1
  18. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_cmk_rules.py +59 -8
  19. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_downtimes.py +53 -17
  20. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_syncer.py +25 -0
  21. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/views.py +9 -286
  22. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jira_cloud/export.py +8 -0
  23. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/maintenance/__init__.py +30 -0
  24. cmdbsyncer-4.2.6/application/templates/admin/assign_project_form.html +33 -0
  25. cmdbsyncer-4.2.4/application/templates/admin/checkmk_rule_project_edit.html → cmdbsyncer-4.2.6/application/templates/admin/project_edit.html +1 -1
  26. cmdbsyncer-4.2.4/application/templates/admin/checkmk_rule_project_import.html → cmdbsyncer-4.2.6/application/templates/admin/project_import.html +2 -2
  27. cmdbsyncer-4.2.4/application/templates/admin/checkmk_rule_project_list.html → cmdbsyncer-4.2.6/application/templates/admin/project_list.html +2 -2
  28. cmdbsyncer-4.2.4/application/templates/admin/checkmk_rule_project_overview.html → cmdbsyncer-4.2.6/application/templates/admin/project_overview.html +1 -1
  29. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/_form_sections.py +21 -8
  30. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/host.py +76 -1
  31. cmdbsyncer-4.2.6/application/views/project.py +355 -0
  32. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6/cmdbsyncer.egg-info}/PKG-INFO +1 -1
  33. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/cmdbsyncer.egg-info/SOURCES.txt +8 -5
  34. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/pyproject.toml +1 -1
  35. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/tests/test_api.py +5 -2
  36. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/tests/test_plugin.py +39 -0
  37. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/tests/test_rule.py +47 -0
  38. cmdbsyncer-4.2.4/application/buildinfo.txt +0 -1
  39. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/LICENSE +0 -0
  40. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/README.md +0 -0
  41. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/_version.py +0 -0
  42. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/api/__init__.py +0 -0
  43. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/api/inventory.py +0 -0
  44. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/api/objects.py +0 -0
  45. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/api/rules.py +0 -0
  46. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/api/syncer.py +0 -0
  47. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/api/views.py +0 -0
  48. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/auth/views.py +0 -0
  49. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/changelog/.pages +0 -0
  50. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/changelog/v3.10.md +0 -0
  51. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/changelog/v3.11.md +0 -0
  52. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/changelog/v3.12.md +0 -0
  53. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/changelog/v3.5.md +0 -0
  54. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/changelog/v3.7.md +0 -0
  55. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/changelog/v3.8.md +0 -0
  56. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/changelog/v3.9.md +0 -0
  57. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/changelog/v4.0.md +0 -0
  58. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/changelog/v4.1.md +0 -0
  59. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/cli.py +0 -0
  60. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/config.py +0 -0
  61. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/docu_links.py +0 -0
  62. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/enterprise.py +0 -0
  63. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/__init__.py +0 -0
  64. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/audit.py +0 -0
  65. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/cron.py +0 -0
  66. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/get_account.py +0 -0
  67. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/inventory.py +0 -0
  68. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/local_config_file.py +0 -0
  69. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/local_config_presets.py +0 -0
  70. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/mongo_keys.py +0 -0
  71. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/notification_channels.py +0 -0
  72. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/notification_dispatch.py +0 -0
  73. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/plugins.py +0 -0
  74. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/sates.py +0 -0
  75. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/sql.py +0 -0
  76. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/syncer_jinja.py +0 -0
  77. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/tablib_formater.py +0 -0
  78. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/tests/__init__.py +0 -0
  79. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/helpers/tests/test_sql.py +0 -0
  80. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/mcp_server.py +0 -0
  81. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/models/__init__.py +0 -0
  82. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/models/account.py +0 -0
  83. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/models/config.py +0 -0
  84. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/models/cron.py +0 -0
  85. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/models/field_approval.py +0 -0
  86. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/models/forms.py +0 -0
  87. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/models/host_inventory_tree.py +0 -0
  88. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/models/notification_channel.py +0 -0
  89. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/models/notification_rule.py +0 -0
  90. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/models/notification_state.py +0 -0
  91. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/models/saved_search.py +0 -0
  92. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/models/states.py +0 -0
  93. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/models/user.py +0 -0
  94. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/__init__.py +0 -0
  95. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/custom_attributes/models.py +0 -0
  96. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/custom_attributes/rules.py +0 -0
  97. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/custom_attributes/views.py +0 -0
  98. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/debug.py +0 -0
  99. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/email.py +0 -0
  100. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/inventory/__init__.py +0 -0
  101. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/log/log.py +0 -0
  102. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/log/models.py +0 -0
  103. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/log/views.py +0 -0
  104. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/rule/__init__.py +0 -0
  105. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/rule/filter.py +0 -0
  106. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/rule/match.py +0 -0
  107. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/rule/models.py +0 -0
  108. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/rule/rewrite.py +0 -0
  109. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/search_parser.py +0 -0
  110. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/modules/statefile.py +0 -0
  111. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/notices/strict_certificate_verification.txt +0 -0
  112. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/__init__.py +0 -0
  113. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/ansible/__init__.py +0 -0
  114. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/ansible/admin_views.py +0 -0
  115. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/ansible/inventory.py +0 -0
  116. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/ansible/models.py +0 -0
  117. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/ansible/playbook_rules.py +0 -0
  118. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/ansible/plugin.json +0 -0
  119. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/ansible/rules.py +0 -0
  120. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/ansible/runner.py +0 -0
  121. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/ansible/seed.py +0 -0
  122. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/ansible/site_syncer.py +0 -0
  123. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/ansible/views.py +0 -0
  124. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/bmc_remedy/__init__.py +0 -0
  125. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/bmc_remedy/bmc_remedy.py +0 -0
  126. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/bmc_remedy/plugin.json +0 -0
  127. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/bi.py +0 -0
  128. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/cmk2.py +0 -0
  129. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/dcd.py +0 -0
  130. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/groups.py +0 -0
  131. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/import_v1.py +0 -0
  132. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/import_v2.py +0 -0
  133. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/inventorize.py +0 -0
  134. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/notification_rules.py +0 -0
  135. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/passwords.py +0 -0
  136. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/plugin.json +0 -0
  137. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/poolfolder.py +0 -0
  138. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/rule_passwords.py +0 -0
  139. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/rules.py +0 -0
  140. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/sites.py +0 -0
  141. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tags.py +0 -0
  142. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/__init__.py +0 -0
  143. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_bi.py +0 -0
  144. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_cmk2.py +0 -0
  145. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_dcd.py +0 -0
  146. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_groups.py +0 -0
  147. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_helpers.py +0 -0
  148. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_import_v1.py +0 -0
  149. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_import_v2.py +0 -0
  150. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_inventorize.py +0 -0
  151. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_notification_rules.py +0 -0
  152. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_passwords.py +0 -0
  153. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_poolfolder.py +0 -0
  154. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_rule_passwords.py +0 -0
  155. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_rules.py +0 -0
  156. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_sites.py +0 -0
  157. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_tags.py +0 -0
  158. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/tests/test_users.py +0 -0
  159. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/checkmk/users.py +0 -0
  160. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/cisco_dna/__init__.py +0 -0
  161. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/cisco_dna/plugin.json +0 -0
  162. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/cisco_dna/syncer.py +0 -0
  163. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/cron.py +0 -0
  164. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/csv/__init__.py +0 -0
  165. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/csv/csv.py +0 -0
  166. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/csv/plugin.json +0 -0
  167. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/file_requests.py +0 -0
  168. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/idoit/__init__.py +0 -0
  169. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/idoit/admin_views.py +0 -0
  170. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/idoit/models.py +0 -0
  171. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/idoit/plugin.json +0 -0
  172. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/idoit/rules.py +0 -0
  173. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/idoit/syncer.py +0 -0
  174. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/idoit/views.py +0 -0
  175. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jdisc/__init__.py +0 -0
  176. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jdisc/applications.py +0 -0
  177. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jdisc/devices.py +0 -0
  178. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jdisc/executables.py +0 -0
  179. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jdisc/jdisc.py +0 -0
  180. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jdisc/plugin.json +0 -0
  181. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jira/__init__.py +0 -0
  182. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jira/jira.py +0 -0
  183. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jira/plugin.json +0 -0
  184. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jira_cloud/__init__.py +0 -0
  185. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jira_cloud/admin_views.py +0 -0
  186. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jira_cloud/jira_cloud.py +0 -0
  187. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jira_cloud/models.py +0 -0
  188. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jira_cloud/plugin.json +0 -0
  189. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jira_cloud/rules.py +0 -0
  190. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jira_cloud/schema_sync.py +0 -0
  191. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/jira_cloud/views.py +0 -0
  192. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/json/__init__.py +0 -0
  193. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/json/json.py +0 -0
  194. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/json/plugin.json +0 -0
  195. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/ldap/__init__.py +0 -0
  196. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/ldap/ldap.py +0 -0
  197. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/ldap/plugin.json +0 -0
  198. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/maintenance/plugin.json +0 -0
  199. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/mssql/__init__.py +0 -0
  200. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/mssql/plugin.json +0 -0
  201. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/mysql/__init__.py +0 -0
  202. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/mysql/mysql.py +0 -0
  203. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/mysql/plugin.json +0 -0
  204. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/__init__.py +0 -0
  205. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/admin_views.py +0 -0
  206. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/cluster.py +0 -0
  207. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/contacts.py +0 -0
  208. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/dataflow.py +0 -0
  209. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/devices.py +0 -0
  210. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/interfaces.py +0 -0
  211. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/ips.py +0 -0
  212. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/models.py +0 -0
  213. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/netbox.py +0 -0
  214. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/networks.py +0 -0
  215. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/plugin.json +0 -0
  216. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/prefixes.py +0 -0
  217. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/rules.py +0 -0
  218. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/tests/__init__.py +0 -0
  219. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/tests/test_import_filter.py +0 -0
  220. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/utils.py +0 -0
  221. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/views.py +0 -0
  222. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/netbox/virtualmachines.py +0 -0
  223. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/prtg/__init__.py +0 -0
  224. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/prtg/plugin.json +0 -0
  225. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/prtg/prtg.py +0 -0
  226. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/pyodbc/__init__.py +0 -0
  227. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/pyodbc/plugin.json +0 -0
  228. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/pyodbc/pyodbc.py +0 -0
  229. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/rest/__init__.py +0 -0
  230. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/rest/plugin.json +0 -0
  231. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/rest/rest.py +0 -0
  232. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/rules/__init__.py +0 -0
  233. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/rules/admin_views.py +0 -0
  234. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/rules/autorules.py +0 -0
  235. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/rules/models.py +0 -0
  236. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/rules/plugin.json +0 -0
  237. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/rules/rule_definitions.py +0 -0
  238. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/rules/rule_import_export.py +0 -0
  239. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/rules/views.py +0 -0
  240. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/syncer_shell.py +0 -0
  241. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/vmware/__init__.py +0 -0
  242. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/vmware/admin_views.py +0 -0
  243. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/vmware/custom_attributes.py +0 -0
  244. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/vmware/models.py +0 -0
  245. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/vmware/plugin.json +0 -0
  246. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/vmware/rules.py +0 -0
  247. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/vmware/views.py +0 -0
  248. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/vmware/vmware.py +0 -0
  249. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/yml/__init__.py +0 -0
  250. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/yml/plugin.json +0 -0
  251. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins/yml/yml.py +0 -0
  252. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/plugins_cli.py +0 -0
  253. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/static/cmdbsyncer_mark.png +0 -0
  254. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/static/css/cmdbsyncer.css +0 -0
  255. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/static/favicon.png +0 -0
  256. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/static/js/main.js +0 -0
  257. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/static/logo_white.png +0 -0
  258. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/_brand.html +0 -0
  259. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/_theme.html +0 -0
  260. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/_host_tabs.html +0 -0
  261. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/account_pick_type.html +0 -0
  262. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/ansible_playbook_run.html +0 -0
  263. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/ansible_project_detail.html +0 -0
  264. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/ansible_rule_list.html +0 -0
  265. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/ansible_run_details.html +0 -0
  266. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/bulk_label_form.html +0 -0
  267. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/changelog_archive.html +0 -0
  268. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/checkmk_rule_mngmt_list.html +0 -0
  269. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/checkmk_settings_list.html +0 -0
  270. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/checkmk_test_folder_scope.html +0 -0
  271. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/copy_as_new_form.html +0 -0
  272. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/data_quality.html +0 -0
  273. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/file/list.html +0 -0
  274. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/file/modals/form.html +0 -0
  275. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/host_details.html +0 -0
  276. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/host_edit.html +0 -0
  277. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/host_inventory_tree.html +0 -0
  278. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/host_list.html +0 -0
  279. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/host_relations_graph.html +0 -0
  280. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/host_timeline.html +0 -0
  281. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/index.html +0 -0
  282. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/local_config_editor.html +0 -0
  283. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/master.html +0 -0
  284. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/model/clone_confirm.html +0 -0
  285. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/model/inline_list_base.html +0 -0
  286. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/object_list.html +0 -0
  287. /cmdbsyncer-4.2.4/application/templates/admin/checkmk_rule_project_import_cmk.html → /cmdbsyncer-4.2.6/application/templates/admin/project_import_cmk.html +0 -0
  288. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/set_cmk_version_form.html +0 -0
  289. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/admin/set_template_form.html +0 -0
  290. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/base.html +0 -0
  291. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/change_password.html +0 -0
  292. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/debug.html +0 -0
  293. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/debug_host.html +0 -0
  294. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/email/newuser.html +0 -0
  295. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/email/notification.html +0 -0
  296. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/email/resetpassword.html +0 -0
  297. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/formular.html +0 -0
  298. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/license_info.html +0 -0
  299. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/login.html +0 -0
  300. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/logout_confirm.html +0 -0
  301. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/request_password.html +0 -0
  302. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/set_2fa.html +0 -0
  303. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/templates/set_theme.html +0 -0
  304. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/themes/dracula.css +0 -0
  305. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/themes/gruvbox-dark.css +0 -0
  306. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/themes/gruvbox-light.css +0 -0
  307. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/themes/nord.css +0 -0
  308. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/themes_registry.py +0 -0
  309. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/_form_fields.py +0 -0
  310. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/account.py +0 -0
  311. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/config.py +0 -0
  312. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/cron.py +0 -0
  313. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/data_quality.py +0 -0
  314. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/default.py +0 -0
  315. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/field_approval.py +0 -0
  316. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/fileadmin.py +0 -0
  317. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/host_filters.py +0 -0
  318. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/host_renderers.py +0 -0
  319. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/host_widgets.py +0 -0
  320. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/license.py +0 -0
  321. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/notification_channel.py +0 -0
  322. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/notification_rule.py +0 -0
  323. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/saved_search.py +0 -0
  324. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/application/views/user.py +0 -0
  325. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/cmdbsyncer.egg-info/dependency_links.txt +0 -0
  326. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/cmdbsyncer.egg-info/entry_points.txt +0 -0
  327. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/cmdbsyncer.egg-info/requires.txt +0 -0
  328. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/cmdbsyncer.egg-info/top_level.txt +0 -0
  329. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/requirements-ansible-windows.txt +0 -0
  330. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/requirements-ansible.txt +0 -0
  331. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/requirements-extras.txt +0 -0
  332. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/requirements.txt +0 -0
  333. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/setup.cfg +0 -0
  334. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/syncerapi/__init__.py +0 -0
  335. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/syncerapi/v1/__init__.py +0 -0
  336. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/syncerapi/v1/core/__init__.py +0 -0
  337. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/syncerapi/v1/inventory/__init__.py +0 -0
  338. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/syncerapi/v1/rest/__init__.py +0 -0
  339. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/tests/test_account_macro.py +0 -0
  340. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/tests/test_api_csrf_invariant.py +0 -0
  341. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/tests/test_default_view.py +0 -0
  342. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/tests/test_mongo_keys.py +0 -0
  343. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/tests/test_plugin_ssti.py +0 -0
  344. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/tests/test_plugins_cli.py +0 -0
  345. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/tests/test_rule_filename_detect.py +0 -0
  346. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/tests/test_rule_match.py +0 -0
  347. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/tests/test_search_parser.py +0 -0
  348. {cmdbsyncer-4.2.4 → cmdbsyncer-4.2.6}/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.4
3
+ Version: 4.2.6
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>
@@ -522,6 +522,12 @@ def _register_web_layer(): # pylint: disable=too-many-locals,too-many-statement
522
522
  category="Modules",
523
523
  menu_icon_type='fa', menu_icon_value='fa-cog'))
524
524
  #.
525
+ # .-- Projects
526
+ from application.models.project import Project
527
+ from application.views.project import ProjectView
528
+ admin.add_view(ProjectView(Project, name="Projects",
529
+ menu_icon_type='fa', menu_icon_value='fa-cubes'))
530
+ #.
525
531
 
526
532
  _register_all_plugin_admin_views(admin)
527
533
 
@@ -0,0 +1 @@
1
+ BUILD_DATE=2026-07-16T14:47:35Z
@@ -1,5 +1,36 @@
1
1
  # Version 4.2
2
2
 
3
+ ## Version 4.2.6
4
+
5
+ ### Projects
6
+ - FEAT: Projects have their own top-level menu entry next to Modules; run `./cmdbsyncer sys self_configure` once after the update so existing projects appear there
7
+ - FEAT: Projects can exclude accounts — the project's members are never exported to the listed accounts, and the exclusion wins over "Exported to Accounts"
8
+ - FEAT: Hosts can be assigned to a Project (host edit form or "Assign Project" bulk action in the host list) — the Checkmk host export then only pushes them to the accounts the project's account filter allows; the assignment never converts an imported host into a CMDB object
9
+
10
+ ### Checkmk
11
+ - FIX: Checkmk Setup/DCD rule exports cache rule results per account, so project rules are no longer served from another account's cached outcomes — previously the first exporting account decided which project rules everyone else got
12
+ - FIX: Opening the host debug page no longer overwrites the cached rule results used by the Checkmk rule export, and its Setup Rules table now shows on every visit instead of only the first
13
+ - FIX: Editing, renaming or deleting a Checkmk Rule Project takes effect on the next export — renames keep the assigned rules attached, and filter changes clear the hosts' cached rule results
14
+ - FIX: Importing a Rule Project from JSON keeps its account filter — imported projects no longer silently export to all accounts
15
+
16
+ ### Interface
17
+ - FIX: The section cards of the modern rule edit forms (e.g. DCD rules: header bars and nested outcome/condition cards) follow the active theme instead of showing hardcoded light backgrounds on the dark themes
18
+
19
+ ### Rules
20
+ - FIX: Editing or deleting a rule now clears the hosts' cached rule results, so exports pick up rule changes immediately instead of using outcomes cached before the edit
21
+ - FIX: `--debug-rules` shows the rule evaluation on every run — an existing rule cache no longer silences the output
22
+ - FEAT: `--debug-rules` shows the deciding condition per rule — which condition failed on a miss, which one matched on a hit
23
+ - FIX: Rule list views no longer crash when a condition was created without negate flags (e.g. programmatically or via JSON import)
24
+
25
+ ## Version 4.2.5
26
+
27
+ ### Setup
28
+ - FEAT: An account can override the global HTTP request timeout with a `request_timeout` custom field (value in seconds) — useful for slow target systems such as large distributed Checkmk setups, where downtime operations can take longer than the default 30 seconds
29
+
30
+ ### Checkmk
31
+ - FIX: Downtime export is much faster on large inventories — it now reads all existing Checkmk downtimes in a single query instead of one per host, so runs that previously appeared to hang for minutes (one livestatus round-trip per matching host) finish quickly
32
+ - FEAT: Downtime export ends with a status line showing how many downtimes were created, how many already existed in Checkmk and how many failed — a run where everything already exists no longer ends silently; the counts are also recorded in the run's log entry
33
+
3
34
  ## Version 4.2.4
4
35
 
5
36
  ### Checkmk
@@ -14,6 +45,9 @@
14
45
  ### Netbox
15
46
  - 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
47
 
48
+ ### Jira
49
+ - FEAT: `jira export_cloud --debug` now prints, per object, which fields it changes and their old and new value — so an object that keeps updating on every run (even with `--dry-run`) can be traced to the exact field responsible
50
+
17
51
  ### Interface
18
52
  - 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
53
 
@@ -142,6 +142,12 @@ class Host(db.Document):
142
142
 
143
143
  folder = db.StringField() # Is just Checkmk related, better solution needed
144
144
 
145
+ # Name of the Project this host belongs to (optional). Exports that
146
+ # honour projects (currently the Checkmk host export) only push the
147
+ # host to accounts the project's account filter allows — same scope
148
+ # as the project's rules.
149
+ project = db.StringField()
150
+
145
151
  log = db.ListField(field=db.StringField())
146
152
 
147
153
  cache = db.DictField()
@@ -0,0 +1,34 @@
1
+ """
2
+ Project
3
+ """
4
+ # pylint: disable=too-few-public-methods
5
+ from application import db
6
+
7
+
8
+ class Project(db.Document):
9
+ """
10
+ Groups syncer objects (currently Checkmk Setup Rules, DCD rules and
11
+ hosts — more may follow) and limits which accounts they are exported
12
+ to. ``limit_by_accounts`` restricts the project's members to the
13
+ listed accounts; an empty list means no restriction.
14
+ ``deny_by_accounts`` excludes accounts and wins over the allow list.
15
+
16
+ The account-scope decision itself lives with the consumers (see
17
+ ``application.plugins.checkmk.helpers.project_allows_account``) —
18
+ this document only carries the data.
19
+ """
20
+ name = db.StringField(required=True, unique=True)
21
+ documentation = db.StringField()
22
+
23
+ # Names of the accounts this project's members may be exported to.
24
+ # Empty = no restriction (all accounts). Stored by name to survive
25
+ # JSON im-/export between separate syncer instances.
26
+ limit_by_accounts = db.ListField(field=db.StringField())
27
+
28
+ # Names of the accounts this project's members are never exported to.
29
+ # The deny list wins over ``limit_by_accounts``.
30
+ deny_by_accounts = db.ListField(field=db.StringField())
31
+
32
+ meta = {
33
+ 'strict': False,
34
+ }
@@ -305,9 +305,22 @@ class Plugin():
305
305
  logger.debug(f"Request ({method.upper()}) to {url}")
306
306
 
307
307
  method = method.lower()
308
+ timeout = app.config['HTTP_REQUEST_TIMEOUT']
309
+ # Per-account override: a `request_timeout` custom field on the
310
+ # account wins over the global HTTP_REQUEST_TIMEOUT. Slow target
311
+ # systems (e.g. large distributed Checkmk setups, where every
312
+ # downtime read/write fans out to all sites) need more than the
313
+ # default 30 seconds without raising it deployment-wide.
314
+ if account_timeout := self.config.get('request_timeout'):
315
+ try:
316
+ timeout = float(account_timeout)
317
+ except (TypeError, ValueError):
318
+ logger.warning(
319
+ f"Ignoring invalid request_timeout {account_timeout!r} "
320
+ f"on account {self.config.get('name')!r}")
308
321
  payload = {
309
322
  'verify': self.verify,
310
- 'timeout': app.config['HTTP_REQUEST_TIMEOUT'],
323
+ 'timeout': timeout,
311
324
  }
312
325
 
313
326
  if headers:
@@ -524,21 +537,21 @@ class Plugin():
524
537
  return self.actions.get_outcomes(db_host, attributes)
525
538
  #.
526
539
 
527
- def debug_rules(self, hostname, model):
540
+ def debug_rules(self, hostname, _model=None):
528
541
  """
529
542
  Debug mode for inspecting rule outcomes on a specific host.
530
-
543
+
531
544
  This method is used with the --debug-rules command line switch to
532
- analyze how rules are applied to a specific host. It clears relevant
533
- caches and shows detailed rule processing information.
534
-
545
+ analyze how rules are applied to a specific host. It clears the
546
+ host's cache and shows detailed rule processing information.
547
+
535
548
  Args:
536
549
  hostname (str): Hostname to debug
537
- model (str): Model/plugin type for cache key filtering
538
-
550
+ _model (str): Unused, kept for caller compatibility
551
+
539
552
  Returns:
540
553
  None: Prints debug information to console
541
-
554
+
542
555
  Note:
543
556
  This method prints directly to console and is intended for
544
557
  interactive debugging sessions.
@@ -551,11 +564,13 @@ class Plugin():
551
564
  }
552
565
  try:
553
566
  db_host = Host.objects.get(hostname=hostname)
554
- for key in list(db_host.cache.keys()):
555
- if key.lower().startswith(model):
556
- del db_host.cache[key]
557
- if "CustomAttributeRule" in db_host.cache:
558
- del db_host.cache['CustomAttributeRule']
567
+ # Clear the host's whole cache. Outcome caches use heterogeneous
568
+ # keys (e.g. 'ExportDowntimes', qualname-based keys), so the old
569
+ # model-prefix filter missed them (and compared a lowercased key
570
+ # against a capitalized prefix). get_outcomes() then returned the
571
+ # cached result before any debug table was rendered — the debug
572
+ # run silently printed nothing.
573
+ db_host.cache = {}
559
574
  db_host.save()
560
575
  except DoesNotExist:
561
576
  print(f"{cc.FAIL}Host not Found{cc.ENDC}")
@@ -23,6 +23,25 @@ _RULE_DESCRIPTIONS = {
23
23
  'anyway': "ALWAYS match",
24
24
  }
25
25
 
26
+
27
+ def format_condition(condition):
28
+ """
29
+ Compact one-line rendering of a FullCondition dict for debug output,
30
+ so the deciding condition of a rule match/miss is readable in a table.
31
+ """
32
+ if not condition:
33
+ return ''
34
+ if condition.get('match_type') == 'tag':
35
+ tag_negate = ' negated' if condition.get('tag_match_negate') else ''
36
+ value_negate = ' negated' if condition.get('value_match_negate') else ''
37
+ return (f"tag {condition.get('tag_match')}{tag_negate} "
38
+ f"'{condition.get('tag')}', "
39
+ f"value {condition.get('value_match')}{value_negate} "
40
+ f"'{condition.get('value', '')}'")
41
+ hostname_negate = ' negated' if condition.get('hostname_match_negate') else ''
42
+ return (f"hostname {condition.get('hostname_match')}{hostname_negate} "
43
+ f"'{condition.get('hostname', '')}'")
44
+
26
45
  class Rule():
27
46
  """
28
47
  Base Rule Class
@@ -233,12 +252,13 @@ class Rule():
233
252
 
234
253
  table = Table(title=title, box=box.ASCII_DOUBLE_HEAD,\
235
254
  header_style="bold blue", title_style="yellow", \
236
- title_justify="left", width=90)
255
+ title_justify="left", width=130)
237
256
  table.add_column("Hit")
238
257
  table.add_column("Description")
239
258
  table.add_column("Rule Name")
240
259
  table.add_column("Rule ID")
241
260
  table.add_column("Last Match")
261
+ table.add_column("Deciding Condition")
242
262
 
243
263
  outcomes = {}
244
264
  rule_objs, prepared_rules = self._iter_rule_docs()
@@ -293,8 +313,20 @@ class Rule():
293
313
  "last_match": str(rule['last_match']),
294
314
  }
295
315
  self.debug_lines.append(debug_data)
316
+ # Show WHY the rule hit or missed: for 'all' rules the first
317
+ # failing condition, for 'any' rules the matching one (on a
318
+ # miss, the last condition checked — all of them failed).
319
+ if rule_hit:
320
+ deciding = format_condition(match_reason)
321
+ if deciding:
322
+ deciding = f"matched: {deciding}"
323
+ else:
324
+ deciding = format_condition(no_match_reason)
325
+ if deciding:
326
+ deciding = f"failed: {deciding}"
296
327
  table.add_row(str(rule_hit), _RULE_DESCRIPTIONS[condition_typ],\
297
- rule['name'][:30], str(rule['_id']), str(rule['last_match']))
328
+ rule['name'][:30], str(rule['_id']), str(rule['last_match']),
329
+ deciding)
298
330
  if rule_hit:
299
331
  # outcomes were pre-converted to plain dicts in
300
332
  # _iter_rule_docs so we don't rebuild them per host.
@@ -408,12 +440,20 @@ class Rule():
408
440
  return self.check_rules(db_host.hostname)
409
441
 
410
442
 
411
- def get_outcomes(self, db_host, attributes, persist_cache=True):
443
+ def get_outcomes(self, db_host, attributes, persist_cache=True,
444
+ use_cache=True):
412
445
  """
413
446
  Handle Return of outcomes.
447
+
448
+ ``use_cache=False`` bypasses the per-host outcome cache entirely
449
+ (neither read nor written). Debug evaluations need this: they run
450
+ with a different rule set than the exports (e.g. all Setup Rules
451
+ instead of the account's project scope) and must not poison the
452
+ export's cache slot — nor return a stale cached result instead of
453
+ producing debug lines.
414
454
  """
415
455
  cache = self.cache_name or self._default_cache_key
416
- if cache in db_host.cache:
456
+ if use_cache and cache in db_host.cache:
417
457
  logger.debug("Using Rule Cache for %s", db_host.hostname)
418
458
  return db_host.cache[cache]
419
459
 
@@ -421,9 +461,10 @@ class Rule():
421
461
  self.hostname = db_host.hostname
422
462
  self.db_host = db_host
423
463
  rules = self.check_rule_match(db_host)
424
- db_host.cache[cache] = rules
425
- if persist_cache:
426
- db_host.save()
427
- else:
428
- setattr(db_host, '_cache_dirty', True)
464
+ if use_cache:
465
+ db_host.cache[cache] = rules
466
+ if persist_cache:
467
+ db_host.save()
468
+ else:
469
+ setattr(db_host, '_cache_dirty', True)
429
470
  return rules
@@ -18,6 +18,7 @@ from application.views.default import DefaultModelView
18
18
  from application.modules.rule.models import filter_actions, rule_types, condition_types
19
19
  from application.docu_links import docu_links
20
20
  from application.helpers.sates import add_changes
21
+ from application.models.host import Host
21
22
  from flask_admin.contrib.mongoengine.filters import BooleanEqualFilter, FilterLike
22
23
 
23
24
 
@@ -258,10 +259,28 @@ def _render_attribute_outcomes(_view, _context, model, _name):
258
259
  return Markup(html)
259
260
 
260
261
 
262
+ def invalidate_host_rule_caches():
263
+ """
264
+ Drop the per-host rule outcome caches after a rule was changed.
265
+
266
+ ``db_host.cache`` is only reset when the host's own labels or inventory
267
+ change — a rule edit alone never touches it. Exports would keep using
268
+ the outcomes cached before the edit until every host happens to be
269
+ re-imported (reported from the field: a corrected downtime rule
270
+ exported nothing for most hosts). Outcome cache keys are heterogeneous
271
+ (e.g. 'ExportDowntimes', qualname-based keys), so a prefix-scoped clear
272
+ is not possible — drop the whole cache, the next run recomputes it.
273
+ """
274
+ Host.objects(cache__ne={}).update(set__cache={})
275
+
276
+
261
277
  def _render_full_conditions(_view, _context, model, _name):
262
278
  """
263
279
  Render full condition set which contains host or labels
264
280
  """
281
+ # Rules created programmatically or via JSON import may carry None in
282
+ # the negate booleans or match-type fields — render them like their
283
+ # defaults instead of raising a KeyError that breaks the whole list view.
265
284
  negate = {
266
285
  True: 'not ',
267
286
  False: ' ',
@@ -275,9 +294,9 @@ def _render_full_conditions(_view, _context, model, _name):
275
294
  <h6 class="card-subtitle mb-2 text-muted">Hostname</h6>
276
295
  <p class="card-text">
277
296
  <span class="badge badge-primary">
278
- {escape(negate[entry.hostname_match_negate])}
279
- {escape(condition_types[entry.hostname_match])}</span>
280
- <span class="badge badge-info">{escape(entry.hostname)}</span>
297
+ {escape(negate[bool(entry.hostname_match_negate)])}
298
+ {escape(condition_types.get(entry.hostname_match, ''))}</span>
299
+ <span class="badge badge-info">{escape(entry.hostname or '')}</span>
281
300
  </p>
282
301
  </div>
283
302
  </div>
@@ -289,16 +308,18 @@ def _render_full_conditions(_view, _context, model, _name):
289
308
  <h6 class="card-subtitle mb-2 text-muted">Key</h6>
290
309
  <p class="card-text">
291
310
  <span class="badge badge-primary">
292
- {escape(negate[entry.tag_match_negate])}{escape(condition_types[entry.tag_match])}
311
+ {escape(negate[bool(entry.tag_match_negate)])}{escape(
312
+ condition_types.get(entry.tag_match, ''))}
293
313
  </span>
294
- <span class="badge badge-info">{escape(entry.tag)}</span>
314
+ <span class="badge badge-info">{escape(entry.tag or '')}</span>
295
315
  </p>
296
316
  <h6 class="card-subtitle mb-2 text-muted">Value</h6>
297
317
  <p class="card-text">
298
318
  <span class="badge badge-primary">
299
- {escape(negate[entry.value_match_negate])} {escape(condition_types[entry.value_match])}
319
+ {escape(negate[bool(entry.value_match_negate)])} {escape(
320
+ condition_types.get(entry.value_match, ''))}
300
321
  </span>
301
- <span class="badge badge-info">{escape(entry.value)}</span>
322
+ <span class="badge badge-info">{escape(entry.value or '')}</span>
302
323
  </p>
303
324
  </div>
304
325
  </div>
@@ -448,6 +469,7 @@ class RuleModelView(DefaultModelView):
448
469
  Overwrite Actions on Model Change
449
470
  """
450
471
  add_changes()
472
+ invalidate_host_rule_caches()
451
473
 
452
474
  try:
453
475
  super().on_model_change(form, model, is_created)
@@ -460,6 +482,7 @@ class RuleModelView(DefaultModelView):
460
482
  Overwrite Actions on Model Delete
461
483
  """
462
484
  add_changes()
485
+ invalidate_host_rule_caches()
463
486
 
464
487
  return super().on_model_delete(model)
465
488
 
@@ -586,6 +609,7 @@ class FiltereModelView(DefaultModelView):
586
609
  Overwrite Actions on Model Change
587
610
  """
588
611
  add_changes()
612
+ invalidate_host_rule_caches()
589
613
 
590
614
  try:
591
615
  super().on_model_change(form, model, is_created)
@@ -598,6 +622,7 @@ class FiltereModelView(DefaultModelView):
598
622
  Overwrite Actions on Model Delete
599
623
  """
600
624
  add_changes()
625
+ invalidate_host_rule_caches()
601
626
 
602
627
  return super().on_model_delete(model)
603
628
 
@@ -209,11 +209,11 @@ def get_host_debug_data(hostname):
209
209
 
210
210
  try:
211
211
  db_host = Host.objects.get(hostname=hostname)
212
- for key in list(db_host.cache.keys()):
213
- if key.lower().startswith('checkmk'):
214
- del db_host.cache[key]
215
- if 'CustomAttributeRule' in db_host.cache:
216
- del db_host.cache['CustomAttributeRule']
212
+ # Clear the host's whole cache: outcome caches use heterogeneous
213
+ # keys (e.g. 'ExportDowntimes', 'cmk_tags_tag_choices', qualname
214
+ # based keys), so the old 'checkmk' prefix filter missed them and
215
+ # the debug run showed stale cached results for those engines.
216
+ db_host.cache = {}
217
217
  db_host.save()
218
218
  except DoesNotExist:
219
219
  print(f"{ColorCodes.FAIL}Host not Found{ColorCodes.ENDC}")
@@ -248,7 +248,11 @@ def get_host_debug_data(hostname):
248
248
  setup_rules = CheckmkRulesetRule()
249
249
  setup_rules.debug = True
250
250
  setup_rules.rules = CheckmkRuleMngmt.objects(enabled=True).order_by('sort_field')
251
- setup_rules.get_outcomes(db_host, attributes['all'])
251
+ # use_cache=False: the debug run evaluates all Setup Rules
252
+ # instead of the account's project scope and must neither
253
+ # poison the export's cache slot nor short-circuit on a
254
+ # cached result (which would leave the debug table empty).
255
+ setup_rules.get_outcomes(db_host, attributes['all'], use_cache=False)
252
256
  rule_logs['Setup Rules'] = setup_rules.debug_lines
253
257
  except Exception as exp: # pylint: disable=broad-exception-caught
254
258
  rule_logs['Setup Rules'] = [{
@@ -432,13 +436,13 @@ def cli_export_rules(account, debug):
432
436
  @click.option("--debug", is_flag=True)
433
437
  def cli_import_project_rules(project, account, folder, recursive, debug):
434
438
  """
435
- Import all Checkmk Setup Rules of a folder into a Rule Project
439
+ Import all Checkmk Setup Rules of a folder into a Project
436
440
 
437
441
  ### Example
438
442
  _./cmdbsyncer checkmk import_project_rules MYPROJECT SITEACCOUNT /server_
439
443
 
440
444
  Args:
441
- project (string): Name of the target Checkmk Rule Project
445
+ project (string): Name of the target Project
442
446
  account (string): Checkmk account to read the rules from
443
447
  folder (string): Checkmk folder to import rules from
444
448
  """
@@ -21,7 +21,6 @@ from .models import (
21
21
  CheckmkRewriteAttributeRule,
22
22
  CheckmkRule,
23
23
  CheckmkRuleMngmt,
24
- CheckmkRuleProject,
25
24
  CheckmkSettings,
26
25
  CheckmkSite,
27
26
  CheckmkTagMngmt,
@@ -37,7 +36,6 @@ from .views import (
37
36
  CheckmkFolderPoolView,
38
37
  CheckmkInventorizeAttributesView,
39
38
  CheckmkMngmtRuleView,
40
- CheckmkRuleProjectView,
41
39
  CheckmkNotificationRuleView,
42
40
  CheckmkPasswordView,
43
41
  CheckmkRuleView,
@@ -108,15 +106,6 @@ def register_admin_views(admin):
108
106
  menu_icon_value='fa-cogs',
109
107
  )
110
108
  )
111
- admin.add_view(
112
- CheckmkRuleProjectView(
113
- CheckmkRuleProject,
114
- name="Setup Rule Projects",
115
- category="Checkmk",
116
- menu_icon_type='fa',
117
- menu_icon_value='fa-cubes',
118
- )
119
- )
120
109
  admin.add_view(
121
110
  CheckmkTagMngmtView(
122
111
  CheckmkTagMngmt,
@@ -627,10 +627,10 @@ class CheckmkRuleSync(CMK2):
627
627
  # Host-independent rules, wired in by inits.export_rules. Evaluated
628
628
  # once in calculate_static_rules instead of per host.
629
629
  self.static_rules = []
630
- # Name of the CheckmkRuleProject this run belongs to (set by
631
- # inits.export_project_rules). Scopes the ``rule_marker`` so a
632
- # project push only ever manages/cleans its own rules — two projects
633
- # can share the same Checkmk instance without clobbering each other.
630
+ # Optional Project name scoping the ``rule_marker``.
631
+ # The current export runs are account-wide (all allowed projects in
632
+ # one pass), so this stays None there — it is kept for callers that
633
+ # manage a single project's rules in isolation.
634
634
  self.project = None
635
635
 
636
636
  @property
@@ -18,6 +18,13 @@ class CheckmkDowntimeSync(CMK2):
18
18
  # Reset Log Details
19
19
  #log_details = []
20
20
 
21
+ def __init__(self, account=False):
22
+ super().__init__(account)
23
+ # Run statistics for the final status line: without it a run where
24
+ # every configured downtime already exists in Checkmk ends silently
25
+ # and looks broken to the operator.
26
+ self.dt_stats = {'created': 0, 'existing': 0, 'failed': 0}
27
+
21
28
 
22
29
  def ahead_days(self, offset):
23
30
  """
@@ -216,16 +223,20 @@ class CheckmkDowntimeSync(CMK2):
216
223
  data['duration'] = int(downtime['duration'])
217
224
  try:
218
225
  self.request(url, method="POST", data=data)
226
+ self.dt_stats['created'] += 1
219
227
  print(f"\n{cc.OKGREEN} *{cc.ENDC} Set Downtime for "\
220
228
  f"{data['start_time']} ({data['comment']})")
221
229
  except CmkException as error:
230
+ self.dt_stats['failed'] += 1
222
231
  self.log_details.append(("error", f"Downtime failed {error}"))
223
232
  print(f"\n{cc.WARNING} *{cc.ENDC} Downtime failed: "\
224
233
  f"{error}")
225
234
 
226
- def do_hosts_downtimes(self, hostname, host_actions, attributes):
235
+ def do_hosts_downtimes(self, hostname, host_actions, attributes, current_downtimes):
227
236
  """
228
- Calls for one Hosts Downtime
237
+ Set the configured downtimes for one host that are not already
238
+ present in Checkmk. ``current_downtimes`` is this host's slice of the
239
+ downtimes ``run()`` read once up front in a single bulk query.
229
240
  """
230
241
  try:
231
242
  configured_downtimes = []
@@ -234,12 +245,11 @@ class CheckmkDowntimeSync(CMK2):
234
245
  configured_downtimes += \
235
246
  list(self.calculate_configured_downtimes(rule, attributes['all']))
236
247
 
237
- current_downtimes = list(
238
- self.get_current_cmk_downtimes(hostname)
239
- )
240
248
  for downtime in configured_downtimes:
241
249
  if downtime not in current_downtimes:
242
250
  self.set_downtime(hostname, downtime)
251
+ else:
252
+ self.dt_stats['existing'] += 1
243
253
  except Exception as exp: # pylint: disable=broad-exception-caught
244
254
  self.log_details.append(('error', f'Exception {exp}'))
245
255
  print(f"Exception: {exp}")
@@ -248,6 +258,13 @@ class CheckmkDowntimeSync(CMK2):
248
258
  """
249
259
  Export Downtimes
250
260
  """
261
+ # Read every current host downtime in a single bulk query up front
262
+ # instead of one query per host inside the loop. On large inventories
263
+ # that turns hundreds of serial livestatus round-trips (one per host
264
+ # matching a downtime rule) into a single call — the previous
265
+ # per-host reads were what made the export appear to hang.
266
+ current_by_host = self.get_all_cmk_downtimes()
267
+
251
268
  # Collect Rules
252
269
  total = Host.active_non_template().count()
253
270
  with Progress(SpinnerColumn(),
@@ -270,9 +287,18 @@ class CheckmkDowntimeSync(CMK2):
270
287
  if not host_actions:
271
288
  progress.advance(task1)
272
289
  continue
273
- self.do_hosts_downtimes(hostname, host_actions, attributes)
290
+ self.do_hosts_downtimes(hostname, host_actions, attributes,
291
+ current_by_host.get(hostname, []))
274
292
  progress.advance(task1)
275
293
 
294
+ # Final status line: a fully deduplicated run creates nothing and
295
+ # would otherwise end without any output, which reads like a failure.
296
+ print(f"{cc.OKGREEN} -- {cc.ENDC}Downtimes: {self.dt_stats['created']} created, "
297
+ f"{self.dt_stats['existing']} already present in Checkmk, "
298
+ f"{self.dt_stats['failed']} failed")
299
+ for key, value in self.dt_stats.items():
300
+ self.log_details.append((f'downtimes_{key}', value))
301
+
276
302
  def run_async(self):
277
303
  """
278
304
  Export Downtimes
@@ -308,25 +334,32 @@ class CheckmkDowntimeSync(CMK2):
308
334
  # pool.close()
309
335
  # pool.join()
310
336
 
311
- def get_current_cmk_downtimes(self, hostname):
312
- """
313
- Read Downtimes from Checkmk
337
+ def get_all_cmk_downtimes(self):
314
338
  """
339
+ Read all host downtimes from Checkmk in one query, grouped by
340
+ hostname (``{hostname: [downtime, ...]}``).
315
341
 
316
- url = f"domain-types/downtime/collections/all?"\
317
- f"host_name={hostname}&downtime_type=host"
342
+ Used by ``run()`` so the whole export does a single downtime read
343
+ instead of one per host — the per-host reads were the reason the
344
+ export appeared to hang on large inventories.
345
+ """
346
+ url = "domain-types/downtime/collections/all?downtime_type=host"
318
347
  response = self.request(url, method="GET")
319
348
  # request() returns an empty ``{}`` body on error responses
320
349
  # (404, non-200, no content). Guard the lookup so a failed
321
350
  # downtime read yields "no current downtimes" instead of a bare
322
351
  # KeyError that would escape as a detail-less run failure.
323
352
  downtimes = response[0].get('value', []) if response[0] else []
353
+ by_host = {}
324
354
  for downtime in downtimes:
325
- yield {
326
- "start" : datetime.datetime.fromisoformat(
327
- downtime["extensions"]["start_time"]),
328
- "end" : datetime.datetime.fromisoformat(
329
- downtime["extensions"]["end_time"]),
330
- "comment" : downtime["extensions"]["comment"],
331
- "duration" : downtime["extensions"].get("duration", False),
332
- }
355
+ extensions = downtime.get('extensions', {})
356
+ hostname = extensions.get('host_name')
357
+ if not hostname:
358
+ continue
359
+ by_host.setdefault(hostname, []).append({
360
+ "start": datetime.datetime.fromisoformat(extensions["start_time"]),
361
+ "end": datetime.datetime.fromisoformat(extensions["end_time"]),
362
+ "comment": extensions["comment"],
363
+ "duration": extensions.get("duration", False),
364
+ })
365
+ return by_host
@@ -32,3 +32,23 @@ def cmk_cleanup_hostname(input_str):
32
32
  for needle, replacer in app.config['REPLACERS']:
33
33
  input_str = input_str.replace(needle, replacer)
34
34
  return re.sub('[^a-zA-Z0-9_-]', '_', input_str.strip()).lower()
35
+
36
+
37
+ def project_allows_account(project, account_name):
38
+ """
39
+ True when a Project's rules (and assigned hosts) may be
40
+ exported to ``account_name``: not on the project's ``deny_by_accounts``
41
+ list, and either no ``limit_by_accounts`` allow list is set or the
42
+ account is on it. The deny list wins over the allow list.
43
+
44
+ Lives here (not on the model) so the account-scope decision is shared
45
+ by the rule exports, the host export and the model without importing
46
+ MongoEngine documents.
47
+ """
48
+ denied = [name for name in (getattr(project, 'deny_by_accounts', None) or [])
49
+ if name]
50
+ if account_name in denied:
51
+ return False
52
+ allowed = [name for name in (getattr(project, 'limit_by_accounts', None) or [])
53
+ if name]
54
+ return not allowed or account_name in allowed