ic-code 1.2.8__tar.gz → 1.3.1__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 (256) hide show
  1. {ic_code-1.2.8/src/ic_code.egg-info → ic_code-1.3.1}/PKG-INFO +4 -2
  2. {ic_code-1.2.8 → ic_code-1.3.1}/README.md +2 -1
  3. {ic_code-1.2.8 → ic_code-1.3.1}/docs/cloudflare_configuration_example.md +1 -1
  4. {ic_code-1.2.8 → ic_code-1.3.1}/docs/user_guide.md +1 -1
  5. {ic_code-1.2.8 → ic_code-1.3.1}/pyproject.toml +2 -1
  6. {ic_code-1.2.8 → ic_code-1.3.1}/requirements.txt +1 -0
  7. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/__init__.py +1 -1
  8. ic_code-1.3.1/src/ic/platforms/aws/healthdashboard/__init__.py +1 -0
  9. ic_code-1.3.1/src/ic/platforms/aws/healthdashboard/reboot.py +350 -0
  10. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/lb/info.py +16 -10
  11. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/clb/info.py +5 -3
  12. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/client.py +9 -4
  13. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/cvm/info.py +7 -9
  14. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/lighthouse/info.py +5 -6
  15. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/nat/info.py +7 -7
  16. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/sg/info.py +9 -9
  17. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/tke/info.py +7 -7
  18. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/vpc/info.py +7 -7
  19. {ic_code-1.2.8 → ic_code-1.3.1/src/ic_code.egg-info}/PKG-INFO +4 -2
  20. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic_code.egg-info/SOURCES.txt +2 -0
  21. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic_code.egg-info/requires.txt +1 -0
  22. {ic_code-1.2.8 → ic_code-1.3.1}/LICENSE +0 -0
  23. {ic_code-1.2.8 → ic_code-1.3.1}/MANIFEST.in +0 -0
  24. {ic_code-1.2.8 → ic_code-1.3.1}/SECURITY.md +0 -0
  25. {ic_code-1.2.8 → ic_code-1.3.1}/config/default.yaml +0 -0
  26. {ic_code-1.2.8 → ic_code-1.3.1}/docs/README.md +0 -0
  27. {ic_code-1.2.8 → ic_code-1.3.1}/docs/_templates/configuration.md +0 -0
  28. {ic_code-1.2.8 → ic_code-1.3.1}/docs/_templates/installation.md +0 -0
  29. {ic_code-1.2.8 → ic_code-1.3.1}/docs/_templates/troubleshooting.md +0 -0
  30. {ic_code-1.2.8 → ic_code-1.3.1}/docs/_templates/usage.md +0 -0
  31. {ic_code-1.2.8 → ic_code-1.3.1}/docs/aws/README.md +0 -0
  32. {ic_code-1.2.8 → ic_code-1.3.1}/docs/aws/aws_cli_prd.md +0 -0
  33. {ic_code-1.2.8 → ic_code-1.3.1}/docs/aws/installation.md +0 -0
  34. {ic_code-1.2.8 → ic_code-1.3.1}/docs/ci-workflow-guide.md +0 -0
  35. {ic_code-1.2.8 → ic_code-1.3.1}/docs/config_migration.md +0 -0
  36. {ic_code-1.2.8 → ic_code-1.3.1}/docs/deployment.md +0 -0
  37. {ic_code-1.2.8 → ic_code-1.3.1}/docs/development/README.md +0 -0
  38. {ic_code-1.2.8 → ic_code-1.3.1}/docs/development/cleanup_summary.md +0 -0
  39. {ic_code-1.2.8 → ic_code-1.3.1}/docs/development/development_guide.md +0 -0
  40. {ic_code-1.2.8 → ic_code-1.3.1}/docs/development/import_patterns.md +0 -0
  41. {ic_code-1.2.8 → ic_code-1.3.1}/docs/development/migration_guide.md +0 -0
  42. {ic_code-1.2.8 → ic_code-1.3.1}/docs/gcp/GCP_CONFIGURATION_GUIDE.md +0 -0
  43. {ic_code-1.2.8 → ic_code-1.3.1}/docs/gcp/GCP_SECURITY_BEST_PRACTICES.md +0 -0
  44. {ic_code-1.2.8 → ic_code-1.3.1}/docs/gcp/README.md +0 -0
  45. {ic_code-1.2.8 → ic_code-1.3.1}/docs/general/configuration.md +0 -0
  46. {ic_code-1.2.8 → ic_code-1.3.1}/docs/installation.md +0 -0
  47. {ic_code-1.2.8 → ic_code-1.3.1}/docs/migration.md +0 -0
  48. {ic_code-1.2.8 → ic_code-1.3.1}/docs/migration_guide.md +0 -0
  49. {ic_code-1.2.8 → ic_code-1.3.1}/docs/oci/README.md +0 -0
  50. {ic_code-1.2.8 → ic_code-1.3.1}/docs/oci/installation.md +0 -0
  51. {ic_code-1.2.8 → ic_code-1.3.1}/docs/release-guide.md +0 -0
  52. {ic_code-1.2.8 → ic_code-1.3.1}/docs/security.md +0 -0
  53. {ic_code-1.2.8 → ic_code-1.3.1}/docs/ssh/configuration.md +0 -0
  54. {ic_code-1.2.8 → ic_code-1.3.1}/docs/troubleshooting.md +0 -0
  55. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/analysis_summary.py +0 -0
  56. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/bump-version.sh +0 -0
  57. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/cf_edge_nsg_make/cf_edge_nsg_make.py +0 -0
  58. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/ci-detect-platforms.py +0 -0
  59. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/ci-generate-matrix.py +0 -0
  60. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/cli_usage_analysis.py +0 -0
  61. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/dependency_mapper.py +0 -0
  62. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/deploy.sh +0 -0
  63. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/final_validation_and_documentation.py +0 -0
  64. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/fix_consolidated_imports.py +0 -0
  65. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/migration_execution_and_validation.py +0 -0
  66. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/oracle-policy/user_policy.py +0 -0
  67. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/performance_optimization.py +0 -0
  68. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/setup_venv.py +0 -0
  69. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/test-ci-detection.sh +0 -0
  70. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/unset_env.sh +0 -0
  71. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/validate_config.py +0 -0
  72. {ic_code-1.2.8 → ic_code-1.3.1}/scripts/validate_dependencies.py +0 -0
  73. {ic_code-1.2.8 → ic_code-1.3.1}/setup.cfg +0 -0
  74. {ic_code-1.2.8 → ic_code-1.3.1}/setup.py +0 -0
  75. {ic_code-1.2.8 → ic_code-1.3.1}/src/common/__init__.py +0 -0
  76. {ic_code-1.2.8 → ic_code-1.3.1}/src/common/gather_env.py +0 -0
  77. {ic_code-1.2.8 → ic_code-1.3.1}/src/common/gcp_config_validator.py +0 -0
  78. {ic_code-1.2.8 → ic_code-1.3.1}/src/common/gcp_monitoring.py +0 -0
  79. {ic_code-1.2.8 → ic_code-1.3.1}/src/common/gcp_utils.py +0 -0
  80. {ic_code-1.2.8 → ic_code-1.3.1}/src/common/log.py +0 -0
  81. {ic_code-1.2.8 → ic_code-1.3.1}/src/common/progress_decorator.py +0 -0
  82. {ic_code-1.2.8 → ic_code-1.3.1}/src/common/slack.py +0 -0
  83. {ic_code-1.2.8 → ic_code-1.3.1}/src/common/utils.py +0 -0
  84. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/cli.py +0 -0
  85. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/commands/__init__.py +0 -0
  86. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/commands/config.py +0 -0
  87. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/commands/security.py +0 -0
  88. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/compat/__init__.py +0 -0
  89. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/compat/cli.py +0 -0
  90. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/compat/common.py +0 -0
  91. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/config/__init__.py +0 -0
  92. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/config/cleanup.py +0 -0
  93. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/config/docs_organizer.py +0 -0
  94. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/config/external.py +0 -0
  95. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/config/installer.py +0 -0
  96. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/config/manager.py +0 -0
  97. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/config/migration.py +0 -0
  98. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/config/migration_utils.py +0 -0
  99. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/config/path_manager.py +0 -0
  100. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/config/schema.py +0 -0
  101. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/config/secrets.py +0 -0
  102. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/config/security.py +0 -0
  103. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/core/__init__.py +0 -0
  104. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/core/dependency_validator.py +0 -0
  105. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/core/logging.py +0 -0
  106. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/core/platform_discovery.py +0 -0
  107. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/core/session.py +0 -0
  108. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/core/silence_logging.py +0 -0
  109. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/__init__.py +0 -0
  110. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/__init__.py +0 -0
  111. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/cloudfront/__init__.py +0 -0
  112. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/cloudfront/info.py +0 -0
  113. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/codepipeline/__init__.py +0 -0
  114. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/codepipeline/build.py +0 -0
  115. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/codepipeline/deploy.py +0 -0
  116. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/ec2/__init__.py +0 -0
  117. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/ec2/info.py +0 -0
  118. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/ec2/list_tags.py +0 -0
  119. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/ec2/tag_check.py +0 -0
  120. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/ecs/__init__.py +0 -0
  121. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/ecs/info.py +0 -0
  122. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/ecs/service.py +0 -0
  123. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/ecs/task.py +0 -0
  124. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/eks/__init__.py +0 -0
  125. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/eks/addons.py +0 -0
  126. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/eks/fargate.py +0 -0
  127. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/eks/info.py +0 -0
  128. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/eks/nodes.py +0 -0
  129. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/eks/pods.py +0 -0
  130. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/eks/update_config.py +0 -0
  131. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/fargate/__init__.py +0 -0
  132. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/fargate/info.py +0 -0
  133. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/lb/__init__.py +0 -0
  134. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/lb/list_tags.py +0 -0
  135. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/lb/tag_check.py +0 -0
  136. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/msk/__init__.py +0 -0
  137. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/msk/broker.py +0 -0
  138. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/msk/info.py +0 -0
  139. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/nat/__init__.py +0 -0
  140. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/nat/list_tags.py +0 -0
  141. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/nat/tag_check.py +0 -0
  142. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/profile/__init__.py +0 -0
  143. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/profile/info.py +0 -0
  144. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/rds/__init__.py +0 -0
  145. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/rds/info.py +0 -0
  146. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/rds/list_tags.py +0 -0
  147. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/rds/tag_check.py +0 -0
  148. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/s3/__init__.py +0 -0
  149. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/s3/info.py +0 -0
  150. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/s3/list_tags.py +0 -0
  151. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/s3/tag_check.py +0 -0
  152. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/sg/__init__.py +0 -0
  153. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/sg/info.py +0 -0
  154. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/vpc/__init__.py +0 -0
  155. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/vpc/info.py +0 -0
  156. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/vpc/list_tags.py +0 -0
  157. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/vpc/tag_check.py +0 -0
  158. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/vpn/__init__.py +0 -0
  159. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/aws/vpn/info.py +0 -0
  160. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/README.md +0 -0
  161. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/__init__.py +0 -0
  162. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/account/__init__.py +0 -0
  163. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/account/info.py +0 -0
  164. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/client.py +0 -0
  165. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/dns/__init__.py +0 -0
  166. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/dns/info.py +0 -0
  167. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/dns/list_info.py +0 -0
  168. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/rules/__init__.py +0 -0
  169. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/rules/info.py +0 -0
  170. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/traffic/__init__.py +0 -0
  171. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/traffic/info.py +0 -0
  172. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/waf/__init__.py +0 -0
  173. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/waf/info.py +0 -0
  174. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/zone/__init__.py +0 -0
  175. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/cloudflare/zone/info.py +0 -0
  176. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/README.md +0 -0
  177. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/__init__.py +0 -0
  178. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/billing/__init__.py +0 -0
  179. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/billing/info.py +0 -0
  180. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/billing/mock_data.json +0 -0
  181. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/compute/__init__.py +0 -0
  182. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/compute/info.py +0 -0
  183. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/compute/mock_data.json +0 -0
  184. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/firewall/__init__.py +0 -0
  185. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/firewall/info.py +0 -0
  186. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/functions/__init__.py +0 -0
  187. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/functions/info.py +0 -0
  188. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/gke/__init__.py +0 -0
  189. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/gke/info.py +0 -0
  190. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/gke/mock_data.json +0 -0
  191. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/lb/__init__.py +0 -0
  192. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/lb/info.py +0 -0
  193. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/run/__init__.py +0 -0
  194. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/run/info.py +0 -0
  195. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/sql/__init__.py +0 -0
  196. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/sql/info.py +0 -0
  197. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/storage/__init__.py +0 -0
  198. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/storage/info.py +0 -0
  199. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/vpc/__init__.py +0 -0
  200. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/vpc/info.py +0 -0
  201. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/gcp/vpc/mock_data.json +0 -0
  202. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/README.md +0 -0
  203. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/__init__.py +0 -0
  204. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/common/__init__.py +0 -0
  205. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/common/utils.py +0 -0
  206. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/compartment/__init__.py +0 -0
  207. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/compartment/info.py +0 -0
  208. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/cost/__init__.py +0 -0
  209. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/cost/credit.py +0 -0
  210. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/cost/usage.py +0 -0
  211. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/info/__init__.py +0 -0
  212. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/info/oci_info.py +0 -0
  213. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/lb/__init__.py +0 -0
  214. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/lb/info.py +0 -0
  215. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/nsg/__init__.py +0 -0
  216. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/nsg/info.py +0 -0
  217. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/obj/__init__.py +0 -0
  218. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/obj/info.py +0 -0
  219. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/policy/__init__.py +0 -0
  220. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/policy/info.py +0 -0
  221. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/policy/search.py +0 -0
  222. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/vcn/__init__.py +0 -0
  223. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/vcn/info.py +0 -0
  224. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/vm/__init__.py +0 -0
  225. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/vm/info.py +0 -0
  226. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/volume/__init__.py +0 -0
  227. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/oci/volume/info.py +0 -0
  228. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/ssh/README.md +0 -0
  229. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/ssh/__init__.py +0 -0
  230. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/ssh/auto/__init__.py +0 -0
  231. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/ssh/auto/scan.py +0 -0
  232. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/ssh/auto_ssh.py +0 -0
  233. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/ssh/server/__init__.py +0 -0
  234. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/ssh/server/info.py +0 -0
  235. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/ssh/server_info.py +0 -0
  236. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/__init__.py +0 -0
  237. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/clb/__init__.py +0 -0
  238. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/cvm/__init__.py +0 -0
  239. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/lighthouse/__init__.py +0 -0
  240. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/nat/__init__.py +0 -0
  241. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/profile/__init__.py +0 -0
  242. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/profile/info.py +0 -0
  243. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/sg/__init__.py +0 -0
  244. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/tke/__init__.py +0 -0
  245. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/platforms/tencent/vpc/__init__.py +0 -0
  246. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/security/README.md +0 -0
  247. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/security/__init__.py +0 -0
  248. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/security/config.py +0 -0
  249. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/security/detector.py +0 -0
  250. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/security/guidance.py +0 -0
  251. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/security/hooks.py +0 -0
  252. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/security/patterns.py +0 -0
  253. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic/security/scanner.py +0 -0
  254. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic_code.egg-info/dependency_links.txt +0 -0
  255. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic_code.egg-info/entry_points.txt +0 -0
  256. {ic_code-1.2.8 → ic_code-1.3.1}/src/ic_code.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ic-code
