dmulyalin / salt-nornir

SALTSTACK Nornir based proxy minion, execution, state and runner modules
MIT License
27 stars 3 forks source link

Unable to pass literal "\n" in config using nr.cfg or nr.cfg_gen #20

Closed OliElli closed 2 years ago

OliElli commented 2 years ago

I am trying to render configuration to junos that contains literal newlines (used internally by Junos) but they are rendered as newlines no matter how I try to escape them.

Example of what I want to render to the switch:

system {
    login {
        message "test\nstring\n";
    }
}

I have been testing using nr.cfg_gen like this:

[olielli@saltmaster~]$ sudo salt nornirproxy nr.cfg_gen '{{ "test\nstring\n" }}' FB='ach1-fw-*' split_lines=False
phx1-nornirproxy-01:
    ----------
    ach1-fw-20:
        ----------
        salt_cfg_gen:
            test
            string

Escaping the newline renders the same way as above, ie '{{ "test\\nstring\\n" }}'

Escaping the escape renders an escaped backslash and a newline:

[olielli@saltmaster ~]$ sudo salt nornirproxy nr.cfg_gen '{{ "test\\\nstring\\\n" }}' FB='ach1-fw-*' split_lines=False
phx1-nornirproxy-01:
    ----------
    ach1-fw-20:
        ----------
        salt_cfg_gen:
            test\
            string\

I suspect some point in the salt -> nornir rendering pipeline the escapes are nullified.

dmulyalin commented 2 years ago

Was testing this use case of sending literal \n over SSH to Juniper devices, tried netmiko, scrapli and napalm plugins against vSRX I have in my lab - none of them worked, all of them threat \n as a new line.

Also tried to send config with literal \n using netmiko directly using this script:

from netmiko import ConnectHandler

device = {
    "device_type": "juniper",
    "host": "192.168.1.220",
    "username": "nornir",
    "password": "nornir123",
}

commands = ['set system login message "test\nbanner\nstring\n"']
with ConnectHandler(**device) as net_connect:
    print(net_connect.find_prompt())
    output = net_connect.send_config_set(commands)

print(output)

it did not work either returning timeout exception:

[root@salt-minion-nrp2 ~]# python3 jun_test.py 
nornir@vSRX-1>
Traceback (most recent call last):
  File "/root/jun_test.py", line 13, in <module>
    output = net_connect.send_config_set(commands)
  File "/usr/local/lib/python3.9/site-packages/netmiko/base_connection.py", line 2170, in send_config_set
    output += self.read_until_pattern(pattern=re.escape(cmd.strip()))
  File "/usr/local/lib/python3.9/site-packages/netmiko/base_connection.py", line 651, in read_until_pattern
    raise ReadTimeout(msg)
netmiko.exceptions.ReadTimeout: 

Pattern not detected: 'set\\ system\\ login\\ message\\ "test\\\nbanner\\\nstring\\\n"' in output.

Things you might try to fix this:
1. Adjust the regex pattern to better identify the terminating string. Note, in
many situations the pattern is automatically based on the network device's prompt.
2. Increase the read_timeout to a larger value.

You can also look at the Netmiko session_log or debug log for more information.

What worked for me is using nr.cli with netmiko use_ps function, use_ps is a function that intorduces promptless mode support for netmiko and capable of sending multiline strings over ssh, here is a file with config on salt master:

[root@salt-master pytest]# cat /etc/salt/templates/juniper_jinja_multiline_test.j2 
configure
set system login message "test\nbanner\nstring\n"
set system host-name {{ host.name }}
commit
exit

running above template using this command produces desired results:

[root@salt-master pytest]#  salt nrp2 nr.cli salt://templates/juniper_jinja_multiline_test.j2 FB=vSRX-1 plugin=netmiko use_ps=True
nrp2:
    ----------
    vSRX-1:
        ----------
        configure:
            configure 
            Entering configuration mode
            [edit]

            [edit]
            nornir@vSRX-1# set system login message "test\nbanner\nstring\n" 

            [edit]
            nornir@vSRX-1# set system host-name vSRX-1 

            [edit]
            nornir@vSRX-1# commit 
              commit complete

            [edit]
            nornir@vSRX-1# exit 
            Exiting configuration mode
[root@salt-master pytest]#

Another workaround that I found is to use NETCONF to configure junos devices, e.g. this is RPC content:

[root@salt-master pytest]# cat /etc/salt/rpc/edit_config_junos_multline_banner.xml 
<config>
    <configuration>
    <system>
        <login>
            <message>test\nbanner\nstring\nnew</message>
        </login>
    </system>
</configuration>
</config>

running this command produces desired results as well:

[root@salt-master pytest]# salt nrp2 nr.nc edit_config config="salt://rpc/edit_config_junos_multline_banner.xml" FC=SRX
nrp2:
    ----------
    vSRX-1:
        ----------
        edit_config:
            <rpc-reply message-id="urn:uuid:a645d4f4-7062-4cb0-993e-d692a9484aca">
              <ok/>
            </rpc-reply>
[root@salt-master pytest]# salt nrp2 nr.cli "show configuration system login message | display xml" FC=SRX
nrp2:
    ----------
    vSRX-1:
        ----------
        show configuration system login message | display xml:
            <rpc-reply xmlns:junos="http://xml.juniper.net/junos/22.1R0/junos">
                <configuration junos:commit-seconds="1660518139" junos:commit-localtime="2022-08-14 19:02:19 EDT" junos:commit-user="nornir">
                        <system>
                            <login>
                                <message>test\nbanner\nstring\n</message>
                            </login>
                        </system>
                </configuration>
                <cli>
                    <banner></banner>
                </cli>
            </rpc-reply>
[root@salt-master pytest]# 

So, all in all tested 5 methods to send configuration to device with literal newline, 2 of them worked so far.

Have you been able to apply config like 'set system login message "test\nbanner\nstring\n"' to Junos over SSH CLI by using any other methods?

dmulyalin commented 2 years ago

Regarding "I suspect some point in the salt -> nornir rendering pipeline the escapes are nullified." - this only seems to be true for cases when \n is given within inline command argument, referencing file content instead, does work fine.

I.e. this does not work as expected: salt nornirproxy nr.cfg_gen test\nstring\n while this works fine: salt nornirproxy nr.cfg_gen salt://configs/test.cfg where salt://configs/test.cfg content is: test\nstring\n

dmulyalin commented 2 years ago

Ok, was able to fix the behaviour, now this command

salt nrp2 nr.cfg 'set system login message "test\n\string\n1234"' FM='*jun*' plugin=netmiko

works as expected, will include fix in next release.

dmulyalin commented 2 years ago

Should be addressed in 0.15.0 release

OliElli commented 2 years ago

Confirmed fixed in 0.15.0, thanks @dmulyalin !!