Edit a remote file using SSH in Perl - regex

Below is the code snippet I have written.
This tries to fetch the server name and file name through the arguments
I am stuck at editing the remote file after the server name and corresponding file name are fetched.
Command run: server.pl sox3d1 TEST50
File server.pl:
use strict;
use warnings;
use Data::Dumper;
use XML::Simple;
my $host=$ARGV[0];
my $db_host=$ARGV[1];
my %servers;
$servers{"ser14316.local.net"}=["sox3d1","sox3d2"] ;
$servers{"ser143hn.local.net"}=["sox4d1","sox4d2"] ;
$servers{"ser14441.local.net"}=["sox6d1","sox6d2"] ;
$servers{"ser18163.local.net"}=["soxuat61","soxuat62","soxuat63"] ;
$servers{"ser1444r.local.net"}=["soxuat51","soxuat52","soxuat53"] ;
my $files=
{
db=>"/appl/$host/db_info.ref",
};
my %groups_by_host;
for my $group (keys(%servers)) {
for my $host (#{ $servers{$group} }) {
push #{ $groups_by_host{$host} }, $group;
}
}
my $server = $groups_by_host{$host}; # we get the server name via variable $host
my ( $ssh_to_server )=#$server; # This is the server to ssh
my $dest_file=$files->{"db"} ; #This will fetch the file from the remote server
print "$ssh_to_server","->","$dest_file \n";
The file in this server has to be edited and below are the steps to do that via ssh.
Search for the string $host (i.e sox3d3) in the remote file $dest_file by doing ssh to server $ssh_to_server
Replace the string TEST30 with TEST50
The file is a semicolon-separated file
File content in remote server (db_info.ref):
sox3d3 ;/appl/sox3d3/current ;TEST30 ;TEST30 ;USER_10 ;USER_30
sox4d4 ;/appl/sox4d4/current ;TEST40 ;TEST40 ;USER_20 ;USER_40
So, the 3rd and 4th columns (TEST30;TEST30) should be replaced with TEST50 as per input.
Have tried the below code to get it working
as i said i was trying for a one line and had many variables in the command , the below worked actually
Update 1:
This basically ssh's the server and changes the file accordingly
system(
'ssh' => ('-q',$ser),
'sed' => ('-i -E', qq('/$host/s#([^;]+;[^;]+;)[^;]+;[^;]+#\\1$db_host
;$db_host #') , $dest_file),

Tried something like this?
my $destination = sprintf( '%s#%s', $user, $host );
my $file = '/path/to/some/file.txt';
system(
'ssh' => $destination,
'sed' => ( '-i', 's/TEST30/TEST50/g', $file ),
);
The ssh documentation shows you can pass a command after the ssh destination. When you do this, ssh will log into the server, run that command, then log out again, instead of dropping you into the shell on the server.
So you can use that to run sed to edit the file.

Related

Connecting Mulitple VCenter Servers and collect information with Powercli

I have a list of VCenter servers. They are on different locations and of different customers. I have created a text file with all the vcenter servers and credentials like below..I have more than 20 Vcenter Servers. I need to collect information of VM, Datastores, etc.(for which I have scripts).
Connect-VIServer vcenter0001 -User vcenter0001\sysdep -Password "Passwowrd1"
Connect-VIServer vcenter0002 -User vcenter0002\sysdep -Password "Passwowrd2"
I want to connect to each VCenter server and execute my scripts. Please help me. Thanks in Advance.
There's a couple ways to accomplish this, first you need to make sure that your configuration is set to allow for multiple connections. This is done with the following:
Set-PowerCLIConfiguration -DefaultVIServerMode Multiple
Note: It may also be necessary to run the following to enforce the change against all session scopes:
Set-PowerCLIConfiguration -DefaultVIServerMode Multiple -Scope User
Set-PowerCLIConfiguration -DefaultVIServerMode Multiple -Scope Session
Afterwards, you can pass multiple vCenter server names in string format or array format to the Connect-VIServer cmdlet to the 'Server' parameter.
Example using strings:
Connect-VIServer -Server vcenter0001,vcenter0002,vcenter0003 -User sysdep -Password "Password"
Example using an array:
$vCenterNames = #('vcenter0001','vcenter0002','vcenter0003')
Connect-VIServer -Server $vCenterNames -User sysdep -Password "Password"
Lastly, since it looks like you may be using local accounts instead of a single domain account, you could look at integrating the VICredentialStore. This saves your credentials in an XML file that will be referenced automatically at time of authentication.
Example Usage:
New-VICredentialStoreItem -Host vcenter0001 -User vcenter0001\sysdep -Password "Password"
New-VICredentialStoreItem -Host vcenter0002 -User vcenter0002\sysdep -Password "Password"
New-VICredentialStoreItem -Host vcenter0003 -User vcenter0003\sysdep -Password "Password"
Connect-VIServer -Server vcenter0001,vcenter0002,vcenter0003
Suppose you have a top secret csv file where you store the connection info (i.e. vi server fqdn, logon user name and passwords) that looked like this:
viserver, username, password
myfav.cust1.org, cust1usr, cust1pw
my2fav.cust2.net, cust2usr, cust2pw
myleastfav.cust3.com, cust3usr, cust3pw
and it was saved in: c:\mysecretdocs\custviservers.csv
you could use import-csv and a foreach statement to do your inventory dirty work with a function that looked something like this:
function get-vminventory
{
$viCntinfo = Import-Csv c:\mysecretdocs\custviservers.csv
foreach ($vi in $viCntInfo)
{
$convi = connect-viserver -server $vi.viserver -username $vi.username -password $vi.password
$vms = get-vm
$vms | select name, MemoryGB, NumCpu,
#{ n = "hostname"; e = { $_.guest.hostname } },
#{ n = "ip"; e = { $_.guest.ipaddress -join ", " } },
#{ n = "viserver"; e = { $convi.Name } }
$discvi = disconnect-viserver -server * -force -confirm:$false
}
}
You can run any of the PowerCLI inventory or custom commands there and select whatever output you want, that's just an example using Get-VM. Either dot source the function or just paste it into your shell. Then execute it and put the output in a csv like this:
get-vminventory | Export-Csv c:\mycustomerdata\vminfo.csv

cat lines into file after regex

I want to add the info below into the file usr/local/nagios/etc/hosts.cfg but want to do it just below ##company in the hosts.cfg file. My setup script will contain the info that needs to be added
I have spend hours trying to get sed to just add a line into a file after a marker but to no avail
define host{
use linux-box
host_name $host_name
alias $alias
address $ip
parents $parent
notification_period 24x7
notification_interval 5
}
Previously I used
cat <> /path /filename
EOT
but now I need to do it in specif places in the file
Given the following file:
# some content
###company
If I run the following command:
sed -i 's/###company/&\ndefine host {\nuse host\nhost_name HOSTNAME/' file
Now, the contents of file are:
# some content
###company
define host {
use host
host_name HOSTNAME
Is this what you're looking for?

How can i wait until something is written to log file in my perl script

I am actually Monitoring a directory for creation of new files(.log files) these files are generated by some tool and tool writes log entries after sometime of the creation of the same file, During this time file will be empty.
and how can i wait until something is written to the log and reason being based on the log entries i will be invoking different script!,
use strict;
use warnings;
use File::Monitor;
use File::Basename;
my $script1 = "~/Desktop/parser1.pl";
my $scrip2t = "~/Desktop/parser2.pl";
my $dir = "~/Desktop/tool/logs";
sub textfile_notifier {
my ($watch_name, $event, $change) = #_;
my #new_file_paths = $change->files_created; #The change object has a property called files_created,
#which contains the names of any new files
for my $path (#new_file_paths) {
my ($base, $fname, $ext) = fileparse($path, '.log'); # $ext is "" if the '.log' extension is
# not found, otherwise it's '.log'.
if ($ext eq '.log') {
print "$path was created\n";
if(-z $path){
# i need to wait until something is written to log
}else{
my #arrr = `head -30 $path`;
foreach(#arr){
if(/Tool1/){
system("/usr/bin/perl $script1 $path \&");
}elsif(/Tool1/){
system("/usr/bin/perl $script2 $path \&");
}
}
}
}
my $monitor = File::Monitor->new();
$monitor->watch( {
name => $dir,
recurse => 1,
callback => {files_created => \&textfile_notifier}, #event => handler
} );
$monitor->scan;
while(1){
$monitor->scan;
}
Basically i am grepping some of the important information from the logs.
For such formulation of your question, something like this might help you:
use File::Tail;
# for log file $logname
my #logdata;
my $file = File::Tail->new(name => $logname, maxinterval => 1);
while (defined(my $newline = $file->read)) {
push #logdata, $newline;
# the decision to launch the script according to data in #logdata
}
Read more here
You are monitoring just the log file creation. Maybe you could use a sleep function inside the call back sub to wait for the log file been wrote. You could monitor file changes too, because some log files could be extended.

Can't enable phar writing

I am actually using wamp 2.5 with PHP 5.5.12 and when I try to create a phar file it returns me the following message :
Uncaught exception 'UnexpectedValueException' with message 'creating archive "..." disabled by the php.ini setting phar.readonly'
even if I turn to off the phar.readonly option in php.ini.
So how can I enable the creation of phar files ?
I had this same problem and pieced together from info on this thread, here's what I did in over-simplified explanation:
in my PHP code that's generating this error, I added echo phpinfo(); (which displays a large table with all sort of PHP info) and in the first few rows verify the path of the php.ini file to make sure you're editing the correct php.ini.
locate on the phpinfo() table where it says phar.readonly and note that it is On.
open the php.ini file from step 1 and search for phar.readonly. Mine is on line 995 and reads ;phar.readonly = On
Change this line to phar.readonly = Off. Be sure that there is no semi-colon at the beginning of the line.
Restart your server
Confirm that you're phar project is now working as expected, and/or search on the phpinfo()table again to see that the phar.readonly setting has changed.
phar.readonly can only be disabled in php.ini due to security reasons.
If you want to check that it's is really not done using other method than php.ini then in terminal type this:-
$ php -r "ini_set('phar.readonly',0);print(ini_get('phar.readonly'));"
If it will give you 1 means phar.readonly is On.
More on phar.configuration
Need to disable in php.ini file
Type which php
Gives a different output depending on machine e.g.
/c/Apps/php/php-7.2.11/php
Then open the path given not the php file.
E.g. /c/Apps/php/php-7.2.11
Edit the php.ini file
could do
vi C:\Apps\php\php-7.2.11\php.ini
code C:\Apps\php\php-7.2.11\php.ini
[Phar]
; http://php.net/phar.readonly
phar.readonly = Off
; http://php.net/phar.require-hash
phar.require_hash = Off
Save
Using php-cli and a hashbang, we can set it on the fly without messing with the ini file.
testphar.php
#!/usr/bin/php -d phar.readonly=0
<?php
print(ini_get('phar.readonly')); // Must return 0
// make sure it doesn't exist
#unlink('brandnewphar.phar');
try {
$p = new Phar(dirname(__FILE__) . '/brandnewphar.phar', 0, 'brandnewphar.phar');
} catch (Exception $e) {
echo 'Could not create phar:', $e;
}
echo 'The new phar has ' . $p->count() . " entries\n";
$p->startBuffering();
$p['file.txt'] = 'hi';
$p['file2.txt'] = 'there';
$p['file2.txt']->compress(Phar::GZ);
$p['file3.txt'] = 'babyface';
$p['file3.txt']->setMetadata(42);
$p->setStub('<?php
function __autoload($class)
{
include "phar://myphar.phar/" . str_replace("_", "/", $class) . ".php";
}
Phar::mapPhar("myphar.phar");
include "phar://myphar.phar/startup.php";
__HALT_COMPILER();');
$p->stopBuffering();
// Test
$m = file_get_contents("phar://brandnewphar.phar/file2.txt");
$m = explode("\n",$m);
var_dump($m);
/* Output:
* there
**/
✓ Must be set executable:
chmod +x testphar.php
✓ Must be called like this:
./testphar.php
// OUTPUT there
⚠️ Must not be called like this:
php testphar.php
// Exception, phar is read only...
⚠️ Won't work called from a CGI web server
php -S localhost:8785 testphar.php
// Exception, phar is read only...
For anyone who has changed the php.ini file, but just doesn't see any changes. Try to use the CLI version of the file. For me, it was in /etc/php/7.4/cli/php.ini
Quick Solution!
Check:
cat /etc/php/7.4/apache2/php.ini | grep phar.readonly
Fix:
sed -i 's/;phar.readonly = On/;phar.readonly = Off/g' /etc/php/7.4/apache2/php.ini

VisualSVN post-commit hook with batch file

I'm running VisualSVN on a Windows server.
I'm trying to add a post-commit hook to update our staging project whenever a commit happens.
In VisualSVN, if I type the command in the hook/post-commit dialog, everything works great.
However, if I make a batch file with the exact same command, I get an error that says the post-commit hook has failed. There is no additional information.
My command uses absolute paths.
I've tried putting the batch file in the VisualSVN/bin directory, I get the same error there.
I've made sure VisualSVN has permissions for the directories where the batch file is.
The only thing I can think of is I'm not calling it correctly from VisualSVN. I'm just replacing the svn update command in the hook/post-commit dialog with the batch file name ("c:\VisualSVN\bin\my-batch-file.bat") I've tried it with and without the path (without the path it doesn't find the file at all).
Do I need to use a different syntax in the SVNCommit dialog to call the batch file? What about within the batch file (It just has my svn update command. It works if I run the batch file from the command line.)
Ultimately I want to use a batch file because I want to do a few more things after the commit.
When using VisualSVN > Select the Repo > Properties > Hooks > Post-commit hook.
Where is the code I use for Sending an Email then running a script, which has commands I want to customize
"%VISUALSVN_SERVER%\bin\VisualSVNServerHooks.exe" ^
commit-notification "%1" -r %2 ^
--from support#domainname.com --to "support#domainname.com" ^
--smtp-server mail.domainname.com ^
--no-diffs ^
--detailed-subject
--no-html
set PWSH=%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe
%PWSH% -command $input ^| C:\ServerScripts\SVNScripts\post-commit-wp.ps1 %1 %2
if errorlevel 1 exit %errorlevel%
The script file is located on C:\ServerScripts\SVNScripts\
post-commit-wp.ps1 and I pass in two VisualSVN variables as %1 and %2
%1 = serverpathwithrep
%2 = revision number
The script file is written in Windows PowerShell
# PATH TO SVN.EXE
$svn = "C:\Program Files\VisualSVN Server\bin\svn.exe"
$pathtowebistesWP = "c:\websites-wp\"
# STORE HOOK ARGUMENTS INTO FRIENDLY NAMES
$serverpathwithrep = $args[0]
$revision = $args[1]
# GET DIR NAME ONLY FROM REPO-PATH STRING
# EXAMPLE: C:\REPOSITORIES\DEVHOOKTEST
# RETURNS 'DEVHOOKTEST'
$dirname = ($serverpathwithrep -split '\\')[-1]
# Combine ServerPath with Dir name
$exportpath = -join($pathtowebistesWP, $dirname);
# BUILD URL TO REPOSITORY
$urepos = $serverpathwithrep -replace "\\", "/"
$url = "file:///$urepos/"
# --------------------------------
# SOME TESTING SCRIPTS
# --------------------------------
# STRING BUILDER PATH + DIRNAME
$name = -join($pathtowebistesWP, "testscript.txt");
# CREATE FILE ON SERVER
New-Item $name -ItemType file
# APPEND TEXT TO FILE
Add-Content $name $pathtowebistesWP
Add-Content $name $exportpath
# --------------------------------
# DO EXPORT REPOSITORY REVISION $REVISION TO THE ExportPath
&"$svn" export -r $revision --force "$url" $exportpath
I added comments to explain each line and what it does. In a nutshell, the scripts:
Gets all the parameters
Build a local dir path
Runs SVN export
Places files to a website/publish directory.
Its a simple way of Deploying your newly committed code to a website.
Did you try to execute batch file using 'call' command? I mean:
call C:\Script\myscript.bat
I was trying the same thing and found that you also must have the script in the hooks folder.. the bat file that is.