Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 18 additions & 20 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ Calculate diff of an OLSR 0.6.x topology:
from netdiff import OlsrParser
from netdiff import diff

old = OlsrParser('./stored-olsr.json')
new = OlsrParser('http://127.0.0.1:9090')
old = OlsrParser(file='./stored-olsr.json')
new = OlsrParser(url='http://127.0.0.1:9090')
diff(old, new)

In alternative, you may also use the subtraction operator:
Expand All @@ -92,8 +92,8 @@ In alternative, you may also use the subtraction operator:
from netdiff import OlsrParser
from netdiff import diff

old = OlsrParser('./stored-olsr.json')
new = OlsrParser('http://127.0.0.1:9090')
old = OlsrParser(file='./stored-olsr.json')
new = OlsrParser(url='http://127.0.0.1:9090')
old - new

The output will be an ordered dictionary with three keys:
Expand Down Expand Up @@ -210,18 +210,17 @@ The available parsers are:
Initialization arguments
~~~~~~~~~~~~~~~~~~~~~~~~

**data**: the only required argument, different inputs are accepted:
Data can be supplied in 3 different ways, in the following order of precedence:

* JSON formatted string representing the topology
* python `dict` (or subclass of `dict`) representing the topology
* string representing a HTTP URL where the data resides
* string representing a telnet URL where the data resides
* string representing a file path where the data resides
* ``data``: ``dict`` or ``str`` representing the topology/graph
* ``url``: URL to fetch data from
* ``file``: file path to retrieve data from

**timeout**: integer representing timeout in seconds for HTTP or telnet requests, defaults to None
Other available arguments:

**verify**: boolean indicating to the `request library whether to do SSL certificate
verification or not <http://docs.python-requests.org/en/latest/user/advanced/#ssl-cert-verification>`_
* **timeout**: integer representing timeout in seconds for HTTP or telnet requests, defaults to ``None``
* **verify**: boolean indicating to the `request library whether to do SSL certificate
verification or not <http://docs.python-requests.org/en/latest/user/advanced/#ssl-cert-verification>`_

Initialization examples
~~~~~~~~~~~~~~~~~~~~~~~
Expand All @@ -231,41 +230,40 @@ Local file example:
.. code-block:: python

from netdiff import BatmanParser
BatmanParser('./my-stored-topology.json')
BatmanParser(file='./my-stored-topology.json')

HTTP example:

.. code-block:: python

from netdiff import NetJsonParser
url = 'https://raw.githubusercontent.com/interop-dev/netjson/master/examples/network-graph.json'
NetJsonParser(url)
NetJsonParser(url=url)

Telnet example with ``timeout``:

.. code-block:: python

from netdiff import OlsrParser
OlsrParser('telnet://127.0.1:8080', timeout=5)
OlsrParser(url='telnet://127.0.1', timeout=5)

HTTPS example with self-signed SSL certificate using ``verify=False``:

.. code-block:: python

from netdiff import NetJsonParser
OlsrParser('https://myserver.mydomain.com/topology.json', verify=False)
OlsrParser(url='https://myserver.mydomain.com/topology.json', verify=False)

NetJSON output
--------------

Netdiff parsers can return a valid `NetJSON`_
``NetworkGraph`` object:
Netdiff parsers can return a valid `NetJSON`_ ``NetworkGraph`` object:

.. code-block:: python

from netdiff import OlsrParser

olsr = OlsrParser('telnet://127.0.0.1:9090')
olsr = OlsrParser(url='telnet://127.0.0.1:9090')

# will return a dict
olsr.json(dict=True)
Expand Down
52 changes: 21 additions & 31 deletions netdiff/parsers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ class BaseParser(object):
revision = None
metric = None

