RedHat to CentOS Server Conversion

RedHat Enterprise Linux is a great OS, but it does have some expense associated with it in terms of update entitlements. In some cases, it may be acceptable to use CentOS Linux instead. From the CentOS website:

The CentOS Linux distribution is a stable, predictable, manageable and reproducible platform derived from the sources of Red Hat Enterprise Linux (RHEL). Since March 2004, CentOS Linux has been a community-supported distribution derived from sources freely provided to the public by Red Hat. As such, CentOS Linux aims to be functionally compatible with RHEL. We mainly change packages to remove upstream vendor branding and artwork. CentOS Linux is no-cost and free to redistribute.

There are many articles which cover, in lesser or greater detail, the process of converting a running server from RedHat to CentOS. The process described here was tested and was found to work for the particular servers I work with. YMMV, as they used to say on UseNet…

 

Prepare the Server and Download CentOS Packages

Login to the server and become root. Ideally, you should do a yum upgrade as a first step to get all packages up to the latest versions. This may not be possible in some circumstances, the steps below have been tested on a server where the outstanding updates were not applied first and there didn’t seem to be any adverse effect. Nonetheless, patching your servers is a Good Thing™ and is to be encouraged.
 

Create a temporary working area

# mkdir -p /home/temp/centos
# cd /home/temp/centos

Verify your version of RHEL, these instructions apply only to v6.

# cat /etc/redhat-release

Determine your architecture (32-bit = i386/i686, 64-bit = x86_64)

# uname -i

Download (wget) the applicable package files for your release and architecture.

The version numbers on these packages will almost certainly have changed by the time you read this. Have a browse of the CentOS mirror site to find the current versions:

32 bit systems: http://mirror.centos.org/centos/6/os/i386/Packages/
64 bit systems: http://mirror.centos.org/centos/6/os/x86_64/Packages/

Replace the ‘x’ values below with the current version numbers
 

CentOS 6.5 / 32-bit packages required

RPM-GPG-KEY-CentOS-6
centos-release-6-x.el6.centos.x.x.i686.rpm
centos-indexhtml-6-x.el6.centos.noarch.rpm
yum-x.x.x-x.el6.centos.noarch.rpm
yum-plugin-fastestmirror-x.x.x-x.el6.noarch.rpm

CentOS 6.5 / 64-bit packages required

RPM-GPG-KEY-CentOS-6
centos-release-6-x.el6.centos.xx.x.x86_64.rpm
centos-indexhtml-6-x.el6.centos.noarch.rpm
yum-x.x.xx-xx.el6.centos.noarch.rpm
yum-plugin-fastestmirror-x.x.xx-xx.el6.noarch.rpm

Initial Conversion

Clean up yum’s cache

# yum clean all

Import the GPG key for the appropriate version of CentOS

# rpm --import RPM-GPG-KEY-CentOS-6

Remove Core RHEL packages

# yum remove rhnlib abrt-plugin-bugzilla redhat-release-notes*
# rpm -e --nodeps redhat-release-server-6Server redhat-indexhtml

The ‘rpm -e’ command might fail saying one of the packages is not installed. If so, just remove that package from the command and run it again.
 

Remove RHEL subscription-manager

# subscription-manager clean
# yum remove subscription-manager

This stops yum from throwing up an error message every time you run it, something like “This system is not registered to Red Hat Subscription Management.”
 

Force install the core CentOS RPMs you downloaded

# rpm -Uvh --force *.rpm

 

Clean up yum and then upgrade

# yum clean all
# yum upgrade

I rebooted the machine at this point, just to assure myself that it would come back up properly. At the same time, I noticed that the descriptions in /boot/grub/grub.conf still mentioned RedHat – a quick edit soon sorted that out, replacing all instances of RedHat with CentOS.
 

Purify Remaining Packages

Once the server is back up and running, a first pass at removing the remaining RHEL packages can be undertaken:

# yum clean all
# yum reinstall $(rpm -qa --qf "%{NAME} %{VENDOR} \n" | grep "Red Hat" | cut –d" " -f1)
# yum remove Red_Hat_Enterprise_Linux-Release_Notes-6-en-US

The reinstall command above will cleanly replace a large portion of the installed RedHat packages. It will, however, almost certainly leave some packages behind. My approach was to run that command multiple times until it was no longer able to replace any of the remaining packages due to various dependency problems. I then carried out another reboot to satisfy myself that nothing was broken.
 
At this point you can get a full list of what’s left by running this command:

rpm -qa --qf "%{NAME}-%{VERSION}-%{RELEASE} %{VENDOR} \n" | grep "Red Hat"

It’s pretty much up to you how much work you want to put into replacing any remaining RedHat packages. The notes below highlight a handful of extra items that I decided to tackle:
 

Replace ntp/ntpdate packages

