Sunday, December 12, 2010

What is DevOps and why is it important

With the recent purchase of Heroku by salesforce made me think about the future of the "SysAdmin".

As I was writing this I saw a recent talk given by Jacob Kaplan-Moss at Django Boston Meetup titled DevOps.

The job of a SysAdmin is changing, frameworks like Django and Ruby on Rails along with services providers like Google Apps, Heroku, VMForce, and Djangy give developers everything they need. The role of a SysAdmin is going to become very specialized, unless your talking about Windows systems ;)

Thursday, November 25, 2010

Gitorious install on Debian Squeeze with stompserver

I have modified this install tutorial from Christian Johansen found here

Some info from here as well

There is also source for a debian package by Marius MÃ¥rnes Mathiesen here the most recent commits are forked here

Squeeze is needed for the new magickwand stuff

Had issues with activemq so using stompserver instead

apt-get install -y sudo git-core git-svn apg build-essential libpcre3 libpcre3-dev sendmail \
make zlib1g zlib1g-dev ssh memcached apache2 \
libonig-dev libyaml-dev geoip-bin libgeoip-dev libgeoip1 \
uuid uuid-dev openjdk-6-jre apache2-prefork-dev curl openssl libcurl4-gnutls-dev

apt-get install -y imagemagick libmagickwand-dev && \
gem install --no-ri --no-rdoc rmagick

apt-get install -y mysql-client mysql-server libmysqlclient15-dev && \
gem install --no-ri --no-rdoc mysql

sudo dpkg -i ruby-enterprise*

echo 'export PATH=/opt/ruby-enterprise/bin:$PATH
export LD_LIBRARY_PATH="/usr/local/lib"
export LDFLAGS="-L/usr/local/lib -Wl,-rpath,/usr/local/lib"' >> /etc/profile

echo '/usr/local/lib
include*.conf' >> /etc/


cd /usr/src
tar xvzf rubygems*
cd rubygems*
ruby setup.rb

cd /usr/src
tar xvfz sphinx-*
cd sphinx-*
make && sudo make install
gem install --no-ri --no-rdoc ultrasphinx

gem install stompserver
echo '#!/bin/sh
# Start/stop the stompserver
# Provides: stomp
# Required-Start: $local_fs $remote_fs $network $syslog
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 1
# Short-Description: Stomp
# Description: Stomp
test -f $DAEMON || exit 0
. /lib/lsb/init-functions
case $1 in
start) log_daemon_msg Starting stompserver stompserver
     start-stop-daemon --start --name stompserver --startas $DAEMON \
                                      --background --user git
     log_end_msg $?
stop) log_daemon_msg Stopping stompserver stompserver
     start-stop-daemon --stop --name stompserver
     log_end_msg $?
restart) log_daemon_msg Restarting stompserver stompserver
    start-stop-daemon --stop --retry 5 --name stompserver
    start-stop-daemon --start --name stompserver --startas $DAEMON \
                                       --background --user git
    log_end_msg $?
    status_of_proc $DAEMON stompserver && exit 0 || exit $?
*) log_action_msg Usage: /etc/init.d/stomp {start|stop|restart|status}
    exit 2
exit 0' > /etc/init.d/stompserver
chmod +x /etc/init.d/stompserver
update-rc.d stompserver defaults

echo '#!/bin/sh
# Start/stop the git poller
# Provides: git-poller
# Required-Start: stomp
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 1
# Short-Description: Gitorious poller
# Description: Gitorious poller

echo  `date` " Starting git-poller" >> "$GITORIOUS_ROOT/log/git-poller.log"
/bin/su -- git -c " cd $GITORIOUS_ROOT; RAILS_ENV=production script/poller $@"' > /etc/init.d/poller
chmod +x /etc/init.d/poller
update-rc.d poller defaults

