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