Esempio n. 1
0
    def post(self, request, pname, command=None):
        pool = self._validate_pool(pname, request)
        if (command is not None and command != 'status'):
            e_msg = ('Unknown balance command: %s' % command)
            handle_exception(Exception(e_msg), request)

        with self._handle_exception(request):
            disk = Disk.objects.filter(pool=pool)[0]
            ps = self._balance_status(pool, disk)
            if (command == 'status'):
                return Response(PoolBalanceSerializer(ps).data)
            force = request.DATA.get('force', False)
            if ((PoolBalance.objects.filter(pool=pool,
                                            status__regex=r'(started|running)')
                 .exists())):
                if (force):
                    p = PoolBalance.objects.filter(
                        pool=pool,
                        status__regex=r'(started|running)').order_by('-id')[0]
                    p.status = 'terminated'
                    p.save()
                else:
                    e_msg = ('A Balance process is already running for '
                             'pool(%s).' % pname)
                    handle_exception(Exception(e_msg), request)

            balance_pid = balance_start(pool, disk.name, force=force)
            ps = PoolBalance(pool=pool, pid=balance_pid)
            ps.save()
            return Response(PoolBalanceSerializer(ps).data)
Esempio n. 2
0
    def post(self, request, pname, command=None):
        pool = self._validate_pool(pname, request)
        if (command is not None and command != 'status'):
            e_msg = ('Unknown balance command: %s' % command)
            handle_exception(Exception(e_msg), request)

        with self._handle_exception(request):
            disk = Disk.objects.filter(pool=pool)[0]
            ps = self._balance_status(pool, disk)
            if (command == 'status'):
                return Response(PoolBalanceSerializer(ps).data)
            force = request.data.get('force', False)
            if ((PoolBalance.objects.filter(pool=pool,
                                            status__regex=r'(started|running)')
                 .exists())):
                if (force):
                    p = PoolBalance.objects.filter(
                        pool=pool,
                        status__regex=r'(started|running)').order_by('-id')[0]
                    p.status = 'terminated'
                    p.save()
                else:
                    e_msg = ('A Balance process is already running for '
                             'pool(%s).' % pname)
                    handle_exception(Exception(e_msg), request)

            balance_pid = balance_start(pool, disk.name, force=force)
            ps = PoolBalance(pool=pool, pid=balance_pid)
            ps.save()
            return Response(PoolBalanceSerializer(ps).data)
