diff --git a/README.md b/README.md index d78de27..44c65e0 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Each returning item has the following field: ### BGPKIT Broker -Origianl Rust version BGPKIT Broker code available at: +Original Rust version BGPKIT Broker code available at: Example: ```python @@ -80,4 +80,42 @@ Available fields: - `collector`: collector name, e.g. `rrc00` or `route-views2` - `data_type`: `rib` or `update` - `order`: order by timestamp, `asc` or `desc`, default + +### BGPKIT ROAS Lookup +BGPKIT ROAS lookup API provides lookup for historical RPKI ROAS data lookup. The following example shows a query that +asks for all the validated ROA payload for RIPE NCC on the date of `2018-01-01`. + +```python +import bgpkit +roas = bgpkit.Roas() +data = roas.query(debug=True, asn=3333, date="2018-01-01") +for entry in data: + print(entry) +assert len(data) == 10 +``` + +``` +{'tal': 'ripencc', 'prefix': '193.0.0.0/21', 'asn': 3333, 'max_len': 21, 'date_ranges': [['2015-03-10', '2016-01-26'], ['2016-01-30', '2018-12-27'], ['2019-01-03', '2019-10-21'], ['2019-10-23', '2020-02-23'], ['2020-02-25', '2020-04-05'], ['2020-04-07', '2020-08-02'], ['2020-08-04', '2021-04-21'], ['2021-04-23', '2021-04-24'], ['2021-04-28', '2022-02-26']]} +{'tal': 'ripencc', 'prefix': '193.0.10.0/23', 'asn': 3333, 'max_len': 23, 'date_ranges': [['2015-03-10', '2016-01-26'], ['2016-01-30', '2018-12-27'], ['2019-01-03', '2019-10-21'], ['2019-10-23', '2020-02-23'], ['2020-02-25', '2020-04-05'], ['2020-04-07', '2020-08-02'], ['2020-08-04', '2021-04-21'], ['2021-04-23', '2021-04-24'], ['2021-04-28', '2022-02-26']]} +{'tal': 'ripencc', 'prefix': '193.0.12.0/23', 'asn': 3333, 'max_len': 23, 'date_ranges': [['2015-03-10', '2016-01-26'], ['2016-01-30', '2018-12-27'], ['2019-01-03', '2019-10-21'], ['2019-10-23', '2020-02-23'], ['2020-02-25', '2020-04-05'], ['2020-04-07', '2020-08-02'], ['2020-08-04', '2021-04-21'], ['2021-04-23', '2021-04-24'], ['2021-04-28', '2022-02-26']]} +{'tal': 'ripencc', 'prefix': '193.0.18.0/23', 'asn': 3333, 'max_len': 23, 'date_ranges': [['2015-03-10', '2016-01-26'], ['2016-01-30', '2018-12-27'], ['2019-01-03', '2019-10-21'], ['2019-10-23', '2020-02-23'], ['2020-02-25', '2020-04-05'], ['2020-04-07', '2020-08-02'], ['2020-08-04', '2021-04-21'], ['2021-04-23', '2021-04-24'], ['2021-04-28', '2022-02-26']]} +{'tal': 'ripencc', 'prefix': '193.0.20.0/23', 'asn': 3333, 'max_len': 23, 'date_ranges': [['2015-03-10', '2016-01-26'], ['2016-01-30', '2018-12-27'], ['2019-01-03', '2019-10-21'], ['2019-10-23', '2020-02-23'], ['2020-02-25', '2020-04-05'], ['2020-04-07', '2020-08-02'], ['2020-08-04', '2021-04-21'], ['2021-04-23', '2021-04-24'], ['2021-04-28', '2022-02-26']]} +{'tal': 'ripencc', 'prefix': '193.0.22.0/23', 'asn': 3333, 'max_len': 23, 'date_ranges': [['2015-03-10', '2016-01-26'], ['2016-01-30', '2018-12-27'], ['2019-01-03', '2019-10-21'], ['2019-10-23', '2020-02-23'], ['2020-02-25', '2020-04-05'], ['2020-04-07', '2020-08-02'], ['2020-08-04', '2021-04-21'], ['2021-04-23', '2021-04-24'], ['2021-04-28', '2022-02-26']]} +{'tal': 'ripencc', 'prefix': '193.0.24.0/22', 'asn': 3333, 'max_len': 26, 'date_ranges': [['2017-01-14', '2018-12-27'], ['2019-01-03', '2019-06-24']]} +{'tal': 'ripencc', 'prefix': '193.0.24.0/24', 'asn': 3333, 'max_len': 24, 'date_ranges': [['2017-02-25', '2018-12-27'], ['2019-01-03', '2019-06-24']]} +{'tal': 'ripencc', 'prefix': '2001:610:240::/42', 'asn': 3333, 'max_len': 42, 'date_ranges': [['2015-03-10', '2016-01-26'], ['2016-01-30', '2018-12-27'], ['2019-01-03', '2019-10-21'], ['2019-10-23', '2020-02-23'], ['2020-02-25', '2020-04-05'], ['2020-04-07', '2020-08-02'], ['2020-08-04', '2021-04-21'], ['2021-04-23', '2021-04-24'], ['2021-04-28', '2022-02-26']]} +{'tal': 'ripencc', 'prefix': '2001:67c:2e8::/48', 'asn': 3333, 'max_len': 48, 'date_ranges': [['2015-03-10', '2016-01-26'], ['2016-01-30', '2018-12-27'], ['2019-01-03', '2019-10-21'], ['2019-10-23', '2020-02-23'], ['2020-02-25', '2020-04-05'], ['2020-04-07', '2020-08-02'], ['2020-08-04', '2021-04-21'], ['2021-04-23', '2021-04-24'], ['2021-04-28', '2022-02-26']]} +``` + +Available query fields: + +- `Roas()` + - `api_url`: the base URL for the BGPKIT ROAS instance. Default: `https://api.roas.bgpkit.com` +- `query()` + - `prefix`: prefix to query in `str` + - `asn`: AS number to query in `int` + - `tal`: trust anchor to query in `str`, available values: `ripencc`, `arin`, `apnic`, `afrinic`, `lacnic` + - `date`: date to query, format: `YYYY-MM-DD`, e.g. `2022-01-01` + - `max_len`: filter results to only VRP's with specific max length + - `debug`: boolean toggle to display debug information, default `False` diff --git a/bgpkit/__init__.py b/bgpkit/__init__.py index 790dd22..45e38e5 100644 --- a/bgpkit/__init__.py +++ b/bgpkit/__init__.py @@ -1,2 +1,3 @@ from .bgpkit_parser import Parser from .bgpkit_broker import Broker +from .bgpkit_roas import Roas diff --git a/bgpkit/bgpkit_broker.py b/bgpkit/bgpkit_broker.py index 10aba41..835bb29 100644 --- a/bgpkit/bgpkit_broker.py +++ b/bgpkit/bgpkit_broker.py @@ -2,6 +2,7 @@ import requests as requests + def check_type(value: any, ty: type) -> bool: try: ty(value) @@ -20,7 +21,7 @@ class BrokerItem: class Broker: - def __init__(self, api_url: str = "https://api.broker.bgpkit.com/v1", page_size: int=100): + def __init__(self, api_url: str = "https://api.broker.bgpkit.com/v1", page_size: int = 100): self.base_url = api_url.strip() self.page_size = int(page_size) diff --git a/bgpkit/bgpkit_roas.py b/bgpkit/bgpkit_roas.py new file mode 100644 index 0000000..618fcdc --- /dev/null +++ b/bgpkit/bgpkit_roas.py @@ -0,0 +1,80 @@ +from dataclasses import dataclass +from typing import Optional, List + +import requests as requests + + +def check_type(value: any, ty: type) -> bool: + try: + ty(value) + return True + except ValueError: + raise ValueError("invalid option input") + + +@dataclass +class RoasItem: + prefix: str + asn: int + tal: str + date_ranges: List[List[str]] + + +@dataclass +class RoasRes: + limit: int + count: int + data: List[RoasItem] + next_page_num: Optional[int] + next_page: Optional[str] + error: Optional[str] + + +class Roas: + + def __init__(self, api_url: str = "https://api.roas.bgpkit.com"): + self.base_url = api_url.strip() + + def query(self, + prefix: str = None, + asn: int = None, + tal: str = None, + date: str = None, + max_len: int = None, + debug: bool = False, + ) -> [RoasItem]: + + if not (prefix or asn or tal or date or max_len): + print("ERROR: must specify at least one query parameter: prefix, asn, tal, date, max_len") + return [] + + params = [] + if prefix: + check_type(prefix, str) + params.append(f"prefix={prefix}") + if asn: + check_type(asn, int) + params.append(f"asn={asn}") + if tal: + check_type(tal, str) + params.append(f"tal={tal}") + if date: + check_type(date, str) + params.append(f"date={date}") + if max_len: + check_type(max_len, int) + params.append(f"max_len={max_len}") + + api_url = f"{self.base_url}/lookup?" + "&".join(params) + data_items = [] + if debug: + print(api_url) + res = RoasRes(**requests.get(api_url).json()) + while res.data: + data_items.extend(res.data) + if res.next_page: + res = RoasRes(**requests.get(res.next_page).json()) + else: + break + + return data_items diff --git a/bgpkit/test_integration.py b/bgpkit/test_integration.py index 541be7a..76ca1f7 100644 --- a/bgpkit/test_integration.py +++ b/bgpkit/test_integration.py @@ -13,11 +13,20 @@ def test_parser(self): def test_broker(self): broker = bgpkit.Broker() - items = broker.query(start_ts=1634693400, end_ts=1634693400) + items = broker.query(start_ts=1643760000, end_ts=1643761200) for item in items: print(item) print(len(items)) assert len(items) == 58 - elems = bgpkit.Parser(items[0].url).parse_all() - assert len(elems) == 222467 + # elems = bgpkit.Parser(items[0].url).parse_all() + # assert len(elems) == 222467 + + def test_roas(self): + roas = bgpkit.Roas() + data = roas.query(debug=True, asn=3333, date="2018-01-01") + for entry in data: + print(entry) + assert len(data) == 10 + + assert len(roas.query()) == 0 diff --git a/setup.py b/setup.py index 5c61955..7501767 100644 --- a/setup.py +++ b/setup.py @@ -1,17 +1,25 @@ import setuptools +# read the contents of your README file +from pathlib import Path +this_directory = Path(__file__).parent +long_description = (this_directory / "README.md").read_text() + setuptools.setup( name='pybgpkit', - version='0.0.1', + version='0.0.2.post1', description='BGPKIT tools Python bindings', url='https://github.com/bgpkit/pybgpkit', author='Mingwei Zhang', author_email='mingwei@bgpkit.com', packages=setuptools.find_packages(), include_package_data=True, + long_description=long_description, + long_description_content_type='text/markdown', install_requires=[ # available on pip 'pybgpkit-parser==0.0.1', + 'requests', ], entry_points={'console_scripts': [ ]}