Retrieve output from HP Switch using Net::SSH2 Perl Module - windows

I am trying to connect to HP Switch via SSH using Net::SSH2 Perl module (Windows OS). The script is able to connect and authenticate to the device, but the output is not getting retrieved. I am getting blank output.
I have tried with Net::SSH2 shell and exec both, but no luck till now.
Also tried with Net::SSH::Any, still no result.
Hp Switch Model : HP 5900 - 7.1.045
Code
use strict;
use Net::SSH2;
my $ssh = Net::SSH2->new();
$ssh->connect('xx.xx.xx.xx');
$ssh->auth(username => 'xxxxx', password => 'xxxxxx');
my $channel = $ssh->channel() or die 'Error creating channel';
$channel->blocking(0);
$channel->shell();
print $channel "display supervlan\n";
sleep(5);
while(<$channel>)
{
print $_;
}
$channel->close();
$ssh->disconnect();
Can somebody please help.

Net::SSH::Any can use any of available backend ssh modules (under windows uses Net::SSH2 if available)
use Net::SSH::Any;
my $ssh = Net::SSH::Any->new(
"127.0.0.1",
port => "22",
user => "xx",
password => "xx",
strict_host_key_checking =>0
);
$ssh->error and die $ssh->error;
my ($out, $err) = $ssh->capture2("ls -l /");
print $out;

Related

Include file conditionally based on a bash script

I have a bash command that will return either 1 or 0. I want to run said command from puppet:
exec { 'Check if Thinkpad':
command => 'sudo dmidecode | grep -q ThinkPad && echo 1 || echo 0',
path => '/usr/bin/:/bin/bash/',
environment => "HOME=/root"
}
Is there a way I can include a file using puppet only if my command returned 1?
file { '/etc/i3/config':
source => 'puppet:///modules/i3/thinkpad',
owner => 'root',
group => 'root',
mode => '0644',
}
You can use an external fact to use the bash script as is. Inside the module's facts.d directory, you could place the script.
#!/bin/bash
if [ dmidecode | grep -q ThinkPad ]
echo 'is_thinkpad=true'
else
echo 'is_thinkpad=false'
fi
You can also use a custom fact inside the lib/facter directory of your module.
Facter.add(:is_thinkpad) do
confine kernel: linux
setcode do
`dmidecode | grep -q ThinkPad && echo true || echo false`
end
end
In both cases, the fact name of is_thinkpad follows the convention for the nomenclature of boolean facts for types of systems. You can then update the code in your manifest for this boolean.
if $facts['is_thinkpad'] == true {
file { '/etc/i3/config':
source => 'puppet:///modules/i3/thinkpad',
owner => 'root',
group => 'root',
mode => '0644',
}
}
This will provide you with the functionality you desire.
https://docs.puppet.com/facter/3.6/custom_facts.html#adding-custom-facts-to-facter
https://docs.puppet.com/facter/3.6/custom_facts.html#external-facts
You will probably need to turn your bash script into a "custom fact" -- which is something I've only done once and don't fully understand enough to teach you how.
I want to say that the easiest way to set up a custom fact is to put your script into /etc/facter/facts.d/ on the agent machine, and make sure it ends with a line that says
echo "thinkpadcheck=1"
or
echo "thinkpadcheck=0"
You can test it with (note: you must be root)
sudo facter -p | grep think
and it should return
thinkpadcheck => 1
But once you have done that, then your puppet script can say
if $thinkpadcheck == 1
{
file { '/etc/i3/config':
source => 'puppet:///modules/i3/thinkpad',
owner => 'root',
group => 'root',
mode => '0644',
}
}
else
{
notify { "thinkpadcheck failed for $hostname" : }
}
I'd like to share another method I found in the Puppet Cookbook 3rd edition (page 118):
message.rb
#!/usr/bin/env ruby
puts "This runs on the master if you are centralized"
Make your script executable with:
sudo chmod +x /usr/local/bin/message.rb
message.pp
$message = generate('/usr/local/bin/message.rb')
notify { $message: }
Then run:
puppet apply message.pp
This example uses a ruby script but any type of script including a basic shell script, as was needed in my case, can be used to set a variable in puppet.

wget script to connect, wait if successful, continue if no connection

