Friday, December 19, 2008

Zenoss 2.3.2 LDAP authentication with Ubuntu 8.04 and the stack installer

I was able to get the Active Directory authentication module loaded for our Ubuntu Server 8.04 stack installer-based Zenoss 2.3.2 installation. There is a bit of confusion about how to do this, as the wiki instructions for setup assume you are using the RPM-based installer or have installed from source. This turned out to not be too difficult given that the Ubuntu 8.04 distribution comes with the python-ldap package. In summary, you need to link in the distribution's installed python-ldap components into the site packages path for Zenoss's local Python 2.4 runtime and compile them. Here are the steps (these assume you have already downloaded and placed the LDAPUserFolder and LDAPMultiPlugins packages in the path identified in the wiki instructions):

Install python-ldap
(As root)
aptitude install python-ldap
Link python-ldap components to Zenoss's site packages path
We need the _ldap.so binary compiled against Python 2.4 and the source files. As the zenoss user:
#The Zenoss local Python site package path is $ZENHOME/lib/python!
cd $ZENHOME/lib/python
mkdir ldap
mkdir ldap/schema
ln -s /usr/share/pyshared/ldif.py
ln -s /usr/share/pyshared/ldapurl.py
ln -s /usr/lib/python2.4/site-packages/_ldap.so
cd ldap
ln -s /usr/share/pyshared/ldap/async.py
ln -s /usr/share/pyshared/ldap/controls.py
ln -s /usr/share/pyshared/ldap/filter.py
ln -s /usr/share/pyshared/ldap/__init__.py
ln -s /usr/share/pyshared/ldap/modlist.py
ln -s /usr/share/pyshared/ldap/cidict.py
ln -s /usr/share/pyshared/ldap/dn.py
ln -s /usr/share/pyshared/ldap/functions.py
ln -s /usr/share/pyshared/ldap/ldapobject.py
ln -s /usr/share/pyshared/ldap/sasl.py
cd schema
ln -s /usr/share/pyshared/ldap/schema/__init__.py
ln -s /usr/share/pyshared/ldap/schema/models.py
ln -s /usr/share/pyshared/ldap/schema/subentry.py
ln -s /usr/share/pyshared/ldap/schema/tokenizer.py
Compile .py files
Now that we have the files linked in from the global shared Python path (where the python-ldap deb installer put them), we need to compile all of the .py files using Zenoss's local python 2.4 installation:
cd $ZENHOME/lib/python
python /usr/local/zenoss/python/lib/python2.4/py_compile.py ldif.py
python /usr/local/zenoss/python/lib/python2.4/py_compile.py ldapurl.py
cd ldap
python /usr/local/zenoss/python/lib/python2.4/py_compile.py *.py
cd schema
python /usr/local/zenoss/python/lib/python2.4/py_compile.py *.py
Now that everything is compiled, restart zope (as zenoss, zopectl restart) and you can proceed with the rest of the instructions in the above wiki article. You will now see the ActiveDirectory Multi Plugin in the plugin list on the http://zenoss-installation:8080/zport/acl_users/manage_workspace page.

Tuesday, December 16, 2008

Faster DFS recovery application

In trying to set up DFS replication, we had a number of files that were not present in both the primary DFS partner and the destination partner. In this case, DFS will move all of the files "missing" from the primary partner out of the tree and into a separate pre-existing path on each destination volume. Microsoft will provide you with a recovery script that calls xcopy to, based on the generated PreExistingManifest.xml file, move the files back into their original locations.