3
- Version: 1.2.8
3
+ Version: 1.3.1
4
4
  Summary: Infrastructure CLI tool for managing AWS, GCP, OCI, CloudFlare, SSH, and more
5
5
  Home-page: https://github.com/dgr009/ic
6
6
  Author: SangYun Kim
@@ -69,6 +69,7 @@ Requires-Dist: pytest-cov<7.0.0,>=5.0.0; extra == "dev"
69
69
  Requires-Dist: black<25.0.0,>=24.0.0; extra == "dev"
70
70
  Requires-Dist: flake8<8.0.0,>=7.0.0; extra == "dev"
71
71
  Requires-Dist: mypy<2.0.0,>=1.10.0; extra == "dev"
72
+ Requires-Dist: pyrefly>=1.2.0; extra == "dev"
72
73
  Requires-Dist: pre-commit<4.0.0,>=3.7.0; extra == "dev"
73
74
  Dynamic: author
74
75
  Dynamic: home-page
@@ -103,6 +104,7 @@ A comprehensive, production-grade Infrastructure Command Line Interface tool for
103
104
  - **Compute**: EC2 instances, ECS services/tasks, EKS clusters/nodes/pods, Fargate profiles
104
105
  - **Storage & DB**: S3 buckets (with tagging compliance checks), RDS instances & clusters
105
106
  - **Networking**: VPC, Subnets, Gateways, Load Balancers (ALB/NLB with listener rules, health check path & target health analysis), Security Groups (with Ingress & Egress rule analysis & tree view), VPN connections
107
+ - **Health & Maintenance**: AWS Health Dashboard EC2 scheduled reboot maintenance check (`ic aws healthdashboard reboot`)
106
108
  - **Integrations**: CloudFront distributions, MSK Kafka brokers, CodePipeline build/deploy status, Profile management
107
109
 
108
110
  ### 2. 🟦 Tencent Cloud Services (Production Ready)
@@ -137,7 +139,7 @@ A comprehensive, production-grade Infrastructure Command Line Interface tool for
137
139
  ### From PyPI (Recommended)
138
140
 
139
141
  ```bash
140
- # Install the latest stable version (v1.2.8+)
142
+ # Install the latest stable version (v1.3.1+)
141
143
  pip install ic-code
142
144
 
143
145
  # Verify installation
@@ -26,6 +26,7 @@ A comprehensive, production-grade Infrastructure Command Line Interface tool for
26
26
  - **Compute**: EC2 instances, ECS services/tasks, EKS clusters/nodes/pods, Fargate profiles
27
27
  - **Storage & DB**: S3 buckets (with tagging compliance checks), RDS instances & clusters
28
28
  - **Networking**: VPC, Subnets, Gateways, Load Balancers (ALB/NLB with listener rules, health check path & target health analysis), Security Groups (with Ingress & Egress rule analysis & tree view), VPN connections
29
+ - **Health & Maintenance**: AWS Health Dashboard EC2 scheduled reboot maintenance check (`ic aws healthdashboard reboot`)
29
30
  - **Integrations**: CloudFront distributions, MSK Kafka brokers, CodePipeline build/deploy status, Profile management
30
31
 
31
32
  ### 2. 🟦 Tencent Cloud Services (Production Ready)
@@ -60,7 +61,7 @@ A comprehensive, production-grade Infrastructure Command Line Interface tool for
60
61
  ### From PyPI (Recommended)
61
62
 
62
63
  ```bash
