Coverage for scripts / find_affected_assets.py: 22%
54 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-25 18:08 -0500
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-25 18:08 -0500
1#!/usr/bin/env python3
2"""
3Find assets affected by an incident or change.
5Searches for assets based on criteria like location, type, or IQL query.
6Useful for impact analysis.
7Requires JSM Premium license or Assets license.
9Usage:
10 find_affected_assets.py --iql 'Location="DC-1"'
11 find_affected_assets.py --iql 'objectType="Server" AND Status="Active"'
12 find_affected_assets.py --type Server --iql 'Location="DC-1"'
13"""
15import argparse
16import sys
17import json
18from pathlib import Path
20# Add shared lib to path
21shared_lib_path = str(Path(__file__).parent.parent.parent.parent / 'shared' / 'scripts' / 'lib')
22if shared_lib_path not in sys.path:
23 sys.path.insert(0, shared_lib_path)
25from config_manager import get_jira_client
28def find_affected_assets(iql: str, object_type: str = None):
29 """
30 Find assets matching criteria.
32 Args:
33 iql: IQL query string
34 object_type: Optional object type filter
36 Returns:
37 List of matching assets
38 """
39 with get_jira_client() as client:
40 # Check license first
41 if not client.has_assets_license():
42 print("ERROR: Assets/Insight not available. Requires JSM Premium license.", file=sys.stderr)
43 sys.exit(1)
45 return client.find_assets_by_criteria(iql)
48def format_text(assets: list, criteria: str) -> str:
49 """Format affected assets as human-readable text."""
50 if not assets:
51 return f"No assets found matching criteria: {criteria}"
53 output = [f"Affected Assets ({len(assets)} found):\n"]
54 output.append(f"Search Criteria: {criteria}\n")
56 for asset in assets:
57 output.append(f"Key: {asset.get('objectKey', 'N/A')}")
58 output.append(f"Label: {asset.get('label', 'N/A')}")
60 if 'objectType' in asset:
61 output.append(f"Type: {asset['objectType'].get('name', 'N/A')}")
63 if 'attributes' in asset:
64 for attr in asset['attributes'][:2]: # Show first 2 attributes
65 attr_name = attr.get('objectTypeAttribute', {}).get('name', 'Unknown')
66 values = attr.get('objectAttributeValues', [])
67 if values:
68 attr_value = values[0].get('value', 'N/A')
69 output.append(f" {attr_name}: {attr_value}")
71 output.append("")
73 return "\n".join(output)
76def format_json(assets: list) -> str:
77 """Format affected assets as JSON."""
78 return json.dumps(assets, indent=2)
81def main():
82 parser = argparse.ArgumentParser(
83 description="Find assets affected by incident or change",
84 formatter_class=argparse.RawDescriptionHelpFormatter,
85 epilog=__doc__
86 )
87 parser.add_argument('--iql', required=True,
88 help='IQL query string for asset criteria')
89 parser.add_argument('--type', help='Optional object type filter')
90 parser.add_argument('--output', choices=['text', 'json'], default='text',
91 help='Output format (default: text)')
92 parser.add_argument('--profile', help='JIRA profile to use')
94 args = parser.parse_args()
96 try:
97 assets = find_affected_assets(args.iql, args.type)
99 if args.output == 'json':
100 print(format_json(assets))
101 else:
102 print(format_text(assets, args.iql))
104 except SystemExit:
105 raise
106 except Exception as e:
107 print(f"Error finding affected assets: {str(e)}", file=sys.stderr)
108 sys.exit(1)
111if __name__ == "__main__":
112 main()