## Posts Tagged ‘scripting’

### ZFS with Redhat Cluster Suite

Friday, July 25th, 2014

This is a very nice project I have been working on. The hardware at hand – two servers, with a shared SAS bus containing several SAS disks. Since it’s a shared bus, no RAID solution would cut it, and as I don’t want to waste disks with ASM (“normal” redundancy meaning half the size…), I went to ZFS storage.

ZFS is a wonderful technology, with many advantages, but with some dangerous pitfalls. As I prefer Linux, I did not bother with any Sloaris solutions, and went directly to Centos 6. I will describe my cluster setup below.

I will disclose the entire setup, including hardware layout, Linux platform, ZFS module parameters, the Redhat Cluster Suite ZFS agent I wrote and the cluster.conf configuration file. I will also share my considerations regarding some of the choices I made. In addition, this system was designed to act as NFS storage for Citrix XenServer pool, so I will have to describe the changed I had to perform on the XenServer itself (which might make it unsupported, but I will have to live with it), to allow it to handle the timeouts resulting by server failover.

So first – the servers – each having a single CPU (quad core), 24GB RAM, and dual 1Gb/s NICs. Also – a tiny internal SATA disk is used for the OS. The shared disks – at the moment, 10 SAS disks, dual port (notice – older HP disks might mark in a very small letters that they are only a single-port SAS disks…), 72GB, 10K RPM. Zpool called ‘share’ with two 5 disks RaidZ1 vdevs. As I mentioned before – ZFS seemed like the best possible option allowing me to achieve my goals at minimal cost.

When I came to this project, I wanted to be able to use a native ZFS cluster agent, and not a ‘script’ agent, which takes a very long time to respond (30 seconds). Also – I wanted to be able to handle multiple storage pools concurrently – each floating on its own. While I have only one at the moment, I wanted the ability to have a fine-grained control over multiple pools. In addition – I am unable (or unwilling?) to handle the multiple filesystems introduced with each pool. I wanted to be able to import or export the pool silently, and with a clear head, thus I had to verify that the multiple filesystems are not in use as part of the export process.

As an agent, I wanted to comply with Redhat Cluster Suite (RHCS from now on) OCF syntax. I used the supplied fs.sh script as an inspiration for my agent script, so some of it might look familiar. All credit goes to the original authors, of course.

The operating system I selected was Centos 6. Centos is based on Redhat Linux, and I find it mature and stable, which is exactly what I want when I plan a production-ready, enterprise-class storage solution. The version had to be x86_64, due to ZFS requirements, and due to the amount of RAM in the server.

To handle ZFS options, I added a file called /etc/modprobe.d/zfs.conf, with the following content

install zfs /bin/rm -f /etc/zfs/zpool.cache && /sbin/modprobe –ignore-install zfs
options zfs zfs_arc_max=12593790976
options zfs zfs_arc_min=12593790975

I had to verify there is no zpool.cache file. Since my pool was rather small (planned for 24 disks max), I was not concerned by the longer import process caused by not having the zpool.cache file. I was more concerned with automatic import process which might happen, and had to prevent it at almost any cost. In addition, I learned from other systems that the arc memory should never exceed half the RAM, and it should be given just a little under that.

Of course, when changing such module settings, you need to recreate initrd (dracut -f) to be on the safe side later on.

The zfs.sh agent script was placed in /usr/share/cluster directory. You must have rgmanager installed for this directory to exist, and anyhow, without rgmanager, you will have no cluster whatsoever.

