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