Archive for category Scripts

Easy Python: multi-threading MySQL queries

There are many times when writing an application that single threaded database operations are simply too slow. In these cases it’s a matter of course that you’ll use multi-threading or forking to spawn secondary processes to handle the database actions. In this simple example for Python multi-threading you’ll see the how simple it is to improve the performance of your python app.

#!/usr/bin/python
## DATE: 2010-08-30
## AUTHOR: Matt Reid
## WEBSITE: http://themattreid.com
## LICENSE: BSD http://www.opensource.org/licenses/bsd-license.php
## Copyright 2010-present Matt Reid

from __future__ import division
from socket import gethostname;
import threading
import sys
import os
import MySQLdb

class threader(threading.Thread):
    def __init__(self,method):
        threading.Thread.__init__(self)
        self.tx =
        self.method = method
    def run(self):
        run_insert()

def run_insert():
    sql = "INSERT INTO table (`id`,`A`,`B`,`C`) VALUES (NULL,'0','0','0');")
        try:
            cursor.execute(sql)
            db.commit()
        except:
            print "insert failed"

def init_thread(): backgrounds = []
    for db in connections:
       logger("Spawning thread: %s"%(db),"d")
       quant = tx / THREADS
       background = threader(method,quant,db)        
       background.start()
       backgrounds.append(background)
    for background in backgrounds:
       background.join()

def main():
    try:
        init_thread()
    except:
        print "failed to initiate threads"

    sys.exit(0)

if __name__ == "__main__":
    mysql_host = "localhost" #default localhost
    mysql_pass = "pass" #default dbbench
    mysql_user = "user" #default dbbench
    mysql_port = 3306 #default 3306
    mysql_db = "schema" #default dbbench
    threads = 4 #must be INT not STR #create connection pool

    connections = []
    for thread in range(THREADS):
      try:
       connections.append(MySQLdb.connect(host=mysql_host, user=mysql_user, passwd=mysql_pass, db=mysql_db, port=mysql_port))
      except MySQLdb.Error, e:
       print "Error %d: %s"%(e.args[0], e.args[1])
       sys.exit (1)

    main()
    

Tags: , , , , ,

Easy Python: display LVM details in XML

If you need to work with LVM in your scripts but haven’t found a good method to access details about Logical Volume Groups, here’s a simple Python script that will print the details about any volumes on your system. This could be useful for writing a partition check script for your MySQL data directory (if you’re not using a standard monitoring system like Nagios).

import sys
import os
import commands
import subprocess
import select

def lvm():
    print ""
    LVM_PATH = "/sbin"
    LVM_BIN = os.path.join(LVM_PATH, 'lvm')
    argv = list()
    argv.append(LVM_BIN)
    argv.append("lvs")
    argv.append("--nosuffix")
    argv.append("--noheadings")
    argv.append("--units")
    argv.append("b")
    argv.append("--separator")
    argv.append(";")
    argv.append("-o")
    argv.append("lv_name,vg_name,lv_size")

    process = subprocess.Popen(argv, stdout=subprocess.PIPE)
    output = ""
    out = process.stdout.readline()
    output += out
    lines = output.splitlines()
    for line in lines:
        line = line.strip()
        words = line.split(";")

        lvname = words[0].strip()
        vgname = words[1].strip()
        lv_size = int(words[2])
        print '''
    %s
    %s
    %s
  '''%(lvname, vgname, lv_size)

    print ""

lvm()

Tags: , , , , , ,

Easy MySQL: how to backup databases to a remote machine

Here’s a simple answer to a simple question. “How do I run a backup of MySQL to another machine without writing to the local server’s filesystem?” – this is especially useful if you are running out of space on the local server and cannot write a temporary file to the filesystem during backups.

Method one – this writes a remote file.
mysqldump [options] [db_name|--all-databases]| gzip -c | ssh user@host.com "cat > /path/to/new/file.sql.gz"

Method two – this writes directly into a remote mysql server
mysqldump [options] [db_name|--all-databases]| mysql --host=[remote host] –user=root –password=[pass] [db_name]

Tags: , , , , , , , , ,

Easy Python: MySQL connection and iteration