This is the contents of the zfs.sh file. Notice that it is not compatible with Luci, so if you’re using it – them kids won’t play well together.

 #!/bin/bash   LC_ALL=C LANG=C PATH=/bin:/sbin:/usr/bin:/usr/sbin export LC_ALL LANG PATH # Private return codes FAIL=2 NO=1 YES=0 YES_STR="yes"   . $(dirname$0)/ocf-shellfuncs   meta_data() { cat <   1.0   This script will import and export ZFS storage pools It will make sure to mount and umount all child filesystems   This is a ZFS pool   Symbolic name for this zfs pool   File System Name   ZFS Pool name or ID   ZFS pool name   ZFS Pool alternate mount   ZFS pool alternate mount   If set, the cluster will kill all processes using this file system when the resource group is stopped. Otherwise, the unmount will fail, and the resource group will be restarted.   Force Unmount   If set and unmounting the file system fails, the node will immediately reboot. Generally, this is used in conjunction with force-unmount support, but it is not required.   Seppuku Unmount       EOT }   ocf_log() { echo $* } verify_driver() { ocf_log info "Verifying ZFS driver" lsmod | grep -w zfs > /dev/null 2>&1 && return 0 ocf_log err "ZFS driver is not loaded" return$OCF_ERR_ARGS }   verify_poolname() { ocf_log info "Verify pool name " if [ -z "$OCF_RESKEY_pool" ] then ocf_log err "Missing pool name" return$OCF_ERR_ARGS fi zpool import | grep pool: | grep -w $OCF_RESKEY_pool > /dev/null 2>&1 && return 0 ocf_log err "Cannot identify pool name" return$OCF_ERR_ARGS }   verify_mounted_poolname() { ocf_log info "Verify pool name " if [ -z "$OCF_RESKEY_pool" ] then ocf_log err "Missing pool name" return$OCF_ERR_ARGS fi zpool list $OCF_RESKEY_pool > /dev/null 2>&1 && return 0 ocf_log err "Cannot identify pool name" return$OCF_ERR_ARGS }   verify_mountpath() { ocf_log info "Verifying alternate root mount path" [ -z "$OCF_RESKEY_mount" ] && return 0 declare mp="${OCF_RESKEY_mount}" case "$mp" in /*) # found it ;; *) # invalid format ocf_log err \ "verify_mountpath: Invalid mount point format (must begin with a '/'): \'$mp\'" return $OCF_ERR_ARGS ;; esac } pool_import() { ocf_log info "Importing pool" OPTS="" [ -n "$OCF_RESKEY_mount" ] && OPTS="-R $OCF_RESKEY_mount" zpool import$OCF_RESKEY_pool $OPTS RET="$?" if [ "$RET" -ne "0" ] then ocf_log info "Cannot import without applying force" zpool import -f$OCF_RESKEY_pool $OPTS RET="$?" fi if [ "$RET" -ne "0" ] then ocf_log err "Pool import failed for$OCF_RESKEY_pool. error=$RET" return 1 fi ocf_log info "Imported ZFS pool" return$RET }   check_and_release_fs() { ocf_log info "Checking and releasing FS" FS="" case ${OCF_RESKEY_force_unmount} in$YES_STR|on|true|1) force_umount=$YES ;; *) force_umount="" ;; esac RET=0 for i in zfs list -t filesystem | grep ^${OCF_RESKEY_pool} | awk '{print $NF}' do # To be on the safe side. Why not? sleep 1 # Is it mounted? if ! df -l | grep -w "$i" > /dev/null 2>&1 then ocf_log info "Filesystem $i is not mounted" continue fi if [ lsof$i | wc -l -gt "0" ] then ocf_log info "Filesystem $i is in use" if [ "$force_umount" ] then ocf_log info "Attempting to kill processes on $i filesystem" fuser -k$i sleep 2 if [ lsof $i | wc -l -gt "0" ] then ocf_log err "Cannot umount filesystem$i - filesystem in use" return 1 fi else ocf_log err "Cannot umount filesystem $i - filesystem in use" return 1 fi fi done return$RET }   self_fence() { ocf_log info "Should we validate and call self-fence?" case ${OCF_RESKEY_self_fence} in$YES_STR|on|true|1) self_fence=$YES ;; *) self_fence="" ;; esac if [ "$self_fence" ]; then ocf_log alert "umount failed - REBOOTING" sync reboot -fn fi return $OCF_ERR_GENERIC } pool_export() { ocf_log info "Exporting zfs pool" check_and_release_fs || self_fence zpool export$OCF_RESKEY_pool RET="$?" if [ "$RET" -ne "0" ] then ocf_log err "Pool export failed for $OCF_RESKEY_pool. error=$RET" return 1 fi return $RET } start() { ocf_log info "Starting ZFS" verify_driver || return$OCF_ERR_ARGS verify_poolname || return $OCF_ERR_ARGS verify_mountpath || return$OCF_ERR_ARGS pool_import # Handle filesystem? }   stop() { ocf_log info "Starting ZFS" verify_driver || return $OCF_ERR_ARGS verify_mounted_poolname || return$OCF_ERR_ARGS verify_mountpath || return $OCF_ERR_ARGS # Handle filesystem? pool_export } is_imported() { ocf_log debug "Checking if$OCF_RESKEY_pool is imported" zpool list ${OCF_RESKEY_pool} > /dev/null 2>&1 return$? }   is_alive() { ocf_log debug "Checking ZFS pool read/write" declare file=".writable_test.$(hostname)" declare TIMEOUT="10s" [ -z "$OCF_CHECK_LEVEL" ] && export OCF_CHECK_LEVEL=0 mount_point=zfs list ${OCF_RESKEY_pool} | grep${OCF_RESKEY_pool} | awk '{print $NF}' test -d "$mount_point" if [ $? -ne 0 ]; then ocf_log err "${OCF_RESOURCE_INSTANCE}: is_alive: $mount_point is not a directory" return$FAIL fi [ $OCF_CHECK_LEVEL -lt 10 ] && return$YES   # depth 10 test (read test) timeout -s 9 $TIMEOUT ls "$mount_point" > /dev/null 2> /dev/null errcode=$? if [$errcode -ne 0 ]; then ocf_log err "${OCF_RESOURCE_INSTANCE}: is_alive: failed read test on [$mount_point]. Return code: $errcode" return$NO fi   [ $OCF_CHECK_LEVEL -lt 20 ] && return$YES   # depth 20 check (write test) rw=$YES for o in echo$OCF_RESKEY_options | sed -e s/,/\ /g; do if [ "$o" = "ro" ]; then rw=$NO fi done if [ $rw -eq$YES ]; then file="$mount_point"/$file while true; do if [ -e "$file" ]; then file=${file}_tmp continue else break fi done timeout -s 9 $TIMEOUT touch$file > /dev/null 2> /dev/null errcode=$? if [$errcode -ne 0 ]; then ocf_log err "${OCF_RESOURCE_INSTANCE}: is_alive: failed write test on [$mount_point]. Return code: $errcode" return$NO fi rm -f $file > /dev/null 2> /dev/null fi return$YES }   monitor() { ocf_log debug "Checking ZFS pool $OCF_RESKEY_pool, Level$OCF_CHECK_LEVEL" verify_driver || return $OCF_ERR_ARGS is_imported RET=$? if [ "$RET" -ne$YES ]; then ocf_log err "${OCF_RESOURCE_INSTANCE}:${OCF_RESKEY_device} is not mounted on ${OCF_RESKEY_mountpoint}" return$OCF_NOT_RUNNING fi is_alive return $RET } if [ -z "$OCF_CHECK_LEVEL" ]; then OCF_CHECK_LEVEL=0 fi   case $1 in start) ocf_log info "zfs start$OCF_RESKEY_pool\n" OCF_CHECK_LEVEL=0 monitor [ "$?" -ne "0" ] && start || ocf_log info "$OCF_RESKEY_pool is already mounted" exit $? ;; stop) ocf_log info "zfs stop$OCF_RESKEY_pool\n" OCF_CHECK_LEVEL=0 monitor [ "$?" -eq "0" ] && stop || ocf_log info "$OCF_RESKEY_pool is not mounted" exit $? ;; status|monitor) ocf_log debug "ZFS monitor$OCF_RESKEY_pool" monitor exit $? ;; meta-data) echo -e "zfs metadat$OCF_RESKEY_address\n" >>/tmp/out meta_data exit 0 ;; validate-all) exit 0 ;; *) echo "usage: $0 {start|stop|status|monitor|restart|meta-data|validate-all}" exit$OCF_ERR_UNIMPLEMENTED ;; esac