I have to create a script that sends a wget contact message to all devices on our network. wget connects to a url and this trigger the endpoint to contact a server.
The problem I have is I need to be able to send the command to each IP address on the network and if the connection is successful do noting for 30 seconds then move on to the next url in the list. However if the connection isn't successful I want the script to move on to the next url with no pause.
Currently I'm using a bash script to send the command with a pause=30 in-between url's, connection attempts set to 1 and time-out set to 1. this works OK for the connections that are successful but it also hangs on the addresses that are not.
Any advise on how I can pause on success and move on after time out on dead addresses?
This is the command I'm currently running,
wget --connect-timeout=1 --tries=1 http://xx.xx.xx.xx:8085/contact_dls.html/ContactDLS
sleep 30
wget --connect-timeout=1 --tries=1 http://xx.xx.xx.xx:8085/contact_dls.html/ContactDLS
etc etc etc
thanks
You don't need wget for this task - everything can be done in Perl.
Simply use code like this:
use LWP::UserAgent;
use HTTP::Request;
my $ua = new LWP::UserAgent;
$ua->timeout(1);
my $req = new HTTP::Request(GET => $url);
my $res = $ua->request($req);
if ($res->is_success()) {
print("Connection to '$url' was OK");
sleep(30);
} else {
print("Cannot access '$url'");
sleep(1);
}
This will hit your url, but will timeout in just 1 second.
I would probably load the urls into an array and iterate through the array. Something like this
#!/usr/bin/perl
use warnings;
use strict;
my #urls = qw(test.com url.com 123abc.com fail.aa);
foreach my $url (#urls){
my $check = `wget --server-response $url 2>&1 | grep HTTP/ | awk '{print \$2}'`;
#if there is a successful response you can set the sleep(3) to sleep(30) for your instance.
if($check){
print "Found $url -- sleeping 3 seconds\n";
sleep(3);
}
else{
print "$url does not exist\n";
#If the url does not exist or respond it will move on to the next item in the array. You could add a sleep(1) before the next for a 1 second pause
next;
}
}
Of course this is assuming that you are using linux. The urls could be loaded another way as well, I don't know how your current script it getting them. The above is an example and you of course would need to be adjusted to fit your environment

Not able to connect to socket using socat

