Tag Archives: python

SABnzbd Installation

SABnzbd Installation for CentOS 7.x

Introduction

SABnzbd is a versatile tool written in Python specifically designed to take an NZB-File as input and then retrieve all of the content defined inside of it for you automatically. Recently there have been new improvements with SABnzbd in the past few releases. So I thought I’d share a working out of the box solution using RPMs. In addition to this, I’ve packaged up my notification script I wrote which can further enhance this great product.

SABnzbd and Usenet

First off; lets summarize Usenet for those who don’t use it or aren’t already familiar with it. In the simplest terms: Usenet is a great big fucking mess; a total disaster. It’s basically a location where literally anyone with access to it can post/upload all they want at their free will anonymously. Don’t get me wrong; this is cool too because it’s basically one very large hard drive without anyone telling you what you can and can’t place onto it. Seriously though, just consider for a second what your computer would look like if you allowed absolutely anyone who has access to the internet to upload content freely and without rules to it. You’d probably run out of hard disk space quickly; and you’ll be left with a lot of content everywhere. Well; that’s exactly what Usenet is (greatly simplified); it’s one large centralized location filled with petabytes of data. The thing with Usenet though is… it isn’t, and won’t be running out of disk space anytime soon.

The good news is, (thankfully) people sift through the heaps of information constantly being posted (onto Usenet) using automated tools regularly. These tools that do the sifting are generally known as Indexers. I talk more about this in another blog I wrote here a while back. But basically whenever an Indexer finds some useful data, it records the location as to where it was found. Indexers record this information in a special file call an NZB-File. NZB-Files are effectively treasure maps containing the coordinates on Usenet to which a specific piece of data can be located at.

NZB-Files are like Treasure Maps

NZB-Files are like Treasure Maps

It should be known that if you’re planning on posting/uploading stuff to Usenet yourself (for backup purposes or what have you), most (good) software will generate you an NZB-File afterwards allowing you to retrieve your data back again later on.

Now lets throw SABnzbd into the picture because it specifically is designed to retrieve content based on an NZB-File it’s provided. The process looks a little like this:

SABnzbd Download Process

SABnzbd Download Process

  1. Acquire NZB-File: Presumably you already have it because you’re just trying to retrieve data you posted in the past. But alternatively, the NZB-File could have also been acquired from and Indexer too.
  2. Give SABnzbd Access to NZB-File: You simply hand off your treasure map to the application who’s actually going to go out there and get the content for you.

    This is truly the bread and butter of SABnzbd. The next set of steps identified below are all fully automated (and done behind the scenes) for you. They require nothing from you, but it’s worth explaining it for those who are interested.

  3. SABnzbd Establishes a Connection to Usenet: The catch with SABnzbd is it’s merely a vessel for fetching the data. You need to have provided it access to Usenet to which it will fetch this data from. Thus SABnzbd must (obviously) connect to a Usenet provider in order to retrieve data from Usenet itself. This step is put in place to make sure you’ve got yourself an account with a provider!
  4. SABnzbd Downloads Content: Segment by segment, each portion of your data is downloaded and re-assembled. There are lots of additional things that go on too such as making sure the data isn’t damaged and attempting to correct it (all automatic) if it is.
  5. SABnzbd Saves Data:If all goes well; and in most circumstances it will. You will have all of the content successfully retrieved at this point!

SABnzbd has tons of automation built into it; I’m really just focusing on the basics here to get you going.

I Want To Try It

Of course you do! So here’s the thing; if you’ve already connected to my repository here, you can get it by simply typing the following:

# Download and install SABnzbd plus the Notification Addon
# on CentOS 7.x using the nuxref repositories located
# here: http://nuxref.com/repo/
yum install -y --enablerepo=nuxref \
               --enablrepo=nuxref-shared \
               sabnzbd sabnzbd-script-notify

You can also just visit the location I host these packages directly (via my repository) here and download the RPMs for yourself:

Note: I provided the source rpms optionally; they are not required unless you want to build this for yourself from scratch.

SABnzbd Environment

It’s worth giving you a quick rundown of how the RPM installs itself upon your computer: First off, it creates a general user/group called sabnzbd. By default this is the user/group it will run as. To grant a user access to all of the content retrieved by SABnzbd, you can just add yourself to the sabnzbd group:

# as root; we can add ourselves to the sabnzbd group
usermod -a -G sabnzbd myuserid

# Note: if this user is logged in, they will have to log out and log
#       back in to have these new security credentials noticed. 

