Sunday, May 29, 2011

VM - how to perform an auto deploy

While writing this blog I'm trying to configure a VM server.
The main idea is: revert to a well-known state and do the sanity-tests from it. So far for the idea, now for the reality. The first thing needed is the WM Server from VMWare. After installation I've still got troubles with the COM object it uses.

Working with the local workstation was by far easier, and the solution was tested on it.

Step 1: connect to the VM.
I've downloaded the VMWareTasks for the job, it's easy to use and removes the hassle of finding when/what went a miss with the VM.
The code for connecting to a VM is simple:
VMWareVirtualHost virtualHost = new VMWareVirtualHost();
virtualHost.ConnectToVMWareWorkstation();

VMWareVirtualMachine virtualMachine = virtualHost.Open(VMX_PATH);
virtualMachine.PowerOn();


Now we have a machine turned on and running.


Step 2: Revert to snapshot
VMWareSnapshot foundSnapshot = virtualMachine.Snapshots.GetNamedSnapshot(SNAPSHOT_NAME);
if (foundSnapshot == null)
return;
foundSnapshot.RevertToSnapshot(TIMEOUT);
virtualMachine.WaitForToolsInGuest();

The last line is a bit weird, but very important. The machine must be fully running in order to work. As we all know, M$ do a fake readiness state while loading necessary things in the back (and preventing work on the way).

Step 3: Login and run things
virtualMachine.LoginInGuest(VM_USERNAME, VM_PASSWORD, 8, TIMEOUT);

VIX_LOGIN_IN_GUEST_REQUIRE_INTERACTIVE_ENVIRONMENT = 8
Create needed folders
foreach (string dir in Dirs)
                {
                    if (!virtualMachine.DirectoryExistsInGuest(dir))
                    {
                        virtualMachine.CreateDirectoryInGuest(dir);
                        Console.WriteLine("Created " + dir);
                    }
                }

Copy files
foreach (KeyValuePair file in Files)
                {
                    CopyFileToMachine(file.Key, file.Value, virtualMachine);
                }

private static void CopyFileToMachine(string source, string target, VMWareVirtualMachine virtualMachine)
        {
            if (virtualMachine.FileExistsInGuest(target))
            {
                virtualMachine.DeleteFileFromGuest(target);
            }
            virtualMachine.CopyFileFromHostToGuest(source, target);
        }
 
Run programs
foreach (KeyValuePair prog in Progs)
                {
                    virtualMachine.RunProgramInGuest(prog.Key, prog.Value,
TIMEOUT);
                }


Step 4:  Get the output back & turn the machine off
foreach (string op_file in ReturnedFiles)
                {
                    CopyFileFromMachine(op_file, virtualMachine);
                }

private static void CopyFileFromMachine(string source, VMWareVirtualMachine virtualMachine)
        {
            if (virtualMachine.FileExistsInGuest(source))
            {
                virtualMachine.CopyFileFromGuestToHost(source, source.Substring(source.LastIndexOf('\\') + 1));
            }
        }

virtualMachine.PowerOff();

That's all, really.
It's worthy to mention there are 4 different types of VMs, but it's important only when initiating the connection, afterwards it's the same.

Happy VMing

Tuesday, May 17, 2011

iTextSharp - how to control the PDF

I'll put it simply like that:
I want a PDF where I control each and every object's location / size / other properties. Usually the objects are either texts or images. So I ended up with a small piece of code which does that, and returns byte array. The byte array is easier to transmit over the net.

Let's begin with the wrapping:
public byte[] GetPDF()
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
iTextSharp.text.Document doc = new iTextSharp.text.Document();
iTextSharp.text.pdf.PdfWriter writer = iTextSharp.text.pdf.PdfWriter.GetInstance(doc, ms);
doc.Open();
iTextSharp.text.Rectangle page = doc.PageSize;
iTextSharp.text.pdf.PdfContentByte cb = writer.DirectContent;
string fontsDirName = (string)Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders").GetValue("Fonts");
iTextSharp.text.FontFactory.RegisterDirectory(fontsDirName);
RenderPDFImages(doc);

RenderPDFTexts(cb, doc.PageSize.Height);
doc.Close();
ms.Flush();
return ms.ToArray();
}


Quite self-explanatory, wrap the Render PDF methods with the creation of the document and the transform to byte array. Note that RenderPDFImages gets the document and RenderPDFTexts gets the content + page's height.
Both of the render methods loop over the objects and call the "Add" method. I'm supplying the "Add" methods, instead of the complete loop.
Important things to remember:
1. The Y coordinate is calculated from the bottom, not from the top. For this reason the code is full of Page Height - Y - Local Height.
2. Before starting to add texts call cb.BeginText(); and when you're done call cb.EndText();

First the AddImage:
private void AddImage(iTextSharp.text.Document doc, System.Drawing.Image img, float X, float Y)
{
iTextSharp.text.Image gif = iTextSharp.text.Image.GetInstance(imageToByteArray(img));
gif.Alignment = iTextSharp.text.Image.ALIGN_LEFT;
gif.SetAbsolutePosition(X, doc.PageSize.Height - Y - gif.PlainHeight);
doc.Add(gif);
}


Note: I convert the image to byte array, that way I can control the type of image inserted + it avoids a strange behavior I've encountered when trying to add it directly.

And the last method, AddText:
private void AddText(iTextSharp.text.pdf.PdfContentByte cb, float PageHeight, string txt, System.Drawing.Font fnt, System.Drawing.Color clr, float x, float y)
{
iTextSharp.text.pdf.BaseFont bf = iTextSharp.text.FontFactory.GetFont(fnt.Name).BaseFont;
cb.SetRGBColorFill(clr.R, clr.G, clr.B);
cb.SetFontAndSize(bf, fnt.Size);

cb.SetTextMatrix(x, PageHeight - y - fnt.Height);
cb.ShowText(txt);

}

There are many more things which can be done with those objects. I used the bare minimum to get the output in PDF format while making sure it looks almost like the original picture.