# cp /etc/ntp/step-tickers /etc/ntp/step-tickers.keep
# cp /etc/ntp.conf /etc/ntp.conf.keep
# yum remove ntp ntpdate
# yum clean all
# yum install ntp ntpdate
# mv /etc/ntp/step-tickers.keep /etc/ntp/step-tickers
# mv /etc/ntp.conf.keep /etc/ntp.conf

Replace plymouth boot scripts

# rpm -e --nodeps plymouth plymouth-scripts plymouth-core-libs
# yum clean all
# yum install plymouth plymouth-scripts plymouth-core-libs

Replace dbus-glib package

# rpm -e --nodeps dbus-glib
# yum clean all
# yum install dbus-glib

Replace initscripts package

# rpm -e --nodeps initscripts
# yum clean all
# yum install initscripts

At this point in the process, you should see that just about the only RedHat packages left are for the kernel. The recommendation is to just ignore them, on the grounds that trying to safely remove/replace a running kernel is just too risky. The test server where this process was developed/tested installed an updated CentOS kernel without incident the next time an updated package was released. The default /etc/yum.conf setting is to retain a maximum of 5 installed kernels, so over time, the RedHat kernels will gradually be aged out.

RPM Packages for CentOS Linux

I have a bunch of RPM packages that I’ve made for CentOS over the last couple of years – nothing hugely earth-shattering, but just some stuff that I like to install by default whenever I have to build a new machine. I’ve created a yum repository on the same server that hosts this web site, so feel free to go ahead and make use of it!

To use this repository with (for example) the yum package manager, download and install linux2000-repo-1.0-1pke.noarch.rpm. This will also install the GPG public key that I use to sign my packages, allowing you to verify that downloaded RPMS have not been tampered with in transit to you.

If you would like to browse the contents of my repo to see what packages are contained therein, the URLs are:

Binary packages: http://packages.linux2000.com/l2k6server-x86_64/rpm

Source packages: http://packages.linux2000.com/l2k6server-x86_64/sources

Please note that my packages are intended to be used on a 64-bit install of CentOS v6.x. They *may* be compatible with a 64-bit install of RedHat Enterprise Linux v6.x, but I have not tested this. I have no plans to port these packages to other versions of CentOS or to other Linux distributions. Source RPMs are available for those who want to try.

These packages are offered as a public service in the hope that they will be considered useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Any use you may make of the contents of this repository is understood by you to be entirely at your own risk.

Feedback, comments or suggestions for improvements can be made to the e-mail address for me which is embedded in most of these packages.

Rusty Engineering Skills

I’ve been involved with IT since leaving college slightly over 30 years ago and for a couple of years before that, when I got swept up in the illegal CB radio craze in my mid to late teens, I used to supplement my pocket money as one of the local ‘rig doctors’, mostly replacing output stage transistors for people who did mad things like trying to use a metal coat hanger or their standard car radio aerial on 27MHZ. Perhaps if the muse takes me one day, I’ll write an article about the little cottage industry myself and one of my CB buddies (who was a BT RF engineer at the time) had going with rig repairs and home brewed accessories.

Computers have almost always (in my lifetime at least!) been so much easier to work on than transceivers, because the fix for most faults is a complete replacement of a self-contained component rather than a board-level repair. Ironically, though, the last 2 major faults that have befallen our main computer at home have required me to get my soldering iron out, which has had a certain nostalgia value about it, not to mention the fact that the second soldering iron episode highlighted just how out of practice I’ve got with the basic fault finding skills that I learned in my teens whilst repairing other peoples CB radios.

The first problem occured during what passed for the summer in 2012. The 22-inch LCD monitor which I’ve had for some time finally gave up the ghost, after a couple of weeks of needing to be switched off and back on again several times in succession in order to get a stable picture. I’d resigned myself to having to buy a replacement and was all set to take it to our local council waste site for proper disposal. Since I know very little about how TVs and computer screens actually work, I thought it might be educational to take it apart and see what was inside it. After removing 12 screws to take the back off and disconnecting the ribbon cable to the LCD panel, I was slightly disappointed to find just 2 fairly bland circuit boards. The smaller of the two had just 3 or 4 VLSI or very large scale integrated circuits on it and virtually no other components, so I assumed that it was the bit that did the job of decoding the digital video signal from the PC and putting it on the 1680×1050 screen. To my surprise, the larger board was quite clearly a switched mode power supply. To my further surprise, the fault was immediately obvious purely based on a visual inspection. There were 5 radial lead electrolytic capacitors at one end of the board, all of the mega-cheap chinese import variety. Each of these was bowed outwards quite significantly at the top, indicating that they had almost reached the point at which the magic smoke would leak out.

Swiftly bringing to bear my noble soldering iron of justice, I soon had the suspect components on the bench in front of me. They were all 100uF 16v, it was 4:20pm on a Sunday afternoon and my local branch of Maplin was still open for another 40 minutes. Ignoring the cries of “What do you mean you’re going to try to fix it?” from my wife, I jumped in the car and headed to the aforementioned purveyors of electrical and electronic components.

