Ruby : Use external script from within a script to do a comparison - ruby

So I got a script (A) that finds a suitable IP address for a new virtual server. At first, it takes a look in the database to see if the first ip he chose isn't already taken by another server. If the IP is not already in use, the script pings it. If there is no response from the ping, then we get to the next step and this is where I'm having a problem.
In the next step, I have to check if the IP address is already registred in the netscaler (router) or not. To do this, I must use another script on the same machine (B). This other script return the list of all the ips defined in the netscaler. When I run it, the output looks like this
x.x.x.x
x.x.x.x
x.x.x.x (and so on..).
I found many ways to execute the script B from whiting the script A, but none of what I found allow me to do what I'd like to.
My goal is to compare the ip my script found with all of those that are listed, without having those last ones printed on the screen.
So, to make it a bit clearer, let's say that the scrip A found the IP : 1.2.3.4
It would then call script B that would return to script A this list
1.2.3.5
1.2.4.5
1.2.5.1
and so on.
and then A would compare 1.2.3.4 with all those returned by script B without actually showing them on screen.
Thank you very much!

I would separate scriptB business logic from scriptB ui (CLI) logic:
scriptA.rb
scriptB.rb
netscaler.rb # extract logic here
Extract your list of all the ips defined in the netscaler logic into separate class/method:
#netscaler.rb
class Netscaler
def self.list_ips
# return array of ips here
end
end
#scriptB.rb
require_relative 'netscaler'
ips = Netscaler.list_ips
puts ips # script B may show these ips on the screen
...
#scriptA.rb
require_relative 'netscaler'
ips = Netscaler.list_ips
# script A will not show them. Instead it will operate on the returned result.
...

You can use backticks to execute script B and return the output:
ip_list = `scriptB`.split("\n")
This can be plugged into Alex's organizational suggestion. I would do this if script B is a non-Ruby script that you don't control.
Note that if there is any leading or trailing whitespace you can add a .map(&:strip) to the end.

Related

Find all distinct IP addresses and print them in lexicographical order

