diff --git a/app/models.py b/app/models.py
index d711617017f6d340cce83131ea406ee5d601308c..7e3732ca24408dfc1767714542f278e8082e20df 100644
--- a/app/models.py
+++ b/app/models.py
@@ -299,6 +299,38 @@ class Network(db.Model):
     def __str__(self):
         return str(self.prefix)
 
+    @property
+    def network_ip(self):
+        return ipaddress.ip_network(self.prefix)
+
+    @property
+    def first_ip(self):
+        return ipaddress.ip_address(self.first)
+
+    @property
+    def last_ip(self):
+        return ipaddress.ip_address(self.last)
+
+    def ip_range(self):
+        """Return the list of IP addresses that can be assigned for this network
+
+        The range is defined by the first and last IP
+        """
+        return [addr for addr in self.network_ip.hosts()
+                if self.first_ip <= addr <= self.last_ip]
+
+    def used_ips(self):
+        """Return the list of IP addresses in use
+
+        The list is sorted
+        """
+        return sorted(host.address for host in self.hosts)
+
+    def available_ips(self):
+        """Return the list of IP addresses available"""
+        return [addr for addr in self.ip_range()
+                if addr not in self.used_ips()]
+
     @staticmethod
     def ip_in_network(ip, prefix):
         """Ensure the IP is in the network
@@ -356,6 +388,10 @@ class Host(db.Model):
 
     mac = db.relationship('Mac', backref='host')
 
+    @property
+    def address(self):
+        return ipaddress.ip_address(self.ip)
+
     def __str__(self):
         return str(self.ip)
 
diff --git a/tests/functional/test_models.py b/tests/functional/test_models.py
new file mode 100644
index 0000000000000000000000000000000000000000..673c239bda95697de9e41656e16d48fe9bef2584
--- /dev/null
+++ b/tests/functional/test_models.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+"""
+tests.functional.test_models
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This module defines models tests.
+
+:copyright: (c) 2017 European Spallation Source ERIC
+:license: BSD 2-Clause, see LICENSE for more details.
+
+"""
+import ipaddress
+from app import models
+
+
+def test_network_ip_properties(session):
+    # Create some networks
+    network1 = models.Network(prefix='172.16.1.0/24', first='172.16.1.10', last='172.16.1.250')
+    network2 = models.Network(prefix='172.16.20.0/26', first='172.16.20.11', last='172.16.20.14')
+    for network in (network1, network2):
+        session.add(network)
+    session.commit()
+
+    assert network1.network_ip == ipaddress.ip_network('172.16.1.0/24')
+    assert network1.first_ip == ipaddress.ip_address('172.16.1.10')
+    assert network1.last_ip == ipaddress.ip_address('172.16.1.250')
+    assert len(network1.ip_range()) == 241
+    assert network1.ip_range() == [ipaddress.ip_address(f'172.16.1.{i}') for i in range(10, 251)]
+    assert network1.ip_range() == network1.available_ips()
+    assert network1.used_ips() == []
+
+    assert network2.network_ip == ipaddress.ip_network('172.16.20.0/26')
+    assert network2.first_ip == ipaddress.ip_address('172.16.20.11')
+    assert network2.last_ip == ipaddress.ip_address('172.16.20.14')
+    assert len(network2.ip_range()) == 4
+    assert network2.ip_range() == [ipaddress.ip_address(f'172.16.20.{i}') for i in range(11, 15)]
+    assert network2.ip_range() == network2.available_ips()
+    assert network2.used_ips() == []
+
+
+def test_network_available_and_used_ips(session):
+    # Create some networks and hosts
+    network1 = models.Network(prefix='172.16.1.0/24', first='172.16.1.10', last='172.16.1.250')
+    network2 = models.Network(prefix='172.16.20.0/26', first='172.16.20.11', last='172.16.20.14')
+    for network in (network1, network2):
+        session.add(network)
+    session.flush()
+    for i in range(10, 20):
+        session.add(models.Host(network_id=network1.id, ip=f'172.16.1.{i}'))
+    session.add(models.Host(network_id=network2.id, ip='172.16.20.13'))
+    session.commit()
+    # Check available and used IPs
+    assert network1.used_ips() == [ipaddress.ip_address(f'172.16.1.{i}') for i in range(10, 20)]
+    assert network1.available_ips() == [ipaddress.ip_address(f'172.16.1.{i}') for i in range(20, 251)]
+    assert network2.used_ips() == [ipaddress.ip_address('172.16.20.13')]
+    assert network2.available_ips() == [ipaddress.ip_address('172.16.20.11'),
+                                        ipaddress.ip_address('172.16.20.12'),
+                                        ipaddress.ip_address('172.16.20.14')]
+
+    # Add more hosts
+    session.add(models.Host(network_id=network2.id, ip='172.16.20.11'))
+    session.add(models.Host(network_id=network2.id, ip='172.16.20.14'))
+    session.commit()
+    assert len(network2.used_ips()) == 3
+    assert network2.used_ips() == [ipaddress.ip_address('172.16.20.11'),
+                                   ipaddress.ip_address('172.16.20.13'),
+                                   ipaddress.ip_address('172.16.20.14')]
+    assert network2.available_ips() == [ipaddress.ip_address('172.16.20.12')]
+
+    # Add last available IP
+    session.add(models.Host(network_id=network2.id, ip='172.16.20.12'))
+    session.commit()
+    assert network2.used_ips() == [ipaddress.ip_address(f'172.16.20.{i}') for i in range(11, 15)]
+    assert list(network2.available_ips()) == []