Some time later and the princely sum of £2.75 spent, I had a bag of 5 replacement capacitors, a hot soldering iron and a somewhat incredulous wife and mother-in-law, both with slightly worried looks on their faces. Not to be deterred, I spent the next few minutes carefully desoldering the pads where the original components had been mounted, soldering the new capacitors in place and trimming back the leads to make a neat job. I managed to put the monitor back together without having any parts left over and was feeling quite pleased with myself and my out-of-practice engineering skills, announcing that I was ready to test my repair and see if it had ‘done the trick’.

At this point, my wife suggested that if I was planning to put myself at risk of electrocution, it would be best to do so in the back garden rather than clutter up the house with my scorched and blackened corpse. Ever the obedient husband, I took the computer, keyboard and mouse outside along with an extension lead, the newly repaired monitor and a VGA cable. This was all deposited on the patio table and connected together as appropriate. Upon gingerly applying mains power, I was delighted to see the monitor come up as normal on the first go with an absolutely rock-steady picture. After leaving everything running for 15 minutes, the range safety officer (a.k.a my wife) declared herself satisfied that the fire/electrocution risk seemed to be minimal and I was finally allowed to bring the repaired device back into the family home.

I really didn’t think I’d ever have cause to use my soldering iron to fix another computer problem. How wrong I was.

I decided at the end of February 2013 that it was about time to upgrade the home computer to Windows 7. Windows XP was fast approaching its ‘best before’ date and I’d always had a 32-bit version installed, despite having a computer that was 64-bit ready in almost all respects. After much wavering, I decided to take the plunge.

In preparation for the upgrade, I ordered an extra 8GB of memory, to bring the total installed up to 12GB. When this arrived, it also seemed that now was as good a time as any to strip the computer down and give it a good internal spring clean, removing all the dust, cleaning out fan filters, etc, etc.

So it was that I found myself once again out in the back garden, this time with the computer itself in bits around me. It took about 3 hours of careful disassembly, cleaning and dusting before I was happy with the result. During this process, I noticed that one of the front panel mounted USB connectors (that had been playing up for a while) had a loose connection to one of the pins on its associated header plug. Since I already had the machine in bits anyway, I stripped off the whole front panel switch and connector assembly and carefully teased the tiny little pin and attached wire back into place. I also took the opportunity to re-arrange some of the cable routing inside the case, both to make it look neater and to try and improve the airflow from the cooling fans.

I re-assembled everything, slotted in the extra 8G of memory and spent a moment admiring my shiny, new-looking computer before taking it back indoors. I wired everything back up, including mains power, speakers, USB printer, USB wireless adapter, monitor cable and USB wireless mouse/keyboard doo-hickey. Switching the mains back on, I saw the power LED light up on the front panel, the main chassis and CPU fans spun up for about a second and then everything went dead. No reassuring ‘beep’ to indicate that the power-on self-test or POST had completed successfully, nothing displayed on the freshly-repaired monitor, just complete silence and no sign of any activity at all. The machine hadn’t even had time to get to the point (usually after a couple of seconds) where it spins up the hard drive. The front panel power switch wasn’t causing aything to happen when I pressed it, but I found that if I pulled the kettle lead out of the power supply for 30 seconds then plugged it back in, I’d get the same thing as before, CPU and case fan spinning for about 1 second with the power LED lit, nothing else happening and a total shutdown almost straight away.

Okay, not to worry, I’ve had the whole thing in bits, I’ve obviously connected something back up wrong or left something not quite seated correctly. The first suspect was the newly-fitted memory modules. I discovered by trial and error that the machine would only do anything different if I removed all of its 4 memory modules. It still wouldn’t respond to the front panel power switch, but would at least get as far as the POST checks, at which point I’d get 2 short beeps in a row, which is the signal with this BIOS that it has failed to find any useable memory. I began to suspect at this point that all 4 memory modules had failed for some reason, which was disappointing since I didn’t have any spare DDR3 sticks in my spares box that I could test with.

Without boring you with all the details, I spent another hour trying to diagnose the problem without success. When I got the point where I’d disconnected and/or removed every component and external cable apart from the CPU and it still wouldn’t power up, I started to wonder if my spring clean had killed something vital on the motherboard with a dose of static electricity. Utterly despondent at this point, I put everything back together and had one more go at powering it up. Still no good. I sat and had a think about the problem over a cup of coffee, mentally reviewing what has to happen in order to get a modern PC to power up and load its operating system. It struck me that my last test had been to have the power LED header plug and the front panel switch header plug connected to the motherboard and nothing else and yet it would still only respond if I removed the kettle lead from the power supply for 30 seconds and plugged it back in. I was effectively simulating a power cut, in which case the machine was doing exactly what it was set to do in the BIOS, namely to stay powered off when the electrical supply was restored. The fact that pressing the front panel power button had had no effect in any of the tests I had devised had somehow slipped past me un-noticed.

