How do I change the hostname using Python on a Raspberry Pi - python

I tried using (going from memory, this may not be 100% accurate):
import socket
socket.sethostname("NewHost")
I got a permissions error.
How would I approach this entirely from within the Python program?

If you only need to do change the hostname until the next reboot, many linux system can change it with:
import subprocess
subprocess.call(['hostname', 'newhost'])
or with less typing but some potential pitfalls:
import os
os.system('hostname %s' % 'newhost')

I wanted to change the hostname permanently, which required making changes in a few places, so I made a shell script:
#!/bin/bash
# /usr/sbin/change_hostname.sh - program to permanently change hostname. Permissions
# are set so that www-user can `sudo` this specific program.
# args:
# $1 - new hostname, should be a legal hostname
sed -i "s/$HOSTNAME/$1/g" /etc/hosts
echo $1 > /etc/hostname
/etc/init.d/hostname.sh
hostname $1 # this is to update the current hostname without restarting
In Python, I ran the script with subprocess.run:
subprocess.run(
['sudo', '/usr/sbin/change_hostname.sh', newhostname])
This was happening from a webserver which was running as www-data, so I allowed it to sudo this specific script without a password. You can skip this step and run the script without sudo if you're running as root or similar:
# /etc.d/sudoers.d/099-www-data-nopasswd-hostname
www-data ALL = (root) NOPASSWD: /usr/sbin/change_hostname.sh

Here is a different approach
import os
def setHostname(newhostname):
with open('/etc/hosts', 'r') as file:
# read a list of lines into data
data = file.readlines()
# the host name is on the 6th line following the IP address
# so this replaces that line with the new hostname
data[5] = '127.0.1.1 ' + newhostname
# save the file temporarily because /etc/hosts is protected
with open('temp.txt', 'w') as file:
file.writelines( data )
# use sudo command to overwrite the protected file
os.system('sudo mv temp.txt /etc/hosts')
# repeat process with other file
with open('/etc/hostname', 'r') as file:
data = file.readlines()
data[0] = newhostname
with open('temp.txt', 'w') as file:
file.writelines( data )
os.system('sudo mv temp.txt /etc/hostname')
#Then call the def
setHostname('whatever')
At the next reboot the hostname will be set to the new name

Related

How can I make python change the characters in a batch file?

I'm making a script that changes your dns and then pings a website to test latency and I've created a list with all the DNS and I want to use an external batch script to change the dns. However, I'm reasonably new to python and I don't know how to make python take data from the list and replace it in the batch file. This would help me very much, thank you!
**Python script **
from tcp_latency import measure_latency
host = input("Enter host: ")
def pinger():
latency = sum(measure_latency(host, port=80, runs=10, timeout=2.5))
latency = latency/10
print("Your average latency is",latency)
dns = ["1.1.1.1","1.0.0.1","8.8.8.8","8.8.4.4","9.9.9.9","149.112.112.112","208.67.222.222","208.67.220.220","8.26.56.26","8.20.247.20","185.228.168.9","185.228.169.9"]
Batch script
#echo off
cls
for /F "skip=3 tokens=1,2,3* delims= " %%G in ('netsh interface show interface') DO (
IF "%%H"=="Disconnected" netsh interface set interface "%%J" enabled
IF "%%H"=="Connected" netsh interface set interface "%%J" enabled
echo %%J
netsh interface ip set dns %%J static 1.1.1.1
)
I haven't tried any approaches just yet
Simple string replacement should work nicely
dns = ["1.1.1.1","1.0.0.1","8.8.8.8","8.8.4.4","9.9.9.9","149.112.112.112","208.67.222.222","208.67.220.220","8.26.56.26","8.20.247.20","185.228.168.9","185.228.169.9"]
# Assumes .bat and .py scripts are in the same directory
bat_file = "tester.bat"
# Read original .bat file
with open(bat_file, "r") as fs:
bat_str = fs.read()
base_name = bat_file.split(".")[0]
for dns_ip in dns:
new_bat_str = bat_str.replace("1.1.1.1", dns_ip)
# Parse new name for .bat file
new_bat_file = f"{base_name}_dns_{dns_ip.replace('.', '')}.bat"
with open(new_bat_file, "w") as fs:
fs.write(new_bat_str)

Attempt to generate email with new lines from Python through Bash mailx command