I have a hierarchy of directories and some files in some of those directories:
/root/development/dir1/file1.txt, file2.txt, ...
/root/development/dir2/file3.txt, file4, ...
/root/development/file6.in, file7.out, ...
...
Some of these files contain IP addresses inside the text. In the form x.x.x.x where each x is a number from 0 to 255 (inclusive).
For example, say we have file1.txt that looks like this:
hello world 127.0.0.1
this is some example 128.99.107.55
file with some correct and incorrect 128.128.4.11 ip 0.11.1115.78 addresses
This file contains only 3 IP addresses, namely 127.0.0.1, 128.99.107.55, and 128.128.4.11, since 0.11.1115.78 is not a valid IP address.
I need to write a program (in java or python) to find all distinct IP addresses from all the files in the /root/development/ directory and print them in lexicographical order.
The input will be a setup shell script, and the code should print the required data to stdout.
An example shell script is as follows:
#!/bin/bash
rm -rf /root/development
mkdir /root/development
mkdir /root/development/dir1
mkdir /root/development/dir2
touch /root/development/dir1/file1.txt
echo -e "hello world 127.0.0.1\nthis is some example 128.99.107.55 \nfile with some correct and incorrect 128.128.4.11 ip 0.11.1115.78 addressesaddresses" >> /root/development/dir1/file1.txt
touch /root/development/dir1/file2.txt
echo -e "hello from 74.0.65.76 and 8.dd.99.88.907 good\nthis is some example 16.1215.76.35 \nfile with some correct and incorrect 15.128.4.65 ip addresses\n0.0.0.0" >> /root/development/dir1/file2.txt
touch /root/development/dir2/file3.txt
echo -e "127.65.64.1 127.0.64.1 127.0.0.1\nexample 128.57.107.76 128.57.907.70 \nfile with some correct and incorrect 67.128.4.11 ip addresses 7.7.7.8" >> /root/development/dir2/file3.txt
touch /root/development/dir2/file4.txt
echo -e "hello world 127.98.0.1\nthis is some example 128.96.107.55 \nfile with some correct and incorrect 128.68.4.11 ip addresses" >> /root/development/dir2/file4.txt
touch /root/development/f.inp
echo -e "hello world 127.0.49.1 \nthis is some example 128.99.58.55 8.88.888.88 77.255.255.254\n7.7.257.25 file with some correct and incorrect 26.56.4.23 ip addresses" >> /root/development/f.inp
Example
For the following /root/development/ directory:
/root/development/dir1/file1.txt
hello world 127.0.0.1
this is some example 128.99.107.55
file with some correct and incorrect 128.128.4.11 ip 0.11.1115.78 addressesaddresses
/root/development/dir1/file2.txt
hello from 74.0.65.76 and 8.dd.99.88.907 good
this is some example 306.5.76.35
file with some correct and incorrect 15.128.4.65 ip addresses
0.0.0.0
/root/development/dir2/file3.txt
127.65.64.1 127.0.64.1 127.0.0.1
exaMple 128.57.107.76 128.57.907.70
file with some correct and incorrect 67.128.4.11 ip addresses 7.7.7.8
/root/devops/dir2/file4.txt
hello world 127.98.0.1
this is some example 128.96.107.55
file with some correct and incorrect 128.68.4.11 ip addresses
/root/development/f.inp
hello world 127.0.49.1
this is some example 128.99.58.55 8.88.888.88 77.255.255.254
7.7.257.25 file with some correct and incorrect 26.56.4.23 ip addresses
The output should be
0.0.0.0
127.0.0.1
127.0.49.1
127.0.64.1
127.65.64.1
127.98.0.1
128.128.4.11
128.57.107.76
128.68.4.11
128.96.107.55
128.99.107.55
128.99.58.55
15.128.4.65
26.56.4.23
67.128.4.11
7.7.7.8
74.0.65.76
77.255.255.254
From your comment, I assume that you have no or very little pre-existing programming knowledge, so I'll try to broadly explain steps of how you could tackle this project.
For each of the following steps, if you don't know how to do it (which is expected if you're new to programming), try an internet search to know how to do that step in your chosen language.
Step 0. First, you should choose between Java and Python. Both can be used for this, it's just a matter of what language you know the best at this time, or have already installed on your computer...
Step 1: write the code to read the content of just one file. You can temporarily write the whole file content to stdout to ensure that this works well.
Step 2. change the code to print only the IP adresses from the file content (maybe using regex to extract the IP adresses from the file content)
Step 3. remove duplicate IP adresses. You'll probably do that by putting IP adresses in a list and apply an existing Java/Python function to that list, and then print the list to stdout
Step 4. sort the list before printing it
Now you want to do the same thing, but on several files:
Step 5. Write code to list all the files of one folder
Step 6. Write code to list all the files of one folder and all it's subfolders, recursively
Step 7. Combine your code from Steps 1-4 with code from Steps 5-6 to achieve the result you want
To approach above problem we will have to be good with regex and file handling. The below code is in python should help you to move forward
Code
from pathlib import Path
import re
# Get lines from the files
myDir = r"C:\Users\myuser\Downloads\find\yourFolder"
result = list(Path(myDir).rglob("*.[tT][xX][tT]"))
ips = []
flatList = []
def getListOfIps():
for i in result:
with open(i) as f:
for text in f.readlines():
text = text.rstrip()
regex = re.findall(
r'(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})', text)
if regex is not None and regex not in ips:
ips.append(regex)
# Get the list of IPs
def check(Ip):
if(re.search(myPattern, Ip)): # re.search returns None (if the pattern doesn’t match)
return True
else:
return False
myPattern = "^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])"
getListOfIps()
flatList = [ item for elem in ips for item in elem]
flatList.sort()
for eachIP in flatList:
if check(eachIP):
print(f"{eachIP}")
# May require for future improvements
# matches for 8.dd.99.88 is (?:[\d]{1,3})\.(?:[\d]|[a-z]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})
if you are new to programming and need some reference program you may look at my repo https://github.com/Kapil987/Python_prac/tree/master

How to deal with shell commands that never stops