63
- # Install the latest stable version (v1.2.8+)
64
+ # Install the latest stable version (v1.3.1+)
64
65
  pip install ic-code
65
66
 
66
67
  # Verify installation
@@ -496,5 +496,5 @@ cloudflare:
496
496
  ---
497
497
 
498
498
  **Last Updated:** November 2024
499
- **IC CLI Version:** 1.2.x+
499
+ **IC CLI Version:** 1.3.x+
500
500
  **CloudFlare API Version:** v4
@@ -4,7 +4,7 @@
4
4
 
5
5
  IC는 통합 클라우드 인프라 관리 CLI 도구로, **AWS**, **GCP**, **OCI**, **CloudFlare**, **SSH** 등 다양한 멀티 클라우드 및 서버 리소스를 단일 CLI 환경에서 손쉽게 조회하고 관리할 수 있습니다.
6
6
 
7
- ## 주요 특징 (v1.2.6+)
7
+ ## 주요 특징 (v1.3.1+)
8
8
 
9
9
  - **YAML 기반 보안 설정 시스템**: 민감한 자격 증명(`secrets.yaml`)과 일반 설정(`default.yaml`)의 명확한 분리
10
10
  - **실시간 프로그레스 바**: 리전 및 계정별 리소스 수집 진행 상황 시각화
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ic-code"
7
- version = "1.2.8"
7
+ version = "1.3.1"
8
8
  authors = [
9
9
  {name = "sykim", email = "cruiser594@gmail.com"},
10
10
  ]
@@ -91,6 +91,7 @@ dev = [
91
91
  "black>=24.0.0,<25.0.0",
92
92
  "flake8>=7.0.0,<8.0.0",
93
93
  "mypy>=1.10.0,<2.0.0",
94
+ "pyrefly>=1.2.0",
94
95
  "pre-commit>=3.7.0,<4.0.0",
95
96
  ]
96
97
 
@@ -55,3 +55,4 @@ setuptools>=69.0.0,<76.0.0 # Package building tools
55
55
  # Development and Testing
56
56
  pytest>=8.0.0,<9.0.0
57
57
  pytest-cov>=5.0.0,<7.0.0
58
+ pyrefly>=1.2.0
@@ -4,7 +4,7 @@ IC (Infra Resource Management CLI) - A comprehensive tool for managing cloud inf
4
4
  This package provides CLI tools and libraries for managing AWS, GCP, OCI, CloudFlare, and SSH resources.