Where I work, we have servers that are pre-configured for the use of the bash mail command to send attachments and messages. I'm working on a notification script that will monitor server activity and generate an email if it detects an issue. I'm using the subprocess.call function in order to send a bash command.
I am successful in sending messages, but in the body portion of the email, it is stringing each notification line together rather than putting each notification on a separate line. I have tried to append each line within the string with "\n" and "\r\n". I have to use double backslashes as python will interpret this as literal new lines when it sends the echo command. I also passed the command "shopt -s xpg_echo" before using the echo with pipe to mail using the double backspaces but this also had no effect. I also tried using echo without the "-e" option and this had no effect either.
The trick is that I need python to send the new line to bash and then somehow get bash to interpret this as a new line using echo piped through to mail. Here is a sample of the code:
import os
import shutil
import sys
import time
import re
import subprocess
import smtplib
serviceports["SCP Test"] = ["22"]
serviceports["Webtier"] = ["9282"]
bashCommand = "netstat -an | grep LISTEN | grep -v LISTENING"
netstat_results = subprocess.check_output(bashCommand, shell=True)
netstat_results = str(netstat_results)
#Iterate through all ports for each service and assign down ports to variable
for servicename, ports in serviceports.items():
for ind_port in ports:
ind_port_chk = ":" + ind_port
count = sum(1 for _ in re.finditer(r'\b%s\b' % re.escape(ind_port_chk), netstat_results))
if count == 0:
warning = servicename + " on port " + ind_port + " is currently down!"
report.append(warning)
for warning in report:
message = message + warning + "\\\n"
fromaddr=serveridsimp + "#xxxxx.com"
toaddr='email#xxxxx.com'
subject="Testing..."
body=message
cmd= cmd='echo -e '+body+' | mail -s '+subject+' -r '+fromaddr+' '+toaddr
send=subprocess.call(cmd,shell=True)
The code runs a netstat command and assigns it to a string. The code will then iterate through the specified ports and search for where that port doesn't exist in the netstat string (netstat_results). It then will create a list object (warning) containing all the ports not located in netstat_results and then append each line adding \n to a string called "message". It then sends an echo piped to the xmail command to generate an email to be sent containing all the ports not found. What happens currently is that I will get an email saying something like this:
SCP Test on port 22 is currently down!nOHS Webtier on port 9282 is currently down!n etc...
I want it to put each message on a new line like so:
SCP Test on port 22 is currently down!
Webtier on port 9282 is currently down!
I am trying to avoid writing the output to a file and then using bash to read it back into the mail command. Is this possible without having to create a file?
I was finally able to fix the issue by changing the command sent to bash and character being appended to the following:
message = message + warning + "\n"
cmd= cmd='echo -e '+'"'+body+'"'+'|awk \'{ print $0" " }\''+' | mail -s '+'"'+subject+'"'+' -r '+fromaddr+' '+toaddr

How to execute 'su' command using parallel-ssh

I want to log in to two hosts using parallel-ssh and execute su command. Then I want to confirm that I am the root user by printing out whoami
Code:
hosts = ['myHost1', 'myHost2']
client = ParallelSSHClient(hosts, user='myUser', password='myPassword')
output = client.run_command('su')
for host in output:
stdin = output[host].stdin
stdin.write('rootPassword\n')
stdin.flush()
client.join(output)
output = client.run_command('whoami')
for host, host_output in output.items():
for line in host_output.stdout:
print("Host [%s] - %s" % (host, line))
Result:
Host [myHost1] - myUser
Host [myHost2] - myUser
Obviously, I expect root in the output. I am following the documentation.
I've tried using all different line endings instead of \n and nothing has changed.
How can I execute su command using parallel-ssh?
Try this:
**def exec_command(hosts):
strr = ""
client = ParallelSSHClient(hosts, user='admin', password='admin_password')
cmd = 'echo root_password |su -c "commmand" root'
output = client.run_command(cmd)
client.join()
for host_out in output:
for line in host_out.stdout:
strr+=line+" "
return strr
**
'echo root_password |su -c "command" root'
try to put sudo=True at the end of run_command
output = client.run_command(<..>, sudo=True)
like in docs
It turns out that what I am trying to do is not achievable.
The first problem
I found in this post that all commands are in their own channel. That means that even if su would be successful it wouldn't affect the second command. The author of the post recommends running
su -c whoami - root
The second problem
I managed to debug the problem even further by changing host_output.stdout to host_output.stderr It turned out that I receive an error which previously was not being shown on the terminal:
standard in must be a tty
Possible solutions to this problem are here . They didn't work for me but might work for you.
For me workaround was to allow on all my hosts root login. And then in parallel-ssh I log in as a root already with all the rights in place.