def __init__(self, data, version=None, revision=None, metric=None,
def __init__(self, data=None, url=None, file=None,
version=None, revision=None, metric=None,
timeout=None, verify=True): # noqa
"""
Initializes a new Parser

:param data: JSON, dict, path to file or HTTP URL of topology
:param data: ``str`` or ``dict`` containing topology data
:param url: HTTP URL to retrieve topology data
:param file: path to file containing topology data
:param version: routing protocol version
:param revision: routing protocol revision
:param metric: routing protocol metric
Expand All @@ -43,11 +46,25 @@ def __init__(self, data, version=None, revision=None, metric=None,
self.metric = metric
self.timeout = timeout
self.verify = verify
if data is None and url is not None:
data = self._get_url(url)
elif data is None and file is not None:
data = self._get_file(file)
elif data is None and url is None and file is None:
raise ValueError('no topology data supplied, on of the following arguments'
'must be supplied: data, url or file')
self.original_data = self.to_python(data)
# avoid throwing NotImplementedError in tests
if self.__class__ is not BaseParser:
self.graph = self.parse(self.original_data)

def _get_url(self, url):
url = urlparse.urlparse(url)
if url.scheme in ['http', 'https']:
return self._get_http(url)
if url.scheme == 'telnet':
return self._get_telnet(url)

def __sub__(self, other):
return diff(other, self)

Expand All @@ -61,42 +78,15 @@ def to_python(self, data):
* a JSON formatted string
* a dict representing a JSON structure
"""
data = self._retrieve_data(data)
if isinstance(data, dict):
return data
elif isinstance(data, six.string_types):
# assuming is JSON
try:
return json.loads(data)
except ValueError:
raise ConversionException('Could not recognize format', data=data)
else:
raise ConversionException('Could not recognize format', data=data)

def _retrieve_data(self, data):
"""
if recognizes a URL or a path
tries to retrieve and returns data
otherwise will return data as is
"""
if isinstance(data, six.string_types):
# if it looks like URL
if '://' in data:
url = urlparse.urlparse(data)
if url.scheme in ['http', 'https']:
return self._get_http(url)
if url.scheme == 'telnet':
return self._get_telnet(url)
# if it looks like a path
elif True in [data.startswith('./'),
data.startswith('../'),
data.startswith('/'),
data.startswith('.\\'),
data.startswith('..\\'),
data.startswith('\\'),
data[1:3].startswith(':\\')]:
return self._get_file(data)
return data
pass
raise ConversionException('Could not recognize format', data=data)

def _get_file(self, path):
try:
Expand Down
58 changes: 26 additions & 32 deletions tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,88 +26,82 @@ def _load_contents(self, file):
def test_version(self):
get_version()

def test_no_data_supplied(self):
with self.assertRaises(ValueError):
BaseParser()

def test_parse_file(self):
dir = os.path.dirname(os.path.realpath(__file__))
path = '{0}/static/olsr-2-links.json'.format(dir)
p = BaseParser(path)
p = BaseParser(file=path)
self.assertIsInstance(p.original_data, dict)
with self.assertRaises(TopologyRetrievalError):
BaseParser('../wrong.json')
BaseParser(file='../wrong.json')

@responses.activate
def test_parse_http(self):
responses.add(
responses.GET,
'http://localhost:9090',
body=self._load_contents('tests/static/olsr-2-links.json')
)
p = BaseParser('http://localhost:9090')
responses.add(responses.GET, 'http://localhost:9090',
body=self._load_contents('tests/static/olsr-2-links.json'))
p = BaseParser(url='http://localhost:9090')
self.assertIsInstance(p.original_data, dict)

@responses.activate
def test_topology_retrieval_error_http_404(self):
responses.add(
responses.GET,
'http://404.com',
body='not found',
status=404
)
responses.add(responses.GET, 'http://404.com',
body='not found', status=404)
with self.assertRaises(TopologyRetrievalError):
BaseParser('http://404.com')
BaseParser(url='http://404.com')

@responses.activate
def test_topology_retrieval_error_http(self):
def request_callback(request):
raise ConnectionError('test exception')
responses.add_callback(
responses.GET,
'http://connectionerror.com',
callback=request_callback,
)
responses.add_callback(responses.GET, 'http://connectionerror.com',
callback=request_callback)
with self.assertRaises(TopologyRetrievalError):
BaseParser('http://connectionerror.com')
BaseParser(url='http://connectionerror.com')

@mock.patch('telnetlib.Telnet')
def test_telnet_retrieval_error(self, MockClass):
telnetlib.Telnet.side_effect=ValueError('testing exception')
telnetlib.Telnet.side_effect = ValueError('testing exception')
with self.assertRaises(TopologyRetrievalError):
BaseParser('telnet://wrong.com')
BaseParser(url='telnet://wrong.com')

@mock.patch('telnetlib.Telnet')
def test_telnet_retrieval(self, MockClass):
with self.assertRaises(ConversionException):
BaseParser('telnet://127.0.0.1')
BaseParser(url='telnet://127.0.0.1')

def test_topology_retrieval_error_file(self):
with self.assertRaises(TopologyRetrievalError):
BaseParser('./tests/static/wrong.json')
BaseParser(file='./tests/static/wrong.json')

def test_parse_json_string(self):
p = BaseParser('{}')
p = BaseParser(data='{}')
self.assertIsInstance(p.original_data, dict)
p = BaseParser(u'{}')
p = BaseParser(data=u'{}')
self.assertIsInstance(p.original_data, dict)

def test_parse_dict(self):
p = BaseParser({})
p = BaseParser(data={})
self.assertIsInstance(p.original_data, dict)

def test_parse_conversion_exception(self):
with self.assertRaises(ConversionException):
BaseParser('wrong [] ; .')
BaseParser(data='wrong [] ; .')

def test_parse_error(self):
with self.assertRaises(ConversionException):
BaseParser(8)
BaseParser(data=8)

def test_parse_not_implemented(self):
class MyParser(BaseParser):
pass
with self.assertRaises(NotImplementedError):
MyParser('{}')
MyParser(data='{}')

def test_json_not_implemented(self):
p = BaseParser('{}')
p = BaseParser(data='{}')
with self.assertRaises(NotImplementedError):
p.json()

Expand Down
4 changes: 2 additions & 2 deletions tests/test_netjson.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@


CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
links2 = '{0}/static/netjson-2-links.json'.format(CURRENT_DIR)
links3 = '{0}/static/netjson-3-links.json'.format(CURRENT_DIR)
links2 = open('{0}/static/netjson-2-links.json'.format(CURRENT_DIR)).read()
links3 = open('{0}/static/netjson-3-links.json'.format(CURRENT_DIR)).read()


class TestNetJsonParser(TestCase):
Expand Down
10 changes: 5 additions & 5 deletions tests/test_olsr.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@


CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
links2 = '{0}/static/olsr-2-links.json'.format(CURRENT_DIR)
links2_cost = '{0}/static/olsr-2-links-cost-changed.json'.format(CURRENT_DIR)
links3 = '{0}/static/olsr-3-links.json'.format(CURRENT_DIR)
links5 = '{0}/static/olsr-5-links.json'.format(CURRENT_DIR)
links5_cost = '{0}/static/olsr-5-links-cost-changed.json'.format(CURRENT_DIR)
links2 = open('{0}/static/olsr-2-links.json'.format(CURRENT_DIR)).read()
links2_cost = open('{0}/static/olsr-2-links-cost-changed.json'.format(CURRENT_DIR)).read()
links3 = open('{0}/static/olsr-3-links.json'.format(CURRENT_DIR)).read()
links5 = open('{0}/static/olsr-5-links.json'.format(CURRENT_DIR)).read()
links5_cost = open('{0}/static/olsr-5-links-cost-changed.json'.format(CURRENT_DIR)).read()


class TestOlsrParser(TestCase):
Expand Down
8 changes: 4 additions & 4 deletions tests/test_olsr_txtinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@


CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
links2 = '{0}/static/olsr-2-links.txt'.format(CURRENT_DIR)
links2_cost = '{0}/static/olsr-2-links-cost-changed.txt'.format(CURRENT_DIR)
links3 = '{0}/static/olsr-3-links.txt'.format(CURRENT_DIR)
links5 = '{0}/static/olsr-5-links.txt'.format(CURRENT_DIR)
links2 = open('{0}/static/olsr-2-links.txt'.format(CURRENT_DIR)).read()
links2_cost = open('{0}/static/olsr-2-links-cost-changed.txt'.format(CURRENT_DIR)).read()
links3 = open('{0}/static/olsr-3-links.txt'.format(CURRENT_DIR)).read()
links5 = open('{0}/static/olsr-5-links.txt'.format(CURRENT_DIR)).read()


class TestOlsrTxtinfoParser(TestCase):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
links2 = '{0}/static/netjson-2-links.json'.format(CURRENT_DIR)
links2 = open('{0}/static/netjson-2-links.json'.format(CURRENT_DIR)).read()


class TestUtils(TestCase):
Expand Down