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 }