5
5
  """
6
6
 
7
- __version__ = "1.2.8"
7
+ __version__ = "1.3.1"
8
8
  __author__ = "SangYun"
9
9
  __email__ = "cruiser594@gmail.com"
10
10
 
@@ -0,0 +1 @@
1
+ # AWS Health Dashboard Service Package
@@ -0,0 +1,350 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ AWS Health Dashboard EC2 Reboot Maintenance Schedule Command
4
+ `ic aws healthdashboard reboot`
5
+
6
+ AWS Health API를 사용하여 EC2 인스턴스의 예정된 재부팅 유지보수 일정
7
+ ("EC2 instance reboot maintenance scheduled")을 조회합니다.
8
+ """
9
+
10
+ import sys
11
+ import argparse
12
+ from datetime import datetime, timezone
13
+ from concurrent.futures import ThreadPoolExecutor, as_completed
14
+
15
+ import boto3
16
+ from botocore.exceptions import BotoCoreError, ClientError
17
+ from rich.console import Console
18
+ from rich.table import Table
19
+ from rich.panel import Panel
20
+ from rich.text import Text
21
+ from rich import box
22
+
23
+ from common.log import log_info_non_console
24
+ from common.progress_decorator import ManualProgress
25
+ from common.utils import get_valid_accounts, get_profiles, DEFINED_REGIONS
26
+
27
+ console = Console()
28
+
29
+ # 대상 타겟 이벤트 코드 정의 (EC2 인스턴스 예정된 재부팅 유지보수)
30
+ TARGET_EVENT_CODES = [
31
+ "AWS_EC2_INSTANCE_REBOOT_MAINTENANCE_SCHEDULED",
32
+ "AWS_EC2_PERSISTENT_INSTANCE_REBOOT_SCHEDULED",
33
+ "AWS_EC2_SCHEDULED_REBOOT"
34
+ ]
35
+
36
+ TARGET_DESCRIPTION_KEYWORD = "ec2 instance reboot maintenance scheduled"
37
+
38
+ def format_datetime_short(dt):
39
+ """datetime 객체를 YY-MM-DD HH:MM UTC 형태로 압축하여 표 가로폭을 최적화합니다."""
40
+ if not dt:
41
+ return "-"
42
+ if isinstance(dt, str):
43
+ return dt
44
+ if dt.tzinfo is None:
45
+ dt = dt.replace(tzinfo=timezone.utc)
46
+
47
+ try:
48
+ return dt.strftime("%Y-%m-%d %H:%M UTC")
49
+ except Exception:
50
+ return str(dt)
51
+
52
+ def get_instance_details(session, instance_id, region):
53
+ """
54
+ EC2 API를 호출하여 인스턴스의 Name 태그, 현재 상태(State), 실제 리전을 조회합니다.
55
+ """
56
+ if not instance_id or not instance_id.startswith("i-"):
57
+ return "-", "unknown", region
58
+
59
+ regions_to_try = [region] if region and region != "global" else DEFINED_REGIONS
60
+
61
+ for reg in regions_to_try:
62
+ try:
63
+ ec2_client = session.client("ec2", region_name=reg)
64
+ response = ec2_client.describe_instances(InstanceIds=[instance_id])
65
+ for reservation in response.get("Reservations", []):
66
+ for instance in reservation.get("Instances", []):
67
+ state = instance.get("State", {}).get("Name", "unknown")
68
+ name = "-"
69
+ for tag in instance.get("Tags", []):
70
+ if tag.get("Key") == "Name":
71
+ name = tag.get("Value", "-")
72
+ break
73
+ return name, state, reg
74
+ except ClientError:
75
+ continue
76
+ except Exception as e:
77
+ log_info_non_console(f"EC2 인스턴스 세부 정보 조회 오류 ({instance_id}/{reg}): {e}")
78
+ continue
79
+
80
+ return "-", "unknown", region
81
+
82
+ def parse_region_from_arn_or_event(event, entity):
83
+ """이벤트 또는 엔티티 ARN에서 AWS Region 정보를 추출합니다."""
84
+ event_region = event.get("region")
85
+ if event_region and event_region != "global":
86
+ return event_region
87
+
88
+ entity_arn = entity.get("entityArn", "")
89
+ if entity_arn and entity_arn.startswith("arn:aws:"):
90
+ parts = entity_arn.split(":")
91
+ if len(parts) >= 4 and parts[3]:
92
+ return parts[3]
93
+
94
+ return "global"
95
+
96
+ def fetch_reboot_events_for_account(account_id, profile_name, include_closed=False):
97
+ """단일 계정에 대해 AWS Health API(us-east-1)를 사용하여 EC2 재부팅 유지보수 이벤트를 수집합니다."""
98
+ log_info_non_console(f"AWS Health API 조회 시작: Account={account_id}, Profile={profile_name}")
99
+
100
+ events_found = []
101
+ error_message = None
102
+
103
+ try:
104
+ session = boto3.Session(profile_name=profile_name, region_name="us-east-1")
105
+ health_client = session.client("health", region_name="us-east-1")
106
+
107
+ status_codes = ["open", "upcoming"]
108
+ if include_closed:
109
+ status_codes.append("closed")
110
+
111
+ filter_params = {
112
+ "services": ["EC2"],
113
+ "eventStatusCodes": status_codes
114
+ }
115
+
116
+ paginator = health_client.get_paginator("describe_events")
117
+ raw_events = []
118
+ for page in paginator.paginate(filter=filter_params):
119
+ for event in page.get("events", []):
120
+ raw_events.append(event)
121
+
122
+ if not raw_events:
123
+ return account_id, profile_name, [], None
124
+
125
+ for event in raw_events:
126
+ event_arn = event.get("arn")
127
+ event_code = event.get("eventTypeCode", "")
128
+ event_status = event.get("statusCode", "unknown")
129
+ start_time = format_datetime_short(event.get("startTime"))
130
+ end_time = format_datetime_short(event.get("endTime"))
131
+ event_region = event.get("region", "global")
132
+
133
+ # 이벤트 설명 상세 조회
134
+ description_text = ""
135
+ try:
136
+ details_res = health_client.describe_event_details(eventArns=[event_arn])
137
+ successful_set = details_res.get("successfulSet", [])
138
+ if successful_set:
139
+ event_desc_obj = successful_set[0].get("eventDescription", {})
140
+ description_text = event_desc_obj.get("latestDescription", "")
141
+ except Exception as de:
142
+ log_info_non_console(f"이벤트 상세 정보 조회 실패 ({event_arn}): {de}")
143
+
144
+ # 정확한 EC2 인스턴스 재부팅 유지보수 일정 필터링
145
+ is_target_code = event_code in TARGET_EVENT_CODES
146
+ is_target_desc = TARGET_DESCRIPTION_KEYWORD in description_text.lower()
147
+
148
+ if not (is_target_code or is_target_desc):
149
+ continue
150
+
151
+ # 영향받는 엔티티(인스턴스) 수집
152
+ affected_entities = []
153
+ try:
154
+ ent_paginator = health_client.get_paginator("describe_affected_entities")
155
+ for ent_page in ent_paginator.paginate(filter={"eventArns": [event_arn]}):
156
+ for entity in ent_page.get("entities", []):
157
+ affected_entities.append(entity)
158
+ except Exception as ee:
159
+ log_info_non_console(f"영향받는 엔티티 조회 실패 ({event_arn}): {ee}")
160
+
161
+ if affected_entities:
162
+ for entity in affected_entities:
163
+ entity_value = entity.get("entityValue", "-") # 인스턴스 ID
164
+ entity_status = entity.get("statusCode", "UNKNOWN") # IMPAIRED, RESOLVED 등
165
+
166
+ parsed_region = parse_region_from_arn_or_event(event, entity)
167
+ inst_name, inst_state, final_region = get_instance_details(session, entity_value, parsed_region)
168
+
169
+ events_found.append({
170
+ "account_id": account_id,
171
+ "profile_name": profile_name,
172
+ "event_code": event_code,
173
+ "event_status": event_status,
174
+ "entity_status": entity_status,
175
+ "start_time": start_time,
176
+ "end_time": end_time,
177
+ "instance_id": entity_value,
178
+ "instance_name": inst_name,
179
+ "instance_state": inst_state,
180
+ "region": final_region,
181
+ "description": description_text
182
+ })
183
+ else:
184
+ events_found.append({
185
+ "account_id": account_id,
186
+ "profile_name": profile_name,
187
+ "event_code": event_code,
188
+ "event_status": event_status,
189
+ "entity_status": "-",
190
+ "start_time": start_time,
191
+ "end_time": end_time,
192
+ "instance_id": "-",
193
+ "instance_name": "-",
194
+ "instance_state": "-",
195
+ "region": event_region,
196
+ "description": description_text
197
+ })
198
+
199
+ except ClientError as e:
200
+ error_code = e.response.get("Error", {}).get("Code", "")
201
+ if error_code == "SubscriptionRequiredException":
202
+ error_message = "AWS Business 또는 Enterprise Support 플랜이 필요합니다."
203
+ elif error_code == "AccessDeniedException":
204
+ error_message = "AWS Health API 접근 권한(health:DescribeEvents 등)이 부족합니다."
205
+ else:
206
+ error_message = f"AWS Health API 오류: {e.response.get('Error', {}).get('Message', str(e))}"
207
+ log_info_non_console(f"Account {account_id} Health API ClientError: {e}")
208
+ except Exception as e:
209
+ error_message = f"오류 발생: {e}"
210
+ log_info_non_console(f"Account {account_id} Health API 예외: {e}")
211
+
212
+ return account_id, profile_name, events_found, error_message
213
+
214
+ def print_results(all_events, account_errors, verbose=False):
215
+ """결과 데이터를 rich 표 및 안내 패널로 출력합니다."""
216
+ console.print()
217
+
218
+ if verbose and account_errors:
219
+ for acct, prof, err in account_errors:
220
+ console.print(f"[bold yellow]⚠️ [{prof} ({acct})][/bold yellow] {err}")
221
+ console.print()
222
+
223
+ if not all_events:
224
+ panel_content = Text()
225
+ panel_content.append("✅ 예정된 EC2 인스턴스 재부팅 일정이 없습니다.\n", style="bold green")
226
+ panel_content.append(" (EC2 instance reboot maintenance scheduled)", style="dim")
227
+ console.print(Panel(panel_content, title="AWS Health Dashboard", border_style="green", box=box.ROUNDED))
228
+ console.print()
229
+ return
230
+
231
+ table = Table(
232
+ title="🚨 EC2 Instance Reboot Maintenance Schedule",
233
+ box=box.ROUNDED,
234
+ header_style="bold cyan",
235
+ show_lines=True,
236
+ expand=True
237
+ )
238
+
239
+ table.add_column("Account", style="cyan", min_width=15, ratio=2)
240
+ table.add_column("Region", style="magenta", min_width=12, ratio=1)
241
+ table.add_column("Instance ID", style="bold yellow", min_width=19, ratio=2)
242
+ table.add_column("Instance Name", style="bold white", min_width=20, ratio=3)
243
+ table.add_column("EC2 State", style="green", min_width=8, ratio=1)
244
+ table.add_column("Entity Status", style="bold white", min_width=10, ratio=1)
245
+ table.add_column("Event Status", style="dim", min_width=9, ratio=1)
246
+ table.add_column("Scheduled Start", style="yellow", min_width=16, ratio=2)
247
+ table.add_column("Scheduled End", style="dim", min_width=16, ratio=2)
248
+
249
+ for item in all_events:
250
+ event_status_display = item["event_status"]
251
+ if item["event_status"].lower() in ["open", "upcoming"]:
252
+ event_status_display = f"[yellow]{item['event_status']}[/yellow]"
253
+ elif item["event_status"].lower() == "closed":
254
+ event_status_display = f"[dim]{item['event_status']}[/dim]"
255
+
256
+ entity_status_str = str(item["entity_status"]).upper()
257
+ if entity_status_str == "RESOLVED":
258
+ entity_status_display = "[bold green]RESOLVED[/bold green]"
259
+ elif entity_status_str in ["IMPAIRED", "IMPACTED"]:
260
+ entity_status_display = "[bold red]IMPAIRED[/bold red]"
261
+ else:
262
+ entity_status_display = entity_status_str
263
+
264
+ state_str = str(item["instance_state"]).lower()
265
+ if state_str == "running":
266
+ state_display = f"[bold green]{item['instance_state']}[/bold green]"
267
+ elif state_str in ["stopped", "stopping"]:
268
+ state_display = f"[bold yellow]{item['instance_state']}[/bold yellow]"
269
+ elif state_str == "terminated":
270
+ state_display = f"[bold red]{item['instance_state']}[/bold red]"
271
+ else:
272
+ state_display = item["instance_state"]
273
+
274
+ table.add_row(
275
+ f"{item['profile_name']}\n({item['account_id']})",
276
+ item["region"],
277
+ item["instance_id"],
278
+ item["instance_name"],
279
+ state_display,
280
+ entity_status_display,
281
+ event_status_display,
282
+ item["start_time"],
283
+ item["end_time"]
284
+ )
285
+
286
+ console.print(table)
287
+ console.print(f"\n총 [bold yellow]{len(all_events)}[/bold yellow]건의 유지보수 일정이 확인되었습니다.")
288
+ console.print()
289
+
290
+ def main(args, config=None):
291
+ """ic aws healthdashboard reboot 실행 엔트리 포인트"""
292
+ all_accounts_flag = getattr(args, 'all_accounts', False)
293
+ account_input = args.account if hasattr(args, 'account') and args.account else None
294
+
295
+ # --all-accounts 플래그가 지정되었거나 계정이 지정되지 않은 경우, 로컬 AWS 프로파일 전체 수집
296
+ if all_accounts_flag or not account_input:
297
+ profiles_dict = get_profiles()
298
+ valid_accounts = []
299
+ if profiles_dict:
300
+ for acct_id, prof_name in profiles_dict.items():
301
+ # 'default' 제외하고 추가하거나 default도 활성화된 프로필이면 유지
302
+ valid_accounts.append((acct_id, prof_name))
303
+ else:
304
+ valid_accounts = get_valid_accounts(None)
305
+ else:
306
+ valid_accounts = get_valid_accounts(account_input)
307
+
308
+ if not valid_accounts:
309
+ console.print("❌ 조회를 진행할 AWS 계정을 찾을 수 없습니다. (.env 또는 AWS 프로파일 설정을 확인하세요)")
310
+ sys.exit(1)
311
+
312
+ include_closed = getattr(args, 'all_status', False)
313
+ all_events = []
314
+ account_errors = []
315
+
316
+ total_ops = len(valid_accounts)
317
+ with ManualProgress("Checking AWS Health Dashboard for EC2 reboot schedules across all profiles...", total=total_ops) as progress:
318
+ with ThreadPoolExecutor(max_workers=min(len(valid_accounts), 15)) as executor:
319
+ futures = {
320
+ executor.submit(fetch_reboot_events_for_account, acct, prof, include_closed): (acct, prof)
321
+ for acct, prof in valid_accounts
322
+ }
323
+
324
+ for future in as_completed(futures):
325
+ acct, prof = futures[future]
326
+ try:
327
+ acct_id, prof_name, events, error_msg = future.result()
328
+ if error_msg:
329
+ account_errors.append((acct_id, prof_name, error_msg))
330
+ if events:
331
+ all_events.extend(events)
332
+ progress.update(f"Completed {prof_name} ({acct_id})", advance=1)
333
+ except Exception as e:
334
+ account_errors.append((acct, prof, f"조회 중 예외 발생: {e}"))
335
+ progress.update(f"Failed {prof} ({acct})", advance=1)
336
+
337
+ print_results(all_events, account_errors, verbose=getattr(args, 'verbose', False))
338
+
339
+ def add_arguments(parser):
340
+ """Command line arguments definition"""
341
+ parser.add_argument('-a', '--account', help='특정 AWS 계정 ID 또는 프로파일 지정 (구분자: ,)')
342
+ parser.add_argument('-A', '--all-accounts', '--all-profiles', action='store_true', help='로컬 AWS 프로필에 등록된 모든 계정 전체 조회')
343
+ parser.add_argument('--all-status', action='store_true', help='이미 종료된(closed) 과거 일정까지 포함하여 조회')
344
+ parser.add_argument('-v', '--verbose', action='store_true', help='상세 에러 로그 및 정보 출력')
345
+
346
+ if __name__ == "__main__":
347
+ parser = argparse.ArgumentParser(description="AWS Health Dashboard EC2 Reboot Schedule Check")
348
+ add_arguments(parser)
349
+ args = parser.parse_args()
350
+ main(args)
@@ -181,18 +181,22 @@ def print_lb_table(all_rows):
181
181
  keys = ["account", "region", "lb_name", "type", "scheme", "listener", "target_group", "hc_path", "targets", "health"]