I am trying to parse rsyslog logs. For this i am sending all my logs to socat which is then sending them to Unix Domain Socket. That socket is created via perl script which is listening on that socket to parse logs.
My bash script to which rsyslog is sending all log is
if [ ! `pidof -x log_parser.pl` ]
then
./log_parser.pl & 1>&1
fi
if [ -S /tmp/sock ]
then
/usr/bin/socat -t0 -T0 - UNIX-CONNECT:/tmp/sock 2>> /var/log/socat.log
fi
/tmp/sock is created using perl script log_parser.pl which is
use IO::Socket::UNIX;
sub socket_create {
$socket_path = '/tmp/sock';
unlink($socket_path);
$listner = IO::Socket::UNIX->new(
Type => SOCK_STREAM,
Local => $socket_path,
Listen => SOMAXCONN,
Blocking => 0,
)
or die("Can't create server socket: $!\n");
$socket = $listner->accept()
or die("Can't accept connection: $!\n");
}
socket_create();
while(1) {
chomp($line=<$socket>);
print "$line\n";
}
There is this error i am getting from socat which is
2015/02/24 11:58:01 socat[4608] E connect(3, AF=1 "/tmp/sock", 11): Connection refused
I am no champion in sockets so i am not able to understand what is this. Please help. Thanks in advance.
The main issue is that when i kill my perl script then bash script is suppose to call it again and start it.
What actually happening is that sript is started but socat is not started instead it give this error and never start.
I can duplicate your error if I don't run your perl program before trying to use socat. Here is what works for me:
1) my_prog.pl:
use strict;
use warnings;
use 5.016;
use Data::Dumper;
use IO::Socket::UNIX;
my $socket_path = '/tmp/sock';
unlink $socket_path;
my $socket = IO::Socket::UNIX->new(
Local => $socket_path,
Type => SOCK_STREAM,
Listen => SOMAXCONN,
) or die "Couldn't create socket: $!";
say "Connected to $socket_path...";
my $CONN = $socket->accept()
or die "Whoops! Failed to open a connection: $!";
{
local $/ = undef; #local -> restore previous value when the enclosing scope, delimited by the braces, is exited.
#Setting $/ to undef puts file reads in 'slurp mode' => whole file is considered one line.
my $file = <$CONN>; #Read one line.
print $file;
}`
2) $ perl my_prog.pl
3) socat -u -v GOPEN:./data.txt UNIX-CONNECT:/tmp/sock
The -u and -v options aren't necessary:
-u Uses unidirectional mode. The first address is only used for
reading, and the second address is only used for writing (exam-
ple).
-v Writes the transferred data not only to their target streams,
but also to stderr. The output format is text with some conver-
sions for readability, and prefixed with "> " or "< " indicating
flow directions.
4) You can also do it like this:
cat data.txt | socat STDIN UNIX-CONNECT:/tmp/sock
Pipe stdout of cat command to socat, then list STDIN as one of socat's files.
Response to comment:
This bash script works for me:
#!/usr/bin/env bash
echo 'bash script'
../pperl_programs/my_prog.pl &
sleep 1s
socat GOPEN:./data.txt UNIX-CONNECT:/tmp/sock
It looks like the perl script doesn't have enough time to setup the socket before socat tries to transfer data.

Perl: stop a command started using perl "system" in windows

in Perl, I started two commands in two different windows command line (Cmd) , as follow:
system("start $cmd1");
system("start $cmd2");
Basically, both commands continue running until I stop them using "CTRL+C".
My question is :
How to send "CTR+C" to each Cmd line (or command) ?
Thank you.
I think you can use
my $pid = system(1, $cmd1);
# One of the following:
kill(INT => $pid); # Sends Ctrl-C
kill(TERM => $pid); # Sends Ctrl-Break
kill(KILL => $pid); # Calls TerminateProcess($handle, 9)
waitpid($pid, 0);
I solved it by creating two detached processes. See the code below: `
use Win32::Process;
use Win32;
sub ErrorReport{
print Win32::FormatMessage( Win32::GetLastError() );
}
Win32::Process::Create($ProcessObj,
"C:\\winnt\\system32\\notepad.exe",
"notepad temp.txt",
0,
DETACHED_PROCESS,
".")|| die ErrorReport();
$ProcessObj->Suspend();
$ProcessObj->Resume();
$ProcessObj->Wait(INFINITE);
`

Hiding User Input

I'm trying to get a script that works both in a native windows shell and a cygwin shell (via ssh) that prompts for and reads a password entered by the user. So far, I have tried the following methods:
using Term::ReadKey and setting ReadMode to 'noecho'
RESULT: returns an error GetConsoleMode failed and quits
using Term::ReadPassword::Win32
RESULT: hangs and never offers a prompt or reads input
using IO::Prompt
RESULT: returns an error Cannot write to terminal and quits
using Term::InKey
RESULT: returns an error Not implemented on MSWin32: The handle is invalid and quits
All of these work in a native Windows shell (command prompt or power shell), but none of them work when I'm in an ssh session to the server.
Really, that's what I'm most interested in, getting it to work in the remote ssh session.
I'm getting ssh via cygwin installed on the Windows server (2003 R2). I'm using strawberry perl and not the cygwin perl (cygwin perl breaks other perl scripts I need to run natively in Windows, not via ssh).
My best guess is that cygwin+Windows is screwing with strawberry perl enough that it can't tell what kind of environment it is in. I'm looking into alternative sshd+Windows solutions to explore this.
These are all the methods I've been able to find in my searching. Does anybody else have any other methods for hiding user input they can suggest?
use Term::ReadKey;
print "Please enter your artifactory user name:";
$username = <STDIN>;
chomp($username);
ReadMode('noecho'); # don't echo
print "Please enter your artifactory password:";
$password = <STDIN>;
chomp($password);
ReadMode(0); #back to normal
print "\n\n";
I would try outputting the environment variables (%ENV) during the sessions that work, and then again during the sessions that don't. I find that, when dealing with terminal IO, you have to carefully tweak the "TERM" variable based on things like the $^O variable and $ENV{SESSIONNAME} (in Windows).
how about Term::ReadKey's ReadMode(4)? i've just used this in a personal project, having found the answer here
works on cygwin / win7, can't vouch for native windows shell however.
use strict;
use warnings;
use Term::ReadKey;
sub get_input {
my $key = 0;
my $user_input = "";
# disable control keys and start reading keys until enter key is pressed (ascii 10)
ReadMode(4);
while (ord($key = ReadKey(0)) != 10)
{
if (ord($key) == 127 || ord($key) == 8) {
# backspace / del was pressed. remove last char and move cursor back one space.
chop ($user_input);
print "\b \b";
} elsif (ord($key) < 32) {
# control characters, do nothing
} else {
$user_input = $user_input . $key;
print "*";
}
}
ReadMode(0);
return $user_input;
}
# variables
my $password = "";
my $username = "";
print "\nPlease input your username: ";
$username = get_input();
print "\nHi, $username\n";
print "\nPlease input your password: ";
$password = get_input();

Resources