Feb 022011
 

Introduction

Backups are a subject I return to semi-frequently with a passion to never be in an “oh shit” scenario.  Last time I built my backup system, bacula with a postgresql DB backend, I determined that I would move to a common database backup script for all of my databases.  Holland fit this bill perfectly with support for postgres, sqlite and mysql.  This allows one command to backup all of my databases on all of my servers and subsequently creates a much simpler bacula configuration (the database job is defined the same as the catalog job).

The Solution

The problem I had when configuring holland to backup postgresql is that there was no example configuration file.  It wasn’t hard to craft a working default postgres configuration and the following is what I came up with (/etc/holland/backupsets/default.conf:

[holland:backup]
plugin = pgdump
backups-to-keep = 1
auto-purge-failures = yes
purge-policy = after-backup
estimated-size-factor = 1.0

[pgdump]
role = postgres

[pgauth]
username = postgres

Conclusion

Setting up holland to backup databases is incredibly easy and flexible.  By having a common backup solution for all databases other configurations become easier and processes can be streamlined.

Feb 012011
 

In the last couple of months I have been plagued by a problem with the system that hosts this website.  A nice kernel panic followed by a random uptime and a crash.  This is a bit difficult to swallow when the server is around 1200 miles away but a reboot and away it goes again.

The actual error is show here:

Feb 01 14:13:27 [kernel] kernel BUG at mm/filemap.c:128!
Feb 01 14:13:27 [kernel] Modules linked in:
Feb 01 14:13:27 [kernel] Pid: 13604, comm: apache2 Not tainted 2.6.36-hardened-r6 #1 0K8980/Dimension 3000
Feb 01 14:13:27 [kernel] EIP: 0060:[<c105af4f>] EFLAGS: 00010046 CPU: 0
Feb 01 14:13:27 [kernel] EIP is at __remove_from_page_cache+0x44/0x91
Feb 01 14:13:27 [kernel] EAX: 00000000 EBX: c16acf40 ECX: 00000015 EDX: 00000015
Feb 01 14:13:27 [kernel] ESI: de877d7c EDI: ffffffff EBP: 00000015 ESP: dc4abe28
Feb 01 14:13:27 [kernel]  DS: 0068 ES: 0068 FS: 00d8 GS: 0033 SS: 0068
Feb 01 14:13:27 [kernel]  de877d7c c16acf40 c105afbc c16acf40 de877d7c c1061079 c16acf40 00000000
Feb 01 14:13:27 [kernel] <0> c1061123 de877d7c 00000015 00000006 ffffffff 00000000 0000000e 00000000
Feb 01 14:13:27 [kernel] <0> c1641f40 c1641f60 c1641f80 c1641fa0 c173ecc0 c1695de0 c16acf40 c15d2f00
Feb 01 14:13:27 [kernel]  [<c105afbc>] ? remove_from_page_cache+0x20/0x27
Feb 01 14:13:27 [kernel]  [<c1061079>] ? truncate_inode_page+0x6c/0x7d
Feb 01 14:13:27 [kernel]  [<c1061123>] ? truncate_inode_pages_range+0x99/0x23a
Feb 01 14:13:27 [kernel]  [<c10612cd>] ? truncate_inode_pages+0x9/0xc
Feb 01 14:13:27 [kernel]  [<c10fa452>] ? ext4_evict_inode+0x83/0x265
Feb 01 14:13:27 [kernel]  [<c108dab2>] ? evict+0x17/0x7b
Feb 01 14:13:27 [kernel]  [<c108e211>] ? iput+0x182/0x1df
Feb 01 14:13:27 [kernel]  [<c108b58e>] ? d_kill+0x2a/0x43
Feb 01 14:13:27 [kernel]  [<c108c292>] ? dput+0xf3/0xfb
Feb 01 14:13:27 [kernel]  [<c107e289>] ? fput+0x191/0x1b3
Feb 01 14:13:27 [kernel]  [<c106e14f>] ? remove_vma+0x34/0x52
Feb 01 14:13:27 [kernel]  [<c106f26a>] ? __do_munmap+0x257/0x2a8
Feb 01 14:13:27 [kernel]  [<c106f335>] ? sys_munmap+0x49/0x60
Feb 01 14:13:27 [kernel]  [<c1378005>] ? syscall_call+0x7/0xb
Feb 01 14:13:27 [kernel] ---[ end trace 4598df0f375c22c4 ]---
Feb 01 14:13:27 [kernel] kernel BUG at mm/filemap.c:128!Feb 01 14:13:27 [kernel] Modules linked in:Feb 01 14:13:27 [kernel] Pid: 13604, comm: apache2 Not tainted 2.6.36-hardened-r6 #1 0K8980/Dimension 3000               Feb 01 14:13:27 [kernel] EIP: 0060:[<c105af4f>] EFLAGS: 00010046 CPU: 0Feb 01 14:13:27 [kernel] EIP is at __remove_from_page_cache+0x44/0x91Feb 01 14:13:27 [kernel] EAX: 00000000 EBX: c16acf40 ECX: 00000015 EDX: 00000015Feb 01 14:13:27 [kernel] ESI: de877d7c EDI: ffffffff EBP: 00000015 ESP: dc4abe28Feb 01 14:13:27 [kernel]  DS: 0068 ES: 0068 FS: 00d8 GS: 0033 SS: 0068Feb 01 14:13:27 [kernel]  de877d7c c16acf40 c105afbc c16acf40 de877d7c c1061079 c16acf40 00000000Feb 01 14:13:27 [kernel] <0> c1061123 de877d7c 00000015 00000006 ffffffff 00000000 0000000e 00000000Feb 01 14:13:27 [kernel] <0> c1641f40 c1641f60 c1641f80 c1641fa0 c173ecc0 c1695de0 c16acf40 c15d2f00Feb 01 14:13:27 [kernel]  [<c105afbc>] ? remove_from_page_cache+0x20/0x27Feb 01 14:13:27 [kernel]  [<c1061079>] ? truncate_inode_page+0x6c/0x7dFeb 01 14:13:27 [kernel]  [<c1061123>] ? truncate_inode_pages_range+0x99/0x23aFeb 01 14:13:27 [kernel]  [<c10612cd>] ? truncate_inode_pages+0x9/0xcFeb 01 14:13:27 [kernel]  [<c10fa452>] ? ext4_evict_inode+0x83/0x265Feb 01 14:13:27 [kernel]  [<c108dab2>] ? evict+0x17/0x7bFeb 01 14:13:27 [kernel]  [<c108e211>] ? iput+0x182/0x1dfFeb 01 14:13:27 [kernel]  [<c108b58e>] ? d_kill+0x2a/0x43Feb 01 14:13:27 [kernel]  [<c108c292>] ? dput+0xf3/0xfbFeb 01 14:13:27 [kernel]  [<c107e289>] ? fput+0x191/0x1b3Feb 01 14:13:27 [kernel]  [<c106e14f>] ? remove_vma+0x34/0x52Feb 01 14:13:27 [kernel]  [<c106f26a>] ? __do_munmap+0x257/0x2a8Feb 01 14:13:27 [kernel]  [<c106f335>] ? sys_munmap+0x49/0x60Feb 01 14:13:27 [kernel]  [<c1378005>] ? syscall_call+0x7/0xbFeb 01 14:13:27 [kernel] ---[ end trace 4598df0f375c22c4 ]---

The uprecords for this machine:

     #               Uptime | System                                     Boot up
----------------------------+---------------------------------------------------
     1    24 days, 20:31:52 | Linux 2.6.32-hardened-r2  Thu Nov 25 20:58:34 2010
     2    18 days, 09:10:28 | Linux 2.6.36-hardened-r6  Sat Jan  8 12:31:38 2011
     3    15 days, 12:41:25 | Linux 2.6.32-hardened-r9  Fri Oct  8 09:48:07 2010
     4    14 days, 03:43:24 | Linux 2.6.32-hardened-r2  Wed Nov  3 11:29:55 2010
     5     9 days, 19:44:59 | Linux 2.6.32-hardened-r2  Sun Oct 24 15:14:10 2010
     6     7 days, 21:49:22 | Linux 2.6.32-hardened-r2  Mon Dec 20 17:31:43 2010
     7     7 days, 17:34:24 | Linux 2.6.36-hardened-r6  Tue Dec 28 15:58:45 2010
     8     7 days, 08:53:26 | Linux 2.6.32-hardened-r2  Thu Nov 18 11:55:57 2010
     9     5 days, 16:58:46 | Linux 2.6.36-hardened-r6  Wed Jan 26 21:43:51 2011
    10     3 days, 00:09:43 | Linux 2.6.36-hardened-r6  Wed Jan  5 12:17:33 2011
----------------------------+---------------------------------------------------
->  13     0 days, 00:14:46 | Linux 2.6.36-hardened-r6  Tue Feb  1 15:02:26 2011
----------------------------+---------------------------------------------------
1up in     0 days, 00:40:31 | at                        Tue Feb  1 15:57:42 2011
t10 in     2 days, 23:54:58 | at                        Fri Feb  4 15:12:09 2011
no1 in    24 days, 20:17:07 | at                        Sat Feb 26 11:34:18 2011
    up   115 days, 09:00:18 | since                     Fri Oct  8 09:48:07 2010
  down     0 days, 21:28:47 | since                     Fri Oct  8 09:48:07 2010
   %up               99.230 | since                     Fri Oct  8 09:48:07 2010

In conclusion, I’m suspecting a hardware issue but if anyone has any ideas as to what might cause this type of problem I’d be more than happy to hear possible causes and solutions until I get a replacement server built out.

Jan 272011
 

I recently made the plunge and began using zsh in lieu of bash.  I’ve not regretted the decision in the slightest but there have been minor annoyances that needed to be dealt with.  The simplest annoyance was the special keys (delete, home, page up, page down, etc).  The solution was quite simple and elegant but not completely obvious.

The bindings usually are read from the file `/etc/inputrc`by bash but zsh does not do this by default.  There are probably more elegant solutions but a quick brute force solution is to create a bindkeys file out of inputrc:

gawk '$1 ~ /.*:/ { print "bindkey",$1,$2 }' /etc/inputrc | \
sed -e 's/://g' > ~/.zshrc-bindkeys

Once this file has been crafted it’s simply a matter of invoking it from your .zshrc with `source ~/.zshrc-bindkeys`.

Jan 182011
 

Introduction

Portage is an amazingly simple and complex piece of technology.  The simplicity in each piece’s ability to do a specific function comes together in a complex package management system that rivals all other forms of package management (at least in my opinion).  Automating updates is something that admins everywhere do out of necessity.  Heck, automating everything is an admin’s life.  Automating portage’s updates is a bit more harrowing than other package management systems but it isn’t impossible.

Problem

As admins we attempt to simplify the work we actually do by writing scripts and programs to do most of our job for us.  It’s often been said that systems admins are the only people whose job description is to remove their job responsibilities.

Portage doesn’t have any default automation for doing nightly or even weekly portage updates but that doesn’t stop the creative from coming up with their own solution.  A simple but elegant solution is to create a small cron script that runs every day.  The problem comes when you want to read the wonderful output of portage (sometimes these messages can guide you when problems are about to occur) to avert disasters.  If the updates are performed from cron, the output will be preserved in an e-mail to the appropriate user but then we have to sift through all of the output at once.  This also doesn’t solve the issue if the updates are performed by another utility such as puppet.  These annoying little changes to the problem require a slightly more elegant solution.

Solution

The solution is to take advantage of portage’s logging specifications.  From the make.conf man file:

  • PORTAGE_ELOG_CLASSES
  • PORTAGE_ELOG_SYSTEM
  • PORTAGE_ELOG_COMMAND
  • PORTAGE_ELOG_MAILURI
  • PORTAGE_ELOG_MAILFROM
  • PORTAGE_ELOG_MAILSUBJECT

Using a combination of these directives in the make.conf file allows us to log the reports from portage to a large number of locations.  If we wanted to simply add mailing output (not the full build output just the messages) we would add the following directives to make.conf:

PORTAGE_ELOG_SYSTEM="save mail"
PORTAGE_ELOG_MAILFROM="portage@alunduil.com"

This simply adds the mailing log utility to portage and specifies that the e-mails come from the address portage@alunduil.com.  Of course, much more complex configurations can be crafted to suit any admins’ needs.

Conclusion

Letting your servers notify you of possible actions is one way of automating maintenance tasks; making maintenance eventually disappear from your task list. By starting with the tasks that are repeated the most frequently, you can quickly free up time for higher level automation and organization which leads to a cleaner and sturdier infrastructure.

Dec 152010
 

Introduction

I know this doesn’t come up nearly as often as other topics but writing an interpreter (a.k.a. a compiler) can be a very useful concept depending on what exactly you need to do with the input to your program. For example, what if we had a fairly strict data store but we for some reason wanted to access it using something like SQL? We’d have to parse the SQL statements and then find a way to mogrify it until we had something that would allow us to get the data we wanted. There is an easier way involving a little parsing theory. For the purposes of this discussion I’m assuming you are at least somewhat familiar with Context Free Grammars.

Creating the Grammar

The first step to creating a parser (especially when using a parser generator) is to find or craft a definition for the grammar. The grammar we’ll use as an example is the expression grammar from tiny basic. This simple grammar is safe for LR or LL parsing (which is important if you look at a common definition of a language like SQL).

Tiny Basic Expression Grammar

expression ::= (+|-|ε) term ((+|-) term)*
term ::= factor ((*|/) factor)*
factor ::= number | (expression)

Enter pyparsing

To create a very simple and extensible LL parser I’ve recently stumbled upon pyparsing. This simple four production grammar expands to the following pyparsing implementation:

[sourcecode language="python" wraplines="false"]
expr = Forward()
factor = ( Word(nums) | Group(Suppress(‘(‘) + expr + Suppress(‘)’)) )
term = Group(factor + ZeroOrMore((Literal(‘*’)|Literal(‘/’)) + factor))
expr << Group(Optional(Literal(‘-’)|Literal(‘+’)) + term + ZeroOrMore((Literal(‘-’)|Literal(‘+’)) + term))
[/sourcecode]

This allows us to turn sentences such as ‘5+5*6/3-(47+56)*34‘ into an easy to work with list such as: ‘[[['5'], ‘+’, ['5', '*', '6', '/', '3'], ‘-’, [[[['47'], ‘+’, ['56']]], ‘*’, ’34′]]]‘. There are probably improvements that could be made to this parser so that it auto-collapses expressions and other fun handlers but for the purposes of a simple grammar this will suffice.

Calling the grammar after defining it is a very simple process: `expr.parseString(’5+5*6/3-(47+56)*34′)`.

Testing Parsers an Easier Way

Sure unit testing should be done (and parsers lend themselves to unit tests very well) but there’s something satisfying about seeing your sentences get parsed out in real time. The obvious answer is, “create a mini-shell like environment.” Python also makes this process extremely simple and only requires a few lines of code to get a basically functional shell for your parser (complete with history):

[sourcecode language="python" wraplines="false"]
import rlcompleter
import readline
import so

if not os.access(".history", os.F_OK): open(".history", "w").close()
readline.read_history_file(".history")
buffer = ""

while True:
try: line = raw_input(pycolorize.light_blue("BASIC$ "))
except EOFError:
readline.write_history_file(".history")
print
break

if line.lower() == "exit" or line.lower() == "quit":
readline.write_history_file(".history")
break

buffer += line
result = ACTION_ON_BUFFER
buffer = ""
[/sourcecode]

Putting It Together

The complete script for reference purposes:

[sourcecode language="python" wraplines="false"]
import rlcompleter
import readline
import os

from pyparsing import *
import pprint

if not os.access(".history", os.F_OK): open(".history", "w").close()
readline.read_history_file(".history")

try:
import pycolorize
except:
sys.path.append(os.path.join(os.path.dirname(__file__), "vendor", "pycolorize"))
import pycolorize

class ExpressionParser(object):
def __init__(self):
self._expr = Forward()
factor = ( Word(nums) | Group(Suppress(‘(‘) + self._expr + Suppress(‘)’)) )
term = Group(factor + ZeroOrMore((Literal(‘*’)|Literal(‘/’)) + factor))
self._expr << Group(Optional(Literal(‘-’)|Literal(‘+’)) + term + ZeroOrMore((Literal(‘-’)|Literal(‘+’)) + term))

def _calculate(self, l):
while any([ isinstance(x, list) for x in l]):
for n,i in enumerate(l):
if isinstance(i, list): l[n] = self._calculate(i)
return str(eval(" ".join(l)))

def __call__(self, string):
return self._calculate(self._expr.parseString(string).asList())

buffer = ""

print pycolorize.green("Enter your SQL commands to tokenize:")
print pycolorize.green("Enter a blank line to exit.")

while True:
try: line = raw_input(pycolorize.light_blue("BASIC$ "))
except EOFError:
readline.write_history_file(".history")
print
break

if line.lower() == "exit" or line.lower() == "quit":
readline.write_history_file(".history")
break

buffer += line
result = None
try: result = ExpressionParser()(buffer)
except ParseBaseException, e:
buffer = ""
pycolorize.error(e.line)
pycolorize.error(" "*(e.col – 1) + "^")
pycolorize.error(str(e))
continue
pycolorize.status("Result: %s", result)
buffer = ""
[/sourcecode]

Conclusion

Writing LL parsers is a breeze with pyparsing but it must be kept in mind that any grammar that has any left recursion will cause errors that may take some time to find or remove. Other parser generators (for C and C++) include bison and lemon. These parser generators are LR parser generators.

By coupling the parser with a small CLI quick checks on new features to the grammar (and by extension the parser) can be a breeze. Putting this all together with unit tests and proper grammar analysis can lead to a well written and extensible language to be used for whatever purpose you may have in mind.

© 2011 Alunduil's Hosting Suffusion theme by Sayontan Sinha