git clone git:// gitorious
chmod -R g+sw /var/www/gitorious
ln -s /var/www/gitorious/script/gitorious /usr/local/bin/gitorious
cd gitorious/
rm public/.htaccess
mkdir -p tmp/pids
chmod ug+x script/*
chmod -R g+w config/ log/ public/ tmp/

which ruby
vi doc/templates/ubuntu/git-daemon
Gitorious ships with ready to use git-ultrasphinx and git-daemon scripts. Be aware that the git-daemon script has hardcoded paths to both Ruby Enterprise Edition and Gitorious. Pop open git-daemon and set correct paths to the Ruby you want to use (stick with REE if you installed it earlier) and your Gitorious installation. These are the lines you need to see (lines 16 and 18 at the time of writing):
GIT_DAEMON="/opt/ruby-enterprise/bin/ruby \
    /var/www/gitorious/script/git-daemon -d"
ln -s /var/www/gitorious/doc/templates/ubuntu/git-ultrasphinx /etc/init.d/.
ln -s /var/www/gitorious/doc/templates/ubuntu/git-daemon /etc/init.d/.

chmod +x /etc/init.d/git-ultrasphinx
chmod +x /etc/init.d/git-daemon
update-rc.d -f git-daemon start 99 2 3 4 5 .
update-rc.d -f git-ultrasphinx start 99 2 3 4 5 .

gem install --no-ri --no-rdoc rails mongrel mime-types textpow chronic \
    ruby-hmac daemons mime-types oniguruma textpow chronic BlueCloth \
    ruby-yadis ruby-openid geoip rspec rspec-rails RedCloth echoe
gem install --no-ri --no-rdoc --version="1.0.1" rack

adduser git
usermod -a -G gitorious git
mkdir /var/git
mkdir /var/git/repositories
mkdir /var/git/tarballs
mkdir /var/git/tarball-work
chown -R git:git /var/git

su git
mkdir ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys
vi ~/.bashrc
Append the following to /home/git/.bashrc
# User specific aliases and functions
export RUBY_HOME=/opt/ruby-enterprise
export GEM_HOME=$RUBY_HOME/lib/ruby/gems/1.8/gems
export PATH=$RUBY_HOME/bin:$PATH

cd /var/www/gitorious
cp config/database.sample.yml config/database.yml
cp config/gitorious.sample.yml config/gitorious.yml
cp config/broker.yml.example config/broker.yml
apg -m 64
See original doc for details on this config file
rake db:migrate RAILS_ENV=production
mysql -uroot -p
create database gitorious;
create database gitorious_test;
create database gitorious_dev;
grant all privileges on gitorious.* to YOURUSER@localhost \
    identified by 'YOURPASSWORD';
grant all privileges on gitorious_test.* to YOURUSER@localhost;
grant all privileges on gitorious_dev.* to YOURUSER@localhost;
sudo rake gems:install
vi config/database.yml
Make your config match your mysql db setup
rake db:migrate RAILS_ENV=production
cd /var/www/gitorious
env RAILS_ENV=production ruby script/create_admin
> user = User.first
> user.login = "yourname" # Change to your desired username
> user.activate
> user.accept_terms
rake ultrasphinx:bootstrap RAILS_ENV=production
chown -R git:gitorious config/environment.rb script/poller log tmp
chmod -R g+w config/environment.rb script/poller log tmp
chmod ug+x script/poller
/etc/init.d/activemq start
env RAILS_ENV=production /etc/init.d/git-daemon start
su git -c \
  "cd /var/www/gitorious && \
    env RAILS_ENV=production script/poller run"

su git -c \
  "cd /var/www/gitorious && script/server -e production"

echo '* */1 * * * root cd /var/www/gitorious && rake ultrasphinx:index RAILS_ENV=production' >> /etc/crontab
cp doc/templates/ubuntu/gitorious-logrotate /etc/logrotate.d/gitorious
sudo chmod +x /etc/logrotate.d/gitorious
gem install passenger

