Skip to content

Commit 10fb69d

Browse files
committed
WIP
Signed-off-by: Dainius Serplis <dserplis@vmware.com>
1 parent 2e4e75f commit 10fb69d

File tree

10 files changed

+151
-8
lines changed

10 files changed

+151
-8
lines changed

.changes/v2.23.0/652-improvements.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
* New public method `VApp.GetParentVDC` to retrieve parent VDC of vApp (previously it was private)
2+
[GH-642]
3+
* New methods `Catalog.CaptureVappTemplate`, `Catalog.CaptureVappTemplateAsync` and type
4+
`types.CaptureVAppParams` that add support for creating catalog template from existing vApp
5+
[GH-642]
6+
* New method `Org.GetVAppByHref` to retrieve a vApp by given HREF [GH-642]

govcd/catalog.go

+30
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,36 @@ func (cat *Catalog) UploadOvfByLink(ovfUrl, itemName, description string) (Task,
360360
return task, nil
361361
}
362362

363+
// CaptureVappTemplate captures a vApp template from an existing vApp
364+
func (cat *Catalog) CaptureVappTemplate(captureParams *types.CaptureVAppParams) (*VAppTemplate, error) {
365+
task, err := cat.CaptureVappTemplateAsync(captureParams)
366+
if err != nil {
367+
return nil, err
368+
}
369+
370+
err = task.WaitTaskCompletion()
371+
if err != nil {
372+
return nil, err
373+
}
374+
375+
// After the task is finished, owner field contains the resulting vApp template
376+
return cat.GetVappTemplateByHref(task.Task.Owner.HREF)
377+
}
378+
379+
func (cat *Catalog) CaptureVappTemplateAsync(captureParams *types.CaptureVAppParams) (Task, error) {
380+
util.Logger.Printf("[TRACE] Capturing vApp template to catalog %s", cat.Catalog.Name)
381+
captureTemplateHref := cat.client.VCDHREF
382+
captureTemplateHref.Path += fmt.Sprintf("/catalog/%s/action/captureVApp", extractUuid(cat.Catalog.ID))
383+
384+
captureParams.Xmlns = types.XMLNamespaceVCloud
385+
captureParams.XmlnsNs0 = types.XMLNamespaceOVF
386+
387+
util.Logger.Printf("[TRACE] Url for capturing vApp template: %s", captureTemplateHref.String())
388+
389+
return cat.client.ExecuteTaskRequest(captureTemplateHref.String(), http.MethodPost,
390+
types.MimeCaptureVappTemplateParams, "error capturing vApp Template: %s", captureParams)
391+
}
392+
363393
// Upload files for vCD created upload links. Different approach then vmdk file are
364394
// chunked (e.g. test.vmdk.000000000, test.vmdk.000000001 or test.vmdk). vmdk files are chunked if
365395
// in description file attribute ChunkSize is not zero.

govcd/catalog_test.go

+31
Original file line numberDiff line numberDiff line change
@@ -1490,3 +1490,34 @@ func (vcd *TestVCD) Test_CatalogCreateCompleteness(check *C) {
14901490
err = catalog.Delete(true, true)
14911491
check.Assert(err, IsNil)
14921492
}
1493+
1494+
func (vcd *TestVCD) Test_CaptureVapp(check *C) {
1495+
fmt.Printf("Running: %s\n", check.TestName())
1496+
1497+
vapp, vm := createNsxtVAppAndVm(vcd, check)
1498+
check.Assert(vapp, NotNil)
1499+
check.Assert(vm, NotNil)
1500+
1501+
// retrieve NSX-T Catalog
1502+
cat, err := vcd.org.GetCatalogByName(vcd.config.VCD.Catalog.NsxtBackedCatalogName, false)
1503+
check.Assert(err, IsNil)
1504+
check.Assert(cat, NotNil)
1505+
1506+
vAppCaptureParams := &types.CaptureVAppParams{
1507+
Name: check.TestName() + "vm-template",
1508+
Source: &types.Reference{
1509+
HREF: vapp.VApp.HREF,
1510+
},
1511+
CustomizationSection: types.CaptureVAppParamsCustomizationSection{
1512+
Info: "CustomizeOnInstantiate Settings",
1513+
CustomizeOnInstantiate: true,
1514+
},
1515+
CopyTpmOnInstantiate: addrOf(false),
1516+
}
1517+
1518+
templ, err := cat.CaptureVappTemplate(vAppCaptureParams)
1519+
check.Assert(err, IsNil)
1520+
check.Assert(templ, NotNil)
1521+
1522+
AddToCleanupList(templ.VAppTemplate.Name, "catalogItem", vcd.org.Org.Name+"|"+vcd.config.VCD.Catalog.NsxtBackedCatalogName, "Test_UploadOvf")
1523+
}