I took the side panel off the computer once more, pulled out the power button header from the motherboard and shorted out the pins with a flat bladed screwdriver. Lo and behold, the computer powered on as normal, only complaining that it couldn’t find an operating system on the hard drive, which was because I’d completely wiped the drive in preparation for the upgrade to Windows 7. The fault appeared to be with either the actual power button itself or the wiring between the button on the front panel and the motherboard.

I quickly removed the whole front panel assembly again and inspected the ribbon cable connecting the small PCB holding the power and reset buttons to the motherboard. Instead of shiny blobs of solder where the 4-strand ribbon cable met the board, the solder on 3 of the 4 pads was dull and grey, classic signs of a ‘dry joint’. I’d obviously disturbed the cable durng the teardown and re-assembly process sufficiently for the poor quality soldering to make its effects known.

So the soldering iron came out again for the second time in 6 months. I removed all 4 ribbon cable strands and cleaned out the pads on the PCB. I trimmed the 4-way ribbon cable back by about 1.5cms and stripped 4 shiny new ends on each strand. After tinning the bare wires with the soldering iron, I re-attached the whole cable to the PCB, freshly soldered and secure. This time, the computer fired up on the first press of the power button and I was finally able to get Windows 7 installed and restore all the documents, pictures, videos and other data from a recent backup.

I’m hoping it will be a long time before I need to get that soldering iron out again, especially if it involves a broken computer or peripheral.

Too Many Computers

Between work and home, including real hardware and VM instances, I find myself hopping backwards and forwards between 5 or 6 different machines at any given time. I also happen to use the PuTTY SSH client quite a lot. I’ve got a bunch of PuTTY shortcuts configured across my little ‘herd’ of computers to give me quick access to the systems that I access most frequently.The problem is, the shortcuts aren’t exactly the same on all of my machines.

I use Windows-based computers with both 32 and 64 bit versions of the OS installed, which means that the PuTTY binary will be in a different location between, for example, a 32-bit Windows XP computer, a 32-bit Windows7 computer and a 64-bit Windows7 computer. This makes synchronisation of my folder of SSH shortcuts across all machines slightly problematical – if I change a shortcut on the 32-bit XP machine, I can’t just copy the changed .lnk file across to one of my 64-bit Windows7 boxes and expect it to work. With this in mind, I wrote a short Python script to deal with this for me. It will recursively scan a folder containing .lnk files and update each one so that it points off to the right place to launch a PuTTY session.

Note that this script depends upon you having Mark Hammonds ‘Python for Windows’ tool kit installed. If you haven’t got this, you can download it from SourceForge. For my script, either copy and paste the code below or scroll down for a direct download link.

This version (v 1.2) incorporates a bug fix which ensures that it only updates shortcuts which refer to ‘putty.exe’, otherwise it can end up overwriting your entire Start menu if you give it the right path.

#!/usr/bin/python
# Check and adjust PuTTY SSH shortcuts
# vim: ai et sw=4 ts=4
#
# Copyright (C)2012 Phil Edwards <phil 'at' linux2000.com>
#
# License: This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or (at your
# option) any later version. This program is distributed in the hope that it
# will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# CHANGELOG:
#
# Ver    Date       Who  Changes
# -----  ---------  ---  ---------------------------------------------------
# 1.0    21SEP2012  PKE  First version
# 1.1    06MAR2013  PKE  Enhanced command line option processing
# 1.2    16MAY2013  PKE  Bug fix - only update shortcuts that contain
#                        putty.exe in the target path

import os
import sys
import time
from optparse import OptionParser
import pythoncom
from win32com.shell import shell

class Win32Shortcut:
    def __init__(self, lnkname):
        self.shortcut = pythoncom.CoCreateInstance(
            shell.CLSID_ShellLink, None,
            pythoncom.CLSCTX_INPROC_SERVER, shell.IID_IShellLink)
        self.shortcut.QueryInterface(pythoncom.IID_IPersistFile).Load(lnkname)

    def __getattr__(self, name):
        return getattr(self.shortcut, name)

