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  }