Keyerror in reading csv file

I am trying to write a script where I pass file name as argument from shell script to python script and python script processes that script.It is giving me keyerror but if I run the same script hardcoding the file name it works fine.
#!/bin/sh
LOCKFILE=./test.txt
if [ -e ${LOCKFILE} ] && kill -0 `cat ${LOCKFILE}`; then
echo "already running"
exit
fi
trap "rm -f ${LOCKFILE}; exit" INT TERM EXIT
echo $$ > ${LOCKFILE}
# do stuff
FILES=/home/sugoi/script/csv/*
for file in $FILES
do
python ./csvTest.py $file
#mv $file ./archive
done
rm -f ${LOCKFILE}
exit
Python:
from pymongo import MongoClient
import csv
import json
import sys
client = MongoClient()
db = client.test
for arg in sys.argv:
try:
csvfile = open(arg, 'r')#if i hardcode file name here it works fine
except IOError as e:
#write to error log
sys.exit(100)
reader = csv.DictReader(csvfile)
header=reader.next()
for each in reader:
row={}
for field in header:
row[field]=each[field]
db.test.update({"_id": row["CustomerId"]}, {"$push": {"activities":{"action": row["Action"],"date" :row["Timestamp"],"productId":row["productId"]}}},True)
What am I doing wrong ?
Two issues.
Your shell script isn't expanding the file list correctly.
FILES=/home/sugoi/script/csv/* needs to be something like:
FILES=`ls -1 /home/sugoi/script/csv/*;`
Your argument to the python script will only be one file at a time, so why loop through sys.argv?
Just use the argument itself, sys.argv[1]. As #Brian Besmanoff pointed out, that needs to be indexed 1 because the script name itself is stored in sys.argv[0].
try:
csvfile = open(sys.argv[1], 'r')
except IOError as e:
(...)
Finally: you can just parse directories with Python instead of looping in a shell script. Look at the os module, particularly os.listdir(). A little more work and you can have the whole thing running inside one Python script instead of juggling between shell and calling a script.
The first value in sys.argv is going to be the name of the script. reference

Unix `at` scheduling with python script: Permission denied

I'm trying to create a scheduled task using the Unix at command. I wanted to run a python script, but quickly realized that at is configured to use run whatever file I give it with sh. In an attempt to circumvent this, I created a file that contained the command python mypythonscript.py and passed that to at instead.
I have set the permissions on the python file to executable by everyone (chmod a+x), but when the at job runs, I am told python: can't open file 'mypythonscript.py': [Errno 13] Permission denied.
If I run source myshwrapperscript.sh, the shell script invokes the python script fine. Is there some obvious reason why I'm having permissions problems with at?
Edit: I got frustrated with the python script, so I went ahead and made a sh script version of the thing I wanted to run. I am now finding that the sh script returns to me saying rm: cannot remove <filename>: Permission denied (this was a temporary file I was creating to store intermediate data). Is there anyway I can authorize these operations with my own credentials, despite not having sudo access? All of this works perfectly when I run it myself, but everything seems to go to shit when I have at do it.
Start the script using python not the actual script name, ex : python path/to/script.py.
at tries to run everything as a sh script.
EDIT: The at command tries running everything as a list of shell commands. So you should start your script like this:
at now + 1 minute < python mypythonscript.py
In this case, the #! line at the beginning of the script is not necessary.
I have been working on task scheduling between servers and clients recently. I just abstracted out my scheduling code and put it up on Github. It was meant to schedule several simulations across multiple machines that have all simulations in their filesystems. The idea is that since each machine had a different processor, it would compute each simulation, scp the results back into the server and request the server for the next simulation. The server responds by scheduling a task on the client to run the next unrun simulation
Hope this will help you.
NOTE: Since I only abstracted and uploaded the files about 5 minutes ago, I haven't had the chance to test the abstractions. However, if you come across any bugs, please let me know and I'll debug then as soon as I can.
Github seems to be down now. So here are the files that you'll need:
On the server:
serverside
#!/bin/bash
projectDir=~/
minute=`atq | sort -t" " -k1 -nr | head -n1 | cut -d' ' -f4 | cut -d":" -f1,2`
curr=`date | cut -d' ' -f4 | cut -d':' -f1,2`
time=`python -c "import sys; hour,minute=map(int,max(sys.argv[1:]).split(':')); minute += 2; hour, minute = [(hour,minute), ((hour+1)%24,minute%60)][minute>=60]; print '%d:%02d'%(hour, minute)" "$minute" "$curr"`
cat <<EOF | at "$time"
python $projectDir/serverside.py $1
EOF
serverside.py
import sys
import time
import smtplib
import subprocess
import os
import itertools
IP = sys.argv[1].strip()
PROJECT_DIR = "" # relative path (relative to the home directory) to the root directory of the project, which contains all subdirs containing simulation files
USERS = { # keys are IPs of the clients, values are user names on those clients
}
HOMES = { # keys are the IPs of clients, values are the absolute paths to the home directories on these clients for the usernames on these clients identified in USERS
}
HOME = None # absolute path to the home directory on the server
SMTP_SERVER = ""
SMTP_PORT = None
FROM_ADDR = None # the email address from which notification emails will be sent
TO_ADDR = None # the email address to which notification emails will be sent
def get_next_simulation():
""" This function returns a list.
The list contains N>0 elements.
Each of the first N-1 elements are names of directories (not paths), which when joined together form a relative path (relative from PROJECT_DIR).
The Nth element is the name of the file - the simulation to be run.
Before the end user implements this function, it is assumed that N=3.
Once this function has been implemented, if N!=3, change the code in the lines annotated with "Change code for N in this line"
Also look for this annotation in clientside.py and clientsideexec """
pass
done = False
DIR1, DIR2, FILENAME = get_next_simulation() # Change code for N in this line
while not done:
try:
subprocess.check_call("""ssh %(user)s#%(host)s 'sh %(home)s/%(project)/clientside %(dir1)s %(dir2)s %(filename)s %(host)s' """ %{'user':USER, 'host':IP, 'home':HOME[IP], 'project':PRJECT_DIR, 'dir1':DIR1, 'dir2':DIR2, 'filename':FILENAME}, shell=True) # Change code for N in this line
done = True
os.remove("%(home)s/%(project)/%(dir1)s/%(dir2)s/%(filename)s" %{'home':HOME, 'project':PROJECT_DIR, 'dir1':DIR1, 'dir2':DIR2, 'filename':FILENAME}) # Change code for N in this line
sm = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
sm.sendmail(FROM_ADDR, TO_ADDR, "running %(project)s/%(dir1)s/%(dir2)s/%(filename)s on %(host)s" %{'project':PROJECT_DIR, 'dir1':DIR1, 'dir2':DIR2, 'filename':FILENAME, 'host':IP}) # Change code for N in this line
except:
pass
On the client:
clientside
#!/bin/bash
projectpath=~/
python $projectpath/clientside.py "$#"
clientside.py
import subprocess
import sys
import datetime
import os
DIR1, DIR2, FILENAME, IP = sys.argv[1:]
try:
subprocess.check_call("sh ~/cisdagp/clientsideexec %(dir1)s %(dir2)s %(filename)s %(ip)s" %{'dir1':, 'dir2':, 'filename':, ip':IP}, shell=True, executable='/bin/bash') # Change code for N in this line
except:
pass
clientsideexec
#!/bin/bash
projectpath=~/
user=''
serverIP=''
SMTP_SERVER=''
SMTP_PORT=''
FROM_ADDR=''
TO_ADDR=''
MESSAGE=''
cat <<EOF | at now + 2 minutes
cd $projectpath/$1/$2 # Change code for N in this line
sh $3
# copy the logfile back to the server
scp logfile$3 $user#$serverIP:$projectpath/$1/$2/
cd $projectpath
python -c "import smtplib; sm = smtplib.SMTP('$SMTP_SERVER', $SMTP_PORT); sm.sendmail('$FROM_ADDR', '$TO_ADDR', '$MESSAGE')"
python clientsiderequest.py
EOF
Could you try: echo 'python mypythonscript.py' | at ...

Categories