Esempio n. 3
0
    def put(self, request, pname, command):
        """
        resize a pool.
        @pname: pool's name
        @command: 'add' - add a list of disks and hence expand the pool
                  'remove' - remove a list of disks and hence shrink the pool
        """
        with self._handle_exception(request):
            if pname == settings.ROOT_POOL:
                e_msg = (
                    "Edit operations are not allowed on this Pool(%s) " "as it contains the operating system." % pname
                )
                handle_exception(Exception(e_msg), request)
            try:
                pool = Pool.objects.get(name=pname)
            except:
                e_msg = "Pool(%s) does not exist." % pname
                handle_exception(Exception(e_msg), request)

            if command == "remount":
                return self._remount(request, pool)

            disks = [self._validate_disk(d, request) for d in request.data.get("disks")]
            num_new_disks = len(disks)
            if num_new_disks == 0:
                e_msg = "List of disks in the input cannot be empty."
                handle_exception(Exception(e_msg), request)
            dnames = [d.name for d in disks]
            mount_disk = Disk.objects.filter(pool=pool)[0].name
            new_raid = request.data.get("raid_level", pool.raid)
            num_total_disks = Disk.objects.filter(pool=pool).count() + num_new_disks
            usage = pool_usage("/%s/%s" % (settings.MNT_PT, pool.name))
            # free_percent = (usage[2]/usage[0]) * 100
            free_percent = (usage[2] * 100) / usage[0]
            threshold_percent = self.ADD_THRESHOLD * 100
            if command == "add":
                for d in disks:
                    if d.pool is not None:
                        e_msg = (
                            "Disk(%s) cannot be added to this Pool(%s) "
                            "because it belongs to another pool(%s)" % (d.name, pool.name, d.pool.name)
                        )
                        handle_exception(Exception(e_msg), request)
                    if d.btrfs_uuid is not None:
                        e_msg = (
                            "Disk(%s) has a BTRFS filesystem from the "
                            "past. If you really like to add it, wipe it "
                            "from the Storage -> Disks screen of the "
                            "web-ui" % d.name
                        )
                        handle_exception(Exception(e_msg), request)
                if new_raid not in self.SUPPORTED_MIGRATIONS[pool.raid]:
                    e_msg = "Pool migration from %s to %s is not supported." % (pool.raid, new_raid)
                    handle_exception(Exception(e_msg), request)

                if PoolBalance.objects.filter(pool=pool, status__regex=r"(started|running)").exists():
                    e_msg = (
                        "A Balance process is already running for this "
                        "pool(%s). Resize is not supported during a "
                        "balance process." % pool.name
                    )
                    handle_exception(Exception(e_msg), request)

                if free_percent < threshold_percent:
                    e_msg = (
                        "Resize is only supported when there is at least "
                        "%d percent free space available. But currently "
                        "only %d percent is free. Remove some data and "
                        "try again." % (threshold_percent, free_percent)
                    )
                    handle_exception(Exception(e_msg), request)

                if new_raid != pool.raid:
                    if (pool.raid in ("single", "raid0")) and new_raid in ("raid1", "raid10"):
                        cur_num_disks = num_total_disks - num_new_disks
                        if num_new_disks < cur_num_disks:
                            e_msg = (
                                "For single/raid0 to raid1/raid10 "
                                "conversion, at least as many as present "
                                "number of disks must be added. %d "
                                "disks are provided, but at least %d are "
                                "required." % (num_new_disks, cur_num_disks)
                            )
                            handle_exception(Exception(e_msg), request)

                resize_pool(pool, mount_disk, dnames)
                balance_pid = balance_start(pool, mount_disk, convert=new_raid)
                ps = PoolBalance(pool=pool, pid=balance_pid)
                ps.save()

                pool.raid = new_raid
                for d_o in disks:
                    d_o.pool = pool
                    d_o.save()

            elif command == "remove":
                if new_raid != pool.raid:
                    e_msg = "Raid configuration cannot be changed while " "removing disks"
                    handle_exception(Exception(e_msg), request)
                for d in disks:
                    if d.pool is None or d.pool != pool:
                        e_msg = "Disk(%s) cannot be removed because it does " "not belong to this Pool(%s)" % (
                            d.name,
                            pool.name,
                        )
                        handle_exception(Exception(e_msg), request)
                remaining_disks = Disk.objects.filter(pool=pool).count() - num_new_disks
                if pool.raid in ("raid0", "single"):
                    e_msg = "Disks cannot be removed from a pool with this " "raid(%s) configuration" % pool.raid
                    handle_exception(Exception(e_msg), request)

                if pool.raid in ("raid5", "raid6"):
                    e_msg = "Disk removal is not supported for pools with " "raid5/6 configuration"
                    handle_exception(Exception(e_msg), request)

                if pool.raid == "raid10":
                    if num_new_disks != 2:
                        e_msg = (
                            "Only two disks can be removed at once from "
                            "this pool because of its raid "
                            "configuration(%s)" % pool.raid
                        )
                        handle_exception(Exception(e_msg), request)
                    elif remaining_disks < 4:
                        e_msg = (
                            "Disks cannot be removed from this pool "
                            "because its raid configuration(%s) "
                            "requires a minimum of 4 disks" % pool.raid
                        )
                        handle_exception(Exception(e_msg), request)

                elif pool.raid == "raid1":
                    if num_new_disks != 1:
                        e_msg = (
                            "Only one disk can be removed at once from "
                            "this pool because of its raid "
                            "configuration(%s)" % pool.raid
                        )
                        handle_exception(Exception(e_msg), request)
                    elif remaining_disks < 2:
                        e_msg = (
                            "Disks cannot be removed from this pool "
                            "because its raid configuration(%s) "
                            "requires a minimum of 2 disks" % pool.raid
                        )
                        handle_exception(Exception(e_msg), request)

                threshold_percent = 100 - threshold_percent
                if free_percent < threshold_percent:
                    e_msg = (
                        "Removing disks is only supported when there is "
                        "at least %d percent free space available. But "
                        "currently only %d percent is free. Remove some "
                        "data and try again." % (threshold_percent, free_percent)
                    )
                    handle_exception(Exception(e_msg), request)

                resize_pool(pool, mount_disk, dnames, add=False)
                balance_pid = balance_start(pool, mount_disk)
                ps = PoolBalance(pool=pool, pid=balance_pid)
                ps.save()

                for d in disks:
                    d.pool = None
                    d.save()

            else:
                e_msg = "command(%s) is not supported." % command
                handle_exception(Exception(e_msg), request)
            usage = pool_usage("/%s/%s" % (settings.MNT_PT, pool.name))
            pool.size = usage[0]
            pool.save()
            return Response(PoolInfoSerializer(pool).data)
