github.com/vmware/govmomi@v0.37.2/govc/cluster/stretch.go (about)

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