If you’ve been looking for a simple python script to use with MySQL that you can use to expand upon for your next project, check this one out. It has error handling for the connection, error handling for the sql call, and loop iteration for the rows returned.

#!/usr/bin/python
import sys
import MySQLdb

my_host = "localhost"
my_user = "user"
my_pass = "password"
my_db = "test"

try:
    db = MySQLdb.connect(host=my_host, user=my_user, passwd=my_pass, db=my_db)
except MySQLdb.Error, e:
     print "Error %d: %s" % (e.args[0], e.args[1])
     sys.exit (1)

cursor = db.cursor()
sql = "select column1, column2 from table";
cursor.execute(sql)
results = cursor.fetchall()
for row in results:
    column1 = row[0]
    column2 = row[1]
    print "column1: %s, column2: %s"%(column1,column2)

db.close()

How to: rotate wordpress posts into headline/feature status

If you’re using the new Arthemia theme for WordPress you might notice that there are two areas of the theme that can have articles promoted to; namely Headline and Featured sections. This is controlled by category association. Basically you have a post and if you want it in the Headline area of the theme you attach the category “headline” to it, similarly for the featured section. Now, let’s say you don’t want to manually change this all the time since it can be time consuming to promote posts to those categories if you want rotating content.

Here’s a simple solution. In this bash script I connect to MySQL and remove the current associations from posts and then randomly choose posts to be promoted to the Headline and Featured categories. This can be modified for other ideas you might have involving categories/posts/randomized associations in WordPress.

The queries contain IDs for the Headline and Featured categories. In my installation, which will be different than yours, has the Headline category as ID=’103′ and Featured as ID=’104′ – replace as needed. I’m also doing some matching (see the WHERE sections) so that I don’t promote posts with certain IDs that are specific to the site for this script. You’ll want to customize the queries as needed for your site. You can find the script here: http://pastebin.com/1QqiM5rh

Tags: , , , , , ,

Benchmarking MySQL ACID performance with SysBench

A couple of question I get a lot from MySQL customers is “how will this hardware upgrade improve my transactions per second (TPS)” and “what level of TPS will MySQL perform on this hardware if I’m running ACID settings?” Running sysbench against MySQL with different values for per-thread and global memory buffer sizes, ACID settings, and other settings gives me concrete values to bring to the customer to show the impact that more RAM, faster CPUs, faster disks, or cnf changes have on the server. Here are some examples for a common question: “If I’m using full ACID settings vs non-ACID settings what performance am I going to get from this server?”

Let’s find out by running sysbench with the following settings (most are self explanatory – if not the man page can explain them):

  • sysbench –test=oltp –db-driver=mysql –oltp-table-size=1000000 –mysql-engine-trx=yes –oltp-test-mode=complex –oltp-read-only=off –oltp-dist-type=special –max-requests=0 –num-threads=8 –max-time=120 –init-rng=on run

MySQL Settings:

In the first test MySQL is set to the following ACID related settings. This will give us results for TPS performance without full ACID compliance – very common settings on a server that is handling blogs, ad serving, general business websites, and other roles where full ACID is not required and performance is valued over the benefits of full ACID. These are important settings when we look at the difference in performance when we change to full ACID in the second test.

  • innodb_flush_log_at_trx_commit = 0
  • sync_binlog=0
  • transaction-isolation=REPEATABLE-READ

System configuration and InnoDB buffer pool size:

  • XEON E5345 Series 2.33ghz 8-core, 16GB RAM, Local SATA 7.2K disks
  • innodb_buffer_pool_size = 10G

Full result set from sysbench:

Summary OLTP test statistics:

  • queries performed:
  • transactions:                        172426 (1436.83 per sec.)
  • read/write requests:                 3276664 (27304.51 per sec.)
  • other operations:                    344882 (2873.91 per sec.)

Non-ACID results:

We can simplify the results by looking at the following TPS results for this non-ACID test:

  • transactions:                        172426 (1436.83 per sec.)

Full ACID results:

Let’s go ahead and run the test again with different ACID settings. This will give us the TPS results for full ACID compliance:

  • innodb_flush_log_at_trx_commit = 1
  • sync_binlog=1
  • transaction-isolation=REPEATABLE-READ

