github.com/openshift/installer@v1.4.17/pkg/destroy/vsphere/client.go (about)

     1  package vsphere
     2  
     3  import (
     4  	"context"
     5  	"net/http"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/pkg/errors"
    10  	"github.com/vmware/govmomi/object"
    11  	"github.com/vmware/govmomi/pbm"
    12  	pbmtypes "github.com/vmware/govmomi/pbm/types"
    13  	"github.com/vmware/govmomi/property"
    14  	"github.com/vmware/govmomi/vapi/rest"
    15  	"github.com/vmware/govmomi/vapi/tags"
    16  	"github.com/vmware/govmomi/vim25"
    17  	"github.com/vmware/govmomi/vim25/mo"
    18  	"github.com/vmware/govmomi/vim25/types"
    19  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    20  
    21  	"github.com/openshift/installer/pkg/asset/installconfig/vsphere"
    22  )
    23  
    24  //go:generate mockgen -source=./client.go -destination=mock/vsphereclient_generated.go -package=mock
    25  
    26  // API represents the calls made to the API.
    27  type API interface {
    28  	Logout()
    29  	ListFolders(ctx context.Context, tagID string) ([]mo.Folder, error)
    30  	ListVirtualMachines(ctx context.Context, tagID string) ([]mo.VirtualMachine, error)
    31  	StopVirtualMachine(ctx context.Context, vmMO mo.VirtualMachine) error
    32  	DeleteFolder(ctx context.Context, f mo.Folder) error
    33  	DeleteVirtualMachine(ctx context.Context, vmMO mo.VirtualMachine) error
    34  	DeleteStoragePolicy(ctx context.Context, policyName string) error
    35  	DeleteTag(ctx context.Context, id string) error
    36  	DeleteTagCategory(ctx context.Context, id string) error
    37  }
    38  
    39  // Client makes calls to the Azure API.
    40  type Client struct {
    41  	client     *vim25.Client
    42  	restClient *rest.Client
    43  	cleanup    vsphere.ClientLogout
    44  }
    45  
    46  const defaultTimeout = time.Minute * 5
    47  
    48  // NewClient initializes a client.
    49  // Logout() must be called when you are done with the client.
    50  func NewClient(vCenter, username, password string) (*Client, error) {
    51  	vim25Client, restClient, cleanup, err := vsphere.CreateVSphereClients(
    52  		context.TODO(),
    53  		vCenter,
    54  		username,
    55  		password)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	return &Client{
    61  		client:     vim25Client,
    62  		restClient: restClient,
    63  		cleanup:    cleanup,
    64  	}, nil
    65  }
    66  
    67  // Logout logs out from the clients used.
    68  func (c *Client) Logout() {
    69  	c.cleanup()
    70  }
    71  
    72  func isNotFound(err error) bool {
    73  	return err != nil && strings.HasSuffix(err.Error(), http.StatusText(http.StatusNotFound))
    74  }
    75  
    76  func (c *Client) getAttachedObjectsOnTag(ctx context.Context, tag, objType string) ([]types.ManagedObjectReference, error) {
    77  	ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
    78  	defer cancel()
    79  
    80  	tagManager := tags.NewManager(c.restClient)
    81  	attached, err := tagManager.GetAttachedObjectsOnTags(ctx, []string{tag})
    82  	if err != nil && !isNotFound(err) {
    83  		return nil, err
    84  	}
    85  
    86  	// Separate the objects attached to the tag based on type
    87  	var objectList []types.ManagedObjectReference
    88  	for _, attachedObject := range attached {
    89  		for _, ref := range attachedObject.ObjectIDs {
    90  			if ref.Reference().Type == objType {
    91  				objectList = append(objectList, ref.Reference())
    92  			}
    93  		}
    94  	}
    95  
    96  	return objectList, nil
    97  }
    98  
    99  func (c *Client) getVirtualMachineManagedObjects(ctx context.Context, moRef []types.ManagedObjectReference) ([]mo.VirtualMachine, error) {
   100  	ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
   101  	defer cancel()
   102  
   103  	var virtualMachineMoList []mo.VirtualMachine
   104  	if len(moRef) > 0 {
   105  		pc := property.DefaultCollector(c.client)
   106  		err := pc.Retrieve(ctx, moRef, nil, &virtualMachineMoList)
   107  		if err != nil {
   108  			return nil, err
   109  		}
   110  	}
   111  	return virtualMachineMoList, nil
   112  }
   113  
   114  func (c *Client) getFolderManagedObjects(ctx context.Context, moRef []types.ManagedObjectReference) ([]mo.Folder, error) {
   115  	ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
   116  	defer cancel()
   117  
   118  	var folderMoList []mo.Folder
   119  	if len(moRef) > 0 {
   120  		pc := property.DefaultCollector(c.client)
   121  		err := pc.Retrieve(ctx, moRef, nil, &folderMoList)
   122  		if err != nil {
   123  			return nil, err
   124  		}
   125  	}
   126  	return folderMoList, nil
   127  }
   128  
   129  // ListFolders returns all ManagedObjects of type "Folder".
   130  func (c *Client) ListFolders(ctx context.Context, tagID string) ([]mo.Folder, error) {
   131  	folderList, err := c.getAttachedObjectsOnTag(ctx, tagID, "Folder")
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	return c.getFolderManagedObjects(ctx, folderList)
   137  }
   138  
   139  // ListVirtualMachines returns ManagedObjects of type "VirtualMachine".
   140  func (c *Client) ListVirtualMachines(ctx context.Context, tagID string) ([]mo.VirtualMachine, error) {
   141  	virtualMachineList, err := c.getAttachedObjectsOnTag(ctx, tagID, "VirtualMachine")
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	return c.getVirtualMachineManagedObjects(ctx, virtualMachineList)
   147  }
   148  
   149  func isPoweredOff(vmMO mo.VirtualMachine) bool {
   150  	return vmMO.Summary.Runtime.PowerState == "poweredOff"
   151  }
   152  
   153  // StopVirtualMachine stops a VM if it's not already powered off.
   154  func (c *Client) StopVirtualMachine(ctx context.Context, vmMO mo.VirtualMachine) error {
   155  	ctx, cancel := context.WithTimeout(ctx, time.Minute*30)
   156  	defer cancel()
   157  
   158  	if !isPoweredOff(vmMO) {
   159  		vm := object.NewVirtualMachine(c.client, vmMO.Reference())
   160  		task, err := vm.PowerOff(ctx)
   161  		if err == nil {
   162  			err = task.Wait(ctx)
   163  		}
   164  		return err
   165  	}
   166  	return nil
   167  }
   168  
   169  // DeleteVirtualMachine deletes a VirtualMachine.
   170  func (c *Client) DeleteVirtualMachine(ctx context.Context, vmMO mo.VirtualMachine) error {
   171  	ctx, cancel := context.WithTimeout(ctx, time.Minute*30)
   172  	defer cancel()
   173  
   174  	vm := object.NewVirtualMachine(c.client, vmMO.Reference())
   175  	task, err := vm.Destroy(ctx)
   176  	if err == nil {
   177  		err = task.Wait(ctx)
   178  	}
   179  	return err
   180  }
   181  
   182  // DeleteFolder deletes a Folder.
   183  func (c *Client) DeleteFolder(ctx context.Context, f mo.Folder) error {
   184  	ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
   185  	defer cancel()
   186  
   187  	folder := object.NewFolder(c.client, f.Reference())
   188  
   189  	task, err := folder.Destroy(ctx)
   190  	if err == nil {
   191  		err = task.Wait(ctx)
   192  	}
   193  	return err
   194  }
   195  
   196  // DeleteStoragePolicy deletes a Storage Policy named `policyName`.
   197  func (c *Client) DeleteStoragePolicy(ctx context.Context, policyName string) error {
   198  	ctx, cancel := context.WithTimeout(ctx, time.Minute*30)
   199  	defer cancel()
   200  
   201  	rtype := pbmtypes.PbmProfileResourceType{
   202  		ResourceType: string(pbmtypes.PbmProfileResourceTypeEnumSTORAGE),
   203  	}
   204  
   205  	category := pbmtypes.PbmProfileCategoryEnumREQUIREMENT
   206  
   207  	pbmClient, err := pbm.NewClient(ctx, c.client)
   208  	if err != nil {
   209  		return err
   210  	}
   211  
   212  	ids, err := pbmClient.QueryProfile(ctx, rtype, string(category))
   213  	if err != nil {
   214  		return err
   215  	}
   216  
   217  	profiles, err := pbmClient.RetrieveContent(ctx, ids)
   218  	if err != nil {
   219  		return err
   220  	}
   221  
   222  	matchingProfileIds := []pbmtypes.PbmProfileId{}
   223  	for _, p := range profiles {
   224  		if p.GetPbmProfile().Name == policyName {
   225  			profileID := p.GetPbmProfile().ProfileId
   226  			matchingProfileIds = append(matchingProfileIds, profileID)
   227  		}
   228  	}
   229  	if len(matchingProfileIds) > 0 {
   230  		_, err = pbmClient.DeleteProfile(ctx, matchingProfileIds)
   231  		if err != nil {
   232  			return err
   233  		}
   234  	}
   235  	return nil
   236  }
   237  
   238  // DeleteTag deletes a Tag named `id`.
   239  func (c *Client) DeleteTag(ctx context.Context, id string) error {
   240  	ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
   241  	defer cancel()
   242  
   243  	tagManager := tags.NewManager(c.restClient)
   244  	tag, err := tagManager.GetTag(ctx, id)
   245  	if isNotFound(err) {
   246  		return nil
   247  	}
   248  	if err == nil {
   249  		err = tagManager.DeleteTag(ctx, tag)
   250  	}
   251  	return err
   252  }
   253  
   254  // DeleteTagCategory deletes a Tag Category named `categoryName`.
   255  func (c *Client) DeleteTagCategory(ctx context.Context, categoryName string) error {
   256  	ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
   257  	defer cancel()
   258  
   259  	tagManager := tags.NewManager(c.restClient)
   260  	ids, err := tagManager.ListCategories(ctx)
   261  	if err != nil {
   262  		return err
   263  	}
   264  
   265  	var errs []error
   266  	for _, id := range ids {
   267  		category, err := tagManager.GetCategory(ctx, id)
   268  		if err != nil {
   269  			if !isNotFound(err) {
   270  				errs = append(errs, errors.Wrapf(err, "could not get category %q", id))
   271  			}
   272  			continue
   273  		}
   274  		if category.Name == categoryName {
   275  			if err = tagManager.DeleteCategory(ctx, category); err != nil {
   276  				return err
   277  			}
   278  			return nil
   279  		}
   280  	}
   281  
   282  	return utilerrors.NewAggregate(errs)
   283  }