You can start it up with the command:

# as root; start up SABnzbd:
systemctl start sabnzbd.service

You’ll be able to access the web page through your browser by punching in http://localhost:8080.

You can set it up to survive reboots with the following command:

# as root; start up SABnzbd when the machine is first
# powered on:
systemctl enable sabnzbd.service
  • All of your log files will show up in /var/log/sabnzbd/sabnzbd.log
  • All of configuration will get written to /etc/sabnzbd/sabnzbd.conf
  • All of the variable data (file processing, etc) will be located in /var/lib/sabnzbd/*. In fact this is a very important directory because unless you configure things differently, all downloaded content will appear in /var/lib/sabnzbd/complete.

SABnzbd Notifications

SABnzbd can keep you posted on what it’s doing by sending you emails when a download completes (or fails). It can send you an notification on Pushbullet, and a few others too.

If you want to use the Notify script I wrote, you’re already almost set up and ready to go because it’s in the sabnzbd-script-notify rpm you already installed.

To enable it, you simply need to need to access the Notifications tab from within the SABnzbd Configuration section.

SABnzbd Notification Setup

SABnzbd Notification Setup

  1. Select the Enable notification script checkbox
  2. Select the Notify.py script from the dropdown list next to the Script category
  3. Next to the Parameter category, you must specify the URL(s) identifying which service(s) you want to notify.

    Depending on what you want plan on alerting, the URL(s) you specify in the Parameter field will vary. You can get a better understanding of the URL options supported here.

Credit

This blog took me a very long time to put together and test! The repository hosting alone accommodates all my blog entries up to this date. If you like what you see and wish to copy and paste this HOWTO, please reference back to this blog post at the very least. It’s really all I ask.

Special thanks to Safihre for reaching out to me and allowing me to contribute to their product with the notification add-on.

Sources

Administration Tricks & Tips

Linux Administration Tools & Tricks

Introduction

All *nux administrators have tools they use to make their job easier. This blog will be an ongoing one where every time I find or create a handy tool, I’ll share it. I’ll do my best to provide a quick write up with it and some simple examples as to how it may be useful to you too.
Currently, this blog touches on the following:

Applications

Please note that most of these tools can be directly retrieved from my repository I’m hosting. You may wish to connect to it to make your life easier.


genssl: SSL/TLS Certificate Generator and Certificate Validation

genssl is a simple bash script I created to automatically generate Private/Public key pairs that can be used by all sorts of applications requiring SSL/TLS encryption. This could be used by any number of servers you’re hosting (Web, Mail, FTP, etc). It will also test previously created keys (validating them). It supports generating keys that are to be signed by Certificate Authority as well as self signed and test keys too.

# Assuming you've connected to my repository,
# just type the following:
yum install -y --enablerepo=nuxref genssl

What You’ll Need To Know
It’s really quite simple, SSL/TLS keys are usually assigned to domain names. Thus this is the only input to the tool in addition to the key type you want to generate (self-signed, test, or registered):

# Generate a simple self-signed Certificate Private/Public
# key pair for the domain example.com you would just type the
# following:
genssl -s example.com
# ^-- creates the following:
#   - example.com.key
#   - example.com.crt
#   - example.com.README

# You can specify as many domain names as you want to
# generate more then one:
genssl -s example.com nuxref.com myothersite.com
# ^-- creates the following:
#   - example.com.key
#   - example.com.crt
#   - example.com.README
#   - nuxref.com.key
#   - nuxref.com.crt
#   - nuxref.com.README
#   - myothersite.com.key
#   - myothersite.com.crt
#   - myothersite.com.README

You can verify the keys with the following command:

# The following scans the current directory
# for all .key, .crt, and/or .csr files and tests that
# they all correctly match against one another.
genssl -v

If you need a signed certificate by a registered Certificate Authority, genssl will get you half way there. It will generate you your public key and an unsigned certificate (.csr). You’ll then need to choose a Certificate Authority that works best for you (cost wise, features you need). You will provide them your unsigned certificate you just generated and in return, they’ll produce (and provide) for you the private key you need (.key). Signed (registered) certificates cost money because you’re paying for someone else to confirm the authenticity of your site when people visit it. This is by far the absolute best way to publicly host a secure website!

# Generate an unsigned certificate and public key
# pair for the domain example.com with the following:
genssl -r example.com
# ^-- creates the following:
#   - example.com.key
#   - example.com.crt
#   - example.com.README

You’ll notice that you also generate a README file for every domain you create keys for. Some of us won’t generate keys often; so this README provides some additional information that will explain how you can use the key. It lists a few Certificate Authoritative locations you can contact, and provides examples on how you can install the key into your system. Depending on the key pair type you generate, the README will provide you with information specific to your cause. Have a look at the contents yourself after you generate a key! I find the information in it useful!

The last thing worth noting about this tool is to create a key pair, you usually provide information about yourself or the site you’re creating the key for. I’ve defaulted all these values to ones you’re probably not interested in using. You can over-ride them by specifying additional switches on the command line. But the absolute easiest (and better) way of doing it, is to just provide a global configuration file it can reference which is /etc/genssl. This file is always sourced (if present). The next file that is checked and sourced is ~/.config/genssl and then ~/.genssl.
Set this with information pertaining to your hosting to ease the generation of of the key details. Here is an example of what any one of these configuration files might look like:

# The country code is represented in it's 2 letter abbreviated version:
# hence: CA, US, UK, etc
# Defaults to "7K" if none is specified
GENSSL_COUNTRY="7K"
# Your organization/company Name
# Defaults to "Lannisters" if none is specified
GENSSL_ORG="Lannisters"
# The Province and or State you reside in
# Defaults to "Westerlands" if none is specified
GENSSL_PROVSTATE="Westerlands"
# Identify the City/Town you reside in
# Defaults to "Casterly Rock" if none is specified
GENSSL_LOCATION="Casterly Rock"
# Define a department; this is loosely used. Some
# just leave it blank
# Defaults to "" (blank) if not is specified
GENSSL_DEPT=""

There are other variables you can override, but the above are the key ones. The default cipher strength is rsa:2048 and keys expire after 730 days (which equates to 2 years). This is usually the max time Certificate Authoritative sites will sign your key for anyway (if registering with them).

You don’t need a configuration file at all for this script to work, there are switches for all of these variables too that you can pass in. Its important to note that passed in switches will always over-ride the configuration file too, but this is the same with most applications anyway.

Some Further Reading


fencemon: A System Monitoring Tool

fencemon is a beautiful creation by Red Hat, the only thing I did was package it as an RPM. I don’t think it’s advertised or used enough by system administrators. It simply runs as a cron and constantly gathers detailed system information. Information such as the current programs running, the system memory remaining, network statistics, disk i/o, etc. It captures content every 10 seconds but does so by using tools that are not i/o intensive. So running this will not slow your machine down enough to justify not using it.

Where this information becomes vital is during a system failure (in the event one ever does occur). It will allow you to see exactly what the last state of the system was (processes in memory, etc) prior to your issue. The detailed information can be used with everyday troubleshooting as well too such as identifying that process that is going crazy overnight and other anonymities that you’re never around to witness first hand.

Why is it called fencemon?
It got it’s name because it’s initial purpose was to run in clustered environments which do a think called ‘fencing’. It’s when another cluster node sees that another is violating some of the simple cluster rules put in place, or it simply isn’t responding anymore. Fencing is effectively the power cycling of the node (so an admin doesn’t have to). In almost all cases (unless there is seriously something wrong with the fenced node), it will reboot and rejoin the cluster correctly. This tool would have allowed an admin to see the final state of the cluster node before it was lost. But any server can crash or go crazy when deploying software in a production environment. We all hope it never happens, but it can. The logging put in place by fencemon can be a life saver!

# Assuming you've connected to my repository,
# just type the following:
yum install -y --enablerepo=nuxref fencemon

What You’ll Need To Know
The constant system capturing that is going on will report itself to /var/log/fencemon/.

The format of the log files is:
hostnameYYYYMMDDHHMMSS-info-capturetype.log

Once an hour elapses, a new file is written to. Each file contains system statistics in 20 second bursts; as you can imagine, there is A LOT of information here.

the following capturetype log files (with their associated commands) are gathered in 20 second increments:

  • hostnameYYYYMMDDHHMMSS-info-ps-wchan.log:
    ps -eo user,pid,%cpu,%mem,vsz,rss,tty,stat,start,time,wchan:32,args
  • hostnameYYYYMMDDHHMMSS-info-vmstat.log:
    vmstat
  • hostnameYYYYMMDDHHMMSS-info-vmstat -d.log:
    vmstat-d
  • hostnameYYYYMMDDHHMMSS-info-netstat.log:
    netstat -s
  • hostnameYYYYMMDDHHMMSS-info-meminfo.log:
    cat /proc/meminfo
  • hostnameYYYYMMDDHHMMSS-info-slabinfo.log:
    cat /proc/slabinfo
  • hostnameYYYYMMDDHHMMSS-info-ethtool-$iface.log:
    # $iface will be whatever is detected on your machine
    # you can also define this variable in /etc/sysconfig/fencemon
    # ifaces="lo eth0"
    # The above definition would cause the following to occur:
    /sbin/ethtool -S lo >> 
       /var/log/fencemon/hostname-YYYYMMDD-HHMMSS-info-ethtool-lo.log
    /sbin/ethtool -S eth0 >> 
       /var/log/fencemon/hostname-YYYYMMDD-HHMMSS-info-ethtool-eth0.log
    

The log files are kept for about 2 days which can occupy about 750MB of disk space. But hey, disk space is cheap now of days. You should have no problem reserving a GIG of disk space for this info. It’s really worth it in the long run!

Some Further Reading


datemath: A Date/Time Manipulation Utility

I won’t go too deep on this subject since I’ve already blogged about it before here. In a nutshell, It can easily provide you a way of manipulating time on the fly to ease scripting. This tool is not rocket science by any means, but it simplifies shell scripting a great deal when preforming functionality that is time sensitive.

This is effectively an extension or the the missing features to the already existing date tool which a lot of developers and admins use regularly.

# Assuming you've connected to my repository,
# just type the following:
yum install -y --enablerepo=nuxref datemath

What You’ll Need To Know
There are 2 key applications; datemath and dateblock. Datemath can ease scripting by returning you time relative to when it was called; for example:

# Note: the current date/time is printed to the
#        screen to give you an idea how the math was
#        applied.
# date +'%Y-%m-%d %H:%M:%S' prints: 2014-11-14 21:49:42
# What will the time be in 20 hours from now?
datemath -o 20
# 2014-11-15 17:49:42

# Top of the hour 9000 minutes in the future:
datemath -n 9000 -f '%Y-%m-%d %H:00:00'
# 2014-11-21 03:00:00

If you use a negative sign, then it looks in the past. There are lots of reasons why you might want to do this; consider this little bit of code:

# Touch a file that is 30 seconds older than 'now'
touch -t $(datemath -s -30 -f '%Y%m%d%H%M.%S') reference_file

# Now we have a reference point when using the 'find'
# command. The below command will list all files that
# at least 30 seconds old. This might be useful if your
# hosting an FTP server and don't want to process files
# until they've arrived completely.
find -type f -newer reference_file
# You could move the fully delivered files to a new
# directory with a slight adjustment to the above line
find -type f -newer reference_file -exec mv -f {} /new/path ;

# Consider this for an archive clean-up for a system
# you maintain. Simply touch a file as being 31 days
# older then 'now'
touch -t $(datemath -d -31 -f '%Y%m%d%H%M.%S') reference_file

# now you can list all the files that are older then
# 31 days.  Add the -delete switch and you can clean
# up old content from a directory.
find /an/archive/directory -type f ! -newer reference_file

# You can easily script sql statements using this tool too
# consider that you need to just select yesterdays data
# for an archive:
START=$(datemath -d -1 -f '%Y-%m-%d 00:00:00')
FINISH=$(date +'%Y-%m-%d 00:00:00')
BACKUP_FILE=/absolute/path/to/put/backup/$START-backup.csv
# Now your SQL statement could be:
/usr/bin/psql -d postgres 
      -c "COPY (SELECT * FROM mysystem.data WHERE 
                mysystem.start_date >= '$START' AND 
                mysystem.finish_date < '$FINISH') 
          TO '$BACKUP_FILE';"
# Now that we've backed our data up, we can remove
# it from our database:
/usr/bin/psql -d postgres 
      -c "DELETE FROM mysystem.data  WHERE 
                mysystem.start_date >= '$START' AND 
                mysystem.finish_date < '$FINISH'"

Some Further Reading


dateblock: A Cron-Like Utility

dateblock allows us to mimic the functionality of sleep by blocking the process. The catch is dateblock blocks until a specific time instead of for a duration of time.

This very subtle difference can prove to be a very effective and powerful too, especially when you want to execute something at a specific time. Cron’s are fine for most scenarios, but they aren’t ideal in a clustered environment where this tool excels. In a clustered environment you may only want an action to occur on the node hosting the application, where a cron would require the action to fire on all nodes.

The python C++ library enables this tools usage even further since you can integrate it with your application.

Just like datemath is, dateblock is discussed in much more detail in my blog about it here.

# Assuming you've connected to my repository,
# just type the following:
yum install -y --enablerepo=nuxref dateblock

# Get the python library too if you want it
yum install -y --enablerepo=nuxref python-dateblock

What You’ll Need To Know
Consider the following:

# block for 30 seconds...
sleep 30

# however dateblock -s 30 would block until the next 30th second
# of the minute is reached. Consider the time:
# date +'%Y-%m-%d %H:%M:%S' prints: 2014-11-14 22:14:16
# dateblock would block until 22:14:30 (just 14 seconds later)
dateblock -s 30

Dateblock works just like a crontab does and supports the crontab format too. It was written by me to specifically allow accuracy for time sensitive applications running. If you write your actions and commands in a script under a dateblock command, you can always guarantee your actions will be called at a precise time.

Since it supports cron entries you can feed it things like this:

# The following will unblock if the minute becomes
# divisible by 10.  so this would unblock at the
# :00, :10, :20, :30: 40: and :50 intervals
dateblock -n /10

# the above command could also be written like this:
dateblock -n 0,10,20,30,40,50

# You can mix and match switches to tweak the tool to
# always work in your favour.

A really nice switch that can help with your debugging and development is the –test (-t) switch. This will cause dateblock to ‘not’ block at all. Instead it will just spit out the current time, followed by the time it ‘would’ have unblocked at had you not put it into test mode. This is good for trying to tweak complicated arguments to get the tool to work in your favour.

# using the test switch, we can make sure we're going
# to unblock at time intervals we expect it to. In this
# example, i use the test switch and a cron formatted
# argument.  In this example, I'm asking it to unblock
# ever 2 days at the start of each day (midnight with
# respect to the systems time)
dateblock -t -c "0 0 0 /2"
Current Time : 2014-11-14 22:24:06 (Fri)
Block Until  : 2014-11-16 00:00:00 (Sun)

# Note: there is an extra 0 above that may through you
#       off.. in the normal cron world, the first
#       argument is the 'minute' entry.  Well since
#       dateblock offers high resolution, the first
#       entry is actually 'seconds', then minute, etc.

There are man pages for both of these tools. You’ll get a lot more detail of the power they offer. Alternatively, if you even remotely interested, check out e my blog entry on them.

The other cool thing is dateblock also has a python library I created.

# Import the module
from dateblock import dateblock

# Here is a similar example as above and we set
# block to false so we know how long were blocking until
unblock_dt = dateblock('0 0 0 /2', block=False)
print("Blocking until %s" % unblock_dt
              .strftime('%Y-%m-%d %H:%M:%S))
# by default, blocking is enabled, so now we can just
# block for the specified time
datetime.datetime('0 0 0 /2')

Some Further Reading


nmap (Network Mapper)

nmap is port scanner & network mapping tool. You can use it to find out all of the open ports a system has on it and you can also use it to see who is on your network. This tool is awesome, but it’s intentions can be easily abused. It’s common sense to scan your own network to make sure that only the essential ports are open to the outside world, but it’s not kind to scan a system to which you are not the owner of. Hence, this tool should be used to identify your systems weaknesses so that you can fix them; it should not be used to identify an others weakness. For this reason, if you do install nmap on your system, consider limiting permission so that only the root user has access to it.

# Assuming you've connected to my repository,
# just type the following:
yum install -y --enablerepo=nuxref-shared nmap

# Restrict its access (optional, but ideal)
chmod o-rwx /usr/bin/nmap

What You’ll Need To Know
Now you can do things like:

  • Scan your system to identify all open ports:
    # Assuming the server you are scanning is 192.168.1.10
    nmap 192.168.1.10
    
  • Scan an entire network to reveal everyone on it (including MAC Address information):
    # Assuming a class C network (24 masked bits) with
    # a network address of 192.168.1.0
    nmap -n -v -sP 192.168.1.0/24
    

Note: Don’t panic if your system appears to have more ports open then you had thought. Well at least don’t panic until you determine where you are running your network map test from. You see, if you run nmap on the same system you are testing against, then there your test might not prove accurate since firewalls do not normally deny any local traffic. The best test is to use a second access point to test against the first. Also, if your server sits on more then one network, make sure to test it from a different server on each network it connects to!

Some Further Reading

  • A blog with a ton of different examples identifying things you can do with this tool.

lsof (LiSt Open Files)

This tool is absolutely fantastic when it comes to preforming diagnostics or handling emergency scenarios. This command is only available to the root user; this is intentional due to the power it offers the system administrators.

I’m not hosting this tool simply because it ships with most Linux flavors (Fedora, CentOS, Red Hat, etc). Therefore, the following should haul it into your system if it’s not already installed:

# just type the following:
yum install -y lsof

# If this doesn't work, the rpm will be found
# on your DVD/CD containing your Linux Operating
# System.

What You’ll Need To Know
This tool when executed on it’s own (without any options) simply lists every open file, device, socket you have on one great big list. This can be useful when paired with grep if your looking for something specific.

# list everything opened
lsof

But it’s true power comes from it’s ability to filter through this list and fetch specific things for you.

# list all of the processes utilizing port 80 on your system:
lsof -i :80 -t

# or be more specific and list all of the processes accessing
# a specific service you are hosting on an IP listening on
# port 80:
# Note: the below assumes we're interested in those accessing
#       the ip address 192.168.0.1
lsof -i @192.168.0.1:80 -t

# If you pair this with the kill command, you can easily kick
# everyone off your server this way:
kill -9 $(lsof -i @192.168.0.1:80 -t)

But the tool is also great for even local administration; let’s say there is a user account on your system with the login id of bob.

# find out what bob is up to and what he's accessing on
# your system this way (you may need to pair this with
# grep since the output can be quite detailed
lsof -u bob

# Perhaps you just want to see all of the network activity
# bob is involved in. The following spits out a really
# easy to read list of all of the network related processes
# bob is dealing with.
lsof -a -u bob -i

# You can kill all of the TCP/IP processes bob is using with
# the following (if bob was violating the system, this might
# be a good course of action to take):
kill -9 $(lsof -t -u bob)

Perhaps you have a file on your system you use regularly, you can find out who is also using it with the following command:

# List who is accessing a file
lsof /path/to/a/file

Some Further Reading

  • lsof Survival Guide: A Stack Overflow post with some awesome tricks you can do with this tool.
  • More great lsof examples. This is someones blog who specifically wrote about this tool specifically. They provide many more documented examples of this tools usage here.

Credit

This blog took me a very long time to put together and test! If you like what you see and wish to copy and paste this HOWTO, please reference back to this blog post at the very least. It’s really all I ask.

Datetools : Datemath and Dateblock for Linux

Datetools: Date Manipulation and Cron Alternative For Linux

Introduction to Datetools

I wrote Datetools (in C++) to allow the manipulation of date time from the command line. It greatly simplified my life and maybe it will help yours out too!. It comprises of two core applications:

  • Dateblock: allows you to block until a scheduled period of time arrives unlike sleep which blocks for a set period of time. I found this so helpful, I ended up additionally building in an python extension for it.
  • Datemath: This application is just a simple way of preforming simple math on the system date.

The source code can be found here on GitHub if you’re interested in compiling it yourself. Or you can just scroll to the bottom of this blog where I provided the packaged goods.

Dateblock

The tool works very similarly to cron and sleep (a combination of the two); you can pass it a crontab string if that’s what you’re used too, or you can simply pass it in variables as arguments as well (as all other commands work):

Here’s is an example of what I mean:

# block until a minute divisible by 10 is reached:
# ex: HH:00, HH:10, HH:20, HH:30, HH:40, and HH:50
dateblock --minute=/10
# We will only reach this line when the above scheduled time has
# been met.
echo "Scheduled time reached; current time is: $(date)"

An equivalent crontab entry would look like this:

# block until a minute divisible by 10 is reached:
/10 * * * * echo "Scheduled time reached; current time is: $(date)"

Dateblock can also do another cool feature called ‘drifting’ which allows you to schedule processes on delayed cycles… Note that drifting is always specified in seconds. For example:

# Unblock on 5 minute cycles, but 2 minutes (120 seconds) into them:
# ex: HH:02, HH:07, HH:12, HH:17, HH:22, etc..
dateblock --minute=/5 --drift=120
# We will only reach this line when the above scheduled time has
# been met.
echo "Scheduled time reached; current time is: $(date)"

An equivalent crontab entry would look like this:

# block until a minute divisible by 10 is reached:
/5 * * * * sleep 120; echo "Scheduled time reached; current time is: $(date)"

The complexity of the tool can be as powerful as you want it to be:

# Unblock only on hours divisible by 5 on the 1st through to the 14th
# of every month (as well as the 20th). Unblock only when 30 seconds
# of that minute has elapsed.
 dateblock -o /5 -d 1-14,20 -s 30
# We will only reach this line when the above scheduled time has
# been met.
echo "Scheduled time reached; current time is: $(date)"

There is no way to reproduce this in a crontab unless the 30 second reference at the end is unnecessary… in that case:

# block until a minute divisible by 10 is reached:
0 /5 1-14,20 * * sleep 120; echo "Scheduled time reached; current time is: $(date)"

Just like crontabs, dateblock supports minute, hour, day of month, month and day of week. In addition, dateblock support seconds too. dateblock accepts traditional crontab entries as well as arguments:

#     day of week (0 - 6) (Sunday=0) --+
#     month (1 - 12) ---------------+  |
#     day of month (1 - 31) -----+  |  |
#     hour (0 - 23) ----------+  |  |  |
#     min (0 - 59) --------+  |  |  |  |
#  ***sec (0 - 59) -----+  |  |  |  |  |
#                       |  |  |  |  |  |
#                       -  -  -  -  -  -
# Dateblock Cron Entry: *  *  *  *  *  *
# Cron Crontab Entry:      *  *  *  *  *

# Unblock on the specific hours of 0 and 12:
# ex: HH:00, HH:12
$> dateblock --cron="0 0 00,12"

You’ll notice in the above, I didn’t bother specifying the remaining cron fields… In this case they will assume the default of *. But you can feel free to specify a * for readability. The other thing to observe is the addition of the second column which isn’t present in a regular crontab entry. It’s rules are no different then what you’ve already learned from other fields.

Testing

Simply adding a –test (-t) switch to your dateblock entry will allow you to test the tool in a debugging mode to which it will present to you the current time followed by when it would have unblocked for had you not provided the –test (-t) switch. It’s a great way to calculate when the next processing time will be.

Python Extension

To handle scheduled processes for my websites, I created a python extension for dateblock. This allowed to extend it’s flexibility with other offline processing… consider the following example:

from dateblock import dateblock
while True:
    # /5 as first argument means unblock on the 5th second of each minute
    dateblock('/5')
    print 'begin processing ...'
    # your code here...
    # if you want, you can even report the next unblock time
    print 'Done processing; blocking until %s' % 
        dateblock('/5', block=False).strftime('%Y-%m-%d %H:%M:%S')

You can also also access the drift as such:

from dateblock import dateblock
print 'Unblock at %s' % 
    dateblock('/5', drift=120, block=False).strftime('%Y-%m-%d %H:%M:%S')

Finally the python extension allows you to pass in a datetime object as an argument for calculating a time based on another (and not the current time which is the default).

from dateblock import dateblock
from datetime import datetime
from datetime import timedelta

# 31 days ago
reftime = datetime.now() - timedelta(days=31)

print('Would blocking until %s' % 
    dateblock('/5', drift=120, block=False, )
      .strftime('%Y-%m-%d %H:%M:%S') + 
    " if time was %s" % reftime
      .strftime('%Y-%m-%d %H:%M:%S'))

Things to Consider

Just like sleep, dateblock uses SIGALARM to manage its wake up time. Therefore if your code relies heavily on SIGALARM for another purposes, dateblock may not be a solution for you since you could interrupt it’s reliability (though not likely). This really shouldn’t be a big concern because this exact same warning comes with the sleep libraries we’ve been using for years. But it does mean that sleep could interfere with dateblock just as dateblock could interfere with sleep if they were both used in separate threads.

Dateblock vs Sleep

Dateblock vs Sleep

Why would I use dateblock over sleep?

Scheduling is the key… If your program relies completely on sleep, then the only thing you’re accomplishing is cpu throttling (controlling unnecessary thrashing). This is approach is fine if you’re going to just retry connecting to an unresponsive server in ?? seconds. But what if timing becomes an important factor of your application? The dateblock tool ensures you only unblock at absolute times vs sleep which unblocks at relative times with respect to when it was called.

Dateblock also allows your program to chronologically work in turn with other applications that may be on their own processing cycle. Such as something delivering data at the top of every hour. You may wish to have your program wake up 5 min after the top of each hour to perform the processing regardless of when your program was started.

Datemath

There isn’t as much to be said about Datemath; I personally never found a Linux/Unix tool that would allow me to script date/time calculations from the command line. For that reason, this tool exists.
Here is an example of the tools function:

# what time is it now?
date +'%Y-%m-%d %H-%M-%S'
# The above output was '2013-10-26 09-42-21' at the time of this blog
# what time will it be 5 months and 3 days from now
datemath --months=5 --days=3 --format='%Y-%m-%d %H-%M-%S'
# the above output was '2014-03-29 09-42-21' at the time of this blog
# and this makes sense... this is the calculation we want.

The tool supports negative values for calculating into the past as well and will handle leap years in the calculations too.

# what time is it now?
date +'%Y-%m-%d %H-%M-%S'
# The above output was '2013-10-26 09-45-45' at the time of this blog
# What was the current date 753 days ago?
datemath --days=-753 --format='%Y-%m-%d %H-%M-%S'
# the above output was '2011-10-04 09-45-45' at the time of this blog
# and this makes sense... this is the calculation we want.

No Python Module For Datemath

There is no python module for datemath because Python’s datetime and timedelta libraries already provide a fantastic solution for the same problem datemath solves…

# Simple code example to show why it really isn't
# necessary to port datemath to Python:
from datetime import datetime
from datetime import timedelta
in_the_past = datetime.now() - timedelta(minutes=15)
print '15 minutes ago: %s' % in_the_past
                              .strftime('%Y-%m-%d %H:%M:%S')

Just give me the goods

No problem, below are the RPMS as well as their accompanied source packages:

Package Download Description
dateblock
el6.rpm
/
el7.rpm
The powerful (cron like) command line interface (CLI) tool.
python-dateblock
el6.rpm
/
el7.rpm
The python extension for dateblock.
datemath
el6.rpm
/
el7.rpm
The datemath command line interface tool.
datetools
el6.rpm
/
el7.rpm
An optional package which includes licensing and information.

Note: The source rpm can be obtained here which builds everything you see in the table above. It’s not required for the application to run, but might be useful for developers or those who want to inspect how I put the package together.

No way, I’m building this myself; I don’t trust you

That’s okay, I understand; here is how you can build it yourself:

# Install 'mock' into your environment if you don't have it already
# This step will require you to be the superuser (root) in your native
# environment.
yum install -y mock

# Grant your normal every day user account access to the mock group
# This step will also require you to be the root user.
usermod -a -G mock YourNonRootUsername

At this point it’s safe to change from the ‘root‘ user back to the user account you granted the mock group privileges to in the step above. We won’t need the root user again until the end of this tutorial when we install our built RPM.

# Create an environment we can work in
mkdir datetool-build

# Change into our temporary working directory
cd datetool-build

curl -L --output datetools-0.8.1.tar.gz \
   https://github.com/caronc/datetools/archive/v0.8.1.tar.gz

# Extract Spec File
tar xfz datetools-0.8.1.tar.gz \
   datetools-0.8.1/datetools.spec \
      --strip-components=1

# Initialize Mock Environment
mock -v -r epel-6-x86_64 --init

# Now install our dependencies
mock -v -r epel-6-x86_64 --install boost-devel libstdc++-devel 
          glib-devel python-devel autoconf automake libtool

# Copy in our downloaded content:
mock -v -r epel-6-x86_64 --copyin datetools-0.8.1.tar.gz
   /builddir/build/SOURCES
mock -v -r epel-6-x86_64 --copyin datetools.spec 
   /builddir/build/SPECS

# Shell into our environment
mock -v -r epel-6-x86_64 --shell

# Change to our build directory
cd builddir/build

# Build our RPMS
rpmbuild -ba SPECS/datetools.spec

# we're now done with our mock environment for now; Press Ctrl-D to exit
# or simply type exit on the command line of our virtual environment
exit

Future Considerations

This is totally up in the air, at the moment the tool does everything I needed at the time. However I could see the following becoming a useful feature in the future:

  • Pass in a different time into both programs (instead of always working with the current time) (You can already do this with the dateblock python extension).
  • Have dateblock additionally take in a program and arguments as input to have it automatically execute the call to it when the scheduled time is reached. In addition to this, it means the dateblock tool would daemonize itself and run in the background on reoccurring schedules.
  • Add a devel package and create a shared library for C++ linking; perhaps the binary tools and extensions could link here too. Right now the library is just so small it’s really nothing to just include it statically as it is now.
  • Got an idea of your own? Pass it along! You can also submit a pull request to me on GitHub here.

Credit

Please note that this information took me several days to put together and test thoroughly. I may not blog often; but I want to re-assure the stability and testing I put into everything I intend share.

If you like what you see and wish to copy and paste this information, please reference back to this blog post at the very least. It’s really all I ask.