All I had to do now was to build the cluster.conf file.

The reason I placed the IP address as the last to start and the first to stop was that the other way around, the NFS client would receive an ordered disconnection command, and would not bother to establish a connection with the remaining server. Abruptly taking away the clustered IP address causes the NFS clients to initiate a reconnection process, of which the systems are supposed to recover

I have left this article incomplete for a while now. It has some stuff I do like to share, so I am sharing it as-is. I will (some day) complete it.

Saturday, June 7th, 2014

Well, tricks is not the right word to describe advanced shell scripting usage, however, it does make some sense. These two topics are relevant to Bash version 4.0 and above, which is common for all modern-enough Linux distributions. Yours probably.

These ‘tricks’ are for advanced Bash scripting, and will assume you know how to handle the other advanced Bash topics. I will not instruct the basics here.

Trick #1 – redirected variable

What it means is the following.

Let’s assume that I have a list of objects, say: ‘LIST=”a b c d”‘, and you want to create a set of new variables by these names, holding data. For example:

a=1
b=abc
c=3
d=$a How can you iterate through the contents of$LIST, and do it right? If you’re having only four objects, you can live with stating them manually, however, for a dynamic list (example: the results of /dev/sd*1 in your system), you might find it a bit problematic.

A solution is to use redirected variables. Up until recently, the method involved a very complex ‘expr’ command which was unpleasant at best, and hard to figure at its worst. Now we can use normal redirected variables, using the exclamation mark. See here:

for OBJECT in $LIST do # Place data into the list export$OBJECT=$RANDOM done for OBJECT in$LIST
do
echo ${!OBJECT} done Firstly – to assign value to the redirected variable, we must use ‘export’ prefix.$OBJECT=$RANDOM will not work. Secondly – to show the content, we need to use exclamation mark inside the variable curly brackets, meaning we cannot call it$!OBJECT, but ${!OBJECT}. We cannot dynamically create the variable name inside the curly brackets either, so${!abc_$SUFFIX} won’t work either. We can create the name beforehand, and then use it, like this: DynName=abc_$SUFFIX ; echo ${!DynName} Trick #2 – Using strings as an array index It was impossible in the past, but now, one of the most useful features of having smart list is accessible in shell. We can now call an array with a label. For example: for FILE in$( ls )
do
array["$FILE"]=$( ls -la $FILE | awk ‘{print$7}’ )
done

In this example we create array cells with the label being the name of the file, and populating them with the size (this is the result of ls -la 7th field) of this file.

This will work only if the array was declared beforehand using the following command (using the array name ‘array’ here):

declare -A array

Later on, it is easier to query data out of the array, as long as you know its index name. For example

FILE=ez-aton.txt
echo ${array[$FILE]}

Of course – assuming there is an entry for ez-aton.txt in this array.