Here is the case;
There is this app called "termux" on android which allows me to use a terminal on android, and one of the addons are androids API's like sensors, tts engines, etc.
I wanted to make a script in ruby using this app, specifically this api, but there is a catch:
The script:
require('json')
JSON.parse(%x'termux-sensor -s "BMI160 Gyro" -n 1')
-s = Name or partially the name of the sensor
-n = Count of times the command will run
returns me:
{
"BMI160 Gyroscope" => {
"values" => [
-0.03...,
0.00...,
1.54...
]
}
}
I didn't copied and pasted the values, but that's not the point, the point is that this command takes almost a full second the load, but there is a way to "make it faster"
If I use the argument "-d" and not use "-n", I can specify the time in milliseconds to delay between data being sent in STDOUT, it also takes a full second to load, but when it loads, the delay works like charm
And since I didn't specify a 'n' number of times, it never stops, and there is the problem
How can I retrieve the data continuously in ruby??
I thought about using another thread so it won't stop my program, but how can I tell ruby to return the last X lines of the STDOUT from a command that hasn't and will not ever stop since "%x'command'" in ruby waits for a return?
If I understood you need to connect to stdout from a long running process.
see if this works for your scenario using IO.popen:
# by running this program
# and open another terminal
# and start writing some data into data.txt
# you will see it appearing in this program output
# $ date >> data.txt
io_obj = IO.popen('tail -f ./data.txt')
while !io_obj.eof?
puts io_obj.readline
end
I found out a built in module that saved me called PTY and the spawn#method plus thread management helped me to keep a variable updated with the command values each time the command outputted new bytes

reading a bash command result

I used to use the "execute_command" found in the former awesome wiki. This command uses io.popen and the lines method to return the command's result.
Now, the doc's advice is to avoid io.popen.
My rc.lua uses io.popen to assign hostname's computer to a variable ordinateur (I'm trying to maintain a unique rc.lua for two quite different computers).
I used to have this line :
ordinateur=execute_command( "hostname" )
I replace it with :
awful.spawn.easy_async_with_shell( "hostname" ,
function(stdout,stderr,reason,exit_code)
ordinateur = stdout
end)
Further in the script, I have tests like
if ordinateur == "asus" then ....
But it fails. Actually ordinateur is nil
I think the rc.lua is read before ordinateur gets its assignment, right ?
So, what can I do ? I'm thinking replace the command with the reading of the /etc/hostname file, is that better ? How am I going to do this with awful.spawn.* commands ?
Thank you
David
If at all possible, use LuaSocket.
> socket = require "socket"
> print(socket.dns.gethostname())
myhost
Another option is to run hostname from the script that launches the window manager, and store the result in an environment variable. Who knows, if you're lucky, it's already in there?!
> print(os.getenv("HOSTNAME") or os.getenv("HOST"))
myhost
It fails later in the script because the command is asynchronous. This means it Awesome keeps going during the command execution and the result will be available later.
This is the whole point of not using io.popen. io.popen will stop everything [related to X11, including all applications] on your computer while it is being executed.
You need to modify your code so all things that access ordinateur do so after the callback has been called. The easiest way to do so is adding that code in the callback.

Passing variables between chef resources