Esempio n. 4
0
    def put(self, request, pname, command):
        """
        resize a pool.
        @pname: pool's name
        @command: 'add' - add a list of disks and hence expand the pool
                  'remove' - remove a list of disks and hence shrink the pool
        """
        with self._handle_exception(request):
            if (pname == settings.ROOT_POOL):
                e_msg = ('Edit operations are not allowed on this Pool(%s) '
                         'as it contains the operating system.' % pname)
                handle_exception(Exception(e_msg), request)
            try:
                pool = Pool.objects.get(name=pname)
            except:
                e_msg = ('Pool(%s) does not exist.' % pname)
                handle_exception(Exception(e_msg), request)

            if (command == 'remount'):
                return self._remount(request, pool)

            disks = [self._validate_disk(d, request) for d in
                     request.data.get('disks')]
            num_new_disks = len(disks)
            if (num_new_disks == 0):
                e_msg = ('List of disks in the input cannot be empty.')
                handle_exception(Exception(e_msg), request)
            dnames = [d.name for d in disks]
            mount_disk = Disk.objects.filter(pool=pool)[0].name
            new_raid = request.data.get('raid_level', pool.raid)
            num_total_disks = (Disk.objects.filter(pool=pool).count() +
                               num_new_disks)
            usage = pool_usage('/%s/%s' % (settings.MNT_PT, pool.name))
            # free_percent = (usage[2]/usage[0]) * 100
            free_percent = (usage[2]* 100)/usage[0]
            threshold_percent = self.ADD_THRESHOLD * 100
            if (command == 'add'):
                for d in disks:
                    if (d.pool is not None):
                        e_msg = ('Disk(%s) cannot be added to this Pool(%s) '
                                 'because it belongs to another pool(%s)' %
                                 (d.name, pool.name, d.pool.name))
                        handle_exception(Exception(e_msg), request)
                    if (d.btrfs_uuid is not None):
                        e_msg = ('Disk(%s) has a BTRFS filesystem from the '
                                 'past. If you really like to add it, wipe it '
                                 'from the Storage -> Disks screen of the '
                                 'web-ui' % d.name)
                        handle_exception(Exception(e_msg), request)
                if (new_raid not in self.SUPPORTED_MIGRATIONS[pool.raid]):
                    e_msg = ('Pool migration from %s to %s is not supported.'
                             % (pool.raid, new_raid))
                    handle_exception(Exception(e_msg), request)

                if (PoolBalance.objects.filter(
                        pool=pool,
                        status__regex=r'(started|running)').exists()):
                    e_msg = ('A Balance process is already running for this '
                             'pool(%s). Resize is not supported during a '
                             'balance process.' % pool.name)
                    handle_exception(Exception(e_msg), request)

                if (free_percent < threshold_percent):
                    e_msg = ('Resize is only supported when there is at least '
                             '%d percent free space available. But currently '
                             'only %d percent is free. Remove some data and '
                             'try again.' % (threshold_percent, free_percent))
                    handle_exception(Exception(e_msg), request)

                if (new_raid != pool.raid):
                    if (((pool.raid in ('single', 'raid0')) and
                         new_raid in ('raid1', 'raid10'))):
                        cur_num_disks = num_total_disks - num_new_disks
                        if (num_new_disks < cur_num_disks):
                            e_msg = ('For single/raid0 to raid1/raid10 '
                                     'conversion, at least as many as present '
                                     'number of disks must be added. %d '
                                     'disks are provided, but at least %d are '
                                     'required.' % (num_new_disks,
                                                    cur_num_disks))
                            handle_exception(Exception(e_msg), request)

                resize_pool(pool, mount_disk, dnames)
                balance_pid = balance_start(pool, mount_disk, convert=new_raid)
                ps = PoolBalance(pool=pool, pid=balance_pid)
                ps.save()

                pool.raid = new_raid
                for d_o in disks:
                    d_o.pool = pool
                    d_o.save()

            elif (command == 'remove'):
                if (new_raid != pool.raid):
                    e_msg = ('Raid configuration cannot be changed while '
                             'removing disks')
                    handle_exception(Exception(e_msg), request)
                for d in disks:
                    if (d.pool is None or d.pool != pool):
                        e_msg = ('Disk(%s) cannot be removed because it does '
                                 'not belong to this Pool(%s)' %
                                 (d.name, pool.name))
                        handle_exception(Exception(e_msg), request)
                remaining_disks = (Disk.objects.filter(pool=pool).count() -
                                   num_new_disks)
                if (pool.raid in ('raid0', 'single',)):
                    e_msg = ('Disks cannot be removed from a pool with this '
                             'raid(%s) configuration' % pool.raid)
                    handle_exception(Exception(e_msg), request)

                if (pool.raid in ('raid5', 'raid6',)):
                    e_msg = ('Disk removal is not supported for pools with '
                             'raid5/6 configuration')
                    handle_exception(Exception(e_msg), request)

                if (pool.raid == 'raid10'):
                    if (num_new_disks != 2):
                        e_msg = ('Only two disks can be removed at once from '
                                 'this pool because of its raid '
                                 'configuration(%s)' % pool.raid)
                        handle_exception(Exception(e_msg), request)
                    elif (remaining_disks < 4):
                        e_msg = ('Disks cannot be removed from this pool '
                                 'because its raid configuration(%s) '
                                 'requires a minimum of 4 disks' % pool.raid)
                        handle_exception(Exception(e_msg), request)

                elif (pool.raid == 'raid1'):
                    if (num_new_disks != 1):
                        e_msg = ('Only one disk can be removed at once from '
                                 'this pool because of its raid '
                                 'configuration(%s)' % pool.raid)
                        handle_exception(Exception(e_msg), request)
                    elif (remaining_disks < 2):
                        e_msg = ('Disks cannot be removed from this pool '
                                 'because its raid configuration(%s) '
                                 'requires a minimum of 2 disks' % pool.raid)
                        handle_exception(Exception(e_msg), request)

                threshold_percent = 100 - threshold_percent
                if (free_percent < threshold_percent):
                    e_msg = ('Removing disks is only supported when there is '
                             'at least %d percent free space available. But '
                             'currently only %d percent is free. Remove some '
                             'data and try again.' %
                             (threshold_percent, free_percent))
                    handle_exception(Exception(e_msg), request)

                resize_pool(pool, mount_disk, dnames, add=False)
                balance_pid = balance_start(pool, mount_disk)
                ps = PoolBalance(pool=pool, pid=balance_pid)
                ps.save()

                for d in disks:
                    d.pool = None
                    d.save()

            else:
                e_msg = ('command(%s) is not supported.' % command)
                handle_exception(Exception(e_msg), request)
            usage = pool_usage('/%s/%s' % (settings.MNT_PT, pool.name))
            pool.size = usage[0]
            pool.save()
            return Response(PoolInfoSerializer(pool).data)
