Beispiel #1
0
    def create():
        """
swift-ring-builder <builder_file> create <part_power> <replicas>
                                         <min_part_hours>
    Creates <builder_file> with 2^<part_power> partitions and <replicas>.
    <min_part_hours> is number of hours to restrict moving a partition more
    than once.
OR
swift-ring-builder <builder_file> create <part_power> <replicas>
                                         <min_part_hours> <policy_info_file>
    policy_info_file contains the information about the policy
        """
        if len(argv) < 6:
            print Commands.create.__doc__.strip()
            exit(EXIT_ERROR)
        if len(argv) > 6:
            builder = RingBuilder(int(argv[3]), float(argv[4]), int(argv[5]),
                                  argv[6])
        else:
            builder = RingBuilder(int(argv[3]), float(argv[4]), int(argv[5]))
        backup_dir = pathjoin(dirname(argv[1]), 'backups')
        try:
            mkdir(backup_dir)
        except OSError as err:
            if err.errno != EEXIST:
                raise
        builder.save(pathjoin(backup_dir, '%d.' % time() + basename(argv[1])))
        builder.save(argv[1])
        exit(EXIT_SUCCESS)
Beispiel #2
0
    def test_composite_same_device_in_the_different_rings_error(self):
        builders = self.create_sample_ringbuilders(2)
        same_device = copy.deepcopy(builders[0].devs[0])

        # create one more ring which duplicates a device in the first ring
        builder = RingBuilder(6, 3, 1)
        _, fname = tempfile.mkstemp(dir=self.tmpdir)
        # add info to feed to add_dev
        same_device.update({'region': 2, 'weight': 100})
        builder.add_dev(same_device)

        # add rest of the devices, which are unique
        for _ in range(3):
            dev = self.pop_region_device(2)
            builder.add_dev(dev)
        builder.rebalance()
        builder.save(fname)
        # sanity
        self.assertTrue(os.path.exists(fname))

        builders.append(builder)

        with self.assertRaises(ValueError) as cm:
            compose_rings(builders)
        self.assertIn(
            'Duplicate ip/port/device combination %(ip)s/%(port)s/%(device)s '
            'found in builders at indexes 0 and 2' % same_device,
            cm.exception.message)
Beispiel #3
0
    def test_different_part_power_error(self):
        # create a ring builder
        # (default, part power is 6 with create_sample_ringbuilders)
        builders = self.create_sample_ringbuilders(1)

        # prepare another ring which has different part power
        incorrect_builder = RingBuilder(4, 3, 1)
        _, fname = tempfile.mkstemp(dir=self.tmpdir)
        for _ in range(4):
            dev = self.pop_region_device(1)
            incorrect_builder.add_dev(dev)
        incorrect_builder.rebalance()
        incorrect_builder.save(fname)
        # sanity
        self.assertTrue(os.path.exists(fname))

        # sanity
        correct_builder = builders[0]
        self.assertNotEqual(correct_builder.part_shift,
                            incorrect_builder.part_shift)
        self.assertNotEqual(correct_builder.part_power,
                            incorrect_builder.part_power)

        builders.append(incorrect_builder)
        with self.assertRaises(ValueError) as cm:
            compose_rings(builders)
        self.assertIn("All builders must have same value for 'part_power'",
                      cm.exception.message)
Beispiel #4
0
def write_stub_builder(tmpdir, region=1, name=''):
    """
    Pretty much just a three node, three replica, 8 part power builder...

    :param tmpdir: a place to write the builder, be sure to clean it up!
    :param region: an integer, fills in region and ip
    :param name: the name of the builder (i.e. <name>.builder)
    """
    name = name or str(region)
    replicas = 3
    builder = RingBuilder(8, replicas, 1)
    for i in range(replicas):
        dev = {
            'weight': 100,
            'region': '%d' % region,
            'zone': '1',
            'ip': '10.0.0.%d' % region,
            'port': '3600',
            'device': 'sdb%d' % i
        }
        builder.add_dev(dev)
    builder.rebalance()
    builder_file = os.path.join(tmpdir, '%s.builder' % name)
    builder.save(builder_file)
    return builder, builder_file
