def set_voice_vlan(handler: ManagementHandler, interface, request): """Set voicevlan on interface A voice vlan is a normal vlan that is defined by the user of NAV as a vlan that is used only for ip telephone traffic. To set a voice vlan we have to make sure the interface is configured to tag both the voicevlan and the "access-vlan". """ if 'voicevlan' in request.POST: cdp_changed = False voice_vlan = fetch_voice_vlan_for_netbox(request, handler) use_cisco_voice_vlan = CONFIG.is_cisco_voice_enabled() and is_cisco( interface.netbox) enable_cdp_for_cisco_voice_port = CONFIG.is_cisco_voice_cdp_enabled() # Either the voicevlan is turned off or turned on turn_on_voice_vlan = request.POST.get('voicevlan') == 'true' account = get_account(request) try: if turn_on_voice_vlan: if use_cisco_voice_vlan: handler.set_cisco_voice_vlan(interface, voice_vlan) if enable_cdp_for_cisco_voice_port: handler.enable_cisco_cdp(interface) cdp_changed = True else: handler.set_interface_voice_vlan(interface, voice_vlan) _logger.info( '%s: %s:%s - %s%s', account.login, interface.netbox.get_short_sysname(), interface.ifname, 'voice vlan enabled', ', CDP enabled' if cdp_changed else '', ) else: if use_cisco_voice_vlan: handler.disable_cisco_voice_vlan(interface) if enable_cdp_for_cisco_voice_port: handler.disable_cisco_cdp(interface) cdp_changed = True else: handler.set_access(interface, interface.vlan) _logger.info( '%s: %s:%s - %s%s', account.login, interface.netbox.get_short_sysname(), interface.ifname, 'voice vlan disabled', ', CDP disabled' if cdp_changed else '', ) except (ManagementError, ValueError, NotImplementedError) as error: messages.error(request, "Error setting voicevlan: %s" % error)
def get_management_handler(netbox: Netbox) -> ManagementHandler: """Gets a ManagementHandler instance from the ManagementFactory""" timeout = CONFIG.getfloat("general", "timeout", fallback=3) retries = CONFIG.getint("general", "retries", fallback=3) try: return ManagementFactory.get_instance(netbox, timeout=timeout, retries=retries) except ManagementError as error: _logger.error('Error getting ManagementHandler instance %s: %s', netbox, error)
def commit_configuration(request): """Commit pending config changes to startup config""" if not CONFIG.is_commit_enabled(): _logger.debug('Not doing a configuration commit, it is configured off') return HttpResponse( "Configuration commit is configured to not be done") interface = get_object_or_404(Interface, pk=request.POST.get('interfaceid')) handler = get_management_handler(interface.netbox) if handler: try: handler.commit_configuration() except ManagementError as error: error_message = 'Error committing configuration on {}: {}'.format( handler.netbox, error) _logger.error(error_message) return HttpResponse(error_message, status=500) except (AttributeError, NotImplementedError): error_message = 'Error committing configuration on {}: {}'.format( handler.netbox, 'Configuration commit not supported') _logger.error(error_message) return HttpResponse(error_message, status=500) return HttpResponse() else: return HttpResponse(status=500)
def restart_interfaces(request): """Restart the interface by setting admin status to down and up""" if not CONFIG.is_restart_interface_enabled(): _logger.debug( "Not doing a restart of interfaces, it is configured off") return HttpResponse() interfaceids = request.POST.getlist("interfaceid", request.POST.getlist("interfaceid[]")) if not interfaceids: return HttpResponse(status=400, content=b"Missing interfaceid argument") interfaces = Interface.objects.filter( pk__in=interfaceids).select_related("netbox") if not interfaces: return HttpResponse(status=400, content=b"No interfaces selected") netboxes = set(i.netbox for i in interfaces) if len(netboxes) > 1: return HttpResponse( status=400, content=b"Can't restart interfaces from different netboxes") netbox = list(netboxes)[0] handler = get_management_handler(netbox) if handler: try: # Restart interface so that clients fetch new addresses handler.cycle_interfaces(interfaces) except NoResponseError: # Failures aren't grossly important here, we ignore them pass return HttpResponse() else: return HttpResponse(status=500, content=b"Could not create management handler")
def handle_trunk_edit(request, agent, interface): """Edit a trunk""" native_vlan = int(request.POST.get('native_vlan', 1)) trunked_vlans = [int(vlan) for vlan in request.POST.getlist('trunk_vlans')] if should_check_access_rights(get_account(request)): # A user can avoid the form restrictions by sending a forged post # request Make sure only the allowed vlans are set old_native, old_trunked = agent.get_native_and_trunked_vlans(interface) allowed_vlans = [ v.vlan for v in find_allowed_vlans_for_user(get_account(request)) ] trunked_vlans = filter_vlans(trunked_vlans, old_trunked, allowed_vlans) native_vlan = native_vlan if native_vlan in allowed_vlans else old_native _logger.info('Interface %s - native: %s, trunk: %s', interface, native_vlan, trunked_vlans) LogEntry.add_log_entry( request.account, u'set-vlan', u'{actor}: {object} - native vlan: "%s", trunk vlans: "%s"' % (native_vlan, trunked_vlans), subsystem=u'portadmin', object=interface, ) if trunked_vlans: agent.set_trunk(interface, native_vlan, trunked_vlans) else: agent.set_access(interface, native_vlan) if CONFIG.is_commit_enabled(): agent.commit_configuration()
def find_allowed_vlans_for_user(account): """Find the allowed vlans for this user based on organization""" allowed_vlans = [] for org in account.organizations.all(): allowed_vlans.extend(find_vlans_in_org(org)) defaultvlan = CONFIG.find_default_vlan() if defaultvlan and defaultvlan not in allowed_vlans: allowed_vlans.append(defaultvlan) return allowed_vlans
def render_trunk_edit(request, interfaceid): """Controller for rendering trunk edit view""" interface = Interface.objects.get(pk=interfaceid) handler = get_management_handler(interface.netbox) if request.method == 'POST': try: handle_trunk_edit(request, handler, interface) except ManagementError as error: messages.error(request, 'Error editing trunk: %s' % error) else: messages.success(request, 'Trunk edit successful') account = request.account netbox = interface.netbox add_readonly_reason(request, handler) try: vlans = handler.get_netbox_vlans() # All vlans on this netbox native_vlan, trunked_vlans = handler.get_native_and_trunked_vlans( interface) except ManagementError as error: vlans = native_vlan = trunked_vlans = allowed_vlans = None messages.error(request, 'Error getting trunk information: {}'.format(error)) else: if should_check_access_rights(account): allowed_vlans = find_allowed_vlans_for_user_on_netbox( account, interface.netbox, handler) else: allowed_vlans = vlans extra_path = [ ( netbox.sysname, reverse('portadmin-sysname', kwargs={'sysname': netbox.sysname}), ), ("Trunk %s" % interface, ), ] context = get_base_context(extra_path) context.update({ 'interface': interface, 'available_vlans': vlans, 'native_vlan': native_vlan, 'trunked_vlans': trunked_vlans, 'allowed_vlans': allowed_vlans, 'trunk_edit': CONFIG.get_trunk_edit(), 'readonly': not handler.is_configurable(), }) return render(request, 'portadmin/trunk_edit.html', context)
def check_format_on_ifalias(ifalias): """Verify that format on ifalias is correct if it is defined in config""" if not ifalias: return True ifalias_format = CONFIG.get_ifaliasformat() if ifalias_format: ifalias_format = re.compile(ifalias_format) if ifalias_format.match(ifalias): return True else: _logger.error('Wrong format on ifalias: %s', ifalias) return False else: return True
def add_dot1x_info(interfaces, handler): """Add information about dot1x state for interfaces""" # Skip if port access control is not enabled (and thus not dot1x) if not handler.is_port_access_control_enabled(): return dot1x_states = handler.get_dot1x_enabled_interfaces() url_template = CONFIG.get_dot1x_external_url() for interface in interfaces: interface.dot1xenabled = dot1x_states.get(interface.ifindex) if url_template: interface.dot1x_external_url = url_template.format( netbox=interface.netbox, interface=interface)
def find_allowed_vlans_for_user_on_netbox( account: profiles.Account, netbox: manage.Netbox, handler: ManagementHandler = None) -> List[FantasyVlan]: """Finds allowed vlans for this user on this netbox""" netbox_vlans = find_vlans_on_netbox(netbox, handler=handler) if CONFIG.is_vlan_authorization_enabled(): if is_admin(account): allowed_vlans = netbox_vlans else: all_allowed_vlans = find_allowed_vlans_for_user(account) allowed_vlans = intersect(all_allowed_vlans, netbox_vlans) else: allowed_vlans = netbox_vlans return sorted(allowed_vlans, key=attrgetter('vlan'))
def set_editable_flag_on_interfaces(interfaces: Sequence[manage.Interface], vlans: Sequence[FantasyVlan]): """Sets the pseudo-attribute `iseditable` on each interface in the interfaces list, indicating whether the PortAdmin UI should allow edits to it or not. An interface will be considered "editable" only if its native vlan matches one of the vlan tags from `vlans`. An interface may be considered non-editable if it is an uplink, depending on how portadmin is configured. """ vlan_tags = {vlan.vlan for vlan in vlans} for interface in interfaces: vlan_is_acceptable = interface.vlan in vlan_tags is_link = bool(interface.to_netbox) refuse_link_edit = not CONFIG.get_link_edit() and is_link interface.iseditable = vlan_is_acceptable and not refuse_link_edit
def find_allowed_vlans_for_user_on_netbox(account, netbox, factory=None): """Find allowed vlans for this user on this netbox ::returns list of Fantasyvlans """ netbox_vlans = find_vlans_on_netbox(netbox, factory=factory) if CONFIG.is_vlan_authorization_enabled(): if is_admin(account): allowed_vlans = netbox_vlans else: all_allowed_vlans = find_allowed_vlans_for_user(account) allowed_vlans = intersect(all_allowed_vlans, netbox_vlans) else: allowed_vlans = netbox_vlans return sorted(allowed_vlans, key=attrgetter('vlan'))
def set_vlan(account, handler: ManagementHandler, interface, request): """Set vlan on netbox if it is requested""" if 'vlan' in request.POST: try: vlan = int(request.POST.get('vlan')) except ValueError: messages.error( request, "Ignoring request to set vlan={}".format( request.POST.get('vlan')), ) return try: if is_cisco(interface.netbox): # If Cisco and trunk voice vlan (not Cisco voice vlan), # we have to set native vlan instead of access vlan voice_activated = request.POST.get('voice_activated', False) if not CONFIG.is_cisco_voice_enabled() and voice_activated: handler.set_native_vlan(interface, vlan) else: handler.set_vlan(interface, vlan) else: handler.set_vlan(interface, vlan) interface.vlan = vlan LogEntry.add_log_entry( account, u'set-vlan', u'{actor}: {object} - vlan set to "%s"' % vlan, subsystem=u'portadmin', object=interface, ) _logger.info( '%s: %s:%s - vlan set to %s', account.login, interface.netbox.get_short_sysname(), interface.ifname, vlan, ) except (ManagementError, TypeError) as error: _logger.error('Error setting vlan: %s', error) messages.error(request, "Error setting vlan: %s" % error)
def fetch_voice_vlan_for_netbox(request: HttpRequest, handler: ManagementHandler): """Fetch the voice vlan for this netbox There may be multiple voice vlans configured. Pick the one that exists on this netbox. If multiple vlans exist, we cannot know which one to use. """ voice_vlans = CONFIG.fetch_voice_vlans() if not voice_vlans: return voice_vlans_on_netbox = list( set(voice_vlans) & set(handler.get_netbox_vlan_tags())) if not voice_vlans_on_netbox: # Should this be reported? At the moment I do not think so. return if len(voice_vlans_on_netbox) > 1: messages.error(request, 'Multiple voice vlans configured on this ' 'netbox') return return voice_vlans_on_netbox[0]
def should_check_access_rights(account): """Return boolean indicating that this user is restricted""" return (CONFIG.is_vlan_authorization_enabled() and not is_admin(account))
def populate_infodict(request, netbox, interfaces): """Populate a dictionary used in every http response""" allowed_vlans = [] voice_vlan = None readonly = False handler = None try: handler = get_and_populate_livedata(netbox, interfaces) allowed_vlans = find_and_populate_allowed_vlans( request.account, netbox, interfaces, handler) voice_vlan = fetch_voice_vlan_for_netbox(request, handler) if voice_vlan: if CONFIG.is_cisco_voice_enabled() and is_cisco(netbox): set_voice_vlan_attribute_cisco(voice_vlan, interfaces, handler) else: set_voice_vlan_attribute(voice_vlan, interfaces) mark_detained_interfaces(interfaces) if CONFIG.is_dot1x_enabled(): add_dot1x_info(interfaces, handler) except NoResponseError: readonly = True messages.error( request, "%s did not respond within the set timeouts. Values displayed are from database" % netbox.sysname, ) if isinstance(handler, SNMPHandler) and not netbox.read_only: messages.error(request, "Read only community not set") except ProtocolError: readonly = True messages.error( request, "Protocol error when contacting %s. Values displayed are from database" % netbox.sysname, ) except DeviceNotConfigurableError as error: readonly = True messages.error(request, str(error)) if handler and not handler.is_configurable(): add_readonly_reason(request, handler) readonly = True ifaliasformat = CONFIG.get_ifaliasformat() aliastemplate = '' if ifaliasformat: tmpl = get_aliastemplate() aliastemplate = tmpl.render({'ifaliasformat': ifaliasformat}) save_to_database(interfaces) auditlog_api_parameters = { 'object_model': 'interface', 'object_pks': ','.join([str(i.pk) for i in interfaces]), 'subsystem': 'portadmin', } info_dict = get_base_context([(netbox.sysname, )], form=get_form(request)) info_dict.update({ 'handlertype': type(handler).__name__, 'interfaces': interfaces, 'netbox': netbox, 'voice_vlan': voice_vlan, 'allowed_vlans': allowed_vlans, 'readonly': readonly, 'aliastemplate': aliastemplate, 'trunk_edit': CONFIG.get_trunk_edit(), 'auditlog_api_parameters': json.dumps(auditlog_api_parameters), }) return info_dict