Example #1
0
 def destroy(self):
     try:
         self.unmount()
         zfs_destroy(self.zfs, recursive=True)
         self.path.rmdir()
     except ZFSError as e:
         raise ZCMError(e.message)
     except OSError as e:
         raise ZCMError('Could not destroy path ' + self.path)
Example #2
0
 def mount(self):
     if not self.active_clone:
         raise ZCMError('There is no active clone, activate one first')
     try:
         zfs_mount(self.active_clone.zfs)
         zfs_mount(self.zfs)
         for clone in self.clones:
             if clone != self.active_clone:
                 zfs_mount(clone.zfs)
     except ZFSError as e:
         raise ZCMError(e.message)
Example #3
0
 def remove(self, id):
     clone = self.get_clone(id)
     if clone == self.active_clone:
         raise ZCMError('Manager with id %s is active, can not remove' % id)
     clones = self.find_clones_with_origin(id)
     promoted = None
     if clones:
         promoted = clones[-1]
         zfs_promote(promoted.zfs)
     try:
         zfs_destroy(clone.zfs)
         if clone.origin:
             zfs_destroy(clone.origin)
         if promoted:
             zfs_destroy('%s@%s' % (promoted.zfs, promoted.id))
         log.info('Removed clone ' + clone.id)
         self.load()
     except ZFSError as e:
         raise ZCMError(e.message)
Example #4
0
 def unmount(self):
     try:
         for clone in self.clones:
             if clone != self.active_clone:
                 zfs_unmount(clone.zfs)
         zfs_unmount(self.zfs)
         if self.active_clone is not None:
             zfs_unmount(self.active_clone.zfs)
     except ZFSError as e:
         # at lest one unmount failed, remount all and fail
         self.mount()
         raise ZCMError(e.message)
Example #5
0
 def auto_remove(self, max_newer=None, max_older=None, max_total=None):
     while max_older is not None and len(self.older_clones) > max_older:
         self.remove(self.older_clones[0].id)
     while max_newer is not None and len(self.newer_clones) > max_newer:
         self.remove(self.newer_clones[0].id)
     while max_total is not None and len(self.clones) > max_total:
         if self.older_clones:
             self.remove(self.older_clones[0].id)
         elif self.newer_clones:
             self.remove(self.newer_clones[0].id)
         else:
             raise ZCMError(
                 'There are no more clones to remove in order to satisfy max limit of '
                 + max_total)
Example #6
0
 def clone(self, max_newer=None, max_total=None, auto_remove=False):
     if not self.active_clone:
         raise ZCMError('There is no active clone, activate one first')
     if not auto_remove and max_newer is not None and len(
             self.newer_clones) >= max_newer:
         raise ZCMException(
             'There are already %d newer clones, can not create another' %
             len(self.newer_clones))
     if not auto_remove and max_total is not None and len(
             self.clones) >= max_total:
         raise ZCMException(
             'There are already %d clones, can not create another' %
             len(self.clones))
     id = self.next_id
     try:
         snapshot = zfs_snapshot(id, self.active_clone.zfs)
         zfs = zfs_clone(self.zfs + '/' + id, snapshot)
     except ZFSError as e:
         raise ZCMError(e.message)
     self.load()
     clone = self.get_clone(id)
     log.info('Created clone ' + clone.id)
     self.auto_remove(max_newer=max_newer, max_total=max_total)
     return clone
Example #7
0
 def load(self):
     if not isinstance(self.zfs, str):
         raise ZCMError('The name property is invalid: ' + str(self.zfs))
     self.path = None
     self.clones = []
     self.older_clones = []
     self.newer_clones = []
     self.active_clone = None
     self.next_id = None
     self.size = None
     last_id = 0
     zfs_list_output = zfs_list(self.zfs,
                                zfs_type='filesystem',
                                properties=[
                                    'name', 'zfs_clone_manager:path',
                                    'origin', 'mountpoint', 'creation',
                                    'used'
                                ],
                                recursive=True)
     if not zfs_list_output:
         raise ZCMError('There is no ZCM manager at %s' % self.zfs)
     for zfs in zfs_list_output:
         if self.path is None:
             self.zfs = zfs['name']
             if zfs['zfs_clone_manager:path'] is None:
                 raise ZCMError('The ZFS %s is not a valid ZCM manager' %
                                zfs['name'])
             self.path = zfs['zfs_clone_manager:path']
             self.size = zfs['used']
         else:
             splitted_name = zfs['name'].split('/')
             name = '/'.join(splitted_name[:-1])
             id = splitted_name[-1]
             if name != self.zfs:
                 raise ZCMError('The ZFS %s is not a valid ZCM clone' %
                                zfs['name'])
             try:
                 last_id = max(last_id, int(id, base=16))
             except ValueError:
                 raise ZCMError('The ZFS %s is not a valid ZCM clone' %
                                zfs['name'])
             origin_id = snapshot_to_origin_id(zfs['origin'])
             clone = Clone(id, zfs['name'], zfs['origin'], origin_id,
                           zfs['mountpoint'], zfs['creation'], zfs['used'])
             if not isinstance(self.path, Path) or not self.path.is_dir():
                 raise ZCMError('The path property is invalid: ' +
                                self.path)
             if zfs['mountpoint'] == self.path:
                 self.active_clone = clone
             else:
                 if self.active_clone:
                     self.newer_clones.append(clone)
                 else:
                     self.older_clones.append(clone)
             self.clones.append(clone)
     self.next_id = format(last_id + 1, '08x')
