Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frontend for configuring PoE in Portadmin #2759

Merged
merged 6 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
: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

Check warning on line 253 in python/nav/web/portadmin/utils.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/portadmin/utils.py#L250-L253

Added lines #L250 - L253 were not covered by tests
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 @@
voice_vlan = None
readonly = False
handler = None
supports_poe = False
poe_options = []

Check warning on line 200 in python/nav/web/portadmin/views.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/portadmin/views.py#L199-L200

Added lines #L199 - L200 were not covered by tests

try:
handler = get_and_populate_livedata(netbox, interfaces)
Expand All @@ -207,6 +213,17 @@
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)

Check warning on line 218 in python/nav/web/portadmin/views.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/portadmin/views.py#L216-L218

Added lines #L216 - L218 were not covered by tests
# 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:

Check warning on line 224 in python/nav/web/portadmin/views.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/portadmin/views.py#L220-L224

Added lines #L220 - L224 were not covered by tests
# Only Cisco and Juniper has PoE support currently
pass

Check warning on line 226 in python/nav/web/portadmin/views.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/portadmin/views.py#L226

Added line #L226 was not covered by tests
except NoResponseError:
readonly = True
messages.error(
Expand All @@ -230,6 +247,14 @@
readonly = True
messages.error(request, str(error))

except (

Check warning on line 250 in python/nav/web/portadmin/views.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/portadmin/views.py#L250

Added line #L250 was not covered by tests
POEIndexNotFoundError,
XMLParseError,
POEStateNotSupportedError,
) as error:
readonly = True
messages.error(request, str(error))

Check warning on line 256 in python/nav/web/portadmin/views.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/portadmin/views.py#L255-L256

Added lines #L255 - L256 were not covered by tests

if handler and not handler.is_configurable():
add_readonly_reason(request, handler)
readonly = True
Expand Down Expand Up @@ -260,6 +285,8 @@
'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 @@
set_ifalias(account, handler, interface, request)
set_vlan(account, handler, interface, request)
set_admin_status(handler, interface, request)
set_poe_state(handler, interface, request)

Check warning on line 393 in python/nav/web/portadmin/views.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/portadmin/views.py#L393

Added line #L393 was not covered by tests
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

Check warning on line 405 in python/nav/web/portadmin/views.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/portadmin/views.py#L400-L405

Added lines #L400 - L405 were not covered by tests
# If there was no match between posted value and known states
raise ValueError(f"Invalid PoE state name: {poe_state_name}")

Check warning on line 407 in python/nav/web/portadmin/views.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/portadmin/views.py#L407

Added line #L407 was not covered by tests


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
23 changes: 23 additions & 0 deletions python/nav/web/templates/portadmin/portlist.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
Tel
</div>
{% endif %}
{% if supports_poe %}
<div class="hide-for-small medium-2 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 @@ -162,6 +167,24 @@
</div>
{% endif %}

{# POE STATE #}
{% if supports_poe %}
{% if interface.supports_poe %}
<div class="medium-2 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
Loading