182
182
 
183
183
  for h in headers:
184
- style = {}
185
- if h == "Account": style = {"style": "bold magenta"}
186
- elif h == "Region": style = {"style": "bold cyan"}
187
- elif h == "Health": style = {"justify": "center"}
188
- table.add_column(h, **style)
189
-
190
- last_account, last_region, last_lb, last_tg = None, None, None, None
184
+ if h == "Account":
185
+ table.add_column(h, style="bold magenta")
186
+ elif h == "Region":
187
+ table.add_column(h, style="bold cyan")
188
+ elif h == "Health":
189
+ table.add_column(h, justify="center")
190
+ else:
191
+ table.add_column(h)
192
+
193
+ last_account, last_region, last_lb, last_listener, last_tg = None, None, None, None, None
191
194
  for i, row in enumerate(all_rows):
192
195
  account_changed = row["account"] != last_account
193
196
  region_changed = row["region"] != last_region
194
197
  lb_changed = row["lb_name"] != last_lb or account_changed or region_changed
195
- tg_changed = row["target_group"] != last_tg or lb_changed
198
+ listener_changed = row["listener"] != last_listener or lb_changed
199
+ tg_changed = row["target_group"] != last_tg or listener_changed
196
200
 
197
201
  if i > 0:
198
202
  if account_changed:
@@ -201,6 +205,8 @@ def print_lb_table(all_rows):
201
205
  table.add_row("", *[Rule(style="dim") for _ in headers[1:]])
202
206
  elif lb_changed:
203
207
  table.add_row("", "", *[Rule(style="dim") for _ in headers[2:]])
208
+ elif listener_changed:
209
+ table.add_row("", "", "", "", "", *[Rule(style="dim") for _ in headers[5:]])
204
210
  elif tg_changed:
205
211
  table.add_row("", "", "", "", "", "", *[Rule(style="dim") for _ in headers[6:]])
206
212
 
@@ -210,7 +216,7 @@ def print_lb_table(all_rows):
210
216
  display_values.append(row["lb_name"] if lb_changed else "")
211
217
  display_values.append(row["type"] if lb_changed else "")
212
218
  display_values.append(row["scheme"] if lb_changed else "")
213
- display_values.append(row["listener"] if lb_changed else "")
219
+ display_values.append(row["listener"] if listener_changed else "")
214
220
  display_values.append(row["target_group"] if tg_changed else "")
215
221
  display_values.append(row["hc_path"] if tg_changed else "")
216
222
  display_values.append(row["targets"])
@@ -218,7 +224,7 @@ def print_lb_table(all_rows):
218
224
 
219
225
  table.add_row(*display_values)
220
226
 
221
- last_account, last_region, last_lb, last_tg = row["account"], row["region"], row["lb_name"], row["target_group"]
227
+ last_account, last_region, last_lb, last_listener, last_tg = row["account"], row["region"], row["lb_name"], row["listener"], row["target_group"]
222
228
 
223
229
  console.print(table)
224
230
 
