diff --git a/ufo/gluster/swift/common/ring.py b/ufo/gluster/swift/common/ring.py index 9bac39c..06aab8d 100644 --- a/ufo/gluster/swift/common/ring.py +++ b/ufo/gluster/swift/common/ring.py @@ -37,6 +37,29 @@ if not reseller_prefix.endswith('_'): reseller_prefix = reseller_prefix + '_' class Ring(ring.Ring): + def _get_part_nodes(self, part): + seen_ids = set() + nodes = [dev for dev in self._devs \ + if dev['device'] == self.acc_name \ + and not (dev['id'] in seen_ids \ + or seen_ids.add(dev['id']))] + if not nodes: + nodes = [self.false_node] + return nodes + + def get_part_nodes(self, part): + """ + Get the nodes that are responsible for the partition. If one + node is responsible for more than one replica of the same + partition, it will only appear in the output once. + + :param part: partition to get nodes for + :returns: list of node dicts + + See :func:`get_nodes` for a description of the node dicts. + """ + return self._get_part_nodes(part) + def get_nodes(self, account, container=None, obj=None): """ Get the partition and nodes for an account/container/object. @@ -63,20 +86,26 @@ class Ring(ring.Ring): hardware description ====== =============================================================== """ - false_node = [{'zone': 1, 'weight': 100.0, 'ip': '127.0.0.1', 'id': 0, \ + self.false_node = {'zone': 1, 'weight': 100.0, 'ip': '127.0.0.1', 'id': 0, \ 'meta': '', 'device': 'volume_not_in_ring', \ - 'port': 6012}] + 'port': 6012} if account.startswith(reseller_prefix): - acc_name = account.replace(reseller_prefix, '', 1) + self.acc_name = account.replace(reseller_prefix, '', 1) else: - acc_name = account + self.acc_name = account part = 0 - seen_ids = set() - nodes = [dev for dev in self._devs \ - if dev['device'] == acc_name \ - and not (dev['id'] in seen_ids \ - or seen_ids.add(dev['id']))] - if not nodes: - nodes = false_node - return part, nodes + return part, self._get_part_nodes(part) + + + def get_more_nodes(self, part): + """ + Generator to get extra nodes for a partition for hinted handoff. + + :param part: partition to get handoff nodes for + :returns: generator of node dicts + + See :func:`get_nodes` for a description of the node dicts. + Should never be called in the swift UFO environment, so yield nothing + """ + yield self.false_node diff --git a/ufo/test/unit/common/test_ring.py b/ufo/test/unit/common/test_ring.py index 4fb964e..48ed952 100644 --- a/ufo/test/unit/common/test_ring.py +++ b/ufo/test/unit/common/test_ring.py @@ -30,14 +30,52 @@ class TestRing(unittest.TestCase): def setUp(self): self.ring = Ring(SWIFT_DIR, ring_name='object') - def test_get_notes(self): + def test_first_device(self): try: __devs = self.ring._devs self.ring._devs = _mock_ring_data() + part, node = self.ring.get_nodes('test') assert node[0]['device'] == 'test' + node = self.ring.get_part_nodes(0) + assert node[0]['device'] == 'test' + for node in self.ring.get_more_nodes(0): + assert node['device'] == 'volume_not_in_ring' + finally: + self.ring._devs = __devs + + def test_invalid_device(self): + try: + __devs = self.ring._devs + self.ring._devs = _mock_ring_data() + part, node = self.ring.get_nodes('test2') - assert node + assert node[0]['device'] == 'volume_not_in_ring' + node = self.ring.get_part_nodes(0) assert node[0]['device'] == 'volume_not_in_ring' finally: self.ring._devs = __devs + + def test_second_device(self): + try: + __devs = self.ring._devs + self.ring._devs = _mock_ring_data() + + part, node = self.ring.get_nodes('iops') + assert node[0]['device'] == 'iops' + node = self.ring.get_part_nodes(0) + assert node[0]['device'] == 'iops' + for node in self.ring.get_more_nodes(0): + assert node['device'] == 'volume_not_in_ring' + finally: + self.ring._devs = __devs + + def test_second_device_with_reseller_prefix(self): + try: + __devs = self.ring._devs + self.ring._devs = _mock_ring_data() + + part, node = self.ring.get_nodes('AUTH_iops') + assert node[0]['device'] == 'iops' + finally: + self.ring._devs = __devs