Skip to content

Commit

Permalink
Merge pull request Uninett#2759 from stveit/portadmin-poe-frontend
Browse files Browse the repository at this point in the history
Frontend for configuring PoE in Portadmin
  • Loading branch information
lunkwill42 authored Nov 24, 2023
2 parents ccd4e13 + 6e8ff9f commit f9a100f
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 3 deletions.
8 changes: 8 additions & 0 deletions python/nav/web/portadmin/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,11 @@ def is_cisco(netbox):
:type netbox: manage.Netbox
"""
return netbox.type.get_enterprise_id() == VENDOR_ID_CISCOSYSTEMS


def add_poe_info(interfaces, handler):
"""Add information about PoE state for interfaces"""
states = handler.get_poe_states(interfaces)
for interface in interfaces:
interface.poe_state = states.get(interface.ifname)
interface.supports_poe = True if interface.poe_state else False
39 changes: 39 additions & 0 deletions python/nav/web/portadmin/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
mark_detained_interfaces,
is_cisco,
add_dot1x_info,
add_poe_info,
)
from nav.portadmin.config import CONFIG
from nav.portadmin.snmp.base import SNMPHandler
Expand All @@ -55,6 +56,9 @@
NoResponseError,
ProtocolError,
ManagementError,
POEIndexNotFoundError,
XMLParseError,
POEStateNotSupportedError,
)
from .forms import SearchForm
from ...portadmin.handlers import DeviceNotConfigurableError
Expand Down Expand Up @@ -192,6 +196,8 @@ def populate_infodict(request, netbox, interfaces):
voice_vlan = None
readonly = False
handler = None
supports_poe = False
poe_options = []

try:
handler = get_and_populate_livedata(netbox, interfaces)
Expand All @@ -207,6 +213,17 @@ def populate_infodict(request, netbox, interfaces):
mark_detained_interfaces(interfaces)
if CONFIG.is_dot1x_enabled():
add_dot1x_info(interfaces, handler)
try:
poe_options = handler.get_poe_state_options()
add_poe_info(interfaces, handler)
# Tag poe as being supported if at least one interface supports poe
for interface in interfaces:
if interface.supports_poe:
supports_poe = True
break
except NotImplementedError:
# Only Cisco and Juniper has PoE support currently
pass
except NoResponseError:
readonly = True
messages.error(
Expand All @@ -230,6 +247,14 @@ def populate_infodict(request, netbox, interfaces):
readonly = True
messages.error(request, str(error))

except (
POEIndexNotFoundError,
XMLParseError,
POEStateNotSupportedError,
) as error:
readonly = True
messages.error(request, str(error))

if handler and not handler.is_configurable():
add_readonly_reason(request, handler)
readonly = True
Expand Down Expand Up @@ -260,6 +285,8 @@ def populate_infodict(request, netbox, interfaces):
'aliastemplate': aliastemplate,
'trunk_edit': CONFIG.get_trunk_edit(),
'auditlog_api_parameters': json.dumps(auditlog_api_parameters),
'supports_poe': supports_poe,
'poe_options': poe_options,
}
)
return info_dict
Expand Down Expand Up @@ -363,11 +390,23 @@ def set_interface_values(account, interface, request):
set_ifalias(account, handler, interface, request)
set_vlan(account, handler, interface, request)
set_admin_status(handler, interface, request)
set_poe_state(handler, interface, request)
save_to_database([interface])
else:
messages.info(request, 'Could not connect to netbox')


def set_poe_state(handler, interface, request):
if 'poe_state' in request.POST:
poe_state_name = request.POST.get('poe_state')
for option in handler.get_poe_state_options():
if option.name == poe_state_name:
handler.set_poe_state(interface, option)
return
# If there was no match between posted value and known states
raise ValueError(f"Invalid PoE state name: {poe_state_name}")


def build_ajax_messages(request):
"""Create a structure suitable for converting to json from messages"""
ajax_messages = []
Expand Down
15 changes: 14 additions & 1 deletion python/nav/web/static/js/src/portadmin.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,16 @@ require(['libs/spin.min', 'libs/jquery-ui.min'], function (Spinner) {
$wrapper.on('change', '.ifadminstatus', function (event) {
actOnChange($(event.target).parents(parentSelector));
});
$wrapper.on('change', '.poelist', function (event) {
actOnChange($(event.target).parents(parentSelector));
});
}

/*
* Mark card changed or not based on values in card
*/
function actOnChange(row) {
if (textFieldChanged(row) || dropDownChanged(row) || voiceVlanChanged(row) || adminStatusChanged(row)) {
if (textFieldChanged(row) || dropDownChanged(row) || voiceVlanChanged(row) || adminStatusChanged(row) || poeDropDownChanged(row)) {
markAsChanged(row);
} else {
markAsUnchanged(row);
Expand Down Expand Up @@ -219,6 +222,13 @@ require(['libs/spin.min', 'libs/jquery-ui.min'], function (Spinner) {
return origOption !== selectedOption;
}

function poeDropDownChanged(row) {
var dropdown = $(row).find(".poelist");
var origOption = $('[data-orig]', dropdown)[0];
var selectedOption = $('option:selected', dropdown)[0];
return origOption !== selectedOption;
}

function voiceVlanChanged(row) {
/*
* XOR checkbox checked and original value to see if changed
Expand Down Expand Up @@ -303,6 +313,9 @@ require(['libs/spin.min', 'libs/jquery-ui.min'], function (Spinner) {
data.ifadminstatus = 2;
}
}
if (poeDropDownChanged($row)) {
data.poe_state = $row.find(".poelist").val();
}
if ($row.find(".voicevlan").prop('checked')) {
data.voice_activated = true;
}
Expand Down
27 changes: 25 additions & 2 deletions python/nav/web/templates/portadmin/portlist.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<div class="hide-for-small medium-1 column">
Linked
</div>
<div class="hide-for-small medium-4 column">
<div class="hide-for-small {% if supports_poe %}medium-3{% else %}medium-4{% endif %} column">
Port description
</div>
<div class="hide-for-small medium-2 column">
Expand All @@ -36,6 +36,11 @@
Tel
</div>
{% endif %}
{% if supports_poe %}
<div class="hide-for-small medium-1 column">
PoE State
</div>
{% endif %}
<div class="column {% if voice_vlan %}medium-1{% else %}medium-2{% endif %} text-right">
{% if not readonly %}
<input type="button" class="saveall_button button tiny" value="Save all"/>
Expand Down Expand Up @@ -76,7 +81,7 @@
</div>

{# Port Description - input field #}
<div class="medium-4 column">
<div class="{% if supports_poe %}medium-3{% else %}medium-4{% endif %} column">
{% if interface.iseditable and not readonly %}
<input class="ifalias" type="text"
value="{{ interface.ifalias|default_if_none:'' }}"
Expand Down Expand Up @@ -162,6 +167,24 @@
</div>
{% endif %}

{# POE STATE #}
{% if supports_poe %}
{% if interface.supports_poe %}
<div class="medium-1 small-4 column">
<form class="custom">
<select class="poelist" name="{{ interface.ifname }}">
{% for poe_option in poe_options %}
<option value="{{ poe_option.name }}" label="{{ poe_option.name }}"
{% if interface.poe_state.name == poe_option.name %}selected="selected"
data-orig="{{ poe_option.name }}"{% endif %}>
{{ vlan }}
{% endfor %}
</select>
</form>
</div>
{% endif %}
{% endif %}

{# Button for saving #}
<div class="{% if voice_vlan %}medium-1 small-4{% else %}medium-2 small-8{% endif %} column text-right">
{% if interface.iseditable and not readonly %}
Expand Down

0 comments on commit f9a100f

Please sign in to comment.