Subscribe in a reader

Recent Articles:

Creating a Basic jQuery Slideshow

Thursday, April 01, 2010 1:39:51 PM

I needed a basic slideshow/image rotator with just the features I wanted and nothing more, and I needed a good excuse to play with jQuery. Here's the result of satisfying both of those needs. Look forward to another article that will expand upon this basic slideshow with pausing, text and navigation controls. Until then, enjoy.

To get started we need to create a basic html file, we’ll call it SlideShow.html Here’s how it should look.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>jQuery Slideshow Demo</title>
<meta name="Author" content="Ryan T. Hilton" />
</head>
<body>
</body>
</html>

Add jQuery from Google by placing this in your <head> section

	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>

We also need to add some styles to the page. Create a file called style.css and add this to the <head> section.

	<link rel="stylesheet" type="text/css" href="style.css" />

And now we’ll add some basic styles to style.css

	body
	{
		background-color: rgb(45,45,45);
	}
	.Container
	{
		width: 500px;
		margin: 20px auto;
		border: 4px solid rgb(255,255,255);
		background-color: rgb(200,200,200);
		color: rgb(45,45,45);
		position: relative;
		height: 309px;
	}
	#slideshow
	{
		margin: 0;
		padding: 0;
		list-style: none;
		position: relative;
	}
	#slideshow li
	{
		float: left;
		position: absolute;
	}

Now, let’s setup the HTML for the slideshow. Add this between the <body> and </body> tags. Just add an extra <li> for each image.

	<div class="Container">
		<ul id="slideshow">
			<li>
				<img src="Images/Image1.jpg" width="500" height="309" />
			</li>
			<li>
				<img src="Images/Image2.jpg" width="500" height="309" />
			</li>
			<li>
				<img src="Images/Image3.jpg" width="500" height="309" />
			</li>
		</ul>
	</div>

Let’s create a new .js file and add it to the <head> section now as well. We’ll call it SlideShow.js and place it in the same directory as the html file

	<script type="text/javascript" src="SlideShow.js"></script>

Now, we can start with the basics of the JavaScript. The rest of this will be done in SlideShow.js
First lets set up some variables to configure the behavior.

	var fadeTime = 1200; // Fade in/out each image over 1.2 seconds
	var pauseTime = 4000; // Pause on each image for 4 seconds

Now we need to setup a few internal variables that we’ll use to maintain the state of the slideshow.

	var current = 0; // Current index
	var cur; // Stores current item
	var next; // Stores next item
	var timer; // Used for starting/stopping the rotation
	var size = 0; // Stores the number of items to rotate
	var paused = false; // Are we paused?

