Closes #49: Introduction of circuit terminations

This commit is contained in:
Jeremy Stretch
2016-12-14 13:47:22 -05:00
parent 298ac1ba7a
commit bf817eb69e
25 changed files with 638 additions and 273 deletions

View File

@@ -82,17 +82,6 @@
{% endif %}
</td>
</tr>
<tr>
<td>Speed</td>
<td>
{% if circuit.upstream_speed %}
<i class="fa fa-arrow-down" title="Downstream"></i> {{ circuit.port_speed_human }} &nbsp;
<i class="fa fa-arrow-up" title="Upstream"></i> {{ circuit.upstream_speed_human }}
{% else %}
{{ circuit.port_speed_human }}
{% endif %}
</td>
</tr>
<tr>
<td>Commit Rate</td>
<td>
@@ -108,66 +97,6 @@
{% with circuit.get_custom_fields as custom_fields %}
{% include 'inc/custom_fields_panel.html' %}
{% endwith %}
</div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Termination</strong>
</div>
<table class="table table-hover panel-body">
<tr>
<td>Site</td>
<td>
<a href="{% url 'dcim:site' slug=circuit.site.slug %}">{{ circuit.site }}</a>
</td>
</tr>
<tr>
<td>Termination</td>
<td>
{% if circuit.interface %}
<span><a href="{% url 'dcim:device' pk=circuit.interface.device.pk %}">{{ circuit.interface.device }}</a> {{ circuit.interface }}</span>
{% else %}
<span class="text-muted">Not defined</span>
{% endif %}
</td>
</tr>
<tr>
<td>IP Addressing</td>
<td>
{% if circuit.interface %}
{% for ip in circuit.interface.ip_addresses.all %}
{% if not forloop.first %}<br />{% endif %}
<a href="{% url 'ipam:ipaddress' pk=ip.pk %}">{{ ip }}</a> ({{ ip.vrf|default:"Global" }})
{% empty %}
<span class="text-muted">None</span>
{% endfor %}
{% else %}
<span class="text-muted">N/A</span>
{% endif %}
</td>
</tr>
<tr>
<td>Cross-Connect</td>
<td>
{% if circuit.xconnect_id %}
{{ circuit.xconnect_id }}
{% else %}
<span class="text-muted">N/A</span>
{% endif %}
</td>
</tr>
<tr>
<td>Patch Panel/Port</td>
<td>
{% if circuit.pp_info %}
{{ circuit.pp_info }}
{% else %}
<span class="text-muted">N/A</span>
{% endif %}
</td>
</tr>
</table>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<strong>Comments</strong>
@@ -180,6 +109,10 @@
{% endif %}
</div>
</div>
</div>
<div class="col-md-6">
{% include 'circuits/inc/circuit_termination.html' with termination=termination_a side='A' %}
{% include 'circuits/inc/circuit_termination.html' with termination=termination_z side='Z' %}
</div>
</div>
{% endblock %}

View File

