Changeset 461

Show
Ignore:
Timestamp:
05/15/08 16:15:24 (8 months ago)
Author:
jmowery
Message:

refactoring storage incremental check-in

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/trunk-pmd-intproto/server/storage.py

    r460 r461  
    188188                self.logs_dir = os.path.join(self.root, '.__logs__') #logs are only kept at the root of the store 
    189189 
    190         def add_file(self, name): 
    191                 '''Add a single file to the store.''' 
    192                 raise NotImplementedError 
     190        def add_file(self, name, e=None): 
     191                '''Add a single file to the store. Where name is the name of the file relative to path and e is its entry if it already exists.''' 
     192                now = int(time.time()) 
     193                storage_file = os.path.join(self.storage_dir, name + '.' + str(now)) 
     194                shutil.copy2(os.path.join(self.path, name), storage_file) 
     195                os.chmod(storage_file, os.stat(storage_file)[stat.ST_MODE] &~(stat.S_IWUSR|stat.S_IWGRP|stat.S_IWOTH)) 
     196                if e is None: 
     197                        e = Entry(name) 
     198                        e.initial = now 
     199                e.applied = now 
     200                e.latest = now 
     201                self.config.set('entries', name, pickle.dumps(e)) 
     202                return e 
    193203 
    194204        def remove_file(self, name): 
     
    196206                raise NotImplementedError 
    197207 
    198         def add_dir(self, name): 
     208        def add_dir(self, name, local=False): 
    199209                '''Add a directory to the store. This function is not recursive.''' 
    200                 raise NotImplementedError 
     210                e = Entry(name) 
     211                e.initial = now 
     212                e.applied = now 
     213                e.latest = now 
     214                if local: 
     215                        e.type = 'LOCAL' 
     216                else: 
     217                        if name == '.': 
     218                                e.type = '.' 
     219                        else: 
     220                                e.type = 'DIR' 
     221                self.config.set('entries', name, pickle.dumps(e)) 
     222                return e 
    201223 
    202224        def remove_dir(self, name): 
     
    239261        '''The Layer object maintains all data relevant to a single hierarchical group.''' 
    240262        modified = False # set to True if the data in any field has changed since last sync to disk 
    241         name = ''        # The name of a Layer is the relative path to the storage_root/..  
     263        name = ''        # The name of a Layer is the relative path to the storage_root/.. 
    242264        description = '' # User configurable description of the type of files contained in the Layer 
    243265        entries = {}     # Dictionary of file name, Entry pairs per controlled file 
     
    253275                        for entry in self.storage.config.get_options('entries'): 
    254276                                self.entries[entry] = pickle.loads(self.storage.config.get('entries', entry)) 
     277                        self.description = self.storage.config.get('general', 'description') 
    255278                        self.name = self.name[len(os.path.dirname(self.storage.root)) + 1:] #shorten name removing directories above root 
    256279 
     
    282305        def export(self, dest_path): 
    283306                '''Create a recursive copy of this Layer and the applied version of all its controlled files and subdirectories at dest_path without any revision control files.''' 
    284                 raise NotImplementedError 
     307                if not os.path.exists(dest_path): 
     308                        os.mkdir(dest_path) 
     309                if not os.path.is_dir(dest_path): 
     310                        raise ValueError, '\'' + dest_path + '\' is not a directory.' 
     311                if not self.storage: 
     312                        return #not controlled, nothing to export 
     313                for file in self.list_controlled_files(): 
     314                        applied_file = self.get_current(file) 
     315                        if applied_file: 
     316                                dest_file = os.path.join(dest_path, os.path.basename(applied_file)) 
     317                                shutil.copy2(applied_file, dest_file) 
     318                                os.chmod(dest_file, os.stat(dest_file)[stat.ST_MODE]|stat.S_IWUSR) 
     319                for d in self.list_controlled_dirs(): 
     320                        l = Layer(os.path.join(self.storage.path, d)) 
     321                        l.export(os.path.join(dest_path, d)) 
     322                        l.update() 
    285323 
    286324        def get_current(self, name): 
     
    295333                        return '' 
    296334                cur = self.storage.find_recent(name, self.entries['.'].applied) 
     335                if cur == 0: 
     336                        return '' 
    297337                return os.path.join(self.storage.storage_dir, name) + '.' + str(cur) 
    298338 
     
    319359                        return True #deletion is a modification 
    320360                statinfo = os.stat(path) 
    321                 if statinfo.st_mtime > self.latest:     
     361                if statinfo.st_mtime > self.latest: 
    322362                        return True 
    323363                return False 
     
    325365        def checkin(self): 
    326366                '''Recursively add new versions for all modified files.''' 
    327                 raise NotImplementedError 
     367                if not self.storage: 
     368                        raise RuntimeError, 'Directory \'' + self.name + '\' is not under revision control.' 
     369                for file in self.list_controlled_files(): 
     370                        if self.is_modified(file) and not self.is_locked(file): 
     371                                self.add(file) 
     372                for d in self.list_controlled_dirs(): 
     373                        l = Layer(os.path.join(self.storage.path, d)) 
     374                        l.checkin() 
     375                        l.update() 
    328376 
    329377        #functions to add and remove files and directories 
     
    358406                        if e.lock: 
    359407                                raise RuntimeError, 'File \'' + name + '\' is locked; cannot add new version.' 
    360                         if e.type == 'DIR': 
    361                                  
     408                        if e.type == 'DIR' or e.type == 'LOCAL': 
     409                                l = Layer(os.path.join(self.storage.path, name)) 
     410                                l.add('.', recursive) 
     411                                l.update() 
     412                                return 
     413                        else: #e.type=='FILE' 
     414                                self.storage.add_file(name, e) 
    362415                else: #no entry yet; create one 
    363                         pass #TODO 
    364                 raise NotImplementedError 
     416                        path = os.path.join(self.storage.path, name) 
     417                        if os.path.isdir(path): 
     418                                e = self.storage.add_dir(name) 
     419                                if recursive: 
     420                                        l = Layer(os.path.join(self.storage.path, name)) 
     421                                        l.add('.', recursive) 
     422                                        l.update() 
     423                        else: 
     424                                e = self.storage.add_file(name) 
     425                        self.entries[name] = e 
     426                self.entries['.'].latest = e.latest 
     427                self.modified = True 
    365428 
    366429        def remove(self, names): 
     
    370433        def control(self): 
    371434                '''Place the layer under revision control; if already controlled, do nothing.''' 
    372                 raise NotImplementedError 
     435                if self.storage: 
     436                        return #already has control files. 
     437                self.storage = StorageData() 
     438                self.storage.path = self.name 
     439                self.storage.root = find_control_root(self.storage.path) 
     440                self.name = self.name[len(os.path.dirname(self.storage.root)) + 1:] #shorten name removing directories above root 
     441                self.storage.storage_dir = os.path.join(self.storage.path, '.__storage__') 
     442                os.mkdir(self.storage.storage_dir) 
     443                self.storage.nodes_dir = os.path.join(self.storage.path, '.__nodes__') 
     444                os.mkdir(self.storage.nodes_dir) 
     445                self.storage.logs_dir = os.path.join(self.storage.path, '__logs__') 
     446                if not os.path.exists(self.storage.logs_dir): 
     447                        os.mkdir(self.storage.logs_dir) 
     448                self.storage.status_path = os.path.join(self.storage.path, '.__status__') 
     449                self.storage.config.add_section('general') 
     450                self.storage.config.set('general', 'description', self.description) 
     451                self.storage.config.set('general', 'root', self.storage.root) 
     452                self.storage.config.add_section('entries') 
     453                self.storage.add_dir('.') 
     454                self.storage.write() 
     455                if is_controlled(os.path.join(self.storage.path, '..')): 
     456                        l = Layer(os.path.join(self.storage.path, '..')) 
     457                        l.add(os.path.basename(self.storage.path)) 
     458                        l.update() 
    373459 
    374460        def clean(self, names, recursive=False): 
     
    446532                return ' ' + applied_stamp + latest_stamp 
    447533 
    448         def status(self, name='.', recursive=False, prefix=''): 
    449                 '''Return a string containing the status of name. If prefix is non-empty, prepend it to name in the returned string.''' 
    450                 raise NotImplementedError 
    451  
    452         def revisions(self, name='.', recursive=False): 
    453                 '''return a list of all versions of name.''' 
    454                 raise NotImplementedError 
     534        def status(self, names='.', recursive=False, prefix=''): 
     535                '''Return a list of strings containing the status of name. If prefix is non-empty, prepend it to name in the returned string.''' 
     536                if names is None: 
     537                        raise ValueError, 'Must specify a name.' 
     538                stats = [] 
     539                if names.__class__ == [].__class__: 
     540                        for name in names: 
     541                                stats += self.status(name, recursive, prefix) 
     542                        return stats 
     543                if self.storage: 
     544                        store_path = self.storage.path 
     545                else: 
     546                        store_path = self.name 
     547                if os.path.basename(names) != names: 
     548                        l = Layer(os.path.join(store_path, names)) 
     549                        stats += l.status(os.path.basename(names), recursive, os.path.join(prefix, os.path.dirname(names))) 
     550                        l.update() 
     551                        return stats 
     552                elif names == '.': 
     553                        stats.append(self.status_prefix(names) + prefix + self.name + self.version_suffix(names)) 
     554                        if recursive: 
     555                                for entry in os.listdir(store_path): 
     556                                        if entry in control_files: 
     557                                                continue 
     558                                        stats.append(self.status_path(entry) + entry + self.version_suffix(entry)) 
     559                        return stats 
     560                e = self.get_entry(names) 
     561                if e: 
     562                        stats.append(self.status_prefix(names) + prefix + names + self.version_suffix(names)) 
     563                        if recursive and e.type != 'FILE': 
     564                                l = Layer(os.path.join(self.storage.path, names) 
     565                                stats += l.status('.', recursive) 
     566                                l.update() 
     567                else: 
     568                        stats.append(self.status_prefix(names) + prefix + names + self.version_suffix(names)) 
     569                return stats 
     570 
     571        def revisions(self, names='.', recursive=False): 
     572                '''Return a dictionary with a list of all versions of each named entry. Format is {(name,group):[<versions>]}''' 
     573                if names is None: 
     574                        raise ValueError, 'Must specify a name.' 
     575                revs = {} 
     576                if names.__class__ == [].__class__: 
     577                        for name in names: 
     578                                nrev =  self.revisions(name, recursive) 
     579                                for i in nrev.keys(): 
     580                                        revs[i] = nrev[i] 
     581                        return revs 
     582                if self.storage: 
     583                        store_path = self.storage.path 
     584                else: 
     585                        store_path = self.name 
     586                if names == '.': #everything in this dir 
     587                        all_vers = set() 
     588                        for file in list_controlled_files(): 
     589                                nrev = self.storage.list_revisions(file) 
     590                                if recursive: 
     591                                        revs[(file, self.name)] = nrev 
     592                                all_vers += set(nrev) 
     593                        revs[('.', self.name)] = list(all_vers) 
     594                        if recursive and len(self.list_controlled_dirs()): 
     595                                nrev = self.revisions(self.list_controlled_dirs(), recursive) 
     596                                for i in nrev.keys(): 
     597                                        revs[i] = nrev[i] 
     598                        return revs 
     599                elif os.path.is_dir(os.path.join(store_path, names)): # some other dir 
     600                        l = Layer(os.path.join(store_path, names)) 
     601                        revs = l.revisions('.', recursive) 
     602                        l.update() 
     603                        return revs 
     604                elif os.path.basename(names) != names: # file in another dir 
     605                        l = Layer(os.path.join(store_path, names)) 
     606                        revs = l.revisions(os.path.basename(names), recursive) 
     607                        l.update() 
     608                        return revs 
     609                elif not self.storage: #local file but . is not controlled 
     610                        return {} 
     611                e = self.get_entry(names) 
     612                if not e: # local file not under control 
     613                        return {} 
     614                return {(names, self.name):self.list_revisions(names)} 
    455615 
    456616        def list_controlled_files(self): 
    457617                '''Return a list of all files under revision control in this Layer.''' 
    458                 raise NotImplementedError 
     618                files = [] 
     619                for e in self.entries.keys(): 
     620                        if self.entries[e].type == 'FILE': 
     621                                files.append(e) 
     622                return files 
    459623 
    460624        def list_controlled_dirs(self): 
    461625                '''Return a list of all controlled subdirectories under this Layer.''' 
    462                 raise NotImplementedError 
     626                dirs = [] 
     627                for e in self.entries.keys(): 
     628                        if self.entries[e] != 'FILES' and e != '.': 
     629                                dirs.append(e) 
     630                return dirs 
    463631 
    464632        def list_controlled(self): 
     
    466634                return self.list_controlled_files() + self.list_controlled_dirs() 
    467635 
    468         def group_name(self): 
    469                 '''Return the group name this Layer represents; this is the name used by Nodes for classification.''' 
    470                 raise NotImplementedError 
    471  
    472636        #locking functions 
    473637        def lock(self, names=['.'], recursive=False): 
     
    481645        def is_locked(self, name='.', recursive=False): #add recursive as option) 
    482646                '''Return True if name was locked by a prior call to lock(); if recursive, return True if any file under name is locked.''' 
    483                 raise NotImplementedError 
     647                if not storage: 
     648                        return False #not controlled, can't be locked 
     649                e = self.get_entry(name) 
     650                if not e: 
     651                        return False #name is not controlled, can't be locked 
     652                if e.type == 'FILE': 
     653                        return e.lock 
     654                elif name == '.': 
     655                        if not recursive: 
     656                                return e.lock 
     657                        if e.lock: 
     658                                return True 
     659                        for file in self.list_controlled_files(): 
     660                                if self.is_locked(file): 
     661                                        return True 
     662                        for d in self.list_controlled_dirs(): 
     663                                l = Layer(os.path.join(self.storage.path, d)) 
     664                                if l.is_locked('.', True): 
     665                                        l.update() 
     666                                        return True 
     667                                l.update() 
     668                else: 
     669                        l = Layer(os.path.join(self.storage.path, name)) 
     670                        if l.is_locked('.', True): 
     671                                l.update() 
     672                                return True 
     673                        l.update() 
     674                return False 
    484675 
    485676        #node handling functions 
     
    505696        def append_log_messages(self, id, messages): 
    506697                '''Append log messages to the stored log cache for node id.''' 
    507                 raise NotImplementedError 
     698                if not storage: 
     699                        return False 
     700                if id not in self.list_nodes(): 
     701                        self.add_node(id) 
     702                log_file = open(os.path.join(self.storage.logs_dir, id), 'a') 
     703                for message in messages: 
     704                        pickle.dump(message, log_file) 
     705                log_file.close() 
     706                return True 
    508707 
    509708        def get_log_messages(self, id): 
    510709                '''Retrieve all log messages stored for node id.''' 
    511                 raise NotImplementedError 
     710                if not storage: 
     711                        return [] 
     712                if id not in self.list_nodes(): 
     713                        return [] 
     714                log_file = open(os.path.join(self.storage.logs_dir, id), 'r') 
     715                messages = pickle.load(log_file) 
     716                log_file.close() 
     717                return messages 
    512718 
    513719        #TODO should there be a clear log?