Puppet: all custom facts gets all results - ruby

I´m trying to work out a way in Puppet to get the current zpool capacity numbers for my FreeBSD storage servers, storing them in custom facts and to generate alert if capacity reaches a "too high" level. Closest match to my problem that I´ve found so far is:
Returning multiple custom facts with puppet Facter
That pointed me to this solution:
operatingsystem = Facter.value('operatingsystem')
case operatingsystem
when "FreeBSD"
present_zpools = IO.popen('zpool list -H -o name').read.chomp
if ! present_zpools.empty?
Facter.add(:zpools) do
setcode do
zpools = IO.popen('for i in $(zpool list -H -o name); do echo $i; done').read.chomp.split("\n")
end
end
def addZpoolCapacityFact(zpool)
zpool_capacity = IO.popen('zpool get -H -o value capacity #{zpool}').read.tr('%','').chomp
Facter.add("capacity_" + zpool) do
setcode do
zpool_capacity
end
end
end
zpools = Facter.value(:zpools)
zpools.each do |zpool|
addZpoolCapacityFact(zpool)
end
end
end
But doesn´t quite produce the result I was expecting, e.g:
capacity_pool1: 10 30
capacity_pool2: 10 30
When I was really expecting:
capacity_pool1: 10
capacity_pool2: 30
What am I doing wrong?

