././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1679240302.5312722 isc_dhcp_leases-0.10.0/0000755000000000000000000000000014405626157013450 5ustar00rootroot././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/.gitignore0000644000000000000000000000105514405625540015434 0ustar00rootroot# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # C extensions *.so # Distribution / packaging .Python env/ bin/ build/ develop-eggs/ dist/ eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .cache nosetests.xml coverage.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject # Rope .ropeproject # Django stuff: *.log *.pot # Sphinx documentation docs/_build/ # IDE .idea/././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/.travis.yml0000644000000000000000000000072014405625540015553 0ustar00rootrootlanguage: python sudo: false cache: pip python: - "2.7" - "3.3" - "3.4" - "3.5" # command to install dependencies install: - "pip install -U setuptools pip" - "pip install -r requirements.txt" - "pip install 'coverage<4'" - "pip install coveralls" # command to run tests # running the test twice isn't very efficient but it does work script: - coverage run --source isc_dhcp_leases setup.py test - python setup.py test after_success: coveralls ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/LICENSE0000644000000000000000000000206714405625540014455 0ustar00rootrootThe MIT License (MIT) Copyright (c) 2014 Martijn Braam Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/MANIFEST0000644000000000000000000000033614405625540014576 0ustar00rootroot# file GENERATED by distutils, do NOT edit setup.cfg setup.py isc_dhcp_leases/__init__.py isc_dhcp_leases/iscdhcpleases.py isc_dhcp_leases/test_iscDhcpLeases.py isc_dhcp_leases/test_lease.py isc_dhcp_leases/test_lease6.py ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1679240302.5312722 isc_dhcp_leases-0.10.0/PKG-INFO0000644000000000000000000000151014405626157014542 0ustar00rootrootMetadata-Version: 2.1 Name: isc_dhcp_leases Version: 0.10.0 Summary: Small python module for reading /var/lib/dhcp/dhcpd.leases from isc-dhcp-server Home-page: https://github.com/MartijnBraam/python-isc-dhcp-leases Author: Martijn Braam Author-email: martijn@brixit.nl License: MIT Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 License-File: LICENSE ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/README.rst0000644000000000000000000001220514405625540015132 0ustar00rootrootpython-isc-dhcp-leases ====================== |Build Status| |PyPI version| |Coverage Status| Small python module for reading /var/lib/dhcp/dhcpd.leases from isc-dhcp-server. This module works in Python 2.7 and 3.x This module also supports reading lease files from the isc dhcp daemon running in IPv6 mode (Since version 0.4.0). Installation ------------ Through pypi ~~~~~~~~~~~~ .. code:: bash $ sudo pip install isc_dhcp_leases Through your distribution package manager ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This python module is currently packaged in Debian unstable (release for Debian 9) and will be packaged in Ubuntu 17.04 (Zesty Zapus) .. code:: bash # For the python 2.7 interpreter $ sudo apt install python-isc-dhcp-leases # For the python 3 interpreter $ sudo apt install python3-isc-dhcp-leases Through git ~~~~~~~~~~~ .. code:: bash $ git clone git@github.com:MartijnBraam/python-isc-dhcp-leases.git $ cd python-isc-dhcp-leases $ python setup.py build $ sudo python setup.py install Usage ----- .. code:: python from isc_dhcp_leases import Lease, IscDhcpLeases leases = IscDhcpLeases('/path/to/dhcpd.leases') leases.get() # Returns the leases as a list of Lease objects leases.get_current() # Returns only the currently valid dhcp leases as dict # The key of the dict is the device mac address and the # Value is a Lease object Or read a gzip'ed file: .. code:: python from isc_dhcp_leases import Lease, IscDhcpLeases # IscDhcpLeases(filename, gzip=False) leases = IscDhcpLeases('/path/to/dhcpd.leases', True) # True param starts the gzip reader leases.get() # Returns the leases as a list of Lease objects leases.get_current() # Returns only the currently valid dhcp leases as dict # The key of the dict is the device mac address and the # Value is a Lease object The Lease object has the following fields (only for IPv4 leases): .. code:: python lease instanceof Lease lease.ip # The ip address assigned by this lease as string lease.ethernet # The mac address of the lease lease.hardware # The OSI physical layer used to request the lease (usually ethernet) lease.start # The start time of this lease as DateTime object lease.end # The time this lease expires as DateTime object or None if this is an infinite lease lease.hostname # The hostname for this lease if given by the client lease.binding_state # The binding state as string ('active', 'free', 'abandoned', 'backup') lease.data # Dict of all the info in the dhcpd.leases file for this lease lease.valid # True if the lease hasn't expired and is not in the future lease.active # True if the binding state is active lease.options # List of extra options in the lease file lease.sets # List of the 'set' items in the lease file The Lease6 object has the following fields (only for IPv6): .. code:: python lease instanceof Lease6 lease.ip # The ip address assigned by this lease as string lease.type # If this is a temporary or permanent address. I's one of the following: # Lease6.TEMPORARY: Temporary lease # Lease6.NON_TEMPORARY: Non-temporary lease # Lease6.PREFIX_DELEGATION: Delegated prefix lease lease.host_identifier # The unique host identifier (replaces mac addresses in IPv6) as bytes lease.host_identifier_string # The host_identifier property formatted as an hexadecimal string lease.duid # The DHCP Unique Identifier (DUID) of the host as bytes lease.iaid # The Interface Association Identifier (IAID) of the host lease.last_communication # The last communication time with the host lease.end # The time this lease expires as DateTime object or None if this is an infinite lease lease.binding_state # The binding state as string ('active', 'free', 'abandoned', 'backup') lease.preferred_life # The preferred lifetime in seconds lease.max_life # The valid lifetime for this address in seconds lease.options # List of extra options in the lease file lease.sets # List of the 'set' items in the lease file lease.data # Dict of all the info in the dhcpd6.leases file for this lease Unit tests ---------- The unit tests can be run with ``setup.py``: .. code:: bash $ python3 setup.py test # With coverage report: $ coverage run setup.py test .. |Build Status| image:: https://travis-ci.org/MartijnBraam/python-isc-dhcp-leases.svg?branch=master :target: https://travis-ci.org/MartijnBraam/python-isc-dhcp-leases .. |PyPI version| image:: https://badge.fury.io/py/isc_dhcp_leases.svg :target: http://badge.fury.io/py/isc_dhcp_leases .. |Coverage Status| image:: https://coveralls.io/repos/MartijnBraam/python-isc-dhcp-leases/badge.svg :target: https://coveralls.io/r/MartijnBraam/python-isc-dhcp-leases ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1679240302.5279388 isc_dhcp_leases-0.10.0/isc_dhcp_leases/0000755000000000000000000000000014405626157016560 5ustar00rootroot././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases/__init__.py0000644000000000000000000000022014405625540020656 0ustar00rootrootfrom __future__ import absolute_import from .iscdhcpleases import IscDhcpLeases, Lease, Lease6 __author__ = 'Martijn Braam ' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases/iscdhcpleases.py0000644000000000000000000003045714405625540021750 0ustar00rootrootimport binascii import codecs import datetime import re import struct import gzip from six import iteritems try: utc = datetime.timezone.utc except: # Support Python 2.7 class UTC(datetime.tzinfo): def fromutc(self, dt): return dt def utcffset(self, dt): return datetime.timedelta(0) def dst(self, dt): return None def tzname(self, dt): return 'UTC' utc = UTC() def check_datetime(dt): if not (dt is None or (isinstance(dt, datetime.datetime) and dt.tzinfo)): raise ValueError('None or offset-aware datetime required') def parse_time(s): """ Like datetime.datetime.strptime(s, "%w %Y/%m/%d %H:%M:%S") but 5x faster. """ result = None if "epoch" in s: epoch_time = float(s.rstrip().split(' ')[1][:-1]) result = datetime.datetime.utcfromtimestamp(epoch_time) else: _, date_part, time_part = s.split(' ') year, mon, day = date_part.split('/') hour, minute, sec = time_part.split(':') result = datetime.datetime(*map(int, (year, mon, day, hour, minute, sec))) return result.replace(tzinfo=utc) def _extract_prop_option(line): """ Extract the (key,value)-tuple from a string like: >>> "option foobar 123" :param line: :return: tuple (key, value) """ line = line[7:] pos = line.find(' ') return line[:pos], line[pos + 1:] def _extract_prop_set(line): """ Extract the (key, value)-tuple from a string like: >>> 'set foo = "bar"' :param line: :return: tuple (key, value) """ token = ' = "' line = line[4:] pos = line.find(token) return line[:pos], line[pos + 4:-1] def _extract_prop_general(line): """ Extract the (key, value)-tuple from a "standard" property line like: >>> 'hardware ethernet 12:34:56:78:90:AB' :param line: :return: tuple (key, value) """ pos = line.find(' ') return line[:pos], line[pos + 1:] def _extract_properties(config): """ Parse a line within a lease block The line should basically match the expression: >>> r"\s+(?P(?:option|set)\s+\S+|\S+) (?P[\s\S]+?);" For easier seperation of the cases and faster parsing this is done using substrings etc.. :param config: :return: tuple of properties dict, options dict and sets dict """ general, options, sets = {}, {}, {} for line in config.splitlines(): # skip empty & malformed lines if not line or not line[-1:] == ';' and '; #' not in line: continue # strip the trailing ';' and remove any whitespaces on the left side line = line[:-1].lstrip() # seperate the three cases if line[:6] == 'option': key, value = _extract_prop_option(line) options[key] = value elif line[:3] == 'set': key, value = _extract_prop_set(line) sets[key] = value else: # fall through to generic case key, value = _extract_prop_general(line) general[key] = value return general, options, sets class IscDhcpLeases(object): """ Class to parse isc-dhcp-server lease files into lease objects """ regex_leaseblock = re.compile(r"lease (?P\d+\.\d+\.\d+\.\d+) {(?P[\s\S]+?)\n}") regex_leaseblock6 = re.compile( r"ia-(?Pta|na|pd) \"(?P[^\"\\]*(?:\\.[^\"\\]*)*)\" {(?P[\s\S]+?)\n}") regex_iaaddr = re.compile(r"ia(addr|prefix) (?P[0-9a-f:]+(/[0-9]+)?) {(?P[\s\S]+?)\n\s+}") def __init__(self, filename, gzip=False, now=None): check_datetime(now) self.filename = filename self.gzip = gzip self.now = now def get(self, include_backups=False): """ Parse the lease file and return a list of Lease instances. """ leases = [] with open(self.filename) if not self.gzip else gzip.open(self.filename) as lease_file: lease_data = lease_file.read() if self.gzip: lease_data = lease_data.decode('utf-8') for match in self.regex_leaseblock.finditer(lease_data): block = match.groupdict() properties, options, sets = _extract_properties(block['config']) if 'hardware' not in properties and not include_backups: # E.g. rows like {'binding': 'state abandoned', ...} continue lease = Lease(block['ip'], properties=properties, options=options, sets=sets, now=self.now) leases.append(lease) for match in self.regex_leaseblock6.finditer(lease_data): block = match.groupdict() properties, options, sets = _extract_properties(block['config']) host_identifier = block['id'] block_type = block['type'] last_client_communication = parse_time(properties['cltt']) for address_block in self.regex_iaaddr.finditer(block['config']): block = address_block.groupdict() properties, options, sets = _extract_properties(block['config']) lease = Lease6(block['ip'], properties, last_client_communication, host_identifier, block_type, options=options, sets=sets, now=self.now) leases.append(lease) return leases def get_current(self): """ Parse the lease file and return a dict of active and valid Lease instances. The key for this dict is the ethernet address of the lease. """ all_leases = self.get() leases = {} for lease in all_leases: if lease.valid and lease.active: if type(lease) is Lease: leases[lease.ethernet] = lease elif type(lease) is Lease6: leases['%s-%s' % (lease.type, lease.host_identifier_string)] = lease return leases class BaseLease(object): """ Base Implementation for all leases. This does most of the common work that is shared among v4 and v6 leases. Attributes: ip The IP address assigned by this lease as string data Dict of all the info in the dhcpd.leases file for this lease options Options on this lease sets Dict of key-value set statement values from this lease """ def __init__(self, ip, properties, options=None, sets=None, now=None): check_datetime(now) if options is None: options = {} if sets is None: sets = {} self.ip = ip self.data = properties self.options = options self.sets = sets _, self.binding_state = properties['binding'].split(' ', 1) self._now = now @property def active(self): """ Shorthand to check if the binding_state is active :return: bool: True if lease is active """ return self.binding_state == 'active' @property def now(self): """ :return: datetime: real current time, unless a historical time is set """ if self._now: return self._now else: return datetime.datetime.utcnow().replace(tzinfo=utc) class Lease(BaseLease): """ Representation of a IPv4 dhcp lease Attributes: ip The IPv4 address assigned by this lease as string hardware The OSI physical layer used to request the lease (usually ethernet) ethernet The ethernet address of this lease (MAC address) start The start time of this lease as DateTime object end The time this lease expires as DateTime object or None if this is an infinite lease hostname The hostname for this lease if given by the client binding_state The binding state as string ('active', 'free', 'abandoned', 'backup') data Dict of all the info in the dhcpd.leases file for this lease """ def __init__(self, ip, properties, **kwargs): super(Lease, self).__init__(ip, properties=properties, **kwargs) if 'starts' in properties: self.start = parse_time(properties['starts']) else: self.start = None if properties.get('ends', 'never') == 'never': self.end = None else: self.end = parse_time(properties['ends']) if 'hardware' in properties: self._hardware = properties['hardware'].split(' ') self.ethernet = self._hardware[1] self.hardware = self._hardware[0] else: self.hardware = None self.ethernet = None self.hostname = properties.get('client-hostname', '').replace("\"", "") @property def valid(self): """ Checks if the lease is currently valid (not expired and not in the future) :return: bool: True if lease is valid """ if self.end is None: if self.start is not None: return self.start <= self.now else: return True else: if self.start is not None: return self.start <= self.now <= self.end else: return self.now <= self.end def __repr__(self): return "".format(self.ip, self.ethernet, self.hostname) def __eq__(self, other): return self.ip == other.ip and self.ethernet == other.ethernet and self.start == other.start class Lease6(BaseLease): """ Representation of a IPv6 dhcp lease Attributes: ip The IPv6 address assigned by this lease as string type If this is a temporary or permanent address host_identifier The unique host identifier (replaces mac addresses in IPv6) duid The DHCP Unique Identifier (DUID) of the host iaid The Interface Association Identifier (IAID) of the host last_communication The last communication time with the host end The time this lease expires as DateTime object or None if this is an infinite lease binding_state The binding state as string ('active', 'free', 'abandoned', 'backup') preferred_life The preferred lifetime in seconds max_life The valid lifetime for this address in seconds data Dict of all the info in the dhcpd.leases file for this lease """ (TEMPORARY, NON_TEMPORARY, PREFIX_DELEGATION) = ('ta', 'na', 'pd') def __init__(self, ip, properties, cltt, host_identifier, address_type, **kwargs): super(Lease6, self).__init__(ip, properties=properties, **kwargs) self.type = address_type self.last_communication = cltt self.host_identifier = self._iaid_duid_to_bytes(host_identifier) self.iaid = struct.unpack('".format(self.ip) def __eq__(self, other): return self.ip == other.ip and self.host_identifier == other.host_identifier def _iaid_duid_to_bytes(self, input_string): """ Parse the IAID_DUID from dhcpd.leases to the bytes representation This method doesn't support the colon separated hex format yet. """ result = codecs.decode(input_string, 'unicode_escape').encode('latin-1') return result if __name__ == "__main__": # pragma: no cover leases = IscDhcpLeases('dhcpd.leases') print(leases.get_current()) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1679240302.5312722 isc_dhcp_leases-0.10.0/isc_dhcp_leases/test_files/0000755000000000000000000000000014405626157020721 5ustar00rootroot././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases/test_files/backup.leases0000644000000000000000000000072214405625540023360 0ustar00rootrootlease 10.0.0.1 { starts 4 2017/10/05 15:22:29; ends 1 2017/10/16 08:09:23; tstp 1 2017/10/16 15:22:29; tsfp 1 2017/10/16 15:22:29; atsfp 1 2017/10/16 15:22:29; cltt 4 2017/10/05 15:22:29; binding state backup; hardware ethernet 2a:b2:2a:b2:2a:b2; uid "\001\000!\314\006\224\351"; } lease 10.0.0.2 { starts 2 2017/10/10 12:05:14; tstp 2 2017/10/10 12:05:14; tsfp 2 2017/10/10 12:05:14; atsfp 2 2017/10/10 12:05:14; binding state backup; }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases/test_files/debian7.leases0000644000000000000000000000302114405625540023417 0ustar00rootroot# The format of this file is documented in the dhcpd.leases(5) manual page. # This lease file was written by isc-dhcp-4.2.2 lease 10.0.0.10 { starts 2 2013/12/10 12:57:04; ends 2 2013/12/10 13:07:04; tstp 2 2013/12/10 13:07:04; cltt 2 2013/12/10 12:57:04; binding state free; hardware ethernet 60:a4:4c:b5:6a:dd; uid "\377\000\000\000\002\000\001\000\001\0321\301\300\000#\213\360F\350"; set vendor-class-identifier = "Some Vendor Identifier"; } lease 10.0.0.15 { starts 4 2014/01/23 13:40:45; ends 4 2014/01/23 13:50:45; tstp 4 2014/01/23 13:50:45; cltt 4 2014/01/23 13:40:45; binding state free; hardware ethernet bc:f5:ac:fe:d1:7d; uid "\001\274\365\254\376\321}"; } lease 10.0.0.18 { starts 2 2014/10/07 13:13:14; ends 2 2014/10/07 15:13:14; tstp 2 2014/10/07 15:13:14; cltt 2 2014/10/07 13:13:14; binding state free; hardware ethernet e0:c9:7a:89:c9:2e; uid "\001\340\311z\211\311."; } lease 10.0.0.21 { starts 1 2015/01/12 10:29:26; ends 1 2015/01/12 10:39:26; tstp 1 2015/01/12 10:39:26; cltt 1 2015/01/12 10:29:26; binding state free; hardware ethernet 9c:2a:70:7c:43:6a; uid "\001\234*p|Cj"; } lease 10.0.0.16 { starts 3 2015/03/18 09:21:44; ends 3 2015/03/18 09:31:44; tstp 3 2015/03/18 09:31:44; cltt 3 2015/03/18 09:21:44; binding state free; hardware ethernet 6c:71:d9:21:55:71; uid "\001lq\331!Uq"; } lease 10.0.0.17 { starts 2 2008/01/08 18:09:46; ends 2 2008/01/08 18:09:46; tstp 2 2008/01/08 18:09:46; binding state abandoned; next binding state free; }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases/test_files/debian7.leases.gz0000644000000000000000000000110414405625540024036 0ustar00rootrootSYdebian7.leasesK0[ش#íT>V٘ 0)}{^DB_g_WzclVerF-_ypb璵owޮag}2j6p|$YLT%"LΒ `Ѐq1\6{r]*hhne1VtekwOHZԢsqھt0Υ19c/#R\x&2)KQIFXۦv\0xr|&IT_'o5c|&dLE1Thmm&(pүG\%FQ>:FmmXSvL^^O^a1Gc1oJrj oVcA:@AjmV )PI:-a㏞Lʗ:f겙]*Ou8H[w 4]Rkq~ʥo=84veYLe3RTzӮܮփ]XzcT=\z=i././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases/test_files/dhcpd6-4.2.4.leases0000644000000000000000000000122614405625540023726 0ustar00rootroot# The format of this file is documented in the dhcpd.leases(5) manual page. # This lease file was written by isc-dhcp-4.2.4 server-duid "\000\001\000\001\035f\037-N\316\347\374,\226"; ia-na "4dv\352\000\001\000\001\035f\037\342\012\000'\000\000\000" { cltt 2 2015/08/18 16:55:37; iaaddr 2001:610:600:891d::60 { binding state active; preferred-life 375; max-life 600; ends 2 2015/08/18 17:05:37; } } ia-pd "4dv\352\000\001\000\001\035f\037\342\012\000'\000\000\000" { cltt 2 2015/08/18 16:55:40; iaprefix 2001:610:500:fff::/64 { binding state active; preferred-life 175; max-life 200; ends 2 2015/08/18 17:05:40; } } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases/test_files/dhcpd6-4.3.3.leases0000644000000000000000000000407214405625540023730 0ustar00rootroot# The format of this file is documented in the dhcpd.leases(5) manual page. # This lease file was written by isc-dhcp-4.3.3 server-duid "\000\001\000\001\036\031\350\241\000\014)\345\0202"; ia-na "\001\000\000\000\000\001\000\001\034\367\020\245\000'\"3+4" { cltt 3 2016/01/06 14:50:34; iaaddr 2001:10:10::106 { binding state active; preferred-life 540; max-life 864; ends 3 2016/01/06 15:04:58; set iana = "2001:10:10:0:0:0:0:106"; set clientduid = "0100011cf710a5002722332b34"; on expiry { log (debug, "===EXPIRY==="); } on release { log (debug, "===RELEASE==="); } } } ia-pd "\000\000\000\000\000\001\000\001\0354L\000\000%\220k\2414" { cltt 3 2016/01/06 14:52:37; iaprefix 2001:10:30:ff00::/56 { binding state active; preferred-life 540; max-life 864; ends 3 2016/01/06 15:07:01; set iapd = "2001:10:30:ff00:0:0:0:0"; set pdsize = "56"; set pdnet = "2001:10:30:ff00:0:0:0:0/56"; set clientduid = "0100011d344c000025906ba134"; on expiry { log (debug, "===EXPIRY==="); } on release { log (debug, "===RELEASE==="); } } } ia-na "\000\000\000\000\000\001\000\001\0354L\000\000%\220k\2414" { cltt 3 2016/01/06 14:52:46; iaaddr 2001:10:30::1fe { binding state active; preferred-life 540; max-life 864; ends 3 2016/01/06 15:07:10; set iana = "2001:10:30:0:0:0:0:1fe"; set clientduid = "0100011d344c000025906ba134"; on expiry { log (debug, "===EXPIRY==="); } on release { log (debug, "===RELEASE==="); } } } ia-pd "\001\000\000\000\000\001\000\001\034\367\020\245\000'\"3+4" { cltt 3 2016/01/06 14:53:35; iaprefix 2001:10:10:ff00::/56 { binding state active; preferred-life 540; max-life 864; ends 3 2016/01/06 15:07:59; set iapd = "2001:10:10:ff00:0:0:0:0"; set pdsize = "56"; set pdnet = "2001:10:10:ff00:0:0:0:0/56"; set clientduid = "0100011cf710a5002722332b34"; on expiry { log (debug, "===EXPIRY==="); } on release { log (debug, "===RELEASE==="); } } }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases/test_files/epoch.leases0000644000000000000000000000125214405625540023210 0ustar00rootrootlease 10.0.0.1 { starts epoch 1507216949; # Thu Oct 05 15:22:29 2017 ends epoch 1508141363; # Mon Oct 16 08:09:23 2017 tstp epoch 1508141363; # Mon Oct 16 08:09:23 2017 tsfp epoch 1508141363; # Mon Oct 16 08:09:23 2017 atsfp epoch 1508141363; # Mon Oct 16 08:09:23 2017 cltt epoch 1508167349; # Thu Oct 05 15:22:29 2017 binding state backup; hardware ethernet 2a:b2:2a:b2:2a:b2; uid "\001\000!\314\006\224\351"; } lease 10.0.0.2 { starts epoch 1507637114; # Tue Oct 1012:05:14 2017 tstp epoch 1507637114; # Tue Oct 1012:05:14 2017 tsfp epoch 1507637114; # Tue Oct 1012:05:14 2017 atsfp epoch 1507637114; # Tue Oct 1012:05:14 2017 binding state backup; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases/test_files/options.leases0000644000000000000000000000072414405625540023610 0ustar00rootrootlease 10.10.10.10 { starts 6 2016/02/27 07:11:41; ends 6 2016/02/27 09:11:41; cltt 6 2016/02/27 07:11:41; binding state active; next binding state free; rewind binding state free; hardware ethernet 24:65:11:d9:a6:b3; uid "\377\021\331\246\263\000\003\000\001$e\021\331\246\263"; option agent.circuit-id 0:1:3:e9; option agent.remote-id a4:a2:4a:33:db:e5; option agent.DOCSIS-device-class 2; option agent.unknown-9 0:0:11:8b:6:1:4:1:2:3:0; client-hostname "KRONOS"; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases/test_files/pfsense.leases0000644000000000000000000000132414405625540023555 0ustar00rootroot# The format of this file is documented in the dhcpd.leases(5) manual page. # This lease file was written by isc-dhcp-4.2.8 lease 10.0.10.72 { starts 1 2015/07/06 07:50:42; ends 1 2015/07/06 08:20:42; cltt 1 2015/07/06 07:50:42; binding state active; next binding state free; rewind binding state free; hardware ethernet 64:5a:04:6a:07:a2; uid "\001dZ\004j\007\242"; client-hostname "Satellite-C700"; } lease 10.0.0.36 { starts 1 2015/07/06 07:38:46; ends 1 2015/07/06 08:38:46; cltt 1 2015/07/06 07:38:46; binding state active; next binding state free; rewind binding state free; hardware ethernet 14:da:e9:04:c8:a3; uid "\001\024\332\351\004\310\243"; client-hostname "Gebruiker-PC"; }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases/test_files/static.leases0000644000000000000000000000035614405625540023405 0ustar00rootrootlease 10.0.0.15 { starts 4 2015/09/10 00:29:00; tstp 4 2015/09/10 00:28:44; tsfp 4 2015/09/10 00:29:00; atsfp 4 2015/09/10 00:29:00; binding state free; hardware ethernet 2a:b2:2a:b2:2a:b2; uid "\001\000!\314\006\224\351"; }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases/test_iscDhcpLeases.py0000644000000000000000000003114314405625540022700 0ustar00rootrootimport datetime from unittest import TestCase from isc_dhcp_leases.iscdhcpleases import IscDhcpLeases, Lease, Lease6, utc from freezegun import freeze_time __author__ = 'Martijn Braam ' class TestIscDhcpLeases(TestCase): def _test_get(self, now=None): leases = IscDhcpLeases("isc_dhcp_leases/test_files/debian7.leases", now=now) lease_start = datetime.datetime(2013, 12, 10, 12, 57, 4, tzinfo=utc) lease_end = lease_start + datetime.timedelta(minutes=10) result = leases.get() self.assertEqual(len(result), 5) self.assertEqual(result[0].ip, "10.0.0.10") self.assertEqual(result[0].valid, False) self.assertEqual(result[0].active, False) self.assertEqual(result[0].binding_state, 'free') self.assertEqual(result[0].hardware, "ethernet") self.assertEqual(result[0].ethernet, "60:a4:4c:b5:6a:dd") self.assertEqual(result[0].hostname, "") self.assertEqual(result[0].start, lease_start) self.assertEqual(result[0].end, lease_end) self.assertEqual(result[0].sets, {'vendor-class-identifier': 'Some Vendor Identifier'}) leases = IscDhcpLeases("isc_dhcp_leases/test_files/pfsense.leases", now=now) lease_start = datetime.datetime(2015, 7, 6, 7, 50, 42, tzinfo=utc) lease_end = lease_start + datetime.timedelta(minutes=30) result = leases.get() self.assertEqual(len(result), 2) self.assertEqual(result[0].ip, "10.0.10.72") self.assertEqual(result[0].valid, True) self.assertEqual(result[0].active, True) self.assertEqual(result[0].binding_state, 'active') self.assertEqual(result[0].hardware, "ethernet") self.assertEqual(result[0].ethernet, "64:5a:04:6a:07:a2") self.assertEqual(result[0].hostname, "Satellite-C700") self.assertEqual(result[0].start, lease_start) self.assertEqual(result[0].end, lease_end) leases = IscDhcpLeases("isc_dhcp_leases/test_files/dhcpd6-4.2.4.leases", now=now) result = leases.get() self.assertEqual(len(result), 2) self.assertEqual(result[0].ip, "2001:610:600:891d::60") self.assertEqual(result[0].host_identifier, b"4dv\xea\x00\x01\x00\x01\x1df\x1f\xe2\n\x00'\x00\x00\x00") self.assertEqual(result[0].iaid, 3933627444) self.assertEqual(result[0].duid, b"\x00\x01\x00\x01\x1df\x1f\xe2\n\x00'\x00\x00\x00") self.assertEqual(result[0].valid, True) self.assertEqual(result[0].active, True) self.assertEqual(result[0].binding_state, 'active') self.assertEqual(result[0].preferred_life, 375) self.assertEqual(result[0].max_life, 600) self.assertEqual( result[0].last_communication, datetime.datetime(2015, 8, 18, 16, 55, 37, tzinfo=utc)) self.assertEqual(result[0].type, Lease6.NON_TEMPORARY) self.assertEqual(result[1].ip, "2001:610:500:fff::/64") self.assertEqual(result[1].host_identifier, b"4dv\xea\x00\x01\x00\x01\x1df\x1f\xe2\n\x00'\x00\x00\x00") self.assertEqual(result[1].iaid, 3933627444) self.assertEqual(result[1].duid, b"\x00\x01\x00\x01\x1df\x1f\xe2\n\x00'\x00\x00\x00") self.assertEqual(result[1].valid, True) self.assertEqual(result[1].active, True) self.assertEqual(result[1].binding_state, 'active') self.assertEqual(result[1].preferred_life, 175) self.assertEqual(result[1].max_life, 200) self.assertEqual( result[1].last_communication, datetime.datetime(2015, 8, 18, 16, 55, 40, tzinfo=utc)) self.assertEqual(result[1].type, Lease6.PREFIX_DELEGATION) leases = IscDhcpLeases("isc_dhcp_leases/test_files/dhcpd6-4.3.3.leases", now=now) result = leases.get() self.assertEqual(len(result), 4) self.assertEqual(result[0].ip, "2001:10:10::106") self.assertEqual(result[0].host_identifier, b"\001\000\000\000\000\001\000\001\034\367\020\245\000'\"3+4") self.assertEqual(result[0].iaid, 1) self.assertEqual(result[0].duid, b"\x00\x01\x00\x01\x1c\xf7\x10\xa5\x00\'\"3+4") self.assertEqual(result[0].valid, True) self.assertEqual(result[0].active, True) self.assertEqual(result[0].binding_state, 'active') self.assertEqual(result[0].preferred_life, 540) self.assertEqual(result[0].max_life, 864) self.assertEqual( result[0].last_communication, datetime.datetime(2016, 1, 6, 14, 50, 34, tzinfo=utc)) self.assertEqual(result[0].type, Lease6.NON_TEMPORARY) self.assertEqual(result[0].sets, dict(iana='2001:10:10:0:0:0:0:106', clientduid='0100011cf710a5002722332b34')) self.assertEqual(result[1].ip, "2001:10:30:ff00::/56") self.assertEqual(result[1].host_identifier, b"\x00\x00\x00\x00\x00\x01\x00\x01\x1d4L\x00\x00%\x90k\xa14") self.assertEqual(result[1].iaid, 0) self.assertEqual(result[1].duid, b"\x00\x01\x00\x01\x1d4L\x00\x00%\x90k\xa14") self.assertEqual(result[1].valid, True) self.assertEqual(result[1].active, True) self.assertEqual(result[1].binding_state, 'active') self.assertEqual(result[1].preferred_life, 540) self.assertEqual(result[1].max_life, 864) self.assertEqual( result[1].last_communication, datetime.datetime(2016, 1, 6, 14, 52, 37, tzinfo=utc)) self.assertEqual(result[1].type, Lease6.PREFIX_DELEGATION) self.assertEqual(result[1].sets, dict(iapd='2001:10:30:ff00:0:0:0:0', pdsize='56', pdnet='2001:10:30:ff00:0:0:0:0/56', clientduid='0100011d344c000025906ba134')) leases = IscDhcpLeases("isc_dhcp_leases/test_files/options.leases", now=now) lease_start = datetime.datetime(2016, 2, 27, 7, 11, 41, tzinfo=utc) lease_end = lease_start + datetime.timedelta(hours=2) result = leases.get() self.assertEqual(len(result), 1) self.assertEqual(result[0].ip, "10.10.10.10") self.assertEqual(result[0].valid, False) self.assertEqual(result[0].active, True) self.assertEqual(result[0].binding_state, 'active') self.assertEqual(result[0].hardware, "ethernet") self.assertEqual(result[0].ethernet, "24:65:11:d9:a6:b3") self.assertEqual(result[0].hostname, "KRONOS") self.assertEqual(result[0].start, lease_start) self.assertEqual(result[0].end, lease_end) self.assertEqual(len(result[0].options), 4) self.assertDictEqual(result[0].options, {'agent.DOCSIS-device-class': '2', 'agent.circuit-id': '0:1:3:e9', 'agent.remote-id': 'a4:a2:4a:33:db:e5', 'agent.unknown-9': '0:0:11:8b:6:1:4:1:2:3:0'}) leases = IscDhcpLeases("isc_dhcp_leases/test_files/static.leases", now=now) lease_start = datetime.datetime(2015, 9, 10, 0, 29, 0, tzinfo=utc) lease_end = None result = leases.get() self.assertEqual(len(result), 1) self.assertEqual(result[0].ip, "10.0.0.15") self.assertEqual(result[0].valid, False) self.assertEqual(result[0].active, False) self.assertEqual(result[0].binding_state, "free") self.assertEqual(result[0].hardware, "ethernet") self.assertEqual(result[0].start, lease_start) self.assertEqual(result[0].end, lease_end) @freeze_time("2015-07-6 8:15:0") def test_get_frozen(self): self._test_get() def test_get_historical(self): self._test_get( now=datetime.datetime(2015, 7, 6, 8, 15, 0, tzinfo=utc)) @freeze_time("2015-06-6 8:15:0") def test_backup_leases(self): leases = IscDhcpLeases("isc_dhcp_leases/test_files/backup.leases") lease_start = [ datetime.datetime(2017, 10, 5, 15, 22, 29, tzinfo=utc), datetime.datetime(2017, 10, 10, 12, 5, 14, tzinfo=utc), ] lease_end = [ datetime.datetime(2017, 10, 16, 8, 9, 23, tzinfo=utc), None, ] result = leases.get() self.assertEqual(len(result), 1) result = leases.get(include_backups=True) self.assertEqual(len(result), 2) self.assertEqual(result[0].ip, "10.0.0.1") self.assertEqual(result[0].valid, False) self.assertEqual(result[0].active, False) self.assertEqual(result[0].binding_state, "backup") self.assertEqual(result[0].hardware, "ethernet") self.assertEqual(result[0].start, lease_start[0]) self.assertEqual(result[0].end, lease_end[0]) self.assertEqual(result[1].ip, "10.0.0.2") self.assertEqual(result[1].valid, False) self.assertEqual(result[1].active, False) self.assertEqual(result[1].binding_state, "backup") self.assertIsNone(result[1].hardware) self.assertEqual(result[1].start, lease_start[1]) self.assertEqual(result[1].end, lease_end[1]) @freeze_time("2015-06-6 8:15:0") def test_epoch_leases(self): leases = IscDhcpLeases("isc_dhcp_leases/test_files/epoch.leases") lease_start = [ datetime.datetime(2017, 10, 5, 15, 22, 29, tzinfo=utc), datetime.datetime(2017, 10, 10, 12, 5, 14, tzinfo=utc), ] lease_end = [ datetime.datetime(2017, 10, 16, 8, 9, 23, tzinfo=utc), None, ] result = leases.get() self.assertEqual(len(result), 1) result = leases.get(include_backups=True) self.assertEqual(len(result), 2) self.assertEqual(result[0].ip, "10.0.0.1") self.assertEqual(result[0].valid, False) self.assertEqual(result[0].active, False) self.assertEqual(result[0].binding_state, "backup") self.assertEqual(result[0].hardware, "ethernet") self.assertEqual(result[0].start, lease_start[0]) self.assertEqual(result[0].end, lease_end[0]) self.assertEqual(result[1].ip, "10.0.0.2") self.assertEqual(result[1].valid, False) self.assertEqual(result[1].active, False) self.assertEqual(result[1].binding_state, "backup") self.assertIsNone(result[1].hardware) self.assertEqual(result[1].start, lease_start[1]) self.assertEqual(result[1].end, lease_end[1]) @freeze_time("2015-07-6 8:15:0") def test_get_current(self): leases = IscDhcpLeases("isc_dhcp_leases/test_files/debian7.leases") result = leases.get_current() self.assertEqual(len(result), 0) leases = IscDhcpLeases("isc_dhcp_leases/test_files/pfsense.leases") result = leases.get_current() self.assertEqual(len(result), 2) self.assertTrue("14:da:e9:04:c8:a3" in result) self.assertTrue("64:5a:04:6a:07:a2" in result) self.assertTrue(result["14:da:e9:04:c8:a3"].valid) self.assertTrue(result["64:5a:04:6a:07:a2"].valid) def test_get_current_ipv6(self): with freeze_time("2015-08-18 17:0:0"): leases = IscDhcpLeases("isc_dhcp_leases/test_files/dhcpd6-4.2.4.leases") result = leases.get_current() self.assertEqual(len(result), 2) self.assertIn('na-346476ea000100011d661fe20a0027000000', result) self.assertIn('pd-346476ea000100011d661fe20a0027000000', result) for key, r in result.items(): self.assertTrue(r.valid, key) with freeze_time("2015-08-18 18:0:0"): leases = IscDhcpLeases("isc_dhcp_leases/test_files/dhcpd6-4.2.4.leases") result = leases.get_current() self.assertEqual(len(result), 0) def test_gzip_handling(self): leases = IscDhcpLeases("isc_dhcp_leases/test_files/debian7.leases.gz", True) lease_start = datetime.datetime(2013, 12, 10, 12, 57, 4, tzinfo=utc) lease_end = lease_start + datetime.timedelta(minutes=10) result = leases.get() self.assertEqual(len(result), 5) self.assertEqual(result[0].ip, "10.0.0.10") self.assertEqual(result[0].valid, False) self.assertEqual(result[0].active, False) self.assertEqual(result[0].binding_state, 'free') self.assertEqual(result[0].hardware, "ethernet") self.assertEqual(result[0].ethernet, "60:a4:4c:b5:6a:dd") self.assertEqual(result[0].hostname, "") self.assertEqual(result[0].start, lease_start) self.assertEqual(result[0].end, lease_end) self.assertEqual(result[0].sets, {'vendor-class-identifier': 'Some Vendor Identifier'}) def test_naive_time(self): with self.assertRaises(ValueError): IscDhcpLeases("isc_dhcp_leases/test_files/debian7.leases", now=datetime.datetime.now()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases/test_lease.py0000644000000000000000000000746414405625540021270 0ustar00rootrootimport datetime from unittest import TestCase from isc_dhcp_leases.iscdhcpleases import Lease, utc from freezegun import freeze_time __author__ = 'Martijn Braam ' class TestLease(TestCase): def setUp(self): self.lease_data = { 'starts': '2 2013/12/10 12:57:04', 'uid': '"\\377\\000\\000\\000\\002\\000\\001\\000\\001\\0321\\301\\300\\000#\\213\\360F\\350"', 'binding': 'state free', 'ends': 'never', 'hardware': 'ethernet 60:a4:4c:b5:6a:dd', 'cltt': '2 2013/12/10 12:57:04', 'tstp': '2 2013/12/10 13:07:04', 'client-hostname': '"Satellite-C700"' } def test_init(self): lease = Lease("192.168.0.1", self.lease_data) self.assertEqual(lease.ip, "192.168.0.1") self.assertEqual(lease.hardware, "ethernet") self.assertEqual(lease.ethernet, "60:a4:4c:b5:6a:dd") self.assertEqual(lease.hostname, "Satellite-C700") self.assertEqual( lease.start, datetime.datetime(2013, 12, 10, 12, 57, 4, tzinfo=utc)) self.assertIsNone(lease.end) self.assertTrue(lease.valid) self.assertFalse(lease.active) self.assertEqual(lease.binding_state, 'free') def test_repr(self): lease = Lease("192.168.0.1", self.lease_data) self.assertEqual(repr(lease), '') def _test_valid(self, now=None): lease = Lease("192.168.0.1", self.lease_data, now=now) self.assertTrue(lease.valid) # Lease is forever lease.end = datetime.datetime(2015, 7, 6, 13, 57, 4, tzinfo=utc) self.assertTrue(lease.valid) # Lease is within start and end lease.end = lease.end - datetime.timedelta(hours=7) self.assertFalse(lease.valid) # Lease is ended lease.start = datetime.datetime(2015, 7, 6, 12, 57, 4, tzinfo=utc) lease.end = lease.start + datetime.timedelta(hours=1) self.assertFalse(lease.valid) # Lease is in the future @freeze_time("2015-07-6 8:15:0") def test_valid_frozen(self): self._test_valid() def test_valid_historical(self): self._test_valid( now=datetime.datetime(2015, 7, 6, 8, 15, 0, tzinfo=utc)) def test_eq(self): lease_a = Lease("192.168.0.1", self.lease_data) lease_b = Lease("192.168.0.1", self.lease_data) self.assertEqual(lease_a, lease_b) lease_b.ip = "172.16.42.1" self.assertNotEqual(lease_a, lease_b) lease_b.ip = "192.168.0.1" lease_b.ethernet = "60:a4:4c:b5:6a:de" self.assertNotEqual(lease_a, lease_b) def test_init_no_starts_property(self): self.lease_data.pop('starts') lease = Lease("192.168.0.1", self.lease_data) self.assertEqual(lease.ip, "192.168.0.1") self.assertEqual(lease.hardware, "ethernet") self.assertEqual(lease.ethernet, "60:a4:4c:b5:6a:dd") self.assertEqual(lease.hostname, "Satellite-C700") self.assertIsNone(lease.end) self.assertIsNone(lease.start) self.assertTrue(lease.valid) self.assertFalse(lease.active) self.assertEqual(lease.binding_state, 'free') @freeze_time("2015-07-6 8:15:0") def test_valid_no_starts_property(self): self.lease_data.pop('starts') lease = Lease("192.168.0.1", self.lease_data) self.assertTrue(lease.valid) # Lease is forever lease.end = datetime.datetime(2015, 7, 6, 6, 57, 4, tzinfo=utc) self.assertFalse(lease.valid) # Lease is ended lease.end = lease.end + datetime.timedelta(hours=3) self.assertTrue(lease.valid) # Lease is not expired def test_naive_time(self): with self.assertRaises(ValueError): Lease("192.168.0.1", self.lease_data, now=datetime.datetime.now()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases/test_lease6.py0000644000000000000000000000663014405625540021350 0ustar00rootrootimport datetime from unittest import TestCase from isc_dhcp_leases.iscdhcpleases import Lease6, utc from freezegun import freeze_time __author__ = 'Martijn Braam ' class TestLease6(TestCase): def setUp(self): self.lease_time = datetime.datetime(2015, 8, 18, 16, 55, 37, tzinfo=utc) self.lease_data = { 'binding': 'state active', 'ends': 'never', 'preferred-life': '375', 'max-life': '600' } def test_init(self): lease = Lease6("2001:610:600:891d::60", self.lease_data, self.lease_time, "4dv\\352\\000\\001\\000\\001\\035f\\037\\342\\012\\000'\\000\\000\\000", "na") self.assertEqual(lease.ip, "2001:610:600:891d::60") self.assertEqual(lease.host_identifier, b"4dv\xea\x00\x01\x00\x01\x1df\x1f\xe2\n\x00'\x00\x00\x00") self.assertEqual(lease.valid, True) self.assertEqual(lease.iaid, 3933627444) self.assertEqual(lease.duid, b"\x00\x01\x00\x01\x1df\x1f\xe2\n\x00'\x00\x00\x00") self.assertEqual(lease.active, True) self.assertEqual(lease.binding_state, 'active') self.assertEqual(lease.preferred_life, 375) self.assertEqual(lease.max_life, 600) self.assertEqual(lease.last_communication, self.lease_time) self.assertEqual(lease.type, Lease6.NON_TEMPORARY) def test_repr(self): lease = Lease6("2001:610:600:891d::60", self.lease_data, self.lease_time, "4dv\\352\\000\\001\\000\\001\\035f\\037\\342\\012\\000'\\000\\000\\000", "na") self.assertEqual(repr(lease), '') def _test_valid(self, now=None): lease = Lease6("2001:610:600:891d::60", self.lease_data, self.lease_time, "4dv\\352\\000\\001\\000\\001\\035f\\037\\342\\012\\000'\\000\\000\\000", "na", now=now) self.assertTrue(lease.valid) # Lease is forever lease.end = datetime.datetime(2015, 7, 6, 13, 57, 4, tzinfo=utc) self.assertTrue(lease.valid) # Lease is before end lease.end = lease.end - datetime.timedelta(hours=7) self.assertFalse(lease.valid) # Lease is ended @freeze_time("2015-07-6 8:15:0") def test_valid_frozen(self): self._test_valid() def test_valid_historical(self): self._test_valid( now=datetime.datetime(2015, 7, 6, 8, 15, 0, tzinfo=utc)) def test_eq(self): lease_a = Lease6("2001:610:600:891d::60", self.lease_data, self.lease_time, "4dv\\352\\000\\001\\000\\001\\035f\\037\\342\\012\\000'\\000\\000\\000", "na") lease_b = Lease6("2001:610:600:891d::60", self.lease_data, self.lease_time, "4dv\\352\\000\\001\\000\\001\\035f\\037\\342\\012\\000'\\000\\000\\000", "na") self.assertEqual(lease_a, lease_b) lease_b.ip = "2001:610:600:891d::42" self.assertNotEqual(lease_a, lease_b) lease_b.ip = "2001:610:600:891d::60" lease_b.host_identifier = "gd4\352\000\001\000\001\035b\037\322\012\000'\000\000\000" self.assertNotEqual(lease_a, lease_b) def test_naive_time(self): with self.assertRaises(ValueError): Lease6("2001:610:600:891d::60", self.lease_data, self.lease_time, "4dv\\352\\000\\001\\000\\001\\035f\\037\\342\\012\\000'\\000\\000\\000", "na", now=datetime.datetime.now()) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1679240302.5312722 isc_dhcp_leases-0.10.0/isc_dhcp_leases.egg-info/0000755000000000000000000000000014405626157020252 5ustar00rootroot././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240302.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases.egg-info/PKG-INFO0000644000000000000000000000151014405626156021343 0ustar00rootrootMetadata-Version: 2.1 Name: isc-dhcp-leases Version: 0.10.0 Summary: Small python module for reading /var/lib/dhcp/dhcpd.leases from isc-dhcp-server Home-page: https://github.com/MartijnBraam/python-isc-dhcp-leases Author: Martijn Braam Author-email: martijn@brixit.nl License: MIT Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 License-File: LICENSE ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240302.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases.egg-info/SOURCES.txt0000644000000000000000000000147314405626156022142 0ustar00rootroot.gitignore .travis.yml LICENSE MANIFEST README.rst requirements.txt setup.cfg setup.py isc_dhcp_leases/__init__.py isc_dhcp_leases/iscdhcpleases.py isc_dhcp_leases/test_iscDhcpLeases.py isc_dhcp_leases/test_lease.py isc_dhcp_leases/test_lease6.py isc_dhcp_leases.egg-info/PKG-INFO isc_dhcp_leases.egg-info/SOURCES.txt isc_dhcp_leases.egg-info/dependency_links.txt isc_dhcp_leases.egg-info/requires.txt isc_dhcp_leases.egg-info/top_level.txt isc_dhcp_leases/test_files/backup.leases isc_dhcp_leases/test_files/debian7.leases isc_dhcp_leases/test_files/debian7.leases.gz isc_dhcp_leases/test_files/dhcpd6-4.2.4.leases isc_dhcp_leases/test_files/dhcpd6-4.3.3.leases isc_dhcp_leases/test_files/epoch.leases isc_dhcp_leases/test_files/options.leases isc_dhcp_leases/test_files/pfsense.leases isc_dhcp_leases/test_files/static.leases././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240302.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases.egg-info/dependency_links.txt0000644000000000000000000000000114405626156024317 0ustar00rootroot ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240302.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases.egg-info/requires.txt0000644000000000000000000000000414405626156022643 0ustar00rootrootsix ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240302.0 isc_dhcp_leases-0.10.0/isc_dhcp_leases.egg-info/top_level.txt0000644000000000000000000000002014405626156022773 0ustar00rootrootisc_dhcp_leases ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240032.0 isc_dhcp_leases-0.10.0/requirements.txt0000644000000000000000000000002014405625540016717 0ustar00rootrootfreezegun>=0.3.4././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1679240302.5312722 isc_dhcp_leases-0.10.0/setup.cfg0000644000000000000000000000012014405626157015262 0ustar00rootroot[metadata] description-file = README.rst [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1679240211.0 isc_dhcp_leases-0.10.0/setup.py0000644000000000000000000000355014405626023015155 0ustar00rootrootfrom distutils.core import setup, Command def discover_and_run_tests(): import os import sys import unittest # get setup.py directory setup_file = sys.modules['__main__'].__file__ setup_dir = os.path.abspath(os.path.dirname(setup_file)) # use the default shared TestLoader instance test_loader = unittest.defaultTestLoader # use the basic test runner that outputs to sys.stderr test_runner = unittest.TextTestRunner() # automatically discover all tests # NOTE: only works for python 2.7 and later test_suite = test_loader.discover(setup_dir) # run the test suite result = test_runner.run(test_suite) if len(result.failures) + len(result.errors) > 0: exit(1) class DiscoverTest(Command): user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): discover_and_run_tests() setup( name='isc_dhcp_leases', version='0.10.0', packages=['isc_dhcp_leases'], url='https://github.com/MartijnBraam/python-isc-dhcp-leases', install_requires=['six'], license='MIT', author='Martijn Braam', author_email='martijn@brixit.nl', description='Small python module for reading /var/lib/dhcp/dhcpd.leases from isc-dhcp-server', cmdclass={'test': DiscoverTest}, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Topic :: Software Development', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5' ] )