Beispiel #5
0
    def test_compose_rings_float_replica_count_builder_error(self):
        builders = self.create_sample_ringbuilders(1)

        # prepare another ring which has float replica count
        incorrect_builder = RingBuilder(6, 1.5, 1)
        _, fname = tempfile.mkstemp(dir=self.tmpdir)
        for _ in range(4):
            dev = self.pop_region_device(1)
            incorrect_builder.add_dev(dev)
        incorrect_builder.rebalance()
        incorrect_builder.save(fname)
        # sanity
        self.assertTrue(os.path.exists(fname))
        self.assertEqual(1.5, incorrect_builder.replicas)
        # the first replica has 2 ** 6 partitions
        self.assertEqual(
            2 ** 6, len(incorrect_builder._replica2part2dev[0]))
        # but the second replica has the half of the first partitions
        self.assertEqual(
            2 ** 5, len(incorrect_builder._replica2part2dev[1]))
        builders.append(incorrect_builder)

        with self.assertRaises(ValueError) as cm:
            compose_rings(builders)
        self.assertIn("Problem with builders", cm.exception.message)
        self.assertIn("Non integer replica count", cm.exception.message)
    def create_sample_ring(self):
        """ Create a sample ring with two devices

        At least two devices are needed to test removing
        a device, since removing the last device of a ring
        is not allowed """

        # Ensure there is no existing test builder file because
        # create_sample_ring() might be used more than once in a single test
        try:
            os.remove(self.tmpfile)
        except OSError:
            pass

        ring = RingBuilder(6, 3, 1)
        ring.add_dev({'weight': 100.0,
                      'region': 0,
                      'zone': 0,
                      'ip': '127.0.0.1',
                      'port': 6000,
                      'device': 'sda1',
                      'meta': 'some meta data',
                      })
        ring.add_dev({'weight': 100.0,
                      'region': 1,
                      'zone': 1,
                      'ip': '127.0.0.2',
                      'port': 6001,
                      'device': 'sda2'
                      })
        ring.save(self.tmpfile)
Beispiel #7
0
 def gen_builder(self, balanced=False):
     builder = RingBuilder(18, 3, 1)
     for i in xrange(self.device_count):
         zone = i
         ipaddr = "1.1.1.1"
         port = 6010
         device_name = "sd%s" % i
         weight = 100.0
         meta = "meta for %s" % i
         next_dev_id = 0
         if builder.devs:
             next_dev_id = max(d['id'] for d in builder.devs if d) + 1
         builder.add_dev({
             'id': next_dev_id,
             'zone': zone,
             'ip': ipaddr,
             'port': int(port),
             'device': device_name,
             'weight': weight,
             'meta': meta
         })
     # add an empty dev
     builder.devs.append(None)
     if balanced:
         builder.rebalance()
     return builder
Beispiel #8
0
    def test_reconcile_remove_device(self):
        # given a ring builder with two devices
        b = RingBuilder(10, 1, 1)
        r = RingController(b, "object")
        ds = ["r1z1-192.168.0.2:6000/d3", "r1z2-192.168.2.2:5000/d1"]
        r.reconcile(ds)

        # when device is removed
        ds = ["r1z2-192.168.2.2:5000/d1"]
        r.reconcile(ds)

        # then only one device is present
        devs = b.search_devs([])
        self.assertEqual(1, len(devs))
        self.assertEqual(devs, [{
            'device': 'd1',
            'id': 1,
            'ip': '192.168.2.2',
            'meta': '',
            'parts': 1024,
            'parts_wanted': 0,
            'port': 5000,
            'region': 1,
            'replication_ip': None,
            'replication_port': None,
            'weight': 1.0,
            'zone': 2
        }])
Beispiel #9
0
    def create_sample_ringbuilders(self, num_builders=2):
        """
        Create sample rings with four devices

        :returns: a list of ring builder instances
        """

        builders = []
        for region in range(num_builders):
            fname = os.path.join(self.tmpdir, 'builder_%s.builder' % region)
            builder = RingBuilder(6, 3, 0)
            for _ in range(5):
                dev = self.pop_region_device(region)
                builder.add_dev(dev)
            # remove last dev to simulate a ring with some history
            builder.remove_dev(dev['id'])
            # add a dev that won't be assigned any parts
            new_dev = self.pop_region_device(region)
            new_dev['weight'] = 0
            builder.add_dev(new_dev)
            builder.rebalance()
            builder.save(fname)
            self.assertTrue(os.path.exists(fname))
            builders.append(builder)

        return builders
