|
| 1 | +#!/usr/bin/python |
| 2 | + |
| 3 | +DOCUMENTATION = ''' |
| 4 | +module: extract_log |
| 5 | +version_added: "1.0" |
| 6 | +short_description: Unrotate logs and extract information starting from a row with predefined string |
| 7 | +description: The module scans the 'directory' in search of files which filenames start with 'file_prefix'. |
| 8 | +The found files are ungzipped and combined together in the rotation order. After that all lines after |
| 9 | +'start_string' are copied into a file with name 'target_filename'. |
| 10 | +
|
| 11 | +Options: |
| 12 | + - option-name: directory |
| 13 | + description: a name of a directory with target log files |
| 14 | + required: True |
| 15 | + Default: None |
| 16 | +
|
| 17 | + - option-name: file_prefix |
| 18 | + description: a prefix of target log files |
| 19 | + required: True |
| 20 | + Default: None |
| 21 | +
|
| 22 | + - option-name: start_string |
| 23 | + description: a string which last copy is used as a start tag for extracting log information |
| 24 | + required: True |
| 25 | + Default: None |
| 26 | +
|
| 27 | + - option-name: target_filename |
| 28 | + description: a filename of a file where the extracted lines will be saved |
| 29 | + required: True |
| 30 | + Default: None |
| 31 | +
|
| 32 | +''' |
| 33 | + |
| 34 | +EXAMPLES = ''' |
| 35 | +- name: Extract all syslog entries since the last reboot |
| 36 | + extract_log: |
| 37 | + directory: '/var/log' |
| 38 | + file_prefix: 'syslog' |
| 39 | + start_string: 'Initializing cgroup subsys cpuset' |
| 40 | + target_filename: '/tmp/syslog' |
| 41 | +
|
| 42 | +- name: Copy the exctracted syslog entries to the local machine |
| 43 | + fetch: |
| 44 | + src: '/tmp/syslog' |
| 45 | + dest: '/tmp/' |
| 46 | + flat: yes |
| 47 | +
|
| 48 | +- name: Extract all sairedis.rec entries since the last reboot |
| 49 | + extract_log: |
| 50 | + directory: '/var/log/swss' |
| 51 | + file_prefix: 'sairedis.rec' |
| 52 | + start_string: 'recording on:' |
| 53 | + target_filename: '/tmp/sairedis.rec' |
| 54 | +
|
| 55 | +- name: Copy the exctracted sairedis.rec entries to the local machine |
| 56 | + fetch: |
| 57 | + src: '/tmp/sairedis.rec' |
| 58 | + dest: '/tmp/' |
| 59 | + flat: yes |
| 60 | +
|
| 61 | +- name: Extract all swss.rec entries since the last reboot |
| 62 | + extract_log: |
| 63 | + directory: '/var/log/swss' |
| 64 | + file_prefix: 'swss.rec' |
| 65 | + start_string: 'recording started' |
| 66 | + target_filename: '/tmp/swss.rec' |
| 67 | +
|
| 68 | +- name: Copy the exctracted swss.rec entries to the local machine |
| 69 | + fetch: |
| 70 | + src: '/tmp/swss.rec' |
| 71 | + dest: '/tmp/' |
| 72 | + flat: yes |
| 73 | +''' |
| 74 | + |
| 75 | +import os |
| 76 | +import gzip |
| 77 | +import re |
| 78 | +import sys |
| 79 | +from datetime import datetime |
| 80 | +from ansible.module_utils.basic import * |
| 81 | + |
| 82 | +from pprint import pprint |
| 83 | + |
| 84 | + |
| 85 | +def extract_line(directory, filename, target_string): |
| 86 | + path = os.path.join(directory, filename) |
| 87 | + file = None |
| 88 | + if 'gz' in path: |
| 89 | + file = gzip.GzipFile(path) |
| 90 | + else: |
| 91 | + file = open(path) |
| 92 | + result = None |
| 93 | + with file: |
| 94 | + result = [(filename, line) for line in file if target_string in line] |
| 95 | + return result |
| 96 | + |
| 97 | + |
| 98 | +def list_files(directory, prefixname): |
| 99 | + return [filename for filename in os.listdir(directory) if filename.startswith(prefixname)] |
| 100 | + |
| 101 | + |
| 102 | +def extract_number(s): |
| 103 | + ns = re.findall(r'\d+', s) |
| 104 | + if len(ns) == 0: |
| 105 | + return 0 |
| 106 | + else: |
| 107 | + return int(ns[0]) |
| 108 | + |
| 109 | + |
| 110 | +def convert_date(s): |
| 111 | + str_date = re.findall(r'^\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}\.?\d*', s)[0] |
| 112 | + dt = None |
| 113 | + try: |
| 114 | + dt = datetime.strptime(str_date, '%b %d %X.%f') |
| 115 | + except ValueError: |
| 116 | + pass |
| 117 | + if dt is None: |
| 118 | + dt = datetime.strptime(str_date, '%b %d %X') |
| 119 | + |
| 120 | + return dt |
| 121 | + |
| 122 | + |
| 123 | +def comparator(l, r): |
| 124 | + nl = extract_number(l[0]) |
| 125 | + nr = extract_number(r[0]) |
| 126 | + if nl == nr: |
| 127 | + dl = convert_date(l[1]) |
| 128 | + dr = convert_date(r[1]) |
| 129 | + if dl == dr: |
| 130 | + return 0 |
| 131 | + elif dl < dr: |
| 132 | + return -1 |
| 133 | + else: |
| 134 | + return 1 |
| 135 | + elif nl > nr: |
| 136 | + return -1 |
| 137 | + else: |
| 138 | + return 1 |
| 139 | + |
| 140 | + |
| 141 | +def filename_comparator(l, r): |
| 142 | + nl = extract_number(l) |
| 143 | + nr = extract_number(r) |
| 144 | + if nl == nr: |
| 145 | + return 0 |
| 146 | + elif nl > nr: |
| 147 | + return -1 |
| 148 | + else: |
| 149 | + return 1 |
| 150 | + |
| 151 | + |
| 152 | +def extract_latest_line_with_string(directory, filenames, start_string): |
| 153 | + target_lines = [] |
| 154 | + for filename in filenames: |
| 155 | + target_lines.extend(extract_line(directory, filename, start_string)) |
| 156 | + |
| 157 | + sorted_target_lines = sorted(target_lines, cmp=comparator) |
| 158 | + |
| 159 | + return sorted_target_lines[-1] |
| 160 | + |
| 161 | + |
| 162 | +def calculate_files_to_copy(filenames, file_with_latest_line): |
| 163 | + sorted_filenames = sorted(filenames, cmp=filename_comparator) |
| 164 | + files_to_copy = [] |
| 165 | + do_copy = False |
| 166 | + for filename in sorted_filenames: |
| 167 | + if filename == file_with_latest_line: |
| 168 | + do_copy = True |
| 169 | + if do_copy: |
| 170 | + files_to_copy.append(filename) |
| 171 | + |
| 172 | + return files_to_copy |
| 173 | + |
| 174 | + |
| 175 | +def combine_logs_and_save(directory, filenames, start_string, target_filename): |
| 176 | + do_copy = False |
| 177 | + with open(target_filename, 'w') as fp: |
| 178 | + for filename in filenames: |
| 179 | + path = os.path.join(directory, filename) |
| 180 | + file = None |
| 181 | + if 'gz' in path: |
| 182 | + file = gzip.GzipFile(path) |
| 183 | + else: |
| 184 | + file = open(path) |
| 185 | + with file: |
| 186 | + for line in file: |
| 187 | + if line == start_string: |
| 188 | + do_copy = True |
| 189 | + if do_copy: |
| 190 | + fp.write(line) |
| 191 | + |
| 192 | + |
| 193 | +def extract_log(directory, prefixname, target_string, target_filename): |
| 194 | + filenames = list_files(directory, prefixname) |
| 195 | + file_with_latest_line, latest_line = extract_latest_line_with_string(directory, filenames, target_string) |
| 196 | + files_to_copy = calculate_files_to_copy(filenames, file_with_latest_line) |
| 197 | + combine_logs_and_save(directory, files_to_copy, latest_line, target_filename) |
| 198 | + |
| 199 | + |
| 200 | +def main(): |
| 201 | + module = AnsibleModule( |
| 202 | + argument_spec=dict( |
| 203 | + directory=dict(required=True, type='str'), |
| 204 | + file_prefix=dict(required=True, type='str'), |
| 205 | + start_string=dict(required=True, type='str'), |
| 206 | + target_filename=dict(required=True, type='str'), |
| 207 | + ), |
| 208 | + supports_check_mode=False) |
| 209 | + |
| 210 | + p = module.params; |
| 211 | + try: |
| 212 | + extract_log(p['directory'], p['file_prefix'], p['start_string'], p['target_filename']) |
| 213 | + except: |
| 214 | + err = str(sys.exc_info()) |
| 215 | + module.fail_json(msg="Error: %s" % err) |
| 216 | + module.exit_json() |
| 217 | + |
| 218 | + |
| 219 | +if __name__ == '__main__': |
| 220 | + main() |
0 commit comments