Replacing Witnesses of Multiple ROBO Clusters with a Single Shared Witness

You can replace witnesses of multiple ROBO clusters with a single shared witness.

Here is an example for replacing in batches, multiple witnesses of ROBO clusters with a single shared witness:

def replaceWitnessInBatch(si, vscs, witness, clusterRefs):
   """ Replace witness with the same Shared witness in batches for
       for multiple robo clusters in one operation.

   Requirements:
      1. The candidate cluster must be vSAN robo cluster.
      2. There is no network isolation between witness and the multiple
      clusters given.
   """
   checkCompatibility(si, vscs, clusterRefs, witness)
   for cluster in clusterRefs:
      if len(vscs.GetWitnessHosts(cluster)) != 1:
         msg = "ERROR: cluster %s is not a robo cluster" % cluster.name
         sys.exit(msg)
   print("Replacing the old witness(es) with shared witness %s" \
         " for clusters: %s" % (witness.name,
         [cluster.name for cluster in clusterRefs]))
   spec = vim.vsan.VsanVcStretchedClusterConfigSpec(
      witnessHost = witness,
      clusters = [vim.cluster.VsanStretchedClusterConfig(
         cluster = cluster
      ) for cluster in clusterRefs]
   )
   replaceWitnessTask = vscs.ReplaceWitnessHostForClusters(spec)
   vsanapiutils.WaitForTasks([replaceWitnessTask], si)

def removeWitnessForClusters(si, vscs, witness, clusterRefs):
   totalTasks = []
   for cluster in clusterRefs:
      print("Removing witness %s from cluster %s" % \
            (witness.name, cluster.name))
      removeTask = vscs.RemoveWitnessHost(cluster, witness)
      totalTasks.append(vsanapiutils.ConvertVsanTaskToVcTask(
                        removeTask, si._stub))
   vsanapiutils.WaitForTasks(totalTasks, si)

def getWitnessClusters(si, vscs, witness):
   clusterNames = []
   getWitnessClustrs = vscs.QueryWitnessHostClusterInfo(witness)
   for cluster in getWitnessClustrs:
      clusterMo = vim.ClusterComputeResource(cluster.cluster._moId, si._stub)
      clusterNames.append(clusterMo.name)
   return clusterNames

class LogWitnessStatus(object):
   def __init__(self, si, vscs, witness):
      self.si = si
      self.vscs = vscs
      self.witness = witness

   def __enter__(self):
      print("Before Ops: shared witness %s has joined the following clusters:"
            " %s" % (self.witness.name,
            getWitnessClusters(self.si, self.vscs, self.witness)))

   def __exit__(self, *a):
      print("After Ops: Now shared witness %s has joined the following"
            " clusters: %s" % (self.witness.name,
            getWitnessClusters(self.si, self.vscs, self.witness)))

def main():
   args = GetArgs()
   if args.password:
      password = args.password
   else:
      password = getpass.getpass(prompt='Enter password for host %s and '
                                        'user %s: ' % (args.host,args.user))
   # For python 2.7.9 and later, the default SSL context has more strict
   # connection handshaking rule. We may need turn off the hostname checking
   # and client side cert verification.
   context = None
   if sys.version_info[:3] > (2,7,8):
      context = ssl.create_default_context()
      context.check_hostname = False
      context.verify_mode = ssl.CERT_NONE

   si = SmartConnect(host=args.host,
                     user=args.user,
                     pwd=password,
                     port=int(args.port),
                     sslContext=context)
   atexit.register(Disconnect, si)

   # Detecting whether the host is vCenter or ESXi.
   aboutInfo = si.content.about
   apiVersion = vsanapiutils.GetLatestVmodlVersion(args.host)

   if aboutInfo.apiType == 'VirtualCenter':
      majorApiVersion = aboutInfo.apiVersion
      if LooseVersion(majorApiVersion) < LooseVersion('7.0.1'):
         msg = "The Virtual Center with version %s (lower than 7.0U1) is not "\
               "supported." % aboutInfo.apiVersion
         sys.exit(msg)
      # Get vSAN health system from the vCenter Managed Object references.
      vcMos = vsanapiutils.GetVsanVcMos(
            si._stub, context=context, version=apiVersion)
      vscs = vcMos['vsan-stretched-cluster-system']

      witness = getComputeInstance(args.witness, si)
      if not witness:
         msg = 'Given witness host %s is not found in %s' % \
               (args.witness, args.vc)
         sys.exit(msg)
      witness = witness.host[0]
      allClusters= []
      if args.roboClusters:
         roboClusters = [clusterName.strip() for clusterName \
                         in args.roboClusters.split(',')]
         roboClusters = getClusterInstances(roboClusters, si)
         allClusters.extend(roboClusters)
         with LogWitnessStatus(si, vscs, witness):
            replaceWitnessInBatch(si, vscs, witness, roboClusters)

      if args.normalClusters:
         twoNodesClusters = [clusterName.strip() for clusterName \
                             in args.normalClusters.split(',')]
         twoNodesClusters = getClusterInstances(twoNodesClusters, si)
         allClusters.extend(twoNodesClusters)
         with LogWitnessStatus(si, vscs, witness):
            convertToRoboClusterInBatch(si, vscs, witness, twoNodesClusters)

      with LogWitnessStatus(si, vscs, witness):
         removeWitnessForClusters(si, vscs, witness, allClusters)
   else:
      print('Remote host should be a Virtual Center ')
      return -1