class RunTimeOptions:
    def __init__(self):
        self.__me__ = os.path.basename(sys.argv[0])
        self.__version__ = '1.2'
        self.__copyright__ = "Copyright (C)2013 Phil Edwards <phil 'at' linux2000.com> All Rights Reserved"
        self.__summary__ = 'Automatic PuTTY shortcut fixer'
       
        self.puttyPath = None
        self.found = 0
        self.updated = 0
        self.defaultPath = os.environ['appdata'] + "\Microsoft\Internet Explorer\Quick Launch"
       
        if os.path.exists("C:\Program Files (x86)\PuTTY\putty.exe"):
            self.puttyPath = 'C:\Program Files (x86)\PuTTY\putty.exe'

        if os.path.exists("C:\Program Files\PuTTY\putty.exe"):
            self.puttyPath = 'C:\Program Files\PuTTY\putty.exe'
   
        self.getCommandLineOptions()

        self.logMsg(txt = '%s v%s %s' % (self.__me__, self.__version__, self.__summary__))
        self.logMsg(txt = 'command line arguments: [%s]' % ' '.join(sys.argv[1:]))
        self.logMsg(txt = 'Executable found at [%s]' % self.puttyPath)

    def logMsg(self, **kwargs):
        txt = kwargs.get('txt', '')
        dateStamp = kwargs.get('dateStamp', True)
        msgLine = ''

        if txt != '':
            if dateStamp: msgLine += '[' + time.strftime("%Y%m%d-%H%M%S", time.localtime()) + '] '
            if not self.doUpdate: msgLine += 'NOUPDATE: '
            msgLine += txt
            print msgLine

    def debugMsg(self, **kwargs):
        txt = kwargs.get('txt', '')
        dateStamp = kwargs.get('dateStamp', True)
        pause = kwargs.get('pause', False)
        msgLine = ''

        if self.debugMode and txt != '':
            if dateStamp: msgLine += '[' + time.strftime("%Y%m%d-%H%M%S", time.localtime()) + '] '
            msgLine += 'DEBUG: ' + txt
            if pause:
                bull = raw_input(msgLine + ': Press any key to continue : ')
            else:
                print msgLine

    def getCommandLineOptions(self):
        vString = '%%prog (%s) v%s' % (self.__summary__, self.__version__)

        dString = 'Script to examine PuTTY SSH shortcuts found in the folder '
        dString += 'referenced by BASEPATH and update them so that '
        dString += 'they contain the correct path to the PuTTY executable'

        eString = ''
       
        uString = 'usage: %prog [options] BASEPATH'

        parser = OptionParser(version = vString, description = dString,
            epilog = eString, usage = uString)

        parser.add_option('-d', '--dry-run',
            action = 'store_false', dest = 'doupdate', default = True,
            help = 'process as normal, but do not update any files')

        parser.add_option('-x', '--debug',
            action = 'store_true', dest = 'debugmode', default = False,
            help = 'emit debug messages for troubleshooting')

        parser.add_option('-v', '--verbose',
            action = 'store_true', dest = 'verbose', default = False,
            help = 'be more verbose when processing')

        (options, args) = parser.parse_args()
       
        if len(args) == 0:
            self.basePath = self.defaultPath
        elif len(args) == 1:
            self.basePath = args[0]
        else:
            print 'ERROR: ambiguous command line parameters'
            sys.exit(0)

        if self.puttyPath is None:
            print 'ERROR: Unable to locate PuTTY executable'
            sys.exit(0)
           
        if not os.path.exists(self.basePath):
            print 'ERROR: base path does not exist'
            sys.exit(0)

        self.doUpdate = options.doupdate
        self.debugMode = options.debugmode
        self.verbose = options.verbose

        self.debugMsg(txt = 'options = %s' % options)
        self.debugMsg(txt = 'args = %s' % args)

def processItem(my, path):
    my.found += 1
   
    s = Win32Shortcut(path)
    iconPath = s.GetIconLocation()[0]
    itemPath = s.GetPath(0)[0]
    my.debugMsg(txt = 'itemPath is %s' % itemPath, pause = True)

    if itemPath != my.puttyPath and 'putty.exe' in itemPath.lower():
        s.SetPath(my.puttyPath)
        pf = s.QueryInterface (pythoncom.IID_IPersistFile)
        if my.doUpdate: pf.Save(path, 0)
        my.updated += 1
        my.logMsg(txt = '%s updated' % path)
    else:
        if my.verbose:
            my.logMsg(txt = '%s OK' % path)

def processFolder(my, path):
    if not os.path.isdir(path): return
   
    if my.verbose:
        my.logMsg(txt = 'Processing folder [%s]' % path)
       
    flist = os.listdir(path)
    for item in flist:
        newPath = os.path.join(path, item)
        if os.path.isdir(newPath):
            processFolder(my, newPath)
        elif item.endswith('.lnk'):
            processItem(my, os.path.join(path, item))

def main():
    my = RunTimeOptions()

    my.logMsg(txt = 'Processing started')
    processFolder(my, my.basePath)
    my.logMsg(txt = '%s shortcuts found' % my.found)
    my.logMsg(txt = '%s shortcuts updated' % my.updated)
    my.logMsg(txt = 'Processing complete')

if __name__ == '__main__':
    main()
Download1413 downloads

Thank $DEITY For Disk Imaging Software

I often get called on to deal with family computer and/or data related mishaps. Disk imaging software has saved the day on several occasions recently.

The event that prompted this post actually happened a few weeks ago. Daughter #2 brought her 12 month old Acer laptop to me, complaining of random BSOD crashes. I took a quick look at it and noticed lots of suspicious symptoms straight away, for example the Windows security centre service was disabled and refused to restart, the AV system tray icon was not showing and the AV software itself wouldn’t start up. A boot into safe mode and a scan with Malware Bytes soon confirmed that the machine had been hosed by one of the many fake AntiVirus malware strains. The malware itself was able to be removed, but it had done extensive damage to the registry and the only option to ensure a full repair seemed to be a full re-install of Windows 7.

