[976] | 1 |
|
---|
| 2 | import csv
|
---|
| 3 | import sys
|
---|
| 4 | import datetime
|
---|
| 5 | import re
|
---|
| 6 |
|
---|
| 7 | from ....builtins import *
|
---|
| 8 |
|
---|
| 9 | from ...exceptions import *
|
---|
| 10 | from ...expressions import *
|
---|
| 11 | from ...streams import *
|
---|
| 12 |
|
---|
| 13 | from ....dependencies.odict import OrderedDict
|
---|
| 14 |
|
---|
| 15 | csv.field_size_limit(2147483647) # increase csv field size limit; default is 131072 which can choke on some NBU jobs
|
---|
| 16 |
|
---|
| 17 | ##
|
---|
| 18 | ## bpdbjobs has one record per-line
|
---|
| 19 | ##
|
---|
| 20 | def stream(stream):
|
---|
| 21 |
|
---|
| 22 | return NewLineStream(stream)
|
---|
| 23 |
|
---|
| 24 |
|
---|
| 25 | ##
|
---|
| 26 | ## NBU Job Types
|
---|
| 27 | ##
|
---|
| 28 | job_types = {
|
---|
| 29 | '0' : 'backup',
|
---|
| 30 | '1' : 'archive',
|
---|
| 31 | '2' : 'restore',
|
---|
| 32 | '3' : 'verify',
|
---|
| 33 | '4' : 'duplication', # Changed to 'duplicate' in NBU 6.5
|
---|
| 34 | '5' : 'import',
|
---|
| 35 | '6' : 'dbbackup', # Changed to 'catalog backup' in NBU 6.5
|
---|
| 36 | '7' : 'vault',
|
---|
| 37 | '8' : 'label',
|
---|
| 38 | '9' : 'erase',
|
---|
| 39 | '10' : 'tpreq', # Changed to 'tape request' in NBU 6.5
|
---|
| 40 | '11' : 'tpclean', # Changed to 'clean' in NBU 6.5
|
---|
| 41 | '12' : 'tpformat', # Changed to 'format tape' in NBU 6.5
|
---|
| 42 | '13' : 'vmphyinv', # Changed to 'physical inventory' in NBU 6.5
|
---|
| 43 | '14' : 'dqts', # Changed to 'qualification' in NBU 6.5
|
---|
| 44 | '15' : 'dbrecover', # Not documented in NBU 6.5
|
---|
| 45 | '16' : 'mcontents', # Not documented in NBU 6.5
|
---|
| 46 | '17' : 'image_cleanup', # DSSU image cleanup
|
---|
| 47 | }
|
---|
| 48 |
|
---|
| 49 |
|
---|
| 50 | ##
|
---|
| 51 | ## NBU Job States
|
---|
| 52 | ##
|
---|
| 53 | job_states = {
|
---|
| 54 | '0' : 'queued',
|
---|
| 55 | '1' : 'active',
|
---|
| 56 | '2' : 'wait for retry',
|
---|
| 57 | '3' : 'done',
|
---|
| 58 | '4' : 'suspended',
|
---|
| 59 | '5' : 'incomplete',
|
---|
| 60 | '6' : '6',
|
---|
| 61 | '7' : '7',
|
---|
| 62 | }
|
---|
| 63 |
|
---|
| 64 |
|
---|
| 65 | ##
|
---|
| 66 | ## NBU Schedule Types
|
---|
| 67 | ##
|
---|
| 68 | schedule_types = {
|
---|
| 69 | '0' : 'FULL',
|
---|
| 70 | '1' : 'INCR',
|
---|
| 71 | '2' : 'UBAK',
|
---|
| 72 | '3' : 'UARC',
|
---|
| 73 | '4' : 'CINC',
|
---|
| 74 | }
|
---|
| 75 |
|
---|
| 76 |
|
---|
| 77 | ##
|
---|
| 78 | ## NBU Job Subtypes
|
---|
| 79 | ##
|
---|
| 80 | subtypes = {
|
---|
| 81 | '0' : 'immediate',
|
---|
| 82 | '1' : 'scheduled',
|
---|
| 83 | '2' : 'user-initiated',
|
---|
| 84 | '3' : 'quick erase',
|
---|
| 85 | '4' : 'long erase',
|
---|
| 86 | }
|
---|
| 87 |
|
---|
| 88 |
|
---|
| 89 | ##
|
---|
| 90 | ## NBU Retention Units
|
---|
| 91 | ##
|
---|
| 92 | retention_units = {
|
---|
| 93 | '0' : 'Unknown',
|
---|
| 94 | '1' : 'Days',
|
---|
| 95 | }
|
---|
| 96 |
|
---|
| 97 |
|
---|
| 98 | ##
|
---|
| 99 | ## NBU Class / Policy Types
|
---|
| 100 | ##
|
---|
| 101 | ## http://seer.entsupport.symantec.com/docs/261264.htm
|
---|
| 102 | ##
|
---|
| 103 | class_types = {
|
---|
| 104 | '0' : 'Standard',
|
---|
| 105 | '1' : 'Proxy', # NetBackup internal setting
|
---|
| 106 | '2' : 'Non-Standard', # NetBackup internal setting
|
---|
| 107 | '3' : 'Apollo-wbak', # NetBackup DataCenter only
|
---|
| 108 | '4' : 'Oracle',
|
---|
| 109 | '5' : 'Any policy type',
|
---|
| 110 | '6' : 'Informix-On-BAR',
|
---|
| 111 | '7' : 'Sybase',
|
---|
| 112 | '8' : 'MS-Sharepoint', # NetBackup Server / Enterprise Server only
|
---|
| 113 | '9' : 'MS-Windows', # Not active, use MS-Windows-NT (13) for Windows 2000/NT/XP/2003 clients.
|
---|
| 114 | '10' : 'NetWare',
|
---|
| 115 | '11' : 'DataTools-SQL-BackTrack',
|
---|
| 116 | '12' : 'Auspex-FastBackup',
|
---|
| 117 | '13' : 'MS-Windows-NT',
|
---|
| 118 | '14' : 'OS/2',
|
---|
| 119 | '15' : 'MS-SQL-Server',
|
---|
| 120 | '16' : 'MS-Exchange-Server',
|
---|
| 121 | '17' : 'SAP', # NetBackup Server / Enterprise Server only
|
---|
| 122 | '18' : 'DB2', # NetBackup Server / Enterprise Server only
|
---|
| 123 | '19' : 'NDMP',
|
---|
| 124 | '20' : 'FlashBackup', # NetBackup Server / Enterprise Server only
|
---|
| 125 | '21' : 'Split-Mirror', # NetBackup Server / Enterprise Server only
|
---|
| 126 | '22' : 'AFS', # NetBackup Server / Enterprise Server only
|
---|
| 127 | '23' : 'DFS', # Not an active policy type.
|
---|
| 128 | '24' : 'DataStore',
|
---|
| 129 | '25' : 'Lotus-Notes',
|
---|
| 130 | '26' : 'NCR-Teradata', # No longer supported, contact NCR to see if they can provide an extension.
|
---|
| 131 | '27' : 'OpenVMS',
|
---|
| 132 | '28' : 'MPE/iX',
|
---|
| 133 | '29' : 'FlashBackup-Windows', # NetBackup Server / Enterprise Server only
|
---|
| 134 | '30' : 'Vault',
|
---|
| 135 | '31' : 'BE-MS-SQL-Server',
|
---|
| 136 | '32' : 'BE-MS-Exchange-Server',
|
---|
| 137 | '33' : 'Macintosh', # Not active, use Standard (0) for Macintosh OSX clients.
|
---|
| 138 | '34' : 'Disk Staging', # NetBackup Server / Enterprise Server only
|
---|
| 139 | '35' : 'NBU-Catalog', # NetBackup 6.0 only
|
---|
| 140 | }
|
---|
| 141 |
|
---|
| 142 | policy_types = class_types
|
---|
| 143 |
|
---|
| 144 |
|
---|
| 145 | ##
|
---|
| 146 | ## Operation Types
|
---|
| 147 | ##
|
---|
| 148 | operation_types = {
|
---|
| 149 | '0' : 'mount',
|
---|
| 150 | '1' : 'position',
|
---|
| 151 | '2' : 'connect',
|
---|
| 152 | '3' : 'write',
|
---|
| 153 | '4' : 'vault initialize',
|
---|
| 154 | '5' : 'vault duplication',
|
---|
| 155 | '6' : 'vault duplication complete',
|
---|
| 156 | '7' : 'vault catalog backup',
|
---|
| 157 | '8' : 'vault eject',
|
---|
| 158 | '9' : '9',
|
---|
| 159 | '10' : 'report',
|
---|
| 160 | '11' : 'duplicate',
|
---|
| 161 | '12' : 'import',
|
---|
| 162 | '13' : 'verify',
|
---|
| 163 | '14' : 'restore',
|
---|
| 164 | '15' : 'catalog-backup',
|
---|
| 165 | '16' : 'vault',
|
---|
| 166 | '17' : 'label',
|
---|
| 167 | '18' : 'erase',
|
---|
| 168 | }
|
---|
| 169 |
|
---|
| 170 |
|
---|
| 171 | ##
|
---|
| 172 | ## Parse a bpdbjobs record
|
---|
| 173 | ##
|
---|
| 174 | ## bpdbjobs -report -all_columns
|
---|
| 175 | ##
|
---|
| 176 | def parse(record, format='bpdbjobs -report -all_columns', version=None, tz=None):
|
---|
| 177 |
|
---|
| 178 | if format in ['bpdbjobs -report -all_columns', 'bpdbjobs -report -most_columns']:
|
---|
| 179 |
|
---|
| 180 | record = record.replace('\r', '') # compensate for embedded carriage-returns
|
---|
| 181 | record = csv.reader([record], escapechar='\\').next()
|
---|
| 182 |
|
---|
| 183 | i = 0
|
---|
| 184 | job = ExtendedDict()
|
---|
| 185 |
|
---|
| 186 | #
|
---|
| 187 | # Leading Columns
|
---|
| 188 | #
|
---|
| 189 | job['jobid'] = int(record[0]) if re_integer.match(record[0]) else None
|
---|
| 190 | job['jobtype'] = job_types[record[1]] if record[1] in job_types else None
|
---|
| 191 |
|
---|
| 192 | if job.jobtype == 'backup':
|
---|
| 193 | job.backup = True
|
---|
| 194 | else:
|
---|
| 195 | job.backup = False
|
---|
| 196 |
|
---|
| 197 | if job.jobtype == 'duplication':
|
---|
| 198 | job.duplication = True
|
---|
| 199 | else:
|
---|
| 200 | job.duplication = False
|
---|
| 201 |
|
---|
| 202 | job['state'] = job_states[record[2]] if record[2] in job_states else None
|
---|
| 203 |
|
---|
| 204 | if job.state == 'active':
|
---|
| 205 | job.active = True
|
---|
| 206 | else:
|
---|
| 207 | job.active = False
|
---|
| 208 |
|
---|
| 209 | if job.state == 'queued':
|
---|
| 210 | job.queued = True
|
---|
| 211 | else:
|
---|
| 212 | job.queued = False
|
---|
| 213 |
|
---|
| 214 | if job.state == 'done':
|
---|
| 215 | job.done = True
|
---|
| 216 | else:
|
---|
| 217 | job.done = False
|
---|
| 218 |
|
---|
| 219 | job['status'] = int(record[3]) if re_integer.match(record[3]) else None
|
---|
| 220 | #job['class'] = record[4] if record[4] else None
|
---|
| 221 | job['policy'] = record[4] if record[4] else None
|
---|
| 222 | job['schedule'] = record[5] if record[5] else None
|
---|
| 223 | job['client'] = record[6] if record[6] else None
|
---|
| 224 | job['server'] = record[7] if record[7] else None
|
---|
| 225 | job['started'] = datetime.datetime.fromtimestamp(int(record[8]), tz) if re_integer.match(record[8]) else None
|
---|
| 226 | job['elapsed'] = int(record[9]) if re_integer.match(record[9]) else None
|
---|
| 227 | job['ended'] = datetime.datetime.fromtimestamp(int(record[10]), tz) if re_integer.match(record[10]) else None
|
---|
| 228 | job['stunit'] = record[11] if record[11] else None
|
---|
| 229 | job['try'] = int(record[12]) if re_integer.match(record[12]) else None
|
---|
| 230 | job['operation'] = operation_types[record[13]] if record[13] in operation_types else None
|
---|
| 231 | job['kbytes'] = long(record[14]) if re_integer.match(record[14]) else None
|
---|
| 232 | job['files'] = long(record[15]) if re_integer.match(record[15]) else None
|
---|
| 233 | job['pathlastwritten'] = record[16] if record[16] else None
|
---|
| 234 | job['percent'] = int(record[17]) if re_integer.match(record[17]) else None
|
---|
| 235 | job['jobpid'] = int(record[18]) if re_integer.match(record[18]) else None
|
---|
| 236 | job['owner'] = record[19] if record[19] else None
|
---|
| 237 | job['subtype'] = subtypes[record[20]] if record[20] in subtypes else None
|
---|
| 238 | #job['classtype'] = class_types[record[21]] if record[21] in class_types else None
|
---|
| 239 | job['policytype'] = policy_types[record[21]] if record[21] in policy_types else None
|
---|
| 240 | job['schedule_type'] = schedule_types[record[22]] if record[22] in schedule_types else None
|
---|
| 241 | job['priority'] = record[23] if record[23] else None
|
---|
| 242 | job['group'] = record[24] if record[24] else None
|
---|
| 243 | job['masterserver'] = record[25] if record[25] else None
|
---|
| 244 | job['retentionunits'] = retention_units[record[26]] if record[26] in retention_units else None
|
---|
| 245 | job['retentionperiod'] = record[27] if record[27] else None
|
---|
| 246 | job['compression'] = record[28] if record[28] else None
|
---|
| 247 | job['kbyteslastwritten'] = long(record[29]) if re_integer.match(record[29]) else None
|
---|
| 248 | job['fileslastwritten'] = long(record[30]) if re_integer.match(record[30]) else None
|
---|
| 249 |
|
---|
| 250 | #
|
---|
| 251 | # Files
|
---|
| 252 | #
|
---|
| 253 | filelistcount = int(record[31]) if re_integer.match(record[31]) else None
|
---|
| 254 | filelist = []
|
---|
| 255 |
|
---|
| 256 | i = 32
|
---|
| 257 |
|
---|
| 258 | if filelistcount is not None and filelistcount > 0:
|
---|
| 259 | for i in range(i, i + filelistcount):
|
---|
| 260 | file = record[i]
|
---|
| 261 | if file:
|
---|
| 262 | filelist.append(file)
|
---|
| 263 | i += 1
|
---|
| 264 |
|
---|
| 265 | job['filelistcount'] = filelistcount
|
---|
| 266 | job['filelist'] = filelist
|
---|
| 267 |
|
---|
| 268 | #
|
---|
| 269 | # Tries
|
---|
| 270 | #
|
---|
| 271 | trycount = int(record[i]) if record[i] else None
|
---|
| 272 | tries = []
|
---|
| 273 |
|
---|
| 274 | if trycount:
|
---|
| 275 |
|
---|
| 276 | for t in range(trycount):
|
---|
| 277 |
|
---|
| 278 | Try = ExtendedDict()
|
---|
| 279 | #Try = OrderedDict()
|
---|
| 280 |
|
---|
| 281 | #Try['trypid'] = record[i+1] if record[i+1] else None
|
---|
| 282 | Try['pid'] = record[i+1] if record[i+1] else None
|
---|
| 283 | #Try['trystunit'] = record[i+2] if record[i+2] else None
|
---|
| 284 | Try['stunit'] = record[i+2] if record[i+2] else None
|
---|
| 285 | #Try['tryserver'] = record[i+3] if record[i+3] else None
|
---|
| 286 | Try['server'] = record[i+3] if record[i+3] else None
|
---|
| 287 | #Try['trystarted'] = datetime.datetime.fromtimestamp(int(record[i+4]), tz) if re_integer.match(record[i+4]) else None
|
---|
| 288 | Try['started'] = datetime.datetime.fromtimestamp(int(record[i+4]), tz) if re_integer.match(record[i+4]) else None
|
---|
| 289 | Try['elapsed'] = int(record[i+5]) if re_integer.match(record[i+5]) else None
|
---|
| 290 | #Try['tryended'] = datetime.datetime.fromtimestamp(int(record[i+6]), tz) if re_integer.match(record[i+6]) else None
|
---|
| 291 | Try['ended'] = datetime.datetime.fromtimestamp(int(record[i+6]), tz) if re_integer.match(record[i+6]) else None
|
---|
| 292 | #Try['trystatus'] = int(record[i+7]) if re_integer.match(record[i+7]) else None
|
---|
| 293 | Try['status'] = int(record[i+7]) if re_integer.match(record[i+7]) else None
|
---|
| 294 | #Try['trystatusdescription'] = record[i+8] if record[i+8] else None
|
---|
| 295 | Try['statusdescription'] = record[i+8] if record[i+8] else None
|
---|
| 296 |
|
---|
| 297 | trystatuscount = int(record[i+9]) if re_integer.match(record[i+9]) else None
|
---|
| 298 | trystatuslines = []
|
---|
| 299 |
|
---|
| 300 | i += 10
|
---|
| 301 |
|
---|
| 302 | #
|
---|
| 303 | # Process status lines if there are any
|
---|
| 304 | #
|
---|
| 305 | if trystatuscount is not None and trystatuscount > 0:
|
---|
| 306 |
|
---|
| 307 | for i in range(i, i + trystatuscount):
|
---|
| 308 | trystatuslines.append(record[i])
|
---|
| 309 |
|
---|
| 310 | #Try['trystatuscount'] = trystatuscount
|
---|
| 311 | Try['statuscount'] = trystatuscount
|
---|
| 312 | #Try['trystatuslines'] = trystatuslines
|
---|
| 313 | Try['statuslines'] = trystatuslines
|
---|
| 314 | #Try['trybyteswritten'] = long(record[i+1]) if re_integer.match(record[i+1]) else None
|
---|
| 315 | #Try['byteswritten'] = long(record[i+1]) if re_integer.match(record[i+1]) else None
|
---|
| 316 | #Try['trykbyteswritten'] = long(record[i+1]) if re_integer.match(record[i+1]) else None
|
---|
| 317 | #Try['kbyteswritten'] = long(record[i+1]) if re_integer.match(record[i+1]) else None
|
---|
| 318 | #Try['trykbytes'] = long(record[i+1]) if re_integer.match(record[i+1]) else None
|
---|
| 319 | Try['kbytes'] = long(record[i+1]) if re_integer.match(record[i+1]) else None
|
---|
| 320 | #Try['tryfileswritten'] = long(record[i+2]) if re_integer.match(record[i+2]) else None
|
---|
| 321 | Try['fileswritten'] = long(record[i+2]) if re_integer.match(record[i+2]) else None
|
---|
| 322 |
|
---|
| 323 | tries.append(Try)
|
---|
| 324 |
|
---|
| 325 | #
|
---|
| 326 | # Only increment by 1 if there were no status lines
|
---|
| 327 | #
|
---|
| 328 | if trystatuscount is not None and trystatuscount > 0:
|
---|
| 329 | i += 2
|
---|
| 330 | else:
|
---|
| 331 | i += 1
|
---|
| 332 |
|
---|
| 333 | job['trycount'] = trycount
|
---|
| 334 | job['tries'] = tries
|
---|
| 335 |
|
---|
| 336 | #
|
---|
| 337 | # Trailing Columns
|
---|
| 338 | #
|
---|
| 339 | remaining = len(record[i+1:])
|
---|
| 340 | job['parentjob'] = record[i+1] if record[i+1] else None
|
---|
| 341 | job['kbpersec'] = record[i+2] if record[i+2] else None
|
---|
| 342 | job['copy'] = record[i+3] if record[i+3] else None
|
---|
| 343 | job['robot'] = record[i+4] if record[i+4] else None
|
---|
| 344 | job['vault'] = record[i+5] if record[i+5] else None
|
---|
| 345 | job['profile'] = record[i+6] if record[i+6] else None
|
---|
| 346 | job['session'] = record[i+7] if record[i+7] else None
|
---|
| 347 | job['ejecttapes'] = record[i+8] if record[i+8] else None
|
---|
| 348 | job['srcstunit'] = record[i+9] if record[i+9] else None
|
---|
| 349 | job['srcserver'] = record[i+10] if record[i+10] else None
|
---|
| 350 | job['srcmedia'] = record[i+11] if record[i+11] else None
|
---|
| 351 | job['dstmedia'] = record[i+12] if record[i+12] else None
|
---|
| 352 | job['stream'] = record[i+13] if record[i+13] else None
|
---|
| 353 | job['suspendable'] = record[i+14] if record[i+14] else None
|
---|
| 354 | job['resumable'] = record[i+15] if record[i+15] else None
|
---|
| 355 | job['restartable'] = record[i+16] if record[i+16] else None
|
---|
| 356 | job['datamovement'] = record[i+17] if record[i+17] else None
|
---|
| 357 | job['frozenimage'] = record[i+18] if record[i+18] else None
|
---|
| 358 | job['backupid'] = record[i+19] if record[i+19] else None
|
---|
| 359 | job['killable'] = record[i+20] if record[i+20] else None
|
---|
| 360 | job['controllinghost'] = record[i+21] if record[i+21] else None
|
---|
| 361 |
|
---|
| 362 | #
|
---|
| 363 | # DEBUG
|
---|
| 364 | #j = 0
|
---|
| 365 | #for key in job.keys():
|
---|
| 366 | # j += 1
|
---|
| 367 | # print '%2d: %20s: %s' % (j, key, job[key])
|
---|
| 368 |
|
---|
| 369 | return job
|
---|
| 370 |
|
---|
| 371 | else:
|
---|
| 372 |
|
---|
| 373 | raise ParseError, 'Unknown format %s' % (format)
|
---|
| 374 |
|
---|