isc_dhcp_leases-0.9.0/0000755000175000017500000000000013171644626015774 5ustar martijnmartijn00000000000000isc_dhcp_leases-0.9.0/PKG-INFO0000644000175000017500000000050013171644626017064 0ustar martijnmartijn00000000000000Metadata-Version: 1.0 Name: isc_dhcp_leases Version: 0.9.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 Description: UNKNOWN Platform: UNKNOWN isc_dhcp_leases-0.9.0/setup.cfg0000644000175000017500000000005113171611777017612 0ustar martijnmartijn00000000000000[metadata] description-file = README.rst isc_dhcp_leases-0.9.0/setup.py0000644000175000017500000000250313171641415017477 0ustar martijnmartijn00000000000000from 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.9.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}, ) isc_dhcp_leases-0.9.0/isc_dhcp_leases/0000755000175000017500000000000013171644626021104 5ustar martijnmartijn00000000000000isc_dhcp_leases-0.9.0/isc_dhcp_leases/__init__.py0000644000175000017500000000022013171611777023210 0ustar martijnmartijn00000000000000from __future__ import absolute_import from .iscdhcpleases import IscDhcpLeases, Lease, Lease6 __author__ = 'Martijn Braam ' isc_dhcp_leases-0.9.0/isc_dhcp_leases/test_iscDhcpLeases.py0000644000175000017500000002377613171641345025241 0ustar martijnmartijn00000000000000from unittest import TestCase from isc_dhcp_leases.iscdhcpleases import IscDhcpLeases, Lease, Lease6 from freezegun import freeze_time from datetime import datetime __author__ = 'Martijn Braam ' class TestIscDhcpLeases(TestCase): @freeze_time("2015-07-6 8:15:0") def test_get(self): leases = IscDhcpLeases("isc_dhcp_leases/test_files/debian7.leases") 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, datetime(2013, 12, 10, 12, 57, 4)) self.assertEqual(result[0].end, datetime(2013, 12, 10, 13, 7, 4)) self.assertEqual(result[0].sets, {'vendor-class-identifier': 'Some Vendor Identifier'}) leases = IscDhcpLeases("isc_dhcp_leases/test_files/pfsense.leases") 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, datetime(2015, 7, 6, 7, 50, 42)) self.assertEqual(result[0].end, datetime(2015, 7, 6, 8, 20, 42)) leases = IscDhcpLeases("isc_dhcp_leases/test_files/dhcpd6-4.2.4.leases") 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(2015, 8, 18, 16, 55, 37)) 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(2015, 8, 18, 16, 55, 40)) self.assertEqual(result[1].type, Lease6.PREFIX_DELEGATION) leases = IscDhcpLeases("isc_dhcp_leases/test_files/dhcpd6-4.3.3.leases") 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(2016, 1, 6, 14, 50, 34)) 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(2016, 1, 6, 14, 52, 37)) 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") 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, datetime(2016, 2, 27, 7, 11, 41)) self.assertEqual(result[0].end, datetime(2016, 2, 27, 9, 11, 41)) 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") 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, datetime(2015, 9, 10, 0, 29, 0)) self.assertIsNone(result[0].end) @freeze_time("2015-06-6 8:15:0") def test_backup_leases(self): leases = IscDhcpLeases("isc_dhcp_leases/test_files/backup.leases") 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, datetime(2017, 10, 5, 15, 22, 29)) self.assertEqual(result[0].end, datetime(2017, 10, 16, 8, 9, 23)) 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, datetime(2017, 10, 10, 12, 5, 14)) self.assertIsNone(result[1].end) @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) 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, datetime(2013, 12, 10, 12, 57, 4)) self.assertEqual(result[0].end, datetime(2013, 12, 10, 13, 7, 4)) self.assertEqual(result[0].sets, {'vendor-class-identifier': 'Some Vendor Identifier'}) isc_dhcp_leases-0.9.0/isc_dhcp_leases/test_lease.py0000644000175000017500000000456713171611777023623 0ustar martijnmartijn00000000000000from unittest import TestCase from isc_dhcp_leases.iscdhcpleases import Lease from freezegun import freeze_time from datetime import datetime __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"' } @freeze_time("2015-07-6 8:15:0") 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(2013, 12, 10, 12, 57, 4)) 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), '') @freeze_time("2015-07-6 8:15:0") def test_valid(self): lease = Lease("192.168.0.1", self.lease_data) self.assertTrue(lease.valid) # Lease is forever lease.end = datetime(2015, 7, 6, 13, 57, 4) self.assertTrue(lease.valid) # Lease is within start and end lease.end = datetime(2015, 7, 6, 6, 57, 4) self.assertFalse(lease.valid) # Lease is ended lease.start = datetime(2015, 7, 6, 12, 57, 4) lease.end = datetime(2015, 7, 6, 13, 57, 4) self.assertFalse(lease.valid) # Lease is in the future 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)isc_dhcp_leases-0.9.0/isc_dhcp_leases/iscdhcpleases.py0000644000175000017500000002604113171641345024266 0ustar martijnmartijn00000000000000import binascii import codecs import datetime import re import struct import gzip from six import iteritems def parse_time(s): """ Like datetime.datetime.strptime(s, "%w %Y/%m/%d %H:%M:%S") but 5x faster. """ _, date_part, time_part = s.split(' ') year, mon, day = date_part.split('/') hour, minute, sec = time_part.split(':') return datetime.datetime(*map(int, (year, mon, day, hour, minute, sec))) 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:] == ';': 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): self.filename = filename self.gzip = gzip 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) 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) 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): 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) @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' 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, options=None, sets=None): super(Lease, self).__init__(ip, properties=properties, options=options, sets=sets) self.start = parse_time(properties['starts']) 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: return self.start <= datetime.datetime.utcnow() else: return self.start <= datetime.datetime.utcnow() <= 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, options=None, sets=None): options = options or {} sets = sets or {} super(Lease6, self).__init__(ip, properties=properties, options=options, sets=sets) 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()) isc_dhcp_leases-0.9.0/isc_dhcp_leases/test_lease6.py0000644000175000017500000000565113171611777023704 0ustar martijnmartijn00000000000000from unittest import TestCase from isc_dhcp_leases.iscdhcpleases import Lease6 from freezegun import freeze_time from datetime import datetime __author__ = 'Martijn Braam ' class TestLease6(TestCase): def setUp(self): self.lease_data = { 'binding': 'state active', 'ends': 'never', 'preferred-life': '375', 'max-life': '600' } @freeze_time("2015-07-6 8:15:0") def test_init(self): lease = Lease6("2001:610:600:891d::60", self.lease_data, datetime(2015, 8, 18, 16, 55, 37), "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, datetime(2015, 8, 18, 16, 55, 37)) self.assertEqual(lease.type, Lease6.NON_TEMPORARY) def test_repr(self): lease = Lease6("2001:610:600:891d::60", self.lease_data, datetime(2015, 8, 18, 16, 55, 37), "4dv\\352\\000\\001\\000\\001\\035f\\037\\342\\012\\000'\\000\\000\\000", "na") self.assertEqual(repr(lease), '') @freeze_time("2015-07-6 8:15:0") def test_valid(self): lease = Lease6("2001:610:600:891d::60", self.lease_data, datetime(2015, 8, 18, 16, 55, 37), "4dv\\352\\000\\001\\000\\001\\035f\\037\\342\\012\\000'\\000\\000\\000", "na") self.assertTrue(lease.valid) # Lease is forever lease.end = datetime(2015, 7, 6, 13, 57, 4) self.assertTrue(lease.valid) # Lease is before end lease.end = datetime(2015, 7, 6, 6, 57, 4) self.assertFalse(lease.valid) # Lease is ended def test_eq(self): lease_a = Lease6("2001:610:600:891d::60", self.lease_data, datetime(2015, 8, 18, 16, 55, 37), "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, datetime(2015, 8, 18, 16, 55, 37), "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)