Just going off on an apparent tangent for a moment, manufacturers rarely provide physical media with a new computer. Instead, some kind of backup software is pre-installed which allows a set of physical media to be created by the end user. Whilst this seems like a good idea at first glance, my experience has been that it invariably fails utterly. The story almost always goes like this:

Amidst the excitement of getting a new laptop out of its box and having a shiny new toy to play with, the message that appears on screen requesting the creation of recovery media gets dismissed with a click on the ‘Not Now’ button.

The same prompt pops up again a bit later, by which time the owner is busy setting up their e-mail account, getting on to Facebook and posting ‘I haz new laptop’ status updates – the prompt once again gets dismissed.

Whilst this is going on, the operating system will have been silently downloading and installing updates in the background. Some of these will require a reboot, which irritates the owner because it interrupts the process of playing with the new toy.

At reboot time, the prompt for creation of recovery media appears again, only this time the owner notices that there is also a little tick box which says ‘Never remind me again’…

12 months down the line, the laptop has been used, abused and generally thrashed to within an inch of its life and suddenly starts playing up in some manner, or it gets hit with a virus. This is where I end up being asked to look at it. If it turns out that a new hard drive is required, or maybe (and more commonly, especially with a virus infestation) just a re-install of Windows to put everything back to a known clean state, the first thing I ask for is the recovery media. Inevitably the response to this is “The what?”. I patiently explain about recovery media and how they would have been prompted to create same when they first got the laptop. “Oh, yeah, I just ignored that. Was it important, then?” – at which point I’m in <herewegoagain> mode.

So, broken laptop, users data may or may not be intact, OS is definitely screwed, no data backup available. What to do? The last 3 times I’ve been in this situation, I’ve used a product called Macrium Reflect to get what I could off the broken machine prior to repair/rebuild. Together with an adapter cable to allow PATA/SATA drives to be hooked up to a USB port, recovering the data becomes a fairly straightforward, if sometimes time-consuming, exercise:

  • Extract the hard drive from the broken machine, connect it to my computer and image it using Macrium Reflect
  • Put the drive back in its host machine, launch the manufacturers factory recovery process and put the machine back to its default ‘out of the box’ state
  • Remove the vendors assorted items of bloatware, re-install any required applications software and allow Windows to apply all outstanding updates
  • Extract the hard drive from the now working machine, connect it to my computer and restore the users data (documents, pictures, videos, etc) from the disk image taken earlier
  • Put the drive back into the host machine

With the machine now in a known good state, including all user data, it is possible at this point to create a second disk image using Macrium. This can be kept on offline storage (for example, an external hard drive) until such time as the machine breaks again or gets hit with another virus. Next time round, the recovery process is much simpler, needing only a restore of the second disk image to get everything back to normal in a single operation. Even better, Macrium can be installed on the machine you just repaired and configured to take a regular backup. Provided you’re careful with storage requirements, the end user is then keeping a copy of their own data in readiness for the next disaster that strikes them.

Macrium is free for personal use and registration for the full version is GBP £47.95 for a single machine licence, rising to GBP £95.90 for a ‘household’ licence which covers up to 4 machines. The licenced version adds incremental backup features, encryption of backed up data and integration with the Windows boot menu to allow easy recovery. I’ll make the usual disclaimer that I have no connection with Macrium Software other than as an very satisfied user of their products.

 

 

Got My Slice!

My Raspberry pi finally arrived today. Haven’t decided what to do with it yet, haven’t even opened the box, in fact!

image

Oh, forgot – this article is also the first one I’ve posted from the WordPress app on my Galaxy S3, so double whammy!

Python Command Line Script Template

Despite having used Python as my scripting language of choice for several years, I still find myself stumbling across the odd ‘hidden gem’ when searching through the ‘global module index’, which is part of the documentation that comes with the language.

A fine example of this cropped up recently when I was writing some command line utilities to help with a particular project. I like to make these things as user-friendly as possible, mainly so that if someone else has to use them, they can do so without me having to write reams of documentation explaining how each one works. I also have an intense dislike for the process of re-inventing wheels, it therefore made sense to come up with some boilerplate code that I could use for each script which covered just the basics of getting command line options, providing a help feature and allowing for debug messages to be added for diagnostic purposes when testing the code.

Python comes with several modules which deal with command line option processing, each of which has its own strengths and weaknesses. The code template shown here uses the optparse module, which is able to provide all of the features that I wanted to have in my scripts, without making me re-code everything by hand for each one. I wanted each script to:

  • Provide online help for available command line options, using the accepted convention of running the script with a ‘-h’ or ‘–help’ argument.
  • Support a ‘-v’ or ‘–verbose’ argument so that there is the option to provide more detailed output when the script is run.
  • Support the use of a ‘–dry-run’ option so that a script can show what it would do, but not actually make any changes to input data or files
  • Have predefined functions for displaying status and/or debug messages