@@ -1,5 +1,4 @@
{% extends 'utilities/obj_edit.html' %}
{% load static from staticfiles %}
{% load form_helpers %}
{% block form %}
@@ -11,15 +10,6 @@
{% render_field form.type %}
{% render_field form.tenant %}
{% render_field form.install_date %}
{% render_field form.xconnect_id %}
{% render_field form.pp_info %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Bandwidth</strong></div>
<div class="panel-body">
{% render_field form.port_speed %}
{% render_field form.upstream_speed %}
{% render_field form.commit_rate %}
</div>
</div>
@@ -31,26 +21,6 @@
</div>
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Termination</strong></div>
<div class="panel-body">
{% render_field form.site %}
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#select" aria-controls="home" role="tab" data-toggle="tab">Select</a></li>
<li role="presentation"><a href="#search" aria-controls="search" role="tab" data-toggle="tab">Search</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="select">
{% render_field form.rack %}
{% render_field form.device %}
</div>
<div class="tab-pane" id="search">
{% render_field form.livesearch %}
</div>
</div>
{% render_field form.interface %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Comments</strong></div>
<div class="panel-body">
@@ -58,7 +28,3 @@
</div>
</div>
{% endblock %}
{% block javascript %}
<script src="{% static 'js/livesearch.js' %}"></script>
{% endblock %}

View File

@@ -0,0 +1,25 @@
{% extends 'utilities/confirmation_form.html' %}
{% block title %}Swap Circuit Terminations{% endblock %}
{% block message %}
<p>Swap these terminations for circuit {{ circuit }}?</p>
<ul>
<li>
<strong>A side:</strong>
{% if termination_a %}
{{ termination_a.site }} {% if termination_a.interface %}- {{ termination_a.interface.device }} {{ termination_a.interface }}{% endif %}
{% else %}
<span class="text-muted">None</span>
{% endif %}
</li>
<li>
<strong>Z side:</strong>
{% if termination_z %}
{{ termination_z.site }} {% if termination_z.interface %}- {{ termination_z.interface.device }} {{ termination_z.interface }}{% endif %}
{% else %}
<span class="text-muted">None</span>
{% endif %}
</li>
</ul>
{% endblock %}

View File

@@ -0,0 +1,94 @@
{% extends '_base.html' %}
{% load staticfiles %}
{% load form_helpers %}
{% block title %}
Circuit {{ obj.circuit }} - Side {{ form.term_side.value }}
{% endblock %}
{% block content %}
<form action="." method="post" class="form form-horizontal">
{% csrf_token %}
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h3>Circuit {{ obj.circuit }} - Side {{ form.term_side.value }}</h3>
{% if form.non_field_errors %}
<div class="panel panel-danger">
<div class="panel-heading"><strong>Errors</strong></div>
<div class="panel-body">
{{ form.non_field_errors }}
</div>
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Location</strong></div>
<div class="panel-body">
<div class="form-group">
<label class="col-md-3 control-label">Provider</label>
<div class="col-md-9">
<p class="form-control-static">{{ obj.circuit.provider }}</p>
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label">Circuit</label>
<div class="col-md-9">
<p class="form-control-static">{{ obj.circuit.cid }}</p>
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label">Termination</label>
<div class="col-md-9">
<p class="form-control-static">{{ form.term_side.value }}</p>
</div>
</div>
{% render_field form.site %}
<div class="row">
<div class="col-md-9 col-md-offset-3">
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#select" aria-controls="home" role="tab" data-toggle="tab">Select</a></li>
<li role="presentation"><a href="#search" aria-controls="search" role="tab" data-toggle="tab">Search</a></li>
</ul>
</div>
</div>
<div class="tab-content">
<div class="tab-pane active" id="select">
{% render_field form.rack %}
{% render_field form.device %}
</div>
<div class="tab-pane" id="search">
{% render_field form.livesearch %}
</div>
</div>
{% render_field form.interface %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Termination Details</strong></div>
<div class="panel-body">
{% render_field form.port_speed %}
{% render_field form.upstream_speed %}
{% render_field form.xconnect_id %}
{% render_field form.pp_info %}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 col-md-offset-3 text-right">
{% if obj.pk %}
<button type="submit" name="_update" class="btn btn-primary">Update</button>
{% else %}
<button type="submit" name="_create" class="btn btn-primary">Create</button>
{% endif %}
<a href="{{ cancel_url }}" class="btn btn-default">Cancel</a>
</div>
</div>
</form>
{% endblock %}
{% block javascript %}
<script src="{% static 'js/livesearch.js' %}"></script>
{% endblock %}

View File

@@ -0,0 +1,95 @@
<div class="panel panel-default">
<div class="panel-heading">
<div class="pull-right">
{% if not termination and perms.circuits.add_circuittermination %}
<a href="{% url 'circuits:circuittermination_add' circuit=circuit.pk %}?term_side={{ side }}" class="btn btn-xs btn-success">
<span class="fa fa-plus" aria-hidden="true"></span> Add
</a>
{% endif %}
{% if termination and perms.circuits.change_circuittermination %}
<a href="{% url 'circuits:circuittermination_edit' pk=termination.pk %}" class="btn btn-xs btn-warning">
<span class="fa fa-pencil" aria-hidden="true"></span> Edit
</a>
<a href="{% url 'circuits:circuit_terminations_swap' pk=circuit.pk %}" class="btn btn-xs btn-primary">
<span class="fa fa-refresh" aria-hidden="true"></span> Swap
</a>
{% endif %}
{% if termination and perms.circuits.delete_circuittermination %}
<a href="{% url 'circuits:circuittermination_delete' pk=termination.pk %}" class="btn btn-xs btn-danger">
<span class="fa fa-trash" aria-hidden="true"></span> Delete
</a>
{% endif %}
</div>
<strong>Termination - {{ side }} Side</strong>
</div>
{% if termination %}
<table class="table table-hover panel-body">
<tr>
<td>Site</td>
<td>
<a href="{% url 'dcim:site' slug=termination.site.slug %}">{{ termination.site }}</a>
</td>
</tr>
<tr>
<td>Termination</td>
<td>
{% if termination.interface %}
<span><a href="{% url 'dcim:device' pk=termination.interface.device.pk %}">{{ termination.interface.device }}</a> {{ termination.interface }}</span>
{% else %}
<span class="text-muted">Not defined</span>
{% endif %}
</td>
</tr>
<tr>
<td>Speed</td>
<td>
{% if termination.upstream_speed %}
<i class="fa fa-arrow-down" title="Downstream"></i> {{ termination.port_speed_human }} &nbsp;
<i class="fa fa-arrow-up" title="Upstream"></i> {{ termination.upstream_speed_human }}
{% else %}
{{ termination.port_speed_human }}
{% endif %}
</td>
</tr>
<tr>
<td>IP Addressing</td>
<td>
{% if termination.interface %}
{% for ip in termination.interface.ip_addresses.all %}
{% if not forloop.first %}<br />{% endif %}
<a href="{% url 'ipam:ipaddress' pk=ip.pk %}">{{ ip }}</a> ({{ ip.vrf|default:"Global" }})
{% empty %}
<span class="text-muted">None</span>
{% endfor %}
{% else %}
<span class="text-muted">N/A</span>
{% endif %}
</td>
</tr>
<tr>
<td>Cross-Connect</td>
<td>
{% if termination.xconnect_id %}
{{ termination.xconnect_id }}
{% else %}
<span class="text-muted">N/A</span>
{% endif %}
</td>
</tr>
<tr>
<td>Patch Panel/Port</td>
<td>
{% if termination.pp_info %}
{{ termination.pp_info }}
{% else %}
<span class="text-muted">N/A</span>
{% endif %}
</td>
</tr>
</table>
{% else %}
<div class="panel-body">
<span class="text-muted">None</span>
</div>
{% endif %}
</div>

View File

@@ -134,14 +134,8 @@
<a href="{% url 'circuits:circuit' pk=c.pk %}">{{ c.cid }}</a>
</td>
<td>
<a href="{% url 'dcim:site' slug=c.site.slug %}">{{ c.site }}</a>
<a href="{% url 'circuits:circuit_list' %}?type={{ c.type.slug }}">{{ c.type }}</a>
</td>
<td>
{% if c.interface %}
<a href="{% url 'dcim:device' pk=c.interface.device.pk %}">{{ c.interface.device }}</a>
{% endif %}
</td>
<td>{{ c.port_speed_human }}</td>
</tr>
{% empty %}
<tr>
@@ -149,6 +143,13 @@
</tr>
{% endfor %}
</table>
{% if perms.circuits.add_circuit %}
<div class="panel-footer text-right">
<a href="{% url 'circuits:circuit_add' %}?provider={{ provider.pk }}" class="btn btn-xs btn-primary">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add circuit
</a>
</div>
{% endif %}
</div>
</div>
</div>

View File

@@ -57,7 +57,7 @@
<div class="panel-body">
{% render_field form.platform %}
{% render_field form.status %}
{% if obj %}
{% if obj.pk %}
{% render_field form.primary_ip4 %}
{% render_field form.primary_ip6 %}
{% endif %}

View File

@@ -14,7 +14,7 @@
<small>{{ iface.mac_address|default:'' }}</small>
</td>
{% if not iface.is_physical %}
<td colspan="2">Virtual</td>
<td colspan="2" class="text-muted">Virtual interface</td>
{% elif iface.connection %}
{% with iface.get_connected_interface as connected_iface %}
<td>
@@ -24,10 +24,16 @@
<span title="{{ connected_iface.get_form_factor_display }}">{{ connected_iface }}</span>
</td>
{% endwith %}
{% elif iface.circuit %}
<td colspan="2">
<a href="{% url 'circuits:circuit' pk=iface.circuit.pk %}">{{ iface.circuit }}</a>
</td>
{% elif iface.circuit_termination %}
{% with iface.circuit_termination.get_peer_termination as peer_termination %}
<td colspan="2">
<i class="fa fa-fw fa-globe"></i>
{% if peer_termination %}
<a href="{% url 'dcim:site' slug=peer_termination.site.slug %}">{{ peer_termination.site }}</a> via
{% endif %}
<a href="{% url 'circuits:circuit' pk=iface.circuit_termination.circuit_id %}">{{ iface.circuit_termination.circuit }}</a>
</td>
{% endwith %}
{% else %}
<td colspan="2">
<span class="text-muted">Not connected</span>
@@ -35,7 +41,7 @@
{% endif %}
<td class="text-right">
{% if show_graphs %}
{% if iface.circuit or iface.connection %}
{% if iface.circuit_termination or iface.connection %}
<button type="button" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#graphs_modal" data-obj="{{ device.name }} - {{ iface.name }}" data-url="{% url 'dcim-api:interface_graphs' pk=iface.pk %}" title="Show graphs">
<i class="glyphicon glyphicon-signal" aria-hidden="true"></i>
</button>
@@ -56,8 +62,11 @@
<a href="{% url 'dcim:interfaceconnection_delete' pk=iface.connection.pk %}?device={{ device.pk }}" class="btn btn-danger btn-xs" title="Delete connection">
<i class="glyphicon glyphicon-remove" aria-hidden="true"></i>
</a>
{% elif iface.circuit and perms.circuits.change_circuit %}
<a href="{% url 'circuits:circuit_edit' pk=iface.circuit.pk %}" class="btn btn-danger btn-xs" title="Edit circuit">
{% elif iface.circuit_termination and perms.circuits.change_circuittermination %}
<button class="btn btn-warning btn-xs interface-toggle connected" disabled="disabled" title="Circuits cannot be marked as planned or connected">
<i class="glyphicon glyphicon-ban-circle" aria-hidden="true"></i>
</button>
<a href="{% url 'circuits:circuittermination_edit' pk=iface.circuit_termination.pk %}" class="btn btn-danger btn-xs" title="Edit circuit termination">
<i class="glyphicon glyphicon-remove" aria-hidden="true"></i>
</a>
{% else %}
@@ -71,7 +80,7 @@
</a>
{% endif %}
{% if perms.dcim.delete_interface %}
{% if iface.connection or iface.circuit %}
{% if iface.connection or iface.circuit_termination %}
<button class="btn btn-danger btn-xs" disabled="disabled">
<i class="glyphicon glyphicon-trash" aria-hidden="true"></i>
</button>

View File

@@ -10,7 +10,7 @@
{% render_field form.vrf %}
{% render_field form.tenant %}
{% render_field form.status %}
{% if obj %}
{% if obj.pk %}
<div class="form-group">
<label class="col-md-3 control-label">Device</label>
<div class="col-md-9">

View File

@@ -6,7 +6,7 @@
<div class="col-md-6 col-md-offset-3">
<form action="." method="post" class="form">
{% csrf_token %}
<div class="panel panel-danger">
<div class="panel panel-{{ panel_class|default:"danger" }}">
<div class="panel-heading">{% block title %}{% endblock %}</div>
<div class="panel-body">
{% block message %}<p>Are you sure?</p>{% endblock %}
@@ -22,7 +22,7 @@
</div>
</div>
<div class="text-right">
<button type="submit" name="_confirm" class="btn btn-danger">Confirm</button>
<button type="submit" name="_confirm" class="btn btn-{{ button_class|default:"danger" }}">Confirm</button>
<a href="{{ cancel_url }}" class="btn btn-default">Cancel</a>
</div>
</div>

View File

@@ -2,15 +2,18 @@
{% load form_helpers %}
{% block title %}
{% if obj %}Editing {{ obj_type }} {{ obj }}{% else %}Add a new {{ obj_type }}{% endif %}
{% if obj.pk %}Editing {{ obj_type }} {{ obj }}{% else %}Add a new {{ obj_type }}{% endif %}
{% endblock %}
{% block content %}
<form action="." method="post" class="form form-horizontal">
{% csrf_token %}
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h3>{% if obj %}Editing {{ obj_type }} {{ obj }}{% else %}Add a new {{ obj_type }}{% endif %}</h3>
<h3>{% if obj.pk %}Editing {{ obj_type }} {{ obj }}{% else %}Add a new {{ obj_type }}{% endif %}</h3>
{% if form.non_field_errors %}
<div class="panel panel-danger">
<div class="panel-heading"><strong>Errors</strong></div>
@@ -31,7 +34,7 @@
</div>
<div class="row">
<div class="col-md-6 col-md-offset-3 text-right">
{% if obj %}
{% if obj.pk %}
<button type="submit" name="_update" class="btn btn-primary">Update</button>
{% else %}
<button type="submit" name="_create" class="btn btn-primary">Create</button>