def test_run_cmd_silent(docker_env, caplog): caplog.set_level(logging.DEBUG) gateway_ip, gateway_port = docker_env.get_host_ip_port('gateway') gateway_session = SSHSession(host=gateway_ip, port=gateway_port, username='******', password='******').open() # run command and check full command is logged text = 'text with public and private data' cmd = "echo '%s'" % text assert gateway_session.run_cmd(cmd).output == text assert cmd in caplog.text # check nothing is logged when silent is True text = 'another text with public and private data' cmd = "echo '%s'" % text assert gateway_session.run_cmd(cmd, silent=True).output == text assert cmd not in caplog.text # check data is concealed when silent is a list text = 'a third text with public and private data' cmd = "echo '%s'" % text assert gateway_session.run_cmd(cmd, silent=['third', 'private data']).output == text assert cmd not in caplog.text assert 'a XXXXXXX text with public and XXXXXXX' in caplog.text # check data is concealed when silent is a list with regexp text = 'another text to test regexp' cmd = "echo '%s'" % text assert gateway_session.run_cmd(cmd, silent=['\s+']).output == text assert cmd not in caplog.text assert 'anotherXXXXXXXtextXXXXXXXtoXXXXXXXtestXXXXXXXregexp' in caplog.text
def test_ssh_connection_error(docker_env): gateway_ip, gateway_port = docker_env.get_host_ip_port('gateway') # open first ssh session to gateway gateway_session1 = SSHSession(host=gateway_ip, port=gateway_port, username='******', password='******').open() # modify password from session 1 gateway_session1.run_cmd('echo "user1:newpassword" | sudo -S chpasswd') # try to open 2nd session with pytest.raises(exception.ConnectionError) as excinfo: SSHSession(host=gateway_ip, port=gateway_port, username='******', password='******').open() assert type(excinfo.value.__cause__ ) == paramiko.ssh_exception.AuthenticationException # set back correct password from session 1 gateway_session1.run_cmd('echo "user1:password1" | sudo -S chpasswd') # try again to open 2nd session gateway_session2 = SSHSession(host=gateway_ip, port=gateway_port, username='******', password='******').open() assert gateway_session2.is_active()
def test_active_close_session(docker_env): gateway_ip, gateway_port = docker_env.get_host_ip_port('gateway') gateway_session = SSHSession(host=gateway_ip, port=gateway_port, username='******', password='******').open() assert gateway_session.is_active() # open an already active session should be harmless gateway_session.open() assert gateway_session.is_active() remotehost_ip, remotehost_port = docker_env.get_host_ip_port('remotehost') remotehost_session = gateway_session.get_remote_session( host=tests_util.get_host_ip(), port=remotehost_port, username='******', password='******') assert remotehost_session.is_active() # check that gateway session is well closed gateway_session.close() assert not gateway_session.is_active() # remote session is also automatically closed assert not remotehost_session.is_active() # closing a closed session does nothing gateway_session.close() # running command on an inactive session raise a SSHException with pytest.raises(exception.SSHException): gateway_session.run_cmd('ls')
def create_new_cert_on_host(host_session: SSHSession, certificate_file: CertificateFile, ca_cert: str, ca_key: str): try: ca_cert_file_path = '/tmp/ca.cert.pem' ca_key_file_path = '/tmp/ca.key.pem' host_session.run_cmd(f'echo "{ca_cert}" > {ca_cert_file_path}; echo "{ca_key}" > {ca_key_file_path} ') host_session.run_cmd(f'openssl x509 -in {ca_cert_file_path} -out {certificate_file.file_path}.new -days 365 \ -signkey {ca_key_file_path} -sha256') except Exception as e: print("Could not create new certificate file") raise e
def test_exists(docker_env): gateway_ip, gateway_port = docker_env.get_host_ip_port('gateway') gateway_session = SSHSession(host=gateway_ip, port=gateway_port, username='******', password='******').open() assert not gateway_session.exists('/home/user1/non_existing_file') gateway_session.run_cmd('touch /home/user1/existing_file') assert gateway_session.exists('/home/user1/existing_file') gateway_session.run_cmd('rm /home/user1/existing_file') assert not gateway_session.exists('/home/user1/existing_file') # create file visible only by user2 gateway_session.run_cmd([ 'sudo mkdir /etc/user2_private_dir', 'sudo touch /etc/user2_private_dir/existing_file', 'sudo chown user2:user2 /etc/user2_private_dir', 'sudo chmod 600 /etc/user2_private_dir' ]) # check it is not visible by user1 by default assert not gateway_session.exists('/etc/user2_private_dir/existing_file') # check it is readable with root access assert gateway_session.exists('/etc/user2_private_dir/existing_file', use_sudo=True) # cleanup gateway_session.run_cmd('sudo rm -rf /etc/user2_private_dir')
def test_active_close_session(docker_env): gateway_ip, gateway_port = docker_env.get_host_ip_port() gateway_session = SSHSession(host=gateway_ip, port=gateway_port, username='******', password='******').open() assert gateway_session.is_active() # open an already active session should be harmless gateway_session.open() assert gateway_session.is_active() remotehost_session = gateway_session.get_remote_session( host='remotehost', port=22, username='******', password='******') assert remotehost_session.is_active() # check that gateway session is well closed gateway_session.close() assert not gateway_session.is_active() # remote session is also automatically closed assert not remotehost_session.is_active() # closing a closed session does nothing gateway_session.close() # running command on an inactive session will automatically open the session assert gateway_session.run_cmd('ls').exit_code == 0
def test_run_cmd_sudo(docker_env): gateway_ip, gateway_port = docker_env.get_host_ip_port('gateway') gateway_session = SSHSession(host=gateway_ip, port=gateway_port, username='******', password='******').open() # run command as user2 assert gateway_session.run_cmd('whoami', username='******').output == 'user2' # run bash builtins commands with sudo (here command 'source') gateway_session.file(remote_path='/home/user2/ssh_setenv', use_sudo=True, owner='user2', content='MY_VAR=variable_set') gateway_session.run_cmd('source /home/user2/ssh_setenv', username='******')
def test_input_data(docker_env): gateway_ip, gateway_port = docker_env.get_host_ip_port('gateway') gateway_session = SSHSession(host=gateway_ip, port=gateway_port, username='******', password='******').open() commands = [ 'read -p "Requesting user input value?" my_var', 'echo $my_var' ] # without input given, command will hang until timeout is reached with pytest.raises(exception.TimeoutError): gateway_session.run_cmd(commands, timeout=5) # with input given, command should run correctly and return the value entered assert gateway_session.get_cmd_output(commands, input_data={ 'Requesting user input value': 'dummy_value' }).split()[-1] == "dummy_value"
def respaldo_ssh(hostname, ip, cisco_os, username, password, enable_password): try: ruta = os.getcwd() with open( ruta + "/full backup manual/respaldo_" + hostname + "_" + datetime.now().strftime("%d%m%Y_%H%M"), "w") as file: if cisco_os == "nxos": ssh_session = SSHSession(ip, username, password=password).open() for comando in commands_nxos: resultado = ssh_session.run_cmd(comando) file.write(resultado.output) ssh_session.close() elif cisco_os == "iosxe": ssh_session = SSHSession(ip, username, password=password).open() for comando in commands_iosxe: resultado = ssh_session.run_cmd(comando) file.write(resultado.output) ssh_session.close() elif cisco_os == "junos": ssh_session = SSHSession(ip, username, password=password, timeout=10).open() for comando in commands_junos: resultado = ssh_session.run_cmd(comando) file.write(resultado.output) ssh_session.close() except Exception as e: print(e) return ("fallido") return ("realizado")
def test_run_cmd_success_exit_code(docker_env): gateway_ip, gateway_port = docker_env.get_host_ip_port('gateway') gateway_session = SSHSession(host=gateway_ip, port=gateway_port, username='******', password='******').open() # check invalid type for parameter with pytest.raises(TypeError): gateway_session.run_cmd('hostname', success_exit_code={'key': 'value'}) # valid command with custom success exit code should raise RunCmdError with pytest.raises(exception.RunCmdError) as exc_info: gateway_session.run_cmd('hostname', success_exit_code=3) assert exc_info.value.exit_code == 0 assert exc_info.value.success_exit_code == [3] # dummy command should not raise error as exit code 127 is valid too # and test also that list is supported gateway_session.run_cmd('dummy commmand', success_exit_code=[0, 127]) gateway_session.run_cmd('hostname', success_exit_code=[0, 127])
def test_run_cmd_retry(docker_env): gateway_ip, gateway_port = docker_env.get_host_ip_port('gateway') gateway_session = SSHSession(host=gateway_ip, port=gateway_port, username='******', password='******').open() with pytest.raises(exception.RunCmdError) as exc_info: gateway_session.run_cmd('dummy commmand', retry=2, retry_interval=1) assert exc_info.value.runs_nb == 3 # prepare command that append a character in file at each new run temporary_filename1 = util.id_generator(size=7) cmd = "echo -n 'p' >> {0} && grep 'pppp' {0}".format(temporary_filename1) # command should still raise exception after 2 retries(=3 runs) as we except 4 p with pytest.raises(exception.RunCmdError) as exc_info: gateway_session.run_cmd(cmd, retry=2, retry_interval=1) assert exc_info.value.runs_nb == 3 # same command should work fine with 3 retries(=4 runs) temporary_filename2 = util.id_generator(size=8) cmd = cmd.replace(temporary_filename1, temporary_filename2) result = gateway_session.run_cmd(cmd, retry=3, retry_interval=1) # by default no history kept assert len(result.result_list) == 0 # check history is kept when requested temporary_filename3 = util.id_generator(size=8) cmd = cmd.replace(temporary_filename2, temporary_filename3) result = gateway_session.run_cmd(cmd, retry=3, retry_interval=1, keep_retry_history=True) assert len(result.result_list) == 4
def test_run_cmd_interrupt_remote_command(docker_env, monkeypatch, caplog): caplog.set_level(logging.DEBUG) """Test behavior of run_cmd when user hit Contrl-C while a command is being executed remotely""" gateway_ip, gateway_port = docker_env.get_host_ip_port('gateway') gateway_session = SSHSession(host=gateway_ip, port=gateway_port, username='******', password='******').open() mock_input = '__builtin__.raw_input' if util.PY2 else 'builtins.input' # 1. if user request to interrupt remote command with pytest.raises(KeyboardInterrupt): # raise KeyboardInterrupt while command is running with mock.patch('select.select', side_effect=KeyboardInterrupt('Fake Ctrl-C')): # request to terminate remote command simulating the user entering "Y" in the terminal monkeypatch.setattr(mock_input, lambda x: "Y") gateway_session.run_cmd('sleep 30') # check command is no longer running on remote host assert gateway_session.get_exit_code( 'ps aux | grep -v grep | grep "sleep 30"') == 1 # 2. user request to NOT interrupt remote command with pytest.raises(KeyboardInterrupt): # raise KeyboardInterrupt while command is running with mock.patch('select.select', side_effect=KeyboardInterrupt('Fake Ctrl-C')): # request to terminate remote command simulating the user entering "N" in the terminal monkeypatch.setattr(mock_input, lambda x: "N") gateway_session.run_cmd('sleep 40') # check command is still running on remote host assert gateway_session.get_exit_code( 'ps aux | grep -v grep | grep "sleep 40"') == 0 # 3. user press enter (default value of util.yes_no_query used), we expect remote command to be stopped with pytest.raises(KeyboardInterrupt): # raise KeyboardInterrupt while command is running with mock.patch('select.select', side_effect=KeyboardInterrupt('Fake Ctrl-C')): # send empty string simulating the user pressing enter in the terminal monkeypatch.setattr(mock_input, lambda x: '') gateway_session.run_cmd('sleep 50') # check command is no longer running on remote host assert gateway_session.get_exit_code( 'ps aux | grep -v grep | grep "sleep 50"') == 1 # 4. user press Contrl-C twice, check remote command is still running with pytest.raises(KeyboardInterrupt): # raise KeyboardInterrupt while command is running with mock.patch('select.select', side_effect=KeyboardInterrupt('Fake Ctrl-C')): # user press a second time Contrl-C with mock.patch(mock_input, side_effect=KeyboardInterrupt('2nd Fake Ctrl-C')): gateway_session.run_cmd('sleep 60') # check command is still running on remote host assert gateway_session.get_exit_code( 'ps aux | grep -v grep | grep "sleep 60"') == 0 # 5. user press Contrl-C once but take time to answer if remote must be closed or not, and channel is closed # so we cannot terminate remote command but remote command finished its execution with pytest.raises(KeyboardInterrupt): # raise KeyboardInterrupt while command is running with mock.patch('select.select', side_effect=KeyboardInterrupt('Fake Ctrl-C')): # request to terminate remote command simulating the user entering "Y" in the terminal # but user answered after 4s while command finished after 3s so underline channel is already closed # and command still successfully run monkeypatch.setattr(mock_input, lambda x: time.sleep(4) or "Y") gateway_session.run_cmd('sleep 3') assert 'Remote command execution already finished with exit code' in caplog.text # 6. user press Contrl-C once but take time to answer if remote must be closed or not, and channel is closed # so we cannot terminate remote command and channel does't not have more information about remote command execution with pytest.raises(KeyboardInterrupt): # raise KeyboardInterrupt while command is running with mock.patch('select.select', side_effect=KeyboardInterrupt('Fake Ctrl-C')): # request to terminate remote command simulating the user entering "Y" in the terminal # but user answered after 4s while command finished after 3s so underline channel is already closed monkeypatch.setattr('paramiko.channel.Channel.recv_exit_status', lambda x: -1) gateway_session.run_cmd('sleep 3') assert 'Unable to terminate remote command because channel is closed.' in caplog.text
def test_run_cmd(docker_env, capfd): gateway_ip, gateway_port = docker_env.get_host_ip_port('gateway') gateway_session = SSHSession(host=gateway_ip, port=gateway_port, username='******', password='******').open() assert gateway_session.is_active() # basic successful command (exit_code, output) = gateway_session.run_cmd('hostname') assert exit_code == 0 assert output == 'gateway.example.com' # successful list command gateway_session.run_cmd(['cd /etc', 'ls']) # wrong command (exit_code, output) = gateway_session.run_cmd('dummy commmand', raise_if_error=False) assert exit_code == 127 with pytest.raises(exception.RunCmdError) as excinfo: gateway_session.run_cmd('dummy commmand') assert excinfo.value.exit_code == 127 assert excinfo.value.command == 'dummy commmand' # wrong command type with pytest.raises(TypeError): gateway_session.run_cmd({'key': 'value'}) # standard output is empty by default (without continuous_output flag) gateway_session.run_cmd('ls -lta /') out, err = capfd.readouterr() assert len(out) == 0 # display continuous output on stdout while command is running gateway_session.run_cmd('ls -lta /', continuous_output=True) out, err = capfd.readouterr() assert len(out) > 0 assert isinstance( out, unicode if util.PY2 else str) # noqa: unicode only exists in python 2
def respaldo_ssh_manual(hostname, ip, cisco_os, username, password, enable_password): try: ruta = os.getcwd() with open( ruta + "/full backup manual/respaldo_" + hostname + "_" + datetime.now().strftime("%d%m%Y_%H%M"), "w") as file: if cisco_os == "nxos": nx_node = { 'device_type': 'cisco_nxos', 'ip': ip, 'username': username, 'password': password, 'secret': enable_password, 'port': 22, } ssh_session = SSHSession(ip, username, password=password).open() for comando in commands_nxos: resultado = ssh_session.run_cmd(comando) file.write(resultado.output) ssh_session.close() elif cisco_os == "iosxe": ssh_session = SSHSession(ip, username, password=password).open() for comando in commands_iosxe: resultado = ssh_session.run_cmd(comando) file.write(resultado.output) ssh_session.close() elif cisco_os == "junos": ssh_session = SSHSession(ip, username, password=password, timeout=10).open() for comando in commands_junos: resultado = ssh_session.run_cmd(comando) file.write(resultado.output) ssh_session.close() elif cisco_os == "ios": ios_node = { 'device_type': 'cisco_ios', 'ip': ip, 'username': username, 'password': password, 'secret': enable_password, 'port': 22, } net_connect_ios = ConnectHandler(**ios_node) net_connect_ios.enable() for comando in commands_ios: resultado = net_connect_ios.send_command( comando, strip_prompt=False, strip_command=False) file.write(resultado) elif cisco_os == "asa": asa_node = { 'device_type': 'cisco_asa', 'ip': ip, 'username': username, 'password': password, 'secret': enable_password, 'port': 22, } net_connect_asa = ConnectHandler(**asa_node) net_connect_asa.enable() for comando in commands_asa: resultado = net_connect_asa.send_command(comando) file.write(resultado) return ({"resultado": "equipo " + hostname + " OK"}) except Exception as e: return ("Error " + str(e)) return ("realizado")