The problem we had was that shelling out to xcopy when you have millions of relatively small files was going to take, well, months to complete. I built the following .NET (3.5, C#) console application which proved to do this at hundreds of times the rate of the Microsoft script. The only issue is that it does not replicate permissions; since we did not need that for our recovery, it fit the bill.

Please use at your own risk. I make no warranties. I recommend specifying an alternate recovery path when calling the application so you can validate output first.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace DFSRecovery
{
/// <summary>
/// Handles copying DFS files back into the original folder structure.
/// </summary>
class Program
{
/// <summary>
/// The main application loop.
/// </summary>
/// <param name="args">The args. See usage text.</param>
static void Main(string[] args)
{
if (args.Length < 3)
{
Console.WriteLine("\nDFSRecovery version " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString() + " [Arthur Penn, http://devarthur.blogspot.com]");
Console.WriteLine("Usage: DFSRecovery.exe \"\\\\path\\to\\PreExistingManifest.xml\" \"\\\\path\\to\\pre-existing\\folder\" \"\\\\path\\to\\output\\folder\" [print only=true|false]");
Environment.Exit(1);
}

// Load the PreExistingManifest.xml document and select the values we need
var doc = XDocument.Load(args[0]);
string preExistingFolder = args[1];
string outputFolder = args[2];
bool printOnly = false;
if (args.Length > 3)
{
printOnly = bool.Parse(args[3]);
}

int rc = 0;

var actions = from n in doc.Descendants("Resource")
select new {
FileOrFolder = ((string)n.Descendants("Attributes").First()),
Source = Path.Combine(preExistingFolder, (string)n.Descendants("NewName").First()),
Destination = Path.Combine(outputFolder, ((string)n.Descendants("Path").First()).Substring(7,
((string)n.Descendants("Path").First()).Length - 7))
};

foreach (var item in actions)
{
try
{
if (File.Exists(item.Source))
{
if (File.Exists(item.Destination))
{
if (printOnly)
{
Console.WriteLine("Target file exists: \"" + item.Destination + "\"");
}
}
else
{
CopyFile(item.Source, item.Destination, printOnly);
}
}
else
{
// It's a directory
CopyDirectory(item.Source, item.Destination, printOnly);
//break;
}
}
catch (Exception x)
{
rc = 1;
Console.WriteLine("Exception copying \"" + item.Source + "\" to \"" + item.Destination +
"\": " + x.ToString());
}
}

Environment.Exit(rc);
}

/// <summary>
/// Ensures the directory is present.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="isDirectory">if set to <c>true</c> [is directory].</param>
/// <param name="printOnly">if set to <c>true</c> [print only].</param>
static void EnsureDirectory(string path, bool isDirectory, bool printOnly)
{
string targetFolder = (isDirectory ? path : path.Substring(0, path.LastIndexOf("\\")));
if (Directory.Exists(targetFolder))
{
if (printOnly)
{
Console.WriteLine("Target folder exists: \"" + targetFolder + "\"");
}
}
else
{
if (printOnly)
{
Console.WriteLine("Creating target folder: \"" + targetFolder + "\"");
}
else
{
Directory.CreateDirectory(targetFolder);
}
}
}

/// <summary>
/// Copies the directory.
/// </summary>
/// <param name="sourcePath">The source path.</param>
/// <param name="destinationPath">The destination path.</param>
/// <param name="printOnly">if set to <c>true</c> [print only].</param>
static void CopyDirectory(string sourcePath, string destinationPath, bool printOnly)
{
EnsureDirectory(destinationPath, true, printOnly);
foreach (string file in Directory.GetFiles(sourcePath))
{
//#if DEBUG
// Console.Write("From CopyDirectory: ");
//#endif
string fileName = file.Substring(file.LastIndexOf("\\") + 1);
CopyFile(file, Path.Combine(destinationPath, fileName), printOnly);
}

// Recursively process directories
foreach (string directory in Directory.GetDirectories(sourcePath))
{
string sourceSubDirectory = directory.Substring(directory.LastIndexOf("\\") + 1);
string destinationSubDirectory = Path.Combine(destinationPath, sourceSubDirectory);

CopyDirectory(directory, destinationSubDirectory, printOnly);
}
}

/// <summary>
/// Copies the file.
/// </summary>
/// <param name="sourcePath">The source path.</param>
/// <param name="destinationPath">The destination path.</param>
/// <param name="printOnly">if set to <c>true</c> [print only].</param>
static void CopyFile(string sourcePath, string destinationPath, bool printOnly)
{
if (printOnly)
{
Console.WriteLine("Copying \"" + sourcePath + "\" to \"" + destinationPath + "\"");
}
else
{
EnsureDirectory(destinationPath, false, printOnly);
File.Copy(sourcePath, destinationPath);
}
}
}
}

Thursday, November 13, 2008

Capturing Control Key Sequences in Silverlight 2

It took me a while to locate this, but I found in this MSDN article how to capture control key sequences in Silverlight 2. I was expecting to be able to 'and' the control key with the pressed alpha key, but that's not the way it works. I attached the following event handler to my layout root grid's KeyUp event. This performs a 'save' when pressing Ctrl+S, and 'save and close' when pressing Ctrl+Shift+S:

        /// <summary>
        /// Handles keyboard shortcuts.
        /// </summary>
        /// <param name="sender">Event sender.</param>
        /// <param name="e">Event args.</param>
        private void LayoutRoot_KeyUp(object sender, KeyEventArgs e)
        {
            if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
            {
                switch (e.Key)
                {
                    case Key.S:
                        // Ctrl+S: save; Ctrl+Shift+S: save and close
                        e.Handled = true;
                        SaveMyItem((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift);
                        break;
                }
            }
        }

Tuesday, October 28, 2008

Compiling Mono 2.0.1 on Ubuntu Gutsy Server 8.04

I didn't want to use the aging Mono version present in Ubuntu Server 8.04, so I set out to compile Mono 2.0 (and subsequently 2.0.1, via the same process). This turned out not to be too bad.

First, install the requisite packages:
aptitude install build-essential swig autoconf gawk mono-common binfmt-support bison pkg-config libglib2.0-dev
Yes, that's not a typo--you do want one of Ubuntu's Mono packages, mono-common. This will enable shell execution of Mono executables via ./ notation rather than having to execute "mono /path/to/executable."

Once you are done, download and unpack the source for Mono. This will get you 2.0.1:
wget http://ftp.novell.com/pub/mono/sources/mono/mono-2.0.1.tar.bz2
tar xf mono-2.0.1.tar.bz2

Now you are ready to build and install Mono (the make step will take a while):
cd mono-2.0.1
./configure --with-libgdiplus=no
make
make install
Lastly, you need one symlink so the binfmt-support package can execute Mono executables directly via the shell:
ln -s /usr/local/bin/mono /usr/bin/cli
That's it. Typing the command "mono -V" should yield the about information for Mono 2.0.1. Follow the instructions under "Testing the Mono installation" and confirm you can not only build and execute the example.exe application, but that you can execute it with ./ notation (e.g. ./example.exe).

Cheers!

Thursday, October 09, 2008

D-Link DWL-G122 wireless USB adapter on Vista

I have a D-Link DWL-G122 wireless adapter (B/G) that I wanted to get working on Vista. I found a few posts, including this forum thread, but nothing worked for me. It turns out I have an older revision B adapter... and I ended up getting this to work by installing the Windows XP drivers for the revision B from D-Link:

ftp://files.dlink.com.au/products/DWL-G122/REV_B/Drivers/

I installed this by right-clicking the adapter in Device Manager, choosing:
  1. Update Driver Software...
  2. Browse my computer for driver software
  3. Let me pick from a list of device drivers on my computer
  4. Network Adapters category
  5. "Have Disk" button... then finally browsing to the extracted contents of the above driver.
Enjoy.

Sunday, October 05, 2008

Vista power saving never activates... thoughts?

I was hoping for some help with getting Vista's power saving to function. I have a Windows Vista Business Service Pack 1 (x64) installation. I have power options set up as follows:

Turn off the display:
[on battery] 5 minutes
[plugged in] 20 minutes

Put the computer to sleep:
[on battery] 15 minutes
[plugged in] 1 hour

Initially, power saving was working as expected. However, now it never enters power saving mode or even turns off the monitor. I have tried changing the plan settings around (including changing from one plan to another and creating a custom plan with the desired settings) with no success.

Does anyone have any ideas?

** UPDATE 28 Oct ** This was caused by the Vista Photos screensaver! Other screensavers allowed power saving to function, but the Photos screensaver did not.

Wednesday, October 01, 2008

MOSS doesn't like having the indexer role moved

We needed to expand our MOSS farm from one server to two so that we could have the search and indexing performed by a second machine, as we were putting the one poor server under significant periodic load. So, we stood up the second instance and joined it to the farm, and attempted to assign the search and indexing roles to this new instance. After doing so, when we would go to the search settings link in the SSP, we got the following message:

“The search service is currently offline. Visit the Services on Server page in SharePoint Central Administration to verify whether the service is enabled. This might also be because an indexer move is in progress.”

I searched and found wildly different solutions for fixing this. I ended up doing the following things to correct it:
  1. On the new index server, I had to stop and restart the Office Search role after the initial move. I did this with stsadm via the following commands: 1) stsadm -o osearch -action stop 2) stsadm -o osearch -action start -role IndexQuery -farmserviceaccount DOMAIN\accountname -farmservicepassword PASSWORD
  2. Access the SSP administration page (http://url-of-central-admin/_admin/managessp.aspx), and on the drop-down menu for the SSP in question, choose Edit Properties.
  3. In the section titled Process Accounts with access to this SSP, add the search service account to the dialog box.
  4. In the section titled Index Server, select the new index server for the farm.
  5. Click OK to apply your changes.
  6. Reboot the index server and restart full crawls of the content sources.