Esempio n. 5
0
    def put(self, request, pname, command):
        """
        resize a pool.
        @pname: pool's name
        @command: 'add' - add a list of disks and hence expand the pool
                  'remove' - remove a list of disks and hence shrink the pool
        """
        with self._handle_exception(request):
            if (pname == settings.ROOT_POOL):
                e_msg = ('Edit operations are not allowed on this Pool(%s) '
                         'as it contains the operating system.' % pname)
                handle_exception(Exception(e_msg), request)
            try:
                pool = Pool.objects.get(name=pname)
            except:
                e_msg = ('Pool(%s) does not exist.' % pname)
                handle_exception(Exception(e_msg), request)

            if (command == 'remount'):
                return self._remount(request, pool)

            disks = [
                self._validate_disk(d, request)
                for d in request.data.get('disks')
            ]
            num_new_disks = len(disks)
            if (num_new_disks == 0):
                e_msg = ('List of disks in the input cannot be empty.')
                handle_exception(Exception(e_msg), request)
            dnames = [d.name for d in disks]
            mount_disk = Disk.objects.filter(pool=pool)[0].name
            new_raid = request.data.get('raid_level', pool.raid)
            num_total_disks = (Disk.objects.filter(pool=pool).count() +
                               num_new_disks)
            usage = pool_usage('/%s/%s' % (settings.MNT_PT, pool.name))
            # free_percent = (usage[2]/usage[0]) * 100
            free_percent = (usage[2] * 100) / usage[0]
            threshold_percent = self.ADD_THRESHOLD * 100
            if (command == 'add'):
                for d in disks:
                    if (d.pool is not None):
                        e_msg = ('Disk(%s) cannot be added to this Pool(%s) '
                                 'because it belongs to another pool(%s)' %
                                 (d.name, pool.name, d.pool.name))
                        handle_exception(Exception(e_msg), request)
                    if (d.btrfs_uuid is not None):
                        e_msg = ('Disk(%s) has a BTRFS filesystem from the '
                                 'past. If you really like to add it, wipe it '
                                 'from the Storage -> Disks screen of the '
                                 'web-ui' % d.name)
                        handle_exception(Exception(e_msg), request)
                if (new_raid not in self.SUPPORTED_MIGRATIONS[pool.raid]):
                    e_msg = ('Pool migration from %s to %s is not supported.' %
                             (pool.raid, new_raid))
                    handle_exception(Exception(e_msg), request)

                if (PoolBalance.objects.filter(
                        pool=pool,
                        status__regex=r'(started|running)').exists()):
                    e_msg = ('A Balance process is already running for this '
                             'pool(%s). Resize is not supported during a '
                             'balance process.' % pool.name)
                    handle_exception(Exception(e_msg), request)

                if (free_percent < threshold_percent):
                    e_msg = ('Resize is only supported when there is at least '
                             '%d percent free space available. But currently '
                             'only %d percent is free. Remove some data and '
                             'try again.' % (threshold_percent, free_percent))
                    handle_exception(Exception(e_msg), request)

                if (new_raid != pool.raid):
                    if (((pool.raid in ('single', 'raid0'))
                         and new_raid in ('raid1', 'raid10'))):
                        cur_num_disks = num_total_disks - num_new_disks
                        if (num_new_disks < cur_num_disks):
                            e_msg = ('For single/raid0 to raid1/raid10 '
                                     'conversion, at least as many as present '
                                     'number of disks must be added. %d '
                                     'disks are provided, but at least %d are '
                                     'required.' %
                                     (num_new_disks, cur_num_disks))
                            handle_exception(Exception(e_msg), request)

                resize_pool(pool, mount_disk, dnames)
                balance_pid = balance_start(pool, mount_disk, convert=new_raid)
                ps = PoolBalance(pool=pool, pid=balance_pid)
                ps.save()

                pool.raid = new_raid
                for d_o in disks:
                    d_o.pool = pool
                    d_o.save()

            elif (command == 'remove'):
                if (new_raid != pool.raid):
                    e_msg = ('Raid configuration cannot be changed while '
                             'removing disks')
                    handle_exception(Exception(e_msg), request)
                for d in disks:
                    if (d.pool is None or d.pool != pool):
                        e_msg = ('Disk(%s) cannot be removed because it does '
                                 'not belong to this Pool(%s)' %
                                 (d.name, pool.name))
                        handle_exception(Exception(e_msg), request)
                remaining_disks = (Disk.objects.filter(pool=pool).count() -
                                   num_new_disks)
                if (pool.raid in (
                        'raid0',
                        'single',
                )):
                    e_msg = ('Disks cannot be removed from a pool with this '
                             'raid(%s) configuration' % pool.raid)
                    handle_exception(Exception(e_msg), request)

                if (pool.raid in (
                        'raid5',
                        'raid6',
                )):
                    e_msg = ('Disk removal is not supported for pools with '
                             'raid5/6 configuration')
                    handle_exception(Exception(e_msg), request)

                if (pool.raid == 'raid10'):
                    if (num_new_disks != 2):
                        e_msg = ('Only two disks can be removed at once from '
                                 'this pool because of its raid '
                                 'configuration(%s)' % pool.raid)
                        handle_exception(Exception(e_msg), request)
                    elif (remaining_disks < 4):
                        e_msg = ('Disks cannot be removed from this pool '
                                 'because its raid configuration(%s) '
                                 'requires a minimum of 4 disks' % pool.raid)
                        handle_exception(Exception(e_msg), request)

                elif (pool.raid == 'raid1'):
                    if (num_new_disks != 1):
                        e_msg = ('Only one disk can be removed at once from '
                                 'this pool because of its raid '
                                 'configuration(%s)' % pool.raid)
                        handle_exception(Exception(e_msg), request)
                    elif (remaining_disks < 2):
                        e_msg = ('Disks cannot be removed from this pool '
                                 'because its raid configuration(%s) '
                                 'requires a minimum of 2 disks' % pool.raid)
                        handle_exception(Exception(e_msg), request)

                threshold_percent = 100 - threshold_percent
                if (free_percent < threshold_percent):
                    e_msg = ('Removing disks is only supported when there is '
                             'at least %d percent free space available. But '
                             'currently only %d percent is free. Remove some '
                             'data and try again.' %
                             (threshold_percent, free_percent))
                    handle_exception(Exception(e_msg), request)

                resize_pool(pool, mount_disk, dnames, add=False)
                balance_pid = balance_start(pool, mount_disk)
                ps = PoolBalance(pool=pool, pid=balance_pid)
                ps.save()

                for d in disks:
                    d.pool = None
                    d.save()

            else:
                e_msg = ('command(%s) is not supported.' % command)
                handle_exception(Exception(e_msg), request)
            usage = pool_usage('/%s/%s' % (settings.MNT_PT, pool.name))
            pool.size = usage[0]
            pool.save()
            return Response(PoolInfoSerializer(pool).data)