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  }