Example #8
0
    def activate(self,
                 id,
                 max_newer=None,
                 max_older=None,
                 max_total=None,
                 auto_remove=False):
        next_active = self.get_clone(id)
        if next_active == self.active_clone:
            raise ZCMException('Manager %s already active' % id)
        if not auto_remove and (max_newer is not None
                                or max_older is not None):
            newer_count = 0
            older_count = 0
            has_reach_active = False
            for clone in self.clones:
                if clone == next_active:
                    has_reach_active = True
                else:
                    if has_reach_active:
                        newer_count += 1
                    else:
                        older_count += 1
            if not auto_remove and max_newer is not None and newer_count > max_newer:
                raise ZCMException(
                    'Command denied, Activating %s violates the maximum number of newer clones (%d/%d)'
                    % (id, newer_count, max_newer))
            if not auto_remove and max_older is not None and older_count > max_older:
                raise ZCMException(
                    'Command denied, Activating %s violates the maximum number of older clones (%d/%d)'
                    % (id, older_count, max_older))

        self.unmount()
        try:
            if self.active_clone is not None:
                zfs_inherit(self.active_clone.zfs, 'mountpoint')
            zfs_set(next_active.zfs, mountpoint=self.path)
        except ZFSError as e:
            raise ZCMError(e.message)
        self.active_clone = next_active
        self.mount()

        log.info('Activated clone ' + id)
        self.load()
        self.auto_remove(max_newer=max_newer,
                         max_older=max_older,
                         max_total=max_total)
        return next_active
Example #9
0
 def initialize_manager(zfs_str, path_str, migrate=None):
     path = Path(path_str)
     zfs_list_output = zfs_list(zfs_str,
                                zfs_type='all',
                                properties=[
                                    'name', 'type',
                                    'zfs_clone_manager:path', 'origin',
                                    'mountpoint'
                                ],
                                recursive=True)
     if zfs_list_output:
         zfs = zfs_list_output[0]
         if zfs['zfs_clone_manager:path']:
             raise ZCMError(
                 'The ZFS %s is a ZCM manager, will not initialize' %
                 zfs_str)
         if len(zfs_list_output) > 1:
             raise ZCMError(
                 'The ZFS %s has children, can not initialize ZCM with it' %
                 zfs_str)
         if zfs['type'] != 'filesystem':
             raise ZCMError(
                 'The ZFS %s is of type %s, can not initialize ZCM with it'
                 % (zfs_str, zfs['type']))
         if migrate != 'ZFS':
             raise ZCMError(
                 'ZFS %s already exists, will not initialize a manager with it'
                 % zfs_str)
         if zfs['mountpoint'] != path and path.exists():
             raise ZCMError(
                 'Path %s already exists (and it is not the ZFS %s mountpoint, can not use it'
                 % (path_str, zfs_str))
         random_id = id_generator()
         zfs_parts = zfs_str.split('/')[:-1]
         zfs_parts.append(random_id)
         random_zfs = '/'.join(zfs_parts)
         try:
             zfs_unmount(zfs_str)
             zfs_rename(zfs_str, random_zfs)
             zfs_create(zfs_str, zcm_path=path_str, recursive=True)
             zfs_unmount(zfs_str)
             zfs_rename(random_zfs, zfs_str + '/00000000')
             zfs_set(zfs_str + '/00000000', mountpoint=path)
             zfs_mount(zfs_str + '/00000000')
             zfs_set(zfs_str, mountpoint=Path(path, '.clones'))
             zfs_mount(zfs_str)
             log.info('Migrated ZFS %s at path %s to ZCM' %
                      (zfs_str, path_str))
         except ZFSError as e:
             raise ZCMError(e.message)
         return
     original_path = None
     if path.exists():
         if migrate != 'PATH':
             raise ZCMError(
                 'Path %s already exists, will not initialize a manager' %
                 path_str)
         random_id = id_generator()
         original_path = Path(path.parents[0], random_id)
         path.rename(original_path)
     try:
         zfs_create(zfs_str, zcm_path=path_str, recursive=True)
         zfs_unmount(zfs_str)
         zfs_create('00000000', zfs_str, mountpoint=path)
         zfs_set(zfs_str, mountpoint=Path(path, '.clones'))
         zfs_mount(zfs_str)
         log.info('Created ZCM %s at path %s' % (zfs_str, path_str))
     except ZFSError as e:
         raise ZCMError(e.message)
     if original_path:
         if copy_directory(original_path, path) != 0:
             raise ZCMError(
                 'Could not move content of original directory, kept at %s'
                 % original_path)
         shutil.rmtree(original_path)
         log.info('Moved content of path %s to clone' % path_str)
Example #10
0
 def get_clone(self, id):
     for clone in self.clones:
         if clone.id == id:
             return clone
     raise ZCMError('There is no clone with id ' + id)