Managing vSphere with pyVmomi - Part 4 - Adding a NFS share to a Cluster of Hosts

This post is part 4 of a small series of articles about how to use pyVMomi to build, configure and manage vmware vsphere environments.

For more information on the earlier posts please refer to Part 1, Part 2 or Part 3

Updating our YAML file

You will recall that the strategy for our sample program is to encapsulate the configuration we want to achieve in a simple YAML file.

In this section we will extend that YAML file structure such that NFS shared storage can be associated with the Clusters.

YAML configuration file

 1cat labconfig.yaml
 2lab:
 3    vsphere:
 4        host:
 5        -
 6                   ip: 192.168.10.101
 7                   hostname: "beast1.example.com"
 8                   user: root
 9                   pw: vmware123
10        -
11                   ip: 192.168.10.102
12                   hostname: "beast2.example.com"
13                   user: root
14                   pw: vmware123
15        vcenter:
16                   ip: 192.168.10.11
17                   user: administrator@vsphere.local
18                   pw: vmware
19        topology:
20        -          dc:
21                        name: LABDC
22                        clusters:
23                        -    name: LABC
24                             datastore:
25                                  type: nfs
26                                  ip:   192.168.10.66
27                                  path: /mnt/vol1/VMWARE-NFS
28                                  name: NAS
29                             members:
30                             -  ip: 192.168.10.101
31                             -  ip: 192.168.10.102
As you can see, a simple change has been made to associate a NAS share with the cluster.

Adding a NAS Datastore to a host

 1    def create_nasdatastore(self, host_name, nas_ip, nas_mount, datastore_name):
 2
 3            if datastore_name is None:
 4                raise ValueError("Missing value for datastore_name.")
 5            if nas_ip is None:
 6                raise ValueError("Missing value for nas_ip.")
 7            if nas_mount is None:
 8                raise ValueError("Missing value for nas_mount.")
 9            if host_name is None:
10                raise ValueError("Missing value for host_name.")
11
12            print(host_name)
13            host = self.get_obj([self.pyVmomi.vim.HostSystem], host_name)
14            datastore_spec = self.pyVmomi.vim.host.NasVolume.Specification()
15            datastore_spec.remoteHost=nas_ip
16            datastore_spec.remotePath=nas_mount
17            datastore_spec.localPath=datastore_name
18            datastore_spec.accessMode="readWrite"
19
20            datastore=host.configManager.datastoreSystem.CreateNasDatastore(datastore_spec)
21            return datastore

By now, you’re most likely getting the hang of the pyVmomi implementation of the vSphere API.
You can see from the above code we create a specification for a new NasVolume and then call the CreateNasDarastore mthod using that specification as input

Here is the updated program :

#!/usr/bin/python
"""
Using the pyVmomi python bindings for vSphere connect to a VCSA and configure Datacenters and Clusters
as per th spefified configuration file.
"""

import atexit
import yaml
import inspect
import time
import subprocess