Beispiel #10
0
    def create():
        """
swift-ring-builder <builder_file> create <part_power> <replicas>
                                         <min_part_hours>
    Creates <builder_file> with 2^<part_power> partitions and <replicas>.
    <min_part_hours> is number of hours to restrict moving a partition more
    than once.
        """
        if len(argv) < 6:
            print(Commands.create.__doc__.strip())
            exit(EXIT_ERROR)
        # 1、生成RingBuilder对象实例
        builder = RingBuilder(int(argv[3]), float(argv[4]), int(argv[5]))

        # 2、创建备份目录
        backup_dir = pathjoin(dirname(builder_file), 'backups')
        try:
            mkdir(backup_dir)
        except OSError as err:
            if err.errno != EEXIST:
                raise

        # 3、保存原始数据到备份目录,以及/etc/swift目录中
        builder.save(
            pathjoin(backup_dir, '%d.' % time() + basename(builder_file)))
        builder.save(builder_file)
        exit(EXIT_SUCCESS)
Beispiel #11
0
def _load_builder(path):
    # lifted straight from /usr/bin/swift-ring-builder
    from swift.common.ring import RingBuilder
    try:
        builder = pickle.load(open(path, 'rb'))
        if not hasattr(builder, 'devs'):
            builder_dict = builder
            builder = RingBuilder(1, 1, 1)
            builder.copy_from(builder_dict)
    except ImportError:  # Happens with really old builder pickles
        builder = RingBuilder(1, 1, 1)
        builder.copy_from(pickle.load(open(path, 'rb')))
    for dev in builder.devs:
        if dev and 'meta' not in dev:
            dev['meta'] = ''

    return builder
