Pasting my python code below:
from crontab import CronTab
tab = CronTab()
cmd1 = 'actual command'
cron_job = tab.new(cmd)
cron_job.minute.every(1)
cron_job.enable()
tab.write_to_user(user=True)
I am setting these cronjob based on some user input on an app. So overtime a user clicks submit, I want to create a new cronjob under the same crontab file (since its a webapp, it runs under the context of a standard user).
But it seems every time a user clicks submit, my crontab overwrites the previous task. Am I missing something here?
The way you are recreating the tab variable on each request does not append to the crontab in question; it overwrites it.
If you pass user=True to the CronTab constructor you will be able to append to the existing crontab:
from crontab import CronTab
tab = CronTab(user=True)
cmd1 = 'echo Hello'
cmd2 = 'echo CronTab'
job1 = tab.new(cmd1)
job1.minute.every(1)
job1.enable()
job2 = tab.new(cmd2)
job2.hour.every(1)
job2.enable()
print list(tab.commands) # ['echo Hello', 'echo CronTab']
# Explicitly specify we want the user's crontab:
tab2 = CronTab(user=True)
print list(tab2.commands) # ['echo Hello', 'echo CronTab']
Related
I am running a python file that, among other things, opens and runs a subprocess that runs the 'speedtest-cli --simple' command line. When executed normally, this works fine and returns the ping, download and upload, but when called with crontab, the values are blank. Values are normally only blank if an internet connection can not be established.
#!/usr/bin/env python
#Speed test opens a subprocess that runs the speed test from the command terminal, returning ping, download and upload speeds.
def speedTest():
cmd = 'speedtest-cli --simple' #Command to be run in terminal
envi = os.environ
response = subprocess.Popen(cmd, env=envi, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.read().decode('utf-8')#Run subprocess
try: #Try to pull relevant data
ping = re.findall('Ping:\s(.*?)\s', response, re.MULTILINE)
download = re.findall('Download:\s(.*?)\s', response, re.MULTILINE)
upload = re.findall('Upload:\s(.*?)\s', response, re.MULTILINE)
except: #-2 Error code for Speedtest Error, entered if pull fails (NOTE: even if the pull isn't able to connect, the try should still succeed. This case should not be entered.)
print('failed')
ping = ['-2']
download = ['-2']
upload = ['-2']
errflag = -2 #Set errflag to -2 to track error
pass
if (ping == []): #-3 Error code for Speedtest Connection Error. Entered if pull is successful but connection could not be made.
ping.append('-3')
download.append('-3')
upload.append('-3')
errflag = -3 #Set errflag to -3 to track error
return [ping[0],download[0],upload[0]] #Return ping, download, and upload.
the crontab that runs it is shown here
Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').
#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command
* * * * * /home/pi/datatest.py >> out.txt 2>&1
Any suggestions on how to obtain output values in crontab would be much appreciated.
I want to run the shell command 'su - testuser -c "id"' and get the output. In the console it asks the password after that. My intention is to run it in a python script where it logs into antoher user (neither the source nor the destination user has root rights). The problem is, that the password should be entered non-interactive, so that I can just start the script and see the output without entering the password. So I can start the python script and it automatically runs the command without waiting for the password and gives me the output.
I tried it using the pexpect package:
child = pexpect.spawn('su - testuser -c "id"')
child.expect_exact('Password:')
child.sendline('mypassword')
print(child.before) # Prints the output of the "id" command to the console
The problem is that the code doesn't function. The output is like a random string instead of the id and so on. How can I do that?
Using child.read instead of print(child.before) solves it.
>>> child = pexpect.spawn('su - testuser -c "id"')
>>> child.expect_exact('Password:')
0
>>> child.sendline('1234')
5
>>> print(child.before)
b''
>>> child.read()
b' \r\nuid=1002(testuser) gid=1003(testuser) groups=1003(testuser)\r\n'
You can read more details on here
I want to execute a python script, which switches to another user by automatically writing the user password. Both users have no root rights. After the login I want to execute the OS Commands "whoami" to check if the login was successful. Here's the code:
child = pexpect.spawn('su - otheruser)
child.expect_exact('Password:')
child.sendline('password')
print("logged in...")
child.expect('')
child.sendline('whoami')
print(child.before)
I want to print the output from the command to the console (just for debugging) but the output is like "b272' (a combination of random letters) and not the actual whoami user. How can I fix that?
Later I want to create from the switched user some files and so on. So basically, I want to execute OS Commands in a python script which is logged in an other user.
Pexpect searches are not greedy, so it will stop at the first match. When I tested your code with before, match.groups(), after, and buffer, I didn't get an EOF or TIMEOUT, so it must have matched right at the beginning of the read and returned nothing (I'm surprised you got any results at all).
I recommend always following a sendline with an expect, and the end of a prompt (]$) is a good thing to expect, instead of an empty string.
Here is my take on your code, including creating a file:
NOTE - Tested on Centos 7.9, using Python 2.7.
import pexpect
child = pexpect.spawn("su - orcam")
child.expect_exact("Password:")
child.sendline("**********")
child.expect_exact("]$")
print("Logged in...\n")
child.sendline("whoami")
child.expect_exact("]$")
print(child.before + "\n")
child.sendline("echo -e 'Hello, world.' >> hello.txt")
child.expect_exact("]$")
child.sendline("cat hello.txt")
child.expect_exact("]$")
print(child.before + "\n")
child.sendline("exit")
index = child.expect_exact(["logout", pexpect.EOF, ])
print("Logged out: {0}".format(index))
Output:
Logged in...
whoami
orcam
[orcam#localhost ~
cat hello.txt
Hello, world.
[orcam#localhost ~
Logged out: 0
I would like to ask for help with the python-crontab module. I have a simple shell script to record an internet radio stream using the curl command. I want to schedule recordings ahead by scheduling them in a crontab. I found the python-crontab module that alows me to write directly to crontab. But each time I schedule a new recording the older crontab entry is over-written. Is it possible to write persistant crontab entries using the python-crontab?
I simplified my code to demonstrate my problem:
from crontab import CronTab
def get_recording_parameters(Min,Hour,day,month,job_number):
radio_cron = CronTab()
cmd = "sh /home/pifik/Documents/record_radio.sh"
cron_job = radio_cron.new(cmd, comment='job_'+str(job_number))
cron_job.setall(Min, Hour, day, month, None)
radio_cron.write()
If I run it with the following parameters: get_recording_parameters(0,22,23,12,1), and check the crontab in Terminal with the crontab -l command I get 0 22 23 12 * sh /home/pifik/Documents/record_radio.sh # job_1.
If I run it again with different parameters, for example: get_recording_parameters(10,23,25,12,2) and check the crontab with crontab -l I get 10 23 25 12 * sh /home/pifik/Documents/record_radio.sh # job_2, the job 1 is overwritten.
I tried to change the 3rd line of code to radio_cron = CronTab(tabfile='/home/pifik/Documents/filename.tab') and it helps that all new entries are appended in the filename.tab but nothing is written to the crontab.
I am running Ubuntu 14.04 and Python 3.4.3.
It looks like each time you add a job and write it back out it's overwriting what was already in the crontab. I read the documentation and I can't make heads or tails out of it. It seems you should be able to read in what was already there and add to it but for the life of me I can't figure out how from the docs.
You can get around that issue by re-working it as a class that puts together all the jobs before it writes them back out. Of course that paints you into the same corner you're already in which is appending doesn't work (unless you lay in the old entries again before writing):
#!/home/sklassen/py/try-pycrontab/env/bin/python
from crontab import CronTab
class CronSet:
def __init__(self):
self._crontab = CronTab()
def add_job(self, min, hour, day, month, job_number):
cmd = "sh /home/pifik/Documents/record_radio.sh"
job = self._crontab.new(cmd, comment='job'+str(job_number))
job.setall(min, hour, day, month, None)
def save(self):
self._crontab.write()
def main():
c = CronSet()
c.add_job(0, 22, 23, 12, 1)
c.add_job(0, 23, 23, 12, 2)
c.save()
if __name__ == '__main__':
main()
# running 'crontab -l' produces the following
# 0 22 23 12 * sh /home/pifik/Documents/record_radio.sh # job1
# 0 23 23 12 * sh /home/pifik/Documents/record_radio.sh # job2
I modified Steven's code to make it work for me.
In order not to lose the previously scheduled crontab jobs I create a cron_jobs.txt file and copy all existing scheduled jobs from crontab to cron_jobs.txt with the subprocess call "crontab -l > /home/pifik/Documents/cron_jobs.txt". Every time I do it I overwrite everything in the file. Then I create a new recording job and append it to the cron_jobs.txt file. After that I overwrite the crontab by running "subprocess.call('crontab /home/pifik/Documents/cron_jobs.txt', shell=True)".
This is a workaround to make it work but I am still interested to know if it is possible to use the python-crontab module to append new jobs directly without the need for the cron_jobs.txt file. Arguably, the way I do it now, it could be also done without the module but the module makes it easier for me to further manage the jobs by activating or deactivating them and deleting the expired jobs. I will put some tkinter GUI on it and I'll be done for now.
import subprocess
from crontab import CronTab
class CronSet:
def __init__(self):
self._crontab = CronTab(tabfile="/home/pifik/Documents/cron_jobs.txt")
def add_job(self, minute, hour, day, month, title):
subprocess.call('crontab -l > /home/pifik/Documents/cron_jobs.txt', shell=True)
choice=input('''1. Cesky Rozhlas 1
2. Cesky Rozhlas 2
Enter your choice (1 or 2): ''')
length = int(input("Enter the length of recording in minutes: "))*60
if choice ==str(1):
stream = "http://amp.cesnet.cz:8000/cro1-256.ogg"
else:
stream = "http://amp.cesnet.cz:8000/cro2-256.ogg"
cmd = "curl %s -m %i -o /home/pifik/Documents/Recordings/%s.ogg" %(stream, length, title)
job = self._crontab.new(cmd, comment=title)
job.setall(minute, hour, day, month, None)
def save(self):
self._crontab.write()
def main():
c = CronSet()
month = input("Enter month(1-12): ")
day = input("Enter day(1-31): ")
hour = input("Enter hour(0-24): ")
minute = input("Enter minute(0-59): ")
title = input("Enter title of recording: ")
c.add_job(minute, hour, day, month, title)
c.save()
subprocess.call('crontab /home/pifik/Documents/cron_jobs.txt', shell=True)
if __name__ == '__main__':
main()
The issue is that you haven't specified any user or filename for CronTab to load from. So it doesn't. The bug is that it writes out the empty crontab to the user by default, even if you don't specify the user.
The existing documentation says:
from crontab import CronTab
empty_cron = CronTab()
my_user_cron = CronTab(user=True)
users_cron = CronTab(user='username')
Which is correct in that you're making an empty crontab. So I've gone ahead and committed a fix and a test to make sure it causes an error if you try and write an empty_cron without specifying the user or filename.
Please add user=True to your code to make it work as you expect.
Say I have a fabfile.py that looks like this:
def setup():
pwd = getpass('mysql password: ')
run('mysql -umoo -p%s something' % pwd)
The output of this is:
[host] run: mysql -umoo -pTheActualPassword
Is there a way to make the output look like this?
[host] run: mysql -umoo -p*******
Note: This is not a mysql question!
Rather than modifying / overriding Fabric, you could replace stdout (or any iostream) with a filter.
Here's an example of overriding stdout to censor a specific password. It gets the password from Fabric's env.password variable, set by the -I argument. Note that you could do the same thing with a regular expression, so that you wouldn't have to specify the password in the filter.
I should also mention, this isn't the most efficient code in the world, but if you're using fabric you're likely gluing a couple things together and care more about manageability than speed.
#!/usr/bin/python
import sys
import string
from fabric.api import *
from fabric.tasks import *
from fabric.contrib import *
class StreamFilter(object):
def __init__(self, filter, stream):
self.stream = stream
self.filter = filter
def write(self,data):
data = data.replace(self.filter, '[[TOP SECRET]]')
self.stream.write(data)
self.stream.flush()
def flush(self):
self.stream.flush()
#task
def can_you_see_the_password():
sys.stdout = StreamFilter(env.password, sys.stdout)
print 'Hello there'
print 'My password is %s' % env.password
When run:
fab -I can_you_see_the_password
Initial value for env.password:
this will produce:
Hello there
My password is [[TOP SECRET]]
It may be better to put the password in the user's ~/.my.cnf under the [client] section. This way you don't have to put the password in the python file.
[client]
password=TheActualPassword
When you use the Fabric command run, Fabric isn't aware of whether or not the command you are running contains a plain-text password or not. Without modifying/overriding the Fabric source code, I don't think you can get the output that you want where the command being run is shown but the password is replaced with asterisks.
You could, however, change the Fabric output level, either for the entire Fabric script or a portion, so that the command being run is not displayed. While this will hide the password, the downside is that you wouldn't see the command at all.
Take a look at the Fabric documentation on Managing Output.
Write a shell script that invokes the command in question with the appropriate password, but without echoing that password. You can have the shell script lookup the password from a more secure location than from your .py files.
Then have fabric call the shell script instead.
This solves both the problem of having fabric not display the password and making sure you don't have credentials in your source code.
from fabric.api import run, settings
with settings(prompts={'Enter password: ': mysql_password}):
run("mysql -u {} -p -e {}".format(mysql_user,mysql_query))
or if no prompt available:
from fabric.api import run, hide
with hide('output','running','warnings'):
run("mycommand --password {}".format(my_password))