a2enmod rewrite
a2enmod deflate
a2enmod passenger
a2enmod expires
a2enmod rewrite

vi conf/vhost.conf
Update config
ln -s /var/www/gitorious/conf/vhost.conf /etc/apache2/sites-available/gitorious
a2ensite gitorious

Wednesday, August 11, 2010

Nagios3 on Debian external commands

Enabling external commands in nagios on a Debian deb install of nagios is a bit of a pain.

First in /etc/nagios3/nagios.cfg change

Then you will get errors about
Error: Could not stat() command file '/var/lib/nagios3/rw/nagios.cmd'!

If you
rm /var/lib/nagios3/rw/nagios.cmd
chmod a+x /var/lib/nagios3
chmod g+s /var/lib/nagios3/rw
/etc/init.d/nagios restart

That error should go away and www-data should have read access to that file. You may need to
chgrp www-data /var/lib/nagios3/rw
But that was already there for me.

Sunday, July 18, 2010

Aastra disappoints again

I have been doing VoIP work with Asterisk since pre 1.0 days, which is about 6-7 years. The very first phones we choose were the Aastra/Sayason 480i which were very feature rich and pretty good hardware. The software quality is what seemed to be lacking, it was a while ago so I can't remember specific examples. We decided, back at that time, to switch to Polycom 501s. The 501s were not much more in price, had the same features, maybe even more at that time, and just felt like a much better product.

After a few years Aastra came out with the 57i. The popular Nerd Vittles Blog run by Ward Mundy called this phone the best asterisk phone back in 2008 I was still hesitant to use it due to my poor experience that I had with Aastra phones previously.

We decided to try them again about 6 months ago, and with the new XML scripts that Aastra has made for Trixbox/Elastix/PIF these phones work great with Asterisk and I have not had one issue with the phones firmware.

Now due to my renewed faith in Aastra we decided to try the Aastralink Pro 160 bundle, which comes with a few phones. Our customer also needed wireless as well so we thought we would try the MBU400 which the ALP 160 fully supports. This is where the fun started.

First the Aastralink Pro 160. It has decent hardware, 2 FXS ports and 6 FXO ports, and it a pretty reasonable price compared to building your own system with Sangoma or Digium PCI cards. For the software that is a different story.  As an Asterisk hacker I have been spoiled by flexibility. Even with Elastix (my choice of FreePBX distro) I can always throw something in extensions_custom.conf and change the dial plan to suite any customer's needs. Now I knew the ALP 160 didn't give me root access so I figured the basic stuff would be supported by the web based GUI, I guessed wrong.
  1. You are forced to dial 9 (PSTN) or 8 (SIP) to dial out, you can't change this and my customers hate dialing prefixes
  2. Not only do you have to dial a 8 prefix for SIP but if you have multiple SIP trunks you have to dial another prefix for the trunk number, ie 80 or 81 or 82
  3. Inbound SIP calls all go to the same place, you cannot control calls based on SIP trunk or DID, you can control where the calls from the FXO ports go.
  4. Each extension must have a Aastra hardware phone and must be online. You can have any SIP device as a mirrored extension but if the phone that it's mirrored to goes offline so does it.
  5. I am guessing that timing is not sync'd between the FXO and FXS because I could not get faxing to work for the life of me.

So what other options are there for a small PBX? Xorcom makes one that is decent, this is what we switched our customer to. Building your own out of a Atom based barebones kit is a good option too. Or Ward calls his choice the Orgasmatron but they don't have any FXS or FXO.