Beispiel #12
0
def storage_policies(request):
    """
    Creates a storage policy to swift with an specific ring.
    Allows create replication storage policies and erasure code storage policies
    """

    if request.method == "GET":
        try:
            r = get_redis_connection()
        except RedisError:
            return JSONResponse('Error connecting with DB',
                                status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        keys = r.keys("storage-policy:*")
        storage_policy_list = []
        for key in keys:
            storage_policy = r.hgetall(key)
            to_json_bools(storage_policy, 'deprecated', 'default', 'deployed')
            storage_policy['id'] = str(key).split(':')[-1]
            storage_policy['devices'] = json.loads(storage_policy['devices'])
            storage_policy_list.append(storage_policy)
        return JSONResponse(storage_policy_list, status=status.HTTP_200_OK)

    if request.method == "POST":
        try:
            r = get_redis_connection()
        except RedisError:
            return JSONResponse('Error connecting with DB',
                                status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        data = JSONParser().parse(request)

        if data['policy_type'] == 'EC':
            data['replicas'] = int(data['ec_num_data_fragments']) + int(
                data['ec_num_parity_fragments'])

        try:
            sp_id = str(r.incr('storage-policies:id'))
            key = 'storage-policy:' + sp_id

            ring = RingBuilder(int(data['partition_power']),
                               int(data['replicas']), int(data['time']))
            ring.save(get_policy_file_path(settings.SWIFT_CFG_TMP_DIR, sp_id))

            r.hmset(key, data)
        except:
            return JSONResponse('Error creating the Storage Policy',
                                status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        return JSONResponse('Account created successfully',
                            status=status.HTTP_201_CREATED)

    return JSONResponse('Only HTTP POST requests allowed.',
                        status=status.HTTP_405_METHOD_NOT_ALLOWED)
Beispiel #13
0
 def _write_stub_builder(self, region):
     replicas = 3
     builder = RingBuilder(8, replicas, 1)
     for i in range(replicas):
         dev = {
             'weight': 100,
             'region': '%d' % region,
             'zone': '1',
             'ip': '10.0.0.%d' % region,
             'port': '3600',
             'device': 'sdb%d' % i
         }
         builder.add_dev(dev)
     builder.rebalance()
     builder_file = os.path.join(self.tmpdir, '%d.builder' % region)
     builder.save(builder_file)
     return builder, builder_file
def write_ring(args, devices, builderfile):
    # Make an educated guess about the used port. These are the defaults for
    # TripleO-based deployments in Mitaka
    builder_fname = os.path.basename(builderfile)
    if 'account' in builder_fname:
        port = 6002
    elif 'container' in builder_fname:
        port = 6001
    elif 'object' in builder_fname:
        port = 6000
    else:
        port = 6000

    logging.debug('Set port for new devices to %d' % port)

    if not os.path.isfile(builderfile):
        logging.info('%s not found, creating new builder file', builderfile)
        rb = RingBuilder(args.part_power, args.replicas, args.min_part_hours)
    else:
        logging.info('Using existing builder file %s', builderfile)
        rb = RingBuilder.load(builderfile)

    # Add all missing devices
    for dev in devices:
        _dev = rb.search_devs({'meta': dev['meta']})
        if not _dev:
            dev['weight'] = float(dev.get('size')) / 10**9
            dev['region'] = 1
            dev['zone'] = 1
            dev['port'] = port
            dev['replication_ip'] = dev['ip']
            dev['replication_port'] = dev['port']
            rb.add_dev(dev)
            logging.info('Added device %s / %s', dev['ip'], dev['device'])
        else:
            logging.info('Ignoring existing device %s / %s', dev['ip'],
                         dev['device'])
    try:
        rb.rebalance()
    except RingValidationError as exc:
        logging.error(exc)
    rb.save(builderfile)
    ring_file = os.path.splitext(builderfile)[0] + '.ring.gz'
    ring_data = rb.get_ring()
    ring_data.save(ring_file)
    return [builderfile, ring_file]
Beispiel #15
0
    def write_builder():
        """
swift-ring-builder <ring_file> write_builder [min_part_hours]
    Recreate a builder from a ring file (lossy) if you lost your builder
    backups.  (Protip: don't lose your builder backups).
    [min_part_hours] is one of those numbers lost to the builder,
    you can change it with set_min_part_hours.
        """
        if exists(builder_file):
            print 'Cowardly refusing to overwrite existing ' \
                'Ring Builder file: %s' % builder_file
            exit(EXIT_ERROR)
        if len(argv) > 3:
            min_part_hours = int(argv[3])
        else:
            stderr.write("WARNING: default min_part_hours may not match "
                         "the value in the lost builder.\n")
            min_part_hours = 24
        ring = Ring(ring_file)
        for dev in ring.devs:
            dev.update({
                'parts': 0,
                'parts_wanted': 0,
            })
        builder_dict = {
            'part_power': 32 - ring._part_shift,
            'replicas': float(ring.replica_count),
            'min_part_hours': min_part_hours,
            'parts': ring.partition_count,
            'devs': ring.devs,
            'devs_changed': False,
            'version': 0,
            '_replica2part2dev': ring._replica2part2dev_id,
            '_last_part_moves_epoch': None,
            '_last_part_moves': None,
            '_last_part_gather_start': 0,
            '_remove_devs': [],
        }
        builder = RingBuilder(1, 1, 1)
        builder.copy_from(builder_dict)
        for parts in builder._replica2part2dev:
            for dev_id in parts:
                builder.devs[dev_id]['parts'] += 1
        builder._set_parts_wanted()
        builder.save(builder_file)
Beispiel #16
0
def remake_rings(partpower=16,
                 weight=100,
                 replicas=3,
                 min_part_hours=1,
                 drives=False):
    for service in ['object', 'container', 'account']:
        builder_file = '/etc/swift/%s.builder' % service
        ring_file = '/etc/swift/%s.ring.gz' % service
        try:
            os.remove(builder_file)
            os.remove(ring_file)
        except OSError:
            pass
        builder = RingBuilder(partpower, replicas, min_part_hours)
        builder.save(builder_file)
        devstrs = _get_builder_strings(service, drives)
        for devstr in devstrs:
            new_dev = _parse_add_values(devstr, str(weight))
            builder.add_dev(new_dev[0])
        builder.save(builder_file)
        builder.rebalance(builder_file)
        builder.get_ring().save(ring_file)
Beispiel #17
0
    def test_reconcile_add_devices(self):
        # given a empty ring builder
        b = RingBuilder(10, 1, 1)
        r = RingController(b, "object")

        # when new devices are added
        ds = ["r1z1-192.168.0.2:6000/d3", "r1z1-192.168.2.2:5000/d1"]
        r.reconcile(ds)

        # then new devices are present
        devs = b.search_devs([])
        self.assertEqual(2, len(devs))
        self.assertEqual(devs, [{
            'device': 'd3',
            'id': 0,
            'ip': '192.168.0.2',
            'meta': '',
            'parts': 512,
            'parts_wanted': 0,
            'port': 6000,
            'region': 1,
            'replication_ip': None,
            'replication_port': None,
            'weight': 1.0,
            'zone': 1
        }, {
            'device': 'd1',
            'id': 1,
            'ip': '192.168.2.2',
            'meta': '',
            'parts': 512,
            'parts_wanted': 0,
            'port': 5000,
            'region': 1,
            'replication_ip': None,
            'replication_port': None,
            'weight': 1.0,
            'zone': 1
        }])
Beispiel #18
0
def main(argv):
    setup_logging()
    args = read_args()

    logger.debug(args)
    load_config()
    v1 = client.CoreV1Api()
    config_map = load_config_map(v1, args.config_map_name)

    if config_map.binary_data != None and args.ring_type in config_map.binary_data:
        builder_ = base64.b64decode(config_map.binary_data[args.ring_type])
        logger.info("loading existing ring builder")
        builder = RingBuilder.load("", open=lambda a, b: io.BytesIO(builder_))
    else:
        logger.info("creating a new ring builder")
        builder = RingBuilder(10, 1, 1)

    r = RingController(builder, args.ring_type, logger=logger)
    logger.info("reconciling ring")
    r.reconcile(args.devices)
    ring_data = r.get_ring_data()

    patch_config_map(v1, config_map.metadata, ring_data)
Beispiel #19
0
    def test_get_ring_data(self):
        # given a ring builder with two devices
        b = RingBuilder(10, 1, 1)
        r = RingController(b, "object")
        ds = ["r1z1-192.168.0.2:6000/d3", "r1z2-192.168.2.2:5000/d1"]
        r.reconcile(ds)

        # when ring is serialized
        ring_data = r.get_ring_data()

        # then ring and builder is returned
        self.assertIn("object.ring.gz", ring_data)
        self.assertIn("object", ring_data)

        # then correct compressed ring is returned
        object_ring = decompress(base64.b64decode(ring_data["object.ring.gz"]))
        expected_ring = b'{"byteorder": "little", "devs": [{"device": "d3", "id": 0, "ip": "192.168.0.2", "meta": "", "port": 6000, "region": 1, "replication_ip": null, "replication_port": null, "weight": 1.0, "zone": 1}, {"device": "d1", "id": 1, "ip": "192.168.2.2", "meta": "", "port": 5000, "region": 1, "replication_ip": null, "replication_port": null, "weight": 1.0, "zone": 2}], "part_shift": 22, "replica_count": 1, "version": 3}'
        self.assertIn(expected_ring, object_ring)

        # then correct serialized builder is returned
        builder_ = base64.b64decode(ring_data["object"])
        loadedBuilder = RingBuilder.load(
            "", open=lambda a, b: io.BytesIO(builder_))
        self.assertEqual(b.devs, loadedBuilder.devs)
Beispiel #20
0
    def test_different_replica_count_works(self):
        # create a ring builder
        # (default, part power is 6 with create_sample_ringbuilders)
        builders = self.create_sample_ringbuilders(1)

        # prepare another ring which has different replica count
        builder = RingBuilder(6, 1, 1)
        _, fname = tempfile.mkstemp(dir=self.tmpdir)
        for _ in range(4):
            dev = self.pop_region_device(1)
            builder.add_dev(dev)
        builder.rebalance()
        builder.save(fname)
        # sanity
        self.assertTrue(os.path.exists(fname))
        builders.append(builder)

        rd = compose_rings(builders)
        rd.save(self.output_ring)
        got_ring = Ring(self.output_ring)
        self.assertEqual(got_ring.partition_count, 2**6)
        self.assertEqual(got_ring.replica_count, 4)  # 3 + 1
        self.assertEqual(got_ring._part_shift, 26)
        self.assertDevices(got_ring, builders)
Beispiel #21
0
def initialize_ring(path, part_power, replicas, min_hours):
    """Initialize a new swift ring with given parameters."""
    from swift.common.ring import RingBuilder
    ring = RingBuilder(part_power, replicas, min_hours)
    _write_ring(ring, path)
Beispiel #22
0
import math
import pickle
"""
    https://docs.openstack.org/swift/pike/admin/objectstorage-troubleshoot.html

    Using existing swift tools, there is no way to recover a builder file from a ring.gz file. However, 
    if you have a knowledge of Python, it is possible to construct a builder file that is pretty close to the one you have lost.
"""

ring = RingData.load('/home/shahbazi/Desktop/rings/account.ring.gz')

partitions = len(ring._replica2part2dev_id[0])

replicas = len(ring._replica2part2dev_id)

builder = RingBuilder(int(math.log(partitions, 2)), replicas, 1)

builder.devs = ring.devs

builder._replica2part2dev = ring._replica2part2dev_id

builder._last_part_moves_epoch = 0

builder._last_part_moves = array('B', (0 for _ in range(partitions)))

builder.change_min_part_hours(24)

# builder._set_parts_wanted()

for d in builder._iter_devs():
    d['parts'] = 0