mirror of
https://github.com/TakeusV1/flask_ipam.git
synced 2026-10-13 17:15:33 +02:00
V1.0
This commit is contained in:
1 parent
a8d9727527
commit
602bb7d706
8 files changed
+174
-21
No files matched your search
+5
-4
@@ -1,5 +1,5 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, PasswordField, IntegerField
|
||||
from wtforms import StringField, PasswordField, IntegerField, SelectField
|
||||
from wtforms.validators import DataRequired, Length, IPAddress, NumberRange
|
||||
|
||||
class LoginForm(FlaskForm):
|
||||
@@ -9,9 +9,10 @@ class LoginForm(FlaskForm):
|
||||
class NewPrefixForm(FlaskForm):
|
||||
subnet = StringField(validators=[DataRequired(),IPAddress(ipv4=True),Length(max=128)])
|
||||
prefix = IntegerField(validators=[DataRequired(),NumberRange(min=0,max=32)])
|
||||
description = StringField(validators=[DataRequired(),Length(max=128)])
|
||||
description = StringField(validators=[Length(max=128)])
|
||||
|
||||
class NewAllocationForm(FlaskForm):
|
||||
ipv4 = StringField(validators=[DataRequired(),IPAddress(ipv4=True),Length(max=128)])
|
||||
hostname = StringField(validators=[DataRequired(),Length(max=128)])
|
||||
description = StringField(validators=[DataRequired(),Length(max=128)])
|
||||
host_name = StringField(validators=[DataRequired(),Length(max=128)])
|
||||
host_desc = StringField(validators=[Length(max=128)])
|
||||
#host_type = SelectField(choices=[(0,'None'),(1,'Computer'),(2,'Server'),(3,'Virtual Machine'),(4,'Network Device'),(5,'Other')],validators=[DataRequired()])
|
||||
+19
-2
@@ -18,6 +18,23 @@ class Network(db.Model):
|
||||
class Allocation(db.Model):
|
||||
ipv4 = db.Column(db.String(16),primary_key=True,unique=True)
|
||||
net_id = db.Column(db.Integer(),db.ForeignKey('network.id'))
|
||||
hardware_id = db.Column(db.Integer())
|
||||
hostname = db.Column(db.String(32),unique=True)
|
||||
description = db.Column(db.String(64))
|
||||
description = db.Column(db.String(64))
|
||||
device_id = db.Column(db.Integer(),db.ForeignKey('inventory.id'))
|
||||
|
||||
class Inventory(db.Model):
|
||||
id = db.Column(db.Integer(),primary_key=True,unique=True)
|
||||
ip_addr = db.Column(db.Text())
|
||||
# Host Information
|
||||
host_type = db.Column(db.Integer())
|
||||
host_name = db.Column(db.String(32))
|
||||
host_desc = db.Column(db.String(64))
|
||||
# Hardware Information
|
||||
host_model = db.Column(db.String(32))
|
||||
host_manufacturer = db.Column(db.String(32))
|
||||
host_serialnumber = db.Column(db.String(32))
|
||||
# Network Information
|
||||
host_os = db.Column(db.String(32))
|
||||
host_location = db.Column(db.String(32))
|
||||
host_owner = db.Column(db.String(32))
|
||||
host_contact = db.Column(db.String(32))
|
||||
+76
-8
@@ -47,24 +47,92 @@ def networks():
|
||||
|
||||
return render_template('panel/networks.html', title="Prefixes", is_networks=True, app_version=app_version, prefixes=networks, form=form)
|
||||
|
||||
@base.route('/network/allocations/<int:net_id>',methods=['GET','POST'])
|
||||
@base.route('/network/<int:net_id>/allocations',methods=['GET','POST'])
|
||||
@login_required
|
||||
def allocations(net_id):
|
||||
|
||||
db_allocations = Allocation.query.filter_by(net_id=net_id).all()
|
||||
db_network = Network.query.filter_by(id=net_id).first()
|
||||
|
||||
db_network = Network.query.filter_by(id=net_id).first()
|
||||
|
||||
form = NewAllocationForm()
|
||||
if request.method == 'POST' and form.validate_on_submit():
|
||||
network = IPv4Network(f"{str(db_network.network_address)}/{str(db_network.prefix)}")
|
||||
# Check if IPv4 Address is already allocated
|
||||
if Allocation.query.filter_by(ipv4=str(form.ipv4.data)).first() != None:
|
||||
flash("IPv4 Address Already Allocated", 'danger')
|
||||
return redirect(url_for('base.allocations',net_id=net_id))
|
||||
# Check if IPv4 Address is in the Network
|
||||
if IPv4Address(form.ipv4.data) in network.hosts():
|
||||
add_alloc = Allocation(ipv4=str(form.ipv4.data),net_id=db_network.id,hostname=str(form.hostname.data),description=str(form.description.data))
|
||||
add_alloc = Allocation(ipv4=str(form.ipv4.data),net_id=db_network.id,hostname=str(form.host_name.data),description=str(form.host_desc.data))
|
||||
# Add Inventory if Host Type is not None
|
||||
##if int(form.host_type.data) != 0:
|
||||
## add_inv = Inventory(ip_addr=str(form.ipv4.data)+',',host_type=form.host_type.data,host_name=form.host_name.data,host_desc=form.host_desc.data)
|
||||
## db.session.add(add_inv)
|
||||
##
|
||||
## mod_alloc = Allocation.query.filter_by(ipv4=str(form.ipv4.data)).first()
|
||||
## mod_alloc.device_id = add_inv.id
|
||||
db.session.add(add_alloc)
|
||||
db.session.commit()
|
||||
return redirect(url_for('base.allocations',net_id=net_id))
|
||||
|
||||
return 'nok'
|
||||
flash("Invalid IPv4 Address", 'danger')
|
||||
return redirect(url_for('base.allocations',net_id=net_id))
|
||||
|
||||
return render_template('panel/allocations.html', title="Allocations", is_networks=True, app_version=app_version, form=form, allocations=db_allocations, network=db_network, Inventory=Inventory)
|
||||
|
||||
## MODAL ROUTES
|
||||
|
||||
@base.get('/network/<int:net_id>/allocations/<string:ipv4>/del_modal')
|
||||
@login_required
|
||||
def allocation_del_modal(net_id,ipv4):
|
||||
modal = {
|
||||
'title': 'Delete Allocation',
|
||||
'body': f'Are you sure you want to delete <b>{ipv4}</b> allocation ?',
|
||||
'color': 'warning',
|
||||
'action': 'delete',
|
||||
'action_text': 'Delete',
|
||||
'action_url': url_for('base.allocation_delete',net_id=net_id,ipv4=ipv4)
|
||||
}
|
||||
return render_template('panel/_modal.html', modal=modal, net_id=net_id, ipv4=ipv4)
|
||||
|
||||
@base.get('/network/<int:net_id>/del_modal')
|
||||
@login_required
|
||||
def network_del_modal(net_id):
|
||||
db_network = Network.query.filter_by(id=net_id).first()
|
||||
modal = {
|
||||
'title': 'Delete Network',
|
||||
'body': f'Are you sure you want to delete <b>{db_network.network_address}/{db_network.prefix}</b> network ?',
|
||||
'color': 'danger',
|
||||
'action': 'delete',
|
||||
'action_text': 'Delete',
|
||||
'action_url': url_for('base.network_delete',net_id=net_id)
|
||||
}
|
||||
return render_template('panel/_modal.html', modal=modal, net_id=net_id)
|
||||
|
||||
## DELETE ROUTES
|
||||
|
||||
@base.get('/network/<int:net_id>/allocations/<string:ipv4>/delete')
|
||||
@login_required
|
||||
def allocation_delete(net_id,ipv4):
|
||||
db_alloc = Allocation.query.filter_by(net_id=net_id,ipv4=ipv4).first()
|
||||
db.session.delete(db_alloc)
|
||||
db.session.commit()
|
||||
flash("Allocation Deleted", 'warning')
|
||||
return redirect(url_for('base.allocations',net_id=net_id))
|
||||
|
||||
@base.get('/network/<int:net_id>/delete')
|
||||
@login_required
|
||||
def network_delete(net_id):
|
||||
db_network = Network.query.filter_by(id=net_id).first()
|
||||
db_alloc = Allocation.query.filter_by(net_id=db_network.id).all()
|
||||
|
||||
|
||||
return render_template('panel/allocations.html', title="Allocations", is_networks=True, app_version=app_version, form=form, allocations=db_allocations, network=db_network)
|
||||
if db_network != None:
|
||||
if db_alloc != None:
|
||||
for alloc in db_alloc:
|
||||
db.session.delete(alloc)
|
||||
db.session.delete(db_network)
|
||||
db.session.commit()
|
||||
flash("Network Deleted", 'warning')
|
||||
return redirect(url_for('base.networks'))
|
||||
else:
|
||||
flash("Network Not Found", 'danger')
|
||||
return redirect(url_for('base.networks'))
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
var htmx=function(){"use strict";const Q={onLoad:null,process:null,on:null,off:null,trigger:null,ajax:null,find:null,findAll:null,closest:null,values:function(e,t){const n=cn(e,t||"post");return n.values},remove:null,addClass:null,removeClass:null,toggleClass:null,takeClass:null,swap:null,defineExtension:null,removeExtension:null,logAll:null,logNone:null,logger:null,config:{historyEnabled:true,historyCacheSize:10,refreshOnHistoryMiss:false,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:true,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:true,allowScriptTags:true,inlineScriptNonce:"",inlineStyleNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:false,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",scrollBehavior:"instant",defaultFocusScroll:false,getCacheBusterParam:false,globalViewTransitions:false,methodsThatUseUrlParams:["get","delete"],selfRequestsOnly:true,ignoreTitle:false,scrollIntoViewOnBoost:true,triggerSpecsCache:null,disableInheritance:false,responseHandling:[{code:"204",swap:false},{code:"[23]..",swap:true},{code:"[45]..",swap:false,error:true}],allowNestedOobSwaps:true},parseInterval:null,_:null,version:"2.0.4"};Q.onLoad=j;Q.process=kt;Q.on=ye;Q.off=be;Q.trigger=he;Q.ajax=Rn;Q.find=u;Q.findAll=x;Q.closest=g;Q.remove=z;Q.addClass=K;Q.removeClass=G;Q.toggleClass=W;Q.takeClass=Z;Q.swap=$e;Q.defineExtension=Fn;Q.removeExtension=Bn;Q.logAll=V;Q.logNone=_;Q.parseInterval=d;Q._=e;const n={addTriggerHandler:St,bodyContains:le,canAccessLocalStorage:B,findThisElement:Se,filterValues:hn,swap:$e,hasAttribute:s,getAttributeValue:te,getClosestAttributeValue:re,getClosestMatch:o,getExpressionVars:En,getHeaders:fn,getInputValues:cn,getInternalData:ie,getSwapSpecification:gn,getTriggerSpecs:st,getTarget:Ee,makeFragment:P,mergeObjects:ce,makeSettleInfo:xn,oobSwap:He,querySelectorExt:ae,settleImmediately:Kt,shouldCancel:ht,triggerEvent:he,triggerErrorEvent:fe,withExtensions:Ft};const r=["get","post","put","delete","patch"];const H=r.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");function d(e){if(e==undefined){return undefined}let t=NaN;if(e.slice(-2)=="ms"){t=parseFloat(e.slice(0,-2))}else if(e.slice(-1)=="s"){t=parseFloat(e.slice(0,-1))*1e3}else if(e.slice(-1)=="m"){t=parseFloat(e.slice(0,-1))*1e3*60}else{t=parseFloat(e)}return isNaN(t)?undefined:t}function ee(e,t){return e instanceof Element&&e.getAttribute(t)}function s(e,t){return!!e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function te(e,t){return ee(e,t)||ee(e,"data-"+t)}function c(e){const t=e.parentElement;if(!t&&e.parentNode instanceof ShadowRoot)return e.parentNode;return t}function ne(){return document}function m(e,t){return e.getRootNode?e.getRootNode({composed:t}):ne()}function o(e,t){while(e&&!t(e)){e=c(e)}return e||null}function i(e,t,n){const r=te(t,n);const o=te(t,"hx-disinherit");var i=te(t,"hx-inherit");if(e!==t){if(Q.config.disableInheritance){if(i&&(i==="*"||i.split(" ").indexOf(n)>=0)){return r}else{return null}}if(o&&(o==="*"||o.split(" ").indexOf(n)>=0)){return"unset"}}return r}function re(t,n){let r=null;o(t,function(e){return!!(r=i(t,ue(e),n))});if(r!=="unset"){return r}}function h(e,t){const n=e instanceof Element&&(e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector);return!!n&&n.call(e,t)}function T(e){const t=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i;const n=t.exec(e);if(n){return n[1].toLowerCase()}else{return""}}function q(e){const t=new DOMParser;return t.parseFromString(e,"text/html")}function L(e,t){while(t.childNodes.length>0){e.append(t.childNodes[0])}}function A(e){const t=ne().createElement("script");se(e.attributes,function(e){t.setAttribute(e.name,e.value)});t.textContent=e.textContent;t.async=false;if(Q.config.inlineScriptNonce){t.nonce=Q.config.inlineScriptNonce}return t}function N(e){return e.matches("script")&&(e.type==="text/javascript"||e.type==="module"||e.type==="")}function I(e){Array.from(e.querySelectorAll("script")).forEach(e=>{if(N(e)){const t=A(e);const n=e.parentNode;try{n.insertBefore(t,e)}catch(e){O(e)}finally{e.remove()}}})}function P(e){const t=e.replace(/<head(\s[^>]*)?>[\s\S]*?<\/head>/i,"");const n=T(t);let r;if(n==="html"){r=new DocumentFragment;const i=q(e);L(r,i.body);r.title=i.title}else if(n==="body"){r=new DocumentFragment;const i=q(t);L(r,i.body);r.title=i.title}else{const i=q('<body><template class="internal-htmx-wrapper">'+t+"</template></body>");r=i.querySelector("template").content;r.title=i.title;var o=r.querySelector("title");if(o&&o.parentNode===r){o.remove();r.title=o.innerText}}if(r){if(Q.config.allowScriptTags){I(r)}else{r.querySelectorAll("script").forEach(e=>e.remove())}}return r}function oe(e){if(e){e()}}function t(e,t){return Object.prototype.toStrinLine truncated
|
||||
@@ -17,6 +17,7 @@
|
||||
<body>
|
||||
{% block content %}{% endblock %}
|
||||
<!-- JAVASCRIPT -->
|
||||
<script src="{{ url_for('static',filename='js/htmx.min.js') }}"></script>
|
||||
<script src="{{ url_for('static',filename='js/bootstrap.bundle.min.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title text-{{modal.color}}">{{ modal.title }}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>{{ modal.body|safe }}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
{% if modal.action == 'delete' %}
|
||||
<a href="{{modal.action_url}}" class="btn btn-{{modal.color}}">{{modal.action_text}}</a>
|
||||
{% else %}
|
||||
<button type="button" class="btn btn-primary">Save changes</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -13,6 +13,7 @@
|
||||
<th scope="col">IPv4</th>
|
||||
<th scope="col">Hostname</th>
|
||||
<th scope="col">Description</th>
|
||||
<th scope="col">Device</th>
|
||||
<th scope="col">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -23,9 +24,25 @@
|
||||
<td>{{allocation.ipv4}}</td>
|
||||
<td>{{allocation.hostname}}</td>
|
||||
<td><b>{{allocation.description}}</b></td>
|
||||
<td>
|
||||
{% if Inventory.query.filter_by(id=allocation.device_id).first() %}
|
||||
<a href="{{Inventory.query.filter_by(id=allocation.device_id).first().id}}" style="text-decoration:none;">link</a>
|
||||
{% else %}
|
||||
N/A
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a href="" class="btn btn-sm btn-outline-info" style="padding-top:1px;padding-bottom:1px">edit</a>
|
||||
<button class="btn btn-sm btn-outline-danger"style="padding-top:1px;padding-bottom:1px">delete</button>
|
||||
<button
|
||||
hx-get="{{ url_for('base.allocation_del_modal',net_id=network.id,ipv4=allocation.ipv4) }}"
|
||||
hx-trigger="click"
|
||||
hx-target="#modal-get"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#modal-get"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
style="padding-top:1px;padding-bottom:1px"
|
||||
> Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@@ -39,16 +56,16 @@
|
||||
<form method="POST">
|
||||
{{ form.csrf_token }}
|
||||
<div class="mb-3">
|
||||
<label for="ipv4" class="form-label">IPv4</label>
|
||||
<label class="form-label">IPv4</label>
|
||||
{{ form.ipv4(class="form-control", placeholder=network.network_address) }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="prefix" class="form-label">Hostname</label>
|
||||
{{ form.hostname(class="form-control", placeholder="example") }}
|
||||
<label class="form-label">Hostname</label>
|
||||
{{ form.host_name(class="form-control", placeholder="example") }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Description</label>
|
||||
{{ form.description(class="form-control", placeholder="webserver") }}
|
||||
<label class="form-label">Description</label>
|
||||
{{ form.host_desc(class="form-control", placeholder="webserver") }}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-secondary w-100">Submit</button>
|
||||
</form>
|
||||
@@ -56,4 +73,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- MODAL -->
|
||||
<div id="modal-get"
|
||||
class="modal modal-blur fade"
|
||||
style="display: none"
|
||||
aria-hidden="false"
|
||||
tabindex="-1">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
||||
<div class="modal-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -27,7 +27,16 @@
|
||||
<td><b>{{network.description}}</b></td>
|
||||
<td>
|
||||
<a href="{{ url_for('base.allocations',net_id=network.id) }}" class="btn btn-sm btn-outline-info" style="padding-top:1px;padding-bottom:1px">Allocations</a>
|
||||
<button class="btn btn-sm btn-outline-danger"style="padding-top:1px;padding-bottom:1px">delete</button>
|
||||
<button
|
||||
hx-get="{{ url_for('base.network_del_modal',net_id=network.id) }}"
|
||||
hx-trigger="click"
|
||||
hx-target="#modal-get"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#modal-get"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
style="padding-top:1px;padding-bottom:1px"
|
||||
> Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@@ -58,4 +67,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- MODAL -->
|
||||
<div id="modal-get"
|
||||
class="modal modal-blur fade"
|
||||
style="display: none"
|
||||
aria-hidden="false"
|
||||
tabindex="-1">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
||||
<div class="modal-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user