The best use I found for this feature so far was for comparing large lists, without the need to reorder the objects in the array. I find it to boost the capabilities of arrays in Bash, and arrays, in general, are very powerful tools to handle long and complex lists, when you need to note the position.

That’s all fox. Note that the blog editor might change quites (single and double) and dashes to the UTF-8 versions, which will not go well in a copy/paste attempt to experiment with the code examples placed here. You might need to edit the contents and fix the quotes/dashes manually.

If you have any questions, comment here, I will be happy to elaborate. I hope to be able to add more complex Bash stuff I get into once a while

### XenServer – Setting virtual disks names based on the VM names

Wednesday, January 2nd, 2013

One of the worst things you can have in XenServer, is some wize-guy performing a ‘forget storage’ on a storage device still holding virtual disks related to VMs. As XenServer database is internal (for the whole pool) and not per-VM, all references to this virtual disks disappear, and you remain with bunch of VMs without disks, and later on, when the recovered from the shock and restored the SR, with a bunch of virtual disks you have no clue as to where they belong. Why? Because we are lazy, and we tend to skip the part where you can (or is it – should?) define a custom name for your virtual disks so you would know later on (for example – in the case specified above) where they belong(ed).

To solve this annoying issue, and to save time for Citrix XenServer admins, I have created a script which resets the VDI (virtual disk object) names to the name of the VM+ the logical position of the virtual disk (example: xvda, hdb, etc), related to the VM. That way, it will become very easy to identify the disks in case of such annoying micro-catastrophy (micro because no data is lost, just where it belongs…).

The script can be called manually, and since we’re lazy people, and we will forget to handle it manually every said interval, and will accumulate virtual machines with “Template of XYZ” virtual disks, it can be called from cron. When called manually, it asks the user to proceed by pressing ‘Enter’. If called from cron, it just runs.

Enjoy!

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65  #!/bin/bash # This script will reset the names of the virtual disks used for each VM to a standard name, based on the VM name # and the disk position # It is meant to solve problems where due to 'forget storage' operations or the likes # virtual disk associations disappear, and you face many disks having the same name # # Written by Ez-Aton: http://run.tournament.org.il     if [ -t 1 ] then echo "This script will reset *all* VM disks to a name constructed of the VM and the disk name (xvda, hdb, etc)" echo "This operation is not reversible, however, it can be called repeatedly" echo "If you want this script to skip a said virtual disk, make sure its name includes the name of the VM" echo "For example 'vm1 the real important data disk' for a disk used by vm1." echo "Note that the name is case sensitive, and it is very important that to match the name using upper/lower case letters as needed" echo "To abort, press Ctrl+C" echo "To proceed, press Enter" read abc fi   VM_LIST=xe vm-list is-control-domain=false --minimal | tr , ' '   for i in $VM_LIST do # Resetting several parameters, so we have a clean start VM_NAME="" VBD_LIST="" VDI_LIST="" # We iterate through all existing VMs, to get both their names, and their disks VM_NAME="xe vm-param-get uuid=$i param-name=name-label" if [ -z "$VM_NAME" ] then # We have a problem with empty VM names, so we will use the VMs uuid VM_NAME=$i fi VBD_LIST=xe vbd-list vm-uuid=$i --minimal | tr , ' ' for j in$VBD_LIST do # Resetting several parameters, so we have a clean start VDI_UUID="" DEV_NAME="" # We iterate through all existing VBDs to reset the VDI nane VDI_UUID=xe vbd-param-get uuid=$j param-name=vdi-uuid if [ "$VDI_UUID" == "" ] then # This is a virtual CDROM continue fi DEV_NAME=xe vbd-param-get uuid=$j param-name=device VDI_NAME=xe vbd-param-get uuid=$j param-name=vdi-name-label   # Test if the name was reset in the past or manually TGT_NAME="$VM_NAME$DEV_NAME" if [[ "$TGT_NAME" = "$VDI_NAME" ]] then # There is nothing to do echo "Name already includes VM name, so nothing to do" else # Here we reset the VDI name echo xe vdi-param-set uuid=$VDI_UUID name-label="$TGT_NAME" xe vdi-param-set uuid=$VDI_UUID name-label="$TGT_NAME" fi done done

### Bonding + VLAN tagging + Bridge – updated

Wednesday, April 25th, 2012

In the past I hacked around a problem with the order of starting (and with several bugs) a network stack combined of network bonding (teaming) + VLAN tagging, and then with network bridging (aka – Xen bridges). This kind of setup is very useful for introducing VLAN networks to guest VMs. This works well on Xen (community, Server), however, on RHEL/Centos 5 versions, the startup scripts (ifup and ifup-eth) are buggy, and do not handle this operation correctly. It means that, depending on the update release you use, results might vary from “everything works” to “I get bridges without VLANs” to “I get VLANs without bridges”.

I have hacked a solution in the past, modifying /etc/sysconfig/network-scripts/ifup-eth and fixing some bugs in it, however, both maintaining the fix on every release of ‘initscripts’ package has proven, well, not to happen…

