github.com/vmware/govmomi@v0.43.0/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 }