Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Standalone VM #356

Merged
merged 22 commits into from
Feb 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ for all networks types for NSX-V and NSX-T backed VDCs [#354](https://github.com
* Added `NsxtImportableSwitch` structure with `GetNsxtImportableSwitchByName` and `GetAllNsxtImportableSwitches` to
lookup NSX-T segments for use in NSX-T Imported networks [#354](https://github.com/vmware/go-vcloud-director/pull/354)
* Added `vdc.IsNsxt` and `vdc.IsNsxv` methods to verify if VDC is backed by NSX-T or NSX-V [#354](https://github.com/vmware/go-vcloud-director/pull/354)
* Added types `types.CreateVmParams` and `types.InstantiateVmTemplateParams`
* Added Vdc methods `CreateStandaloneVMFromTemplate`, `CreateStandaloneVMFromTemplateAsync` `CreateStandaloneVm`, `CreateStandaloneVmAsync`
* Added Vdc methods `QueryVmByName`, `QueryVmById`, `QueryVmList`
* Added VM methods `Delete`, `DeleteAsync`

BREAKING CHANGES:
* Renamed `types.VM` to `types.Vm` to facilitate implementation of standalone VM

BUGS FIXED:
* Made IPAddress field for IPAddresses struct to array [#350](https://github.com/vmware/go-vcloud-director/pull/350)
Expand Down
2 changes: 1 addition & 1 deletion govcd/api.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
* Copyright 2021 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
*/

// Package govcd provides a simple binding for vCloud Director REST APIs.
Expand Down
24 changes: 21 additions & 3 deletions govcd/api_vcd_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// +build api openapi functional catalog vapp gateway network org query extnetwork task vm vdc system disk lb lbAppRule lbAppProfile lbServerPool lbServiceMonitor lbVirtualServer user search nsxv nsxt auth affinity ALL

/*
* Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
* Copyright 2021 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
dataclouder marked this conversation as resolved.
Show resolved Hide resolved
*/

package govcd
Expand Down Expand Up @@ -1033,6 +1033,24 @@ func (vcd *TestVCD) removeLeftoverEntities(entity CleanupEntity) {
vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
}
return
case "standaloneVm":
vm, err := vcd.vdc.QueryVmById(entity.Name) // The VM ID must be passed as Name
if IsNotFound(err) {
vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
return
}
if err != nil {
vcd.infoCleanup("removeLeftoverEntries: [ERROR] retrieving standalone VM '%s'. %s\n",
entity.Name, err)
return
}
err = vm.Delete()
if err != nil {
vcd.infoCleanup("removeLeftoverEntries: [ERROR] deleting VM '%s' : %s\n",
entity.Name, err)
return
}
vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
case "vm":
vapp, err := vcd.vdc.GetVAppByName(entity.Parent, true)
if err != nil {
Expand Down Expand Up @@ -1635,13 +1653,13 @@ func Test_splitParent(t *testing.T) {
}
}

func (vcd *TestVCD) findFirstVm(vapp VApp) (types.VM, string) {
func (vcd *TestVCD) findFirstVm(vapp VApp) (types.Vm, string) {
for _, vm := range vapp.VApp.Children.VM {
if vm.Name != "" {
return *vm, vm.Name
}
}
return types.VM{}, ""
return types.Vm{}, ""
}

func (vcd *TestVCD) findFirstVapp() VApp {
Expand Down
9 changes: 5 additions & 4 deletions govcd/entity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package govcd
import (
"fmt"
"reflect"
"strings"

. "gopkg.in/check.v1"
)
Expand Down Expand Up @@ -148,7 +149,7 @@ func (vcd *TestVCD) testFinderGetGenericEntity(def getterTestDefinition, check *
fmt.Printf("# Detected entity type %s\n", reflect.TypeOf(entity1))
}

check.Assert(reflect.TypeOf(entity1).String(), Equals, wantedType)
check.Assert(strings.ToLower(reflect.TypeOf(entity1).String()), Equals, strings.ToLower(wantedType))

check.Assert(entity1, NotNil)
check.Assert(entity1.name(), Equals, entityName)
Expand All @@ -165,7 +166,7 @@ func (vcd *TestVCD) testFinderGetGenericEntity(def getterTestDefinition, check *
check.Assert(entity2, NotNil)
check.Assert(entity2.name(), Equals, entityName)
check.Assert(entity2.id(), Equals, entityId)
check.Assert(reflect.TypeOf(entity2).String(), Equals, wantedType)
check.Assert(strings.ToLower(reflect.TypeOf(entity2).String()), Equals, strings.ToLower(wantedType))

// 3. Get the entity by Name or ID, using a known ID
if testVerbose {
Expand All @@ -178,7 +179,7 @@ func (vcd *TestVCD) testFinderGetGenericEntity(def getterTestDefinition, check *
check.Assert(entity3, NotNil)
check.Assert(entity3.name(), Equals, entityName)
check.Assert(entity3.id(), Equals, entityId)
check.Assert(reflect.TypeOf(entity3).String(), Equals, wantedType)
check.Assert(strings.ToLower(reflect.TypeOf(entity3).String()), Equals, strings.ToLower(wantedType))

// 4. Get the entity by Name or ID, using the entity name
if testVerbose {
Expand All @@ -191,7 +192,7 @@ func (vcd *TestVCD) testFinderGetGenericEntity(def getterTestDefinition, check *
check.Assert(entity4, NotNil)
check.Assert(entity4.name(), Equals, entityName)
check.Assert(entity4.id(), Equals, entityId)
check.Assert(reflect.TypeOf(entity4).String(), Equals, wantedType)
check.Assert(strings.ToLower(reflect.TypeOf(entity4).String()), Equals, strings.ToLower(wantedType))

// 5. Attempting a search by name with an invalid name
if testVerbose {
Expand Down
2 changes: 1 addition & 1 deletion govcd/filter_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func (vapp QueryVapp) GetMetadataValue(key string) string {
// --------------------------------------------------------------
func (vm QueryVm) GetHref() string { return vm.HREF }
func (vm QueryVm) GetName() string { return vm.Name }
func (vm QueryVm) GetType() string { return "VM" }
func (vm QueryVm) GetType() string { return "Vm" }
func (vm QueryVm) GetIp() string { return vm.IpAddress }
func (vm QueryVm) GetDate() string { return vm.DateCreated }
func (vm QueryVm) GetParentName() string { return vm.ContainerName }
Expand Down
19 changes: 18 additions & 1 deletion govcd/monitor.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
* Copyright 2021 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
*/

// Contains auxiliary functions to show library entities structure.
Expand Down Expand Up @@ -30,6 +30,7 @@ import (
// network
// externalNetwork
// vapp
// vm
// task
// Edge Gateway service configuration

Expand All @@ -53,6 +54,15 @@ func prettyVapp(vapp types.VApp) string {
return ""
}

// Returns a VM structure as JSON
func prettyVm(vm types.Vm) string {
byteBuf, err := json.MarshalIndent(vm, " ", " ")
if err == nil {
return fmt.Sprintf("%s\n", string(byteBuf))
}
return ""
}

// Returns an OrgUser structure as JSON
func prettyUser(user types.User) string {
byteBuf, err := json.MarshalIndent(user, " ", " ")
Expand Down Expand Up @@ -187,6 +197,13 @@ func ShowVapp(vapp types.VApp) {
out("screen", prettyVapp(vapp))
}

func LogVm(vm types.Vm) {
out("log", prettyVm(vm))
}

func ShowVm(vm types.Vm) {
out("screen", prettyVm(vm))
}
func ShowOrg(org types.Org) {
out("screen", prettyOrg(org))
}
Expand Down
5 changes: 2 additions & 3 deletions govcd/vapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,6 @@ func addNewVMW(vapp *VApp, name string, vappTemplate VAppTemplate,
// https://github.com/vmware/go-vcloud-director/issues/252
// ======================================================================
func (vapp *VApp) RemoveVM(vm VM) error {

err := vapp.Refresh()
if err != nil {
return fmt.Errorf("error refreshing vApp before removing VM: %s", err)
Expand Down Expand Up @@ -574,7 +573,7 @@ func (vapp *VApp) ChangeStorageProfile(name string) (Task, error) {
return Task{}, fmt.Errorf("error retrieving storage profile %s for vApp %s", name, vapp.VApp.Name)
}

newProfile := &types.VM{
newProfile := &types.Vm{
Name: vapp.VApp.Children.VM[0].Name,
StorageProfile: &storageProfileRef,
Xmlns: types.XMLNamespaceVCloud,
Expand All @@ -596,7 +595,7 @@ func (vapp *VApp) ChangeVMName(name string) (Task, error) {
return Task{}, fmt.Errorf("vApp doesn't contain any children, interrupting customization")
}

newName := &types.VM{
newName := &types.Vm{
Name: name,
Xmlns: types.XMLNamespaceVCloud,
}
Expand Down
2 changes: 1 addition & 1 deletion govcd/vapp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ func (vcd *TestVCD) Test_GetVM(check *C) {
var def = getterTestDefinition{
parentType: "VApp",
parentName: vapp.VApp.Name,
entityType: "VM",
entityType: "Vm",
entityName: vmName,
getByName: getByName,
getById: getById,
Expand Down
165 changes: 164 additions & 1 deletion govcd/vdc.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
* Copyright 2021 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
*/

package govcd
Expand Down Expand Up @@ -901,6 +901,169 @@ func (vdc *Vdc) GetVappList() []*types.ResourceReference {
return list
}

// CreateStandaloneVmAsync starts a standalone VM creation without a template, returning a task
func (vdc *Vdc) CreateStandaloneVmAsync(params *types.CreateVmParams) (Task, error) {
util.Logger.Printf("[TRACE] Vdc.CreateStandaloneVmAsync - Creating VM ")

if vdc.Vdc.HREF == "" {
return Task{}, fmt.Errorf("cannot create VM, Object VDC is empty")
}

href := ""
for _, link := range vdc.Vdc.Link {
if link.Type == types.MimeCreateVmParams && link.Rel == "add" {
href = link.HREF
break
}
}
if href == "" {
return Task{}, fmt.Errorf("error retrieving VM creation link from VDC %s", vdc.Vdc.Name)
}
if params == nil {
return Task{}, fmt.Errorf("empty parameters passed to standalone VM creation")
}
params.XmlnsOvf = types.XMLNamespaceOVF

return vdc.client.ExecuteTaskRequest(href, http.MethodPost, types.MimeCreateVmParams, "error creating standalone VM: %s", params)
}

// getVmFromTask finds a VM from a running standalone VM creation task
// It retrieves the VM owner (the hidden vApp), and from that one finds the new VM
func (vdc *Vdc) getVmFromTask(task Task, name string) (*VM, error) {
owner := task.Task.Owner.HREF
if owner == "" {
return nil, fmt.Errorf("task owner is null for VM %s", name)
}
vapp, err := vdc.GetVAppByHref(owner)
if err != nil {
return nil, err
}
if vapp.VApp.Children == nil {
return nil, ErrorEntityNotFound
}
if len(vapp.VApp.Children.VM) == 0 {
return nil, fmt.Errorf("vApp %s contains no VMs", vapp.VApp.Name)
}
if len(vapp.VApp.Children.VM) > 1 {
return nil, fmt.Errorf("vApp %s contains more than one VM", vapp.VApp.Name)
}
for _, child := range vapp.VApp.Children.VM {
util.Logger.Printf("[TRACE] Looking at: %s", child.Name)
return vapp.client.GetVMByHref(child.HREF)
}
return nil, ErrorEntityNotFound
}

// CreateStandaloneVm creates a standalone VM without a template
func (vdc *Vdc) CreateStandaloneVm(params *types.CreateVmParams) (*VM, error) {

task, err := vdc.CreateStandaloneVmAsync(params)
if err != nil {
return nil, err
}
err = task.WaitTaskCompletion()
if err != nil {
return nil, err
}
return vdc.getVmFromTask(task, params.Name)
}

// QueryVmByName finds a standalone VM by name
Didainius marked this conversation as resolved.
Show resolved Hide resolved
// The search fails either if there are more VMs with the wanted name, or if there are none
// It can also retrieve a standard VM (created from vApp)
func (vdc *Vdc) QueryVmByName(name string) (*VM, error) {
vmList, err := vdc.QueryVmList(types.VmQueryFilterOnlyDeployed)
if err != nil {
return nil, err
}
var foundVM []*types.QueryResultVMRecordType
for _, vm := range vmList {
if vm.Name == name {
foundVM = append(foundVM, vm)
}
}
if len(foundVM) == 0 {
return nil, ErrorEntityNotFound
}
if len(foundVM) > 1 {
return nil, fmt.Errorf("more than one VM found with name %s", name)
}
return vdc.client.GetVMByHref(foundVM[0].HREF)
}

// QueryVmById retrieves a standalone VM by ID
// It can also retrieve a standard VM (created from vApp)
func (vdc *Vdc) QueryVmById(id string) (*VM, error) {
vmList, err := vdc.QueryVmList(types.VmQueryFilterOnlyDeployed)
if err != nil {
return nil, err
}
var foundVM []*types.QueryResultVMRecordType
for _, vm := range vmList {
if equalIds(id, vm.ID, vm.HREF) {
foundVM = append(foundVM, vm)
}
}
if len(foundVM) == 0 {
return nil, ErrorEntityNotFound
}
if len(foundVM) > 1 {
return nil, fmt.Errorf("more than one VM found with ID %s", id)
}
return vdc.client.GetVMByHref(foundVM[0].HREF)
}

// CreateStandaloneVMFromTemplateAsync starts a standalone VM creation using a template
func (vdc *Vdc) CreateStandaloneVMFromTemplateAsync(params *types.InstantiateVmTemplateParams) (Task, error) {

util.Logger.Printf("[TRACE] Vdc.CreateStandaloneVMFromTemplateAsync - Creating VM")

if vdc.Vdc.HREF == "" {
return Task{}, fmt.Errorf("cannot create VM, provided VDC is empty")
}

href := ""
for _, link := range vdc.Vdc.Link {
if link.Type == types.MimeInstantiateVmTemplateParams && link.Rel == "add" {
href = link.HREF
break
}
}
if href == "" {
return Task{}, fmt.Errorf("error retrieving VM instantiate from template link from VDC %s", vdc.Vdc.Name)
}

if params.Name == "" {
return Task{}, fmt.Errorf("[CreateStandaloneVMFromTemplateAsync] missing VM name")
}
if params.SourcedVmTemplateItem == nil {
return Task{}, fmt.Errorf("[CreateStandaloneVMFromTemplateAsync] missing SourcedVmTemplateItem")
}
if params.SourcedVmTemplateItem.Source == nil {
return Task{}, fmt.Errorf("[CreateStandaloneVMFromTemplateAsync] missing vApp template Source")
}
if params.SourcedVmTemplateItem.Source.HREF == "" {
return Task{}, fmt.Errorf("[CreateStandaloneVMFromTemplateAsync] empty HREF in vApp template Source")
}
params.XmlnsOvf = types.XMLNamespaceOVF

return vdc.client.ExecuteTaskRequest(href, http.MethodPost, types.MimeInstantiateVmTemplateParams, "error creating standalone VM from template: %s", params)
}

// CreateStandaloneVMFromTemplate creates a standalone VM from a template
func (vdc *Vdc) CreateStandaloneVMFromTemplate(params *types.InstantiateVmTemplateParams) (*VM, error) {

task, err := vdc.CreateStandaloneVMFromTemplateAsync(params)
if err != nil {
return nil, err
}
err = task.WaitTaskCompletion()
if err != nil {
return nil, err
}
return vdc.getVmFromTask(task, params.Name)
}

// GetCapabilities allows to retrieve a list of VDC capabilities. It has a list of values. Some particularly useful are:
// * networkProvider - overlay stack responsible for providing network functionality. (NSX_V or NSX_T)
// * crossVdc - supports cross vDC network creation
Expand Down
2 changes: 2 additions & 0 deletions govcd/vdccomputepolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
// In UI called VM sizing policy. In API VDC compute policy
type VdcComputePolicy struct {
VdcComputePolicy *types.VdcComputePolicy
Href string
Didainius marked this conversation as resolved.
Show resolved Hide resolved
client *Client
}

Expand Down Expand Up @@ -48,6 +49,7 @@ func getVdcComputePolicyById(client *Client, id string) (*VdcComputePolicy, erro

vdcComputePolicy := &VdcComputePolicy{
VdcComputePolicy: &types.VdcComputePolicy{},
Href: urlRef.String(),
client: client,
}

Expand Down
Loading