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

1#!/usr/bin/env python3 

2""" 

3Find assets affected by an incident or change. 

4 

5Searches for assets based on criteria like location, type, or IQL query. 

6Useful for impact analysis. 

7Requires JSM Premium license or Assets license. 

8 

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""" 

14 

15import argparse 

16import sys 

17import json 

18from pathlib import Path 

19 

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) 

24 

25from config_manager import get_jira_client 

26 

27 

28def find_affected_assets(iql: str, object_type: str = None): 

29 """ 

30 Find assets matching criteria. 

31 

32 Args: 

33 iql: IQL query string 

34 object_type: Optional object type filter 

35 

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) 

44 

45 return client.find_assets_by_criteria(iql) 

46 

47 

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}" 

52 

53 output = [f"Affected Assets ({len(assets)} found):\n"] 

54 output.append(f"Search Criteria: {criteria}\n") 

55 

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')}") 

59 

60 if 'objectType' in asset: 

61 output.append(f"Type: {asset['objectType'].get('name', 'N/A')}") 

62 

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}") 

70 

71 output.append("") 

72 

73 return "\n".join(output) 

74 

75 

76def format_json(assets: list) -> str: 

77 """Format affected assets as JSON.""" 

78 return json.dumps(assets, indent=2) 

79 

80 

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') 

93 

94 args = parser.parse_args() 

95 

96 try: 

97 assets = find_affected_assets(args.iql, args.type) 

98 

99 if args.output == 'json': 

100 print(format_json(assets)) 

101 else: 

102 print(format_text(assets, args.iql)) 

103 

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) 

109 

110 

111if __name__ == "__main__": 

112 main()