Second the MBU 400. Now this is a nice looking unit. It actually is identical hardware to the SNOM m3. It integrates pretty easy with the Aastralink Pro 160 (except it can't be a mirrored extension it has to be its own extension). But for some reason the unit we had would not connect back to the access point after being out of range, unless there was some sort of manual intervention. So I spent hours going back and forth with Aastra tech support. Now you figure people calling reseller tech support would be fairly technical, so the support people should be just as technical or even more. This is not the case for Aastra. The most common response I get from them for a few different issues that I had called about was "I don't know." If it is a tough problem that is fine, but at least offer a next step or some sort of escalation.

So what did we do? We switched to Polycom, again. This time to the Polycom Kirk 5040 handsets with the 600 access point, and a couple repeaters. These were a bit tough to dig through the manual and figure out how to get it setup, I did need help from our distributor on setting up the repeaters, but other than that these things have been rock solid for our customer.

The point of me writing this, other than to bash on Aastra, is to plead for them to stop trying to be a software company. I have heard of and experienced many show stopping issues in their firmware.  Every time I upgrade an Aastra device I am not sure what I am going to get. It has been like this since the 480i days 7 years ago. I don't know what their development and testing process is, but I doubt that it is good. On the flip side, the hardware they make seems to be great, the problem is it takes them years to release firmware for it that is as good as the hardware is.

Thursday, July 15, 2010

Rails on Debian

Ran into a strange issue while debugging a Rails app that I am working on. I finally found the exact problem and issue here

Turns out that
the error comes from an incompatibility between Ruby 1.8.7 and Rails 2.0.2. Ruby 1.8.7 has String#chars and returns an Enumerator object, but Rails 2.0.2 expects an ActiveSupport::Multibyte::Chars object.
Which is pointed out here

Putting this in your environment.rb fixes the issue
unless '1.9'.respond_to?(:force_encoding)
  String.class_eval do
      remove_method :chars
    rescue NameError
      # OK

Wednesday, July 14, 2010

Fixing Python indentations in VIM

Had this problem today where I created a file with eclipse then edited it with vim and that made the tabs inconsistent. Found this post on stackoverflow that had a couple of suggestions

1. Use - this requires apt-get install python-examples on debian/ubuntu
2. :%s/\t/ /g - this is what I used and it worked great

Other vim tricks I have used lately:
- Remove empty lines :%s/^[\ \t]*\n//g
- Re-indent entire file gg=G
- Remove lines with comments g/^#/d

Tuesday, June 01, 2010

Modular Django Admin Part 3

Django Auth has a lot of stuff built in

A list of all the default templates and urls you need for login password reset and password change. Most of these templates are in django/contrib/admin/templates/registration

url(r'^accounts/login/$', 'django.contrib.auth.views.login',name='login'),
url(r'^accounts/$', 'django.contrib.auth.views.login',name='login'),
url(r'^accounts/logout/$', 'django.contrib.auth.views.logout',{'next_page': '/'},name='logout'),
url(r'^accounts/password_change/$', 'django.contrib.auth.views.password_change',name='password_change'),
url(r'^accounts/password_change_done/$', 'django.contrib.auth.views.password_change_done',name='password_change_done'),
url(r'^accounts/password_reset/$', 'django.contrib.auth.views.password_reset',name='password_reset'),
url(r'^accounts/accounts/password_reset/done/$', 'django.contrib.auth.views.password_reset_done',name='password_reset_done'),
url(r'^accounts/reset/(?P[0-9A-Za-z]+)-(?P.+)/$', 'django.contrib.auth.views.password_reset_confirm',name='password_reset_confirm'),
url(r'^accounts/reset/done/$', 'django.contrib.auth.views.password_reset_complete',name='password_reset_complete'),

template/registration/login.html (django/contrib/admin/templates/admin/login.html)

So now we can login see generic view lists of our models, not we need to edit them.

Sunday, May 16, 2010

Django Modular Admin Part 2

I just realized that alot of the functionality that the admin login has is not part of django.contrib.auth but is only in the admin.

A couple of things I have figure out as a result with form validation errors in the template.
{{ form.non_field_errors }} is for a ValidationError that is for the whole form.

A snippet of my login.html template

<div id="welcome_login" title="Welcome To My Site"> 
  <p><b>Admin Login</b></p>
  <form action="." method="post" enctype="multipart/form-data" class="forms" name="form" >{% csrf_token %}
   {% if form.non_field_errors %}
   <div class="response-msg error ui-corner-all">
    <span>Error message</span>
    {{ form.non_field_errors }}
   {% endif %}
     <label for="id_username" class="desc">{% trans 'Username:' %}</label> 
      {% if form.username.errors %}
      <div class="response-msg error ui-corner-all">
       <span>Error message</span>
       {{ form.username.errors }}
      {% endif %}
      <input type="text" tabindex="1" maxlength="255" value="" class="field text full" name="username" id="email" />

     <label for="id_password" class="desc">{% trans 'Password:' %}</label>
      {% if form.password.errors %}
      <div class="response-msg error ui-corner-all">
       <span>Error message</span>
       {{ form.password.errors }}
      {% endif %}
      <input type="password" tabindex="1" maxlength="255" value="" class="field text full" name="password" id="password" />
      <input type="hidden" name="this_is_the_login_form" value="1" />

More to come on django auth

Thursday, May 13, 2010

Modular Django Admin Part 1

The django admin is awesome, we all know that, but there is always the debate to role your own or to customize the admin. Make the admin more modular would be ideal and that is what we will be trying to do.

In part one we will just start with the basics and setup a tabular list using the object_list generic view with gives you pagination for free.

One trick to get the key/value pairs into the template of the model is using a function to return self.__dict__.iteritems()

The Model (myapp/

class MyObject(models.Model):
    name = models.CharField(max_length=75)
    slug = models.SlugField(unique=True)
    def __unicode__(self):

    def attrs(self):
        for attr, value in self.__dict__.iteritems():
            yield attr, value

The View (myapp/

from django.views.generic.list_detail import object_list
def applicant_list_jobs(request):
    return object_list(request, queryset=Job.objects.all(), paginate_by=5)

The Template (templates/myapp/myobject_list.html)

{% extends "admin/change_list.html" %}
{% block content %}
  {% for object in object_list %}
  <tr class="{% cycle 'row1' 'row2' %}">
   {% for key,value in object.attrs %}
   <td>{{ value }}</td>
   {% endfor %}
  {% endfor %}
 <p class="paginator">
 {% if page_obj.has_previous %}
  <a href="?page={{ page_obj.previous_page_number }}"><</a>
 {% endif %}
  Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
 {% if page_obj.has_next %}
  <a href="?page={{ page_obj.next_page_number }}">></a>
 {% endif %}
{% endblock content %}

Next up will be where the templates and urls go for login and the built in authentication tools.

Saturday, March 13, 2010

Django Model Values as Dict

This is pretty easy to do but I always forget
obj_dict = MyModel.Objects.filter(id__gt=0).values()
And you can also add MyModel.objects.filter(id__gt=0).values_list('COLUMN',flat=True)

Friday, February 26, 2010

Hylafax 6.0 Debian Lenny T38modem 1.2.0 Asterisk 1.6

After fighting with t38modem for a week I finally found versions of opal and ptlib that work with t38modem 1.2 for sending faxes (have not tested receiving yet). ptlib-2.6.6 opal-3.6.7 t38modem-1.2.0 I am using most of the other instructions from this how to below. Hylafax 6.0 Debian (or Ubuntu) T38modem 1.0 Asterisk 1.6

Thursday, February 25, 2010

Debian Backports copy paste

echo 'deb lenny-backports main contrib non-free' >> /etc/apt/sources.list
apt-get update && apt-get install debian-backports-keyring --force-yes && apt-get update
instructions [Debian Backports]: "deb lenny-backports main contrib non-free"

Monday, February 15, 2010

Django | User authentication in Django | Django documentation

Django auth doesn't have all the urls listed anywhere so here they are:
    (r'^accounts/login/$', 'django.contrib.auth.views.login'),
    (r'^accounts/logout/$', 'django.contrib.auth.views.logout'),
    (r'^accounts/password_change/$', 'django.contrib.auth.views.password_change'),
    (r'^accounts/password_change_done/$', 'django.contrib.auth.views.password_change_done'),
    (r'^accounts/password_reset/$', 'django.contrib.auth.views.password_reset'),
    (r'^accounts/accounts/password_reset/done/$', 'django.contrib.auth.views.password_reset_done'),
    (r'^accounts/reset/(?P[0-9A-Za-z]+)-(?P.+)/$', 'django.contrib.auth.views.password_reset_confirm'),
    (r'^accounts/reset/done/$', 'django.contrib.auth.views.password_reset_complete'),  
Django | User authentication in Django | Django documentation

Friday, January 29, 2010

Django | Generic views | Django documentation

Took me forever to figure this out with generi views, but the I just don't read well. Here is an example template:
<p class=\"paginator\">
 <span class=\"step-links\">
  {% if page_obj.has_previous %}
   <a href=\"?page={{ page_obj.previous_page_number }}\"><</a>
  {% endif %}

  <span class=\"current\">
   Page <span class=\"this-page\">{{ page }}</span> of {{ paginator.num_pages }}

  {% if page_obj.has_next %}
   <a href=\"?page={{ page_obj.next_page_number }}\">></a>
  {% endif %}

"If the results are paginated, the context will contain these extra variables: paginator: An instance of django.core.paginator.Paginator. page_obj: An instance of django.core.paginator.Page." Django Generic views object_list:

Thursday, January 28, 2010

Dynamic Inlines Built into Django Admin in 1.2

So I was talking at a Django round table last night and talking about the fixes I did to the dynamic inlines for the admin. And someone mentioned that a)jquery is now included in Django 1.2 and b) dynmaic inlines are built in as well. /django/trunk/django/contrib/admin/media/js/inlines.js

Friday, January 22, 2010

Django snippets: jQuery Double Click Edit ManyToMany in Admin

Take a look at my code on django snippets. Just drop this into your templates/admin/change_form.html and any select for a foreign key or many to many will allow a double click to pop up and edit

Django snippets: jQuery Double Click Edit ManyToMany in Admin

Wednesday, January 06, 2010

Multiple Inserts with a Subquery | Code Spatter

Multiple Inserts with a Subquery | Code Spatter

Django Dynamic Formset

I posted Previously about this jquery plugin. I have just figured out a very plugable way to get this working in the admin with adding just one form.
Add the following to templates/admin/APP/MODEL/change_form.html and also update the MODEL in the prefix setting.
{% extends "admin/change_form.html" %}
{% load i18n admin_modify adminmedia %}
{% block extrahead %}
{{ block.super }}

  // Define this so we don't have to bother with the admin jsi18n stuff:
  function gettext(msgid) { return msgid; }

  $(function() {
      $('.inline-related tbody tr').formset({
   prefix: 'MODEL_set',
   addText: 'Add',
   deleteText: 'Delete',

  .add-row {
      background:url({% admin_media_prefix %}img/admin/icon_addlink.gif) no-repeat left center;
  .delete-row {
      background:url({% admin_media_prefix %}img/admin/icon_deletelink.gif) no-repeat left center;

{% endblock %}
Thanks Stanislaus
Django Dynamic Formset

Friday, January 01, 2010

Download SpeedFan - Access temperature sensor in your computer

Download SpeedFan - Access temperature sensor in your computer

howto:chroot_debian - DNS323Wiki

Installing debian on the DLink DNS323 NAS is really easy, just download and extact the files to the root directory and restart it.
Default password is 12345678 make sure you reset it.
Also update your /etc/apt/source.list to lenny and apt-get update (don't forget to apt-get install debian-archive-keyring)
howto:chroot_debian - DNS323Wiki