github.com/vmware/govmomi@v0.51.0/vapi/vcenter/vcenter_vmtx.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 vcenter 6 7 import ( 8 "context" 9 "crypto/sha1" 10 "fmt" 11 "log" 12 "net/http" 13 "path" 14 15 "github.com/vmware/govmomi/vapi/internal" 16 "github.com/vmware/govmomi/vapi/library" 17 "github.com/vmware/govmomi/vim25/mo" 18 "github.com/vmware/govmomi/vim25/types" 19 ) 20 21 // vcenter vm template 22 // The vcenter.vm_template API provides structures and services that will let its client manage VMTX template in Content Library. 23 // http://vmware.github.io/vsphere-automation-sdk-rest/6.7.1/index.html#SVC_com.vmware.vcenter.vm_template.library_items 24 25 // Template create spec 26 type Template struct { 27 Description string `json:"description,omitempty"` 28 DiskStorage *DiskStorage `json:"disk_storage,omitempty"` 29 DiskStorageOverrides []DiskStorageOverride `json:"disk_storage_overrides,omitempty"` 30 Library string `json:"library,omitempty"` 31 Name string `json:"name,omitempty"` 32 Placement *Placement `json:"placement,omitempty"` 33 SourceVM string `json:"source_vm,omitempty"` 34 VMHomeStorage *DiskStorage `json:"vm_home_storage,omitempty"` 35 } 36 37 // CPU defines Cores and CPU count 38 type CPU struct { 39 CoresPerSocket int `json:"cores_per_socket,omitempty"` 40 Count int `json:"count,omitempty"` 41 } 42 43 // DiskInfo defines disk capacity and storage info 44 type DiskInfo struct { 45 Capacity int `json:"capacity,omitempty"` 46 DiskStorage DiskStorage `json:"disk_storage,omitempty"` 47 } 48 49 // Disks defines the disk information 50 type Disks struct { 51 Key string `json:"key"` 52 Value *DiskInfo `json:"value"` 53 } 54 55 // Memory defines the memory size in MB 56 type Memory struct { 57 SizeMB int `json:"size_mib,omitempty"` 58 } 59 60 // NicDetails defines the network adapter details 61 type NicDetails struct { 62 Network string `json:"network,omitempty"` 63 BackingType string `json:"backing_type,omitempty"` 64 MacType string `json:"mac_type,omitempty"` 65 } 66 67 // Nics defines the network identifier 68 type Nics struct { 69 Key string `json:"key,omitempty"` 70 Value *NicDetails `json:"value,omitempty"` 71 } 72 73 // TemplateInfo for a VM template contained in an existing library item 74 type TemplateInfo struct { 75 CPU CPU `json:"cpu,omitempty"` 76 Disks []Disks `json:"disks,omitempty"` 77 GuestOS string `json:"guest_OS,omitempty"` 78 Memory Memory `json:"memory,omitempty"` 79 Nics []Nics `json:"nics,omitempty"` 80 VMHomeStorage DiskStorage `json:"vm_home_storage,omitempty"` 81 VmTemplate string `json:"vm_template,omitempty"` 82 } 83 84 // Placement information used to place the virtual machine template 85 type Placement = library.Placement 86 87 // StoragePolicy for DiskStorage 88 type StoragePolicy struct { 89 Policy string `json:"policy,omitempty"` 90 Type string `json:"type"` 91 } 92 93 // DiskStorage defines the storage specification for VM files 94 type DiskStorage struct { 95 Datastore string `json:"datastore,omitempty"` 96 StoragePolicy *StoragePolicy `json:"storage_policy,omitempty"` 97 } 98 99 // DiskStorageOverride storage specification for individual disks in the virtual machine template 100 type DiskStorageOverride struct { 101 Key string `json:"key"` 102 Value DiskStorage `json:"value"` 103 } 104 105 // GuestCustomization spec to apply to the deployed VM 106 type GuestCustomization struct { 107 Name string `json:"name,omitempty"` 108 } 109 110 // HardwareCustomization spec which specifies updates to the deployed VM 111 type HardwareCustomization struct { 112 // TODO 113 } 114 115 // DeployTemplate specification of how a library VM template clone should be deployed. 116 type DeployTemplate struct { 117 Description string `json:"description,omitempty"` 118 DiskStorage *DiskStorage `json:"disk_storage,omitempty"` 119 DiskStorageOverrides []DiskStorageOverride `json:"disk_storage_overrides,omitempty"` 120 GuestCustomization *GuestCustomization `json:"guest_customization,omitempty"` 121 HardwareCustomization *HardwareCustomization `json:"hardware_customization,omitempty"` 122 Name string `json:"name,omitempty"` 123 Placement *Placement `json:"placement,omitempty"` 124 PoweredOn bool `json:"powered_on"` 125 VMHomeStorage *DiskStorage `json:"vm_home_storage,omitempty"` 126 } 127 128 // CheckOut specification 129 type CheckOut struct { 130 Name string `json:"name,omitempty"` 131 Placement *Placement `json:"placement,omitempty"` 132 PoweredOn bool `json:"powered_on,omitempty"` 133 } 134 135 // CheckIn specification 136 type CheckIn struct { 137 Message string `json:"message"` 138 } 139 140 // CreateTemplate creates a library VMTX item in content library from an existing VM 141 func (c *Manager) CreateTemplate(ctx context.Context, vmtx Template) (string, error) { 142 url := c.Resource(internal.VCenterVMTXLibraryItem) 143 var res string 144 spec := struct { 145 Template `json:"spec"` 146 }{vmtx} 147 return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res) 148 } 149 150 // GetLibraryTemplateInfo fetches the library template info using template library id 151 func (c *Manager) GetLibraryTemplateInfo(ctx context.Context, libraryItemID string) (*TemplateInfo, error) { 152 url := c.Resource(path.Join(internal.VCenterVMTXLibraryItem, libraryItemID)) 153 var res TemplateInfo 154 err := c.Do(ctx, url.Request(http.MethodGet), &res) 155 if err != nil { 156 return nil, err 157 } 158 return &res, nil 159 } 160 161 // DeployTemplateLibraryItem deploys a VM as a copy of the source VM template contained in the given library item 162 func (c *Manager) DeployTemplateLibraryItem(ctx context.Context, libraryItemID string, deploy DeployTemplate) (*types.ManagedObjectReference, error) { 163 url := c.Resource(path.Join(internal.VCenterVMTXLibraryItem, libraryItemID)).WithParam("action", "deploy") 164 var res string 165 spec := struct { 166 DeployTemplate `json:"spec"` 167 }{deploy} 168 err := c.Do(ctx, url.Request(http.MethodPost, spec), &res) 169 if err != nil { 170 return nil, err 171 } 172 return &types.ManagedObjectReference{Type: "VirtualMachine", Value: res}, nil 173 } 174 175 // CheckOut a library item containing a VM template. 176 func (c *Manager) CheckOut(ctx context.Context, libraryItemID string, checkout *CheckOut) (*types.ManagedObjectReference, error) { 177 url := c.Resource(path.Join(internal.VCenterVMTXLibraryItem, libraryItemID, "check-outs")).WithParam("action", "check-out") 178 var res string 179 spec := struct { 180 *CheckOut `json:"spec"` 181 }{checkout} 182 err := c.Do(ctx, url.Request(http.MethodPost, spec), &res) 183 if err != nil { 184 return nil, err 185 } 186 return &types.ManagedObjectReference{Type: "VirtualMachine", Value: res}, nil 187 } 188 189 // CheckIn a VM into the library item. 190 func (c *Manager) CheckIn(ctx context.Context, libraryItemID string, vm mo.Reference, checkin *CheckIn) (string, error) { 191 p := path.Join(internal.VCenterVMTXLibraryItem, libraryItemID, "check-outs", vm.Reference().Value) 192 url := c.Resource(p).WithParam("action", "check-in") 193 var res string 194 spec := struct { 195 *CheckIn `json:"spec"` 196 }{checkin} 197 return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res) 198 } 199 200 // TemplateLibrary params for synchronizing subscription library OVF items to VM Template items 201 type TemplateLibrary struct { 202 Source library.Library 203 Destination library.Library 204 Placement Target 205 Include func(library.Item, *library.Item) bool 206 SyncItem func(context.Context, library.Item, *Deploy, *Template) error 207 } 208 209 func (c *Manager) includeTemplateLibraryItem(src library.Item, dst *library.Item) bool { 210 return dst == nil 211 } 212 213 // SyncTemplateLibraryItem deploys an Library OVF item from which a VM template (vmtx) Library item is created. 214 // The deployed VM is deleted after being converted to a Library vmtx item. 215 func (c *Manager) SyncTemplateLibraryItem(ctx context.Context, item library.Item, deploy *Deploy, spec *Template) error { 216 destroy := false 217 if spec.SourceVM == "" { 218 ref, err := c.DeployLibraryItem(ctx, item.ID, *deploy) 219 if err != nil { 220 return err 221 } 222 223 destroy = true 224 spec.SourceVM = ref.Value 225 } 226 227 _, err := c.CreateTemplate(ctx, *spec) 228 229 if destroy { 230 // Delete source VM regardless of CreateTemplate result 231 url := c.Resource("/vcenter/vm/" + spec.SourceVM) 232 derr := c.Do(ctx, url.Request(http.MethodDelete), nil) 233 if derr != nil { 234 if err == nil { 235 // Return Delete error if CreateTemplate was successful 236 return derr 237 } 238 // Return CreateTemplate error and just log Delete error 239 log.Printf("destroy %s: %s", spec.SourceVM, derr) 240 } 241 } 242 243 return err 244 } 245 246 func vmtxSourceName(l library.Library, item library.Item) string { 247 sum := sha1.Sum([]byte(path.Join(l.Name, item.Name))) 248 return fmt.Sprintf("vmtx-src-%x", sum) 249 } 250 251 // SyncTemplateLibrary converts TemplateLibrary.Source OVF items to VM Template items within TemplateLibrary.Destination 252 // The optional TemplateLibrary.Include func can be used to filter which items are synced. 253 // By default all items that don't exist in the Destination library are synced. 254 // The optional TemplateLibrary.SyncItem func can be used to change how the item is synced, by default SyncTemplateLibraryItem is used. 255 func (c *Manager) SyncTemplateLibrary(ctx context.Context, l TemplateLibrary, items ...library.Item) error { 256 m := library.NewManager(c.Client) 257 var err error 258 if len(items) == 0 { 259 items, err = m.GetLibraryItems(ctx, l.Source.ID) 260 if err != nil { 261 return err 262 } 263 } 264 265 templates, err := m.GetLibraryItems(ctx, l.Destination.ID) 266 if err != nil { 267 return err 268 } 269 270 existing := make(map[string]*library.Item) 271 for i := range templates { 272 existing[templates[i].Name] = &templates[i] 273 } 274 275 include := l.Include 276 if include == nil { 277 include = c.includeTemplateLibraryItem 278 } 279 280 sync := l.SyncItem 281 if sync == nil { 282 sync = c.SyncTemplateLibraryItem 283 } 284 285 for _, item := range items { 286 if item.Type != library.ItemTypeOVF { 287 continue 288 } 289 290 // Deploy source VM from library ovf item 291 deploy := Deploy{ 292 DeploymentSpec: DeploymentSpec{ 293 Name: vmtxSourceName(l.Destination, item), 294 DefaultDatastoreID: l.Destination.Storage[0].DatastoreID, 295 AcceptAllEULA: true, 296 }, 297 Target: l.Placement, 298 } 299 300 // Create library vmtx item from source VM 301 storage := &DiskStorage{ 302 Datastore: deploy.DeploymentSpec.DefaultDatastoreID, 303 } 304 spec := Template{ 305 Name: item.Name, 306 Library: l.Destination.ID, 307 DiskStorage: storage, 308 VMHomeStorage: storage, 309 Placement: &Placement{ 310 Folder: deploy.Target.FolderID, 311 ResourcePool: deploy.Target.ResourcePoolID, 312 }, 313 } 314 315 if !l.Include(item, existing[item.Name]) { 316 continue 317 } 318 319 if err = sync(ctx, item, &deploy, &spec); err != nil { 320 return err 321 } 322 } 323 324 return nil 325 }