github.com/vmware/govmomi@v0.51.0/cli/cluster/stretch.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package cluster
     6  
     7  import (
     8  	"context"
     9  	"flag"
    10  	"fmt"
    11  	"strings"
    12  
    13  	"github.com/vmware/govmomi/cli"
    14  	"github.com/vmware/govmomi/cli/flags"
    15  	"github.com/vmware/govmomi/object"
    16  	vim "github.com/vmware/govmomi/vim25/types"
    17  	"github.com/vmware/govmomi/vsan"
    18  	"github.com/vmware/govmomi/vsan/methods"
    19  	"github.com/vmware/govmomi/vsan/types"
    20  )
    21  
    22  type stretch struct {
    23  	*flags.DatacenterFlag
    24  
    25  	WitnessHost            string
    26  	FirstFaultDomainHosts  string
    27  	SecondFaultDomainHosts string
    28  	FirstFaultDomainName   string
    29  	SecondFaultDomainName  string
    30  	PreferredFaultDomain   string
    31  }
    32  
    33  func init() {
    34  	cli.Register("cluster.stretch", &stretch{})
    35  }
    36  
    37  func (cmd *stretch) Usage() string {
    38  	return "CLUSTER"
    39  }
    40  
    41  func (cmd *stretch) Description() string {
    42  	return `Convert a vSAN cluster into a stretched cluster
    43  
    44  The vSAN cluster is converted to a stretched cluster with a witness host
    45  specified by the 'witness' flag.  The datastore hosts are placed into one
    46  of two fault domains that are specified in each host list. The name of the
    47  preferred fault domain can be specified by the 'preferred-fault-domain' flag.
    48  
    49  Examples:
    50    govc cluster.stretch -dc remote-site-1 \
    51      -witness /dc-name/host/192.168.112.2 \
    52      -first-fault-domain-hosts 192.168.113.121 \
    53      -second-fault-domain-hosts 192.168.113.45,192.168.113.70 \
    54      cluster-name`
    55  }
    56  
    57  func (cmd *stretch) Register(ctx context.Context, f *flag.FlagSet) {
    58  	cmd.DatacenterFlag, ctx = flags.NewDatacenterFlag(ctx)
    59  	cmd.DatacenterFlag.Register(ctx, f)
    60  
    61  	f.StringVar(&cmd.WitnessHost, "witness", "", "Witness host for the stretched cluster")
    62  	f.StringVar(&cmd.FirstFaultDomainHosts, "first-fault-domain-hosts", "", "Hosts to place in the first fault domain")
    63  	f.StringVar(&cmd.SecondFaultDomainHosts, "second-fault-domain-hosts", "", "Hosts to place in the second fault domain")
    64  	f.StringVar(&cmd.FirstFaultDomainName, "first-fault-domain-name", "Primary", "Name of the first fault domain")
    65  	f.StringVar(&cmd.SecondFaultDomainName, "second-fault-domain-name", "Secondary", "Name of the second fault domain")
    66  	f.StringVar(&cmd.PreferredFaultDomain, "preferred-fault-domain", "Primary", "Name of the preferred fault domain")
    67  }
    68  
    69  func (cmd *stretch) Process(ctx context.Context) error {
    70  	if err := cmd.DatacenterFlag.Process(ctx); err != nil {
    71  		return err
    72  	}
    73  
    74  	if cmd.WitnessHost == "" ||
    75  		cmd.FirstFaultDomainHosts == "" ||
    76  		cmd.FirstFaultDomainName == "" ||
    77  		cmd.SecondFaultDomainHosts == "" ||
    78  		cmd.SecondFaultDomainName == "" ||
    79  		cmd.PreferredFaultDomain == "" {
    80  		return flag.ErrHelp
    81  	}
    82  
    83  	return nil
    84  }
    85  
    86  func (cmd *stretch) Run(ctx context.Context, f *flag.FlagSet) error {
    87  	if f.NArg() != 1 {
    88  		return flag.ErrHelp
    89  	}
    90  
    91  	client, err := cmd.Client()
    92  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	vsanClient, err := vsan.NewClient(ctx, client)
    97  	if err != nil {
    98  		return err
    99  	}
   100  
   101  	finder, err := cmd.Finder()
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	clusterResource, err := finder.ClusterComputeResource(ctx, f.Arg(0))
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	witnessHost, err := finder.HostSystem(ctx, cmd.WitnessHost)
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	faultDomainConfig, err := cmd.buildFaultDomainConfig(ctx)
   117  	if err != nil {
   118  		return err
   119  	}
   120  
   121  	req := types.VSANVcConvertToStretchedCluster{
   122  		This:              vsan.VsanVcStretchedClusterSystem,
   123  		Cluster:           clusterResource.Reference(),
   124  		FaultDomainConfig: *faultDomainConfig,
   125  		WitnessHost:       witnessHost.Reference(),
   126  		PreferredFd:       cmd.PreferredFaultDomain,
   127  		DiskMapping:       nil,
   128  	}
   129  
   130  	res, err := methods.VSANVcConvertToStretchedCluster(ctx, vsanClient, &req)
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	logger := cmd.ProgressLogger("stretching cluster... ")
   136  	defer logger.Wait()
   137  
   138  	task := object.NewTask(client, res.Returnval)
   139  	_, err = task.WaitForResult(ctx, logger)
   140  	return err
   141  }
   142  
   143  func (cmd *stretch) buildFaultDomainConfig(ctx context.Context) (*types.VimClusterVSANStretchedClusterFaultDomainConfig, error) {
   144  	var faultDomainConfig types.VimClusterVSANStretchedClusterFaultDomainConfig
   145  	var err error
   146  
   147  	faultDomainConfig.FirstFdName = cmd.FirstFaultDomainName
   148  	faultDomainConfig.FirstFdHosts, err = cmd.getManagedObjectRefs(cmd.FirstFaultDomainHosts, ctx)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	if len(faultDomainConfig.FirstFdHosts) == 0 {
   154  		return nil, fmt.Errorf("no hosts for fault domain %q", cmd.FirstFaultDomainName)
   155  	}
   156  
   157  	faultDomainConfig.SecondFdName = cmd.SecondFaultDomainName
   158  	faultDomainConfig.SecondFdHosts, err = cmd.getManagedObjectRefs(cmd.SecondFaultDomainHosts, ctx)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  
   163  	if len(faultDomainConfig.SecondFdHosts) == 0 {
   164  		return nil, fmt.Errorf("no hosts for fault domain %q", cmd.SecondFaultDomainName)
   165  	}
   166  
   167  	return &faultDomainConfig, nil
   168  }
   169  
   170  func (cmd *stretch) getManagedObjectRefs(domainHosts string, ctx context.Context) ([]vim.ManagedObjectReference, error) {
   171  	finder, err := cmd.Finder()
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  
   176  	var refs []vim.ManagedObjectReference
   177  	for _, host := range strings.Split(domainHosts, ",") {
   178  		h, err := finder.HostSystem(ctx, host)
   179  		if err != nil {
   180  			return nil, err
   181  		}
   182  
   183  		refs = append(refs, h.Reference())
   184  	}
   185  
   186  	return refs, nil
   187  }