govcd/org.go

+14
Original file line numberDiff line numberDiff line change
@@ -600,3 +600,17 @@ func queryCatalogList(client *Client, filterFields map[string]string) ([]*types.
600600
util.Logger.Printf("[DEBUG] QueryCatalogList returned with : %#v and error: %s", catalogs, err)
601601
return catalogs, nil
602602
}
603+
604+
// GetVappByHref returns a vApp reference by running a vCD API call
605+
// If no valid vApp is found, it returns a nil VApp reference and an error
606+
func (org *Org) GetVAppByHref(vappHref string) (*VApp, error) {
607+
newVapp := NewVApp(org.client)
608+
609+
_, err := org.client.ExecuteRequest(vappHref, http.MethodGet,
610+
"", "error retrieving vApp: %s", nil, newVapp.VApp)
611+
612+
if err != nil {
613+
return nil, err
614+
}
615+
return newVapp, nil
616+
}

govcd/vapp.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ type DhcpSettings struct {
6060
}
6161

6262
// Returns the vdc where the vapp resides in.
63-
func (vapp *VApp) getParentVDC() (Vdc, error) {
63+
func (vapp *VApp) GetParentVDC() (Vdc, error) {
6464
for _, link := range vapp.VApp.Link {
6565
if (link.Type == types.MimeVDC || link.Type == types.MimeAdminVDC) && link.Rel == "up" {
6666

@@ -620,7 +620,7 @@ func (vapp *VApp) ChangeStorageProfile(name string) (Task, error) {
620620
return Task{}, fmt.Errorf("vApp doesn't contain any children, interrupting customization")
621621
}
622622

623-
vdc, err := vapp.getParentVDC()
623+
vdc, err := vapp.GetParentVDC()
624624
if err != nil {
625625
return Task{}, fmt.Errorf("error retrieving parent VDC for vApp %s", vapp.VApp.Name)
626626
}
@@ -1442,7 +1442,7 @@ func (vapp *VApp) getOrgInfo() (*TenantContext, error) {
14421442
return previous, nil
14431443
}
14441444
var err error
1445-
vdc, err := vapp.getParentVDC()
1445+
vdc, err := vapp.GetParentVDC()
14461446
if err != nil {
14471447
return nil, err
14481448
}
@@ -1514,7 +1514,7 @@ func (vapp *VApp) Rename(newName string) error {
15141514
}
15151515

15161516
func (vapp *VApp) getTenantContext() (*TenantContext, error) {
1517-
parentVdc, err := vapp.getParentVDC()
1517+
parentVdc, err := vapp.GetParentVDC()
15181518
if err != nil {
15191519
return nil, err
15201520
}

govcd/vapp_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func (vcd *TestVCD) TestGetParentVDC(check *C) {
2929
vapp, err := vcd.vdc.GetVAppByName(vcd.vapp.VApp.Name, false)
3030
check.Assert(err, IsNil)
3131

32-
vdc, err := vapp.getParentVDC()
32+
vdc, err := vapp.GetParentVDC()
3333

3434
check.Assert(err, IsNil)
3535
check.Assert(vdc.Vdc.Name, Equals, vcd.vdc.Vdc.Name)

govcd/vapptemplate.go

+23-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ package govcd
66

77
import (
88
"fmt"
9-
"github.com/vmware/go-vcloud-director/v2/types/v56"
10-
"github.com/vmware/go-vcloud-director/v2/util"
119
"net/http"
1210
"net/url"
11+
12+
"github.com/vmware/go-vcloud-director/v2/types/v56"
13+
"github.com/vmware/go-vcloud-director/v2/util"
1314
)
1415

1516
type VAppTemplate struct {
@@ -363,3 +364,23 @@ func (vAppTemplate *VAppTemplate) GetLease() (*types.LeaseSettingsSection, error
363364
}
364365
return &leaseSettings, nil
365366
}
367+
368+
// GetCatalogItemHref looks up Href for catalog item in vApp template
369+
func (vAppTemplate *VAppTemplate) GetCatalogItemHref() (string, error) {
370+
for _, link := range vAppTemplate.VAppTemplate.Link {
371+
if link.Rel == "catalogItem" && link.Type == types.MimeCatalogItem {
372+
return link.HREF, nil
373+
}
374+
}
375+
return "", fmt.Errorf("error finding Catalog Item link in vApp template %s", vAppTemplate.VAppTemplate.ID)
376+
}
377+
378+
// GetCatalogItemHref looks up Href for catalog item in vApp template
379+
func (vAppTemplate *VAppTemplate) GetCatalogItemId() (string, error) {
380+
href, err := vAppTemplate.GetCatalogItemHref()
381+
if err != nil {
382+
return "", err
383+
}
384+
385+
return fmt.Sprintf("urn:vcloud:catalogitem:%s", extractUuid(href)), nil
386+
}

govcd/vm.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,7 @@ func (vm *VM) GetParentVdc() (*Vdc, error) {
925925
return nil, fmt.Errorf("could not find parent vApp for VM %s: %s", vm.VM.Name, err)
926926
}
927927

928-
vdc, err := vapp.getParentVDC()
928+
vdc, err := vapp.GetParentVDC()
929929
if err != nil {
930930
return nil, fmt.Errorf("could not find parent vApp for VM %s: %s", vm.VM.Name, err)
931931
}

types/v56/constants.go

+2
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ const (
101101
MimeVM = "application/vnd.vmware.vcloud.vm+xml"
102102
// Mime for instantiate vApp template params
103103
MimeInstantiateVappTemplateParams = "application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml"
104+
// Mime for capture vApp into template
105+
MimeCaptureVappTemplateParams = "application/vnd.vmware.vcloud.captureVAppParams+xml"
104106
// Mime for clone vApp template params
105107
MimeCloneVapp = "application/vnd.vmware.vcloud.cloneVAppParams+xml"
106108
// Mime for product section

types/v56/types.go

+39
Original file line numberDiff line numberDiff line change
@@ -1604,6 +1604,45 @@ type VAppTemplateForUpdate struct {
16041604
Description string `xml:"Description,omitempty"` // Optional description.
16051605
}
16061606

1607+
// CaptureVAppParams is a configuration that can be supplied for capturing a vApp template from
1608+
// existing vApp
1609+
type CaptureVAppParams struct {
1610+
XMLName xml.Name `xml:"CaptureVAppParams"`
1611+
1612+
Xmlns string `xml:"xmlns,attr"`
1613+
XmlnsNs0 string `xml:"xmlns:ns0,attr,omitempty"`
1614+
1615+
// Name of vApp template
1616+
Name string `xml:"name,attr"`
1617+
// Description of vApp template
1618+
Description string `xml:"Description,omitempty"`
1619+
1620+
// Source vApp reference. At least HREF field must be set
1621+
Source *Reference `xml:"Source"`
1622+
1623+
// CustomizationSection section
1624+
CustomizationSection CaptureVAppParamsCustomizationSection `xml:"CustomizationSection"`
1625+
1626+
// TargetCatalogItem can be used to overwrite existing item. To overwrite an existing vApp
1627+
// template with the one created by this capture, place a reference to the existing template
1628+
// here. Otherwise, the operation creates a new vApp template.
1629+
TargetCatalogItem *Reference `xml:"TargetCatalogItem,omitempty"`
1630+
// CopyTpmOnInstantiate defines if TPM device is copied (`true`) to instantiated vApp from this
1631+
// template or `false` if a new TPM device is created for instantiated vApp.
1632+
// Note. Supported on VCD 10.4.2+
1633+
CopyTpmOnInstantiate *bool `xml:"CopyTpmOnInstantiate"`
1634+
}
1635+
1636+
// CaptureVAppParamsCustomizationSection settings for CaptureVAppParams type
1637+
type CaptureVAppParamsCustomizationSection struct {
1638+
// This field must contain value "CustomizeOnInstantiate Settings" so that API does not reject
1639+
// the request
1640+
Info string `xml:"ns0:Info,omitempty"`
1641+
// CustomizeOnInstantiate marks if instantiating this template applies customization settings
1642+
// (`true`). `false` creates an identical copy.
1643+
CustomizeOnInstantiate bool `xml:"CustomizeOnInstantiate"`
1644+
}
1645+
16071646
// VMDiskChange represents a virtual machine only with Disk setting update part
16081647
type VMDiskChange struct {
16091648
XMLName xml.Name `xml:"Vm"`

0 commit comments

Comments
 (0)