Update sonic-host-services for changes in Python

This fixes 3 issues:
* Specify test dependencies under extra_requires
* Update the PAM configuration for Bookworm
* Break a cyclical dependency between sonic-host-services and
  sonic-buildimage by moving the contents of
  src/sonic-host-services-data into sonic-host-services submodule

Signed-off-by: Saikrishna Arcot <sarcot@microsoft.com>
This commit is contained in:
Saikrishna Arcot 2023-09-07 16:46:11 -07:00 committed by Ying Xie
parent 675d83066d
commit 8ec9672f45
32 changed files with 3 additions and 569 deletions

View File

@ -1,7 +1,7 @@
SPATH := $($(SONIC_HOST_SERVICES_DATA)_SRC_PATH)
DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/sonic-host-services-data.mk rules/sonic-host-services-data.dep
DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST)
DEP_FILES += $(shell git ls-files $(SPATH))
DEP_FILES += $(addprefix $(SPATH)/,$(shell git -C $(SPATH) ls-files))
$(SONIC_HOST_SERVICES_DATA)_CACHE_MODE := GIT_CONTENT_SHA
$(SONIC_HOST_SERVICES_DATA)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST)

View File

@ -1,5 +1,5 @@
# SONiC host services data package
SONIC_HOST_SERVICES_DATA = sonic-host-services-data_1.0-1_all.deb
$(SONIC_HOST_SERVICES_DATA)_SRC_PATH = $(SRC_PATH)/sonic-host-services-data
$(SONIC_HOST_SERVICES_DATA)_SRC_PATH = $(SRC_PATH)/sonic-host-services/data
SONIC_DPKG_DEBS += $(SONIC_HOST_SERVICES_DATA)

View File

@ -1,7 +1,7 @@
SPATH := $($(SONIC_HOST_SERVICES_PY3)_SRC_PATH)
DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/sonic-host-services.mk rules/sonic-host-services.dep
DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST)
SMDEP_FILES := $(addprefix $(SPATH)/,$(shell cd $(SPATH) && git ls-files))
SMDEP_FILES := $(addprefix $(SPATH)/,$(shell git -C $(SPATH) ls-files | grep -v ^data))
$(SONIC_HOST_SERVICES_PY3)_CACHE_MODE := GIT_CONTENT_SHA
$(SONIC_HOST_SERVICES_PY3)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST)

View File

