Friday, March 16, 2012

Telerik JustMock enabling the profiler from the command line

I recently ran into the issue of enabling the JustMock profiler for nUnit console on our build server, this is needed to mock statics and concrete classes. Telerik points to a solution but this is over a year old and no longer works, also all of their descriptions of how to enable this programatically also do not work. Telerik also decided to include the JustMockRunner from the mentioned blog in their newer releases in Program Files (x86)\Telerik\JustMock\Libraries\ This actually does work, so Telerik must have modified their version but there's a huge problem with this version, it doesn't allow the passing of any arguments to nunit or allow you to supply more than one test dll. This is a big problem for us so after some digging and reverse engineering I've re-written the JustMockRunner application to just pass all the arguments supplied to nUnit. Actually you don't even have to use nUnit, it should work with any command line unit tester.

Telerik documents 2 or 3 environment variables depending on where you read it that are involved in enabling the profiler, they added another one recently which causes all the old documentation and programs to fail. This new variable is JUSTMOCK_INSTANCE which needs to be set to the process ID of the running program.

I also noticed that the old JustMockRunner was capturing the output into a string and when everything finishes it all gets dumped to the screen at once. I find this behavior to be very annoying and results in a "is it actually running?" moment and with our 1,000+ unit tests there's quite a while for any feedback about what is going on. I have no idea why it was done this way as the default behavior of running another process is to display its output in the current console window. So my version removes it and makes it as simple and flexible as possible.

Without further adieu here's my code:

Update: There's some interest in this solution so I have updated to my code to a later version I came up with that preserves all arguments that are passed to it in their original form. This was a problem when trying to pass arguments with spaces in them like /loc="c:\Program Files\"
using System;
using System.Diagnostics;
using System.Globalization;
using System.Text.RegularExpressions;

namespace JustMockRunner
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("Requires at least 1 argument, nunit-console.exe location");
                return;
            }
            string command, arguments;
            ParseCommand(Environment.CommandLine.Trim(), out command, out arguments);
            
            var process = new Process();
            var processStartInfo = new ProcessStartInfo(command)
                                       {
                                           Arguments = arguments,
                                           UseShellExecute = false
                                       };

            processStartInfo.EnvironmentVariables["COR_ENABLE_PROFILING"] = "0x1";
            processStartInfo.EnvironmentVariables["COR_PROFILER"] = "{D1087F67-BEE8-4f53-B27A-4E01F64F3DA8}";
            processStartInfo.EnvironmentVariables["COMPLUS_ProfAPI_ProfilerCompatibilitySetting"] = "EnableV2Profiler";
            processStartInfo.EnvironmentVariables["JUSTMOCK_INSTANCE"] = Process.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture);


            process.StartInfo = processStartInfo;
            process.Start();
            process.WaitForExit();
        }

        private static void ParseCommand(string input, out string command, out string arguments)
        {
            var reg = new Regex(@"(^""[^""]*(?:\.[^""]*)*""|^\s*\S*)(.*)");
            var parts = reg.Match(reg.Match(input).Groups[2].Value.Trim());

            command = parts.Groups[1].Value.Trim();
            arguments = parts.Groups[2].Value.Trim();
        }
    }
}

Friday, July 2, 2010

iSCSI With OCFS2

I figured since I had to gather this information from all over the web that I will score some internets points by writing about how I did it and collecting it all in one place. Also it gives me something to write in a blog so now I'm cool.

Problem: Need a shared disk source for reading and writing to be used by a Linux cluster of 3 nodes.

I didn't want to use NFS or some of the higher level solutions due to speed and propagation delays, so I looked into some more advanced configurations. I settled on using open-iscsi for the shared disk and ocfs2 as the clustered filesystem. My servers are Ubuntu Hardy Heron (8.04.1 LTS). It wasn't as difficult as I first thought it would be, it was harder trying to decide what I should use.

iSCSI Server
Install on only 1 server, I didn't get into creating failover for this yet.
package: iscsitarget
config file: /etc/ietd.conf

I didn't configure any authentication, I figured its on its own subnet and I'm not one to lock down the security so much that it raises more problems. Plus I had no clue what I was doing and I didn't want authentication to be the thing to stop since there's a few parts that need to work together.

The only things I edited:
The target's ID which was something like
iqn:2006-03.com.example:hoth-esx
and all I did was change the domain and the cluster name after the :
The Lun (not a clue what that stands for) to something like
Lun 0 Path=/dev/sdb,Type=fileio
where /dev/sdb is the disk I was dedicating to this use.

Restart iscsitarget and you should be good to go


Each node that's going to be accessing the shared disk needs to have the rest installed

iSCSI Client
package: open-iscsi
config file: /etc/iscsi/iscsid.conf

The only thing I did was change: node.startup to automatic
There's a lot of settings that I haven't a clue what they do but it worked just fine like this. If you're doing authentication you need to set it up in here too.

Restart open-iscsi

Make the iscsi connection, this only needs to be done once to initially set it up and both commands need to be run on all connecting nodes, obviously change the ip

iscsiadm -m discovery -t sendtargets -p 192.168.1.60

Then copy what that returned and plug it into:

iscsiadm --mode node --targetname iqn:2006-03.com.example:hoth-esx --portal 192.168.1.60:3260 --login

Restart open-iscsi again check your messages log

tail -f /var/log/messages

You should be able to find which device was assigned to it, this can be different from the device on your iscsitarget server, mine was /dev/sdb


OCFS2
packages: ocfs2-tools ocfs2console
config file: /etc/ocfs2/cluster.conf

The config file and folder wasn't created by default, there's an example in
/usr/share/doc/ocfs2-tools/examples/cluster.conf
Copy this to /etc/ocfs2/cluster.conf

I found this pretty straight forward to customize for my needs, this file needs to be identical on all nodes.

After editing the file I told the ocfs2 package to reconfigure itself which brought up a series of prompts
dpkg-reconfigure ocfs2-tools

The only thing I changed was the Cluster Name to match what I put in the cluster.conf file and took the defaults on the rest.

Restart your OCFS2 services:
/etc/init.d/o2cb restart
/etc/init.d/ocfs2 restart


On only 1 node (I did it on the first node I set up) you need to set up the drive partition and format it for ocfs2, nothing complicated

fdisk /dev/sdc
mkfs.ocfs2 /dev/sdc1

All nodes again:

You should now be able to mount this drive
mount -t ocfs2 /dev/sdc1 /mnt/iscsi

I then added an entry to the /etc/fstab so the drive will mount on boot. NOTE: don't use the UUID, the iscsi and the physical device get the same ID and they become confused and crash the system
/dev/sdc1 /mnt/iscsi ocfs2 _netdev 0 0
Its important to have the _netdev option


That's it, your setup should be working. The only problem I have is since my iSCSI target server doesn't have a failover, if that is reboot all the nodes spike in cpu and become non-responsive for a few seconds. Then they can't regain the connection after the server is back online, when this happens either reboot each node or stop the ocfs2 and iscsi services then bring them back up in the proper order.

Shutdown order:
unmount drive if currently mounted
ocfs2
o2cb
open-iscsi

Reverse the order for start up.