Please note that the CVS and issue trackers have moved to GitHub. These Trac pages are no longer kept up-to-date.

root/seattle/trunk/deploymentscripts/verifyfiles.mix@5637

Revision 2600, 10.3 KB (checked in by jsamuel, 10 years ago)

Fix for #605

Line 
1"""
2<Program Name>
3  verifyfiles.py
4
5<Started On>
6  January 13, 2009
7
8<Author>
9  Brent Couvrette
10
11<Purpose>
12  This will look for a seattle install folder and if found, check to see if it
13  is valid according to the given installer.
14 
15<Usage>
16  This module can be used either as an import or standalone.  As an import,
17  just call verify_files, which will return a dictionary of problematic files.
18  See the docstring there for more details.
19 
20  When run as a standalone, it takes one argument, the path to an installer
21  tarball.  In this mode it will just call verify_files and pretty print the
22  returned dictionary.
23"""
24
25from repyportability import *
26import os
27import shutil
28import sys
29import tarfile
30import tempfile
31import persist
32
33include sha.repy
34
35def get_file_hash(filename):
36  """
37  <Purpose>
38    Computes the sha hash of the given file.
39   
40  <Side Effects>
41    None
42   
43  <Exceptions>
44    An exception could be thrown if there are problems reading the file.
45   
46  <Arguments>
47    filename - the path of the file to be hashed.
48   
49  <Return>
50    The hash of the given file.
51  """
52  fileobj = file(filename, 'rb')
53  filedata = fileobj.read()
54  fileobj.close()
55 
56  return sha_hexhash(filedata)
57 
58
59def get_file_info(installer_fn):
60  """
61  <Purpose>
62    Takes an installer file name and extracts a list of files and folders
63    that it should create, as well as some of their properties.
64
65  <Side Effects>
66    None?
67   
68  <Exceptions>
69    An exception should be thrown if installer_fn is not a path to a tar file
70
71  <Arguments>
72    installer_fn - The file name of the installer that we are checking against.
73
74  <Return>
75    A dictionary mapping the neccessary files and directories to a list of
76    attributes of those files/directories.
77  """
78  # Extract the installer into a temp directory so we can check out all of
79  # its files.
80  tempdir = tempfile.mkdtemp()
81  installer_file = tarfile.open(installer_fn)
82  installer_file.extractall(tempdir)
83 
84  fileinfo_dict = {}
85  tempdir = tempdir + '/seattle_repy/'
86  for instfile in os.listdir(tempdir):
87    # For every file in the directory, determine if it is a regular file or a
88    # directory.  Put this information into a dictionary, adding the file's
89    # hash if it is a file.
90    # At this point our install should be flat except for maybe vessel folders
91    # which we don't want to look into anyways.
92    # I think that anything listed here must be either a file or a directory/
93    # Is this actually true?
94    if os.path.isfile(tempdir + instfile):
95      filehash = get_file_hash(tempdir + instfile)
96      fileinfo_dict[instfile] = ['file', filehash]
97    elif os.path.isdir(tempdir + instfile):
98      fileinfo_dict[instfile] = ['dir']
99    else:
100      fileinfo_dict[instfile] = ['unknown']
101      print 'Warning: ' + instfile + ' was in installer, but it is not a ' + \
102          'file or a directory.'
103 
104  shutil.rmtree(tempdir)
105  return fileinfo_dict
106
107
108def verify_files(installer_tar = None, installer_dict = None, look_dir = '.'):
109  """
110  <Purpose>
111    Looks for a seattle_repy folder, and if found, verify that it matches a
112    install made by the given installer.
113
114  <Side Effects>
115    None?
116
117  <Arguments>
118    installer_tar - The file name of the installer tar that should be used to
119                    verify the install.
120    installer_dict - The file name of the dictionary file that should be used
121                     to verify the install.
122
123    look_dir - The directory to look for the seattle_repy directory.  Defaults
124               to '.'
125
126    Note that only one of installer_tar and installer_dict needs to be given.
127    This function will use whichever one is given, or installer_dict if both
128    are given.  An exception will be thrown if neither are given.
129
130  <Exceptions>
131    Exception thrown if neither installer_tar nor installer_dict is given,
132    or if the given filename does not exist.
133
134  <Returns>
135    Dictionary mapping problematic files to the reason for them being
136    problematic.  An empty dictionary indicates that the install is valid.
137  """
138  # Check to see if there is a seattle_repy folder in the current directory
139  if not (os.path.exists(look_dir + '/seattle_repy') and \
140      os.path.isdir(look_dir + '/seattle_repy')):
141    return {'seattle_repy': 'Error:Does not exist'}
142   
143  if installer_tar == None and installer_dict == None:
144    raise Exception("Either installer_tar or installer_dict must be given")
145  elif installer_dict != None:
146    fileinfo_dict = persist.restore_object(installer_dict)
147  else:
148    # Kon: edit installer_fn to installer_tar
149    fileinfo_dict = get_file_info(installer_tar)
150
151  oldcwd = os.getcwd()
152
153  os.chdir(look_dir + '/seattle_repy')
154 
155  filestatus_dict = {}
156 
157  for foundfile in os.listdir('.'):
158    # Check each file in the directory against the fileinfo_dict that we
159    # extracted from the installer.
160    # Are there any special files that I should handle separately?
161    if foundfile == 'vesseldict':
162      # vesseldict is likely to be different than what came with the installer,
163      # so we will only check that it exists, and not bother with anything
164      # else for now.
165      fileinfo_dict[foundfile] = 'found'
166    elif foundfile.startswith('resource.v'):
167      # There are likely to be more resource files than in the installer,
168      # and they will likely be different.  At this point we will assume
169      # they are ok, and just mark them as found.
170      fileinfo_dict[foundfile] = 'found'
171      # At this point we will assume that as long as there is any vessel
172      # resource file, that v1's has been accounted for, as v1's will likely
173      # not exist anymore.
174      fileinfo_dict['resource.v1'] = 'found'
175    elif foundfile.startswith('v') and os.path.isdir(foundfile):
176      # In this case it is almost certainly a vessel directory, and should
177      # just be marked as found.
178      fileinfo_dict[foundfile] = 'found'
179      # At this point we will assume that as long as there is any vessel
180      # directory, that v1 has been accounted for, as v1 will likely not
181      # exist anymore.
182      fileinfo_dict['v1'] = 'found'
183    elif foundfile == 'nodeman.cfg':
184      # nodeman.cfg changes during the install process, so for now it is
185      # good enough just knowing that it exists.
186      fileinfo_dict[foundfile] = 'found'
187    elif foundfile.endswith('.pyc'):
188      # .pyc files get automatically created when things are imported,
189      # so in general these files are to be expected, so we will just
190      # mark them as found, but otherwise ignore them.
191      fileinfo_dict[foundfile] = 'found'
192    elif 'stop' in foundfile:
193      # There may be vessel stop files that we can safely ignore.
194      fileinfo_dict[foundfile] = 'found'
195    elif 'log' in foundfile:
196      # vessel and softwareupdater logs can also safely be ignored.
197      fileinfo_dict[foundfile] = 'found'
198    elif 'status' in foundfile:
199      # Vessel status files can also safely be ignored.
200      fileinfo_dict[foundfile] = 'found'
201    elif 'start_seattle.sh' == foundfile:
202      # start_seattle.sh changes during install, just note its presence
203      fileinfo_dict[foundfile] = 'found'
204    elif fileinfo_dict.has_key(foundfile):
205      if fileinfo_dict[foundfile][0] == 'dir':
206        if not os.path.isdir(foundfile):
207          filestatus_dict[foundfile] = "Error:should be a directory, but \
208              it isn't"
209      elif fileinfo_dict[foundfile][0] == 'file':
210        if not os.path.isfile(foundfile):
211          filestatus_dict[foundfile] = "Error:should be a file, but isn't"
212        else:
213          filehash = get_file_hash(foundfile)
214          if not (filehash == fileinfo_dict[foundfile][1]):
215            filestatus_dict[foundfile] = "Error:File has changed \
216                (Incorrect hash value)"
217           
218      # The file was found regardless errors, so lets indicate that here.     
219      fileinfo_dict[foundfile] = 'found'
220    else:
221      # The file is not in the list of approved files.
222      if os.path.isfile(foundfile):
223        filestatus_dict[foundfile] = "Warning:Unknown file"
224      elif os.path.isdir(foundfile):
225        filestatus_dict[foundfile] = "Warning:Unknown directory"
226     
227  for reqfile in fileinfo_dict.keys():
228    # Make note of any files that should be there but were not found.
229    if fileinfo_dict[reqfile] != 'found':
230      filestatus_dict[reqfile] = "Error:required " + \
231          reqfile + " not found"
232
233  os.chdir(oldcwd) 
234
235  return filestatus_dict
236 
237
238def main():
239  """
240  <Purpose>
241    Main method that figures out what the script should do based on the
242    arguments.  If the -writefile option is given, it will just write the
243    installer information out to the given file name.  If the -readfile option
244    is given, it will assume the filename given is a file that was written
245    with the -writefile mode of this script.  With no options it will assume
246    the filename given is an installer tarball, and will check based on that.
247  <Usage>
248    python verifyfiles.py [-writefile | -readfile] in_fn [out_fn] [look_dir]
249  """
250  # Kon: just in case, so it won't err out initialize it here
251  #filestatus = {}
252
253  if len(sys.argv) == 2:
254    # if there is just one argument, it should be a filename that is a tarball
255    # of an installer.
256    filestatus = verify_files(installer_tar = sys.argv[1])
257  elif len(sys.argv) > 2:
258    # Otherwise we are reading or writing from a dictionary file.  Lets check
259    # which one it is.
260    if sys.argv[1] == '-writefile':
261      if len(sys.argv) > 3:
262        # Make sure there is a output file argument, then get and write the
263        # verification dictionary to said file.
264        fileinfo_dict = get_file_info(sys.argv[2])
265        persist.commit_object(fileinfo_dict, sys.argv[3])
266      else:
267        print 'Error: -writefile requires both an installer to read from and \
268               a file to write to.'
269      exit(0)
270    elif sys.argv[1] == '-readfile':
271      # Get the filestatus based on the given dictionary file.
272      if len(sys.argv) > 3:
273        # a look_dir was given
274        filestatus = verify_files(installer_dict = sys.argv[2], 
275            look_dir = sys.argv[3])
276      else:
277        # No look_dir was given
278        filestatus = verify_files(installer_dict = sys.argv[2])
279  else:
280    print 'Usage: python verifyfiles.py [-writefile | -readfile] \
281           in_fn [out_fn]'
282    exit(0)
283
284  for problem_file in filestatus.keys():
285    print problem_file + ':' + filestatus[problem_file]
286
287  print 'file_checker_finished'
288 
289if __name__ == "__main__":
290  main()
Note: See TracBrowser for help on using the browser.