We get the following results for TPS:

  • transactions:                     3197   (26.58 per sec.)
  • read/write requests:                 60743  (505.04 per sec.)
  • other operations:                    6394   (53.16 per sec.)

Final Results:

So as you can see the difference between full ACID settings and not (on the same server with only those values on the cnf being changed) results in a huge difference in performance on this standard database server. We can now hand this data to the customer and they will know what impact the settings will have on their application’s performance and what to expect when running full ACID vs non-ACID.

More info on using sysbench here: http://sysbench.sourceforge.net

Tags: , , , , , , , , , , ,

A simple webpage test script in Python

Looking around on Google for a webpage test script returns a lot of results. Some of them are useful, some are not. In particular, for Python, the scripts on the first page of results are minimal and lacking a useful copy and paste / ready to go script that will answer the question “is my webpage available?”. So I decided to write a quick one that will give you the return code and email you as an alert if the page does not return with a 200 code (successful). You can find the script here. Update: the webserver was trying to execute the script as a .py file so I just changed it to .txt – for it to work you will want to change the .txt extension to a .py extension after you download it.

If you are familiar with Python scripting, this script could easily be modified to post to a form so that you can test a MySQL transaction (or other transactional DB) to ensure your stack is running as needed. Note: wordpress is clobbering my tab indents on the code so if you opt to copy/paste this code into an editor please tab it out correctly or just download the script from the link above.


#!/usr/bin/python
################################################################################
## Kontrollkit
## NAME: kt-url-monitor.py
## DATE: 2010-02-24
## AUTHOR: Matt Reid
## WEBSITE: http://kontrollsoft.com
## EMAIL: support@kontrollsoft.com
## LICENSE: BSD http://www.opensource.org/licenses/bsd-license.php
################################################################################
## Copyright 2010-present Matt Reid
## All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
##################################################################################
## WHAT THIS SCRIPT DOES #########################################################
## Purpose: Checks URL for valid page and emails if failed.
##################################################################################
## EDIT THE FOLLOWING AS NEEDED
email = "email@email.com" ##Email address to send alert to
host = "hostname.com" ##Hostname base URL without http://
port = "80" ##Port - 80 for http, 443 for https
url = "/index.html" ##Page to look for on host
SENDMAIL = "/usr/sbin/sendmail" ##Binary location for sendmail
logfile = "/var/log/kt-url-monitor.log" ##Logfile to write actions to
## END OF EDITABLE OPTIONS
##################################################################################
import sys
import os
import os.path
import datetime
from httplib import HTTP

def wfile(detail):
fstate = os.path.exists(logfile)
if(fstate == 0):
print "Logfile does not exist. Attempting to create..."
try:
file = open(logfile,"a+")
file.writelines(detail)
file.close()

except IOError:
print "Cannot open logfile for writing. Exiting."
sys.exit(1)

else:
print "Logfile exists, writing...\n"
file = open(logfile,"a+")
file.writelines(detail)
file.close()

def get(host,port,url):
concaturl = host+url
print "Checking Host:",concaturl
h = HTTP(host, port)
h.putrequest('GET', url)
h.putheader('Host', host)
h.putheader('User-agent', 'python-httplib')
h.endheaders()

(returncode, returnmsg, headers) = h.getreply()
if returncode != 200:
print returncode,returnmsg
return returncode

else:
f = h.getfile()
# return f.read() #returns content of page for further parsing
print returncode, returnmsg
return returncode

if __name__ == '__main__':
now = datetime.datetime.now()
date = now.strftime("%Y-%m-%d %H:%M:%S")
dmsg = date+" - URL verification starting - "
wfile(dmsg)
concaturl = "http://"+host+url
state = get(host,port,url)

if(state != 200):
msg = "Link (%s) is broken or unavailable. Return code: %s" % (concaturl,state)+"\n"
wfile(msg)
to = "To: "+email+"\n"
print msg,"\n"
p = os.popen("%s -t" % SENDMAIL, "w")
p.write(to)
p.write("Subject: Demo Server Status\n")
p.write("\n")
p.write(msg)
sts = p.close()
if sts != 0:
print "Sendmail exit status", sts
else:
msg = "Link (%s) is good. Return code: %s" % (concaturl,state)+"\n"
wfile(msg)
print msg+"\n"

