← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~rvb/maas/nodegroup-ip-range into lp:maas

 

Raphaël Badin has proposed merging lp:~rvb/maas/nodegroup-ip-range into lp:maas.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~rvb/maas/nodegroup-ip-range/+merge/115720

This branch adds a method to the nodegroup model to iterate over all the nodegroup's IP Addresses.  This will be used to generate the DNS configuration files for the whole nodegroup when it's added or its network info edited.
-- 
https://code.launchpad.net/~rvb/maas/nodegroup-ip-range/+merge/115720
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rvb/maas/nodegroup-ip-range into lp:maas.
=== modified file 'src/maasserver/models/nodegroup.py'
--- src/maasserver/models/nodegroup.py	2012-07-18 05:06:01 +0000
+++ src/maasserver/models/nodegroup.py	2012-07-19 12:47:04 +0000
@@ -23,6 +23,7 @@
     )
 from maasserver import DefaultMeta
 from maasserver.models.timestampedmodel import TimestampedModel
+from maasserver.utils.network import next_ip
 from piston.models import (
     KEY_SIZE,
     Token,
@@ -108,3 +109,11 @@
         editable=True, unique=True, blank=True, null=True, default='')
     ip_range_high = IPAddressField(
         editable=True, unique=True, blank=True, null=True, default='')
+
+    def iterhosts(self):
+        """Generate Iterator over usable hosts in the nodegroup's subnet."""
+        host_ip = self.ip_range_low
+        while host_ip != self.ip_range_high:
+            yield host_ip
+            host_ip = next_ip(host_ip)
+        yield host_ip

=== modified file 'src/maasserver/tests/test_nodegroup.py'
--- src/maasserver/tests/test_nodegroup.py	2012-07-19 03:01:45 +0000
+++ src/maasserver/tests/test_nodegroup.py	2012-07-19 12:47:04 +0000
@@ -104,3 +104,16 @@
         master.worker_ip = ip
         master.save()
         self.assertEqual(ip, NodeGroup.objects.ensure_master().worker_ip)
+
+
+class TestNodeGroup(TestCase):
+
+    def test_iterhosts_returns_hosts_in_network(self):
+        nodegroup = NodeGroup(
+            name=factory.make_name('nodegroup'),
+            ip_range_low='192.168.0.1',
+            ip_range_high='192.168.0.10',
+            )
+        self.assertItemsEqual(
+            ['192.168.0.%d' % i for i in range(1, 11)],
+            nodegroup.iterhosts())

=== added file 'src/maasserver/utils/network.py'
--- src/maasserver/utils/network.py	1970-01-01 00:00:00 +0000
+++ src/maasserver/utils/network.py	2012-07-19 12:47:04 +0000
@@ -0,0 +1,50 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Network utilities."""
+
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = [
+    'int_to_dotted_quad',
+    'dotted_quad_to_int',
+    'next_ip',
+    ]
+
+import socket
+import struct
+
+
+def int_to_dotted_quad(n):
+    """Convert int to dotted quad string.
+
+    >>> int_to_dotted_quad(3232235521)
+    '192.168.0.1'
+    """
+    return socket.inet_ntoa(struct.pack(str('>L'), n))
+
+
+def dotted_quad_to_int(ip):
+    """Convert decimal dotted quad string to integer.
+
+    >>> dotted_quad_to_int('192.168.0.1')
+    3232235521L
+    """
+    return struct.unpack(str('>L'), socket.inet_aton(ip))[0]
+
+
+def next_ip(ip):
+    """Return the next IP Address.
+
+    IP Addresses being essentially 32-bit integers, this returns
+    the next IP Address conresponding to the next 32-bit integer.
+
+    >>> next_ip('192.168.0.1')
+    '192.168.0.2'
+    """
+    return int_to_dotted_quad(dotted_quad_to_int(ip) + 1)

=== added file 'src/maasserver/utils/tests/test_network.py'
--- src/maasserver/utils/tests/test_network.py	1970-01-01 00:00:00 +0000
+++ src/maasserver/utils/tests/test_network.py	2012-07-19 12:47:04 +0000
@@ -0,0 +1,69 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests for miscellaneous helpers."""
+
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = []
+
+from operator import itemgetter
+import socket
+import struct
+
+from maasserver.utils.network import (
+    dotted_quad_to_int,
+    int_to_dotted_quad,
+    next_ip,
+    )
+from maastesting.testcase import TestCase
+
+
+dottedquad_int = [
+    ('255.255.255.255', 4294967295),
+    ('192.168.0.1',     3232235521),
+    ('0.0.0.12',        12),
+    ('0.0.0.0',         0),
+]
+
+
+class TestUtilities(TestCase):
+
+    def test_dotted_quad_to_int(self):
+        inputs = map(itemgetter(0), dottedquad_int)
+        expected = map(itemgetter(1), dottedquad_int)
+        self.assertEqual(
+            expected, map(dotted_quad_to_int, inputs))
+
+    def test_dotted_quad_to_int_raises_exception_if_invalid_input(self):
+        self.assertRaises(
+            socket.error, dotted_quad_to_int, '1.1.1.345')
+
+    def test_int_to_dotted_quad(self):
+        inputs = map(itemgetter(1), dottedquad_int)
+        expected = map(itemgetter(0), dottedquad_int)
+        self.assertEqual(
+            expected, map(int_to_dotted_quad, inputs))
+
+    def test_int_to_dotted_quad_raises_exception_if_invalid_input(self):
+        self.assertRaises(
+            struct.error, int_to_dotted_quad, 4294967300)
+
+    def test_next_ip_returns_next_ip(self):
+        ip_nextip = [
+            ('192.168.0.255', '192.168.1.0'),
+            ('192.168.0.1',   '192.168.0.2'),
+            ('0.0.0.0',       '0.0.0.1'),
+        ]
+        inputs = map(itemgetter(0), ip_nextip)
+        expected = map(itemgetter(1), ip_nextip)
+        self.assertEqual(
+            expected, map(next_ip, inputs))
+
+    def test_next_ip_raises_exception_at_end_of_spectrum(self):
+        self.assertRaises(struct.error, next_ip, '255.255.255.255')