i would like to show you my use case and then discuss possible solutions:
Problem A:
i have 2 recipes, "a" and "b".. "a" installs some program on my file system (say at "/usr/local/bin/stuff.sh" and recipe "b" needs to run this and do something with the output.
so recipe "a" looks something like:
execute "echo 'echo stuff' > /usr/local/bin/stuff.sh"
(the script just echo(es) "stuff" to stdout)
and recipe "b" looks something like:
include_recipe "a"
var=`/usr/local/bin/stuff.sh`
(note the backquotes, var should contain stuff)
and now i need to do something with it, for instance create a user with this username. so at script "b" i add
user "#{node[:var]}"
As it happens, this doesn't work.. apparently chef runs everything that is not a resource and only then runs the resources so as soon as i run the script chef complains that it cannot compile because it first tries to run the "var=..." line at recipe "b" and fails because the "execute ..." at recipe a did not run yet and so the "stuff.sh" script does not exist yet.
Needless to say, this is extremely annoying as it breaks the "Chef runs everything in order from top to bottom" that i was promised when i started using it.
However, i am not very picky so i started looking for alternative solutions to this problem, so:
Problem B: i've run across the idea of "ruby_block". apparently, this is a resource so it will be evaluated along with the other resources. I said ok, then i'd like to create the script, get the output in a "ruby_block" and then pass it to "user". so recipe "b" now looks something like:
include_recipe "a"
ruby_block "a_block" do
block do
node.default[:var] = `/usr/local/bin/stuff.sh`
end
end
user "#{node[:var]}"
However, as it turns out the variable (var) was not passed from "ruby_block" to "user" and it remains empty. No matter what juggling i've tried to do with it i failed (or maybe i just didn't find the correct juggling method)
To the chef/ruby masters around: How do i solve Problem A? How do i solve Problem B?
You have already solved problem A with the Ruby block.
Now you have to solve problem B with a similar approach:
ruby_block "create user" do
block do
user = Chef::Resource::User.new(node[:var], run_context)
user.shell '/bin/bash' # Set parameters using this syntax
user.run_action :create
user.run_action :manage # Run multiple actions (if needed) by declaring them sequentially
end
end
You could also solve problem A by creating the file during the compile phase:
execute "echo 'echo stuff' > /usr/local/bin/stuff.sh" do
action :nothing
end.run_action(:run)
If following this course of action, make sure that:
/usr/local/bin exist during Chef's compile phase;
Either:
stuff.sh is executable; OR
Execute it through a shell (e.g.: var=`sh /usr/local/bin/stuff.sh`
The modern way to do this is to use a custom resource:
in cookbooks/create_script/resources/create_script.rb
provides :create_script
unified_mode true
property :script_name, :name_property: true
action :run do
execute "creating #{script_name}" do
command "echo 'echo stuff' > #{script_name}"
not_if { File.exist?(script_name) }
end
end
Then in recipe code:
create_script "/usr/local/bin/stuff.sh"
For the second case as written I'd avoid the use of a node variable entirely:
script_location = "/usr/local/bin/stuff.sh"
create_script script_location
# note: the user resources takes a username not a file path so the example is a bit
# strange, but that is the way the question was asked.
user script_location
If you need to move it into an attribute and call it from different recipes then there's no need for ruby_blocks or lazy:
some cookbook's attributes/default.rb file (or a policyfile, etc):
default['script_location'] = "/usr/local/bin/stuff.sh"
in recipe code or other custom resources:
create_script node['script_location']
user node['script_location']
There's no need to lazy things or use ruby_block using this approach.
There are actually a few ways to solve the issue that you're having.
The first way is to avoid the scope issues you're having in the passed blocks and do something like ths.
include_recipe "a"
this = self
ruby_block "a_block" do
block do
this.user `/usr/local/bin/stuff.sh`
end
end
Assuming that you plan on only using this once, that would work great. But if you're legitimately needing to store a variable on the node for other uses you can rely on the lazy call inside ruby to do a little work around of the issue.
include_recipe "a"
ruby_block "a_block" do
block do
node.default[:var] = `/usr/local/bin/stuff.sh`.strip
end
end
user do
username lazy { "#{node[:var]}" }
end
You'll quickly notice with Chef that it has an override for all default assumptions for cases just like this.

Redirect Output of Capistrano

I have a Capistrano deploy file (Capfile) that is rather large, contains a few namespaces and generally has a lot of information already in it. My ultimate goal is, using the Tinder gem, paste the output of the entire deployment into Campfire. I have Tinder setup properly already.
I looked into using the Capistrano capture method, but that only works for the first host. Additionally that would be a lot of work to go through and add something like:
output << capture 'foocommand'
Specifically, I am looking to capture the output of any deployment from that file into a variable (in addition to putting it to STDOUT so I can see it), then pass that output in the variable into a function called notify_campfire. Since the notify_campfire function is getting called at the end of a task (every task regardless of the namespace), it should have the task name available to it and the output (which is stored in that output variable). Any thoughts on how to accomplish this would be greatly appreciated.
I recommend not messing with the Capistrano logger, Instead use what unix gives you and use pipes:
cap deploy | my_logger.rb
Where your logger reads STDIN and STDOUT and both records, and pipes it back to the appropriate stream.
For an alternative, the Engineyard cap recipies have a logger – this might be a useful reference if you do need to edit the code, but I recommend not doing.
It's sort of a hackish means of solving your problem, but you could try running the deploy task in a Rake task and capturing the output using %x.
# ...in your Rakefile...
task :deploy_and_notify do
output = %x[ cap deploy ] # Run your deploy task here.
notify_campfire(output)
puts output # Echo the output.
end

Resources