@ -1,6 +0,0 @@
debian/*.debhelper
debian/debhelper-build-stamp
debian/sonic-host-services-data/
sonic-host-services-data_*.buildinfo
sonic-host-services-data_*.changes
sonic-host-services-data_*.deb

View File

@ -1,7 +0,0 @@
# This file describes the maintainers for sonic-host-services-data
# See the SONiC project governance document for more information
Name = "Joe LeVeque"
Email = "jolevequ@microsoft.com"
Github = jleveque
Mailinglist = sonicproject@googlegroups.com

View File

@ -1,19 +0,0 @@
# sonic-host-services-data
Data files required for SONiC host services
## To build
```
dpkg-buildpackage -rfakeroot -b -us -uc
```
## To clean
```
dpkg-buildpackage -rfakeroot -Tclean
```
---
See the [SONiC Website](https://sonic-net.github.io/SONiC/) for more information about the SONiC project.

View File

@ -1,5 +0,0 @@
sonic-host-services-data (1.0-1) UNRELEASED; urgency=low
* Initial release
-- Joe LeVeque <jolevequ@microsoft.com> Tue, 20 Oct 2020 02:35:43 +0000

View File

@ -1 +0,0 @@
11

View File

@ -1,11 +0,0 @@
Source: sonic-host-services-data
Maintainer: Joe LeVeque <jolevequ@microsoft.com>
Section: misc
Priority: optional
Standards-Version: 0.1
Build-Depends: debhelper (>=11)
Package: sonic-host-services-data
Architecture: all
Depends: ${misc:Depends}
Description: Data files required for SONiC host services

View File

@ -1,2 +0,0 @@
templates/*.j2 /usr/share/sonic/templates/
org.sonic.hostservice.conf /etc/dbus-1/system.d

View File

@ -1,24 +0,0 @@
#!/usr/bin/make -f
ifeq (${ENABLE_HOST_SERVICE_ON_START}, y)
HOST_SERVICE_OPTS := --no-start
else
HOST_SERVICE_OPTS := --no-start --no-enable
endif
build:
%:
dh $@
override_dh_installsystemd:
dh_installsystemd --no-start --name=caclmgrd
dh_installsystemd --no-start --name=hostcfgd
dh_installsystemd --no-start --name=featured
dh_installsystemd --no-start --name=aaastatsd
dh_installsystemd --no-start --name=procdockerstatsd
dh_installsystemd --no-start --name=determine-reboot-cause
dh_installsystemd --no-start --name=process-reboot-cause
dh_installsystemd $(HOST_SERVICE_OPTS) --name=sonic-hostservice

View File

@ -1,14 +0,0 @@
[Unit]
Description=AAA Statistics Collection daemon
Requires=hostcfgd.service
After=hostcfgd.service updategraph.service
BindsTo=sonic.target
After=sonic.target
[Service]
Type=simple
ExecStart=/usr/local/bin/aaastatsd
Restart=on-failure
RestartSec=10
TimeoutStopSec=3

View File

@ -1,12 +0,0 @@
[Unit]
Description=Delays aaastatsd daemon until SONiC has started
PartOf=aaastatsd.service
[Timer]
OnUnitActiveSec=0 sec
OnBootSec=1min 30 sec
Unit=aaastatsd.service
[Install]
WantedBy=timers.target sonic.target

View File

@ -1,15 +0,0 @@
[Unit]
Description=Control Plane ACL configuration daemon
Requires=updategraph.service
After=updategraph.service
BindsTo=sonic.target
After=sonic.target
[Service]
Type=simple
ExecStart=/usr/local/bin/caclmgrd
Restart=always
RestartSec=30
[Install]
WantedBy=sonic.target

View File

@ -1,12 +0,0 @@
[Unit]
Description=Reboot cause determination service
Requires=rc-local.service database.service
After=rc-local.service database.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/bin/determine-reboot-cause
[Install]
WantedBy=multi-user.target

View File

@ -1,10 +0,0 @@
[Unit]
Description=Feature configuration daemon
Requires=updategraph.service
After=updategraph.service
BindsTo=sonic.target
After=sonic.target
[Service]
Type=simple
ExecStart=/usr/local/bin/featured

View File

@ -1,12 +0,0 @@
[Unit]
Description=Delays feature daemon until SONiC has started
PartOf=featured.service
[Timer]
OnUnitActiveSec=0 sec
OnBootSec=1min 30 sec
Unit=featured.service
[Install]
WantedBy=timers.target sonic.target

View File

@ -1,11 +0,0 @@
[Unit]
Description=Host config enforcer daemon
Requires=updategraph.service
After=updategraph.service
BindsTo=sonic.target
After=sonic.target
[Service]
Type=simple
ExecStart=/usr/local/bin/hostcfgd

View File

@ -1,12 +0,0 @@
[Unit]
Description=Delays hostcfgd daemon until SONiC has started
PartOf=hostcfgd.service
[Timer]
OnUnitActiveSec=0 sec
OnBootSec=1min 30 sec
Unit=hostcfgd.service
[Install]
WantedBy=timers.target sonic.target

View File

@ -1,14 +0,0 @@
[Unit]
Description=Process and docker CPU/memory utilization data export daemon
Requires=database.service updategraph.service
After=database.service updategraph.service
BindsTo=sonic.target
After=sonic.target
[Service]
Type=simple
ExecStart=/usr/local/bin/procdockerstatsd
Restart=always
[Install]
WantedBy=sonic.target

View File

@ -1,8 +0,0 @@
[Unit]
Description=Retrieve the reboot cause from the history files and save them to StateDB
Requires=database.service determine-reboot-cause.service
After=database.service determine-reboot-cause.service
[Service]
Type=simple
ExecStart=/usr/local/bin/process-reboot-cause

View File

@ -1,9 +0,0 @@
[Unit]
Description=Delays process-reboot-cause until network is stably connected
[Timer]
OnBootSec=1min 30 sec
Unit=process-reboot-cause.service
[Install]
WantedBy=timers.target

View File

@ -1,16 +0,0 @@
[Unit]
Description=SONiC Host Service
[Service]
Type=dbus
BusName=org.SONiC.HostService
ExecStart=/usr/bin/python3 -u /usr/local/bin/sonic-host-server
Restart=on-failure
RestartSec=10
TimeoutStopSec=3
[Install]
WantedBy=mgmt-framework.service telemetry.service

View File

@ -1,18 +0,0 @@
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Only root can own the bus -->
<policy user="root">
<allow own_prefix="org.SONiC.HostService"/>
</policy>
<!-- Allow user "root" to invoke methods on the bus -->
<policy user="root">
<allow send_destination="org.SONiC.HostService"/>
<allow receive_sender="org.SONiC.HostService"/>
</policy>
</busconfig>

View File

@ -1,83 +0,0 @@
#THIS IS AN AUTO-GENERATED FILE
#
# /etc/pam.d/common-auth- authentication settings common to all services
# This file is included from other service-specific PAM config files,
# and should contain a list of the authentication modules that define
# the central authentication scheme for use on the system
# (e.g., /etc/shadow, LDAP, Kerberos, etc.). The default is to use the
# traditional Unix authentication mechanisms.
#
# here are the per-package modules (the "Primary" block)
{% if auth['login'] == 'local' %}
auth [success=1 default=ignore] pam_unix.so nullok try_first_pass
{% elif auth['login'] == 'local,tacacs+' %}
auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not auth['failthrough'] }}] pam_unix.so nullok try_first_pass
{% for server in servers | sub(0, -1) %}
auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not auth['failthrough'] }}] pam_tacplus.so server={{ server.ip }}:{{ server.tcp_port }} secret={{ server.passkey }} login={{ server.auth_type }} timeout={{ server.timeout }} {% if server.vrf %} vrf={{ server.vrf }} {% endif %} {{ 'source_ip=%s' % src_ip if src_ip }} try_first_pass
{% endfor %}
{% if servers | count %}
{% set last_server = servers | last %}
auth [success=1 default=ignore] pam_tacplus.so server={{ last_server.ip }}:{{ last_server.tcp_port }} secret={{ last_server.passkey }} login={{ last_server.auth_type }} timeout={{ last_server.timeout }} {% if last_server.vrf %} vrf={{ last_server.vrf }} {% endif %} {{ 'source_ip=%s' % src_ip if src_ip }} try_first_pass
{% endif %}
{% elif auth['login'] == 'tacacs+' or auth['login'] == 'tacacs+,local' %}
{% for server in servers %}
auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not auth['failthrough'] }}] pam_tacplus.so server={{ server.ip }}:{{ server.tcp_port }} secret={{ server.passkey }} login={{ server.auth_type }} timeout={{ server.timeout }} {%if server.vrf %} vrf={{ server.vrf }} {% endif %} {{ 'source_ip=%s' % src_ip if src_ip }} try_first_pass
{% endfor %}
auth [success=1 default=ignore] pam_unix.so nullok try_first_pass
{% elif auth['login'] == 'local,radius' %}
auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die maxtries=die' if not auth['failthrough'] }}] pam_unix.so nullok try_first_pass
# For the RADIUS servers, on success jump to the cacheing the MPL(Privilege)
{% for server in servers %}
auth [success={{ (servers | count) - loop.index0 }} new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not auth['failthrough'] }}] pam_radius_auth.so conf=/etc/pam_radius_auth.d/{{ server.ip }}_{{ server.auth_port }}.conf privilege_level protocol={{ server.auth_type }} retry={{ server.retransmit }}{% if server.nas_ip is defined %} nas_ip_address={{ server.nas_ip }}{% endif %}{% if server.nas_id is defined %} client_id={{ server.nas_id }}{% endif %}{% if debug %} debug{% endif %}{% if trace %} trace{% endif %}{% if server.statistics %} statistics={{ server.ip }}{% endif %} try_first_pass
{% endfor %}
auth requisite pam_deny.so
# Cache MPL(Privilege)
auth [success=1 default=ignore] pam_exec.so /usr/sbin/cache_radius
{% elif auth['login'] == 'radius,local' %}
# root user can only be authenticated locally. Jump to local.
{% if servers | count %}
auth [success={{ (servers | count) }} default=ignore] pam_succeed_if.so user = root
{% else %}
auth [success=ok default=ignore] pam_succeed_if.so user = root
{% endif %}
# For the RADIUS servers, on success jump to the cache the MPL(Privilege)
{% for server in servers %}
auth [success={{ (servers | count) + 1 - loop.index0 }} new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not auth['failthrough'] }}] pam_radius_auth.so conf=/etc/pam_radius_auth.d/{{ server.ip }}_{{ server.auth_port }}.conf privilege_level protocol={{ server.auth_type }} retry={{ server.retransmit }}{% if server.nas_ip is defined %} nas_ip_address={{ server.nas_ip }}{% endif %}{% if server.nas_id is defined %} client_id={{ server.nas_id }}{% endif %}{% if debug %} debug{% endif %}{% if trace %} trace{% endif %}{% if server.statistics %} statistics={{ server.ip }}{% endif %} try_first_pass
{% endfor %}
# Local
auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die maxtries=die' if not auth['failthrough'] }}] pam_unix.so nullok try_first_pass
auth requisite pam_deny.so
# Cache MPL(Privilege)
auth [success=1 default=ignore] pam_exec.so /usr/sbin/cache_radius
{% elif auth['login'] == 'radius' %}
# root user can only be authenticated locally. Jump to local.
auth [success={{ (servers | count) + 2 }} default=ignore] pam_succeed_if.so user = root
# For the RADIUS servers, on success jump to the cache the MPL(Privilege)
{% for server in servers %}
auth [success={{ (servers | count) - loop.index0 }} new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not auth['failthrough'] }}] pam_radius_auth.so conf=/etc/pam_radius_auth.d/{{ server.ip }}_{{ server.auth_port }}.conf privilege_level protocol={{ server.auth_type }} retry={{ server.retransmit }}{% if server.nas_ip is defined %} nas_ip_address={{ server.nas_ip }}{% endif %}{% if server.nas_id is defined %} client_id={{ server.nas_id }}{% endif %}{% if debug %} debug{% endif %}{% if trace %} trace{% endif %}{% if server.statistics %} statistics={{ server.ip }}{% endif %} try_first_pass
{% endfor %}
auth requisite pam_deny.so
# Cache MPL(Privilege)
auth [success=2 default=ignore] pam_exec.so /usr/sbin/cache_radius
# Local
auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die maxtries=die' if not auth['failthrough'] }}] pam_unix.so nullok try_first_pass
{% else %}
auth [success=1 default=ignore] pam_unix.so nullok try_first_pass
{% endif %}
#
# here's the fallback if no module succeeds
auth requisite pam_deny.so
# prime the stack with a positive return value if there isn't one already;
# this avoids us returning an error just because nothing sets a success code
# since the modules above will each just jump around
auth required pam_permit.so
# and here are more per-package modules (the "Additional" block)

View File

@ -1,43 +0,0 @@
#THIS IS AN AUTO-GENERATED FILE
#
# /etc/pam.d/common-password - password-related modules common to all services
#
# This file is included from other service-specific PAM config files,
# and should contain a list of modules that define the services to be
# used to change user passwords. The default is pam_unix.
# Explanation of pam_unix options:
# The "yescrypt" option enables
#hashed passwords using the yescrypt algorithm, introduced in Debian
#11. Without this option, the default is Unix crypt. Prior releases
#used the option "sha512"; if a shadow password hash will be shared
#between Debian 11 and older releases replace "yescrypt" with "sha512"
#for compatibility . The "obscure" option replaces the old
#`OBSCURE_CHECKS_ENAB' option in login.defs. See the pam_unix manpage
#for other options.
# As of pam 1.0.1-6, this file is managed by pam-auth-update by default.
# To take advantage of this, it is recommended that you configure any
# local modules either before or after the default block, and use
# pam-auth-update to manage selection of other modules. See
# pam-auth-update(8) for details.
# here are the per-package modules (the "Primary" block)
{% if passw_policies %}
{% if passw_policies['state'] == 'enabled' %}
password requisite pam_cracklib.so retry=3 maxrepeat=0 {% if passw_policies['len_min'] %}minlen={{passw_policies['len_min']}}{% endif %} {% if passw_policies['upper_class'] %}ucredit=-1{% else %}ucredit=0{% endif %} {% if passw_policies['lower_class'] %}lcredit=-1{% else %}lcredit=0{% endif %} {% if passw_policies['digits_class'] %}dcredit=-1{% else %}dcredit=0{% endif %} {% if passw_policies['special_class'] %}ocredit=-1{% else %}ocredit=0{% endif %} {% if passw_policies['reject_user_passw_match'] %}reject_username{% endif %} enforce_for_root
password required pam_pwhistory.so {% if passw_policies['history_cnt'] %}remember={{passw_policies['history_cnt']}}{% endif %} use_authtok enforce_for_root
{% endif %}
{% endif %}
password [success=1 default=ignore] pam_unix.so obscure yescrypt
# here's the fallback if no module succeeds
password requisite pam_deny.so
# prime the stack with a positive return value if there isn't one already;
# this avoids us returning an error just because nothing sets a success code
# since the modules above will each just jump around
password required pam_permit.so
# and here are more per-package modules (the "Additional" block)
# end of pam-auth-update config

View File

@ -1,69 +0,0 @@
# /etc/security/limits.conf
#
# This file generate by j2 template file: src/sonic-host-services-data/templates/limits.conf.j2
#
# Each line describes a limit for a user in the form:
#
# <domain> <type> <item> <value>
#
# Where:
# <domain> can be:
# - a user name
# - a group name, with @group syntax
# - the wildcard *, for default entry
# - the wildcard %, can be also used with %group syntax,
# for maxlogin limit
# - NOTE: group and wildcard limits are not applied to root.
# To apply a limit to the root user, <domain> must be
# the literal username root.
#
# <type> can have the two values:
# - "soft" for enforcing the soft limits
# - "hard" for enforcing hard limits
#
# <item> can be one of the following:
# - core - limits the core file size (KB)
# - data - max data size (KB)
# - fsize - maximum filesize (KB)
# - memlock - max locked-in-memory address space (KB)
# - nofile - max number of open file descriptors
# - rss - max resident set size (KB)
# - stack - max stack size (KB)
# - cpu - max CPU time (MIN)
# - nproc - max number of processes
# - as - address space limit (KB)
# - maxlogins - max number of logins for this user
# - maxsyslogins - max number of logins on the system
# - priority - the priority to run user process with
# - locks - max number of file locks the user can hold
# - sigpending - max number of pending signals
# - msgqueue - max memory used by POSIX message queues (bytes)
# - nice - max nice priority allowed to raise to values: [-20, 19]
# - rtprio - max realtime priority
# - chroot - change root to directory (Debian-specific)
#
#
# <value> is related with <item>:
# All items support the values -1, unlimited or infinity indicating
# no limit, except for priority and nice.
#
# If a hard limit or soft limit of a resource is set to a valid value,
# but outside of the supported range of the local system, the system
# may reject the new limit or unexpected behavior may occur. If the
# control value required is used, the module will reject the login if
# a limit could not be set.
#
# <domain> <type> <item> <value>
#
# * soft core 0
# root hard core 100000
# * hard rss 10000
# @student hard nproc 20
# @faculty soft nproc 20
# @faculty hard nproc 50
# ftp hard nproc 0
# ftp - chroot /ftp
# @student - maxlogins 4
# End of file

View File

@ -1,12 +0,0 @@
#THIS IS AN AUTO-GENERATED FILE
#
# This file generate by j2 template file: src/sonic-host-services-data/templates/pam_limits.j2
#
# /etc/pam.d/pam-limits settings common to all services
# This file is included from other service-specific PAM config files,
# and should contain a list of the authentication modules that define
# the central authentication scheme for use on the system
# (e.g., /etc/shadow, LDAP, Kerberos, etc.). The default is to use the
# traditional Unix authentication mechanisms.
#
# here are the per-package modules (the "Primary" block)

View File

@ -1,3 +0,0 @@
# server[:port] shared_secret timeout(s) source_ip vrf
[{{ server.ip }}]:{{ server.auth_port }} {{ server.passkey }} {{ server.timeout }} {% if server.src_ip %} {{ server.src_ip }} {% endif %} {% if server.vrf %} {% if not server.src_ip %} - {% endif %} {{ server.vrf }}{% endif %}

View File

@ -1,58 +0,0 @@
#THIS IS AN AUTO-GENERATED FILE
# Generated from: /usr/share/sonic/templates/radius_nss.conf.j2
# RADIUS NSS Configuration File
#
# Debug: on|off|trace
# Default: off
#
# debug=on
{% if debug %}
debug=on
{% endif %}
#
# User Privilege:
# Default:
# user_priv=15;pw_info=remote_user_su;gid=1000;group=sudo,docker;shell=/usr/bin/sonic-launch-shell
# user_priv=1;pw_info=remote_user;gid=999;group=docker;shell=/usr/bin/sonic-launch-shell
# Eg:
# user_priv=15;pw_info=remote_user_su;gid=1000;group=sudo,docker;shell=/usr/bin/sonic-launch-shell
# user_priv=7;pw_info=netops;gid=999;group=docker;shell=/usr/bin/sonic-launch-shell
# user_priv=1;pw_info=operator;gid=100;group=docker;shell=/usr/bin/sonic-launch-shell
#
# many_to_one:
# y: Map RADIUS users to one local user per privilege.
# n: Create local user account on first successful authentication.
# Default: n
#
# Eg:
# many_to_one=y
#
# unconfirmed_disallow:
# y: Do not allow unconfirmed users (users created before authentication)
# n: Allow unconfirmed users.
# Default: n
# Eg:
# unconfirmed_disallow=y
#
# unconfirmed_ageout:
# <seconds>: Wait time before purging unconfirmed users
# Default: 600
#
# Eg:
# unconfirmed_ageout=900
#
# unconfirmed_regexp:
# <regexp>: The RE to match the command line of processes for which the
# creation of unconfirmed users are to be allowed.
# Default: (.*: <user> \[priv\])|(.*: \[accepted\])
# where: <user> is the unconfirmed user.
#

View File

@ -1,60 +0,0 @@
# Configuration for libnss-tacplus
# debug - If you want to open debug log, set it on
# Default: off
# debug=on
{% if debug %}
debug=on
{% endif %}
# local_accounting - If you want to local accounting, set it
# Default: None
# local_accounting
{% if local_accounting %}
local_accounting
{% endif %}
# tacacs_accounting - If you want to tacacs+ accounting, set it
# Default: None
# tacacs_accounting
{% if tacacs_accounting %}
tacacs_accounting
{% endif %}
# local_authorization - If you want to local authorization, set it
# Default: None
# local_authorization
{% if local_authorization %}
local_authorization
{% endif %}
# tacacs_authorization - If you want to tacacs+ authorization, set it
# Default: None
# tacacs_authorization
{% if tacacs_authorization %}
tacacs_authorization
{% endif %}
# src_ip - set source address of TACACS+ protocol packets
# Default: None (auto source ip address)
# src_ip=2.2.2.2
{% if src_ip %}
src_ip={{ src_ip }}
{% endif %}
# server - set ip address, tcp port, secret string and timeout for TACACS+ servers
# Default: None (no TACACS+ server)
# server=1.1.1.1:49,secret=test,timeout=3
{% for server in servers %}
server={{ server.ip }}:{{ server.tcp_port }},secret={{ server.passkey }},timeout={{ server.timeout }}{% if server.vrf %},vrf={{ server.vrf }}{% endif %}{{''}}
{% endfor %}
# user_priv - set the map between TACACS+ user privilege and local user's passwd
# Default:
# user_priv=15;pw_info=remote_user_su;gid=1000;group=sudo,docker;shell=/bin/bash
# user_priv=1;pw_info=remote_user;gid=999;group=docker;shell=/bin/bash
# many_to_one - create one local user for many TACACS+ users which has the same privilege
# Default: many_to_one=n
# many_to_one=y