RSS LinkedIn Twitter
June 24th, 2013 By jwd Categories: Uncategorized

When I install CentOS using the XenServer template, it doesn’t allow make any changes to the disk drive configuration. This has two side effects that typically annoy me. First, it calculates an amount of swap space which could be anywhere from 50% to 200% of the amount of RAM you have (I’m not entirely sure of the calculation method it uses, but it takes into account disk size and amount of RAM). Secondly, if I have a drive that’s larger than 32GB, it will partition a chunk of it to be mounted at /home. I like to use separate logical volumes for a lot of things, however, /home is not typically one of them.

I’m going to cover both situations in this post. First let’s change the size of the swap space, and assign that space back to the root logical volume.

Resizing the Swap Drive

1. Turn off swapping.

swapoff -v /dev/VolGroup/lv_swap

2. Reduce the size of the logical volume.

lvm lvreduce /dev/VolGroup/lv_swap -L -8192M

3. Make the new swap space.

mkswap /dev/VolGroup/lv_swap

4. Turn swapping back on.

swapon -va

5. Check to see if your swap partition is the size you wanted it.

cat /proc/swaps
free

Deleting the lv_home Logical Drive.

1. Unmount /home.

umount /home

2. Remove the /home mount from fstab

vi /etc/fstab

3. Remove the line from fstab that looks like:

/dev/mapper/VolGroup-lv_home /home                       ext4    defaults        1 1

4. Remount drives

mount -a

5. Remove the logical drive.

lvremove /dev/VolGroup/lv_home

I usually find that a reboot at this point gives me better results. I’m not sure the cause of it, but on occasion I have not rebooted until the end, and found out I had corrupted my drive by the end.

Resize the lv_root Logical Drive.

1. Extend the logical volume.

lvextend -l +100%FREE /dev/VolGroup/lv_root

2. Resize the volume to use the remaining free space.

resize2fs /dev/VolGroup/lv_root

3. Check that the drive is the size you were expecting.

df -h

June 24th, 2013 By jwd Categories: Uncategorized

Importing a backed up XenServer snapshot is simple, but you’ll need two things to do it. First, you’ll need the UUID of the storage repository that you’ll be importing the snapshot onto. Second, you’ll need the location of the backed up XVA file.

To get the UUID of the storage repository, use the sr-list command.

xe sr-list
uuid ( RO)                : a02a1791-0210-f911-e992-7030aaa6f419
          name-label ( RW): DS3500
    name-description ( RW): Hardware HBA SR [IBM - /dev/sda [sdb]]
                host ( RO): 
                type ( RO): lvmohba
        content-type ( RO):

Then you can use the vm-import command to import the backup.

xe vm-import filename=/backups/media_2013_06_23.xva sr-uuid=a02a1791-0210-f911-e992-7030aaa6f419
June 24th, 2013 By jwd Categories: Uncategorized

I’ve upgraded most of my XenServer instances to version 6.1, so this isn’t as big of a problem as it used to be. With the older version (5.5-ish) there was a problem reclaiming disk space when a snapshot was deleted.

Here are the commands that I use to create and delete snapshots.

xe vm-list

This will output a number of entries that look like the following:

uuid ( RO)           : 24d5bddb-08a7-2462-ce7b-2111dfbc6d0e
     name-label ( RW): media
    power-state ( RO): running

You will want to grab the UUID for use in the next command, which actually creates the snapshot, and outputs the UUID of the snapshot (which you will want to get for further commands)

xe vm-snapshot vm=24d5bddb-08a7-2462-ce7b-2111dfbc6d0e new-name-label=media_2013_06_24
> 0074f681-7c62-5ee7-4971-d8f61a4960ea

I then execute the following commands. First identifying the snapshot as not being a template. And secondly, exporting the snapshot (as an XVA) to the backup location.

xe template-param-set is-a-template=false uuid=0074f681-7c62-5ee7-4971-d8f61a4960ea
xe vm-export vm=0074f681-7c62-5ee7-4971-d8f61a4960ea filename=/backups/media_2013_06_24.xva

Finally, you can delete the snapshot and reclaim the disk space.

xe vm-uninstall uuid=0074f681-7c62-5ee7-4971-d8f61a4960ea force=true
June 22nd, 2013 By jwd Categories: Uncategorized

I can’t believe it’s been over two years since I’ve written a blog post. Simply incredible, particularly because it feels like it’s only been a few months.

