github.com/vmware/govmomi@v0.51.0/cli/disk/manager.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 disk
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"os"
    11  	"time"
    12  
    13  	"github.com/vmware/govmomi/cli/flags"
    14  	"github.com/vmware/govmomi/object"
    15  	"github.com/vmware/govmomi/vim25"
    16  	"github.com/vmware/govmomi/vim25/types"
    17  	"github.com/vmware/govmomi/vslm"
    18  	vslmtypes "github.com/vmware/govmomi/vslm/types"
    19  )
    20  
    21  // Manager provides a layer for switching between Virtual Storage Object Manager (VSOM)
    22  // and Virtual Storage Lifecycle Manager (VSLM). The majority of VSOM methods require a
    23  // Datastore param, and most VSLM methods do not as it uses the "Global Catalog".
    24  // VSOM was introduced in vSphere 6.5 (11/2016) and VSLM in vSphere 6.7 U2 (04/2019).
    25  // The govc disk commands were introduced prior to 6.7 U2 and continue to use VSOM
    26  // when an optional Datastore (-ds flag) is provided. Otherwise, VSLM global methods
    27  // are used when a Datastore is not specified.
    28  // Also note that VSOM methods can be used when connected directly to an ESX hosts,
    29  // but VSLM global methods require vCenter.
    30  // A disk managed by these methods are also known as a "First Class Disk" (FCD).
    31  type Manager struct {
    32  	Client              *vim25.Client
    33  	Datastore           *object.Datastore
    34  	ObjectManager       *vslm.ObjectManager
    35  	GlobalObjectManager *vslm.GlobalObjectManager
    36  }
    37  
    38  var (
    39  	// vslm does not have a PropertyCollector, the Wait func polls via VslmQueryInfo
    40  	taskTimeout = time.Hour
    41  
    42  	// Some methods require a Datastore param regardless using vsom or vslm.
    43  	// By default we use Global vslm for those methods, use vsom when this is "false".
    44  	useGlobal = os.Getenv("GOVC_GLOBAL_VSLM") != "false"
    45  )
    46  
    47  func NewManagerFromFlag(ctx context.Context, cmd *flags.DatastoreFlag) (*Manager, error) {
    48  	c, err := cmd.Client()
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	ds, err := cmd.DatastoreIfSpecified()
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	return NewManager(ctx, c, ds)
    59  }
    60  
    61  func NewManager(ctx context.Context, c *vim25.Client, ds *object.Datastore) (*Manager, error) {
    62  	m := &Manager{
    63  		Client:        c,
    64  		Datastore:     ds,
    65  		ObjectManager: vslm.NewObjectManager(c),
    66  	}
    67  
    68  	if ds == nil {
    69  		if err := m.initGlobalManager(ctx); err != nil {
    70  			return nil, err
    71  		}
    72  	}
    73  
    74  	return m, nil
    75  }
    76  
    77  func (m *Manager) initGlobalManager(ctx context.Context) error {
    78  	if m.GlobalObjectManager == nil {
    79  		vc, err := vslm.NewClient(ctx, m.Client)
    80  		if err != nil {
    81  			return err
    82  		}
    83  		m.GlobalObjectManager = vslm.NewGlobalObjectManager(vc)
    84  	}
    85  	return nil
    86  }
    87  
    88  func (m *Manager) CreateDisk(ctx context.Context, spec types.VslmCreateSpec) (*types.VStorageObject, error) {
    89  	if useGlobal && m.Client.IsVC() {
    90  		if err := m.initGlobalManager(ctx); err != nil {
    91  			return nil, err
    92  		}
    93  		task, err := m.GlobalObjectManager.CreateDisk(ctx, spec)
    94  		if err != nil {
    95  			return nil, err
    96  		}
    97  		res, err := task.Wait(ctx, taskTimeout)
    98  		if err != nil {
    99  			return nil, err
   100  		}
   101  
   102  		obj := res.(types.VStorageObject)
   103  		return &obj, nil
   104  	}
   105  
   106  	task, err := m.ObjectManager.CreateDisk(ctx, spec)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	res, err := task.WaitForResult(ctx)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	obj := res.Result.(types.VStorageObject)
   115  	return &obj, nil
   116  }
   117  
   118  func (m *Manager) Delete(ctx context.Context, id string) error {
   119  	if m.Datastore != nil {
   120  		task, err := m.ObjectManager.Delete(ctx, m.Datastore, id)
   121  		if err != nil {
   122  			return err
   123  		}
   124  		return task.Wait(ctx)
   125  	}
   126  
   127  	task, err := m.GlobalObjectManager.Delete(ctx, types.ID{Id: id})
   128  	if err != nil {
   129  		return err
   130  	}
   131  	_, err = task.Wait(ctx, taskTimeout)
   132  	return err
   133  }
   134  
   135  func (m *Manager) Retrieve(ctx context.Context, id string) (*types.VStorageObject, error) {
   136  	if m.Datastore != nil {
   137  		return m.ObjectManager.Retrieve(ctx, m.Datastore, id)
   138  	}
   139  	return m.GlobalObjectManager.Retrieve(ctx, types.ID{Id: id})
   140  }
   141  
   142  func (m *Manager) List(ctx context.Context, qs ...vslmtypes.VslmVsoVStorageObjectQuerySpec) ([]types.ID, error) {
   143  	if m.Datastore != nil {
   144  		return m.ObjectManager.List(ctx, m.Datastore)
   145  	}
   146  
   147  	res, err := m.GlobalObjectManager.List(ctx, qs...)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  
   152  	return res.Id, nil
   153  }
   154  
   155  func (m *Manager) RegisterDisk(ctx context.Context, path, name string) (*types.VStorageObject, error) {
   156  	if useGlobal && m.Client.IsVC() {
   157  		if err := m.initGlobalManager(ctx); err != nil {
   158  			return nil, err
   159  		}
   160  		return m.GlobalObjectManager.RegisterDisk(ctx, path, name) // VslmRegisterDisk
   161  	}
   162  	return m.ObjectManager.RegisterDisk(ctx, path, name) // RegisterDisk
   163  }
   164  
   165  func (m *Manager) CreateSnapshot(ctx context.Context, id, desc string) (types.ID, error) {
   166  	if m.Datastore != nil {
   167  		task, err := m.ObjectManager.CreateSnapshot(ctx, m.Datastore, id, desc)
   168  		if err != nil {
   169  			return types.ID{}, err
   170  		}
   171  		res, err := task.WaitForResult(ctx, nil)
   172  		if err != nil {
   173  			return types.ID{}, err
   174  		}
   175  		return res.Result.(types.ID), nil
   176  	}
   177  
   178  	task, err := m.GlobalObjectManager.CreateSnapshot(ctx, types.ID{Id: id}, desc)
   179  	if err != nil {
   180  		return types.ID{}, err
   181  	}
   182  	res, err := task.Wait(ctx, taskTimeout)
   183  	if err != nil {
   184  		return types.ID{}, err
   185  	}
   186  	return res.(types.ID), err
   187  }
   188  
   189  func (m *Manager) DeleteSnapshot(ctx context.Context, id, sid string) error {
   190  	if m.Datastore != nil {
   191  		task, err := m.ObjectManager.DeleteSnapshot(ctx, m.Datastore, id, sid)
   192  		if err != nil {
   193  			return err
   194  		}
   195  		return task.Wait(ctx)
   196  	}
   197  
   198  	task, err := m.GlobalObjectManager.DeleteSnapshot(ctx, types.ID{Id: id}, types.ID{Id: sid})
   199  	if err != nil {
   200  		return err
   201  	}
   202  	_, err = task.Wait(ctx, taskTimeout)
   203  	return err
   204  }
   205  
   206  func (m *Manager) RetrieveSnapshotInfo(ctx context.Context, id string) ([]types.VStorageObjectSnapshotInfoVStorageObjectSnapshot, error) {
   207  	if m.Datastore != nil {
   208  		info, err := m.ObjectManager.RetrieveSnapshotInfo(ctx, m.Datastore, id)
   209  		if err != nil {
   210  			return nil, err
   211  		}
   212  		return info.Snapshots, nil
   213  	}
   214  
   215  	return m.GlobalObjectManager.RetrieveSnapshotInfo(ctx, types.ID{Id: id})
   216  }
   217  
   218  func (m *Manager) AttachTag(ctx context.Context, id string, tag types.VslmTagEntry) error {
   219  	if useGlobal && m.Client.IsVC() {
   220  		if err := m.initGlobalManager(ctx); err != nil {
   221  			return err
   222  		}
   223  		// TODO: use types.VslmTagEntry
   224  		return m.GlobalObjectManager.AttachTag(ctx, types.ID{Id: id}, tag.ParentCategoryName, tag.TagName)
   225  	}
   226  	return m.ObjectManager.AttachTag(ctx, id, tag)
   227  }
   228  
   229  func (m *Manager) DetachTag(ctx context.Context, id string, tag types.VslmTagEntry) error {
   230  	if useGlobal && m.Client.IsVC() {
   231  		if err := m.initGlobalManager(ctx); err != nil {
   232  			return err
   233  		}
   234  		// TODO: use types.VslmTagEntry
   235  		return m.GlobalObjectManager.DetachTag(ctx, types.ID{Id: id}, tag.ParentCategoryName, tag.TagName)
   236  	}
   237  	return m.ObjectManager.DetachTag(ctx, id, tag)
   238  }
   239  
   240  func (m *Manager) ListAttachedObjects(ctx context.Context, category, tag string) ([]types.ID, error) {
   241  	if m.Datastore != nil {
   242  		// ListVStorageObjectsAttachedToTag
   243  		return m.ObjectManager.ListAttachedObjects(ctx, category, tag)
   244  	}
   245  
   246  	// VslmListVStorageObjectsAttachedToTag
   247  	return m.GlobalObjectManager.ListAttachedObjects(ctx, category, tag)
   248  }
   249  
   250  func (m *Manager) ListAttachedTags(ctx context.Context, id string) ([]types.VslmTagEntry, error) {
   251  	if m.Datastore != nil {
   252  		// ListTagsAttachedToVStorageObject
   253  		return m.ObjectManager.ListAttachedTags(ctx, id)
   254  	}
   255  
   256  	// VslmListTagsAttachedToVStorageObject
   257  	return m.GlobalObjectManager.ListAttachedTags(ctx, types.ID{Id: id})
   258  }
   259  
   260  func (m *Manager) ReconcileDatastoreInventory(ctx context.Context) error {
   261  	if m.Datastore != nil {
   262  		// ReconcileDatastoreInventory_Task
   263  		task, err := m.ObjectManager.ReconcileDatastoreInventory(ctx, m.Datastore)
   264  		if err != nil {
   265  			return err
   266  		}
   267  		return task.Wait(ctx)
   268  	}
   269  
   270  	// VslmReconcileDatastoreInventory_Task also requires a Datastore
   271  	return errors.New("-R requires -ds") // TODO
   272  }
   273  
   274  func (m *Manager) AttachDisk(ctx context.Context, vm *object.VirtualMachine, id string) error {
   275  	if m.Datastore != nil {
   276  		return vm.AttachDisk(ctx, id, m.Datastore, 0, nil)
   277  	}
   278  
   279  	task, err := m.GlobalObjectManager.AttachDisk(ctx, types.ID{Id: id}, vm, 0, nil)
   280  	if err != nil {
   281  		return err
   282  	}
   283  	_, err = task.Wait(ctx, taskTimeout)
   284  	return err
   285  }