How about we get started making things work? We’ll start by creating an initialization function. This will get everything setup and start the rotation.

	function StartSlideShow()
	{
		// Hide all of the items
		$('ul#slideshow > *').css("display", "none")
		.css("left", "0")
		.css("top", "0")
		.css("position", "absolute");

		// Display the first item
		$('ul#slideshow li:first').css("display", "block")
		.css("left", "0")
		.css("top", "0")
		.css("position", "absolute");

		// Set size variable to number of items
		size = $('ul#slideshow li').size();
		
		// Start the rotation timer
		StartTimer();
	}

	function StartTimer()
	{
		// Make sure we have a clean slate
		clearInterval(interval);
		// Call Switch() every x milliseconds
		interval = setInterval(‘Switch()', pauseTime);
	}

	function Switch()
	{	
		cur = ($(‘ul#slideshow li’).eq(current));
		// Check to see if we are at the end of the list
		if ((current + 1) == size)
		{
			next = ($('ul#slideshow li').eq(0));
			current = 0;
		}
		else
		{
			next = ($('ul#slideshow li').eq(current + 1));
			current = current + 1;
		}
		// Fade between the images
		cur.fadeOut(fadeTime);
		next.fadeIn(fadeTime);
	}

Ok, we are all done with SlideShow.js for now. Let’s go back to our SlideShow.html file. In the head section, add the following. This will tell jQuery to run StartSlideShow() when the page is loaded.

<script type=”text/javascript”>
<!--
	$(document).ready(function() {
		StartSlideShow();
	});
-->
</script>

You should now be able to run your slideshow by opening the HTML file in your favorite browser.

View Demo
Download


MAS90 ODBC Tool

Friday, February 26, 2010 1:10:26 PM

I've been expanding my support offerings to an old client as of late into the realm of Sage Software's MAS90. It started off with a redesign of their website and the addition of customer data needing to be uploaded regularly to the web site's database from their internal MAS90 installation.

They are currently run MAS90 using the ProviderX ODBC connections. I haven't worked out all of the nomenclature and inner workings yet, so forgive me if I misspeak here.

Not knowing anything about MAS90 when I started I had to leap over a few hurdles to get .Net applications to talk to the database, run queries and tell me the schema of the tables. There may be an easier way of doing this but I was unable to find one, so I broke out some .Net ODBC tutorials, what a mess.

After constantly rewriting and recompiling my test applications to test the different queries I finally got sick of it and wrote a command line interface (CLI) for interacting with the MAS90 database.

Once configured it allows you to throw ad-hoc queries at the database and, if you choose, see the schema. It is a very rough cut but I hope that it helps somebody else out. If not, it has done and will continue to do what I need it to do.

You can download the application here

From the readme:

This is the initial release of my MAS90 CLI. This was developed for testing queries against MAS90 for a customer so that I could learn the internal structure of the MAS90 database tables and extract sample data from them.

To use:
   Open MAS90CLI.exe.config and setup your connection string, such as server name and path to your ProviderX libraries

You can also set default values in the configuration file

run MAS90CLI.exe it should connect successfully and put you at a MAS90> prompt.

Setting Variables:
   Syntax:
      set [variable] = [value]
      set showschema = true
      set recordlimit = 30
   Variables:
      timeout - timeout in seconds for the query to run (0+)
      recordlimit - number of records to return before quitting (1+, 0 = all)
      showschema - prints out the schema for debugging purposes (true/false)

Viewing Variables:
   Syntax:
      get [variable]
      get showschema
   Variables:
      timeout
      recordlimit
      showschema

Running Queries: At the MAS90> prompt type in your SQL query. You may need to reconfigure your console window to fit the data

MAS90> SELECT * FROM CUS_Invoices ORDER BY InvoiceDate DESC

Queries use the standard SQL syntax as allowable via the ProviderX ODBC provider.

You should start seeing the data appear followed by the number of records found and the time the query took to run

Quitting:
   From the MAS90> prompt type in 'quit' or 'exit'

If you have any questions or problems feel free to contact me:

Ryan T. Hilton
Pacific NW Data Consultants
Ryan@pnwdc.com
http://PNWDC.com - http://RTHilton.com


Virus and Malware Cleanup and Protection

Tuesday, September 22, 2009 11:54:13 AM

I have been asked many times recently to clean up computers that have been infected with various Trojans, Virii, Worms, etc. They all end about the same way, me making a house call.

Now, while I am usually willing to help people sometimes it just makes sense for people to help themselves some. Hopefully this will help you.

Recommended Software
I recommend AVG for day to day anti-virus software. They seem to stay up to date with signatures, it is free for home use and usually takes care of most of the threats I've come across.

For cleanup I also recommend MalwareByte's Anti-Malware. I have found it to clean up several trojans that nothing else seems to touch

Cleanup Instructions
One common theme I have found with removal of malware is that it needs to be performed in a special bootup mode called Safe Mode. Safe Mode allows Windows to load a minimal amount of resources and limit what starts up automatically, including most pieces of malware and their self-defense mechanisms. To boot into Safe Mode follow these steps:
  1. Shutdown Your Computer
  2. Press the power button on your computer
  3. You should now see the BIOS loading, this usually has your computer manufacturers logo, hardware information or both.
  4. Press F8 repeatedly until you are prompted for how you would like to start Windows - If you see the Windows loading screen then you will need to try again.
  5. From the list select 'Safe Mode with Networking'
  6. You should now see many lines of text scroll across your screen rapidly followed by Windows starting up and telling you that you are in Safe Mode

From Safe Mode, download and install both products. These should be installed immediately after you receive a new computer but since you are reading this article chances are that wasn't done. Not to worry, we will do this now.

After you have installed both products first run AVG, have it update the virus definitions from the internet and then run a full scan. Any items discovered should be selected and deleted.

Now, repeat the above step for MalwareByte's Anti-Malware. This product is key for removal of some items such as 'AntiVirus 2009' and 'VirusShield 2009'. To date this is the only program that I have found to affectively remove these programs.

Your computer should hopefully be clean now and running much faster. For some added benefit you should also defragment your C: drive.

If you have any other ideas, feel free to share them in the comments.

Custom Thread Manager In C#

Friday, June 19, 2009 12:59:31 PM

I was recently working on a project that required me to limit the number of background threads that could be executing at any given time. It could be my lack of knowledge of how the built in .Net classes work for managing thread execution, but I decided to embark on a quest to work up my own solution to this problem. The below code was hastily written to solve the problem at hand, but you should be able to see the underlying concept I used. Bare with me.

First, I needed some thread parameters so that I could pass these to my scanner. Here is the class that I created to handle this. Name is the name of the Domain Controller we are going to query.

public class ThreadParams
{
    public string BaseOU = "";
    public string Name = "";
    public ThreadParams(string baseOU, string name)
    {
        BaseOU = baseOU;
        Name = name;
    }
}    

Next I created a class that acted as the shell of my scanner, called LastLogonScanner.

public class LastLogonScanner
{
    private System.DirectoryServices.ActiveDirectory.DomainControllerCollection _domainControllers;
    public System.DirectoryServices.ActiveDirectory.DomainControllerCollection DomainControllers
    {
        get
        {
            return _domainControllers;
        }
        set
        {
            _domainControllers = value;
        }
    }
    private System.DirectoryServices.ActiveDirectory.Domain _domain;
    public System.DirectoryServices.ActiveDirectory.Domain Domain
    {
        get
        {
            return _domain;
        }
        set
        {
            _domain = value;
        }
    }
    public LastLogonScanner()
    {
        _domain = GetDomain();
        EventLogger(String.Format("Enumerating DCs for domain {0}", _domain.Name));
        _domainControllers = GetDCList(_domain);
        EventLogger(String.Format("Found {0} DCs", _domainControllers.Count.ToString()));
    }
    public System.DirectoryServices.ActiveDirectory.Domain GetDomain()
    {
        return System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain();
    }
    public System.DirectoryServices.ActiveDirectory.DomainControllerCollection GetDCList(System.DirectoryServices.ActiveDirectory.Domain domain)
    {
        return domain.DomainControllers;
    }
}

Now, here is where the real meat of it lies. For each DC it will create a thread. Then it will monitor each thread to see if it has started, if it has not been started and there are less than 5 running threads, start up a new thread and pass the base OU and the Domain Controller you wish to scan to it.


public void GetLastLogonTimes(string baseOU)
{
    List<Thread> threads = new List<Thread>();
    foreach (System.DirectoryServices.ActiveDirectory.DomainController dc in _domainControllers)
    {
        Thread t = new Thread(new ParameterizedThreadStart(GetUsersForDC));
        t.Name = dc.Name;
        threads.Add(t);
    }
    while (true)
    {
        bool threadsActive = false;
        int runningCount = 0;
        int pendingCount = 0;
        foreach (Thread t in threads)
        {
            if (t.IsAlive)
            {
                runningCount += 1;
            }
            if (t.ThreadState == ThreadState.Unstarted)
            {
                pendingCount += 1;
            }
        }
        if (runningCount < 5)
        {
            if (pendingCount > 0)
            {
                foreach (Thread t in threads)
                {
                    if (t.ThreadState == ThreadState.Unstarted)
                    {
                        ThreadParams tp = new ThreadParams(baseOU, t.Name);
                        t.Start((object)tp);
                        break;
                    }
                }
            }
        }
        foreach (Thread t in threads)
        {
            if (t.IsAlive)
            {
                threadsActive = true;
                break;
            }
        }
        if (!threadsActive)
        {
            break;
        }
    }
}

A bit of a kludge, I know, but it was very affective.

You can download the full program here.


Server 2003 Pre-R2 Domain-Wide Last Login Scanner

Friday, June 19, 2009 12:13:49 PM

Looking for inactive accounts in your domain? The solution might be more difficult than you think, but I have the answer. I was recently tasked with providing a means to scan a domain of  almost 100 domain controllers (DC) to see which accounts were 30, 60 and 90 days without a login. Because we were not yet at a full 2003 R2 domain functionality level we could not take advantage of the LastLogon attribute directly.

For those who aren't aware, prior to Windows Server 2003 R2 the LastLogon schema attribute was not a replicated field. The solution that we came up with would be to connect to each DC in the domain directly, enumerate the list of users, retrieve their last logon time and then perform a comparison to find the most recent entry across all DCs. Here is a run down of the logic:

  1. Connect to local DC
  2. Enumerate list of all DCs in the domain
  3. Close connection
  4. For each DC discovered in step 2, create a connection to it
  5. Query all users and their LastLogon attribute
  6. Create array and store data from step 5 into it
  7. Close connection
  8. Compare arrays and find the most recent login for each user
  9. Output data to CSV file
  10. Manually analyze the data or write another program to do same

Pretty basic, though there were some pitfalls that I encountered. One of them was I did not want to hammer the network, so I needed to limit my number of simultaneous connections. We decided to only connect to 5 DCs at a time.

You can download the code and take it for a spin here.


DFS-R Fails To Replicate Some Files

Tuesday, October 21, 2008 1:14:44 PM

DFS-R fails to differentiate “long” filenames with filenames that appear as 8.3 short names. While this is rarely an issue, and even less so as our older files become obsolete, I have seen it happen in a couple situations. For example, take these two files:

   Notification Procedures.doc
   Notifi~1.doc

To reproduce this fully, create a new folder on a replicated volume. Create a new document with the first filename and wait for it to replicate. Now, from the command prompt, type in ‘dir /x <Source Directory>’ and note the 8.3 filename is “NOTIFI~1.DOC”. Type in the same command but use the destination directory this time. You will note that it, too, has the same 8.3 filename. Next we need to create the second file. Create a new document and save it as “Notifi~1.doc”. You should not see this file replicate. If you now run the ‘dir /x’ command for the two directories you will see that the first document on the source has been changed to “NOTIFI~2.DOC” however on the destination this change has not been made. DFS will note that the file exists in both locations and no replication will occur.

Again, this isn’t an issue unless you have a large collection of legacy filenames especially those that only maintained their short name.

One workaround that I have found, which will work after the fact, is to disable 8.3 filename creation on your NTFS volumes. You can do this on Server 2003, XP and Vista using the command ‘fsutil.exe behavior set disable8dot3 1’. For Windows NT and Windows 2000 you need to make a registry change. For more information on this change you can reference this Microsoft article.


Server 2003 R2 DFS-R Replication Issues

Monday, October 20, 2008 3:21:50 PM

I have been heavily investing in Domain DFS (Distributed File System) to help simplify our storage management. Microsoft's DFS really helps, providing the ability to store static files at each branch office and replicate changes to the local site using DFS-R (DFS Replication), that way the user will have quick access to them regardless of where they are. Another benefit of this combination is that you can create a warm failover by replicating the data throughout the day to your main site and then setting it as the primary DFS path if the branch server fails.

While implementing this solution and setting up my replication I came across some pretty big issues, such as file counts between the source and destination being off by thousands (more on the source side). After visiting forum after forum I have finally learned that this was due to the files having the Temporary NTFS attribute set on them, which DFS-R will not schedule for replication. In looking at a large sampling of the files using fsutil (fsutil usn readdata <Filename>)they all contained this attribute as noted by the attribute being greater than 0x100 (The Temporary attribute). For example, 0x180, 0x120, etc. Most of these files were PDF files. I have not found any documentation yet regarding why the PDF files might have this attribute set, but I am going to keep looking.

To solve this problem I created a C# project that would scan the filesystem looking for these files and removing this attribute. A bit of a brute force approach, but there aren't very many legitimate uses for this in our environment. The source of the code is here.


Enterprise Backup Vendors - How Much Should They Bend Over for Business?

Friday, October 10, 2008 8:35:14 PM

I am currently working on a project that has been spanning almost a year trying to find an Enterprise-Ready backup solution that will work within our organization as well as serve as an example for other, similar organizations. I believe I have found the solution, but politics on both ends have managed to squeeze themselves into the mix.

I have currently been entertaining CommVault. It has an excellent reputation, has been around a while, supports all of the features we require (Backup, CDP, Archiving, DR, CDP, eDiscovery, etc.) Now, for my little chunk of the network alone this product will cost around $50k to implement after all is said and done. For our organization we would be looking at upper six figures, possibly seven.

Now, here is where the politicians come in to play. We have already purchased some of their CDP components and are using them in production. We wanted to evaluate the rest of their backup line in our environment to supplant our existing ArcServe configuration, because it is far from being on par with our needs. CommVault was able to come to the table with two options, try it for 30 days or lease to own for a year. What we were looking for was a 90-180 day window where we could install the software, get it stable and running and proven, document the process and invite the other stakeholders in for a Q&A session with demo. At this point a decision would be made by all involved to either move forward with a purchase or to find another product.

Another political issue with this is how the project will be funded. If we were to move forward with this project on the lease method there is no guarantee that six months into the lease we will have the budget available to continue. On the other hand if we were to just purchase the trial part of the project independently of our headquarters then we would not be reimbursed when they purchase for themselves or for all of the other groups, taking a hefty chunk out of our operating budget.

We won't know for certain what product will be a clear winner until we have it up and running and proven with production data in a production environment, as that is when things always tend to break. A clear winner may not be known based on whether the product is capable of doing the job or not, but whether the rest of the team agrees that it is the product that they want to be supporting for the next 5-10 years. A standardized Enterprise-wide system like this isn't a small undertaking and there are many things to consider.

So, are we demanding too much or are we justified in our asking for an extended evaluation period of roughly $50k worth of software? Or, should we just fork over the money, hope the product works and move on? Should I forget about a product that I consider best of breed from what I've seen and turn to products that I could care less for, such as Symatec's product line that now has "Replication 80% integrated into the interface" thanks to their acquisitions?

I have lost most of my momentum on this project because of these issues and I am struggling to keep focused on it to come up with a solution that will move us away from the nightmare that is Computer Associates and not present us with new nightmares. I would really love to be able to sleep at night knowing that my backups are working and that I will be able to recover from them, no matter how bad the crash may be.

VMWare ESXi 3.5 Virtual Machine Migration

Friday, October 10, 2008 6:38:21 PM

I recently had the joy of installing the latest free release from VMWare, ESXi 3.5. I must say it has been quite a pleasure so far. I am now up to my third installation of ESXi and can't wait to load up a few more hosts. Here are the two setups that I am currently running for ESXi:

Dell 2850
2 GB RAM
Local RAID Array for VM Storage
1 Guest Operating System (Server 2003)

Dell 2850
4 GB RAM
Local RAID Array for ISO Storage
XioTech Magnitude 3D 3000 Fibre Channel SAN for VM Storage
9 Guest Operating Systems (Server 2003, Debian Etch)

Dell 2850
4 GB RAM
Local RAID Array for ISO Storage
XioTech Magnitude 3D 3000 Fibre Channel SAN for VM Storage
1 Guest Operating Systems (Server 2003)

The first system was loaded this way to add an additional Debian installation side by side with the Server 2003 installation and to improve some reliability issues we were seeing on the Server 2003 loaded bare metal.

The second and third system were originally Server 2003 R2 hosts with VMWare Server loaded hosting the 10 currently running Virtual Machines. These were running OK, but having to patch the host OS was a bit of a pain and we were pretty much at capacity. This is why we decided to move to ESXi. It would allow us a few more VMs with current hardware with the ability to add more RAM to the systems to scale them out further. Once we have a bit more money we will be investing in licensing for performing VMotion, but that is still a few months out, I will just be happy when I can get Virtual Center loaded and start performing Virtual Consolidate Backup (VCB) based backups.

Running pretty much all of the original VMs on a single ESXi host has proven to be almost a perfect fit. CPU utilization was very low, though memory was creeping up to about 3.5 GB used. One of our guests, running Microsoft SCCM, was complaining today about being low on Virtual Memory, so we're going to amp up the memory allocation to help it out so it isn't paging all that data.

Now, for what you came here for, moving the Virtual Machines. Since the last two hosts are both connected to the Fibre Channel XioTech SAN I configured a single volume for hosting the VMs on which they would share, thanks to VMFS-3. The first step is to shutdown the VMs that you want to move. After each VM has been shutdown right click on the VM in the Virtual Infrastructure Client (VI Client) and select "Remove From Inventory." This will ensure that it won't be accidentally started back up on this host. Now, from your destination server select the Configuration tab in the VI Client and navigate to the Storage option. Find the Datastore that contains your VM files and right click and choose "Browse Datastore." Now you must locate the VM Configuration file (*.vmx) for each VM that you wish to move. Right click this file and select "Add To Inventory." You should now be able to right click each host and power them on. You will be prompted that the VM has been moved and asked to Create or Keep the identifier, if you do not see this and your VM does not start, click the Summary tab and the question will be there. I chose to keep the identifier as this machine was "moved." Your VM should now completely start.

You will want to make sure that your network configuration is the same on both ESXi hosts or you may not have network connectivity when you start the VM from its new location.

Hope this helps, happy virtualizing.

Warning: Windows customization resources were not found on this server

Thursday, January 31, 2008 10:57:52 AM

I just got VMWare ESX 3.5 loaded up and I went to create a new Virtual Machine from a template.  I wanted to customize this VM, but the customization options were not available and it reported "Warning: Windows customization resources were not found on this server".  Googling around the web, and reading the VMWare documentation, everything said to extract SysPrep 1.1 into the “C:\Documents and Settings\All Users\Application Data\VMware\VMware VirtualCenter\sysprep\1.1” folder.  I did this, no luck.  I tried to extract the contents of the tools folder only into the root of this directory, no luck.  I tried extracting them to the “\sysprep\xp” folder, no luck.

After a bit of pondering and gluing some hair back onto my scalp I thought.  Why not just grab them from the XP CD and see if that works.  I also remembered that there were some significant changes to SysPrep for Windows XP.

Here’s how I fixed it:
Copy the contents of “XP CD\SUPPORT\TOOLS\DEPLOY.CAB” into the folder “C:\Documents and Settings\All Users\Application Data\VMware\VMware VirtualCenter\sysprep\xp” on your Virtual Center server.  Everything should be working now.

Good luck!


Past Articles: