def test_to_json(self): host = "test_host" block = AllocationBlock(network, host) # Set up an allocation attr = { AllocationBlock.ATTR_HANDLE_ID: "test_key", AllocationBlock.ATTR_SECONDARY: { "key1": "value1", "key2": "value2" } } block.attributes.append(attr) block.allocations[5] = 0 assert_equal(block.count_free_addresses(), BLOCK_SIZE - 1) # Read out the JSON json_str = block.to_json() json_dict = json.loads(json_str) assert_equal(json_dict[AllocationBlock.CIDR], str(network)) assert_equal(json_dict[AllocationBlock.AFFINITY], "host:test_host") assert_dict_equal(json_dict[AllocationBlock.ATTRIBUTES][0], attr) expected_allocations = [None] * BLOCK_SIZE expected_allocations[5] = 0 assert_list_equal(json_dict[AllocationBlock.ALLOCATIONS], expected_allocations) # Verify we can read the JSON back in. result = Mock(spec=EtcdResult) result.value = json_str block2 = AllocationBlock.from_etcd_result(result) assert_equal(block2.to_json(), json_str)
def test_init_block_id(self): host = "test_host" block = AllocationBlock(network, host) assert_equal(block.host_affinity, host) assert_equal(block.cidr, network) assert_equal(block.count_free_addresses(), BLOCK_SIZE)
def test_from_etcd_result_no_unallocated(self): """ Test the from_etcd_result processing when the allocation order is missing (this is allowed since it is a new field). """ result = Mock(spec=EtcdResult) # Build a JSON object for the Block attr0 = { AllocationBlock.ATTR_HANDLE_ID: "test_key1", AllocationBlock.ATTR_SECONDARY: { "key1": "value11", "key2": "value21" } } attr1 = { AllocationBlock.ATTR_HANDLE_ID: "test_key2", AllocationBlock.ATTR_SECONDARY: { "key1": "value12", "key2": "value22" } } allocations = [None] * BLOCK_SIZE allocations[0] = 0 allocations[1] = 0 allocations[4] = 1 json_dict = { AllocationBlock.CIDR: str(network), AllocationBlock.AFFINITY: "host:Sammy Davis, Jr.", AllocationBlock.STRICT_AFFINITY: True, AllocationBlock.ALLOCATIONS: allocations, AllocationBlock.ATTRIBUTES: [attr0, attr1] } result.value = json.dumps(json_dict) block = AllocationBlock.from_etcd_result(result) assert_equal(block.count_free_addresses(), BLOCK_SIZE - 3) assert_equal(block.db_result, result) assert_equal(block.cidr, network) assert_equal(block.host_affinity, "Sammy Davis, Jr.") assert_true(block.strict_affinity) assert_list_equal(block.allocations[:5], [0, 0, None, None, 1]) assert_dict_equal(block.attributes[0], attr0) assert_dict_equal(block.attributes[1], attr1) # Verify the allocation order is correctly calculated from the # unassigned entries. unallocated = [o for o in range(BLOCK_SIZE) if o not in (0, 1, 4)] assert_list_equal(block.unallocated, unallocated) # Verify we can get JSON back out. json_dict[AllocationBlock.UNALLOCATED] = unallocated assert_equal(json.dumps(json_dict), block.to_json())
def _read_block(self, block_cidr): """ Read the block from the data store. :param block_cidr: The IPNetwork identifier for a block. :return: An AllocationBlock object """ key = _block_datastore_key(block_cidr) try: result = self.etcd_client.read(key) except EtcdKeyNotFound: raise KeyError(str(block_cidr)) block = AllocationBlock.from_etcd_result(result) return block
def _read_block(self, block_cidr): """ Read the block from the data store. :param block_cidr: The IPNetwork identifier for a block. :return: An AllocationBlock object """ key = _block_datastore_key(block_cidr) try: # Use quorum=True to ensure we don't get stale reads. Without this # we allow many subtle race conditions, such as creating a block, # then later reading it and finding it doesn't exist. result = self.etcd_client.read(key, quorum=True) except EtcdKeyNotFound: raise KeyError(str(block_cidr)) block = AllocationBlock.from_etcd_result(result) return block
def test_update_result(self): result = Mock(spec=EtcdResult) # Build a JSON object for the Block attr0 = { AllocationBlock.ATTR_HANDLE_ID: "test_key1", AllocationBlock.ATTR_SECONDARY: { "key1": "value11", "key2": "value21" } } attr1 = { AllocationBlock.ATTR_HANDLE_ID: "test_key2", AllocationBlock.ATTR_SECONDARY: { "key1": "value12", "key2": "value22" } } allocations = [None] * BLOCK_SIZE allocations[0] = 0 allocations[1] = 0 allocations[2] = 1 json_dict = { AllocationBlock.CIDR: str(network), AllocationBlock.AFFINITY: "host:Sammy Davis, Jr.", AllocationBlock.ALLOCATIONS: allocations, AllocationBlock.ATTRIBUTES: [attr0, attr1] } result.value = json.dumps(json_dict) block = AllocationBlock.from_etcd_result(result) # Modify the block. block.allocations[3] = 1 # Get the update. It should be the same result object, but with the # value set to the new JSON. block_json_str = block.to_json() updated = block.update_result() assert_equal(updated, result) assert_equal(result.value, block_json_str) # Verify the update appears in the JSON block_json_dict = json.loads(block_json_str) json_dict[AllocationBlock.ALLOCATIONS][3] = 1 assert_dict_equal(block_json_dict, json_dict)
def test_from_etcd_result_no_affinity(self): """ Mainline test of from_etcd_result() """ result = Mock(spec=EtcdResult) # Build a JSON object for the Block. Assume the strict_affinity flag is # not present so that we default the value (to False). attr0 = { AllocationBlock.ATTR_HANDLE_ID: "test_key1", AllocationBlock.ATTR_SECONDARY: { "key1": "value11", "key2": "value21" } } attr1 = { AllocationBlock.ATTR_HANDLE_ID: "test_key2", AllocationBlock.ATTR_SECONDARY: { "key1": "value12", "key2": "value22" } } allocations = [None] * BLOCK_SIZE allocations[0] = 0 allocations[1] = 0 allocations[2] = 1 unallocated = list(range(3, BLOCK_SIZE)) json_dict = { AllocationBlock.CIDR: str(network), AllocationBlock.ALLOCATIONS: allocations, AllocationBlock.ATTRIBUTES: [attr0, attr1], AllocationBlock.UNALLOCATED: unallocated } result.value = json.dumps(json_dict) block = AllocationBlock.from_etcd_result(result) assert_equal(block.count_free_addresses(), BLOCK_SIZE - 3) assert_equal(block.db_result, result) assert_equal(block.cidr, network) assert_is_none(block.host_affinity) assert_false(block.strict_affinity) assert_list_equal(block.allocations[:3], [0, 0, 1]) assert_dict_equal(block.attributes[0], attr0) assert_dict_equal(block.attributes[1], attr1)
def test_from_etcd_result(self): result = Mock(spec=EtcdResult) # Build a JSON object for the Block attr0 = { AllocationBlock.ATTR_HANDLE_ID: "test_key1", AllocationBlock.ATTR_SECONDARY: { "key1": "value11", "key2": "value21" } } attr1 = { AllocationBlock.ATTR_HANDLE_ID: "test_key2", AllocationBlock.ATTR_SECONDARY: { "key1": "value12", "key2": "value22" } } allocations = [None] * BLOCK_SIZE allocations[0] = 0 allocations[1] = 0 allocations[2] = 1 json_dict = { AllocationBlock.CIDR: str(network), AllocationBlock.AFFINITY: "host:Sammy Davis, Jr.", AllocationBlock.ALLOCATIONS: allocations, AllocationBlock.ATTRIBUTES: [attr0, attr1] } result.value = json.dumps(json_dict) block = AllocationBlock.from_etcd_result(result) assert_equal(block.count_free_addresses(), BLOCK_SIZE - 3) assert_equal(block.db_result, result) assert_equal(block.cidr, network) assert_equal(block.host_affinity, "Sammy Davis, Jr.") assert_list_equal(block.allocations[:3], [0, 0, 1]) assert_dict_equal(block.attributes[0], attr0) assert_dict_equal(block.attributes[1], attr1) # Verify we can get JSON back out. json_str = block.to_json() assert_equal(result.value, json_str)
def test_auto_assign_v6(self): block0 = _test_block_empty_v6() attr = {"key21": "value1", "key22": "value2"} ips = block0.auto_assign(1, "key2", attr, TEST_HOST) assert_list_equal([BLOCK_V6_1[0]], ips) assert_equal(block0.attributes[0][AllocationBlock.ATTR_HANDLE_ID], "key2") assert_dict_equal(block0.attributes[0][AllocationBlock.ATTR_SECONDARY], attr) assert_equal(block0.count_free_addresses(), BLOCK_SIZE - 1) # Allocate again from the first block, with a different key. ips = block0.auto_assign(3, "key3", attr, TEST_HOST) assert_list_equal([BLOCK_V6_1[1], BLOCK_V6_1[2], BLOCK_V6_1[3]], ips) assert_equal(block0.attributes[1][AllocationBlock.ATTR_HANDLE_ID], "key3") assert_dict_equal(block0.attributes[1][AllocationBlock.ATTR_SECONDARY], attr) assert_equal(block0.count_free_addresses(), BLOCK_SIZE - 4) # Allocate with different attributes. ips = block0.auto_assign(3, "key3", {}, TEST_HOST) assert_list_equal([BLOCK_V6_1[4], BLOCK_V6_1[5], BLOCK_V6_1[6]], ips) assert_equal(block0.attributes[2][AllocationBlock.ATTR_HANDLE_ID], "key3") assert_dict_equal(block0.attributes[2][AllocationBlock.ATTR_SECONDARY], {}) assert_equal(block0.count_free_addresses(), BLOCK_SIZE - 7) # Allocate 3 from a new block. block1 = _test_block_empty_v6() ips = block1.auto_assign(3, "key2", attr, TEST_HOST) assert_list_equal([BLOCK_V6_1[0], BLOCK_V6_1[1], BLOCK_V6_1[2]], ips) assert_equal(block1.count_free_addresses(), BLOCK_SIZE - 3) # Allocate again with same keys. ips = block1.auto_assign(3, "key2", attr, TEST_HOST) assert_list_equal([BLOCK_V6_1[3], BLOCK_V6_1[4], BLOCK_V6_1[5]], ips) assert_equal(block1.count_free_addresses(), BLOCK_SIZE - 6) # Assert we didn't create another attribute entry. assert_equal(len(block1.attributes), 1) # Test allocating 0 IPs with a new key. ips = block1.auto_assign(0, "key3", attr, TEST_HOST) assert_list_equal(ips, []) assert_equal(len(block1.attributes), 1) assert_equal(block1.count_free_addresses(), BLOCK_SIZE - 6) # Allocate addresses, so the block is nearly full ips = block1.auto_assign(BLOCK_SIZE - 8, None, {}, TEST_HOST) assert_equal(len(ips), BLOCK_SIZE - 8) assert_equal(block1.count_free_addresses(), 2) # Allocate 4 addresses. 248+3+3 = 254, so only 2 addresses left ips = block1.auto_assign(4, None, {}, TEST_HOST) assert_list_equal([BLOCK_V6_1[-2], BLOCK_V6_1[-1]], ips) assert_equal(block1.count_free_addresses(), 0) # Block is now full, further attempts return no addresses ips = block1.auto_assign(4, None, {}, TEST_HOST) assert_list_equal([], ips) # Test that we can cope with already allocated addresses that aren't # sequential. block2 = _test_block_not_empty_v6() assert_equal(block2.count_free_addresses(), BLOCK_SIZE - 2) ips = block2.auto_assign(4, None, {}, TEST_HOST) assert_list_equal([BLOCK_V6_1[0], BLOCK_V6_1[1], BLOCK_V6_1[3], BLOCK_V6_1[5]], ips) assert_equal(block2.count_free_addresses(), BLOCK_SIZE - 6) # Test ordinal math still works for small IPv6 addresses sm_cidr = IPNetwork("::1234:5600/122") block3 = AllocationBlock(sm_cidr, "test_host1") ips = block3.auto_assign(4, None, {}, TEST_HOST) assert_list_equal([sm_cidr[0], sm_cidr[1], sm_cidr[2], sm_cidr[3]], ips) assert_equal(block3.count_free_addresses(), BLOCK_SIZE - 4)
def test_update_result_from_pre_unallocated(self): """ Test mainline update_result() processing. This includes a check to ensure that updating a block that is stored without the allocation order is then correctly updated to include the allocation order. """ result = Mock(spec=EtcdResult) # Build a JSON object for the Block attr0 = { AllocationBlock.ATTR_HANDLE_ID: "test_key1", AllocationBlock.ATTR_SECONDARY: { "key1": "value11", "key2": "value21" } } attr1 = { AllocationBlock.ATTR_HANDLE_ID: "test_key2", AllocationBlock.ATTR_SECONDARY: { "key1": "value12", "key2": "value22" } } allocations = [None] * BLOCK_SIZE allocations[0] = 0 allocations[1] = 0 allocations[2] = 1 json_dict = { AllocationBlock.CIDR: str(network), AllocationBlock.AFFINITY: "host:Sammy Davis, Jr.", AllocationBlock.ALLOCATIONS: allocations, AllocationBlock.ATTRIBUTES: [attr0, attr1] } result.value = json.dumps(json_dict) block = AllocationBlock.from_etcd_result(result) # Verify that the allocation order is correctly initialised. unallocated = list(range(3, BLOCK_SIZE)) assert_list_equal(block.unallocated, unallocated) # Modify the block (and the expected allocation order) block.allocations[3] = 1 block.unallocated.remove(3) unallocated.remove(3) # Get the update. It should be the same result object, but with the # value set to the new JSON. block_json_str = block.to_json() updated = block.update_result() assert_equal(updated, result) assert_equal(result.value, block_json_str) # Verify the update appears in the JSON and that the JSON now includes # the allocation order. json_dict[AllocationBlock.UNALLOCATED] = unallocated block_json_dict = json.loads(block_json_str) json_dict[AllocationBlock.ALLOCATIONS][3] = 1 assert_dict_equal(block_json_dict, json_dict)
def test_from_etcd_result(self): """ Mainline test of from_etcd_result() """ result = Mock(spec=EtcdResult) # Build a JSON object for the Block attr0 = { AllocationBlock.ATTR_HANDLE_ID: "test_key1", AllocationBlock.ATTR_SECONDARY: { "key1": "value11", "key2": "value21" } } attr1 = { AllocationBlock.ATTR_HANDLE_ID: "test_key2", AllocationBlock.ATTR_SECONDARY: { "key1": "value12", "key2": "value22" } } allocations = [None] * BLOCK_SIZE allocations[0] = 0 allocations[1] = 0 allocations[2] = 1 unallocated = list(range(3, BLOCK_SIZE)) json_dict = { AllocationBlock.CIDR: str(network), AllocationBlock.AFFINITY: "host:Sammy Davis, Jr.", AllocationBlock.ALLOCATIONS: allocations, AllocationBlock.ATTRIBUTES: [attr0, attr1], AllocationBlock.UNALLOCATED: unallocated } result.value = json.dumps(json_dict) block = AllocationBlock.from_etcd_result(result) assert_equal(block.count_free_addresses(), BLOCK_SIZE - 3) assert_equal(block.db_result, result) assert_equal(block.cidr, network) assert_equal(block.host_affinity, "Sammy Davis, Jr.") assert_list_equal(block.allocations[:3], [0, 0, 1]) assert_dict_equal(block.attributes[0], attr0) assert_dict_equal(block.attributes[1], attr1) # Verify we can get JSON back out. json_str = block.to_json() assert_equal(result.value, json_str) # Modify the allocation order in the JSON so that it does not match # the allocations, and check the various unallocated asserts. # Check repeats json_dict[AllocationBlock.UNALLOCATED] = unallocated + [3] result.value = json.dumps(json_dict) self.assertRaises(AssertionError, AllocationBlock.from_etcd_result, result) # Check invalid entry json_dict[AllocationBlock.UNALLOCATED] = unallocated + [0] result.value = json.dumps(json_dict) self.assertRaises(AssertionError, AllocationBlock.from_etcd_result, result) # Check missing entry json_dict[AllocationBlock.UNALLOCATED] = unallocated[1:] result.value = json.dumps(json_dict) self.assertRaises(AssertionError, AllocationBlock.from_etcd_result, result)
def test_auto_assign_v6(self): block0 = _test_block_empty_v6() attr = {"key21": "value1", "key22": "value2"} ips = block0.auto_assign(1, "key2", attr) assert_list_equal([IPAddress("2001:abcd:def0::")], ips) assert_equal(block0.attributes[0][AllocationBlock.ATTR_HANDLE_ID], "key2") assert_dict_equal(block0.attributes[0][AllocationBlock.ATTR_SECONDARY], attr) assert_equal(block0.count_free_addresses(), BLOCK_SIZE - 1) # Allocate again from the first block, with a different key. ips = block0.auto_assign(3, "key3", attr) assert_list_equal([IPAddress("2001:abcd:def0::1"), IPAddress("2001:abcd:def0::2"), IPAddress("2001:abcd:def0::3")], ips) assert_equal(block0.attributes[1][AllocationBlock.ATTR_HANDLE_ID], "key3") assert_dict_equal(block0.attributes[1][AllocationBlock.ATTR_SECONDARY], attr) assert_equal(block0.count_free_addresses(), BLOCK_SIZE - 4) # Allocate with different attributes. ips = block0.auto_assign(3, "key3", {}) assert_list_equal([IPAddress("2001:abcd:def0::4"), IPAddress("2001:abcd:def0::5"), IPAddress("2001:abcd:def0::6")], ips) assert_equal(block0.attributes[2][AllocationBlock.ATTR_HANDLE_ID], "key3") assert_dict_equal(block0.attributes[2][AllocationBlock.ATTR_SECONDARY], {}) assert_equal(block0.count_free_addresses(), BLOCK_SIZE - 7) # Allocate 3 from a new block. block1 = _test_block_empty_v6() ips = block1.auto_assign(3, "key2", attr) assert_list_equal([IPAddress("2001:abcd:def0::"), IPAddress("2001:abcd:def0::1"), IPAddress("2001:abcd:def0::2")], ips) assert_equal(block1.count_free_addresses(), BLOCK_SIZE - 3) # Allocate again with same keys. ips = block1.auto_assign(3, "key2", attr) assert_list_equal([IPAddress("2001:abcd:def0::3"), IPAddress("2001:abcd:def0::4"), IPAddress("2001:abcd:def0::5")], ips) assert_equal(block1.count_free_addresses(), BLOCK_SIZE - 6) # Assert we didn't create another attribute entry. assert_equal(len(block1.attributes), 1) # Test allocating 0 IPs with a new key. ips = block1.auto_assign(0, "key3", attr) assert_list_equal(ips, []) assert_equal(len(block1.attributes), 1) assert_equal(block1.count_free_addresses(), BLOCK_SIZE - 6) # Allocate another 248 addresses, so the block is nearly full ips = block1.auto_assign(248, None, {}) assert_equal(len(ips), 248) assert_equal(block1.count_free_addresses(), 2) # Allocate 4 addresses. 248+3+3 = 254, so only 2 addresses left ips = block1.auto_assign(4, None, {}) assert_list_equal([IPAddress("2001:abcd:def0::fe"), IPAddress("2001:abcd:def0::ff")], ips) assert_equal(block1.count_free_addresses(), 0) # Block is now full, further attempts return no addresses ips = block1.auto_assign(4, None, {}) assert_list_equal([], ips) # Test that we can cope with already allocated addresses that aren't # sequential. block2 = _test_block_not_empty_v6() assert_equal(block2.count_free_addresses(), BLOCK_SIZE - 2) ips = block2.auto_assign(4, None, {}) assert_list_equal([IPAddress("2001:abcd:def0::"), IPAddress("2001:abcd:def0::1"), IPAddress("2001:abcd:def0::3"), IPAddress("2001:abcd:def0::5")], ips) assert_equal(block2.count_free_addresses(), BLOCK_SIZE - 6) # Test ordinal math still works for small IPv6 addresses block3 = AllocationBlock(IPNetwork("::1234:5600/120"), "test_host1") ips = block3.auto_assign(4, None, {}) assert_list_equal([IPAddress("::1234:5600"), IPAddress("::1234:5601"), IPAddress("::1234:5602"), IPAddress("::1234:5603")], ips) assert_equal(block3.count_free_addresses(), BLOCK_SIZE - 4)
def _test_block_empty_v6(): block = AllocationBlock(BLOCK_V6_1, "test_host1", False) return block
def test_auto_assign_v6(self): block0 = _test_block_empty_v6() attr = {"key21": "value1", "key22": "value2"} ips = block0.auto_assign(1, "key2", attr, TEST_HOST) assert_list_equal([BLOCK_V6_1[0]], ips) assert_equal(block0.attributes[0][AllocationBlock.ATTR_HANDLE_ID], "key2") assert_dict_equal(block0.attributes[0][AllocationBlock.ATTR_SECONDARY], attr) assert_equal(block0.count_free_addresses(), BLOCK_SIZE - 1) # Allocate again from the first block, with a different key. ips = block0.auto_assign(3, "key3", attr, TEST_HOST) assert_list_equal([BLOCK_V6_1[1], BLOCK_V6_1[2], BLOCK_V6_1[3]], ips) assert_equal(block0.attributes[1][AllocationBlock.ATTR_HANDLE_ID], "key3") assert_dict_equal(block0.attributes[1][AllocationBlock.ATTR_SECONDARY], attr) assert_equal(block0.count_free_addresses(), BLOCK_SIZE - 4) # Allocate with different attributes. ips = block0.auto_assign(3, "key3", {}, TEST_HOST) assert_list_equal([BLOCK_V6_1[4], BLOCK_V6_1[5], BLOCK_V6_1[6]], ips) assert_equal(block0.attributes[2][AllocationBlock.ATTR_HANDLE_ID], "key3") assert_dict_equal(block0.attributes[2][AllocationBlock.ATTR_SECONDARY], {}) assert_equal(block0.count_free_addresses(), BLOCK_SIZE - 7) # Allocate 3 from a new block. block1 = _test_block_empty_v6() ips = block1.auto_assign(3, "key2", attr, TEST_HOST) assert_list_equal([BLOCK_V6_1[0], BLOCK_V6_1[1], BLOCK_V6_1[2]], ips) assert_equal(block1.count_free_addresses(), BLOCK_SIZE - 3) # Allocate again with same keys. ips = block1.auto_assign(3, "key2", attr, TEST_HOST) assert_list_equal([BLOCK_V6_1[3], BLOCK_V6_1[4], BLOCK_V6_1[5]], ips) assert_equal(block1.count_free_addresses(), BLOCK_SIZE - 6) # Assert we didn't create another attribute entry. assert_equal(len(block1.attributes), 1) # Test allocating 0 IPs with a new key. ips = block1.auto_assign(0, "key3", attr, TEST_HOST) assert_list_equal(ips, []) assert_equal(len(block1.attributes), 1) assert_equal(block1.count_free_addresses(), BLOCK_SIZE - 6) # Allocate addresses, so the block is nearly full ips = block1.auto_assign(BLOCK_SIZE - 8, None, {}, TEST_HOST) assert_equal(len(ips), BLOCK_SIZE - 8) assert_equal(block1.count_free_addresses(), 2) # Allocate 4 addresses. 248+3+3 = 254, so only 2 addresses left ips = block1.auto_assign(4, None, {}, TEST_HOST) assert_list_equal([BLOCK_V6_1[-2], BLOCK_V6_1[-1]], ips) assert_equal(block1.count_free_addresses(), 0) # Block is now full, further attempts return no addresses ips = block1.auto_assign(4, None, {}, TEST_HOST) assert_list_equal([], ips) # Test that we can cope with already allocated addresses that aren't # sequential. block2 = _test_block_not_empty_v6() assert_equal(block2.count_free_addresses(), BLOCK_SIZE - 2) ips = block2.auto_assign(4, None, {}, TEST_HOST) assert_list_equal( [BLOCK_V6_1[0], BLOCK_V6_1[1], BLOCK_V6_1[3], BLOCK_V6_1[5]], ips) assert_equal(block2.count_free_addresses(), BLOCK_SIZE - 6) # Test ordinal math still works for small IPv6 addresses sm_cidr = IPNetwork("::1234:5600/122") block3 = AllocationBlock(sm_cidr, "test_host1", False) ips = block3.auto_assign(4, None, {}, TEST_HOST) assert_list_equal([sm_cidr[0], sm_cidr[1], sm_cidr[2], sm_cidr[3]], ips) assert_equal(block3.count_free_addresses(), BLOCK_SIZE - 4)
def test_update_result_from_pre_unallocated(self): """ Test mainline update_result() processing. This includes a check to ensure that updating a block that is stored without the allocation order is then correctly updated to include the allocation order. """ result = Mock(spec=EtcdResult) # Build a JSON object for the Block attr0 = { AllocationBlock.ATTR_HANDLE_ID: "test_key1", AllocationBlock.ATTR_SECONDARY: { "key1": "value11", "key2": "value21" } } attr1 = { AllocationBlock.ATTR_HANDLE_ID: "test_key2", AllocationBlock.ATTR_SECONDARY: { "key1": "value12", "key2": "value22" } } allocations = [None] * BLOCK_SIZE allocations[0] = 0 allocations[1] = 0 allocations[2] = 1 json_dict = { AllocationBlock.CIDR: str(network), AllocationBlock.AFFINITY: "", # Test a block with no affinity AllocationBlock.STRICT_AFFINITY: False, AllocationBlock.ALLOCATIONS: allocations, AllocationBlock.ATTRIBUTES: [attr0, attr1] } result.value = json.dumps(json_dict) block = AllocationBlock.from_etcd_result(result) # Verify the block has no affinity assert_equal(block.host_affinity, None) # Verify that the allocation order is correctly initialised. unallocated = list(range(3, BLOCK_SIZE)) assert_list_equal(block.unallocated, unallocated) # Modify the block (and the expected allocation order) block.allocations[3] = 1 block.unallocated.remove(3) unallocated.remove(3) # Get the update. It should be the same result object, but with the # value set to the new JSON. block_json_str = block.to_json() updated = block.update_result() assert_equal(updated, result) assert_equal(result.value, block_json_str) # Verify the update appears in the JSON and that the JSON now includes # the allocation order. json_dict[AllocationBlock.UNALLOCATED] = unallocated block_json_dict = json.loads(block_json_str) json_dict[AllocationBlock.ALLOCATIONS][3] = 1 assert_dict_equal(block_json_dict, json_dict)
def test_from_etcd_result(self): """ Mainline test of from_etcd_result() """ result = Mock(spec=EtcdResult) # Build a JSON object for the Block. Assume the strict_affinity flag is # not present so that we default the value (to False). attr0 = { AllocationBlock.ATTR_HANDLE_ID: "test_key1", AllocationBlock.ATTR_SECONDARY: { "key1": "value11", "key2": "value21" } } attr1 = { AllocationBlock.ATTR_HANDLE_ID: "test_key2", AllocationBlock.ATTR_SECONDARY: { "key1": "value12", "key2": "value22" } } allocations = [None] * BLOCK_SIZE allocations[0] = 0 allocations[1] = 0 allocations[2] = 1 unallocated = list(range(3, BLOCK_SIZE)) json_dict = { AllocationBlock.CIDR: str(network), AllocationBlock.AFFINITY: "host:Sammy Davis, Jr.", AllocationBlock.ALLOCATIONS: allocations, AllocationBlock.ATTRIBUTES: [attr0, attr1], AllocationBlock.UNALLOCATED: unallocated } result.value = json.dumps(json_dict) block = AllocationBlock.from_etcd_result(result) assert_equal(block.count_free_addresses(), BLOCK_SIZE - 3) assert_equal(block.db_result, result) assert_equal(block.cidr, network) assert_equal(block.host_affinity, "Sammy Davis, Jr.") assert_false(block.strict_affinity) assert_list_equal(block.allocations[:3], [0, 0, 1]) assert_dict_equal(block.attributes[0], attr0) assert_dict_equal(block.attributes[1], attr1) # Verify we can get JSON back out. Note that the strict affinity flag # will now be present. json_dict[AllocationBlock.STRICT_AFFINITY] = False json_str = block.to_json() assert_equal(json.dumps(json_dict), json_str) # Modify the allocation order in the JSON so that it does not match # the allocations, and check the various unallocated asserts. # Check repeats json_dict[AllocationBlock.UNALLOCATED] = unallocated + [3] result.value = json.dumps(json_dict) self.assertRaises(AssertionError, AllocationBlock.from_etcd_result, result) # Check invalid entry json_dict[AllocationBlock.UNALLOCATED] = unallocated + [0] result.value = json.dumps(json_dict) self.assertRaises(AssertionError, AllocationBlock.from_etcd_result, result) # Check missing entry json_dict[AllocationBlock.UNALLOCATED] = unallocated[1:] result.value = json.dumps(json_dict) self.assertRaises(AssertionError, AllocationBlock.from_etcd_result, result)