So, instead, I present you with a smarter solution, better adept to updates supplied from time to time by RedHat or Centos, using predefined ‘hooks’ in the ifup scripts.

Create the file /sbin/ifup-pre-local with the following contents:

 #!/bin/bash # $1 is the config file #$2 is not interesting # We will start the vlan bonding before any bridge   DIR=/etc/sysconfig/network-scripts   [ -z "$1" ] && exit 0 .$1   if [ "${DEVICE%%[0-9]*}" == "xenbr" ] then for device in$(LANG=C egrep -l "^[[:space:]]*BRIDGE=\"?${DEVICE}\"?" /etc/sysconfig/network-scripts/ifcfg-*) ; do /sbin/ifup$device done fi

You can download this scrpit. Don’t forget to change it to be executable. It will call ifup for any parent device of xenbr* device called at. If the parent device is already up, no harm is done. If the parent device is not up, it will be brought up, and then the xenbr device can start normally.

### Cables connection in Israel for Linux

Thursday, May 14th, 2009

Update to 0.2. Links remain the same. At the moment I cannot host many versions (it’s mostly uncomfortable), but this might change in the future.

I have created a GUI cables installer and configurator for L2TP on Linux.

I have noticed that there is no GUI solution, so, after this has been brought up, I have done it (!!!)

I have uploaded these files here, and you are welcome to use them.

Remember – they are designed for a blank Ubuntu (currently. More distros will be supported in the future, upon request) with not much of junk installed. Also – they are designed for the simple user. Double-click and run. That’s it.

L2TP Cables connection in Israel (and across the world, where relevant) by Ez-Aton

This is an installer and configurator for L2TP over cables in Israel
With some luck, by running this installer, you will be able to connect
to the Internet with a dialer!

The system assumes you have little technical knowledge of Linux and you
are not expected to have any. Follow the defaults, and you should be fine.

This configuration will be cross distro in the future, meaning it will work
to be able to do so, please assist by sending information on systems I am
not familiar with yet, per the appendix at the bottom.
Also, you can feel free to send me info in case the system did not work for
you (and let me know what are the differences from a default installation),
or, as always, send me money.

Visit my technical blog for updates and all kind of other technical stuff, at

http://run.tournament.org.il

OSS work is meant to be based on others work, and that I have done. I would
like to thank (and mention below) the resources for without this would not
have happened.

I hope you enjoy this dialer!

Ez

—How to use
Simply double-click on the “cables” icon on your desktop, and the system will
get you connected.
For CLI utilization: Run /usr/local/bin/cables

—Tools and resources used:
To create this package I have used the following tools and resources
makeself http://megastep.org/makeself/
xl2tpd by http://www.xelerance.com/software/xl2tpd/
xl2tpd guide for Israel Cables http://stuff.pulkes.org/l2tp/
ISP LNS list http://www.cables.org.il/cable-vpn/vpn.html
My connect/disconnect scripts from http://run.tournament.org.il

This package contents are under GNUv2 license, meaning you have full permission
to modify the contents of this package, except for the binary packages included
with it, where you are binded by their respective licenses.

—My Distro/ISP is not supported!
Well, these things happen. Over 300 distros our there, and I can’t have them all.
However – you have your own distro, right? For me to add it to this package
(assuming you don’t want to do this yourself) you will have to supply me with the
following info:
* What distro, kernel and version, and how you get the distro name
(for example – on Redhat – /etc/redhat-release. On Ubuntu – /etc/lsb-release)
* The file containing the version inforamtion (see above)
* The versions available from your repositories of xl2tpd or l2tpd for older
releases, and where you can get them
* All other info you think relevant

—Change log
0.2 – Added ability to enter manual LNS address. Added Orange LNS. Fixed fixroute to allow both IP and hostname without problems. Fixed cables connection script to run fixroute anyhow.
0.1 – Initial release

If you want the scripts and sources (not for the simple user!), you can get there here: l2tp-cables

### HP EVA bug – Snapshot removed through sssu is still there

Friday, May 2nd, 2008

This is an interesting bug I have encountered:

The output of an sssu command should look like this:

EVA> DELETE STORAGE “\Virtual Disks\Linux\oracle\SNAP_ORACLE”

EVA>

It still leaves the snapshot (SNAP_ORACLE in this case) visible, until the web interface is used to press on “Ok”.

This happened to me on HP EVA with HP StorageWorks Command View EVA 7.0 build 17.

When sequential delete command is given, it looks like this:

EVA> DELETE STORAGE “\Virtual Disks\Linux\oracle\SNAP_ORACLE”

Error: Error cannot get object properties. [ Deletion completed]

EVA>

When this command is given for a non-existing snapshot, it looks like this:

EVA> DELETE STORAGE “\Virtual Disks\Linux\oracle\SNAP_ORACLE”

So I run the removal command twice (scripted) on an sssu session without “halt_on_errors”. This removes the snapshots correctly.

### Rotate Beryl/Compiz cube from command line