Tags: , , , ,

Fun with the pipe

Of course there are better ways to manage security on webservers, but I was playing around with the apache error log file today and wanted to get all of the subnets for the script kiddies that are hammering on this dev box. A quick one liner and we’re done!

ROOT-(0)> grep "error" error_log |awk -F[ {'print $4'}| sort | sed -e 's/]//g' | grep -v "/var/www/html/" | grep -v "/usr/local/src/code" | awk {'print $2'} | grep -v "75.142.246.205" |uniq | awk -F. {'print $1"."$2"."$3"/24"'} |uniq
114.80.93/24
119.63.193/24
124.115.0/24
205.234.253/24
208.115.111/24
208.80.195/24
221.194.132/24
58.61.164/24
65.55.106/24
65.55.207/24
66.249.71/24
67.202.34/24
72.14.199/24
74.125.44/24
76.73.37/24
88.131.106/24
92.52.69/24

Change control generator

Perhaps you have found yourself in the middle of planning a large maintenance that involves many servers and many clients and all of the work is the same and you noticed that you don’t want to write a change control document for each client or each server.

Well, here’s some code that will write one for you. Just fill in your array of clients/hosts and then the content of the change control document – then run the file with PHP and you’ll have your change control docs all ready to go.

Note that all blank lines have been removed because wordpress’ code tag sucks and breaks on empty lines in code

< ?php
function write_cr($content,$client,$hostname,$today) {
$FULLPATH="/tmp";
$filename=("$FULLPATH/cr/$client.$hostname.$today.txt");
if (!file_exists("$filename")) { touch("$filename");}
if (is_writable($filename)) {
if (!$handle = fopen($filename, 'a')) {
echo "Cannot open file ($filename)";
exit;
}
else {
if (fwrite($handle, $content) === FALSE) {
echo "Cannot write to file [$filename]";
exit;
}
}
fclose($handle);
}
else {
echo "The file [$filename] is not writable.";
}
}
function crfile($client,$hostname,$today) {
$contents="
CHANGE CONTROL DOCUMENT FOR: $client-$hostname-$today
Date: $today
Name: $client
Phone:
Email:
Downtime Required:
Summary of Changes:
Reason for change:
Testing done prior to change:
Reveral plan:
Triggers for reversal:
Reversal plan time requirement:
Test Plan:
Who will test the changes:
Who will sign off on the changes:
";
write_cr($contents,$client,$hostname,$today);
}
$today = date('d');
$list = array(
"client1" => "hostname1",
"client2" => "hostname2");
foreach($list as $k => $v) {
$client = $k;
$hostname = $v;
crfile($client,$hostname,$today);
}
?>

Tags: ,

Monolith DBA Toolkit 0.4.3 Released

The Monolith Toolkit of scripts for DBA routines. 0.4.3 has been released. You can download it here: http://code.google.com/p/monolith-toolkit/

Some information on the toolkit and what it contains:

  • mt-backup-parallel -> runs mysql backups in parallel super fast, has lots of reporting features
  • mt-check-replication -> script to report on replication status for slave servers
  • mt-rhcluster-check-filesystems -> reports on redhat cluster filesystems (for mysql active/passive clustering)
  • mt-rhcluster-script-wrapper -> wrapper script for running any of these scripts on a redhat cluster, chooses the active node to run the script on
  • mt-connections-log -> logs connections to mysql to disk, reports on threshold overages
  • mt-flush-tables-sequence -> runs through schema.tables to flush in sequence before global flush
  • mt-rotate-logs -> rotates general and slow query logs on periodic basis
  • mt-my.cnf_xG -> MySQL configuration file for a particular RAM size installation

Changes for this release:

  • Fixed the cnf files error that specified G instead of M for tmp table size and max_heap_table_size.
  • Fixed the parallel backup script pruning command. Uses system rm instead of UNLINK.
  • Fixed the default number of forks to create for parallel backup so we don’t overload the server.

Tags: , , ,