OK, solved!
The problem was using IO.popen two times in same script, even though I tried nil'ing the variables, the first split function applied to variable 'zpools' was also run on 'zpool_capacity', I think, which made the result look like:
"capacity_pool1":"10\n12","capacity_pool2":"10\n12"
Notice the '\n' between the numbers? I´m sure there´s a Ruby way to be able to use IO.popen multiple times but I don´t know how, so I just changed the commands to execute with plain backticks (`) and here´s the working code:
operatingsystem = Facter.value('operatingsystem')
case operatingsystem
when "FreeBSD"
present_zpools = `zpool list -H -o name`.chomp
if ! present_zpools.empty?
Facter.add(:zpools) do
setcode do
zpools = `for i in $(zpool list -H -o name); do echo $i; done`.chomp.split("\n")
end
end
def addZpoolCapacityFact(zpool)
zpool_capacity = `zpool get -H -o value capacity #{zpool}`.tr('%','').chomp
Facter.add(zpool + "_capacity") do
setcode do
zpool_capacity
end
end
end
zpools = Facter.value(:zpools)
zpools.each do |zpool|
addZpoolCapacityFact(zpool)
end
end
end
Now result looks like I´d expect:
pool1_capacity: 10
pool2_capacity: 30

Related

I have three flags in OptionParser but it only gives access to two of them

I am building a CLI gem with Ruby and I'm using OptionParser to add flags with arguments. Whenever one flag is called with an argument a function get's called.
I have three flags. My problem is, when I run the -h command to see the options(flags) available to use, it only shows three of them (skipping the middle one) and even if I try to use that flag, (that is not listed in help) it runs the code of the last flag.
Here is my code for the flags:
def city_weather
options = {}
OptionParser.new do |opts|
opts.banner = "Welcome to El Tiempo! \n Usage: cli [options]"
opts.on('-today', 'Get today\'s weather') do |today|
options[:today] = today
weather_today(ARGV.first)
end
opts.on('-av_max', 'Get this week\'s average maximum temperature') do |av_max|
options[:av_max] = av_max
week_average_max(ARGV.first)
end
opts.on('-av_min', 'Get this week\'s average minimum temperature') do |av_min|
options[:av_min] = av_min
week_average_min(ARGV.first)
end
end.parse!
ARGV.first
end
It is the -av_max flag that does not work.
When I run -av_max the week_average_min(ARGV.first) get's executed.
When I run the -h command here is what it shows:
Welcome to El Tiempo!
Usage: cli [options]
-today Get today's weather
-av_min Get this week's average minimum temperature
My question is, is it possible to have three flags with OptionParser? If so, how could I make this middle flat work?
You need to add another dash for long arguments:
opts.on('--av_min', 'Get this week\'s average minimum temperature') do |av_min|
options[:av_min] = av_min
week_average_min(ARGV.first)
end

Perl Net::MAC::Vendor OUI

The follow is on MacOS High Sierra with Perl v5.28.2. The version of Net::MAC::Vendor is 1.265. This sort of a repeat post. My prior post lacked a lot of detail.
I am trying to put together a script to list ip addresses, MAC addresses, and vendors on a network. The Net::MAC::Vendor::lookup function is returning a timeout error amongst other things. I have checked a few IEEE links that were supposed to have the OUI data but they are all dead returning no data. I have seen a number of mentions stating the file can be found in some installations of linux. I have search high and low and have not found an oui.txt file on my system. If I downloaded a copy I wouldn't know where to put it or how I could have the Net::MAC::Vendor function to locate it. Also, if I did find a link I still wouldn't know how to direct the vendor lookup function to use it.
The errors I am getting are as follows:
Use of uninitialized value in concatenation (.) or string at /Users/{username}/perl5/perlbrew/perls/perl-5.28.2/lib/site_perl/5.28.2/Net/MAC/Vendor.pm line 320.
Failed fetching [https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-L&format=html&text=D8-D7-75] HTTP status []
message [Connect timeout] at simplemacvendor.pl line 23.
Could not fetch data from the IEEE! at simplemacvendor.pl line 23.
The sample code:
#!/usr/bin/perl;
use strict;
use feature qw(say);
use Data::Dumper qw(Dumper);
use Net::MAC::Vendor;
open(ARP, "arp -na|") || die "Failed $!\n";
my #arp_table;
while (<ARP>) {
if ($_ =~ m/incomplet/) {next;}
if ($_ =~ m/Address/) {next;}
my #line = split(' ',$_);
my $computer = {};
$line[1] =~ s/(\()([0-9\.]*)(\))/\2/;
$computer->{ip} = $line[1];
$computer->{mac} = $line[3];
$computer->{if} = $line[5];
say Dumper($computer);
# Get vendor info
my $vendor_info = Net::MAC::Vendor::lookup( $computer->{mac} ); # line 23
$computer->{vendor} = $vendor_info->[0];
push #arp_table , $computer;
}
print "ARP Table with vendors:\n";
for my $i (0 .. $#arp_table) {
print "$arp_table[$i]{ip}\t";
print "$arp_table[$i]{if}\t";
print "$arp_table[$i]{mac}\t";
print "$arp_table[$i]{vendor}";
print "\n";
}

How to interactively run mount command from a (Ruby) script?

I am trying to write a Ruby script that runs the mount command interactively behind the scenes. The problem is, if I redirect input and output of the mount command to pipes, it doesn't work. Somehow, mount seems to realise that it's not talking directly to stdin/stdout and falls over. Either that, or it's a more wide-ranging problem that would affect all interactive commands; I don't know.
I want to be able to parse the output of mount, line by line, and shove answers into its input pipe when it asks questions. This shouldn't be an unreasonable expectation. Can someone help, please?
Examples:
def read_until(pipe, stop_at, timeoutsec = 10, verbose = false)
lines = []; line = ""
while result = IO.select([pipe], nil, nil, timeoutsec)
next if result.empty?
begin
c = pipe.read(1) rescue c = nil
end
break if c.nil?
line << c
break if line =~ stop_at
# Start a new line?
if line[-1] == ?\n
puts line if verbose
lines << line.strip
line = ""
end
end
return lines, line.match(stop_at)
end
cmd = "mount.ecryptfs -f /tmp/1 /tmp/2"
status = Open3::popen2e(cmd) { |i,o,t|
o.fcntl(3, 4) # Set non-blocking (this doesn't make any difference)
i.fcntl(3, 4) # Set non-blocking (this doesn't make any difference)
puts read_until(o, /some pattern/, 1, true) # Outputs [[], nil]
}
I've also tried spawn:
a, b = IO.pipe
c, d = IO.pipe
pid = spawn(cmd, :in=>a, :out=>d)
puts read_until(c, /some pattern/, 1, true) # Outputs [[], nil]
I've tried subprocess, pty and a host of other solutions - basically, if it's on Google, I've tried it. It seems that mount just knows if I'm not passing it a real shell, and deliberately blocks. See:
pid = spawn(cmd, :in=>STDIN, :out=>STDOUT) # Works
pid = spawn(cmd, :in=>somepipe, :out=>STDOUT) # Blocks after first line of output, for no reason whatsoever. It's not expecting any input at this point.
I even tried spawning a real shell (e.g. bash) and sending the mount command to it via an input pipe. Same problem.
Please ignore any obvious errors in the above: I have tried several solutions tonight, so the actual code has been rewritten many times. I wrote the above from memory.
What I want is the following:
Run mount command with arguments, getting pipes for its input and output streams
Wait for first specific question on output pipe
Answer specific question by writing to input pipe
Wait for second specific question on output pipe
...etc...
And so on.
You may find Kernel#system useful. It opens a subshell, so if you are ok w/ the user just interacting with mount directly this will make everything much easier.

How do I create a file using sudo and write into it?

I created a bash script file:
#!/bin/bash
default_card=`head -1 /proc/asound/modules`
echo $default_card
if [ ! -e /etc/modprobe.d/sound.blacklist.conf ] ; then
echo "Default sound card(snd_hda_intel) is not added in black list"
/usr/bin/expect <<delim
exp_internal 0
set timeout 20
spawn sudo sh -c "echo 'blacklist snd_hda_intel' > /etc/modprobe.d/sound.blacklist.conf"
expect "password for ubuntu:"
send "1234\n"
expect eof
delim
else
echo "Default sound cardis already added in black list";
fi
I am creating a black list file in "/etc/modprobe.d". Creating or deleting any file from "/etc" requires sudo access.
I want to implement the same functionality in Ruby using a Rake task. I created the task as:
desc "Check/creates soundcard blacklist"
task :create_blacklist do
begin
if !File.exists?("/etc/modprobe.d/sound.blacklist.conf")
# code for creating new file and write into it
......
......
else
puts "Sound-card blacklist file is present at /etc/modprobe.d/sound.blacklist.conf"
end
rescue Exception => e
puts "problem creating file #{e.message}"
end
end
I don't know how to create new file using sudo, and write into it.
I am using Ruby 1.9.3 (without RVM).
Look at https://stackoverflow.com/a/18366155/128421, https://stackoverflow.com/a/18398804/128421, and "communicating w/ command-line program (OR ruby expect)" for more information.
Ruby's IO class implements expect but it's not too full-featured:
=== Implementation from IO
------------------------------------------------------------------------------
IO#expect(pattern,timeout=9999999) -> Array
IO#expect(pattern,timeout=9999999) { |result| ... } -> nil
------------------------------------------------------------------------------
Reads from the IO until the given pattern matches or the timeout is over.
It returns an array with the read buffer, followed by the matches. If a block
is given, the result is yielded to the block and returns nil.
When called without a block, it waits until the input that matches the given
pattern is obtained from the IO or the time specified as the timeout passes.
An array is returned when the pattern is obtained from the IO. The first
element of the array is the entire string obtained from the IO until the
pattern matches, followed by elements indicating which the pattern which
matched to the anchor in the regular expression.
The optional timeout parameter defines, in seconds, the total time to wait for
the pattern. If the timeout expires or eof is found, nil is returned or
yielded. However, the buffer in a timeout session is kept for the next expect
call. The default timeout is 9999999 seconds.

rails 3 paperclip processor watermark error in filename

I have a problem with paperclip in rails 3. When I upload a file my processor throws error because imagemagick get command:
"composite -gravity South /home/xxx/xxx/public/images/watermark.png /tmp/a s20121207-5819-1dq7y81.jpg /tmp/a s20121207-5819-1dq7y8120121207-5819-1juqw7a"
composite: unable to open image `/tmp/a':
processor:
def make
dst = Tempfile.new([#basename, #format].compact.join("."))
dst.binmode
if #watermark_path
command = "composite"
params = "-gravity #{#position} #{#watermark_path} #{fromfile} "
params += tofile(dst)
begin
p " >>>>>>>>>>>>>>>>> #{command} #{params}"
success = Paperclip.run(command, params)
rescue PaperclipCommandLineError
success = false
end
unless success
raise PaperclipError, "There was an error processing the watermark for #{#basename}" if #whiny
end
return dst
else
return #file
end
end
def fromfile
File.expand_path(#file.path)
end
def tofile(destination)
File.expand_path(destination.path)
end
it only occurs when filename have whitespace or other non alfanum chars. The /tmp/a should be /tmp/a b.jpg.
I'va tried http://www.davesouth.org/stories/make-url-friendly-filenames-in-paperclip-attachments and more but still filename in processor is wrong
any ideas? or processor that works ok for this issue? :(
Try it maybe with:
dst = Tempfile.new([#basename, #format].compact.join(".").gsub(" ","")
I came back to this today and figured out you just need to enclose the path with a quote as in the ImageMagic documentation (http://www.imagemagick.org/script/command-line-processing.php):
If the image path includes one or more spaces, enclose the path in quotes:
'my title.jpg'
As an example, in my processor I have:
command = "convert \"#{File.expand_path(#file.path)}\" -crop #{crop_area} -resize 150x150 \"#{File.expand_path(destination.path)}\""
Paperclip.run(command)
.
I am on Windows at the moment and single quote doesn't seem to work.
Like you, I tried changing attributes of #file with no success.
Hope it helps.

Resources