Skip to content
Snippets Groups Projects
Commit 782709a3 authored by Benjamin Bertrand's avatar Benjamin Bertrand
Browse files

Add validators for first and last IP in Network

The database constraints will catch the same error
(and raise an IntegrityError).

Having some validation at the model level allows to display nicer error
messages.
We use CSEntryError exception and not ValidationError so that it can be
used both by the API and views.
parent e1ba3816
No related branches found
No related tags found
No related merge requests found
......@@ -298,15 +298,40 @@ class Network(db.Model):
def __str__(self):
return str(self.prefix)
@staticmethod
def ip_in_network(ip, prefix):
"""Ensure the IP is in the network
:param str user_id: unicode ID of a user
:returns: a tuple with the IP and network as (IPv4Address, IPv4Network)
:raises: CSEntryError if the IP is not in the network
"""
addr = ipaddress.ip_address(ip)
net = ipaddress.ip_network(prefix)
if addr not in net:
raise utils.CSEntryError(f'IP address {ip} is not in network {prefix}', status_code=422)
return (addr, net)
@validates('first')
def validate_first(self, key, ip):
"""Ensure the first IP is in the network"""
self.ip_in_network(ip, self.prefix)
return ip
@validates('last')
def validate_last(self, key, ip):
"""Ensure the last IP is in the network"""
addr, net = self.ip_in_network(ip, self.prefix)
if addr < ipaddress.ip_address(self.first):
raise utils.CSEntryError(f'Last IP address {ip} is less than the first address {self.first}', status_code=422)
return ip
@validates('hosts')
def validate_hosts(self, key, host):
"""Ensure the host IP is in the network range"""
addr = ipaddress.ip_address(host.ip)
net = ipaddress.ip_network(self.prefix)
if addr not in net:
raise utils.CSEntryError(f'IP address shall be in network {self.prefix}', status_code=422)
addr, net = self.ip_in_network(host.ip, self.prefix)
if addr < ipaddress.ip_address(self.first) or addr > ipaddress.ip_address(self.last):
raise utils.CSEntryError(f'IP address shall be in range {self.first} - {self.last}', status_code=422)
raise utils.CSEntryError(f'IP address {host.ip} is not in range {self.first} - {self.last}', status_code=422)
return host
def to_dict(self):
......
......@@ -516,16 +516,16 @@ def test_create_network_constraint_fail(client, session, admin_token):
'first': '172.16.2.10',
'last': '172.16.1.250'}
response = post(client, '/api/networks', data=data, token=admin_token)
check_response_message(response, 'IntegrityError', 409)
check_response_message(response, 'IP address 172.16.2.10 is not in network 172.16.1.0/24', 422)
# last not in prefix
data = {'prefix': '172.16.1.0/24',
'first': '172.16.1.10',
'last': '172.16.5.250'}
response = post(client, '/api/networks', data=data, token=admin_token)
check_response_message(response, 'IntegrityError', 409)
check_response_message(response, 'IP address 172.16.5.250 is not in network 172.16.1.0/24', 422)
# first > last
data = {'prefix': '172.16.1.0/24',
'first': '172.16.1.10',
'last': '172.16.1.9'}
response = post(client, '/api/networks', data=data, token=admin_token)
check_response_message(response, 'IntegrityError', 409)
check_response_message(response, 'Last IP address 172.16.1.9 is less than the first address 172.16.1.10', 422)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment