Ruby script pucking on new line in a file - ruby

If I only have one host in the file the scripts does as intended. As soon as I add another address in the file I get this error. I understand that it does not like the new line character at the end of the first host in the file, how is this problem alleviated? I'm basically looking for the script to run down the hostfile, and for every address or host-name in the file, run the session.
'initialize': newline at the end of hostname
File.read('hostfile').each_line do |hostname|
session = Net::SSH.start(hostname, #username, :password => #password, :encryption => 'aes256-cbc', :host_key => 'ssh-rsa')
cmd_session = Net::SSH::Telnet.new('Session' => session)
cmd_session.cmd("en\r#{#enable}")
cmd_session.cmd('terminal pager 0')
cmd_session.cmd('show threat-detection statistics') { |c| print c }
cmd_session.close
end

session = Net::SSH.start(hostname.strip, #username.....
Should get you going. For further reference see the docs

Related

Delete a file using Ruby SFTP `remove` and `remove!`

I am trying to old delete files from an FTP using Ruby net/sftp, but I keep getting an error saying the file does not exist.
:error=>"Net::SFTP::StatusException (2, \"no such file\")"
I can manually delete files when logging in using the same creds, so I know I have permission.
require 'net/sftp'
ftp = Net::SFTP.start(#ftp_url, #ftp_user, :password => #ftp_pwd)
ftp.dir.entries('somePath').each do |entry|
begin
age_days = (Time.now.to_i - entry.attributes.atime) / 86400
if(age_days > ftp_max_file_age_days)
ftp.remove!(entry.name)
end
rescue Exception => e
# log error here
end
end
I prefer remove! so everything happens synchronously in this case, but I have also tried remove.
I also tried giving it the full path of the file instead of just the entry name (like 'somePath' + entry.name instead of just entry.name). I was thinking perhaps it was because I needed to change the working directory, which apparently net/sftp does not allow.
Thanks in advance!
Check if entry is a directory if yes then use ftp.rmdir. like below -
require 'net/sftp'
ftp = Net::SFTP.start(#ftp_url, #ftp_user, :password => #ftp_pwd)
ftp.dir.entries('somePath').each do |entry|
begin
age_days = (Time.now.to_i - entry.attributes.atime) / 86400
if(age_days > ftp_max_file_age_days)
if File.directory?(entry.name)
ftp.rmdir(entry.name)
else
ftp.remove!(entry.name)
end
end
rescue Exception => e
# log error here
end
end
We were eventually able to delete files using the remove method (instead of remove!.) We made a small change to the way we provide the password.
We confirmed that permissions on the FTP did not change, so I think using non_interactive: true may have been the trick.
require 'net/sftp'
def self.delete_report(endpoint, username, password, report_filename)
SSH_OPTIONS = { non_interactive: true }.freeze
report_filename_base = File.basename(report_filename, '.*')
Net::SFTP.start(endpoint, username, SSH_OPTIONS.merge(password: password)) do |sftp|
sftp.remove(report_filename)
sftp.remove("#{report_filename_base}.fin")
sftp.remove("processed/#{report_filename}")
sftp.remove("processed/#{report_filename_base}.fin")
sftp.remove("failed/#{report_filename}")
sftp.remove("failed/#{report_filename_base}.fin")
sftp.remove("failed/#{report_filename_base}.info")
end
I still don't fully understand why the same method did not work before, but we're able to delete files in subfolders too, as shown in this example.

How can I use arrays to loop through a collective set of variables I read from a file?

I'm trying to make a loop so that during each loop it will take the name and password variables from the file and enter where called.
array = []
File.open("file_users.txt") do |login|
login.each do |item|
name, password = item.chomp.split(',')
array << "#{name}" "#{password}"
browser.goto "https://website.com"
browser.text_field(:id => "user_name").set "#{name}"
browser.text_field(:id => "user_password").set "#{password}"
browser.button(:id => "login").click
sleep(5)
browser.close
end
end
I think the main issue is trying to make the loop call the next set of email and password after using the previous ones.
*edited:
The result I'm trying to get is to pull text from a file, then give it a "name" and "password" value, then have it be entered into the text field on the browser when called...
for example, the text file looks like:
jerryname
jerrypassword
careyname
careypassword
britneyname
britneypassword
The result I want is:
#=> loop 1
puts jerrynamme
puts jerrypassword
#=> logs in
#=> waits, then closes browser
#=> loop 2
puts careyname
puts careypassword
#=> logs in
#=> waits, then closes browser... and so on.
The result I get is the browser opening and the name first being entered then the code just stops....the browser doesn't close, it just remains still.
You say:
the text file looks like:
jerryname jerrypassword careyname careypassword britneyname britneypassword
If the file_users.txt file is as you describe, one line with a list of names and password pairs separated by spaces, the line
File.open("file_users.txt") do |login|
will return the entire contents of the file in first login value. The code
name, password = item.chomp.split(',')
will assign the entire contents of the file to name and set password to nil
You need to build a new "file_users.txt" file in the following form (note the commas and line breaks):
jerryname,jerrypassword
careyname,careypassword
britneyname,britneypassword
Then your code will be closer to working.
PS, this line doesn't seem to be used for anything and can be removed.
array << "#{name}" "#{password}"

Reading a file and creating a user in Chef

I have a list of IP address along with me. In front of those IP I have a username. What I am trying to do is make Chef read the file having IP and username and once it encounter the IP, it should create a user of that name.
But when I do I get a user but the name of the user comes out to be a number.
Here is my recipe
File.open("/tmp/users.txt", "r") do |file|
file.readlines.each_with_index do |ip,user|
if ip = node[:ipaddress]
user ip[user] do
action :create
supports :manage_home => true
comment 'Test User'
home '/home/ip[user]'
shell '/bin/bash'
password 'password'
end
end
end
my users.txt file
231.27.59.232, test1
272.27.59.15, tes2
985.54.25.22, test3
Now when I run the recipe this is what I get
Recipe: repo_update::users
* cookbook_file[/tmp/users.txt] action create (up to date)
* user[1] action create
- create user 1
* user[7] action create
- create user 7
* user[2] action create
- create user 2
Please tell me what is wrong here.
Lots of problem here... The answer of Tejay is the way to go, I'll just try to explain why your code don't work and how to fix it so it could be of some use later :)
File.open("/tmp/users.txt", "r") do |file|
file.readlines.each_with_index do |ip,user|
puts "values are #{ip} and #{user}"
end
end
Gives:
values are 231.27.59.232, test1
and 0
values are 272.27.59.15, tes2
and 1
values are 985.54.25.22, test3
and 2
each_with_index won't split magically your line into two part, it will just assign the last parameter the actual index in the iteration.
A fixed version of your code would be:
File.open("/tmp/users.txt", "r") do |file|
file.readlines.each do |line| # just iterate and get line
ip,myuser=line.gsub("\n",'').split(',') # set ip and myuser variable with values comma separated, using myuser to avoid conflict with the resource name. Using gsub to remove traling carriage return in user name
if ip == node[:ipaddress] # test equality, a single = will assign ip a value and always be true.
user myuser do # create the user using the variable, no need to interpolate here
action :create
supports :manage_home => true
comment 'Test User'
home "/home/#{myuser}" # use interpolation here inside double quotes (won't work in single quotes)
shell '/bin/bash'
password 'password'
end
end
end
end
The problem is this line:
user ip[user] do
You are calling the [] method on the ip string. Furthermore, you're going to get a name collision between the resource user and the block variable. Finally, you are giving each user the home of '/home/ip[user]'. You need to put the string in "'s and wrap the variable in #{ and } Try this:
File.open("/tmp/users.txt", "r") do |file|
file.readlines.each do |line|
entries = line.split(',')
ip = entries[0].strip
username = entries[1].strip
if ip = node[:ipaddress]
user username do
action :create
supports :manage_home => true
comment 'Test User'
home "/home/#{username}"
shell '/bin/bash'
password 'password'
end
end
end
Also, reading this all from a file is a very non cheffy thing to do. Either use a databag or a hash stored in an environment variable, which also saves you from needing to loop at all:
userhash = node['my_users'][node['ipadddress']]
user userhash['username']
action :create
supports :manage_home => true
comment 'test user'
home userhash['home'] || "/home/#{userhash['username']"
shell userhash['shell'] || '/bin/bash'
password userhash['password'] || 'password'
end

Ruby Net::Telnet waitfor() doesnt work

i have check out many different ways to fix this and it might be specific to the enviroment.
i'm creating a script in Ruby to telnet into a remote server, login, enter the shell type (its a prompt after logging in and has no "prompt" in front) and then entering a userid at the next prompt. currently it gets logged in via user and pass, but freezes at the term part.
i have tried to use regex, strings, sleep and then just puts("xterm" but nothing allows it to go past this prompt.
here is a snippet of code that is getting hung up and the output to go along:
$telnet = Net::Telnet::new("Host" => 'hostname',
"Output_log" => 'output.txt',
"Dump_log" => 'Dump.txt',
"Binmode" => false,
"Timeout" => false,
"Prompt" => /.* $ $/) { |c| print c }
$telnet.login("user", "pass") { |c| print c }
$telnet.waitfor("TERM = (xterm) ")
$telnet.puts("xterm") { |c| print c }
$telnet.waitfor(Match => /Enter\s*$/) { |c| print c }
$telnet.puts("userid")
the output is as follows:
HP-UX ds107492 B.11.11 U 9000/800 (tm)
login: user
Password:
Please wait...checking for disk quotas
. i
.[ci
.*s1^i
See /etc/copyright for copyright notices
You have mail.
'You have mail.'
TERM = (xterm)
$s.waitfor("Match" => /TERM\s*/){ |c| print c }
$s.print("xterm\n"){ |c| print c }
seems to be the answer, it allowed me to go past the prompt. still not sure what the difference between this regex and all the others i tried are :/

Ruby and telnet: waitfor doesnt work

I need to login in a server with Ruby and Telnet and execute a few commands. My actual script is:
tn = Net::Telnet::new("Host" => "#{ip}", "Port" => 23, "Timeout" => 60,
"Output_log"=>"output_log.log",
"Dump_log"=> "dump_log.log",
"Prompt" => /[#]/ )
tn.cmd("#{USER}\n#{PASS}") { |c| print c }
puts tn.cmd("Conf")
tn.waitfor(/config/) { |str| puts str }
puts tn.cmd("Int fa23")
puts tn.cmd("Shut")
puts tn.cmd("No shut")
puts tn.cmd("Exit")
tn.close
I must only execute the second command (Int fa23) after the string "config" is found at the output. The problem is that waitfor is not working. Here is the output_log:
Trying XX.XX.XX.XX...
Connected to XX.XX.XX.XX.
User Name:username
Password:*************
BOT-SWT-VSAT-AL-...#Conf
BOT-SWT-VSAT-AL-...(config)#
The script stops with waitfor time out error. What am I doing wrong?
Please add the waitfor right after Net::Telnet::new
You should wait for the connection to get established (which happens when creating a Telnet instance) and then wait for the server to respond back every time before sending the next command.
localhost = Net::Telnet::new("Host" => "*****",
"Port" => ***,
"Timeout" => 10,
"Prompt" => /[$%#>] \z/n)
localhost.waitfor(/USER/) {
localhost.cmd("****") {
localhost.waitfor(/PASS/) {
|c| print c
# your next commands
...
# localhost.close
}
}
}
The key here is to make sure that you have received all the packets from the server (until EOF is received) before responding back. Then sometimes, even consuming/waiting for a space character might count (depending to how your the telnet server is designed).
Then, make sure to set the right regular expression for your match.

Resources