1 |
#!/usr/bin/env python |
2 |
|
3 |
from __future__ import print_function |
4 |
import sys |
5 |
import os |
6 |
import re |
7 |
from subprocess import check_output, check_call, CalledProcessError |
8 |
import smtplib |
9 |
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../base/libs/py')) |
10 |
from drmsCmdl import CmdlParser |
11 |
|
12 |
# Hard code a bunch of stuff since I don't have time to do this correctly. |
13 |
PROD_ROOTDIR = '/home/jsoc/cvs/Development' |
14 |
WAYSTATION = 'waystation' |
15 |
WAYSTATION_USER = 'arta' |
16 |
WAYSTATION_DOMAIN = 'sun.stanford.edu' |
17 |
|
18 |
RV_SUCCESS = 0 |
19 |
RV_ERROR_MAIL = 1 |
20 |
RV_ERROR_ARGS = 2 |
21 |
|
22 |
# This class changes the current working directory, and restores the original working directory when |
23 |
# the context is left. |
24 |
class Chdir: |
25 |
"""Context manager for changing the current working directory""" |
26 |
def __init__(self, newPath): |
27 |
self.newPath = os.path.realpath(newPath) |
28 |
|
29 |
def __enter__(self): |
30 |
self.savedPath = os.path.realpath(os.getcwd()) |
31 |
os.chdir(self.newPath) |
32 |
cdir = os.path.realpath(os.getcwd()) |
33 |
if cdir == self.newPath: |
34 |
return 0 |
35 |
else: |
36 |
return 1 |
37 |
|
38 |
def __exit__(self, etype, value, traceback): |
39 |
os.chdir(self.savedPath) |
40 |
cdir = os.path.realpath(os.getcwd()) |
41 |
if cdir == self.savedPath: |
42 |
return 0 |
43 |
else: |
44 |
return 1 |
45 |
|
46 |
def sendMail(localName, domainName, details): |
47 |
subject = 'Production Binary Tree Check' |
48 |
fromAddr = 'jsoc@solarpost.stanford.edu' |
49 |
toAddrs = [ localName + '@' + domainName ] |
50 |
msg = 'From: jsoc@solarpost.stanford.edu\nTo: ' + ','.join(toAddrs) + '\nSubject: ' + subject + '\n' + details |
51 |
|
52 |
try: |
53 |
server = smtplib.SMTP('solarpost.stanford.edu') |
54 |
server.sendmail(fromAddr, toAddrs, msg) |
55 |
server.quit() |
56 |
except Exception as exc: |
57 |
# If any exception happened, then the email message was not received. |
58 |
raise Exception('emailBadrecipient', 'Unable to send email message.', RV_ERROR_MAIL) |
59 |
|
60 |
def getArgs(): |
61 |
optD = {} |
62 |
|
63 |
try: |
64 |
parser = CmdlParser(usage='%(prog)s [ -h ] [ -m | --mail ]') |
65 |
|
66 |
# Optional |
67 |
# Cannot combine metavar with action for some unknown reason. |
68 |
parser.add_argument('-m', '--mail', help='Send the waystation user the list of changed files in a mail message', dest='mail', action='store_true') |
69 |
|
70 |
args = parser.parse_args() |
71 |
|
72 |
# Read the series info from |
73 |
except Exception as exc: |
74 |
if len(exc.args) != 2: |
75 |
raise # Re-raise |
76 |
|
77 |
etype = exc.args[0] |
78 |
msg = exc.args[1] |
79 |
|
80 |
if etype == 'CmdlParser-ArgUnrecognized' or etype == 'CmdlParser-ArgBadformat' or etype == 'CmdlParser': |
81 |
raise Exception('getArgs', 'Unable to parse command-line arguments.', RV_ERROR_ARGS) |
82 |
else: |
83 |
raise # Re-raise. |
84 |
|
85 |
optD['mail'] = args.mail |
86 |
|
87 |
return optD |
88 |
|
89 |
msg = None |
90 |
rv = RV_SUCCESS |
91 |
|
92 |
|
93 |
# Run the linux rsync command to see which files have timestamps that are newer than the waystation files. |
94 |
# CAVEAT: rsync runs differently and takes different arguments on different machines. |
95 |
# su - arta |
96 |
# cd /home/jsoc/cvs/Development |
97 |
# rsync -aluv --dry-run JSOC/ waystation/JSOC |
98 |
try: |
99 |
optD = getArgs() |
100 |
changedFiles = [] |
101 |
with Chdir(PROD_ROOTDIR) as ret: |
102 |
cmdList = ['rsync', '-aluv', '--dry-run', 'JSOC/', 'waystation/JSOC'] |
103 |
if ret == 0: |
104 |
resp = check_output(cmdList) |
105 |
output = resp.decode('utf-8') |
106 |
|
107 |
if output: |
108 |
# Parse out the status field. |
109 |
outputList = output.splitlines() |
110 |
firstLine = True |
111 |
|
112 |
# One of the bad things about calling an external program is that you cannot control what it does. |
113 |
# In this case, different implementations of rsync format the output differently. So, take a guess |
114 |
# and assume the first line is not part of the file list, and that there is a newline separating |
115 |
# the last item in the file list and the output footer. |
116 |
regExp = re.compile(r'\s*(\S+)\s*') |
117 |
|
118 |
for line in outputList: |
119 |
if firstLine: |
120 |
firstLine = False |
121 |
continue |
122 |
|
123 |
matchObj = regExp.match(line) |
124 |
if matchObj is not None: |
125 |
# We have a non-empty line. |
126 |
# Skip directories. Report files only. |
127 |
item = matchObj.group(1) |
128 |
if os.path.isfile('JSOC/' + item): |
129 |
changedFiles.append(item) |
130 |
else: |
131 |
continue |
132 |
else: |
133 |
# This line is one past the last item in the file list. |
134 |
break |
135 |
|
136 |
# Now if there is at least one file that changed, send an email to WAYSTATION_USER |
137 |
if changedFiles and len(changedFiles) >= 1: |
138 |
if optD['mail']: |
139 |
sendMail(WAYSTATION_USER, WAYSTATION_DOMAIN, 'List of files changed:\n' + '\n'.join(changedFiles)) |
140 |
else: |
141 |
print('Files Modified in Production Directory:\n' + '\n'.join(changedFiles)) |
142 |
|
143 |
else: |
144 |
print('Unable to change directory to ' + PROD_ROOTDIR, file=sys.stderr) |
145 |
|
146 |
except CalledProcessError as exc: |
147 |
if exc.output: |
148 |
print('Error calling rsync: ' + exc.output, file=sys.stderr) |
149 |
except ValueError: |
150 |
print('Bad arguments to rsync: \n' + '\n'.join(cmdList[1:])) |
151 |
except Exception as exc: |
152 |
if len(exc.args) != 3: |
153 |
raise # Re-raise |
154 |
|
155 |
etype = exc.args[0] |
156 |
|
157 |
if etype == 'emailBadrecipient': |
158 |
msg = exc.args[1] |
159 |
rv = exc.args[2] |
160 |
else: |
161 |
raise # Re-raise |
162 |
|
163 |
if msg: |
164 |
print(msg, file=sys.stderr) |
165 |
|
166 |
sys.exit(rv) |