The resulting code template runs to a rather svelte 120 lines. Of these, 19 lines are comments and 24 are blank lines inserted for readability. The code below also has some extra examples of how you might, for example, specify an input file to be processed, or restrict processing to the first few lines of input data only.

You can either copy and paste the code below, or scroll down for a download link:

#!/usr/bin/python
# Skeleton script with command line parsing
# vim: ai et sw=4 ts=4
#
# Copyright (C)2012 Phil Edwards <phil 'at' linux2000.com>
#
# License: This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or (at your
# option) any later version. This program is distributed in the hope that it
# will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# CHANGELOG:
#
# Ver    Date       Who  Changes
# -----  ---------  ---  ---------------------------------------------------
# 1.0    21SEP2012  PKE  First version

import os
import sys
import time
from optparse import OptionParser

class RunTimeOptions:
    def __init__(self):
        self.__me__ = os.path.basename(sys.argv[0])
        self.__version__ = '1.0'
        self.__copyright__ = "Copyright (C)2012 Phil Edwards <phil 'at' linux2000.com> All Rights Reserved"
        self.__summary__ = 'Awesome Python script written by Phil'
        self.getCommandLineOptions()
        self.validateOptions()

        self.logMsg(txt = '%s v%s %s' % (self.__me__, self.__version__, self.__summary__))
        self.logMsg(txt = 'command line arguments: [%s]' % ' '.join(sys.argv[1:]))

    def logMsg(self, **kwargs):
        txt = kwargs.get('txt', '')
        dateStamp = kwargs.get('dateStamp', True)
        msgLine = ''

        if txt != '':
            if dateStamp: msgLine += '[' + time.strftime("%Y%m%d-%H%M%S", time.localtime()) + '] '
            if not self.doUpdate: msgLine += 'NOUPDATE: '
            msgLine += txt
            print msgLine

    def debugMsg(self, **kwargs):
        txt = kwargs.get('txt', '')
        dateStamp = kwargs.get('dateStamp', True)
        pause = kwargs.get('pause', False)
        msgLine = ''

        if self.debugMode and txt != '':
            if dateStamp: msgLine += '[' + time.strftime("%Y%m%d-%H%M%S", time.localtime()) + '] '
            msgLine += 'DEBUG: ' + txt
            if pause:
                bull = raw_input(msgLine + ': Press any key to continue : ')
            else:
                print msgLine

    def getCommandLineOptions(self):
        vString = '%%prog (%s) v%s' % (self.__summary__, self.__version__)

        dString = 'A super awesome Python script. Phil wrote it, so it must be good! '
        dString += 'It does all sorts of really cool stuff, and even has some flashing '
        dString += 'lights to keep you amused.'

        eString = 'OK, maybe I lied about the flashing lights, but the rest '
        eString += 'of it is really good'

        parser = OptionParser(version = vString, description = dString, epilog = eString)

        parser.add_option('-d', '--dry-run',
            action = 'store_false', dest = 'doupdate', default = True,
            help = 'process as normal, but do not update any files')

        parser.add_option('-x', '--debug',
            action = 'store_true', dest = 'debugmode', default = False,
            help = 'emit debug messages for troubleshooting')

        parser.add_option('-i', '--input', dest = 'inputfile',
            #action = 'append', # lets you have more than one file if required
            help = 'read input data from FILE, which must exist', metavar = 'FILE')

        parser.add_option('-l', '--limit', dest = 'limit', type='int',
            help = 'limit processing to the first NUM items found', metavar = 'NUM')

        parser.add_option('-v', '--verbose',
            action = 'store_true', dest = 'verbose', default = False,
            help = 'be more verbose when processing')

        (options, args) = parser.parse_args()

        self.doUpdate = options.doupdate
        self.debugMode = options.debugmode
        self.verbose = options.verbose
        self.inputfile = options.inputfile

        self.debugMsg(txt = 'options = %s' % options)
        self.debugMsg(txt = 'args = %s' % args)

    def validateOptions(self):
        if self.inputfile is not None and not os.path.exists(self.inputfile):
            self.logMsg(txt = 'ERROR: file %s does not exist' % self.inputfile, dateStamp = False)
            sys.exit(0)

def main():
    my = RunTimeOptions()

    my.logMsg(txt = 'Processing started')
    my.debugMsg(txt = 'This is a debug message with no datestamp', dateStamp=False)
    my.debugMsg(txt = 'This debug message has a datestamp')
    my.debugMsg(txt = 'Debug message with datestamp and a pause', pause=True)
    my.debugMsg(txt = 'Debug message without datestamp, with a pause', dateStamp=False, pause=True)

    my.logMsg(txt = 'Processing complete')