Tuesday, February 19th, 2008

We are about to have a stand in a show in Israel. To pull some attention, I have searched for a method to automate a random rotation of the famous Beryl/Compiz cube.

An extension of the method provided in here (using macros) is demonstrated below, using a script.

This is a bit more complicated, as I have used the motions of the mouse to achieve a “show” out of it (just changing desktops isn’t enough nowadays…)

Check out below for the full script.

#!/bin/bash
# This script will rotate the cube one click on each direction each predefined
# timer.
# Written by ezaton at tournament.org.il
# Check out my technical blog “Running Systems” at http://www.tournmament.org.il/run

# Set timer (seconds)
TIMER=10
VERT_TIMER=1

# Possible directions? 4 (up, down, left, right. will be marked from 0 to 3)
# Addition – set it to give higher priority to side-stepping. So max is 10, and only 0&1 represent
# up/down
# Added 10 to represent 2xleft and 11 to represent 2xright
POSS=12

# Temp command file
TMP_FILE=/tmp/rotate.macro

function create_file {
# This function will create and secure the macro file
\rm $TMP_FILE if [ -f$TMP_FILE ]; then
echo “$TMP_FILE still exists” exit 1 fi echo “” >$TMP_FILE
chmod 700 $TMP_FILE } function run_macro { # Run the actual macro cat$TMP_FILE | xmacroplay $DISPLAY &>/dev/null } function left { # This function will build the macro file for the “left” command echo >$TMP_FILE
echo “MotionNotify 100 380″ >> $TMP_FILE echo “KeyStrPress Alt_L” >>$TMP_FILE
echo “KeyStrPress Control_L” >> $TMP_FILE echo “ButtonPress 1″ >>$TMP_FILE
echo “MotionNotify 100 380″ >> $TMP_FILE echo “MotionNotify 130 380″ >>$TMP_FILE
echo “MotionNotify 150 380″ >> $TMP_FILE echo “MotionNotify 170 380″ >>$TMP_FILE
echo “MotionNotify 190 380″ >> $TMP_FILE echo “MotionNotify 210 380″ >>$TMP_FILE
echo “MotionNotify 230 380″ >> $TMP_FILE echo “MotionNotify 250 380″ >>$TMP_FILE
echo “MotionNotify 270 380″ >> $TMP_FILE echo “MotionNotify 290 380″ >>$TMP_FILE
echo “MotionNotify 310 380″ >> $TMP_FILE echo “MotionNotify 330 380″ >>$TMP_FILE
echo “ButtonRelease 1″ >> $TMP_FILE echo “KeyStrRelease Control_L” >>$TMP_FILE
echo “KeyStrRelease Alt_L” >> $TMP_FILE run_macro } function up { # This function will build the macro file for the “up” command echo >$TMP_FILE
echo “MotionNotify 100 100″ >> $TMP_FILE echo “KeyStrPress Alt_L” >>$TMP_FILE
echo “KeyStrPress Control_L” >> $TMP_FILE echo “ButtonPress 1″ >>$TMP_FILE
echo “MotionNotify 100 100″ >> $TMP_FILE echo “MotionNotify 100 120″ >>$TMP_FILE
echo “MotionNotify 100 140″ >> $TMP_FILE echo “MotionNotify 100 160″ >>$TMP_FILE
echo “MotionNotify 100 180″ >> $TMP_FILE echo “MotionNotify 100 200″ >>$TMP_FILE
echo “MotionNotify 100 220″ >> $TMP_FILE echo “MotionNotify 100 240″ >>$TMP_FILE
echo “MotionNotify 100 260″ >> $TMP_FILE echo “MotionNotify 100 280″ >>$TMP_FILE
echo “MotionNotify 100 300″ >> $TMP_FILE echo “MotionNotify 100 320″ >>$TMP_FILE
echo “ButtonRelease 1″ >> $TMP_FILE echo “KeyStrRelease Control_L” >>$TMP_FILE
echo “KeyStrRelease Alt_L” >> $TMP_FILE run_macro } function right { # This function will build the macro file for the “right” command echo >$TMP_FILE
echo “MotionNotify 340 380″ >> $TMP_FILE echo “KeyStrPress Alt_L” >>$TMP_FILE
echo “KeyStrPress Control_L” >> $TMP_FILE echo “ButtonPress 1″ >>$TMP_FILE
echo “MotionNotify 340 380″ >> $TMP_FILE echo “MotionNotify 320 380″ >>$TMP_FILE
echo “MotionNotify 300 380″ >> $TMP_FILE echo “MotionNotify 280 380″ >>$TMP_FILE
echo “MotionNotify 260 380″ >> $TMP_FILE echo “MotionNotify 240 380″ >>$TMP_FILE
echo “MotionNotify 220 380″ >> $TMP_FILE echo “MotionNotify 200 380″ >>$TMP_FILE
echo “MotionNotify 180 380″ >> $TMP_FILE echo “MotionNotify 160 380″ >>$TMP_FILE
echo “MotionNotify 140 380″ >> $TMP_FILE echo “MotionNotify 120 380″ >>$TMP_FILE
echo “ButtonRelease 1″ >> $TMP_FILE echo “KeyStrRelease Control_L” >>$TMP_FILE
echo “KeyStrRelease Alt_L” >> $TMP_FILE run_macro } function down { # This function will build the macro file for the “down” command echo >$TMP_FILE
echo “MotionNotify 100 330″ >> $TMP_FILE echo “KeyStrPress Alt_L” >>$TMP_FILE
echo “KeyStrPress Control_L” >> $TMP_FILE echo “ButtonPress 1″ >>$TMP_FILE
echo “MotionNotify 100 330″ >> $TMP_FILE echo “MotionNotify 100 310″ >>$TMP_FILE
echo “MotionNotify 100 290″ >> $TMP_FILE echo “MotionNotify 100 270″ >>$TMP_FILE
echo “MotionNotify 100 250″ >> $TMP_FILE echo “MotionNotify 100 230″ >>$TMP_FILE
echo “MotionNotify 100 210″ >> $TMP_FILE echo “MotionNotify 100 190″ >>$TMP_FILE
echo “MotionNotify 100 170″ >> $TMP_FILE echo “MotionNotify 100 150″ >>$TMP_FILE
echo “MotionNotify 100 130″ >> $TMP_FILE echo “MotionNotify 100 110″ >>$TMP_FILE
echo “ButtonRelease 1″ >> $TMP_FILE echo “KeyStrRelease Control_L” >>$TMP_FILE
echo “KeyStrRelease Alt_L” >> $TMP_FILE run_macro } function fix_vert { # Fixes a case of vertical extention (non-viewable screen) sleep$VERT_TIMER
case “$1″ in 1) down ;; 0) up ;; esac } # Verify we have xmacroplay which xmacroplay if [ "$?" -ne "0" ]; then
echo “Missing xmacroplay. Install it”
echo “Use apt get install xmacro”
exit 1
fi