There have been a lot of Flex related developments in the industry during that time, the least of which was being hung out to dry by Adobe. I still receive one or two Flex related contract inquiries a month; and I still use it from time to time when I want to crank out a personal app for something or other. However, I can’t (in good conscience) at this point, recommend Flex for an enterprise application that will need to be supported for longer than 2-3 years. Maybe that will change someday, but I won’t be holding my breath in the meantime.

In that time I’ve also started a web hosting company. Shared hosting isn’t really my thing, if you’re looking for shared hosting go to GoDaddy or Bluehost– they can do it for cheaper. I specialize in those whose needs have outgrown what shared hosting can offer. If you think that describes you, then get in touch.

I also still do lots of development, mostly in Java. Some Grails, and I’ve been dabbling in Angular (though it’s painful to work with Javascript after having used the vastly superior ActionScript for many years). Please don’t argue this point– people who have never done any Flex programming really have no idea what they’re talking about when it comes to this.

That said, if I do make time to start writing again (which I really hope to do) expect to see a shift in the things I write about.

March 12th, 2011 By jwd Categories: AIR, Flex 3, Flex 4, Monkey Patching

There are two ways to load content into Flex’s HTML control. The first is to set assign the htmlText property some HTML. The second is to set the location property to the URL of the site you wish to load. The two are mutually exclusive, as described in the ActionScript 3 Reference here.

Speaking specifically of the htmlText property: Setting this property has the side effect of setting the location property to null, and vice versa.

The implication of this is that if you load content via setting the location property, you cannot use the htmlText property to view the source HTML of the site you loaded– it will return null. So the question is, how do we do this?

The trick is to use the DOM. Adobe has a livedoc article here called Accessing DOM and JavaScript objects from ActionScript. The DOM can be accessed from the window.document property of the HTMLLoader. So assume I have declared the following HTML control.

<mx:HTML id="html" location="http://www.googolflex.com" width="100%" height="100%" />

I could then view the loaded contents in a variety of ways. The HTMLLoader I mentioned previously is a member of the HTML control, and is conveniently named htmlLoader.

var o : Object = html.htmlLoader.window.document.getElementsByTagName("html")[0];
trace( o.textContent );
trace( o.innerText );
trace( o.innerHTML );

All of these methods return Strings, and so could be assigned or output as needed. I found the livedocs article to be pretty illuminating, it essentially gives you a lot of the power of JavaScript using ActionScript in manipulating the HTML that you’ve loaded. You could do some pretty cool stuff with that.

March 11th, 2011 By jwd Categories: Blogging

I’ve been looking for a fluid width WordPress theme for some time. I’ve finally found one that I like. I hope it makes my code examples easier to read. In case you’re wondering, I’m using the “Deep Mix” theme by SthElse, with a few customizations.

For syntax highlighting, I’ve switched to WP-SynHighlight.

Thanks for reading!

March 10th, 2011 By jwd Categories: Actionscript, Flex 3, Flex 4

Here’s a pair of methods I use to generate random usernames for an online test. They could be used to generate random strings for any purpose, though.

len is the length of the random string you want returned.

WP-SYNHIGHLIGHT PLUGIN: NOTHING TO HIGHLIGHT! PLEASE READ README.TXT IN PLUGIN FOLDER!
public static function generateRandomString(len:uint = 1, userAlphabet:String = "abcdefghijklmnopqrstuvwxyz"):String{
var alphabet:Array = userAlphabet.split("");
var alphabetLength:int = alphabet.length;
var randomLetters:String = "";
for (var i:uint = 0; i < len; i++){ randomLetters += alphabet[int(Math.floor(Math.random() * alphabetLength))]; } return randomLetters; } [/codesyntax] [codesyntax lang="actionscript3" tab_width="2" blockstate="expanded"] public static function generateRandomNumber(len:uint = 1, userAlphabet:String = "0123456789"):String{ var alphabet:Array = userAlphabet.split(""); var alphabetLength:int = alphabet.length; var randomNumbers:String = ""; for (var i:uint = 0; i < len; i++){ randomLetters += alphabet[int(Math.floor(Math.random() * alphabetLength))]; } return randomNumbers; } [/codesyntax] There's no need to point out that the generateRandomNumber method could be optimized. I know. My specific use case called for a string of 4 characters followed by a 3 digit number. If you weren't restricted to that, there's no reason you couldn't combine the two 'alphabets'. In fact, you could include other characters if you were trying to generate a strong password.