class Vcenter(object):
    def __init__(self, vcenter_params):
        self.pyVmomi =  __import__("pyVmomi")
        self.server = vcenter_params['ip']
        self.username = vcenter_params['user']
        self.password = vcenter_params['pw']
        self.connect_to_vcenter()


    def create_datacenter(self, dcname=None, folder=None):
        datacenter = self.get_obj([self.pyVmomi.vim.Datacenter], dcname)
        if datacenter is not None:
            print("datacenter %s already exists" % dcname)
            return datacenter
        else:
            if len(dcname) > 79:
                raise ValueError("The name of the datacenter must be under 80 characters.")
            if folder is None:
                folder = self.service_instance.content.rootFolder
            if folder is not None and isinstance(folder, self.pyVmomi.vim.Folder):

                print("Creating Datacenter %s " % dcname )

                dc_moref = folder.CreateDatacenter(name=dcname)
                return dc_moref

    def create_cluster(self, cluster_name, datacenter):

        cluster = self.get_obj([self.pyVmomi.vim.ClusterComputeResource], cluster_name)
        if cluster is not None:
            print("cluster already exists")
            return cluster

        else:
            if cluster_name is None:
                raise ValueError("Missing value for name.")
            if datacenter is None:
                raise ValueError("Missing value for datacenter.")

            print("Creating Cluster %s " % cluster_name )

            cluster_spec = self.pyVmomi.vim.cluster.ConfigSpecEx()

            host_folder = datacenter.hostFolder
            cluster = host_folder.CreateClusterEx(name=cluster_name, spec=cluster_spec)
            return cluster

    def add_host(self, cluster_name, hostname, sslthumbprint,  username, password):
        host = self.get_obj([self.pyVmomi.vim.HostSystem], hostname)
        if host is not None:
            print("host already exists")
            return host
        else:
            if hostname is None:
                raise ValueError("Missing value for name.")
            cluster = self.get_obj([self.pyVmomi.vim.ClusterComputeResource], cluster_name)
            if cluster is None:
                error = 'Error - Cluster %s not found. Unable to add host %s' % (cluster_name, hostname)
                raise ValueError(error)

            try:
                hostspec = self.pyVmomi.vim.host.ConnectSpec(hostName=hostname,userName=username, sslThumbprint=sslthumbprint, password=password, force=True)
                task=cluster.AddHost(spec=hostspec,asConnected=True)
            except self.pyVmomi.vmodl.MethodFault as error:
                print "Caught vmodl fault : " + error.msg
                return -1
            self.wait_for_task(task)
            host = self.get_obj([self.pyVmomi.vim.HostSystem], hostname)
            return host

    def create_nasdatastore(self, host_name, nas_ip, nas_mount, datastore_name):

            if datastore_name is None:
                raise ValueError("Missing value for datastore_name.")
            if nas_ip is None:
                raise ValueError("Missing value for nas_ip.")
            if nas_mount is None:
                raise ValueError("Missing value for nas_mount.")
            if host_name is None:
                raise ValueError("Missing value for host_name.")

            print(host_name)
            host = self.get_obj([self.pyVmomi.vim.HostSystem], host_name)
            datastore_spec = self.pyVmomi.vim.host.NasVolume.Specification()
            datastore_spec.remoteHost=nas_ip
            datastore_spec.remotePath=nas_mount
            datastore_spec.localPath=datastore_name
            datastore_spec.accessMode="readWrite"

            datastore=host.configManager.datastoreSystem.CreateNasDatastore(datastore_spec)
            return datastore


    def get_obj(self, vimtype, name):
        """
        Get the vsphere object associated with a given text name
        """
        obj = None
        container = self.content.viewManager.CreateContainerView(self.content.rootFolder, vimtype, True)
        for c in container.view:
                if c.name == name:
                    obj = c
                    break
        return obj

    def wait_for_task(self, task):

        while task.info.state == (self.pyVmomi.vim.TaskInfo.State.running or self.pyVmomi.vim.TaskInfo.State.queued):
            time.sleep(2)

        if task.info.state == self.pyVmomi.vim.TaskInfo.State.success:
            if task.info.result is not None:
                out = 'Task completed successfully, result: %s' % (task.info.result,)
                print out
        elif task.info.state == self.pyVmomi.vim.TaskInfo.State.error:
            out = 'Error - Task did not complete successfully: %s' % (task.info.error,)
            raise ValueError(out)
        return task.info.result


    def connect_to_vcenter(self):
        from pyVim import connect

        print("Connecting to %s using username %s" % (self.server, self.username))
        self.service_instance = connect.SmartConnect(host=self.server,
                                                user=self.username,
                                                pwd=self.password,
                                                port=443)
        self.content = self.service_instance.RetrieveContent()
        about = self.service_instance.content.about
        print("Connected to %s, %s" % (self.server, about.fullName))
        atexit.register(connect.Disconnect, self.service_instance)

    def getsslThumbprint(self,ip):

        p1 = subprocess.Popen(('echo', '-n'), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        p2 = subprocess.Popen(('openssl', 's_client', '-connect', '{0}:443'.format(ip)), stdin=p1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        p3 = subprocess.Popen(('openssl', 'x509', '-noout', '-fingerprint', '-sha1'), stdin=p2.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        out = p3.stdout.read()
        ssl_thumbprint = out.split('=')[-1].strip()
        return ssl_thumbprint


def main():

    config = yaml.load(open("labconfig.yaml"))

    vc=Vcenter(config["lab"]["vsphere"]["vcenter"]);
    dc=config["lab"]["vsphere"]["topology"]
    hostlist = config['lab']['vsphere']['host']

    # for each datacenter, get the name and optionally if there is a key for clusters then create matching clusters
    # in this datacenter, otherwise just create the datacenter.

    for i in dc:
       datacenter = vc.create_datacenter(dcname=i['dc']['name'])
       if i['dc'].has_key('clusters') :
          for j in i['dc']['clusters']:
             cluster=vc.create_cluster(j['name'],datacenter)
             if j.has_key('members') :
                for k in j['members']:
                   iptoadd=None
                   user=None
                   pw=None
                   for l in hostlist:             # lookup the member details in the hosts section in the YAML to find the
                      if l['ip']==k['ip']:        #
                         iptoadd=l['ip']          #
                         user=l['user']           # user and pasword details
                         pw=l['pw']
                   if iptoadd == None:
                       print("Couldnt find credentials for ip %s" % k['ip'])
                   else:
                       sslthumbprint=vc.getsslThumbprint(iptoadd)
                       vc.add_host(j['name'],iptoadd,sslthumbprint,user,pw)
                       vc.create_nasdatastore(iptoadd,j['datastore']['ip'],j['datastore']['path'],j['datastore']['name'])
    return 0

# Start program
if __name__ == "__main__":
    main()

  1. Part 1 :- Connecting to the VCSA
  2. Part 2 :- Creating Datacenters and Clusters
  3. Part 3 :- Adding Hosts to Clusters
  4. Part 4 :- Adding a NFS Share to a Cluster of Hosts