# Do we use X and have a defined display? Won’t work otherwise…
if [ -z "$DISPLAY" ]; then echo “DISPLAY is not defined. Exiting” exit 1 fi create_file # We start where all is viewable while true; do # Select direction DIRECTION=$RANDOM
let “DIRECTION %= $POSS” # Debug: echo “***$DIRECTION ***”
case “$DIRECTION” in 0) up fix_vert 1 ;; 1) down fix_vert 0 ;; [2-5]) left ;; [6-9]) right ;; 10) left left ;; 11) right right ;; esac sleep$TIMER
done

exit 0

### Quick provisioning of virtual machines

Friday, February 1st, 2008

When one wants to achieve fast provisioning of virtual machines, some solutions might come into account. The one I prefer uses Linux LVM snapshot capabilities to duplicate one working machine into few.

This can happen, of course, only if the host running VMware-Server is Linux.

LVM snapshots have one vast disadvantage – performance. When a block on the source of the snapshot is being changed for the first time, the original block is being replicated to each and every snapshot COOW space. It means that a creation of a 1GB file on a volume having ten snapshots means a total copy of 10GB of data across your disks. You cannot ignore this performance impact.

LVM2 has support for read/write snapshots. I have come up with a nice way of utilizing this capability to my benefit. An R/W snapshot which is being changed does not replicate its changes to any other snapshot. All changes are considered local to this snapshot, and are being maintained only in its COOW space. So adding a 1GB file to a snapshot has zero impact on the rest of the snapshots or volumes.

The idea is quite simple, and it works like this:

1. Create adequate logical volume with a given size (I used 9GB for my own purposes). The name of the LV in my case will be /dev/VGVM3/centos-base

2. Mount this LV on a directory, and create a VM inside it. In my case, it’s in /vmware/centos-base

3. Install the VM as the baseline for all your future VMs. If you might not want Apache on some of them, don’t install it on the baseline.

4. Install vmware-tools on the baseline.

5. Disable the service “kudzu”

6. Update as required

7. In my case I always use DHCP. You can set it to obtain its IP once from a given location, or whatever you feel like.

8. Shut down the VM.

9. In the VM’s .vmx file add a line like this:

uuid.action = “create”

I have added below (expand to read) two scripts which will create the snapshot, mount it and register it, including new MAC and UUID.

Press below for the scripts I have used to create and destroy VMs

create-replica.sh:

#!/bin/sh
# This script will replicate vms from a given (predefined) source to a new system
# Written by Ez-Aton, http://www.tournament.org.il/run
# Arguments: name

# FUNCITONS BE HERE
test_can_do () {
# To be able to snapshot, we need a set of things to happen
if [ -d $DIR/$TARGET ] ; then
echo “Directory already exists. You don’t want to do it…”
exit 1
fi
if [ -f $VG/$TARGET ] ; then
echo “Target snapshot exists”
exit 1
fi
if [ vmrun list | grep -c $DIR/$SRC/$SRC.vmx -gt "0" ] ; then echo “Source VM is still running. Shut it down before proceeding” exit 1 fi if [ vmware-cmd -l | grep -c$DIR/$TARGET/$SRC.vmx -ne "0" ] ; then
echo “VM already registered. Unregister first”
exit 1
fi
}

do_snapshot () {
# Take the snapshot
lvcreate -s -n $TARGET -L$SNAPSIZE $VG/$SRC
RET=$? if [ "$RET" -ne "0" ]; then
echo “Failed to create snapshot”
exit 1
fi
}

mount_snapshot () {
# This function creates the required directories and mounts the snapshot there
mkdir $DIR/$TARGET
mount $VG/$TARGET $DIR/$TARGET
RET=$? if [ "$RET" -ne "0" ]; then
echo “Failed to mount snapshot”
exit 1
fi
}

alter_snap_vmx () {
# This function will alter the name in the VMX and make it the $TARGET name cat$DIR/$TARGET/$SRC.vmx | grep -v “displayName” > $DIR/$TARGET/$TARGET.vmx echo “displayName = \”$TARGET\”" >> $DIR/$TARGET/$TARGET.vmx cat$DIR/$TARGET/$TARGET.vmx > $DIR/$TARGET/$SRC.vmx \rm$DIR/$TARGET/$TARGET.vmx
}

register_vm () {
# This function will register the VM to VMWARE
vmware-cmd -s register $DIR/$TARGET/$SRC.vmx } # MAIN if [ -z "$1" ]; then
echo “Arguments: The target name”
exit 1
fi

# Parameters:
SRC=centos-base         #The name of the source image, and the source dir
PREFIX=centos             #All targets will be created in the name centos-$NAME DIR=/vmware #My VMware VMs default dir SNAPSIZE=6G #My COOW space VG=/dev/VGVM3 #The name of the VG TARGET=”$PREFIX-$1″ test_can_do do_snapshot mount_snapshot alter_snap_vmx register_vm exit 0 remove-replica.sh: #!/bin/sh # This script will remove a snapshot machine # Written by Ez-Aton, http://www.tournament.org.il/run # Arguments: machine name #FUNCTIONS does_it_exist () { # Check if the described VM exists if [ vmware-cmd -l | grep -c$DIR/$TARGET/$SRC.vmx -eq "0" ]; then
echo “No such VM”
exit 1
fi
if [ ! -e $VG/$TARGET ]; then
echo “There is no matching snapshot volume”
exit 1
fi
if [ lvs $VG/$TARGET | awk '{print $5}' | grep -c$SRC -eq "0" ]; then
echo “This is not a snapshot, or a snapshot of the wrong LV”
exit 1
fi
}

# This function verifies that the right thing is actually done
echo “You are about to remove a virtual machine and an LVM. Details:”
echo “Machine name: $TARGET” echo “Logical Volume:$VG/$TARGET” echo -n “Are you sure? (y/N): “ read RES if [ "$RES" != "Y" ]&&[ "$RES" != "y" ]; then echo “Decided not to do it” exit 0 fi echo “” echo “You have asked to remove this machine” echo -n “Again: Are you sure? (y/N): “ read RES if [ "$RES" != "Y" ]&&[ "$RES" != "y" ]; then echo “Decided not to do it” exit 0 fi echo “Removing VM and snapshot” } shut_down_vm () { # Shut down the VM and unregister it vmware-cmd$DIR/$TARGET/$SRC.vmx stop hard
vmware-cmd -s unregister $DIR/$TARGET/$SRC.vmx } remove_snapshot () { # Umount and remove the snapshot umount$DIR/$TARGET RET=$?
if [ "$RET" -ne "0" ]; then echo “Cannot umount$DIR/$TARGET” exit 1 fi lvremove -f$VG/$TARGET RET=$?
if [ "$RET" -ne "0" ]; then echo “Cannot remove snapshot LV” exit 1 fi } remove_dir () { # Removes the mount point rmdir$DIR/$TARGET } #MAIN if [ -z "$1" ]; then
echo “No machine name. Exiting”
exit 1
fi

#PARAMETERS:
DIR=/vmware                #VMware default VMs location
VG=/dev/VGVM3            #The name of the VG

real 0m0.311s
user 0m0.024s
sys 0m0.020s

~$time snmpwalk -c COMMUNITY -v2c Server2 1.3.6.1.4.1.2021.13.15 > /dev/null real 0m8.303s user 0m0.044s sys 0m0.012s Looks like a huge difference. However, I believe it’s currently good enough for me. ### Correction of a small but annoying error Thursday, April 19th, 2007 For some reason (probably a typo) I’ve missed an important character in an example I gave here, but I have just recently fixed it. Anyhow, to clarify this, here is the extended description of the correction. The$IFS Bash system variable defines what is the default separator between strings. Changing it can help when dealing with, for example, file names with spaces in them, variables which should be considered one unit, but are separated by semicolon, etc.

To change the default string separator from "space or tab or new-line" to new-line only. you need to set, in Bash the following parameter:

IFS=\$’\n’