VMware {code} Community
jeffpatton
Enthusiast
Enthusiast

using VMware.Vim

Hi, I may be posting this in the wrong forum so I apologize, please move it to the appropriate forum or let me know and I'll post there.

A couple of months ago one of the Platform guys I work with sent me an IM asking if there was a way to run powershell inside a webpage. After a bit of googling I found that you could indeed load powershell into a webapge and make it do things. Now the reason he asked me this is because I do nearly everything possible in Powershell, so he figured I was a good resource. So why did he ask?

We manage roughly 1100 vm's and w're anticpiating onboarding about 600-700 more over the next few months. They want a method by which the average tech could easily create a VM in the appropriate cluster with the proper settings. The idea is that the user would pick from a list of "Golden Images" (pre-configured vm's) and then apply a specific customization file to it. We worked out a rather lengthy script that works perfectly well for both the linux and windows vm's.

Once we had a functional script worked out I started working that into a webpage. I reference the powershell sdk so i can create a powershell instance, then i add-psnappin for the powercli stuff. From there I populate the script with entries from various textboxes and dropdowns. During testing this appeared to work flawlessly. My test environment is a windows 8 laptop with vs2012 loaded, so the page is loaded up in iis express 8.

Once we uploaded that to the server, i had to install the powershell sdk and the powercli on the server first. it seemed to work, now all of his work is in linux so he tested it several different ways, handed it off to one of the linux guys he works with until we got all the kinks worked out. when we passed it over to the windows guys that's where things seemed to go wrong. At the moment I have a fully functional web app that will happily build you linux vm's all the live long day, but on the windows side i routinely get stuck on the unattend portion. it fails every single time,this got me thinking that perhaps there was some difference between iis express and iis but thus far i've been unable to find anything.

So then i started thinking that perhaps attempting to run this through in essence two layers, powershell and powercli was a bit much. So i started to mess around with vmware.vim, this seems to me to be how the powercli works. i've found several very wonderful articles and this one today was especially helpful (http://velemental.com/2012/03/09/a-deep-dive-doing-it-the-manual-way-with-vmware-vim-and-powershell/) I've been going through and reworking his powershell code into csharp, which on the whole isn't too hard, but i've come across my first stumbling block.

this one line of code is quite angry

vimClient.VimService.RetrieveProperties(vimServiceContent.propertyCollector,pfs);

according to vs2012, RetrieveProperties is not a valid method. here is the error message below

Error     1     'System.Web.Services.Protocols.SoapHttpClientProtocol' does not contain a definition for 'RetrieveProperties' and no extension method 'RetrieveProperties' accepting a first argument of type 'System.Web.Services.Protocols.SoapHttpClientProtocol' could be found (are you missing a using directive or an assembly reference?)     C:\scripts\WebSites\WebSite1\Default.aspx.cs     55     30     WebSite1(1)

I have tried referencing the vmware-vim.dll that is available in the GAC as well as the one found in Program Files (x86) to no avail. Now the reason this bothers me is because it's obviously there as the powershell code in the article above works flawlessly, and when i access https://server/sdk/vim.wsdl i see information for retrieveproperties.

i was hoping that i could keep this relatively simple without having to go the sdk, if someone can help me out with either the unattend error, which doesn't actually appear to be an error. or the retrieveproperties error i would really appreciate it. I'll include the code i have below so you can load it up and look at it.

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using VMware.Vim; public partial class _Default : System.Web.UI.Page {     VimClient vimClient = new VimClient();     ServiceContent vimServiceContent = new ServiceContent();         protected void Page_Load(object sender, EventArgs e)     {     }     protected void cmdConnect_Click(object sender, EventArgs e)     {         string sViServer = txtViServer.Text.ToString();         string sUsername = txtUsername.Text.ToString();         string sPassword = txtPassword.Text.ToString();                 //         // Connect to vmware sdk service         //         vimClient.Connect("https://" + sViServer + "/sdk");         vimClient.Login(sUsername, sPassword);         vimServiceContent = vimClient.ServiceContent;                 //         // Get a list of datacenters         //         object DataCenter = getFolder(new string[] {"childEntity"}, "Folder", vimServiceContent.RootFolder.Type, vimServiceContent.RootFolder.Value);     }     protected object getFolder(string[] pathSet, string psType, string morefType, string morefValue)     {         PropertySpec[] ps = new PropertySpec[1];         ps[0].Type = psType;         ps[0].PathSet = pathSet;         ObjectSpec[] os = new ObjectSpec[1];         os[0].Obj.Type = morefType;         os[0].Obj.Value = morefValue;                 PropertyFilterSpec[] pfs = new PropertyFilterSpec[1];         pfs[0].PropSet = ps;         pfs[0].ObjectSet = os;         ObjectContent[] vmFoldersObjectContent = new ObjectContent[1];         vimClient.VimService.RetrieveProperties(vimServiceContent.propertyCollector,pfs);         return vmFoldersObjectContent[0].PropSet[0].Val;     } }

Jeffrey S. Patton Systems Specialist, Enterprise Systems University of Kansas 1001 Sunnyside Ave. Lawrence, KS. 66045 http://patton-tech.com
13 Replies
jeffpatton
Enthusiast
Enthusiast

Ok, so I have significantly reworked my code, and am now pulling relevant information from vsphere. I'm going to paste a few snippets for anyone who may have a little more insight into this than I.

first clip, this is how i'm pulling datastores, clusters, portgroups and vms

List<EntityViewBase> appVirtualMachines = vimClient.FindEntityViews(typeof(VirtualMachine), null, cloneFilter, null);
foreach (EntityViewBase appVirtualMachine in appVirtualMachines)
{
    VirtualMachine thisVirtualMachine = (VirtualMachine)appVirtualMachine;
    ListItem vmName = new ListItem();
    vmName.Text = thisVirtualMachine.Name;
    vmName.Value = thisVirtualMachine.MoRef.ToString();
    cboClones.Items.Add(vmName);
}

Basically where you see VirtualMachine (it's the type) replace that with DataCenter, Cluster, DataStore or DistributedVirtualPortgroup and you'll get the things you want. Now to start this worked very well, I store the name of the thing in the text property of a dropdown and the moref as a string in the value property. My thinking was that if I had the moRef I should be able to get at the object, but a couple of tests have failed miserably.

Attempt #1

ManagedObjectReference dcMoRef = new ManagedObjectReference(cboClusters.SelectedValue); Datacenter thisDC = new Datacenter(vimClient,dcMoRef);

So in the IDE that appeared to work, thisDC gave me properties i would expect to see, it was syntactically correct, but during runtime it failed and returned null. So I assume it's "creating" thisDC in memory and the client and moref stuff isn't getting passed or processed. My thinking was perhaps I was missing something, so i did something similar to how i get a list of vm's (code above).

Attempt #2

ManagedObjectReference dcMoRef = new ManagedObjectReference(cboClusters.SelectedValue); EntityViewBase appDC = vimClient.FindEntityView(typeof(Datacenter), dcMoRef, null, null); Datacenter thisDC = (Datacenter)appDC;

Again, syntactically correct, but when run it still returns null.

Considering I need to move forward here, the idea was to start to rewrite the app, using only vmware.vim to handle everything. I *think* this is possilbe since I have some examples showing that in powershell. In fact I have two powershell consoles open as I go through this, one powercli and the other straight powershell with vmware.vim.dll loaded up. comparing and contrasting between the two and massaging that into the csharp.

Not sure anyone will respond to this so I'm going to toss some of my links in here as I progress, hopefully i'll get something working before long.

http://velemental.com/2012/03/09/a-deep-dive-doing-it-the-manual-way-with-vmware-vim-and-powershell/

http://pubs.vmware.com/vsphere-50/index.jsp?topic=%2Fcom.vmware.powercli.ug.doc_50%2FGUID-02DC68A8-5...

http://communities.vmware.com/message/2156946

http://blogs.vmware.com/vipowershell/2010/07/managedobjectreference-creation-now-its-easier.html

http://www.vmdev.info/?p=202

http://stackoverflow.com/questions/5493697/which-vmware-api-should-i-use

Jeffrey S. Patton Systems Specialist, Enterprise Systems University of Kansas 1001 Sunnyside Ave. Lawrence, KS. 66045 http://patton-tech.com
Reply
0 Kudos
jeffpatton
Enthusiast
Enthusiast

So I'm back, I'm now pulling datastores in portgroups based on the moref stored as a value in a dropdown. Here is the listing

//
// Get a list of datastores
//
string SelectedDataCenter = cboClusters.SelectedItem.Text;
NameValueCollection dcFilter = new NameValueCollection();
dcFilter.Add("name", SelectedDataCenter);
List<EntityViewBase> appDC = vimClient.FindEntityViews(typeof(Datacenter), null,dcFilter, null);
Datacenter thisDC = (Datacenter)appDC[0];
ManagedObjectReference[] DSmoRefs = thisDC.Datastore;
foreach (ManagedObjectReference dsmoRef in DSmoRefs)
{
    Datastore thisDs = new Datastore(vimClient, dsmoRef);
    ListItem vmDatastore = new ListItem();
    vmDatastore.Text = thisDs.Name;
    vmDatastore.Value = thisDs.MoRef.ToString();
    cboDatastores.Items.Add(vmDatastore);
}
       
//
// Get a list of network portgroups
//
ManagedObjectReference[] PGmoRefs = thisDC.Network;
foreach (ManagedObjectReference pgmoRef in PGmoRefs)
{
    DistributedVirtualPortgroup thisPort = new DistributedVirtualPortgroup(vimClient, pgmoRef);
    ListItem vmPortName = new ListItem();
    vmPortName.Text = thisPort.Name;
    vmPortName.Value = thisPort.MoRef.ToString();
    cboNetwork.Items.Add(vmPortName);
}

So, I kind of went a little different way, and as I paste this in I'm wondering if I could get around the line where I reference the first (and only) item in the collection returned from the findentityviews. Earlier testing I was using findentityview, but I think the problem was in my filter I was using "fields" that appeared in powershell but didn't actually show up when I browsed the mob.

So as a POC this seems to work, and is much quicker than attempting to load a snap-in inside powershell, inside a webpage 😉 not that i personally didn't think that was cool.

Ok, so I'm getting the right datastores and portgroups, but i'm not getting the pretty names, which for the moment i'm ok with. My next challenge is how to get a list of specfiles. My original code for that didn't work at all.

// // Get a list of OS Customizations // List<EntityViewBase> appOsCustomizationSpecs = vimClient.FindEntityViews(typeof(CustomizationSpec), null, null, null); foreach (EntityViewBase appOsCustomizationSpec in appOsCustomizationSpecs) {     CustomizationSpec thisSpec = (CustomizationSpec)appOsCustomizationSpec;     ListItem specName = new ListItem(thisSpec.Identity.ToString()); }

I get red wavy's under the first line in the foreach. The message is cannot convert type vmware.vim.entityviewbase to vmware.vim.customizationspec. Any thoughts or insight would be appreciated.

Jeffrey S. Patton Systems Specialist, Enterprise Systems University of Kansas 1001 Sunnyside Ave. Lawrence, KS. 66045 http://patton-tech.com
Reply
0 Kudos
jeffpatton
Enthusiast
Enthusiast

Back again with my next installment, so in the code I posted up the day before i was getting back the names of the datastores but it was in the form of datastore-datastore-01, which isn't how they appear in the interface or when you get-datastore. So I found this old thread (http://communities.vmware.com/thread/208350) and adapted it to my code.

string SelectedDataCenter = cboClusters.SelectedItem.Text;
NameValueCollection thisDcFilter = new NameValueCollection();
thisDcFilter.Add("name", SelectedDataCenter);
EntityViewBase appDC = vimClient.FindEntityView(typeof(Datacenter), null,thisDcFilter, null);
Datacenter thisDC = (Datacenter)appDC;
ManagedObjectReference[] DSmoRefs = thisDC.Datastore;
foreach (ManagedObjectReference DSmoRef in DSmoRefs)
{
    Datastore thisDatastore = (Datastore)vimClient.GetView(DSmoRef,null);
    ListItem vmDatastore = new ListItem();
    vmDatastore.Text = thisDatastore.Info.Name;
    vmDatastore.Value = thisDatastore.MoRef.ToString();
    cboDatastores.Items.Add(vmDatastore);
}

This works but it appears to add several seconds to the run, and with only about 23 unique datastores total that seems a bit much. I had hoped there was a way to do this via the datastoreinfo type, but thus far I've not had any luck. So at the moment, I'm now receiving properly named datastores and i'll use a similar method to grab up the portgroups.

if anyone has any suggestions or if I find a way to use the datastoreinfo i'll post.

Jeffrey S. Patton Systems Specialist, Enterprise Systems University of Kansas 1001 Sunnyside Ave. Lawrence, KS. 66045 http://patton-tech.com
Reply
0 Kudos
jeffpatton
Enthusiast
Enthusiast

So after poking around in the mob some more I realized I was going about this wrong. I think what I had in mind originally would work for programtatically creating a new specfile, but that's not really what i want. At any rate, in the mob it appeared that the cusotmizationspecmanager had an info property and i was able to connect to it in a similar fashion as i did with the datastores and portgroups.

//
// Get a list of OS Customizations
//
CustomizationSpecManager specManager = (CustomizationSpecManager)vimClient.GetView(vimServiceContent.CustomizationSpecManager, null);
foreach (CustomizationSpecInfo specInfo in specManager.Info)
{
    ListItem vmSpecfile = new ListItem();
    vmSpecfile.Text = specInfo.Name;
    vmSpecfile.Value = specInfo.Type;
    cboCustomization.Items.Add(vmSpecfile);
}

I was also under the impression that these were similar to other objects, but there is no moref associated with these.

Jeffrey S. Patton Systems Specialist, Enterprise Systems University of Kansas 1001 Sunnyside Ave. Lawrence, KS. 66045 http://patton-tech.com
Reply
0 Kudos
jeffpatton
Enthusiast
Enthusiast

Friday was a very productive day for me, I was able to get a rough draft of the updated code working. It doesn't clone yet, I've not gotten to that step yet, but it does do the basics. Connect to a specified viserver, return a list of datacenters, for the selected (displayed) DC get a list of stores, portgroups, and any customizations. The clones are a list of vm's that are prefixed with GM, that is read in from the web.config file.

So i'm going to include the code behind the connect button, this pre-populates the webpage with all the data, there is zero error handling, but it *ought* to just work for anyone with at least one datacenter. for standalone esxi, i doubt it will very well at all, so I apologize in advance for that.

first thing you will need to add a reference to vmware.vim.dll, second you will need the following at the top of your code behind file.

using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Web; using System.Web.Configuration; using System.Web.UI; using System.Web.UI.WebControls; using VMware.Vim;

second you will need a few controls, i have three text boxes

txtViServer

txtUsername

txtPassword

I have one command button

cmdConnect

I have 5 dropdown lists, some are singular and some are plural, i need to update these to be plural, tweek of mine 😉

cboClones

cboCustomization

cboClusters ( need to change this to datacenters )

cboDatastores

cboNetwork

i have one additional text box at the bottom to display various thing while i'm debugging, but at the moment it is where the list of hosts are displayed.

txtResult

If you have all that, then you should be able to just drop this code in behind the click event of cmdConnect.

public void cmdConnect_Click(object sender, EventArgs e)
{
    sViServer = txtViServer.Text.ToString();
    sUsername = txtUsername.Text.ToString();
    sPassword = txtPassword.Text.ToString();
    //
    // Connect to vmware sdk service
    //
    vimClient.Connect("https://" + sViServer.Trim() + "/sdk");
    vimSession = this.vimClient.Login(sUsername, sPassword);
    vimServiceContent = this.vimClient.ServiceContent;
     
    //
    // Get a list of datacenters
    //
    List<EntityViewBase> appDatacenters = vimClient.FindEntityViews(typeof(Datacenter), null, null, null);
    foreach (EntityViewBase appDatacenter in appDatacenters)
    {
        Datacenter thisDatacenter = (Datacenter)appDatacenter;
        ListItem vmDatacenter = new ListItem();
        vmDatacenter.Text = thisDatacenter.Name;
        vmDatacenter.Value = thisDatacenter.MoRef.ToString();
        cboClusters.Items.Add(vmDatacenter);
    }
    //
    // Get a list of datastores
    //
    string SelectedDataCenter = cboClusters.SelectedItem.Text;
    NameValueCollection thisDcFilter = new NameValueCollection();
    thisDcFilter.Add("name", SelectedDataCenter);
    EntityViewBase appDC = vimClient.FindEntityView(typeof(Datacenter), null,thisDcFilter, null);
    Datacenter thisDC = (Datacenter)appDC;
    ManagedObjectReference[] DSmoRefs = thisDC.Datastore;
    foreach (ManagedObjectReference DSmoRef in DSmoRefs)
    {
        //
        // Commented code is faster, but doesn't yield the proper name
        //
        // Datastore thisDatastore = new Datastore(vimClient,DSmoRef);
        // DistributedVirtualPortgroup thisPortGroup = new DistributedVirtualPortgroup(vimClient,PGmoRef);
        //
        Datastore thisDatastore = (Datastore)vimClient.GetView(DSmoRef,null);
        ListItem vmDatastore = new ListItem();
        vmDatastore.Text = thisDatastore.Info.Name;
        vmDatastore.Value = thisDatastore.MoRef.ToString();
        cboDatastores.Items.Add(vmDatastore);
    }
      
    //
    // Get a list of network portgroups
    //
    ManagedObjectReference[] PGmoRefs = thisDC.Network;
    foreach (ManagedObjectReference PGmoRef in PGmoRefs)
    {
        DistributedVirtualPortgroup thisPortGroup = (DistributedVirtualPortgroup)vimClient.GetView(PGmoRef,null);
        ListItem vmPortGroup = new ListItem();
        vmPortGroup.Text = thisPortGroup.Summary.Name;
        vmPortGroup.Value = thisPortGroup.MoRef.ToString();
        cboNetwork.Items.Add(vmPortGroup);
    }
    //
    // Get a list of hosts in the cluster
    //
    ManagedObjectReference hostFolderMoRef = thisDC.HostFolder;
    Folder hostFolder = (Folder)vimClient.GetView(hostFolderMoRef,null);
    ManagedObjectReference[] childEntityMoRefs = hostFolder.ChildEntity;
    foreach (ManagedObjectReference childEntityMoRef in childEntityMoRefs)
    {
        ClusterComputeResource thisCluster = (ClusterComputeResource)vimClient.GetView(childEntityMoRef,null);
        ManagedObjectReference[] clusterHostMoRefs = thisCluster.Host;
        foreach (ManagedObjectReference clusterHostMoRef in clusterHostMoRefs)
        {
            HostSystem hostSystem = (HostSystem)vimClient.GetView(clusterHostMoRef,null);
            txtResult.Text += hostSystem.Name + "\r\n";
        }
    }
       
    //
    // Get a list of OS Customizations
    //
    CustomizationSpecManager specManager = (CustomizationSpecManager)vimClient.GetView(vimServiceContent.CustomizationSpecManager, null);
    foreach (CustomizationSpecInfo specInfo in specManager.Info)
    {
        ListItem vmSpecfile = new ListItem();
        vmSpecfile.Text = specInfo.Name;
        vmSpecfile.Value = specInfo.Type;
        cboCustomization.Items.Add(vmSpecfile);
    }
    //
    // Get a list of clones
    //
    NameValueCollection cloneFilter = new NameValueCollection();
    cloneFilter.Add("name", WebConfigurationManager.AppSettings["clonePrefix"].ToString());
    List<EntityViewBase> appVirtualMachines = vimClient.FindEntityViews(typeof(VirtualMachine), null, cloneFilter, null);
    foreach (EntityViewBase appVirtualMachine in appVirtualMachines)
    {
        VirtualMachine thisVirtualMachine = (VirtualMachine)appVirtualMachine;
        ListItem vmName = new ListItem();
        vmName.Text = thisVirtualMachine.Name;
        vmName.Value = thisVirtualMachine.MoRef.ToString();
        cboClones.Items.Add(vmName);
    }
    vimClient.Disconnect();
}
Jeffrey S. Patton Systems Specialist, Enterprise Systems University of Kansas 1001 Sunnyside Ave. Lawrence, KS. 66045 http://patton-tech.com
Reply
0 Kudos
jeffpatton
Enthusiast
Enthusiast

More forward progress today, it feels as though I'm very close to having a working POC. Going to post up a couple of links that I've found that have helped. The last part of this that I'm working on is the cloning, so these links are related to that.

http://www.vmdev.info/?p=202

http://communities.vmware.com/thread/140504

string selectedVMname = cboClones.SelectedItem.ToString();
string selectedDatastore = cboDatastores.SelectedItem.ToString();
string SelectedDataCenter = cboDatacenters.SelectedItem.ToString();
string selectedPortGroupsmoRef = cboPortGroups.SelectedValue.ToString();
string selectedCustomization = cboCustomizations.SelectedItem.ToString();
List<ManagedObjectReference> hostCollection = new List<ManagedObjectReference>();
Random rand = new Random();
ManagedObjectReference randomHost;
sViServer = txtViServer.Text.ToString();
sUsername = txtUsername.Text.ToString();
sPassword = txtPassword.Text.ToString();
vimClient.Connect("https://" + this.sViServer + "/sdk");
vimSession = vimClient.Login(this.sUsername, this.sPassword);
vimServiceContent = vimClient.ServiceContent;
      
//
// Connect to datacenter
//
NameValueCollection dcFilter = new NameValueCollection();
dcFilter.Add("name", SelectedDataCenter);
EntityViewBase appDC = vimClient.FindEntityView(typeof(Datacenter), null, dcFilter, null);
Datacenter thisDC = (Datacenter)appDC;
//
// Get a list of hosts in the cluster
//
ManagedObjectReference hostFolderMoRef = thisDC.HostFolder;
Folder hostFolder = (Folder)vimClient.GetView(hostFolderMoRef, null);
ManagedObjectReference[] childEntityMoRefs = hostFolder.ChildEntity;
foreach (ManagedObjectReference childEntityMoRef in childEntityMoRefs)
{
    ClusterComputeResource thisCluster = (ClusterComputeResource)vimClient.GetView(childEntityMoRef, null);
    ManagedObjectReference[] clusterHostMoRefs = thisCluster.Host;
    foreach (ManagedObjectReference clusterHostMoRef in clusterHostMoRefs)
    {
        HostSystem hostSystem = (HostSystem)vimClient.GetView(clusterHostMoRef, null);
        hostCollection.Add(hostSystem.MoRef);
    }
}
//
// Connect to selected vm to clone
//
NameValueCollection vmFilter = new NameValueCollection();
vmFilter.Add("Name", selectedVMname);
EntityViewBase thisVm = vimClient.FindEntityView(typeof(VirtualMachine), null, vmFilter, null);
VirtualMachine sourceVm = (VirtualMachine)thisVm;
//
// Randomly pick host from collection for folder
//
randomHost = hostCollection[rand.Next(0,hostCollection.Count)];
HostSystem selectedHost = (HostSystem)vimClient.GetView(randomHost, null);
//
// Connect to the datastore
//
NameValueCollection dsFilter = new NameValueCollection();
dsFilter.Add("name", selectedDatastore);
EntityViewBase myDs = vimClient.FindEntityView(typeof(Datastore), null, dsFilter, null);
Datastore selectedStore = (Datastore)myDs;
//
// Connect to the customizationspec
//
CustomizationSpecManager specManager = (CustomizationSpecManager)vimClient.GetView(vimServiceContent.CustomizationSpecManager, null);
CustomizationSpecItem thisSpec = specManager.GetCustomizationSpec(selectedCustomization);
       
VirtualMachineCloneSpec mySpec = new VirtualMachineCloneSpec();
//mySpec.Location.Datastore = selectedStore.MoRef;
mySpec.Location = new VirtualMachineRelocateSpec();
mySpec.Location.Host = randomHost;
mySpec.Customization = thisSpec.Spec;
       
//
// Perform the clone
//
//sourceVm.CloneVM_Task(sourceVm.Parent, "my-sample-vm", mySpec);
vimClient.Disconnect();

The line

//mySpec.Location.Datastore = selectedStore.MoRef;

Appears to be sticking point at the moment, I thought it was expecting a moref of the datastore, so when I pass that in I get an error

Object reference not set to an instance of an object, oh well it's quite close though.

Jeffrey S. Patton Systems Specialist, Enterprise Systems University of Kansas 1001 Sunnyside Ave. Lawrence, KS. 66045 http://patton-tech.com
jeffpatton
Enthusiast
Enthusiast

Gah! error previous was ordering, I was instantiating the location object AFTER i tried to add on object to it..sigh

Jeffrey S. Patton Systems Specialist, Enterprise Systems University of Kansas 1001 Sunnyside Ave. Lawrence, KS. 66045 http://patton-tech.com
Reply
0 Kudos
jeffpatton
Enthusiast
Enthusiast

The hit's just keep coming, the two threads below were very helpful in attempting to connecting a clone to a specific port group. I don't kow if this will technically work, and at the moment it's not as asthetically pleasing as I'd like it to be, but I wanted to get this up before I head out.


http://communities.vmware.com/thread/267542

http://communities.vmware.com/message/1298627

string selectedVMname = cboClones.SelectedItem.ToString();
string selectedDatastore = cboDatastores.SelectedItem.ToString();
string SelectedDataCenter = cboDatacenters.SelectedItem.ToString();
string selectedPortGroup = cboPortGroups.SelectedItem.ToString();
string selectedCustomization = cboCustomizations.SelectedItem.ToString();
List<ManagedObjectReference> hostCollection = new List<ManagedObjectReference>();
Random rand = new Random();
ManagedObjectReference randomHost;
VirtualDevice networkDevice = new VirtualDevice();
sViServer = txtViServer.Text.ToString();
sUsername = txtUsername.Text.ToString();
sPassword = txtPassword.Text.ToString();
vimClient.Connect("https://" + this.sViServer + "/sdk");
vimSession = vimClient.Login(this.sUsername, this.sPassword);
vimServiceContent = vimClient.ServiceContent;
//
// Connect to datacenter
//
NameValueCollection dcFilter = new NameValueCollection();
dcFilter.Add("name", SelectedDataCenter);
EntityViewBase appDC = vimClient.FindEntityView(typeof(Datacenter), null, dcFilter, null);
Datacenter thisDC = (Datacenter)appDC;
//
// Get a list of hosts in the cluster
//
ManagedObjectReference hostFolderMoRef = thisDC.HostFolder;
Folder hostFolder = (Folder)vimClient.GetView(hostFolderMoRef, null);
ManagedObjectReference[] childEntityMoRefs = hostFolder.ChildEntity;
foreach (ManagedObjectReference childEntityMoRef in childEntityMoRefs)
{
    ClusterComputeResource thisCluster = (ClusterComputeResource)vimClient.GetView(childEntityMoRef, null);
    ManagedObjectReference[] clusterHostMoRefs = thisCluster.Host;
    foreach (ManagedObjectReference clusterHostMoRef in clusterHostMoRefs)
    {
        HostSystem hostSystem = (HostSystem)vimClient.GetView(clusterHostMoRef, null);
        hostCollection.Add(hostSystem.MoRef);
    }
}
//
// Connect to selected vm to clone
//
NameValueCollection vmFilter = new NameValueCollection();
vmFilter.Add("Name", selectedVMname);
EntityViewBase thisVm = vimClient.FindEntityView(typeof(VirtualMachine), null, vmFilter, null);
VirtualMachine sourceVm = (VirtualMachine)thisVm;
//
// Randomly pick host from collection for folder
//
randomHost = hostCollection[rand.Next(0,hostCollection.Count)];
HostSystem selectedHost = (HostSystem)vimClient.GetView(randomHost, null);
//
// Connect to the datastore
//
NameValueCollection dsFilter = new NameValueCollection();
dsFilter.Add("name", selectedDatastore);
EntityViewBase myDs = vimClient.FindEntityView(typeof(Datastore), null, dsFilter, null);
Datastore selectedStore = (Datastore)myDs;
//
// Connect to portgroup
//
NameValueCollection pgFilter = new NameValueCollection();
pgFilter.Add("name", selectedPortGroup);
EntityViewBase myPg = vimClient.FindEntityView(typeof(DistributedVirtualPortgroup), null, pgFilter, null);
DistributedVirtualPortgroup selectedPg = (DistributedVirtualPortgroup)myPg;
//
// Connect to the customizationspec
//
CustomizationSpecManager specManager = (CustomizationSpecManager)vimClient.GetView(vimServiceContent.CustomizationSpecManager, null);
CustomizationSpecItem thisSpec = specManager.GetCustomizationSpec(selectedCustomization);
SeSparseVirtualDiskSpec diskSpec = new SeSparseVirtualDiskSpec();
diskSpec.DiskType = "thin";
VirtualMachineCloneSpec mySpec = new VirtualMachineCloneSpec();
mySpec.Location = new VirtualMachineRelocateSpec();
mySpec.Location.Datastore = selectedStore.MoRef;
mySpec.Location.Transform = VirtualMachineRelocateTransformation.sparse;
mySpec.Location.Host = randomHost;
mySpec.Customization = thisSpec.Spec;
mySpec.Config = new VirtualMachineConfigSpec();
// get nic on vm
foreach (VirtualDevice vDevice in sourceVm.Config.Hardware.Device)
{
    if (vDevice.DeviceInfo.Label.Contains("network"))
    {
        networkDevice = vDevice;
    }
}
VirtualDeviceConfigSpec devSpec = new VirtualDeviceConfigSpec();
devSpec.Device = networkDevice;
VirtualEthernetCardNetworkBackingInfo nicBack = new VirtualEthernetCardNetworkBackingInfo();
nicBack.DeviceName = cboPortGroups.SelectedItem.Text;
nicBack.Network = selectedPg.MoRef;
devSpec.FileOperation = VirtualDeviceConfigSpecFileOperation.replace;
devSpec.Device.Backing = nicBack;
mySpec.Config.DeviceChange[0] = devSpec;
//
// Perform the clone
//
//sourceVm.CloneVM_Task(sourceVm.Parent, "my-sample-vm", mySpec);
vimClient.Disconnect();
Jeffrey S. Patton Systems Specialist, Enterprise Systems University of Kansas 1001 Sunnyside Ave. Lawrence, KS. 66045 http://patton-tech.com
Reply
0 Kudos
jeffpatton
Enthusiast
Enthusiast

I've re-written the spaghetti i've been using into more formal functions, enjoy. If you use these make sure you add a reference to vmware.vim.dll and add the following two statements to the top

using System.Collections.Specialized;

using VMware.Vim;

protected VimClient ConnectServer(string viServer, string viUser, string viPassword)
{
    VimClient vimClient = new VimClient();
    ServiceContent vimServiceContent = new ServiceContent();
    UserSession vimSession = new UserSession();
    vimClient.Connect("https://" + viServer.Trim() + "/sdk");
    vimSession = vimClient.Login(viUser, viPassword);
    vimServiceContent = vimClient.ServiceContent;
    return vimClient;
}
protected List<Datacenter> GetDataCenter(VimClient vimClient, string dcName = null)
{
    List<Datacenter> lstDatacenters = new List<Datacenter>();
    List<EntityViewBase> appDatacenters = new List<EntityViewBase>();
    if (dcName == null)
    {
        appDatacenters = vimClient.FindEntityViews(typeof(Datacenter), null, null, null);
    }
    else
    {
        NameValueCollection dcFilter = new NameValueCollection();
        dcFilter.Add("name", dcName);
        appDatacenters = vimClient.FindEntityViews(typeof(Datacenter), null, dcFilter, null);
    }
    foreach (EntityViewBase appDatacenter in appDatacenters)
    {
        Datacenter thisDatacenter = (Datacenter)appDatacenter;
        lstDatacenters.Add(thisDatacenter);
    }
    return lstDatacenters;
}
protected List<Datastore> GetDataStore(VimClient vimClient, Datacenter selectedDC)
{
    List<Datastore> lstDatastores = new List<Datastore>();
    List<EntityViewBase> appDatastores = vimClient.FindEntityViews(typeof(Datastore), selectedDC.MoRef, null, null);
    foreach (EntityViewBase appDatastore in appDatastores)
    {
        Datastore thisDatastore = (Datastore)appDatastore;
        lstDatastores.Add(thisDatastore);
    }
    return lstDatastores;
}
protected List<DistributedVirtualPortgroup> GetDVPortGroups(VimClient vimClient, Datacenter selectedDC)
{
    List<DistributedVirtualPortgroup> lstPortGroups = new List<DistributedVirtualPortgroup>();
    List<EntityViewBase> appPortGroups = vimClient.FindEntityViews(typeof(DistributedVirtualPortgroup), selectedDC.MoRef, null, null);
    if (appPortGroups != null)
    {
        foreach (EntityViewBase appPortGroup in appPortGroups)
        {
            DistributedVirtualPortgroup thisPortGroup = (DistributedVirtualPortgroup)appPortGroup;
            lstPortGroups.Add(thisPortGroup);
        }
        return lstPortGroups;
    }
    else
    {
        return null;
    }
}
protected List<Network> GetPortGroups(VimClient vimClient, Datacenter selectedDC)
{
    List<Network> lstPortGroups = new List<Network>();
    List<EntityViewBase> appPortGroups = vimClient.FindEntityViews(typeof(Network), selectedDC.MoRef, null, null);
    if (appPortGroups != null)
    {
        foreach (EntityViewBase appPortGroup in appPortGroups)
        {
            Network thisPortGroup = (Network)appPortGroup;
            lstPortGroups.Add(thisPortGroup);
        }
        return lstPortGroups;
    }
    else
    {
        return null;
    }
}
protected List<VirtualMachine> GetVirtualMachines(VimClient vimClient, Datacenter selectedDC = null, string vmName = null)
{
    List<VirtualMachine> lstVirtualMachines = new List<VirtualMachine>();
    NameValueCollection vmFilter = new NameValueCollection();
    ManagedObjectReference DcMoRef = new ManagedObjectReference();
    if (vmName != null)
    {
        vmFilter.Add("name", vmName);
    }
    else
    {
        vmFilter = null;
    }
    if (selectedDC != null)
    {
        DcMoRef = selectedDC.MoRef;
    }
    else
    {
        DcMoRef = null;
    }
    List<EntityViewBase> appVirtualMachines = vimClient.FindEntityViews(typeof(VirtualMachine), DcMoRef, vmFilter, null);
    if (appVirtualMachines != null)
    {
        foreach (EntityViewBase appVirtualMachine in appVirtualMachines)
        {
            VirtualMachine thisVirtualMachine = (VirtualMachine)appVirtualMachine;
            lstVirtualMachines.Add(thisVirtualMachine);
        }
        return lstVirtualMachines;
    }
    else
    {
        return null;
    }
}
protected List<CustomizationSpecInfo> GetCustomizationSpecs(VimClient vimClient)
{
    List<CustomizationSpecInfo> lstSpecInfo = new List<CustomizationSpecInfo>();
    CustomizationSpecManager specManager = (CustomizationSpecManager)vimClient.GetView(vimClient.ServiceContent.CustomizationSpecManager, null);
    if (specManager != null)
    {
        foreach (CustomizationSpecInfo specInfo in specManager.Info)
        {
            lstSpecInfo.Add(specInfo);
        }
        return lstSpecInfo;
    }
    else
    {
        return null;
    }
}
Jeffrey S. Patton Systems Specialist, Enterprise Systems University of Kansas 1001 Sunnyside Ave. Lawrence, KS. 66045 http://patton-tech.com
Reply
0 Kudos
jeffpatton
Enthusiast
Enthusiast

Three more functions to go with the previously posted ones.

protected List<ClusterComputeResource> GetClusters(VimClient vimClient, string clusterName = null)
{
    List<ClusterComputeResource> lstClusters = new List<ClusterComputeResource>();
    List<EntityViewBase> appClusters = new List<EntityViewBase>();
    if (clusterName == null)
    {
        appClusters = vimClient.FindEntityViews(typeof(ClusterComputeResource), null, null, null);
    }
    else
    {
        NameValueCollection clusterFilter = new NameValueCollection();
        clusterFilter.Add("name", clusterName);
        appClusters = vimClient.FindEntityViews(typeof(ClusterComputeResource), null, clusterFilter, null);
    }
    if (appClusters != null)
    {
        foreach (EntityViewBase appCluster in appClusters)
        {
            ClusterComputeResource thisCluster = (ClusterComputeResource)appCluster;
            lstClusters.Add(thisCluster);
        }
        return lstClusters;
    }
    else
    {
        return null;
    }
}
protected List<HostSystem> GetHosts(VimClient vimClient, string hostParent = null)
{
    List<HostSystem> lstHosts = new List<HostSystem>();
    List<EntityViewBase> appHosts = new List<EntityViewBase>();
    if (hostParent == null)
    {
        appHosts = vimClient.FindEntityViews(typeof(HostSystem), null, null, null);
    }
    else
    {
        NameValueCollection hostFilter = new NameValueCollection();
        hostFilter.Add("parent", hostParent);
        appHosts = vimClient.FindEntityViews(typeof(HostSystem), null, hostFilter, null);
    }
    if (appHosts != null)
    {
        foreach (EntityViewBase appHost in appHosts)
        {
            HostSystem thisHost = (HostSystem)appHost;
            lstHosts.Add(thisHost);
        }
        return lstHosts;
    }
    else
    {
        return null;
    }
}
protected List<Datacenter> GetDcFromCluster(VimClient vimClient, string clusterParent)
{
    List<Datacenter> lstDataCenters = new List<Datacenter>();
    NameValueCollection parentFilter = new NameValueCollection();
    parentFilter.Add("hostFolder", clusterParent);
    List<EntityViewBase> arrDataCenters = vimClient.FindEntityViews(typeof(Datacenter), null, parentFilter, null);
    if (arrDataCenters != null)
    {
        foreach (EntityViewBase arrDatacenter in arrDataCenters)
        {
            Datacenter thisDatacenter = (Datacenter)arrDatacenter;
            lstDataCenters.Add(thisDatacenter);
        }
        return lstDataCenters;
    }
    else
    {
        return null;
    }
}
Jeffrey S. Patton Systems Specialist, Enterprise Systems University of Kansas 1001 Sunnyside Ave. Lawrence, KS. 66045 http://patton-tech.com
Reply
0 Kudos
jeffpatton
Enthusiast
Enthusiast

I've updated some of my functions so I could pull them in by name

protected List<Datastore> GetDataStore(VimClient vimClient, Datacenter selectedDC = null, string dsName = null)
{
    List<Datastore> lstDatastores = new List<Datastore>();
    NameValueCollection dsFilter = new NameValueCollection();
    ManagedObjectReference DcMoRef = new ManagedObjectReference();
    if (dsName != null)
    {
        dsFilter.Add("name", dsName);
    }
    else
    {
        dsFilter = null;
    }
    if (selectedDC != null)
    {
        DcMoRef = selectedDC.MoRef;
    }
    else
    {
        DcMoRef = null;
    }
    List<EntityViewBase> appDatastores = vimClient.FindEntityViews(typeof(Datastore), DcMoRef, dsFilter, null);
    if (appDatastores != null)
    {
        foreach (EntityViewBase appDatastore in appDatastores)
        {
            Datastore thisDatastore = (Datastore)appDatastore;
            lstDatastores.Add(thisDatastore);
        }
        return lstDatastores;
    }
    else
    {
        return null;
    }
}
protected List<DistributedVirtualPortgroup> GetDVPortGroups(VimClient vimClient, Datacenter selectedDC = null, string pgName = null) {     List<DistributedVirtualPortgroup> lstPortGroups = new List<DistributedVirtualPortgroup>();     NameValueCollection pgFilter = new NameValueCollection();     ManagedObjectReference DcMoRef = new ManagedObjectReference();     if (pgName != null)     {         pgFilter.Add("name", pgName);     }     else     {         pgFilter = null;     }     if (selectedDC != null)     {         DcMoRef = selectedDC.MoRef;     }     else     {         DcMoRef = null;     }     List<EntityViewBase> appPortGroups = vimClient.FindEntityViews(typeof(DistributedVirtualPortgroup), DcMoRef, pgFilter, null);     if (appPortGroups != null)     {         foreach (EntityViewBase appPortGroup in appPortGroups)         {             DistributedVirtualPortgroup thisPortGroup = (DistributedVirtualPortgroup)appPortGroup;             lstPortGroups.Add(thisPortGroup);         }         return lstPortGroups;     }     else     {         return null;     } }
protected List<Network> GetPortGroups(VimClient vimClient, Datacenter selectedDC = null, string pgName = null) {     List<Network> lstPortGroups = new List<Network>();     NameValueCollection pgFilter = new NameValueCollection();     ManagedObjectReference DcMoRef = new ManagedObjectReference();     if (pgName != null)     {         pgFilter.Add("name", pgName);     }     else     {         pgFilter = null;     }     if (selectedDC != null)     {         DcMoRef = selectedDC.MoRef;     }     else     {         DcMoRef = null;     }     List<EntityViewBase> appPortGroups = vimClient.FindEntityViews(typeof(Network), DcMoRef, pgFilter, null);     if (appPortGroups != null)     {         foreach (EntityViewBase appPortGroup in appPortGroups)         {             Network thisPortGroup = (Network)appPortGroup;             lstPortGroups.Add(thisPortGroup);         }         return lstPortGroups;     }     else     {         return null;     } }
protected List<CustomizationSpecInfo> GetCustomizationSpecs(VimClient vimClient, string specName = null) {     List<CustomizationSpecInfo> lstSpecInfo = new List<CustomizationSpecInfo>();     CustomizationSpecManager specManager = (CustomizationSpecManager)vimClient.GetView(vimClient.ServiceContent.CustomizationSpecManager, null);     if (specName == null)     {         if (specManager != null)         {             foreach (CustomizationSpecInfo specInfo in specManager.Info)             {                 lstSpecInfo.Add(specInfo);             }             return lstSpecInfo;         }         else         {             return null;         }     }     else     {         if (specManager != null)         {             CustomizationSpecItem specItem = specManager.GetCustomizationSpec(specName);             lstSpecInfo.Add(specItem.Info);             return lstSpecInfo;         }         else         {             return null;         }     } }
Jeffrey S. Patton Systems Specialist, Enterprise Systems University of Kansas 1001 Sunnyside Ave. Lawrence, KS. 66045 http://patton-tech.com
Reply
0 Kudos
jeffpatton
Enthusiast
Enthusiast

I clicked post too soon, looks like I need an actual CustomizationSpecItem for the cloning, so a revert to the old specinfo fuction and a new specitem function.

sorry about that.

protected List<CustomizationSpecInfo> GetCustomizationSpecs(VimClient vimClient)
{
    List<CustomizationSpecInfo> lstSpecInfo = new List<CustomizationSpecInfo>();
    CustomizationSpecManager specManager = (CustomizationSpecManager)vimClient.GetView(vimClient.ServiceContent.CustomizationSpecManager, null);
    if (specManager != null)
    {
        foreach (CustomizationSpecInfo specInfo in specManager.Info)
        {
            lstSpecInfo.Add(specInfo);
        }
        return lstSpecInfo;
    }
    else
    {
        return null;
    }
}
protected CustomizationSpecItem GetCustomizationSpecItem(VimClient vimClient, string specName = null)
{
    CustomizationSpecManager specManager = (CustomizationSpecManager)vimClient.GetView(vimClient.ServiceContent.CustomizationSpecManager, null);
    if (specManager != null)
    {
        CustomizationSpecItem itmCustomizationSpecItem = specManager.GetCustomizationSpec(specName);
        return itmCustomizationSpecItem;
    }
    else
    {
        return null;
    }
}
Jeffrey S. Patton Systems Specialist, Enterprise Systems University of Kansas 1001 Sunnyside Ave. Lawrence, KS. 66045 http://patton-tech.com
Reply
0 Kudos
jeffpatton
Enthusiast
Enthusiast

Well the POC works, I'm currently cloning a VM using the vmware.vim.dll and nothing else...big achievement for myself. It seems to me that documentation on this is very lacking but what do I know, it could be I just don't know how to search the internets 😉

Before you use this code, please check it against your environment and make sure that it works. All the functions just return information from your environment and don't write anything, but use this at your own risk. Always check code that you download from the internet.

You will need to add a reference to the following files for this to work properly

C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.Vim.dll

C:\Windows\assembly\GAC_MSIL\VimService51\5.1.0.0__10980b081e887e9f\VimService51.dll

C:\Windows\assembly\GAC_MSIL\VimService51.XmlSerializers\5.1.0.0__10980b081e887e9f\VimService51.XmlSerializers.dll

There may be other vimservice files in the GAC that you may need to include, but I think these are the only ones I'm using. But both my dev box and production box have more or less the same software installed so I can't make any guarantees. If you receive a nasty error for file not found, most likely you will need to add additional .dlls from the vimservice in the GAC.

Ok, I would consider this code to be beta level quality, there is no real error handling, all I really do is return null if something doesn't yield any results. Additionally I noticed some interesting behaviour. It seems that the Network and DistributedVirtualPortGroup will actually return the same things. At least in our environment both of those will return the virtual "networks" attached to a given host/cluster. I can confirm that the DistributedVirtualPortGroup will error out when attempting to get a list of ports from a standalone host, as we have a standalone host datacenter that I tested many of these functions against. So, for the moment if you always want to get an answer you may want to use just the GetPortGroups function.

I'll most likely post more details on my personal blog (http://patton-tech.com) I actually have a couple of posts queued up, and this would be a good one. But here is a list of the functions in the attachment and basic usage.

protected VimClient ConnectServer(string viServer, string viUser, string viPassword)

This function will connect to a given server, for my code I load this up from the web.config file

and pass in user/pass from text box controls. This returns a connection object that I pass around,

I would imagine you could pass the session around via cookies, but this is very quick and I close

the connection after each use.

string sViServer = WebConfigurationManager.AppSettings["viServer"].ToString();

protected List<Datacenter> GetDataCenter(VimClient vimClient, string dcName = null)

All subsequent functions require the connection object to work, I usually do the connect at the beginning.

You will notice you can pass in the name of a Datacenter if you have it, if so a filter is applied and

only that DC is returned. If you leave it out, you will get a list of all DC's in your environment.

protected List<Datastore> GetDataStore(VimClient vimClient, Datacenter selectedDC = null, string dsName = null)

This function will return all datastores, you can pass in a specific DC to get a list of stores on it, or

pass in the name of the DS to get just that object.

protected List<DistributedVirtualPortgroup> GetDVPortGroups(VimClient vimClient, Datacenter selectedDC = null, string pgName = null)

This is the portgroup function I mentioned earlier, it returns all DVPortGroups, or just ones on a specific

DC or if provided a name, just that DVPortGroup.

protected List<Network> GetPortGroups(VimClient vimClient, Datacenter selectedDC = null, string pgName = null)

This is the other portgroup function and works identically to the previous function.

protected List<VirtualMachine> GetVirtualMachines(VimClient vimClient, Datacenter selectedDC = null, string vmName = null)

This function will return all VM's, or VM's on a given DC, or a specific VM based on name. I use

this function to give me a list of our clone servers by reading in a regex from my web.config file.

List<VirtualMachine> lstVirtualMachines = GetVirtualMachines(vimClient, null, WebConfigurationManager.AppSettings["clonePrefix"].ToString());

protected List<CustomizationSpecInfo> GetCustomizationSpecs(VimClient vimClient)

This function will return CustomizationSpecInfo objects, which are much different than actual

CustomizationSpec objects. I use this to populate a dropdown with a list of customizations and

their types.

protected CustomizationSpecItem GetCustomizationSpecItem(VimClient vimClient, string specName = null)

This function will return the CustomizationSpecItem, you can ask for all, but I use this to get the

selected CustomizationSpec from a dropdown.

protected List<ClusterComputeResource> GetClusters(VimClient vimClient, string clusterName = null)

This function will return a list of clusters in the environment, or a specific cluster by name. This

one took a bit to figure out because there doesn't appear to be any real relationship between a cluster

and a DataCenter. These seem to be used more or less interchangably, but since we only want folks to

put VM's in one of the clusters I wrote this function for that purpose.

protected List<HostSystem> GetHosts(VimClient vimClient, string hostParent = null)

This function will return a list of hosts, or a specific host from a given parent, where a parent is either

the Parent property (MoRef) of a cluster or datacenter. This is used in the clone process as the ultimate

location of a VM, I use the .Net Random function to pick one randomly.

protected List<Datacenter> GetDcFromCluster(VimClient vimClient, string clusterParent)

This function you will notice requires the clusterParent (moRef) I use these as strings because for the most

part they are stored as string objects in the .Value property of the dropdowns. This function will return

the Datacenter object that a given cluster is connected to.

Jeffrey S. Patton Systems Specialist, Enterprise Systems University of Kansas 1001 Sunnyside Ave. Lawrence, KS. 66045 http://patton-tech.com