cmdbsyncer 4.2.1__tar.gz → 4.2.2__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 (344) hide show
  1. {cmdbsyncer-4.2.1/cmdbsyncer.egg-info → cmdbsyncer-4.2.2}/PKG-INFO +1 -1
  2. cmdbsyncer-4.2.2/application/buildinfo.txt +1 -0
  3. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v4.2.md +18 -0
  4. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/syncer_jinja.py +12 -0
  5. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/runner.py +11 -0
  6. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/site_syncer.py +1 -0
  7. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/admin_views.py +18 -26
  8. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/cmk_rules.py +23 -4
  9. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/inits.py +34 -4
  10. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/models.py +1 -0
  11. cmdbsyncer-4.2.2/application/plugins/checkmk/rule_passwords.py +126 -0
  12. cmdbsyncer-4.2.2/application/plugins/checkmk/tests/test_rule_passwords.py +160 -0
  13. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/views.py +44 -18
  14. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/static/css/cmdbsyncer.css +28 -0
  15. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/checkmk_rule_project_import_cmk.html +38 -2
  16. cmdbsyncer-4.2.2/application/templates/admin/checkmk_test_folder_scope.html +219 -0
  17. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2/cmdbsyncer.egg-info}/PKG-INFO +1 -1
  18. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/cmdbsyncer.egg-info/SOURCES.txt +2 -0
  19. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/pyproject.toml +1 -1
  20. cmdbsyncer-4.2.1/application/buildinfo.txt +0 -1
  21. cmdbsyncer-4.2.1/application/templates/admin/checkmk_test_folder_scope.html +0 -101
  22. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/LICENSE +0 -0
  23. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/README.md +0 -0
  24. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/__init__.py +0 -0
  25. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/_version.py +0 -0
  26. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/api/__init__.py +0 -0
  27. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/api/inventory.py +0 -0
  28. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/api/objects.py +0 -0
  29. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/api/rules.py +0 -0
  30. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/api/syncer.py +0 -0
  31. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/api/views.py +0 -0
  32. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/auth/views.py +0 -0
  33. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/.pages +0 -0
  34. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v3.10.md +0 -0
  35. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v3.11.md +0 -0
  36. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v3.12.md +0 -0
  37. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v3.5.md +0 -0
  38. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v3.7.md +0 -0
  39. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v3.8.md +0 -0
  40. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v3.9.md +0 -0
  41. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v4.0.md +0 -0
  42. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v4.1.md +0 -0
  43. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/cli.py +0 -0
  44. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/config.py +0 -0
  45. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/docu_links.py +0 -0
  46. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/enterprise.py +0 -0
  47. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/__init__.py +0 -0
  48. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/audit.py +0 -0
  49. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/cron.py +0 -0
  50. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/get_account.py +0 -0
  51. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/inventory.py +0 -0
  52. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/local_config_file.py +0 -0
  53. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/local_config_presets.py +0 -0
  54. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/mongo_keys.py +0 -0
  55. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/notification_channels.py +0 -0
  56. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/notification_dispatch.py +0 -0
  57. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/plugins.py +0 -0
  58. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/sates.py +0 -0
  59. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/sql.py +0 -0
  60. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/tablib_formater.py +0 -0
  61. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/tests/__init__.py +0 -0
  62. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/tests/test_sql.py +0 -0
  63. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/mcp_server.py +0 -0
  64. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/__init__.py +0 -0
  65. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/account.py +0 -0
  66. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/config.py +0 -0
  67. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/cron.py +0 -0
  68. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/field_approval.py +0 -0
  69. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/forms.py +0 -0
  70. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/host.py +0 -0
  71. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/host_inventory_tree.py +0 -0
  72. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/notification_channel.py +0 -0
  73. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/notification_rule.py +0 -0
  74. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/notification_state.py +0 -0
  75. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/saved_search.py +0 -0
  76. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/states.py +0 -0
  77. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/user.py +0 -0
  78. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/__init__.py +0 -0
  79. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/custom_attributes/models.py +0 -0
  80. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/custom_attributes/rules.py +0 -0
  81. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/custom_attributes/views.py +0 -0
  82. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/debug.py +0 -0
  83. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/email.py +0 -0
  84. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/inventory/__init__.py +0 -0
  85. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/log/log.py +0 -0
  86. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/log/models.py +0 -0
  87. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/log/views.py +0 -0
  88. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/plugin.py +0 -0
  89. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/rule/__init__.py +0 -0
  90. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/rule/filter.py +0 -0
  91. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/rule/match.py +0 -0
  92. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/rule/models.py +0 -0
  93. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/rule/rewrite.py +0 -0
  94. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/rule/rule.py +0 -0
  95. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/rule/views.py +0 -0
  96. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/search_parser.py +0 -0
  97. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/statefile.py +0 -0
  98. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/notices/strict_certificate_verification.txt +0 -0
  99. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/__init__.py +0 -0
  100. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/__init__.py +0 -0
  101. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/admin_views.py +0 -0
  102. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/inventory.py +0 -0
  103. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/models.py +0 -0
  104. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/playbook_rules.py +0 -0
  105. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/plugin.json +0 -0
  106. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/rules.py +0 -0
  107. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/seed.py +0 -0
  108. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/views.py +0 -0
  109. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/bmc_remedy/__init__.py +0 -0
  110. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/bmc_remedy/bmc_remedy.py +0 -0
  111. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/bmc_remedy/plugin.json +0 -0
  112. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/__init__.py +0 -0
  113. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/bi.py +0 -0
  114. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/cmk2.py +0 -0
  115. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/dcd.py +0 -0
  116. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/downtimes.py +0 -0
  117. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/groups.py +0 -0
  118. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/helpers.py +0 -0
  119. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/import_v1.py +0 -0
  120. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/import_v2.py +0 -0
  121. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/inventorize.py +0 -0
  122. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/notification_rules.py +0 -0
  123. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/passwords.py +0 -0
  124. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/plugin.json +0 -0
  125. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/poolfolder.py +0 -0
  126. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/rules.py +0 -0
  127. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/sites.py +0 -0
  128. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/syncer.py +0 -0
  129. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tags.py +0 -0
  130. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/__init__.py +0 -0
  131. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_bi.py +0 -0
  132. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_cmk2.py +0 -0
  133. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_cmk_rules.py +0 -0
  134. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_dcd.py +0 -0
  135. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_downtimes.py +0 -0
  136. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_groups.py +0 -0
  137. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_helpers.py +0 -0
  138. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_import_v1.py +0 -0
  139. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_import_v2.py +0 -0
  140. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_inventorize.py +0 -0
  141. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_notification_rules.py +0 -0
  142. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_passwords.py +0 -0
  143. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_poolfolder.py +0 -0
  144. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_rules.py +0 -0
  145. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_sites.py +0 -0
  146. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_syncer.py +0 -0
  147. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_tags.py +0 -0
  148. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_users.py +0 -0
  149. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/users.py +0 -0
  150. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/cisco_dna/__init__.py +0 -0
  151. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/cisco_dna/plugin.json +0 -0
  152. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/cisco_dna/syncer.py +0 -0
  153. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/cron.py +0 -0
  154. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/csv/__init__.py +0 -0
  155. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/csv/csv.py +0 -0
  156. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/csv/plugin.json +0 -0
  157. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/file_requests.py +0 -0
  158. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/idoit/__init__.py +0 -0
  159. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/idoit/admin_views.py +0 -0
  160. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/idoit/models.py +0 -0
  161. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/idoit/plugin.json +0 -0
  162. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/idoit/rules.py +0 -0
  163. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/idoit/syncer.py +0 -0
  164. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/idoit/views.py +0 -0
  165. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jdisc/__init__.py +0 -0
  166. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jdisc/applications.py +0 -0
  167. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jdisc/devices.py +0 -0
  168. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jdisc/executables.py +0 -0
  169. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jdisc/jdisc.py +0 -0
  170. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jdisc/plugin.json +0 -0
  171. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira/__init__.py +0 -0
  172. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira/jira.py +0 -0
  173. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira/plugin.json +0 -0
  174. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/__init__.py +0 -0
  175. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/admin_views.py +0 -0
  176. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/export.py +0 -0
  177. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/jira_cloud.py +0 -0
  178. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/models.py +0 -0
  179. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/plugin.json +0 -0
  180. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/rules.py +0 -0
  181. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/schema_sync.py +0 -0
  182. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/views.py +0 -0
  183. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/json/__init__.py +0 -0
  184. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/json/json.py +0 -0
  185. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/json/plugin.json +0 -0
  186. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ldap/__init__.py +0 -0
  187. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ldap/ldap.py +0 -0
  188. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ldap/plugin.json +0 -0
  189. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/maintenance/__init__.py +0 -0
  190. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/maintenance/plugin.json +0 -0
  191. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/mssql/__init__.py +0 -0
  192. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/mssql/plugin.json +0 -0
  193. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/mysql/__init__.py +0 -0
  194. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/mysql/mysql.py +0 -0
  195. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/mysql/plugin.json +0 -0
  196. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/__init__.py +0 -0
  197. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/admin_views.py +0 -0
  198. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/cluster.py +0 -0
  199. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/contacts.py +0 -0
  200. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/dataflow.py +0 -0
  201. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/devices.py +0 -0
  202. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/interfaces.py +0 -0
  203. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/ips.py +0 -0
  204. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/models.py +0 -0
  205. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/netbox.py +0 -0
  206. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/networks.py +0 -0
  207. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/plugin.json +0 -0
  208. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/prefixes.py +0 -0
  209. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/rules.py +0 -0
  210. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/tests/__init__.py +0 -0
  211. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/tests/test_import_filter.py +0 -0
  212. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/views.py +0 -0
  213. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/virtualmachines.py +0 -0
  214. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/prtg/__init__.py +0 -0
  215. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/prtg/plugin.json +0 -0
  216. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/prtg/prtg.py +0 -0
  217. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/pyodbc/__init__.py +0 -0
  218. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/pyodbc/plugin.json +0 -0
  219. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/pyodbc/pyodbc.py +0 -0
  220. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rest/__init__.py +0 -0
  221. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rest/plugin.json +0 -0
  222. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rest/rest.py +0 -0
  223. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/__init__.py +0 -0
  224. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/admin_views.py +0 -0
  225. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/autorules.py +0 -0
  226. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/models.py +0 -0
  227. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/plugin.json +0 -0
  228. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/rule_definitions.py +0 -0
  229. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/rule_import_export.py +0 -0
  230. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/views.py +0 -0
  231. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/syncer_shell.py +0 -0
  232. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/__init__.py +0 -0
  233. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/admin_views.py +0 -0
  234. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/custom_attributes.py +0 -0
  235. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/models.py +0 -0
  236. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/plugin.json +0 -0
  237. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/rules.py +0 -0
  238. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/views.py +0 -0
  239. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/vmware.py +0 -0
  240. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/yml/__init__.py +0 -0
  241. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/yml/plugin.json +0 -0
  242. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/yml/yml.py +0 -0
  243. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins_cli.py +0 -0
  244. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/static/cmdbsyncer_mark.png +0 -0
  245. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/static/favicon.png +0 -0
  246. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/static/js/main.js +0 -0
  247. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/static/logo_white.png +0 -0
  248. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/_brand.html +0 -0
  249. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/_theme.html +0 -0
  250. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/_host_tabs.html +0 -0
  251. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/account_pick_type.html +0 -0
  252. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/ansible_playbook_run.html +0 -0
  253. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/ansible_project_detail.html +0 -0
  254. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/ansible_rule_list.html +0 -0
  255. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/ansible_run_details.html +0 -0
  256. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/bulk_label_form.html +0 -0
  257. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/changelog_archive.html +0 -0
  258. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/checkmk_rule_mngmt_list.html +0 -0
  259. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/checkmk_rule_project_edit.html +0 -0
  260. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/checkmk_rule_project_import.html +0 -0
  261. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/checkmk_rule_project_overview.html +0 -0
  262. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/checkmk_settings_list.html +0 -0
  263. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/copy_as_new_form.html +0 -0
  264. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/data_quality.html +0 -0
  265. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/file/list.html +0 -0
  266. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/file/modals/form.html +0 -0
  267. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/host_details.html +0 -0
  268. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/host_edit.html +0 -0
  269. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/host_inventory_tree.html +0 -0
  270. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/host_list.html +0 -0
  271. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/host_relations_graph.html +0 -0
  272. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/host_timeline.html +0 -0
  273. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/index.html +0 -0
  274. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/local_config_editor.html +0 -0
  275. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/master.html +0 -0
  276. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/model/clone_confirm.html +0 -0
  277. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/model/inline_list_base.html +0 -0
  278. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/object_list.html +0 -0
  279. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/set_cmk_version_form.html +0 -0
  280. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/set_template_form.html +0 -0
  281. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/base.html +0 -0
  282. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/change_password.html +0 -0
  283. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/debug.html +0 -0
  284. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/debug_host.html +0 -0
  285. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/email/newuser.html +0 -0
  286. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/email/notification.html +0 -0
  287. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/email/resetpassword.html +0 -0
  288. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/formular.html +0 -0
  289. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/license_info.html +0 -0
  290. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/login.html +0 -0
  291. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/logout_confirm.html +0 -0
  292. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/request_password.html +0 -0
  293. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/set_2fa.html +0 -0
  294. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/set_theme.html +0 -0
  295. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/themes/dracula.css +0 -0
  296. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/themes/gruvbox-dark.css +0 -0
  297. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/themes/gruvbox-light.css +0 -0
  298. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/themes/nord.css +0 -0
  299. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/themes_registry.py +0 -0
  300. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/_form_fields.py +0 -0
  301. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/_form_sections.py +0 -0
  302. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/account.py +0 -0
  303. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/config.py +0 -0
  304. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/cron.py +0 -0
  305. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/data_quality.py +0 -0
  306. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/default.py +0 -0
  307. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/field_approval.py +0 -0
  308. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/fileadmin.py +0 -0
  309. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/host.py +0 -0
  310. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/host_filters.py +0 -0
  311. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/host_renderers.py +0 -0
  312. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/host_widgets.py +0 -0
  313. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/license.py +0 -0
  314. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/notification_channel.py +0 -0
  315. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/notification_rule.py +0 -0
  316. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/saved_search.py +0 -0
  317. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/user.py +0 -0
  318. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/cmdbsyncer.egg-info/dependency_links.txt +0 -0
  319. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/cmdbsyncer.egg-info/entry_points.txt +0 -0
  320. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/cmdbsyncer.egg-info/requires.txt +0 -0
  321. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/cmdbsyncer.egg-info/top_level.txt +0 -0
  322. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/requirements-ansible-windows.txt +0 -0
  323. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/requirements-ansible.txt +0 -0
  324. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/requirements-extras.txt +0 -0
  325. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/requirements.txt +0 -0
  326. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/setup.cfg +0 -0
  327. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/syncerapi/__init__.py +0 -0
  328. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/syncerapi/v1/__init__.py +0 -0
  329. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/syncerapi/v1/core/__init__.py +0 -0
  330. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/syncerapi/v1/inventory/__init__.py +0 -0
  331. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/syncerapi/v1/rest/__init__.py +0 -0
  332. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_account_macro.py +0 -0
  333. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_api.py +0 -0
  334. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_api_csrf_invariant.py +0 -0
  335. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_default_view.py +0 -0
  336. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_mongo_keys.py +0 -0
  337. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_plugin.py +0 -0
  338. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_plugin_ssti.py +0 -0
  339. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_plugins_cli.py +0 -0
  340. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_rule.py +0 -0
  341. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_rule_filename_detect.py +0 -0
  342. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_rule_match.py +0 -0
  343. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_search_parser.py +0 -0
  344. {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/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.1
3
+ Version: 4.2.2
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-14T11:01:00Z
@@ -1,5 +1,23 @@
1
1
  # Version 4.2
2
2
 
3
+ ## Version 4.2.2
4
+
5
+ ### Ansible
6
+ - 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
+ - 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
+ - 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
9
+
10
+ ### Checkmk
11
+ - 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
+ - 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
+ - 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
+ - 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
+ - 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
+ - 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
17
+
18
+ ### Interface
19
+ - 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
+
3
21
  ## Version 4.2.1
4
22
 
5
23
  ### Ansible
@@ -48,6 +48,17 @@ def _cmk_cleanup_hostname(value):
48
48
  return _cleanup(value)
49
49
 
50
50
 
51
+ def _cmk_password(name):
52
+ """
53
+ Resolve a syncer Checkmk Password name to its password-store ident
54
+ (``cmdbsyncer_<id>``) so rules can reference a stored secret instead of an
55
+ inlined one. Lazily imported to avoid circular imports.
56
+ """
57
+ from application.plugins.checkmk.rule_passwords import password_ident
58
+
59
+ return password_ident(name)
60
+
61
+
51
62
  def syncer_eval(string, default=None):
52
63
  """
53
64
  Evals given object
@@ -139,6 +150,7 @@ _GLOBALS = {
139
150
  'merge_list_of_dicts': merge_list_of_dicts,
140
151
  'cmk_cleanup_tag_id': _cmk_cleanup_tag_id,
141
152
  'cmk_cleanup_hostname': _cmk_cleanup_hostname,
153
+ 'cmk_password': _cmk_password,
142
154
  'get_ip_network': get_ip_network,
143
155
  'get_ip4_interface': get_ip_interface,
144
156
  'get_ip_interface': get_ip_interface,
@@ -350,6 +350,17 @@ def _execute(stats_id, run_params: dict, cwd: Path, provider: str):
350
350
  run_tmp = tempfile.mkdtemp(prefix='cmdbsyncer-ansible-')
351
351
  env['ANSIBLE_LOCAL_TEMP'] = run_tmp
352
352
  env['HOME'] = run_tmp
353
+ # ANSIBLE_LOCAL_TEMP / $HOME only cover the controller-side scratch
354
+ # dir. A task delegated to localhost (delegate_to: localhost) runs
355
+ # over the *local* connection, whose remote_tmp still defaults to
356
+ # ``~/.ansible/tmp`` — and that ``~`` is expanded via the passwd
357
+ # database (the web user's non-writable home, e.g.
358
+ # ``/usr/share/httpd``), ignoring $HOME. That makes such tasks abort
359
+ # "Failed to create temporary directory". Pin remote_tmp to the
360
+ # writable per-run dir (a literal /tmp path, no ``~`` to expand) so
361
+ # local-delegated tasks work; real SSH hosts just create this path
362
+ # under their own /tmp.
363
+ env['ANSIBLE_REMOTE_TEMP'] = run_tmp
353
364
  inventory_path = _write_run_inventory_spec(
354
365
  run_tmp, provider, env['CMDBSYNCER_INVENTORY_MODE'],
355
366
  cmdbsyncer_bin=_cmdbsyncer_bin(),
@@ -47,6 +47,7 @@ class SyncSites(Plugin):
47
47
  'cmk_edition': site.settings_master.cmk_edition,
48
48
  'cmk_version': site.settings_master.cmk_version,
49
49
  'cmk_version_filename': filename,
50
+ 'installation_staging_path': site.settings_master.installation_staging_path or '',
50
51
  'subscription_username': site.settings_master.subscription_username,
51
52
  'subscription_password': site.settings_master.subscription_password,
52
53
  'cmk_user': cmk_user,
@@ -72,6 +72,15 @@ def register_admin_views(admin):
72
72
  menu_icon_value='fa-filter',
73
73
  )
74
74
  )
75
+ admin.add_view(
76
+ CheckmkTestFolderScopeView(
77
+ name="Limit Host Export to Folders",
78
+ category="Checkmk",
79
+ endpoint="checkmk_test_folder_scope",
80
+ menu_icon_type='fa',
81
+ menu_icon_value='fa-folder-open',
82
+ )
83
+ )
75
84
  admin.add_view(
76
85
  CheckmkRuleView(
77
86
  CheckmkRule,
@@ -99,6 +108,15 @@ def register_admin_views(admin):
99
108
  menu_icon_value='fa-cogs',
100
109
  )
101
110
  )
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
+ )
102
120
  admin.add_view(
103
121
  CheckmkTagMngmtView(
104
122
  CheckmkTagMngmt,
@@ -183,32 +201,6 @@ def register_admin_views(admin):
183
201
  menu_icon_value='fa-folder-open',
184
202
  )
185
203
  )
186
- admin.add_sub_category(name="Export Scoping", parent_name="Checkmk")
187
- # add_sub_category does not apply category_icon_classes to sub-categories,
188
- # so set the menu icon directly on the created SubMenuCategory.
189
- _ttp_category = admin.get_category_menu_item("Export Scoping")
190
- if _ttp_category is not None:
191
- _ttp_category.icon_type = 'fa'
192
- _ttp_category.icon_value = 'fa-filter'
193
- admin.add_view(
194
- CheckmkRuleProjectView(
195
- CheckmkRuleProject,
196
- name="Setup Rule Projects",
197
- category="Export Scoping",
198
- menu_icon_type='fa',
199
- menu_icon_value='fa-cubes',
200
- )
201
- )
202
- admin.add_view(
203
- CheckmkTestFolderScopeView(
204
- name="Limit Host Export to Folders",
205
- category="Export Scoping",
206
- endpoint="checkmk_test_folder_scope",
207
- menu_icon_type='fa',
208
- menu_icon_value='fa-folder-open',
209
- )
210
- )
211
-
212
204
  admin.add_view(
213
205
  CheckmkInventorizeAttributesView(
214
206
  CheckmkInventorizeAttributes,
@@ -11,7 +11,7 @@ from pprint import pformat
11
11
 
12
12
  from rich.progress import Progress, SpinnerColumn, TimeElapsedColumn, MofNCompleteColumn
13
13
 
14
- from application import logger
14
+ from application import app, logger
15
15
  from application.models.host import Host
16
16
  from application.plugins.checkmk.cmk2 import CmkException, CMK2
17
17
  from application.helpers.syncer_jinja import render_jinja, get_list
@@ -44,6 +44,25 @@ def normalize_cmk_folder(folder):
44
44
  return normalize_folder(folder)
45
45
 
46
46
 
47
+ def scope_folder(folder):
48
+ """
49
+ Normalise a folder for a folder-scope selection.
50
+
51
+ Like :func:`normalize_cmk_folder`, but also prefixes a leading ``/`` and
52
+ lowercases the path when ``CMK_LOWERCASE_FOLDERNAMES`` is on. Checkmk's API
53
+ folder names are always lowercase, and the export side (``rules.py``) writes
54
+ them that way, so a picked or typed folder compares equal to the folder a
55
+ host actually lands in.
56
+ """
57
+ folder = (folder or '').strip()
58
+ if folder and not folder.startswith('/'):
59
+ folder = '/' + folder
60
+ folder = normalize_cmk_folder(folder)
61
+ if app.config['CMK_LOWERCASE_FOLDERNAMES']:
62
+ folder = folder.lower()
63
+ return folder
64
+
65
+
47
66
  def folder_in_scope(rule_folder, target_folder, recursive=False):
48
67
  """
49
68
  True when ``rule_folder`` should be imported for ``target_folder``.
@@ -90,7 +109,7 @@ def iter_rule_folders():
90
109
  for rule in CheckmkRuleMngmt.objects():
91
110
  for outcome in rule.outcomes:
92
111
  if outcome.folder and '{{' not in outcome.folder:
93
- folders.add(normalize_cmk_folder(outcome.folder))
112
+ folders.add(scope_folder(outcome.folder))
94
113
 
95
114
  for rule in CheckmkRule.objects(enabled=True):
96
115
  for outcome in rule.outcomes:
@@ -99,11 +118,11 @@ def iter_rule_folders():
99
118
  param = (outcome.action_param or '').strip()
100
119
  if not param or '{{' in param:
101
120
  continue
102
- folders.add(normalize_cmk_folder(param))
121
+ folders.add(scope_folder(param))
103
122
 
104
123
  for pool in CheckmkFolderPool.objects():
105
124
  if pool.folder_name:
106
- folders.add(normalize_cmk_folder(pool.folder_name))
125
+ folders.add(scope_folder(pool.folder_name))
107
126
 
108
127
  return sorted(folders)
109
128
 
@@ -14,6 +14,11 @@ from application.plugins.checkmk.rules import CheckmkRulesetRule, DefaultRule
14
14
  from application.plugins.checkmk.inventorize import InventorizeHosts
15
15
  from application.plugins.checkmk.dcd import CheckmkDCDRuleSync
16
16
  from application.plugins.checkmk.passwords import CheckmkPasswordSync
17
+ from application.plugins.checkmk.rule_passwords import (
18
+ rewrite_explicit_passwords,
19
+ preserve_password_macros,
20
+ referenced_password_names,
21
+ )
17
22
  from application.plugins.checkmk.groups import CheckmkGroupSync
18
23
  from application.plugins.checkmk.users import CheckmkUserSync
19
24
  from application.plugins.checkmk.bi import BI
@@ -384,6 +389,7 @@ def import_project_rules_from_folder(project_name, account, folder, # pylint: d
384
389
  found = syncer.fetch_rules_in_folder(folder, recursive=recursive)
385
390
 
386
391
  imported = 0
392
+ password_hints = set()
387
393
  for entry in found:
388
394
  cmk_id = entry.get('cmk_id')
389
395
  if not cmk_id:
@@ -402,18 +408,42 @@ def import_project_rules_from_folder(project_name, account, folder, # pylint: d
402
408
  f"Imported from Checkmk account '{account}', "
403
409
  f"folder '{folder}' (rule {cmk_id})")
404
410
  outcome = entry['outcome']
411
+ # Checkmk masks explicit passwords as '******' on read, so a rule
412
+ # imported here can't carry a usable secret. Rewrite each into a
413
+ # syncer password-store reference (a cmk_password macro); on a
414
+ # re-import keep the macro already on the rule so a renamed one is
415
+ # not reverted to its default hint.
416
+ new_value, hints = rewrite_explicit_passwords(
417
+ outcome.get('value_template', ''))
418
+ if hints:
419
+ old_outcome = rule.outcomes[0] if rule.outcomes else None
420
+ if old_outcome is not None:
421
+ new_value = preserve_password_macros(
422
+ old_outcome.value_template, new_value)
423
+ outcome['value_template'] = new_value
424
+ password_hints.update(referenced_password_names(new_value))
405
425
  rule.outcomes = [RuleMngmtOutcome(**outcome)]
406
426
  rule.primary_ruleset = outcome.get('ruleset', '')
407
427
  rule.save()
408
428
  imported += 1
409
429
 
430
+ if password_hints:
431
+ hint_list = ', '.join(sorted(password_hints))
432
+ print(f"{ColorCodes.WARNING} * Rules reference password store "
433
+ f"entries: {hint_list}. Create matching Checkmk Passwords in "
434
+ f"the syncer and run 'checkmk export_passwords <account>' so "
435
+ f"the reference resolves in the target Checkmk.{ColorCodes.ENDC}")
436
+
410
437
  message = (f"Imported {imported} rule(s) from Checkmk folder "
411
438
  f"'{folder}' into project '{project_name}'")
412
439
  print(f'{ColorCodes.OKGREEN}{message}{ColorCodes.ENDC}')
413
- log.log(message, source="cmk_project_rule_import",
414
- details=[('account', account), ('folder', folder),
415
- ('recursive', str(recursive)),
416
- ('imported', str(imported))])
440
+ details = [('account', account), ('folder', folder),
441
+ ('recursive', str(recursive)),
442
+ ('imported', str(imported))]
443
+ if password_hints:
444
+ details.append(('password_store_entries',
445
+ ', '.join(sorted(password_hints))))
446
+ log.log(message, source="cmk_project_rule_import", details=details)
417
447
  return imported
418
448
  except CmkException as error_obj:
419
449
  # Record the failure, then re-raise so callers can surface it.
@@ -505,6 +505,7 @@ class CheckmkSettings(db.Document):
505
505
  cmk_version = db.StringField()
506
506
  cmk_edition = db.StringField(choices=editions)
507
507
  cmk_version_filename = db.StringField()
508
+ installation_staging_path = db.StringField()
508
509
  inital_password = db.StringField()
509
510
 
510
511
  subscription_username = db.StringField()
@@ -0,0 +1,126 @@
1
+ """
2
+ Bridge Checkmk rule secrets to the syncer's own Checkmk password store.
3
+
4
+ Checkmk masks an ``explicit_password`` in a rule value as ``******`` on every
5
+ read, so a rule imported from one Checkmk instance carries no usable secret and
6
+ would overwrite the target's password with ``******`` on deploy. Instead we
7
+ rewrite every ``explicit_password`` into a ``stored_password`` reference backed
8
+ by a syncer :class:`CheckmkPassword`. The reference is a Jinja macro
9
+ ``{{ cmk_password("name") }}`` that resolves to the entry's stable ident
10
+ ``cmdbsyncer_<id>`` at export time — the same ident the password export writes
11
+ into every Checkmk instance, so a rule deployed to test and prod resolves each
12
+ instance's own stored secret.
13
+ """
14
+ import ast
15
+ import re
16
+
17
+ from application import logger
18
+
19
+ # Checkmk 2.3+ tags password values in a rule's ``value_raw`` as
20
+ # ``('cmk_postprocessed', <kind>, (<ident>, <value>))``. ``explicit_password``
21
+ # inlines the (masked) secret; ``stored_password`` only references a password
22
+ # store ident and carries no secret.
23
+ POSTPROCESSED = 'cmk_postprocessed'
24
+ EXPLICIT_PASSWORD = 'explicit_password'
25
+ STORED_PASSWORD = 'stored_password'
26
+
27
+ # ``{{ cmk_password("name") }}`` — tolerant of surrounding whitespace and either
28
+ # quote style, matching how the macro may be hand-edited on the rule.
29
+ _MACRO_RE = re.compile(r'\{\{\s*cmk_password\([^)]*\)\s*\}\}')
30
+ _MACRO_NAME_RE = re.compile(r'cmk_password\(\s*["\']([^"\']+)["\']\s*\)')
31
+
32
+
33
+ def password_ident(name):
34
+ """
35
+ Resolve a syncer password name to its Checkmk password-store ident.
36
+
37
+ Returns ``cmdbsyncer_<id>`` for the :class:`CheckmkPassword` named ``name``.
38
+ A missing name is logged and resolved to a clearly-invalid ident so only the
39
+ one affected rule fails to deploy (Checkmk rejects the unknown reference)
40
+ instead of aborting the whole export.
41
+ """
42
+ from .models import CheckmkPassword # pylint: disable=import-outside-toplevel
43
+ entry = CheckmkPassword.objects(name=name).first()
44
+ if not entry:
45
+ logger.error(
46
+ "cmk_password(%r): no Checkmk Password with that name in the syncer",
47
+ name)
48
+ return f"cmdbsyncer_missing_{name}"
49
+ return f"cmdbsyncer_{entry.id}"
50
+
51
+
52
+ def _macro_for(hint):
53
+ """Build the ``{{ cmk_password(...) }}`` macro for a default name hint."""
54
+ return '{{ cmk_password("%s") }}' % (hint or 'password')
55
+
56
+
57
+ def rewrite_explicit_passwords(value_raw):
58
+ """
59
+ Rewrite every ``explicit_password`` in a rule ``value_raw`` string into a
60
+ ``stored_password`` password-store reference.
61
+
62
+ Each ``('cmk_postprocessed', 'explicit_password', (<uuid>, '******'))``
63
+ becomes ``('cmk_postprocessed', 'stored_password', ('{{ cmk_password("<hint>") }}', ''))``
64
+ where ``<hint>`` defaults to the value's field name (e.g. ``secret``). The
65
+ hint is only a starting name — rename the macro on the rule and create a
66
+ matching syncer Checkmk Password.
67
+
68
+ Returns ``(new_value_raw, hints)`` where ``hints`` is the list of default
69
+ names inserted (empty, and ``value_raw`` returned unchanged, when the value
70
+ holds no explicit password or cannot be parsed).
71
+ """
72
+ if not isinstance(value_raw, str) or EXPLICIT_PASSWORD not in value_raw:
73
+ return value_raw, []
74
+ try:
75
+ parsed = ast.literal_eval(value_raw)
76
+ except (ValueError, SyntaxError):
77
+ return value_raw, []
78
+
79
+ hints = []
80
+
81
+ def convert(obj, key=None):
82
+ if isinstance(obj, tuple):
83
+ if len(obj) == 3 and obj[0] == POSTPROCESSED \
84
+ and obj[1] == EXPLICIT_PASSWORD:
85
+ hint = key if isinstance(key, str) and key else 'password'
86
+ hints.append(hint)
87
+ return (POSTPROCESSED, STORED_PASSWORD, (_macro_for(hint), ''))
88
+ return tuple(convert(item, key) for item in obj)
89
+ if isinstance(obj, list):
90
+ return [convert(item, key) for item in obj]
91
+ if isinstance(obj, dict):
92
+ return {k: convert(v, k) for k, v in obj.items()}
93
+ return obj
94
+
95
+ new = convert(parsed)
96
+ if not hints:
97
+ return value_raw, []
98
+ return repr(new), hints
99
+
100
+
101
+ def preserve_password_macros(old_value, new_value):
102
+ """
103
+ Carry a rule's existing ``cmk_password`` macros over into a freshly
104
+ re-imported value, so a re-run of the folder import does not revert a
105
+ renamed macro back to its default hint.
106
+
107
+ Positional: when ``old_value`` and ``new_value`` hold the same number of
108
+ macros (the usual case — the rule's password fields did not change), the
109
+ i-th new macro is replaced by the i-th old one. On any mismatch the new
110
+ value is kept unchanged.
111
+ """
112
+ if not old_value:
113
+ return new_value
114
+ old_macros = _MACRO_RE.findall(old_value)
115
+ new_macros = _MACRO_RE.findall(new_value)
116
+ if not old_macros or len(old_macros) != len(new_macros):
117
+ return new_value
118
+ carried = iter(old_macros)
119
+ return _MACRO_RE.sub(lambda _match: next(carried), new_value)
120
+
121
+
122
+ def referenced_password_names(text):
123
+ """Return the set of syncer password names a value references via macro."""
124
+ if not text:
125
+ return set()
126
+ return set(_MACRO_NAME_RE.findall(text))
@@ -0,0 +1,160 @@
1
+ """
2
+ Unit tests for checkmk rule_passwords module
3
+ """
4
+ # pylint: disable=missing-function-docstring,protected-access
5
+ import ast
6
+ import types
7
+ import unittest
8
+ from unittest.mock import patch, MagicMock
9
+
10
+ from application.plugins.checkmk.rule_passwords import (
11
+ rewrite_explicit_passwords,
12
+ preserve_password_macros,
13
+ referenced_password_names,
14
+ password_ident,
15
+ )
16
+
17
+ # The Azure special-agent value the user reported: one explicit_password
18
+ # ('secret') plus an explicit_proxy that must be left untouched.
19
+ AZURE_VALUE = repr({
20
+ 'subscription': 'e602c412-033e-4df7-ac6e-6d1a7942aa71',
21
+ 'tenant': 'ef65c7f0-656e-4646-a2fd-3e45d36a39fd',
22
+ 'client': '743d3780-7009-4865-a894-506d23edf34f',
23
+ 'secret': ('cmk_postprocessed', 'explicit_password',
24
+ ('uuidd3b2cc18-be10-4bfa-85e9-bdc4a27562e0', '******')),
25
+ 'authority': 'global_',
26
+ 'proxy': ('cmk_postprocessed', 'explicit_proxy',
27
+ 'http://134.247.251.203:9092'),
28
+ 'services': ['Microsoft_Network_slash_loadBalancers'],
29
+ 'config': {},
30
+ 'piggyback_vms': 'grouphost',
31
+ })
32
+
33
+
34
+ class TestRewriteExplicitPasswords(unittest.TestCase):
35
+ """rewrite_explicit_passwords"""
36
+
37
+ def test_converts_explicit_and_keeps_proxy(self):
38
+ new_value, hints = rewrite_explicit_passwords(AZURE_VALUE)
39
+ self.assertEqual(hints, ['secret'])
40
+ parsed = ast.literal_eval(new_value)
41
+ # secret is now a stored_password reference with a cmk_password macro
42
+ self.assertEqual(
43
+ parsed['secret'],
44
+ ('cmk_postprocessed', 'stored_password',
45
+ ('{{ cmk_password("secret") }}', '')))
46
+ # the explicit_proxy is untouched
47
+ self.assertEqual(
48
+ parsed['proxy'],
49
+ ('cmk_postprocessed', 'explicit_proxy',
50
+ 'http://134.247.251.203:9092'))
51
+ # unrelated fields survive
52
+ self.assertEqual(parsed['authority'], 'global_')
53
+
54
+ def test_no_password_returns_unchanged(self):
55
+ value = "{'foo': 'bar'}"
56
+ new_value, hints = rewrite_explicit_passwords(value)
57
+ self.assertEqual(new_value, value)
58
+ self.assertEqual(hints, [])
59
+
60
+ def test_unparseable_returns_unchanged(self):
61
+ value = "{'foo': explicit_password(" # contains the marker, won't parse
62
+ new_value, hints = rewrite_explicit_passwords(value)
63
+ self.assertEqual(new_value, value)
64
+ self.assertEqual(hints, [])
65
+
66
+ def test_non_string_returns_unchanged(self):
67
+ new_value, hints = rewrite_explicit_passwords(None)
68
+ self.assertIsNone(new_value)
69
+ self.assertEqual(hints, [])
70
+
71
+ def test_nested_password_uses_field_key_hint(self):
72
+ value = repr({'servers': [
73
+ {'name': 'a',
74
+ 'token': ('cmk_postprocessed', 'explicit_password',
75
+ ('uuidX', '******'))},
76
+ ]})
77
+ new_value, hints = rewrite_explicit_passwords(value)
78
+ self.assertEqual(hints, ['token'])
79
+ parsed = ast.literal_eval(new_value)
80
+ self.assertEqual(
81
+ parsed['servers'][0]['token'],
82
+ ('cmk_postprocessed', 'stored_password',
83
+ ('{{ cmk_password("token") }}', '')))
84
+
85
+ def test_rewritten_value_is_valid_and_round_trips(self):
86
+ # The rewritten value must stay a parseable Python literal whose macro,
87
+ # once a resolver substitutes the ident, yields a valid stored_password.
88
+ new_value, _ = rewrite_explicit_passwords(AZURE_VALUE)
89
+ resolved = new_value.replace('{{ cmk_password("secret") }}',
90
+ 'cmdbsyncer_deadbeef')
91
+ parsed = ast.literal_eval(resolved)
92
+ self.assertEqual(
93
+ parsed['secret'],
94
+ ('cmk_postprocessed', 'stored_password',
95
+ ('cmdbsyncer_deadbeef', '')))
96
+
97
+
98
+ class TestPreservePasswordMacros(unittest.TestCase):
99
+ """preserve_password_macros"""
100
+
101
+ def test_carries_renamed_macro_when_counts_match(self):
102
+ old = "{'secret': ('x', 'y', ('{{ cmk_password(\"azure-prod\") }}', ''))}"
103
+ new = "{'secret': ('x', 'y', ('{{ cmk_password(\"secret\") }}', ''))}"
104
+ merged = preserve_password_macros(old, new)
105
+ self.assertIn('cmk_password("azure-prod")', merged)
106
+ self.assertNotIn('cmk_password("secret")', merged)
107
+
108
+ def test_keeps_new_on_count_mismatch(self):
109
+ old = "{{ cmk_password(\"a\") }} {{ cmk_password(\"b\") }}"
110
+ new = "{{ cmk_password(\"secret\") }}"
111
+ self.assertEqual(preserve_password_macros(old, new), new)
112
+
113
+ def test_empty_old_keeps_new(self):
114
+ new = "{{ cmk_password(\"secret\") }}"
115
+ self.assertEqual(preserve_password_macros('', new), new)
116
+ self.assertEqual(preserve_password_macros(None, new), new)
117
+
118
+
119
+ class TestReferencedPasswordNames(unittest.TestCase):
120
+ """referenced_password_names"""
121
+
122
+ def test_extracts_names_both_quote_styles(self):
123
+ text = ('{{ cmk_password("azure-prod") }} and '
124
+ "{{ cmk_password('other') }}")
125
+ self.assertEqual(referenced_password_names(text),
126
+ {'azure-prod', 'other'})
127
+
128
+ def test_empty_and_none(self):
129
+ self.assertEqual(referenced_password_names(''), set())
130
+ self.assertEqual(referenced_password_names(None), set())
131
+
132
+ def test_no_macro(self):
133
+ self.assertEqual(referenced_password_names("{'k': 'v'}"), set())
134
+
135
+
136
+ class TestPasswordIdent(unittest.TestCase):
137
+ """password_ident resolves against the syncer password store"""
138
+
139
+ def _patch_model(self, entry):
140
+ fake_models = types.ModuleType(
141
+ 'application.plugins.checkmk.models')
142
+ fake_models.CheckmkPassword = MagicMock()
143
+ fake_models.CheckmkPassword.objects.return_value.first.return_value = entry
144
+ return patch.dict(
145
+ 'sys.modules',
146
+ {'application.plugins.checkmk.models': fake_models})
147
+
148
+ def test_found_returns_prefixed_id(self):
149
+ entry = MagicMock()
150
+ entry.id = 'abc123'
151
+ with self._patch_model(entry):
152
+ self.assertEqual(password_ident('azure-prod'), 'cmdbsyncer_abc123')
153
+
154
+ def test_missing_returns_sentinel(self):
155
+ with self._patch_model(None):
156
+ self.assertEqual(password_ident('nope'), 'cmdbsyncer_missing_nope')
157
+
158
+
159
+ if __name__ == '__main__':
160
+ unittest.main()