github.com/vmware/govmomi@v0.51.0/cns/cns_util.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 cns 6 7 import ( 8 "bytes" 9 "context" 10 "crypto/sha1" 11 "crypto/tls" 12 "errors" 13 "fmt" 14 15 "github.com/vmware/govmomi" 16 cnstypes "github.com/vmware/govmomi/cns/types" 17 "github.com/vmware/govmomi/object" 18 vim25types "github.com/vmware/govmomi/vim25/types" 19 ) 20 21 // DefaultVCenterPort is the default port used to access vCenter. 22 const DefaultVCenterPort string = "443" 23 24 // GetTaskInfo gets the task info given a task 25 func GetTaskInfo(ctx context.Context, task *object.Task) (*vim25types.TaskInfo, error) { 26 taskInfo, err := task.WaitForResult(ctx, nil) 27 if err != nil { 28 return nil, err 29 } 30 return taskInfo, nil 31 } 32 33 // GetQuerySnapshotsTaskResult gets the task result of QuerySnapshots given a task info 34 func GetQuerySnapshotsTaskResult(ctx context.Context, taskInfo *vim25types.TaskInfo) (*cnstypes.CnsSnapshotQueryResult, error) { 35 if taskInfo == nil { 36 return nil, errors.New("TaskInfo is empty") 37 } 38 if taskInfo.Result != nil { 39 snapshotQueryResult := taskInfo.Result.(cnstypes.CnsSnapshotQueryResult) 40 return &snapshotQueryResult, nil 41 } 42 return nil, errors.New("TaskInfo result is empty") 43 } 44 45 // GetTaskResult gets the task result given a task info 46 func GetTaskResult(ctx context.Context, taskInfo *vim25types.TaskInfo) (cnstypes.BaseCnsVolumeOperationResult, error) { 47 if taskInfo == nil { 48 return nil, errors.New("TaskInfo is empty") 49 } 50 if taskInfo.Result != nil { 51 volumeOperationBatchResult := taskInfo.Result.(cnstypes.CnsVolumeOperationBatchResult) 52 if len(volumeOperationBatchResult.VolumeResults) == 0 { 53 return nil, errors.New("Cannot get VolumeOperationResult") 54 } 55 return volumeOperationBatchResult.VolumeResults[0], nil 56 } 57 return nil, errors.New("TaskInfo result is empty") 58 } 59 60 // GetTaskResultArray gets the task result array for a specified task info 61 func GetTaskResultArray(ctx context.Context, taskInfo *vim25types.TaskInfo) ([]cnstypes.BaseCnsVolumeOperationResult, error) { 62 if taskInfo == nil { 63 return nil, errors.New("TaskInfo is empty") 64 } 65 if taskInfo.Result != nil { 66 volumeOperationBatchResult := taskInfo.Result.(cnstypes.CnsVolumeOperationBatchResult) 67 if len(volumeOperationBatchResult.VolumeResults) == 0 { 68 return nil, errors.New("Cannot get VolumeOperationResult") 69 } 70 return volumeOperationBatchResult.VolumeResults, nil 71 } 72 return nil, errors.New("TaskInfo result is empty") 73 } 74 75 // dropUnknownCreateSpecElements helps drop newly added elements in the CnsVolumeCreateSpec, which are not known to the prior vSphere releases 76 func dropUnknownCreateSpecElements(c *Client, createSpecList []cnstypes.CnsVolumeCreateSpec) []cnstypes.CnsVolumeCreateSpec { 77 updatedcreateSpecList := make([]cnstypes.CnsVolumeCreateSpec, 0, len(createSpecList)) 78 switch c.Version { 79 case ReleaseVSAN67u3: 80 // Dropping optional fields not known to vSAN 6.7U3 81 for _, createSpec := range createSpecList { 82 createSpec.Metadata.ContainerCluster.ClusterFlavor = "" 83 createSpec.Metadata.ContainerCluster.ClusterDistribution = "" 84 createSpec.Metadata.ContainerClusterArray = nil 85 var updatedEntityMetadata []cnstypes.BaseCnsEntityMetadata 86 for _, entityMetadata := range createSpec.Metadata.EntityMetadata { 87 k8sEntityMetadata := any(entityMetadata).(*cnstypes.CnsKubernetesEntityMetadata) 88 k8sEntityMetadata.ClusterID = "" 89 k8sEntityMetadata.ReferredEntity = nil 90 updatedEntityMetadata = append(updatedEntityMetadata, cnstypes.BaseCnsEntityMetadata(k8sEntityMetadata)) 91 } 92 createSpec.Metadata.EntityMetadata = updatedEntityMetadata 93 _, ok := createSpec.BackingObjectDetails.(*cnstypes.CnsBlockBackingDetails) 94 if ok { 95 createSpec.BackingObjectDetails.(*cnstypes.CnsBlockBackingDetails).BackingDiskUrlPath = "" 96 } 97 updatedcreateSpecList = append(updatedcreateSpecList, createSpec) 98 } 99 createSpecList = updatedcreateSpecList 100 case ReleaseVSAN70: 101 // Dropping optional fields not known to vSAN 7.0 102 for _, createSpec := range createSpecList { 103 createSpec.Metadata.ContainerCluster.ClusterDistribution = "" 104 var updatedContainerClusterArray []cnstypes.CnsContainerCluster 105 for _, containerCluster := range createSpec.Metadata.ContainerClusterArray { 106 containerCluster.ClusterDistribution = "" 107 updatedContainerClusterArray = append(updatedContainerClusterArray, containerCluster) 108 } 109 createSpec.Metadata.ContainerClusterArray = updatedContainerClusterArray 110 _, ok := createSpec.BackingObjectDetails.(*cnstypes.CnsBlockBackingDetails) 111 if ok { 112 createSpec.BackingObjectDetails.(*cnstypes.CnsBlockBackingDetails).BackingDiskUrlPath = "" 113 } 114 updatedcreateSpecList = append(updatedcreateSpecList, createSpec) 115 } 116 createSpecList = updatedcreateSpecList 117 case ReleaseVSAN70u1: 118 // Dropping optional fields not known to vSAN 7.0U1 119 for _, createSpec := range createSpecList { 120 createSpec.Metadata.ContainerCluster.ClusterDistribution = "" 121 var updatedContainerClusterArray []cnstypes.CnsContainerCluster 122 for _, containerCluster := range createSpec.Metadata.ContainerClusterArray { 123 containerCluster.ClusterDistribution = "" 124 updatedContainerClusterArray = append(updatedContainerClusterArray, containerCluster) 125 } 126 createSpec.Metadata.ContainerClusterArray = updatedContainerClusterArray 127 updatedcreateSpecList = append(updatedcreateSpecList, createSpec) 128 } 129 createSpecList = updatedcreateSpecList 130 } 131 return createSpecList 132 } 133 134 // dropUnknownVolumeMetadataUpdateSpecElements helps drop newly added elements in the CnsVolumeMetadataUpdateSpec, which are not known to the prior vSphere releases 135 func dropUnknownVolumeMetadataUpdateSpecElements(c *Client, updateSpecList []cnstypes.CnsVolumeMetadataUpdateSpec) []cnstypes.CnsVolumeMetadataUpdateSpec { 136 // Dropping optional fields not known to vSAN 6.7U3 137 if c.Version == ReleaseVSAN67u3 { 138 updatedUpdateSpecList := make([]cnstypes.CnsVolumeMetadataUpdateSpec, 0, len(updateSpecList)) 139 for _, updateSpec := range updateSpecList { 140 updateSpec.Metadata.ContainerCluster.ClusterFlavor = "" 141 updateSpec.Metadata.ContainerCluster.ClusterDistribution = "" 142 var updatedEntityMetadata []cnstypes.BaseCnsEntityMetadata 143 for _, entityMetadata := range updateSpec.Metadata.EntityMetadata { 144 k8sEntityMetadata := any(entityMetadata).(*cnstypes.CnsKubernetesEntityMetadata) 145 k8sEntityMetadata.ClusterID = "" 146 k8sEntityMetadata.ReferredEntity = nil 147 updatedEntityMetadata = append(updatedEntityMetadata, cnstypes.BaseCnsEntityMetadata(k8sEntityMetadata)) 148 } 149 updateSpec.Metadata.ContainerClusterArray = nil 150 updateSpec.Metadata.EntityMetadata = updatedEntityMetadata 151 updatedUpdateSpecList = append(updatedUpdateSpecList, updateSpec) 152 } 153 updateSpecList = updatedUpdateSpecList 154 } else if c.Version == ReleaseVSAN70 || c.Version == ReleaseVSAN70u1 { 155 updatedUpdateSpecList := make([]cnstypes.CnsVolumeMetadataUpdateSpec, 0, len(updateSpecList)) 156 for _, updateSpec := range updateSpecList { 157 updateSpec.Metadata.ContainerCluster.ClusterDistribution = "" 158 var updatedContainerClusterArray []cnstypes.CnsContainerCluster 159 for _, containerCluster := range updateSpec.Metadata.ContainerClusterArray { 160 containerCluster.ClusterDistribution = "" 161 updatedContainerClusterArray = append(updatedContainerClusterArray, containerCluster) 162 } 163 updateSpec.Metadata.ContainerClusterArray = updatedContainerClusterArray 164 updatedUpdateSpecList = append(updatedUpdateSpecList, updateSpec) 165 } 166 updateSpecList = updatedUpdateSpecList 167 } 168 return updateSpecList 169 } 170 171 // GetServiceLocatorInstance takes as input VC userName, VC password, VC client 172 // and returns a service locator instance for the VC. 173 func GetServiceLocatorInstance(ctx context.Context, userName string, password string, vcClient *govmomi.Client) (*vim25types.ServiceLocator, error) { 174 hostPortStr := fmt.Sprintf("%s:%s", vcClient.URL().Hostname(), DefaultVCenterPort) 175 url := fmt.Sprintf("https://%s/sdk", hostPortStr) 176 177 thumbprint, err := getSslThumbprint(ctx, hostPortStr, vcClient) 178 if err != nil { 179 return nil, fmt.Errorf("failed to get ssl thumbprint. Error: %+v", err) 180 } 181 182 serviceLocatorInstance := &vim25types.ServiceLocator{ 183 InstanceUuid: vcClient.ServiceContent.About.InstanceUuid, 184 Url: url, 185 Credential: &vim25types.ServiceLocatorNamePassword{ 186 Username: userName, 187 Password: password, 188 }, 189 SslThumbprint: thumbprint, 190 } 191 192 return serviceLocatorInstance, nil 193 } 194 195 // getSslThumbprint connects to the given network address, initiates a TLS handshake 196 // and retrieves the SSL thumprint from the resulting TLS connection. 197 func getSslThumbprint(ctx context.Context, addr string, vcClient *govmomi.Client) (string, error) { 198 conn, err := vcClient.Client.DefaultTransport().DialTLSContext(ctx, "tcp", addr) 199 if err != nil { 200 return "", err 201 } 202 defer conn.Close() 203 204 tlsConn, ok := conn.(*tls.Conn) 205 if !ok { 206 return "", fmt.Errorf("cannot convert net connection to tls connection") 207 } 208 209 cert := tlsConn.ConnectionState().PeerCertificates[0] 210 thumbPrint := sha1.Sum(cert.Raw) 211 212 // Get hex representation for each byte of the thumbprint separated by colon. 213 // e.g. B9:12:79:B9:36:1B:B5:C1:2F:20:4A:DD:BD:0C:3D:31:82:99:CB:5C 214 var buf bytes.Buffer 215 for i, f := range thumbPrint { 216 if i > 0 { 217 fmt.Fprintf(&buf, ":") 218 } 219 fmt.Fprintf(&buf, "%02X", f) 220 } 221 222 return buf.String(), nil 223 }