Skip to content
Snippets Groups Projects
Commit 44dbc403 authored by Erik Reid's avatar Erik Reid
Browse files

Finished feature bgp-peer-interface-lookup.

parents 2fe76e87 ccc17b20
No related branches found
No related tags found
No related merge requests found
......@@ -374,7 +374,7 @@ Any non-empty responses are JSON formatted messages.
The `address` parameter should be the ip address of
a remote peer. If this address is found in the system
then information about the interface is returned, otherwise
404 is returned.
404 is returned (or 422 if the address can't be parsed)
The response will be formatted according to the following syntax:
......@@ -391,6 +391,13 @@ Any non-empty responses are JSON formatted messages.
{"pattern": r'^([a-f\d]{4}:){7}[a-f\d]{4}$'}
]
},
"interface-address": {
"type": "string",
"oneOf": [
{"pattern": r'^(\d+\.){3}\d+/\d+$'},
{"pattern": r'^[a-f\d:]+/\d+$'}
]
},
"vpn-rr-peer": {
"type": "object",
"properties": {
......@@ -425,7 +432,19 @@ Any non-empty responses are JSON formatted messages.
"type": "array",
"items": {"$ref": "#/definitions/ip-address"}
},
"interface-info": {
"type": "object",
"properties": {
"name": {"$ref": "#/definitions/ip-address"},
"interface address": {
"$ref": "#/definitions/interface-address"},
"interface name": {"type": "string"},
"router": {"type": "string"}
},
"required": [
"name", "interface address", "interface name", "router"],
"additionalProperties": False
}
},
"type": "object",
......@@ -433,7 +452,11 @@ Any non-empty responses are JSON formatted messages.
"ix-public-peer-info": {"$ref": "#/definitions/ix-public-peer"},
"ix-public-peer-group": {
"$ref": "#/definitions/ix-public-peer-group"},
"vpn-rr-peer-info": {"$ref": "#/definitions/vpn-rr-peer"}
"vpn-rr-peer-info": {"$ref": "#/definitions/vpn-rr-peer"},
"interfaces": {
"type": "array",
"items": {"$ref": "#/definitions/interface-info"}
}
},
"additionalProperties": False
}
......
......@@ -46,36 +46,59 @@ def get_trap_metadata(source_equipment, interface):
return Response(result, mimetype="application/json")
@routes.route("/peer-info/<address>", methods=['GET', 'POST'])
@common.require_accepts_json
def peer_info(address):
def ix_peering_group(address, description):
"""
TODO: this is probably the least efficient way of doing this
(if it's a problem, pre-compute these lists)
def _related_ix_peers(ix_public_peer_info):
"""
TODO: this is probably the least efficient way of doing this
(if it's a problem, pre-compute these lists)
:param ix_public_peer_info: ix public peer info loaded for address
:return:
"""
:param ix_public_peer_info: ix public peer info loaded for address
:return:
"""
protocol = type(address).__name__
keyword = description.split(' ')[0] # regex needed??? (e.g. tabs???)
protocol = type(ipaddress.ip_address(address)).__name__
r = common.get_redis()
for k in r.keys('ix_public_peer:*'):
peer = r.get(k.decode('utf-8')).decode('utf-8')
peer = json.loads(peer)
assert peer['description'] is not None # sanity: as above...
if not peer['description'].startswith(keyword):
continue
peer_address = ipaddress.ip_address(peer['name'])
if protocol == type(peer_address).__name__:
yield peer['name']
def find_interfaces(address):
"""
TODO: this is probably the least efficient way of doing this
(if it's a problem, pre-compute these lists)
:param address: an ipaddress object
:return:
"""
r = common.get_redis()
for k in r.keys('reverse_interface_addresses:*'):
info = r.get(k.decode('utf-8')).decode('utf-8')
info = json.loads(info)
interface = ipaddress.ip_interface(info['interface address'])
if address in interface.network:
yield info
description = ix_public_peer_info['description']
assert description is not None # sanity: at least empty string
keyword = description.split(' ')[0] # regex needed??? (e.g. tabs???)
r = common.get_redis()
@routes.route("/peer-info/<address>", methods=['GET', 'POST'])
@common.require_accepts_json
def peer_info(address):
for k in r.keys('ix_public_peer:*'):
peer = r.get(k.decode('utf-8')).decode('utf-8')
peer = json.loads(peer)
assert peer['description'] is not None # sanity: as above...
if not peer['description'].startswith(keyword):
continue
peer_address = ipaddress.ip_address(peer['name'])
if protocol == type(peer_address).__name__:
yield peer['name']
try:
address_obj = ipaddress.ip_address(address)
except ValueError:
return Response(
response='unable to parse %r as an ip address' % address,
status=422,
mimetype="text/html")
r = common.get_redis()
......@@ -85,14 +108,20 @@ def peer_info(address):
if info:
info = info.decode('utf-8')
result['ix-public-peer-info'] = json.loads(info)
description = result['ix-public-peer-info']['description']
assert description is not None # sanity
result['ix-public-peer-group'] = list(
_related_ix_peers(result['ix-public-peer-info']))
ix_peering_group(address_obj, description))
info = r.get('vpn_rr_peer:%s' % address)
if info:
info = info.decode('utf-8')
result['vpn-rr-peer-info'] = json.loads(info)
interfaces = list(find_interfaces(address_obj))
if interfaces:
result['interfaces'] = interfaces
if not result:
return Response(
response='no peering info found for %s' % address,
......
......@@ -23,10 +23,11 @@ def test_trap_metadata(client_with_mocked_data):
VPN_RR_PEER_INFO_KEYS = {'vpn-rr-peer-info'}
IX_PUBLIC_PEER_INFO_KEYS = {'ix-public-peer-info', 'ix-public-peer-group'}
IX_PUBLIC_PEER_INFO_KEYS = {
'ix-public-peer-info', 'ix-public-peer-group', 'interfaces'}
@pytest.mark.parametrize("peer_address,expected_response_keys", [
@pytest.mark.parametrize('peer_address,expected_response_keys', [
('109.105.110.54', VPN_RR_PEER_INFO_KEYS),
('2001:07f8:001c:024a:0000:0000:316e:0001', IX_PUBLIC_PEER_INFO_KEYS),
('2001:07f8:000b:0100:01d1:a5d1:0310:0029', IX_PUBLIC_PEER_INFO_KEYS),
......@@ -47,6 +48,13 @@ def test_peer_info(
{"pattern": r'^([a-f\d]{4}:){7}[a-f\d]{4}$'}
]
},
"interface-address": {
"type": "string",
"oneOf": [
{"pattern": r'^(\d+\.){3}\d+/\d+$'},
{"pattern": r'^[a-f\d:]+/\d+$'}
]
},
"vpn-rr-peer": {
"type": "object",
"properties": {
......@@ -81,7 +89,19 @@ def test_peer_info(
"type": "array",
"items": {"$ref": "#/definitions/ip-address"}
},
"interface-info": {
"type": "object",
"properties": {
"name": {"$ref": "#/definitions/ip-address"},
"interface address": {
"$ref": "#/definitions/interface-address"},
"interface name": {"type": "string"},
"router": {"type": "string"}
},
"required": [
"name", "interface address", "interface name", "router"],
"additionalProperties": False
}
},
"type": "object",
......@@ -89,7 +109,11 @@ def test_peer_info(
"ix-public-peer-info": {"$ref": "#/definitions/ix-public-peer"},
"ix-public-peer-group": {
"$ref": "#/definitions/ix-public-peer-group"},
"vpn-rr-peer-info": {"$ref": "#/definitions/vpn-rr-peer"}
"vpn-rr-peer-info": {"$ref": "#/definitions/vpn-rr-peer"},
"interfaces": {
"type": "array",
"items": {"$ref": "#/definitions/interface-info"}
}
},
"additionalProperties": False
}
......@@ -105,9 +129,15 @@ def test_peer_info(
assert set(response_data.keys()) == expected_response_keys
def test_peer_not_found(client_with_mocked_data):
def test_peer_invalid_address(client_with_mocked_data):
rv = client_with_mocked_data.get(
'/classifier/peer-info/1.2.3.4.5',
headers=DEFAULT_REQUEST_HEADERS)
assert rv.status_code == 422
def test_peer_not_found(client_with_mocked_data):
rv = client_with_mocked_data.get(
'/classifier/peer-info/1.2.3.4',
headers=DEFAULT_REQUEST_HEADERS)
assert rv.status_code == 404
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment