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

Add domain table

- add domain_id on NetworkScope table to define a default domain
- add domain_id on Network (default to the Network Scope one)

Fixes INFRA-194
parent 5e11d8f4
No related branches found
No related tags found
No related merge requests found
Showing
with 242 additions and 17 deletions
...@@ -41,9 +41,10 @@ def create_scope(): ...@@ -41,9 +41,10 @@ def create_scope():
:jsonparam first_vlan: network scope first vlan :jsonparam first_vlan: network scope first vlan
:jsonparam last_vlan: network scope last vlan :jsonparam last_vlan: network scope last vlan
:jsonparam supernet: network scope supernet :jsonparam supernet: network scope supernet
:jsonparam domain_id: primary key of the default domain
""" """
return create_generic_model(models.NetworkScope, mandatory_fields=( return create_generic_model(models.NetworkScope, mandatory_fields=(
'name', 'first_vlan', 'last_vlan', 'supernet')) 'name', 'first_vlan', 'last_vlan', 'supernet', 'domain_id'))
@bp.route('/networks') @bp.route('/networks')
......
...@@ -101,6 +101,7 @@ def create_app(config=None): ...@@ -101,6 +101,7 @@ def create_app(config=None):
admin.add_view(AdminModelView(models.Status, db.session)) admin.add_view(AdminModelView(models.Status, db.session))
admin.add_view(ItemAdmin(models.Item, db.session)) admin.add_view(ItemAdmin(models.Item, db.session))
admin.add_view(AdminModelView(models.ItemComment, db.session)) admin.add_view(AdminModelView(models.ItemComment, db.session))
admin.add_view(AdminModelView(models.Domain, db.session))
admin.add_view(AdminModelView(models.NetworkScope, db.session)) admin.add_view(AdminModelView(models.NetworkScope, db.session))
admin.add_view(NetworkAdmin(models.Network, db.session, endpoint='networks')) admin.add_view(NetworkAdmin(models.Network, db.session, endpoint='networks'))
admin.add_view(AdminModelView(models.Host, db.session)) admin.add_view(AdminModelView(models.Host, db.session))
......
...@@ -375,6 +375,7 @@ class Network(CreatedMixin, db.Model): ...@@ -375,6 +375,7 @@ class Network(CreatedMixin, db.Model):
description = db.Column(db.Text) description = db.Column(db.Text)
admin_only = db.Column(db.Boolean, nullable=False, default=False) admin_only = db.Column(db.Boolean, nullable=False, default=False)
scope_id = db.Column(db.Integer, db.ForeignKey('network_scope.id'), nullable=False) scope_id = db.Column(db.Integer, db.ForeignKey('network_scope.id'), nullable=False)
domain_id = db.Column(db.Integer, db.ForeignKey('domain.id'), nullable=False)
interfaces = db.relationship('Interface', backref='network') interfaces = db.relationship('Interface', backref='network')
...@@ -389,6 +390,9 @@ class Network(CreatedMixin, db.Model): ...@@ -389,6 +390,9 @@ class Network(CreatedMixin, db.Model):
# as a string # as a string
if 'scope' in kwargs: if 'scope' in kwargs:
kwargs['scope'] = utils.convert_to_model(kwargs['scope'], NetworkScope, 'name') kwargs['scope'] = utils.convert_to_model(kwargs['scope'], NetworkScope, 'name')
# If domain_id is not passed, we set it to the network scope value
if 'domain_id' not in kwargs:
kwargs['domain_id'] = kwargs['scope'].domain_id
super().__init__(**kwargs) super().__init__(**kwargs)
def __str__(self): def __str__(self):
...@@ -489,6 +493,7 @@ class Network(CreatedMixin, db.Model): ...@@ -489,6 +493,7 @@ class Network(CreatedMixin, db.Model):
'description': self.description, 'description': self.description,
'admin_only': self.admin_only, 'admin_only': self.admin_only,
'scope': utils.format_field(self.scope), 'scope': utils.format_field(self.scope),
'domain': str(self.domain),
'interfaces': [str(interface) for interface in self.interfaces], 'interfaces': [str(interface) for interface in self.interfaces],
}) })
return d return d
...@@ -649,12 +654,32 @@ class Cname(CreatedMixin, db.Model): ...@@ -649,12 +654,32 @@ class Cname(CreatedMixin, db.Model):
return d return d
class Domain(CreatedMixin, db.Model):
name = db.Column(db.Text, nullable=False, unique=True)
scopes = db.relationship('NetworkScope', backref='domain')
networks = db.relationship('Network', backref='domain')
def __str__(self):
return str(self.name)
def to_dict(self):
d = super().to_dict()
d.update({
'name': self.name,
'scopes': [str(scope) for scope in self.scopes],
'networks': [str(network) for network in self.networks],
})
return d
class NetworkScope(CreatedMixin, db.Model): class NetworkScope(CreatedMixin, db.Model):
__tablename__ = 'network_scope' __tablename__ = 'network_scope'
name = db.Column(CIText, nullable=False, unique=True) name = db.Column(CIText, nullable=False, unique=True)
first_vlan = db.Column(db.Integer, nullable=False, unique=True) first_vlan = db.Column(db.Integer, nullable=False, unique=True)
last_vlan = db.Column(db.Integer, nullable=False, unique=True) last_vlan = db.Column(db.Integer, nullable=False, unique=True)
supernet = db.Column(postgresql.CIDR, nullable=False, unique=True) supernet = db.Column(postgresql.CIDR, nullable=False, unique=True)
domain_id = db.Column(db.Integer, db.ForeignKey('domain.id'), nullable=False)
description = db.Column(db.Text) description = db.Column(db.Text)
networks = db.relationship('Network', backref='scope') networks = db.relationship('Network', backref='scope')
...@@ -713,6 +738,7 @@ class NetworkScope(CreatedMixin, db.Model): ...@@ -713,6 +738,7 @@ class NetworkScope(CreatedMixin, db.Model):
'last_vlan': self.last_vlan, 'last_vlan': self.last_vlan,
'supernet': self.supernet, 'supernet': self.supernet,
'description': self.description, 'description': self.description,
'domain': str(self.domain),
'networks': [str(network) for network in self.networks], 'networks': [str(network) for network in self.networks],
}) })
return d return d
......
...@@ -41,6 +41,12 @@ class NoValidateSelectField(SelectField): ...@@ -41,6 +41,12 @@ class NoValidateSelectField(SelectField):
pass pass
class DomainForm(CSEntryForm):
name = StringField('Name',
validators=[validators.InputRequired(),
Unique(models.Domain, column='name')])
class NetworkScopeForm(CSEntryForm): class NetworkScopeForm(CSEntryForm):
name = StringField('Name', name = StringField('Name',
description='name must be 3-25 characters long and contain only letters, numbers and dash', description='name must be 3-25 characters long and contain only letters, numbers and dash',
...@@ -53,6 +59,11 @@ class NetworkScopeForm(CSEntryForm): ...@@ -53,6 +59,11 @@ class NetworkScopeForm(CSEntryForm):
supernet = StringField('Supernet', supernet = StringField('Supernet',
validators=[validators.InputRequired(), validators=[validators.InputRequired(),
IPNetwork()]) IPNetwork()])
domain_id = SelectField('Default domain')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.domain_id.choices = utils.get_model_choices(models.Domain, attr='name')
class NetworkForm(CSEntryForm): class NetworkForm(CSEntryForm):
...@@ -68,11 +79,13 @@ class NetworkForm(CSEntryForm): ...@@ -68,11 +79,13 @@ class NetworkForm(CSEntryForm):
address = NoValidateSelectField('Address', choices=[]) address = NoValidateSelectField('Address', choices=[])
first_ip = NoValidateSelectField('First IP', choices=[]) first_ip = NoValidateSelectField('First IP', choices=[])
last_ip = NoValidateSelectField('Last IP', choices=[]) last_ip = NoValidateSelectField('Last IP', choices=[])
domain_id = SelectField('Domain')
admin_only = BooleanField('Admin only') admin_only = BooleanField('Admin only')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.scope_id.choices = utils.get_model_choices(models.NetworkScope, attr='name') self.scope_id.choices = utils.get_model_choices(models.NetworkScope, attr='name')
self.domain_id.choices = utils.get_model_choices(models.Domain, attr='name')
class HostForm(CSEntryForm): class HostForm(CSEntryForm):
......
...@@ -15,7 +15,7 @@ from flask import (Blueprint, render_template, jsonify, session, ...@@ -15,7 +15,7 @@ from flask import (Blueprint, render_template, jsonify, session,
redirect, url_for, request, flash, current_app) redirect, url_for, request, flash, current_app)
from flask_login import login_required from flask_login import login_required
from .forms import (HostForm, InterfaceForm, HostInterfaceForm, NetworkForm, from .forms import (HostForm, InterfaceForm, HostInterfaceForm, NetworkForm,
NetworkScopeForm) NetworkScopeForm, DomainForm)
from ..extensions import db from ..extensions import db
from ..decorators import login_groups_accepted from ..decorators import login_groups_accepted
from .. import models, utils, helpers from .. import models, utils, helpers
...@@ -214,6 +214,32 @@ def delete_interface(): ...@@ -214,6 +214,32 @@ def delete_interface():
return redirect(url_for('network.view_host', name=hostname)) return redirect(url_for('network.view_host', name=hostname))
@bp.route('/domains')
@login_required
def list_domains():
return render_template('network/domains.html')
@bp.route('/domains/create', methods=('GET', 'POST'))
@login_groups_accepted('admin')
def create_domain():
form = DomainForm()
if form.validate_on_submit():
domain = models.Domain(name=form.name.data)
current_app.logger.debug(f'Trying to create: {domain!r}')
db.session.add(domain)
try:
db.session.commit()
except sa.exc.IntegrityError as e:
db.session.rollback()
current_app.logger.warning(f'{e}')
flash(f'{e}', 'error')
else:
flash(f'Domain {domain} created!', 'success')
return redirect(url_for('network.create_domain'))
return render_template('network/create_domain.html', form=form)
@bp.route('/scopes') @bp.route('/scopes')
@login_required @login_required
def list_scopes(): def list_scopes():
...@@ -229,7 +255,8 @@ def create_scope(): ...@@ -229,7 +255,8 @@ def create_scope():
description=form.description.data or None, description=form.description.data or None,
first_vlan=form.first_vlan.data, first_vlan=form.first_vlan.data,
last_vlan=form.last_vlan.data, last_vlan=form.last_vlan.data,
supernet=form.supernet.data) supernet=form.supernet.data,
domain_id=form.domain_id.data)
current_app.logger.debug(f'Trying to create: {scope!r}') current_app.logger.debug(f'Trying to create: {scope!r}')
db.session.add(scope) db.session.add(scope)
try: try:
...@@ -287,6 +314,7 @@ def retrieve_networks(): ...@@ -287,6 +314,7 @@ def retrieve_networks():
network.address, network.address,
network.first_ip, network.first_ip,
network.last_ip, network.last_ip,
str(network.domain),
network.admin_only) network.admin_only)
for network in models.Network.query.all()] for network in models.Network.query.all()]
return jsonify(data=data) return jsonify(data=data)
...@@ -313,6 +341,7 @@ def create_network(): ...@@ -313,6 +341,7 @@ def create_network():
address=form.address.data, address=form.address.data,
first_ip=form.first_ip.data, first_ip=form.first_ip.data,
last_ip=form.last_ip.data, last_ip=form.last_ip.data,
domain_id=form.domain_id.data,
admin_only=form.admin_only.data) admin_only=form.admin_only.data)
current_app.logger.debug(f'Trying to create: {network!r}') current_app.logger.debug(f'Trying to create: {network!r}')
db.session.add(network) db.session.add(network)
...@@ -330,15 +359,16 @@ def create_network(): ...@@ -330,15 +359,16 @@ def create_network():
return render_template('network/create_network.html', form=form) return render_template('network/create_network.html', form=form)
@bp.route('/_retrieve_vlan_and_prefix/<int:scope_id>') @bp.route('/_retrieve_scope_defaults/<int:scope_id>')
@login_required @login_required
def retrieve_vlan_and_prefix(scope_id): def retrieve_scope_defaults(scope_id):
try: try:
scope = models.NetworkScope.query.get(scope_id) scope = models.NetworkScope.query.get(scope_id)
except sa.exc.DataError: except sa.exc.DataError:
current_app.logger.warning(f'Invalid scope_id: {scope_id}') current_app.logger.warning(f'Invalid scope_id: {scope_id}')
data = {'vlans': [], 'prefixes': [], data = {'vlans': [], 'prefixes': [],
'selected_vlan': '', 'selected_prefix': ''} 'selected_vlan': '', 'selected_prefix': '',
'domain_id': ''}
else: else:
vlans = [vlan_id for vlan_id in scope.available_vlans()] vlans = [vlan_id for vlan_id in scope.available_vlans()]
prefixes = scope.prefix_range() prefixes = scope.prefix_range()
...@@ -350,7 +380,8 @@ def retrieve_vlan_and_prefix(scope_id): ...@@ -350,7 +380,8 @@ def retrieve_vlan_and_prefix(scope_id):
data = {'vlans': vlans, data = {'vlans': vlans,
'prefixes': prefixes, 'prefixes': prefixes,
'selected_vlan': vlans[0], 'selected_vlan': vlans[0],
'selected_prefix': selected_prefix} 'selected_prefix': selected_prefix,
'domain_id': scope.domain_id}
return jsonify(data=data) return jsonify(data=data)
...@@ -398,6 +429,15 @@ def retrieve_scopes(): ...@@ -398,6 +429,15 @@ def retrieve_scopes():
scope.description, scope.description,
scope.first_vlan, scope.first_vlan,
scope.last_vlan, scope.last_vlan,
scope.supernet) scope.supernet,
str(scope.domain))
for scope in models.NetworkScope.query.all()] for scope in models.NetworkScope.query.all()]
return jsonify(data=data) return jsonify(data=data)
@bp.route('/_retrieve_domains')
@login_required
def retrieve_domains():
data = [(domain.name,)
for domain in models.Domain.query.all()]
return jsonify(data=data)
$(document).ready(function() {
var domains_table = $("#domains_table").DataTable({
"ajax": function(data, callback, settings) {
$.getJSON(
$SCRIPT_ROOT + "/network/_retrieve_domains",
function(json) {
callback(json);
});
},
"paging": false
});
});
...@@ -9,15 +9,16 @@ $(document).ready(function() { ...@@ -9,15 +9,16 @@ $(document).ready(function() {
$field.val(selected_value); $field.val(selected_value);
} }
function update_vlan_and_prefix() { function update_scope_defaults() {
// Retrieve available vlans and subnet prefixes for the selected network scope // Retrieve available vlans, subnet prefixes and default domain
// and update the vlan_id and prefix select field // for the selected network scope and update the linked select fields
var scope_id = $("#scope_id").val(); var scope_id = $("#scope_id").val();
$.getJSON( $.getJSON(
$SCRIPT_ROOT + "/network/_retrieve_vlan_and_prefix/" + scope_id, $SCRIPT_ROOT + "/network/_retrieve_scope_defaults/" + scope_id,
function(json) { function(json) {
update_selectfield("#vlan_id", json.data.vlans, json.data.selected_vlan); update_selectfield("#vlan_id", json.data.vlans, json.data.selected_vlan);
update_selectfield("#prefix", json.data.prefixes, json.data.selected_prefix); update_selectfield("#prefix", json.data.prefixes, json.data.selected_prefix);
$('#domain_id').val(json.data.domain_id);
update_address(); update_address();
} }
); );
...@@ -50,14 +51,14 @@ $(document).ready(function() { ...@@ -50,14 +51,14 @@ $(document).ready(function() {
); );
} }
// Populate vlan_id and prefix select field on first page load // Populate the default values linked to the scope on first page load
if( $("#scope_id").length ) { if( $("#scope_id").length ) {
update_vlan_and_prefix(); update_scope_defaults();
} }
// Update vlan_id and prefix select field when changing network scope // Update the default values linked to the scope when changing it
$("#scope_id").on('change', function() { $("#scope_id").on('change', function() {
update_vlan_and_prefix(); update_scope_defaults();
}); });
// Update address select field when changing prefix // Update address select field when changing prefix
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
href="{{ url_for('network.list_networks') }}">Networks</a> href="{{ url_for('network.list_networks') }}">Networks</a>
<a class="list-group-item list-group-item-action {{ is_active(path.startswith("/network/scopes")) }}" <a class="list-group-item list-group-item-action {{ is_active(path.startswith("/network/scopes")) }}"
href="{{ url_for('network.list_scopes') }}">Network Scopes</a> href="{{ url_for('network.list_scopes') }}">Network Scopes</a>
<a class="list-group-item list-group-item-action {{ is_active(path.startswith("/network/domains")) }}"
href="{{ url_for('network.list_domains') }}">Domains</a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
......
{% extends "network/domains.html" %}
{% from "_helpers.html" import render_field %}
{% block title %}Register Domain - CSEntry{% endblock %}
{% block domains_main %}
<form id="DomainForm" method="POST">
{{ form.hidden_tag() }}
{{ render_field(form.name) }}
<div class="form-group row">
<div class="col-sm-10">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
{%- endblock %}
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
{{ render_field(form.address) }} {{ render_field(form.address) }}
{{ render_field(form.first_ip) }} {{ render_field(form.first_ip) }}
{{ render_field(form.last_ip) }} {{ render_field(form.last_ip) }}
{{ render_field(form.domain_id) }}
{{ render_field(form.admin_only) }} {{ render_field(form.admin_only) }}
<div class="form-group row"> <div class="form-group row">
<div class="col-sm-10"> <div class="col-sm-10">
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
{{ render_field(form.first_vlan) }} {{ render_field(form.first_vlan) }}
{{ render_field(form.last_vlan) }} {{ render_field(form.last_vlan) }}
{{ render_field(form.supernet) }} {{ render_field(form.supernet) }}
{{ render_field(form.domain_id) }}
<div class="form-group row"> <div class="form-group row">
<div class="col-sm-10"> <div class="col-sm-10">
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary">Submit</button>
......
{% extends "base-fluid.html" %}
{% from "_helpers.html" import is_active %}
{% block title %}Domains - CSEntry{% endblock %}
{% block main %}
{% set path = request.path %}
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link {{ is_active(path.endswith("/network/domains")) }}" href="{{ url_for('network.list_domains') }}">List domains</a>
</li>
<li class="nav-item">
<a class="nav-link {{ is_active(path.startswith("/network/domains/create")) }}" href="{{ url_for('network.create_domain') }}">Register new domain</a>
</li>
{% block domains_nav %}{% endblock %}
</ul>
<br>
{% block domains_main %}
<table id="domains_table" class="table table-bordered table-hover table-sm" cellspacing="0" width="100%">
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
</table>
{%- endblock %}
{%- endblock %}
{% block csentry_scripts %}
<script src="{{ url_for('static', filename='js/domains.js') }}"></script>
{% endblock %}
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
<th>Address</th> <th>Address</th>
<th>First IP</th> <th>First IP</th>
<th>Last IP</th> <th>Last IP</th>
<th>Domain</th>
<th>Admin only</th> <th>Admin only</th>
</tr> </tr>
</thead> </thead>
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
<th>First vlan</th> <th>First vlan</th>
<th>Last vlan</th> <th>Last vlan</th>
<th>Supernet</th> <th>Supernet</th>
<th>Default domain</th>
</tr> </tr>
</thead> </thead>
</table> </table>
......
"""Add domain table
Revision ID: dfd4eae61224
Revises: 713ca10255ab
Create Date: 2018-02-09 09:32:32.221007
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'dfd4eae61224'
down_revision = '713ca10255ab'
branch_labels = None
depends_on = None
def upgrade():
domain = op.create_table(
'domain',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('name', sa.Text(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['user_account.id'], name=op.f('fk_domain_user_id_user_account')),
sa.PrimaryKeyConstraint('id', name=op.f('pk_domain')),
sa.UniqueConstraint('name', name=op.f('uq_domain_name'))
)
# WARNING! If the database is not emppty, we can't set the domain_id to nullable=False before adding a value!
op.add_column('network', sa.Column('domain_id', sa.Integer(), nullable=True))
op.create_foreign_key(op.f('fk_network_domain_id_domain'), 'network', 'domain', ['domain_id'], ['id'])
op.add_column('network_scope', sa.Column('domain_id', sa.Integer(), nullable=True))
op.create_foreign_key(op.f('fk_network_scope_domain_id_domain'), 'network_scope', 'domain', ['domain_id'], ['id'])
# Try to get a user_id (required to create a domain)
conn = op.get_bind()
res = conn.execute('SELECT id FROM user_account LIMIT 1')
results = res.fetchall()
# If no user was found, then the database is empty - no need to add a default value
if results:
user_id = results[0][0]
# Create a default domain
op.execute(domain.insert().values(id=1, user_id=user_id, name='example.org',
created_at=sa.func.now(), updated_at=sa.func.now()))
# Add default domain_id value to network_scope and network
network_scope = sa.sql.table('network_scope', sa.sql.column('domain_id'))
op.execute(network_scope.update().values(domain_id=1))
network = sa.sql.table('network', sa.sql.column('domain_id'))
op.execute(network.update().values(domain_id=1))
# Add the nullable=False constraint
op.alter_column('network', 'domain_id', nullable=False)
op.alter_column('network_scope', 'domain_id', nullable=False)
def downgrade():
op.drop_constraint(op.f('fk_network_scope_domain_id_domain'), 'network_scope', type_='foreignkey')
op.drop_column('network_scope', 'domain_id')
op.drop_constraint(op.f('fk_network_domain_id_domain'), 'network', type_='foreignkey')
op.drop_column('network', 'domain_id')
op.drop_table('domain')
...@@ -29,6 +29,7 @@ register(factories.NetworkFactory) ...@@ -29,6 +29,7 @@ register(factories.NetworkFactory)
register(factories.InterfaceFactory) register(factories.InterfaceFactory)
register(factories.HostFactory) register(factories.HostFactory)
register(factories.MacFactory) register(factories.MacFactory)
register(factories.DomainFactory)
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
......
...@@ -89,6 +89,16 @@ class ItemFactory(factory.alchemy.SQLAlchemyModelFactory): ...@@ -89,6 +89,16 @@ class ItemFactory(factory.alchemy.SQLAlchemyModelFactory):
user = factory.SubFactory(UserFactory) user = factory.SubFactory(UserFactory)
class DomainFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
model = models.Domain
sqlalchemy_session = common.Session
sqlalchemy_session_persistence = 'commit'
user = factory.SubFactory(UserFactory)
name = factory.Sequence(lambda n: f'domain{n}.example.org')
class NetworkScopeFactory(factory.alchemy.SQLAlchemyModelFactory): class NetworkScopeFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta: class Meta:
model = models.NetworkScope model = models.NetworkScope
...@@ -100,6 +110,7 @@ class NetworkScopeFactory(factory.alchemy.SQLAlchemyModelFactory): ...@@ -100,6 +110,7 @@ class NetworkScopeFactory(factory.alchemy.SQLAlchemyModelFactory):
last_vlan = factory.Sequence(lambda n: 1609 + 10 * n) last_vlan = factory.Sequence(lambda n: 1609 + 10 * n)
supernet = factory.Faker('ipv4', network=True) supernet = factory.Faker('ipv4', network=True)
user = factory.SubFactory(UserFactory) user = factory.SubFactory(UserFactory)
domain = factory.SubFactory(DomainFactory)
class NetworkFactory(factory.alchemy.SQLAlchemyModelFactory): class NetworkFactory(factory.alchemy.SQLAlchemyModelFactory):
...@@ -113,6 +124,7 @@ class NetworkFactory(factory.alchemy.SQLAlchemyModelFactory): ...@@ -113,6 +124,7 @@ class NetworkFactory(factory.alchemy.SQLAlchemyModelFactory):
address = factory.Faker('ipv4', network=True) address = factory.Faker('ipv4', network=True)
scope = factory.SubFactory(NetworkScopeFactory) scope = factory.SubFactory(NetworkScopeFactory)
user = factory.SubFactory(UserFactory) user = factory.SubFactory(UserFactory)
domain = factory.SubFactory(DomainFactory)
@factory.lazy_attribute @factory.lazy_attribute
def first_ip(self): def first_ip(self):
......
...@@ -465,7 +465,7 @@ def test_create_network(client, admin_token, network_scope_factory): ...@@ -465,7 +465,7 @@ def test_create_network(client, admin_token, network_scope_factory):
assert response.status_code == 201 assert response.status_code == 201
assert {'id', 'vlan_name', 'vlan_id', 'address', 'first_ip', assert {'id', 'vlan_name', 'vlan_id', 'address', 'first_ip',
'last_ip', 'description', 'admin_only', 'scope', 'last_ip', 'description', 'admin_only', 'scope',
'interfaces', 'created_at', 'updated_at', 'domain', 'interfaces', 'created_at', 'updated_at',
'user'} == set(response.json.keys()) 'user'} == set(response.json.keys())
assert response.json['vlan_name'] == 'network1' assert response.json['vlan_name'] == 'network1'
assert response.json['vlan_id'] == 1600 assert response.json['vlan_id'] == 1600
......
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