March 10th, 2011 By jwd Categories: AIR, File I/O, Flex 3, Flex 4

I love being able to drag-and-drop files or directories onto my AIR apps and have it recursively process all of the files in the directory structure. This is handy for batch file processing utilities, which seems to be a common theme in many of my AIR apps.

The first thing you need to do is set up your AIR app to handle the drag and drop operation. This is pretty straightforward, and is done by setting up listeners for the NATIVE_DRAG_ENTER and NATIVE_DRAGE_DROP events.

private function onCreationComplete() : void {
	this.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onDragIn);
	this.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onDrop);
}

The code that handles the NATIVE_DRAG_ENTER event needs to determine whether or not to accept the dragged items. In this method you see it essentially accepting anything that is dragged into the app. This does not have to be the case, if you prefer to be more selective about the files that are dragged in.

There are several method the more discriminating users may want to call on the event. The event has a clipboard object, with a number of methods. getFormat(), and getData() are the two most useful in my opinion. You can use these to check for a particular filename or extension, etc.

private function onDragIn(event : NativeDragEvent) : void {
	NativeDragManager.acceptDragDrop(this);
}

You see I use the clipboard.getData() method to retrieve the list of dropped files. If you’re only interested in drag-and-drop, you can stop here. The method processDroppedFiles, which accepts an array, is the method that starts the actual processing.

private function onDrop(event : NativeDragEvent) : void {
	try {
		var dropfiles:Array = event.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
		processDroppedFiles(dropfiles);
	}
	catch (error : IOError) {
		trace("Error during drag-and-drop procedure.");
	}
}

A file in my files array can either be a file, in which case I want to do my processing. That’s what the method processFile() does, which I will not detail in this post, because it does whatever work your application is doing with the files.

The file could also be a directory, in which case, I want to call processDirectory(). isDirectory is a property on the file that is true if the file is a directory, and false if… well, duh.

private function processDroppedFiles(files : Array) : void {
	for each (var file:File in files){
		if (file.isDirectory)
			processDirectory(file);
		else
			processFile(file);
	}
}

I could have skipped out on this method if I wanted to… I’m adding it to make it a little more clear what’s happening. The method processDroppedFiles() needs an Array of Files, whereas processDirectory() accepts a File. At this point, we know that the file in question is a directory, so we call the getDirectoryListing() method which returns an Array, and we recurse to processDroppedFiles().

private function processDirectory(dir : File) : void {
	processDroppedFiles(dir.getDirectoryListing());
}

As I pointed out, you could just as easily have included the recursive call in the processDroppedFiles() method itself, like this:

private function processDroppedFiles(files : Array) : void {
	for each (var file:File in files){
		if (file.isDirectory)
			processDroppedFiles(file.getDirectoryListing());
		else
			processFile(file);
	}
}

And this will traverse the directory tree, eventually calling processFile() on each file in the tree.

March 10th, 2011 By jwd Categories: AIR, File I/O, Flex 4, Flex Components

Reading and writing files is something I do in almost every AIR application I write. It has become my language of choice to write batch file processing applications in. Here are a few of the methods I use most commonly when reading and writing files. These are divided into two main groups, binary files and text files. I’ll save reading and writing objects for another post.

When I’m reading a binary file, I usually want it as a ByteArray. You should explore some of the readXXX and writeXXX methods on the FileStream class to see what you can read and write. The functions that process binary files versus the files that process text files are almost identical except for which read/write method is called.

The method readBytes accepts the ByteArray to fill as its parameter.

public static function readBinaryFile(file : File) : ByteArray {
	var stream : FileStream = new FileStream();
	stream.open(file, FileMode.READ);
	var fileBytes : ByteArray = new ByteArray();
	stream.readBytes(fileBytes);
	return fileBytes;
 }

Here is how I write a binary file, whose name is passed in as a parameter. Notice in the previous code snippet I accepted a File reference as a parameter (the processing of the name had been done previously). That process is similar to what you see here: there are a number of objects on the File class that allow you to get a directory. In this case I’m using documentsDirectory, calling resolvePath gets you the pathname you need to create the file reference.

public static function writeBinaryFile(name : String, array : ByteArray) : String {
	try {
		var f : File = File.documentsDirectory.resolvePath(name);
		var fs : FileStream = new FileStream();
		fs.open(f, FileMode.WRITE);
		fs.writeBytes(array);
		fs.close();
		return f.nativePath + " written.";
	}
	catch (err : Error) {
		return err.name;
	}
		return "Error writing file.";
}

