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  }