@@ -90,15 +90,17 @@ def fetch_clb_one_account_region(
90
90
  req = models.DescribeLoadBalancersRequest()
91
91
  req.Offset = offset
92
92
  req.Limit = limit
93
- if name_filter:
94
- req.LoadBalancerName = name_filter
95
-
96
93
  resp = client.DescribeLoadBalancers(req)
97
94
  lbs = resp.LoadBalancerSet or []
98
95
 
99
96
  for lb in lbs:
100
97
  lb_id = lb.LoadBalancerId or "-"
101
98
  lb_name = lb.LoadBalancerName or "-"
99
+
100
+ if name_filter:
101
+ nf = name_filter.lower()
102
+ if nf not in lb_name.lower() and nf not in lb_id.lower():
103
+ continue
102
104
  lb_type_raw = lb.LoadBalancerType or "-"
103
105
  lb_type = _LB_TYPE_COLORS.get(lb_type_raw, lb_type_raw)
104
106
  vips = ", ".join(lb.LoadBalancerVips or []) or "-"
@@ -51,6 +51,9 @@ try:
51
51
  TENCENT_SDK_AVAILABLE = True
52
52
  except ImportError:
53
53
  TENCENT_SDK_AVAILABLE = False
54
+ class TencentCloudSDKException(Exception):
55
+ code: str = ""
56
+ message: str = ""
54
57
 
55
58
  # 기본 리전 목록
56
59
  TENCENT_DEFAULT_REGIONS = ["ap-seoul", "ap-tokyo"]
@@ -315,12 +318,14 @@ def get_accounts(account_filter: Optional[str] = None) -> List[Dict[str, str]]:
315
318
  if not account_filter:
316
319
  return all_accounts
317
320
 
318
- # 이름 또는 ID 기반 필터
319
- filter_set = {f.strip().lower() for f in account_filter.split(",") if f.strip()}
321
+ # 이름 또는 ID 기반 필터 (부분 일치 / substring matching)
322
+ filters = [f.strip().lower() for f in account_filter.split(",") if f.strip()]
320
323
  filtered = [
321
324
  a for a in all_accounts
322
- if a.get("name", "").lower() in filter_set
323
- or str(a.get("id", "")).lower() in filter_set
325
+ if any(
326
+ f in a.get("name", "").lower() or f in str(a.get("id", "")).lower()
327
+ for f in filters
328
+ )
324
329
  ]
325
330
  return filtered
326
331
 
@@ -98,12 +98,6 @@ def fetch_cvm_one_account_region(
98
98
  req.Offset = offset
99
99
  req.Limit = limit
100
100
 
101
- if name_filter:
102
- filter_obj = models.Filter()
103
- filter_obj.Name = "instance-name"
104
- filter_obj.Values = [f"*{name_filter}*"]
105
- req.Filters = [filter_obj]
106
-
107
101
  resp = client.DescribeInstances(req)
108
102
  instances = resp.InstanceSet or []
109
103
 
@@ -113,6 +107,13 @@ def fetch_cvm_one_account_region(
113
107
  if state.upper() == "TERMINATED":
114
108
  continue
115
109
 
110
+ inst_name = inst.InstanceName or inst.InstanceId or "-"
111
+
112
+ if name_filter:
113
+ nf = name_filter.lower()
114
+ if nf not in inst_name.lower() and nf not in (inst.InstanceId or "").lower():
115
+ continue
116
+
116
117
  # 스펙 정보
117
118
  itype = inst.InstanceType or "-"
118
119
  cpu = str(inst.CPU) if inst.CPU is not None else "?"
@@ -145,9 +146,6 @@ def fetch_cvm_one_account_region(
145
146
  for sg in (inst.SecurityGroupIds or [])
146
147
  ) or "-"
147
148
 
148
- # 태그에서 Name 추출
149
- inst_name = inst.InstanceName or inst.InstanceId or "-"
150
-
151
149
  rows.append({
152
150
  "account": account_name,
153
151
  "region": region,
@@ -89,18 +89,17 @@ def fetch_lighthouse_one_account_region(
89
89
  req.Offset = offset
90
90
  req.Limit = limit
91
91
 
92
- if name_filter:
93
- f = models.Filter()
94
- f.Name = "instance-name"
95
- f.Values = [f"*{name_filter}*"]
96
- req.Filters = [f]
97
-
98
92
  resp = client.DescribeInstances(req)
99
93
  instances = resp.InstanceSet or []
100
94
 
101
95
  for inst in instances:
102
96
  inst_id = inst.InstanceId or "-"
103
97
  inst_name = inst.InstanceName or inst_id
98
+
99
+ if name_filter:
100
+ nf = name_filter.lower()
101
+ if nf not in inst_name.lower() and nf not in inst_id.lower():
102
+ continue
104
103
  state = color_state(inst.InstanceState or "UNKNOWN")
105
104
 
106
105
  # CPU / Memory 스펙 정보
@@ -32,7 +32,8 @@ except ImportError:
32
32
 
33
33
  from ic.platforms.tencent.client import (
34
34
  get_accounts, get_credential_for_account, get_tencent_regions,
35
- make_client_profile, check_sdk_available, TENCENT_SDK_AVAILABLE
35
+ make_client_profile, check_sdk_available, TENCENT_SDK_AVAILABLE,
36
+ TencentCloudSDKException
36
37
  )
37
38
 
38
39
  console = Console()
@@ -78,18 +79,17 @@ def fetch_nat_one_account_region(
78
79
  req = models.DescribeNatGatewaysRequest()
79
80
  req.Offset = offset
80
81
  req.Limit = limit
81
- if name_filter:
82
- f = models.Filter()
83
- f.Name = "nat-gateway-name"
84
- f.Values = [f"*{name_filter}*"]
85
- req.Filters = [f]
86
-
87
82
  resp = client.DescribeNatGateways(req)
88
83
  nats = resp.NatGatewaySet or []
89
84
 
90
85
  for nat in nats:
91
86
  nat_id = nat.NatGatewayId or "-"
92
87
  nat_name = nat.NatGatewayName or nat_id
88
+
89
+ if name_filter:
90
+ nf = name_filter.lower()
91
+ if nf not in nat_name.lower() and nf not in nat_id.lower():
92
+ continue
93
93
  state = color_state(nat.State or "UNKNOWN")
94
94
  vpc_id = nat.VpcId or "-"
95
95