Here is the method I use to read text files. Notice that it’s similar to reading a binary file. The readUTFBytes is a handy function if you know you’re dealing with text/strings.

public static function readTextFile(file : File) : String {
	var stream : FileStream = new FileStream();
	stream.open(file, FileMode.READ);
	var s : String = stream.readUTFBytes(stream.bytesAvailable);
	return s;
}

Here is the method I use to create a new text file and write to it.

public static function writeTextFile(name : String, text : String) : String {
	try {
		var f : File = File.documentsDirectory.resolvePath(name);
		var fs : FileStream = new FileStream();
		fs.open(f, FileMode.WRITE);
		fs.writeUTFBytes(text);
		fs.close();
		return f.nativePath + " written.";
	}
	catch (err : Error) {
		return err.name;
	}
	return "Error writing file.";
}

This method is a little different than my other write methods. Normally when I write to a file, I go based off the name. This method is for the use cases where I already have a file reference and need to write to it.

Something else to pay attention to are the FileModes. There are a number of modes to open and/or read a file: APPEND, READ, UPDATE, and WRITE.

public static function writeTextFileFile(file : File, text : String) : String {
	try {
		var fs : FileStream = new FileStream();
		fs.open(file, FileMode.WRITE);
		fs.writeUTFBytes(text);
		fs.close();
		return file.nativePath + " written.";
	}
	catch (err : Error) {
		return err.name;
	}
	return "Error writing file.";
}
March 3rd, 2011 By jwd Categories: Databases, MySQL

I was recently given this problem to work out for a MySQL database. Given a table with the following structure and sample data:

TABLE_1
------------------------------------------------
ID_PK  |  num_1   |  num_2  |  num_3  |
------------------------------------------------
34		200		300	    700
23		400		100	    500
56		100		600	    200
39		900		700	    300

Compute and insert the ranking of that data into a separate table, ranking being defined to roughly mean the row with the highest value for num_1 would be assigned rank 1, and so on. with the expected output as follows:

TABLE_2
-------------------------------------------------------------------------
ID_PK     |  FK_ID  |  Rank_Number_1  |  Rank_Number_2 | Rank_Number_3  |
-------------------------------------------------------------------------
122		34		3		3		1
123		23		2		4		2
124		56		4		2		4
125		39		1		1		3

This can be accomplished using nested select statements, where the rank is computed and added to the results as you pop out of the nesting.

SET @r1=0;
SET @r2=0;
SET @r3=0;
SELECT ID_PK, ranking1, ranking2, ranking3
FROM
(
  SELECT *, @r3:=@r3+1 ranking3
    FROM
    (
	  SELECT *,@r2:=@r2+1 ranking2
	  FROM
	  (
	  SELECT ID_PK, @r1:=@r1+1 ranking1, number_1, number_2, number_3
	  FROM TABLE_1
	  ORDER BY number_1 DESC
	  ) q1
	  ORDER BY number_2 DESC
    ) q2
    ORDER BY number_3 DESC
) q3
ORDER BY ID_PK;

If you wanted to rank more columns, you would need further nesting but following the same pattern allows you to compute as many columns as you want. Back to the original problem, to get these results into TABLE_2, I just preceded the previous SELECT statement with:

INSERT INTO TABLE_2 (FK_ID, Rank_Number_1, Rank_Number_2, Rank_Number_3)
If you’re going to be applying this over a variable number of columns it might make more sense to use a temporary table. In this way you will be able to iterate over each column you want to apply the ranking too, and gradually fill in your TABLE_2.

First create the temporary table:

CREATE TEMPORARY TABLE TABLE_A LIKE TABLE_2;

Then for each column you are going to rank across, run the following query. You would substitute Rank_Number_N, and num_N as needed.

SET @rank=0;
INSERT TABLE_A (Rank_Number_1, FK_ID)
(SELECT @rank:=@rank=1 AS rank, FK_ID
FROM TABLE_1 ORDER BY num_1 DESC

Then insert the contents of the temporary table into TABLE_2.

INSERT INTO TABLE_2 (FK_ID, Rank_Number_1, Rank_Number_2, Rank_Number_3)
	SELECT FK_ID, max(Rank_Number_1), max(Rank_Number_2), max(Rank_Number_3)
	FROM TABLE_A
	GROUP BY FK_ID;

Finally, drop your temporary table.

DROP TEMPORARY TABLE TABLE_A;