if __name__ == '__main__':
    main()
Download a copy1796 downloads

The site finally gets an update…

The original linux2000 site layout was designed in 2002 and was starting to look extremely dated and generally just very web-1.0. Added to that, it wasn’t as easy to make updates as I would have liked. The new site is on a dedicated server and is based around some reasonably up to date blogging software, so look out (hopefully) for slightly more regular updates in the future.

Not all of the content from the old site has been transferred yet, but being the king of procrastination that I am, I’ve spent weeks agonising over colour schemes, menu layouts, etc, etc, so I came to the conclusion that if I didn’t just do the DNS change to make the new site live, it would ever happen. So enjoy – I’ll get the rest of the junk uploaded as and when I can!

Logbook Summary

I’d had a yearning to fly since the age of 13, when I was a cadet in the ATC. I started studying for a group D licence, i.e. microlights, in 1991 and had clocked up less than 4 hours when I suddenly found myself out of a job for around 9 months. In the years that followed, I found another job, moved to the south coast of England, changed jobs again, met the girl who would later become my wife, got a mortgage, had 2 children, etc, etc, etc. Before I knew what was going on, more than 10 years had passed and I had almost forgotten all about flying, until my 40th birthday arrived…

This is how my flying experience stands at present. The full JAA-PPL is just too expensive for me to contemplate at this point, so it”s back to microlighting for me! The minimum requirement for a NPPL with microlight rating is 25 hours and very few people manage it in the minimum time. I”ll just keep telling myself that…

 

  • Last Lesson: 26 June 2011
  • Total Time: 11h 10m
  • Next Lesson: n/a

Aerobatics!

What an amazing way to spend 30 minutes on a sunny Sunday.

I was given a voucher at christmas time by SWMBO for a short flight in a Stampe SV4 at Shoreham. An irritating combination of bookings cancelled due bad wx, work commitments and the aircraft being away for an extended period for maintenance meant that I didn’t get to use my voucher until today.

All I can say is that it was well worth waiting for!

After strapping in and watching while the prop was hand swung, I had my first go at taxying a tail dragger. It seemed to take an age to get any response when pushing the pedals, but when it finally did start to turn, it turned like a dodgem car. Is this common to all tail wheel aeroplanes, or more likley to be just my lack of experience?

We took off and pottered along the coast, heading for Brighton Marina. I took the opportunity to get the feel of the controls, starting with some gentle turns. As I began to feel a bit more confident, the bank angles started to increase until the instructor was happy for me to be twirling us round each wingtip at 60 degrees or more of bank. Great fun and probably not something I’d be allowed to do in the C42 that I’m more used to.

The first aerobatic maneouver was a loop. Using Brighton pier to orient ourselves, the instructor pushed the nose down and let the airspeed build up to 110 knots, then smoothly pulled back. The sensation as we went over the top and I looked up at the sea was incredible! A reasonably tight turn out to sea, climbing back up to about 1800 feet and we pointed back at the pier to have another go. This time I was asked to follow through on the controls to get the feel of the stick forces involved. Paying slightly more attention this time, I noticed the wind singing in the wires next to me as the airspeed bled off at the top and found myself looking straight down at a matchbox sized model of a yacht as we pulled through and back up to level flight. For the 3rd loop, I got to work the stick and throttle myself with the instructor following me and correcting for any mistakes – I’m sure there were several!

We continued along the coast and did the usual ‘ooh, I can see my house from here’ bit. Turning slightly inland, a roll was the next order of the day. Apparently the Stampe has a tendency for the engine to cut on a roll if you’re too ham fisted with it. 99 times out of 100, it starts up again without any problems just by the propellor rotating in the airflow, but the instructor wanted us over the dry bit rather than the wet bit, just in case…

The roll wasn’t quite how I’d imagined it to be. Dive slightly, pull up and stick over to the left and roughly half way forward until we came back round to level again. I’m sure there was something going on with the rudder pedals, too, but it all seemed to happen so quickly I wasn’t sure. We did another 2 of those as well just for good measure, but I didn’t make a particularly good job of following the control inputs.

I was surprised to find that despite this being my first time doing aerobatics, I wasn’t feeling even a tiny bit queasy. Having too much fun I guess!

Regrettably, that was almost the end of the half hour. I was told ‘your aircraft’ and the instructor asked me to fly us along the ridge of the downs back towards Shoreham for a right base join into the circuit for runway 13. As we approached the circuit I handed the controls back and just enjoyed the view as we settled gently onto the grass, then taxy’d back to our little parking spot near the pumps and the fire station.

It was perfect weather today for an introduction to the slighly more hooliganistic corners of the flight envelope. The grin still hasn’t quite worn off, nearly 5 hours later. A big thumbs up for Ian and the rest of the guys/girls at PerryAir and, of course, to my wife for having the idea in the first place!

Now, who wants to demonstrate a stall turn or 3 for me…? 😀