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

Mac to host is a one-to-many relationship

- Several IPs (hosts) can be associated to one MAC address
- Host name shall be non nullable
parent 1bf57261
No related branches found
No related tags found
No related merge requests found
......@@ -273,4 +273,4 @@ def get_hosts():
@jwt_groups_accepted('admin', 'create')
def create_host():
"""Create a new host"""
return create_generic_model(Host, mandatory_fields=('network', 'ip'))
return create_generic_model(Host, mandatory_fields=('network', 'ip', 'name'))
......@@ -385,10 +385,10 @@ class Host(db.Model):
id = db.Column(db.Integer, primary_key=True)
network_id = db.Column(db.Integer, db.ForeignKey('network.id'), nullable=False)
ip = db.Column(postgresql.INET, nullable=False, unique=True)
name = db.Column(db.Text, unique=True)
name = db.Column(db.Text, nullable=False, unique=True)
description = db.Column(db.Text)
mac_id = db.Column(db.Integer, db.ForeignKey('mac.id'))
# This is a One To One relationship (set uselist to False)
mac = db.relationship('Mac', backref='host', uselist=False)
cnames = db.relationship('Cname', backref='host')
def __init__(self, **kwargs):
......@@ -414,13 +414,14 @@ class Host(db.Model):
return str(self.ip)
def __repr__(self):
return f'Host(id={self.id}, network_id={self.network_id}, ip={self.ip}, name={self.name}, mac={self.mac})'
return f'Host(id={self.id}, network_id={self.network_id}, ip={self.ip}, name={self.name}, description={self.description}, mac={self.mac})'
def to_dict(self, long=False):
d = {
'id': self.id,
'ip': self.ip,
'name': self.name,
'description': self.description,
'network_id': self.network_id,
}
if long:
......@@ -431,9 +432,10 @@ class Host(db.Model):
class Mac(db.Model):
id = db.Column(db.Integer, primary_key=True)
address = db.Column(postgresql.MACADDR, nullable=False, unique=True)
host_id = db.Column(db.Integer, db.ForeignKey('host.id'), unique=True)
item_id = db.Column(db.Integer, db.ForeignKey('item.id'), nullable=False)
hosts = db.relationship('Host', backref='mac')
def __str__(self):
return str(self.address)
......@@ -441,15 +443,11 @@ class Mac(db.Model):
d = {
'id': self.id,
'address': self.address,
'host_id': self.host_id,
'item_id': self.item_id,
}
if long:
d['item_ics_id'] = self.item.ics_id
try:
d['host'] = self.host.to_dict()
except AttributeError:
d['host'] = None
d['hosts'] = [host.to_dict() for host in self.hosts]
return d
......
......@@ -8,29 +8,34 @@
<dl class="row">
<dt class="col-sm-3">ICS id</dt>
<dd class="col-sm-9">{{ item['ics_id'] }}</dt>
<dd class="col-sm-9">{{ item['ics_id'] }}</dd>
<dt class="col-sm-3">Created</dt>
<dd class="col-sm-9">{{ item['created'] }}</dt>
<dd class="col-sm-9">{{ item['created'] }}</dd>
<dt class="col-sm-3">Updated</dt>
<dd class="col-sm-9">{{ item['updated'] }}</dt>
<dd class="col-sm-9">{{ item['updated'] }}</dd>
<dt class="col-sm-3">Serial number</dt>
<dd class="col-sm-9">{{ item['serial_number'] }}</dt>
<dd class="col-sm-9">{{ item['serial_number'] }}</dd>
<dt class="col-sm-3">Manufacturer</dt>
<dd class="col-sm-9">{{ item['manufacturer'] }}</dt>
<dd class="col-sm-9">{{ item['manufacturer'] }}</dd>
<dt class="col-sm-3">Model</dt>
<dd class="col-sm-9">{{ item['model'] }}</dt>
<dd class="col-sm-9">{{ item['model'] }}</dd>
<dt class="col-sm-3">Location</dt>
<dd class="col-sm-9">{{ item['location'] }}</dt>
<dd class="col-sm-9">{{ item['location'] }}</dd>
<dt class="col-sm-3">Status</dt>
<dd class="col-sm-9">{{ item['status'] }}</dt>
<dd class="col-sm-9">{{ item['status'] }}</dd>
{% for mac in item['macs'] %}
<dt class="col-sm-3">Address{{ loop.index }}</dt>
<dd class="col-sm-9">{{ mac.address }} / {{ mac.host.ip }} / {{ mac.host.name }}</dt>
{% set macloop = loop %}
<dt class="col-sm-3">MAC Address{{ loop.index }}</dt>
<dd class="col-sm-9">{{ mac.address }}</dd>
{% for host in mac.hosts %}
<dt class="col-sm-3">Host{{ macloop.index }}-{{ loop.index }}</dt>
<dd class="col-sm-9">IP: {{ host.ip }} / name: {{ host.name }}</dd>
{% endfor %}
{% endfor %}
<dt class="col-sm-3">Parent</dt>
<dd class="col-sm-9">{{ link_to_item(item['parent']) }}</dt>
<dd class="col-sm-9">{{ link_to_item(item['parent']) }}</dd>
<dt class="col-sm-3">Children</dt>
<dd class="col-sm-9">{{ link_to_items(item['children']) }}</dt>
<dd class="col-sm-9">{{ link_to_items(item['children']) }}</dd>
</dl>
<h4>History</h4>
......
......@@ -622,24 +622,27 @@ def test_create_host(client, network_factory, user_token):
check_response_message(response, "Missing mandatory field 'ip'", 422)
data = {'network': network.address,
'ip': '192.168.1.20'}
'ip': '192.168.1.20',
'name': 'hostname1'}
response = post(client, '/api/hosts', data=data, token=user_token)
assert response.status_code == 201
assert {'id', 'network_id', 'ip', 'name'} == set(response.json.keys())
assert {'id', 'network_id', 'ip', 'name', 'description'} == set(response.json.keys())
assert response.json['network_id'] == network.id
assert response.json['ip'] == '192.168.1.20'
assert response.json['name'] == 'hostname1'
# Check that IP shall be unique
# Check that IP and name shall be unique
response = post(client, '/api/hosts', data=data, token=user_token)
check_response_message(response, '(psycopg2.IntegrityError) duplicate key value violates unique constraint', 422)
# Check that all parameters can be passed
data2 = {'network': network.address,
'ip': '192.168.1.21',
'name': 'myhostname'}
'name': 'myhostname',
'description': 'my description'}
response = post(client, '/api/hosts', data=data2, token=user_token)
assert response.status_code == 201
assert response.json['name'] == 'myhostname'
assert response.json['description'] == 'my description'
# check all items that were created
assert models.Host.query.count() == 2
......@@ -650,7 +653,8 @@ def test_create_host_invalid_ip(ip, client, network_factory, user_token):
network = network_factory(address='192.168.1.0/24', first_ip='192.168.1.10', last_ip='192.168.1.250')
# invalid IP address
data = {'network': network.address,
'ip': ip}
'ip': ip,
'name': 'hostname'}
response = post(client, '/api/hosts', data=data, token=user_token)
check_response_message(response, f"'{ip}' does not appear to be an IPv4 or IPv6 address", 422)
......@@ -659,6 +663,7 @@ def test_create_host_ip_not_in_network(client, network_factory, user_token):
network = network_factory(address='192.168.1.0/24', first_ip='192.168.1.10', last_ip='192.168.1.250')
# IP address not in range
data = {'network': network.address,
'ip': '192.168.2.4'}
'ip': '192.168.2.4',
'name': 'hostname'}
response = post(client, '/api/hosts', data=data, token=user_token)
check_response_message(response, 'IP address 192.168.2.4 is not in network 192.168.1.0/24', 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