From 6585559bf760a9b344e2e5c2ecb368f2ec0cc81a Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 15 Jul 2019 15:39:07 +0300 Subject: [PATCH 01/80] Add missing file Signed-off-by: Vaidotas Bauzys --- .../go-vcloud-director/v2/govcd/lbapprule.go | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbapprule.go diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbapprule.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbapprule.go new file mode 100644 index 000000000..ca0cafae2 --- /dev/null +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbapprule.go @@ -0,0 +1,216 @@ +/* + * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "fmt" + "net/http" + + "github.com/vmware/go-vcloud-director/v2/types/v56" +) + +// CreateLBAppRule creates a load balancer application rule based on mandatory fields. It is a +// synchronous operation. It returns created object with all fields (including ID) populated or an error. +func (eGW *EdgeGateway) CreateLBAppRule(lbAppRuleConfig *types.LBAppRule) (*types.LBAppRule, error) { + if err := validateCreateLBAppRule(lbAppRuleConfig); err != nil { + return nil, err + } + + httpPath, err := eGW.buildProxiedEdgeEndpointURL(types.LBAppRulePath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + // We expect to get http.StatusCreated or if not an error of type types.NSXError + resp, err := eGW.client.ExecuteRequestWithCustomError(httpPath, http.MethodPost, types.AnyXMLMime, + "error creating load balancer application rule: %s", lbAppRuleConfig, &types.NSXError{}) + if err != nil { + return nil, err + } + + // Location header should look similar to: + // [/network/edges/edge-3/loadbalancer/config/applicationrules/applicationRule-4] + lbAppRuleId, err := extractNSXObjectIDFromPath(resp.Header.Get("Location")) + if err != nil { + return nil, err + } + + readAppRule, err := eGW.ReadLBAppRule(&types.LBAppRule{ID: lbAppRuleId}) + if err != nil { + return nil, fmt.Errorf("unable to retrieve application rule with ID (%s) after creation: %s", + readAppRule.ID, err) + } + return readAppRule, nil +} + +// ReadLBAppRule is able to find the types.LBAppRule type by Name and/or ID. +// If both - Name and ID are specified it performs a lookup by ID and returns an error if the specified name and found +// name do not match. +func (eGW *EdgeGateway) ReadLBAppRule(lbAppRuleConfig *types.LBAppRule) (*types.LBAppRule, error) { + if err := validateReadLBAppRule(lbAppRuleConfig); err != nil { + return nil, err + } + + httpPath, err := eGW.buildProxiedEdgeEndpointURL(types.LBAppRulePath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // Anonymous struct to unwrap response + lbAppRuleResponse := &struct { + LbAppRules []*types.LBAppRule `xml:"applicationRule"` + }{} + + // This query returns all application rules as the API does not have filtering options + _, err = eGW.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, + "unable to read load balancer application rule: %s", nil, lbAppRuleResponse) + if err != nil { + return nil, err + } + + // Search for application rule by ID or by Name + for _, rule := range lbAppRuleResponse.LbAppRules { + // If ID was specified for lookup - look for the same ID + if lbAppRuleConfig.ID != "" && rule.ID == lbAppRuleConfig.ID { + return rule, nil + } + + // If Name was specified for lookup - look for the same Name + if lbAppRuleConfig.Name != "" && rule.Name == lbAppRuleConfig.Name { + // We found it by name. Let's verify if search ID was specified and it matches the lookup object + if lbAppRuleConfig.ID != "" && rule.ID != lbAppRuleConfig.ID { + return nil, fmt.Errorf("load balancer application rule was found by name (%s)"+ + ", but its ID (%s) does not match specified ID (%s)", + rule.Name, rule.ID, lbAppRuleConfig.ID) + } + return rule, nil + } + } + + return nil, ErrorEntityNotFound +} + +// ReadLBAppRuleById wraps ReadLBAppRule and needs only an ID for lookup +func (eGW *EdgeGateway) ReadLBAppRuleByID(id string) (*types.LBAppRule, error) { + return eGW.ReadLBAppRule(&types.LBAppRule{ID: id}) +} + +// ReadLBAppRuleByName wraps ReadLBAppRule and needs only a Name for lookup +func (eGW *EdgeGateway) ReadLBAppRuleByName(name string) (*types.LBAppRule, error) { + return eGW.ReadLBAppRule(&types.LBAppRule{Name: name}) +} + +// UpdateLBAppRule updates types.LBAppRule with all fields. At least name or ID must be specified. +// If both - Name and ID are specified it performs a lookup by ID and returns an error if the specified name and found +// name do not match. +func (eGW *EdgeGateway) UpdateLBAppRule(lbAppRuleConfig *types.LBAppRule) (*types.LBAppRule, error) { + err := validateUpdateLBAppRule(lbAppRuleConfig) + if err != nil { + return nil, err + } + + lbAppRuleConfig.ID, err = eGW.getLBAppRuleIDByNameID(lbAppRuleConfig.Name, lbAppRuleConfig.ID) + if err != nil { + return nil, fmt.Errorf("cannot update load balancer application rule: %s", err) + } + + httpPath, err := eGW.buildProxiedEdgeEndpointURL(types.LBAppRulePath + lbAppRuleConfig.ID) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // Result should be 204, if not we expect an error of type types.NSXError + _, err = eGW.client.ExecuteRequestWithCustomError(httpPath, http.MethodPut, types.AnyXMLMime, + "error while updating load balancer application rule : %s", lbAppRuleConfig, &types.NSXError{}) + if err != nil { + return nil, err + } + + readAppRule, err := eGW.ReadLBAppRule(&types.LBAppRule{ID: lbAppRuleConfig.ID}) + if err != nil { + return nil, fmt.Errorf("unable to retrieve application rule with ID (%s) after update: %s", + readAppRule.ID, err) + } + return readAppRule, nil +} + +// DeleteLBAppRule is able to delete the types.LBAppRule type by Name and/or ID. +// If both - Name and ID are specified it performs a lookup by ID and returns an error if the specified name and found +// name do not match. +func (eGW *EdgeGateway) DeleteLBAppRule(lbAppRuleConfig *types.LBAppRule) error { + err := validateDeleteLBAppRule(lbAppRuleConfig) + if err != nil { + return err + } + + lbAppRuleConfig.ID, err = eGW.getLBAppRuleIDByNameID(lbAppRuleConfig.Name, lbAppRuleConfig.ID) + if err != nil { + return fmt.Errorf("cannot update load balancer application rule: %s", err) + } + + httpPath, err := eGW.buildProxiedEdgeEndpointURL(types.LBAppRulePath + lbAppRuleConfig.ID) + if err != nil { + return fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + return eGW.client.ExecuteRequestWithoutResponse(httpPath, http.MethodDelete, "application/xml", + "unable to delete application rule: %s", nil) +} + +// DeleteLBAppRuleById wraps DeleteLBAppRule and requires only ID for deletion +func (eGW *EdgeGateway) DeleteLBAppRuleByID(id string) error { + return eGW.DeleteLBAppRule(&types.LBAppRule{ID: id}) +} + +// DeleteLBAppRuleByName wraps DeleteLBAppRule and requires only Name for deletion +func (eGW *EdgeGateway) DeleteLBAppRuleByName(name string) error { + return eGW.DeleteLBAppRule(&types.LBAppRule{Name: name}) +} + +func validateCreateLBAppRule(lbAppRuleConfig *types.LBAppRule) error { + if lbAppRuleConfig.Name == "" { + return fmt.Errorf("load balancer application rule Name cannot be empty") + } + + return nil +} + +func validateReadLBAppRule(lbAppRuleConfig *types.LBAppRule) error { + if lbAppRuleConfig.ID == "" && lbAppRuleConfig.Name == "" { + return fmt.Errorf("to read load balancer application rule at least one of `ID`, `Name`" + + " fields must be specified") + } + + return nil +} + +func validateUpdateLBAppRule(lbAppRuleConfig *types.LBAppRule) error { + // Update and create have the same requirements for now + return validateCreateLBAppRule(lbAppRuleConfig) +} + +func validateDeleteLBAppRule(lbAppRuleConfig *types.LBAppRule) error { + // Read and delete have the same requirements for now + return validateReadLBAppRule(lbAppRuleConfig) +} + +// getLBAppRuleIDByNameID checks if at least name or ID is set and returns the ID. +// If the ID is specified - it passes through the ID. If only name was specified +// it will lookup the object by name and return the ID. +func (eGW *EdgeGateway) getLBAppRuleIDByNameID(name, id string) (string, error) { + if name == "" && id == "" { + return "", fmt.Errorf("at least Name or ID must be specific to find load balancer "+ + "application rule got name (%s) ID (%s)", name, id) + } + if id != "" { + return id, nil + } + + // if only name was specified, ID must be found, because only ID can be used in request path + readlbAppRule, err := eGW.ReadLBAppRuleByName(name) + if err != nil { + return "", fmt.Errorf("unable to find load balancer application rule by name: %s", err) + } + return readlbAppRule.ID, nil +} From 989c183180eb391a9d83e0271d96ee1e97dfa07f Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 2 Dec 2019 16:23:43 +0200 Subject: [PATCH 02/80] Draft version of last design Signed-off-by: Vaidotas Bauzys --- go.mod | 3 + go.sum | 2 - vcd/config.go | 28 + vcd/provider.go | 1 + vcd/resource_vcd_vapp_vm.go | 783 ++++++++++++------ vcd/resource_vcd_vm_internal_disk.go | 401 +++++++++ .../go-vcloud-director/v2/govcd/api_vcd.go | 2 +- .../go-vcloud-director/v2/govcd/system.go | 2 +- .../vmware/go-vcloud-director/v2/govcd/vm.go | 142 ++++ .../go-vcloud-director/v2/types/v56/types.go | 170 +++- vendor/modules.txt | 2 +- 11 files changed, 1269 insertions(+), 267 deletions(-) create mode 100644 vcd/resource_vcd_vm_internal_disk.go diff --git a/go.mod b/go.mod index b4967c6be..4c0a2d35d 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,9 @@ module github.com/terraform-providers/terraform-provider-vcd/v2 go 1.13 require ( + github.com/davecgh/go-spew v1.1.1 github.com/hashicorp/terraform-plugin-sdk v1.0.0 github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.5 ) + +replace github.com/vmware/go-vcloud-director/v2 => ../go-vcloud-director diff --git a/go.sum b/go.sum index 0eff6f268..3950c61a4 100644 --- a/go.sum +++ b/go.sum @@ -187,8 +187,6 @@ github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.5 h1:hQDzS94JgM2E36jxetpcGzeRJBK3M9ergjej9wPBDhk= -github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.5/go.mod h1:VqfkCixIzRmj4EzF2yFJKB+aKDW6GkXlLbFh5xZ+qqs= github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.1.0 h1:uJwc9HiBOCpoKIObTQaLR+tsEXx1HBHnOsOOpcdhZgw= github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= diff --git a/vcd/config.go b/vcd/config.go index 327039934..1e0a10606 100644 --- a/vcd/config.go +++ b/vcd/config.go @@ -165,6 +165,34 @@ func (cli *VCDClient) unLockParentVapp(d *schema.ResourceData) { vcdMutexKV.Unlock(key) } +// function lockParentVm locks using vapp_name and vm_name names existing in resource parameters. +// Parent means the resource belongs to the VM being locked +func (cli *VCDClient) lockParentVm(d *schema.ResourceData) { + vappName := d.Get("vapp_name").(string) + if vappName == "" { + panic("vApp name not found") + } + vmName := d.Get("vm_name").(string) + if vmName == "" { + panic("vmName name not found") + } + key := fmt.Sprintf("org:%s|vdc:%s|vapp:%s|vm:%s", cli.getOrgName(d), cli.getVdcName(d), vappName, vmName) + vcdMutexKV.Lock(key) +} + +func (cli *VCDClient) unLockParentVm(d *schema.ResourceData) { + vappName := d.Get("vapp_name").(string) + if vappName == "" { + panic("vApp name not found") + } + vmName := d.Get("vm_name").(string) + if vmName == "" { + panic("vmName name not found") + } + key := fmt.Sprintf("org:%s|vdc:%s|vapp:%s|vm:%s", cli.getOrgName(d), cli.getVdcName(d), vappName, vmName) + vcdMutexKV.Unlock(key) +} + // function lockParentEdgeGtw locks using edge_gateway name existing in resource parameters. // Parent means the resource belongs to the edge gateway being locked func (cli *VCDClient) lockParentEdgeGtw(d *schema.ResourceData) { diff --git a/vcd/provider.go b/vcd/provider.go index f222d265c..890d41ba4 100644 --- a/vcd/provider.go +++ b/vcd/provider.go @@ -128,6 +128,7 @@ func Provider() terraform.ResourceProvider { "vcd_nsxv_dnat": resourceVcdNsxvDnat(), // 2.5 "vcd_nsxv_snat": resourceVcdNsxvSnat(), // 2.5 "vcd_nsxv_firewall_rule": resourceVcdNsxvFirewallRule(), // 2.5 + "vcd_vm_internal_disk": resourceVmInternalDisk(), // 2.6 }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index 3c23bd9d5..b301db684 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -3,6 +3,8 @@ package vcd import ( "bytes" "fmt" + "github.com/davecgh/go-spew/spew" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "log" "net" "sort" @@ -11,271 +13,324 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/hashcode" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "github.com/vmware/go-vcloud-director/v2/govcd" "github.com/vmware/go-vcloud-director/v2/types/v56" ) -func resourceVcdVAppVm() *schema.Resource { - return &schema.Resource{ - Create: resourceVcdVAppVmCreate, - Update: resourceVcdVAppVmUpdate, - Read: resourceVcdVAppVmRead, - Delete: resourceVcdVAppVmDelete, - Importer: &schema.ResourceImporter{ - State: resourceVcdVappVmImport, +var allSchema = map[string]*schema.Schema{ + "vapp_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The vApp this VM belongs to", + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "A name for the VM, unique within the vApp", + }, + "computer_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Computer name to assign to this virtual machine", + }, + "org": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "The name of organization to use, optional if defined at provider " + + "level. Useful when connected as sysadmin working across different organizations", + }, + "vdc": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "The name of VDC to use, optional if defined at provider level", + }, + "template_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The name of the vApp Template to use", + }, + "catalog_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The catalog name in which to find the given vApp Template", + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The VM description", + // Note: description is read only, as we lack the needed fields to set it at creation. + // Currently, this field has the description of the OVA used to create the VM + }, + "memory": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Description: "The amount of RAM (in MB) to allocate to the VM", + ValidateFunc: validateMultipleOf4(), + }, + "cpus": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 1, + Description: "The number of virtual CPUs to allocate to the VM", + }, + "cpu_cores": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 1, + Description: "The number of cores per socket", + }, + "ip": &schema.Schema{ + Computed: true, + ConflictsWith: []string{"network"}, + Deprecated: "In favor of network", + DiffSuppressFunc: suppressIfIPIsOneOf(), + ForceNew: true, + Optional: true, + Type: schema.TypeString, + }, + "mac": { + Computed: true, + ConflictsWith: []string{"network"}, + Deprecated: "In favor of network", + Optional: true, + Type: schema.TypeString, + }, + "initscript": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "Script to run on initial boot or with customization.force=true set", + }, + "metadata": { + Type: schema.TypeMap, + Optional: true, + // For now underlying go-vcloud-director repo only supports + // a value of type String in this map. + Description: "Key value map of metadata to assign to this VM", + }, + "href": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "VM Hyper Reference", + }, + "accept_all_eulas": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Automatically accept EULA if OVA has it", + }, + "power_on": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "A boolean value stating if this VM should be powered on", + }, + "storage_profile": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Storage profile to override the default one", + }, + "network_href": &schema.Schema{ + ConflictsWith: []string{"network"}, + Deprecated: "In favor of network", + Type: schema.TypeString, + Optional: true, + }, + "network": { + ConflictsWith: []string{"ip", "network_name", "vapp_network_name", "network_href"}, + Optional: true, + Type: schema.TypeList, + Description: " A block to define network interface. Multiple can be used.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Required: true, + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"vapp", "org", "none"}, false), + Description: "Network type to use: 'vapp', 'org' or 'none'. Use 'vapp' for vApp network, 'org' to attach Org VDC network. 'none' for empty NIC.", + }, + "ip_allocation_mode": { + Optional: true, + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"POOL", "DHCP", "MANUAL", "NONE"}, false), + Description: "IP address allocation mode. One of POOL, DHCP, MANUAL, NONE", + }, + "name": { + ForceNew: false, + Optional: true, // In case of type = none it is not required + Type: schema.TypeString, + Description: "Name of the network this VM should connect to. Always required except for `type` `NONE`", + }, + "ip": { + Computed: true, + Optional: true, + Type: schema.TypeString, + ValidateFunc: checkEmptyOrSingleIP(), // Must accept empty string to ease using HCL interpolation + Description: "IP of the VM. Settings depend on `ip_allocation_mode`. Omitted or empty for DHCP, POOL, NONE. Required for MANUAL", + }, + "is_primary": { + Default: false, + Optional: true, + // By default if the value is omitted it will report schema change + // on every terraform operation. The below function + // suppresses such cases "" => "false" when applying. + DiffSuppressFunc: falseBoolSuppress(), + Type: schema.TypeBool, + Description: "Set to true if network interface should be primary. First network card in the list will be primary by default", + }, + "mac": { + Computed: true, + Optional: true, + Type: schema.TypeString, + Description: "Mac address of network interface", + }, + }, }, - - Schema: map[string]*schema.Schema{ - "vapp_name": &schema.Schema{ + }, + "network_name": &schema.Schema{ + ConflictsWith: []string{"network"}, + Deprecated: "In favor of network", + ForceNew: true, + Optional: true, + Type: schema.TypeString, + }, + "vapp_network_name": &schema.Schema{ + ConflictsWith: []string{"network"}, + Deprecated: "In favor of network", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "disk": { + Type: schema.TypeSet, + Elem: &schema.Resource{Schema: map[string]*schema.Schema{ + "name": { Type: schema.TypeString, Required: true, - ForceNew: true, - Description: "The vApp this VM belongs to", + Description: "Independent disk name", }, - "name": &schema.Schema{ + "bus_number": { Type: schema.TypeString, Required: true, - ForceNew: true, - Description: "A name for the VM, unique within the vApp", + Description: "Bus number on which to place the disk controller", }, - "computer_name": &schema.Schema{ + "unit_number": { Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "Computer name to assign to this virtual machine", - }, - "org": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Description: "The name of organization to use, optional if defined at provider " + - "level. Useful when connected as sysadmin working across different organizations", + Required: true, + Description: "Unit number (slot) on the bus specified by BusNumber", }, - "vdc": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Description: "The name of VDC to use, optional if defined at provider level", + }}, + Optional: true, + Set: resourceVcdVmIndependentDiskHash, + }, + "override_template_disk": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Description: " A block to match internal_disk interface in template. Multiple can be used. Disk will be matched by bus_type, bus_number and unit_number.", + Elem: &schema.Resource{Schema: map[string]*schema.Schema{ + "bus_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"ide", "parallel", "sas", "paravirtual", "sata"}, true), + Description: "The type of disk controller. Possible values: ide, parallel( LSI Logic Parallel SCSI), sas(LSI Logic SAS (SCSI)), paravirtual(Paravirtual (SCSI)), sata", }, - "template_name": &schema.Schema{ - Type: schema.TypeString, - Required: true, + "size_in_mb": { + Type: schema.TypeInt, ForceNew: true, - Description: "The name of the vApp Template to use", - }, - "catalog_name": &schema.Schema{ - Type: schema.TypeString, Required: true, - ForceNew: true, - Description: "The catalog name in which to find the given vApp Template", - }, - "description": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - Description: "The VM description", - // Note: description is read only, as we lack the needed fields to set it at creation. - // Currently, this field has the description of the OVA used to create the VM - }, - "memory": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - Description: "The amount of RAM (in MB) to allocate to the VM", - ValidateFunc: validateMultipleOf4(), + Description: "The size of the disk in MB.", }, - "cpus": &schema.Schema{ + "bus_number": { Type: schema.TypeInt, - Optional: true, - Default: 1, - Description: "The number of virtual CPUs to allocate to the VM", + ForceNew: true, + Required: true, + Description: "The number of the SCSI or IDE controller itself.", }, - "cpu_cores": &schema.Schema{ + "unit_number": { Type: schema.TypeInt, - Optional: true, - Default: 1, - Description: "The number of cores per socket", - }, - "ip": &schema.Schema{ - Computed: true, - ConflictsWith: []string{"network"}, - Deprecated: "In favor of network", - DiffSuppressFunc: suppressIfIPIsOneOf(), - ForceNew: true, - Optional: true, - Type: schema.TypeString, - }, - "mac": { - Computed: true, - ConflictsWith: []string{"network"}, - Deprecated: "In favor of network", - Optional: true, - Type: schema.TypeString, - }, - "initscript": &schema.Schema{ - Type: schema.TypeString, - Optional: true, ForceNew: true, - Description: "Script to run on initial boot or with customization.force=true set", - }, - "metadata": { - Type: schema.TypeMap, - Optional: true, - // For now underlying go-vcloud-director repo only supports - // a value of type String in this map. - Description: "Key value map of metadata to assign to this VM", - }, - "href": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "VM Hyper Reference", + Required: true, + Description: "The device number on the SCSI or IDE controller of the disk.", }, - "accept_all_eulas": &schema.Schema{ + "thin_provisioned": { Type: schema.TypeBool, + ForceNew: true, Optional: true, - Default: true, - Description: "Automatically accept EULA if OVA has it", + Description: "Specifies whether the disk storage is pre-allocated or allocated on demand.", }, - "power_on": &schema.Schema{ - Type: schema.TypeBool, + "iops": { + Type: schema.TypeInt, + ForceNew: true, Optional: true, - Default: true, - Description: "A boolean value stating if this VM should be powered on", + Description: "Specifies the IOPS for the disk. Default - 0.", }, "storage_profile": &schema.Schema{ Type: schema.TypeString, + ForceNew: true, Optional: true, - Computed: true, - Description: "Storage profile to override the default one", - }, - "network_href": &schema.Schema{ - ConflictsWith: []string{"network"}, - Deprecated: "In favor of network", - Type: schema.TypeString, - Optional: true, - }, - "network": { - ConflictsWith: []string{"ip", "network_name", "vapp_network_name", "network_href"}, - Optional: true, - Type: schema.TypeList, - Description: " A block to define network interface. Multiple can be used.", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "type": { - Required: true, - Type: schema.TypeString, - ValidateFunc: validation.StringInSlice([]string{"vapp", "org", "none"}, false), - Description: "Network type to use: 'vapp', 'org' or 'none'. Use 'vapp' for vApp network, 'org' to attach Org VDC network. 'none' for empty NIC.", - }, - "ip_allocation_mode": { - Optional: true, - Type: schema.TypeString, - ValidateFunc: validation.StringInSlice([]string{"POOL", "DHCP", "MANUAL", "NONE"}, false), - Description: "IP address allocation mode. One of POOL, DHCP, MANUAL, NONE", - }, - "name": { - ForceNew: false, - Optional: true, // In case of type = none it is not required - Type: schema.TypeString, - Description: "Name of the network this VM should connect to. Always required except for `type` `NONE`", - }, - "ip": { - Computed: true, - Optional: true, - Type: schema.TypeString, - ValidateFunc: checkEmptyOrSingleIP(), // Must accept empty string to ease using HCL interpolation - Description: "IP of the VM. Settings depend on `ip_allocation_mode`. Omitted or empty for DHCP, POOL, NONE. Required for MANUAL", - }, - "is_primary": { - Default: false, - Optional: true, - // By default if the value is omitted it will report schema change - // on every terraform operation. The below function - // suppresses such cases "" => "false" when applying. - DiffSuppressFunc: falseBoolSuppress(), - Type: schema.TypeBool, - Description: "Set to true if network interface should be primary. First network card in the list will be primary by default", - }, - "mac": { - Computed: true, - Optional: true, - Type: schema.TypeString, - Description: "Mac address of network interface", - }, - }, - }, - }, - "network_name": &schema.Schema{ - ConflictsWith: []string{"network"}, - Deprecated: "In favor of network", - ForceNew: true, - Optional: true, - Type: schema.TypeString, - }, - "vapp_network_name": &schema.Schema{ - ConflictsWith: []string{"network"}, - Deprecated: "In favor of network", - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - "disk": { - Type: schema.TypeSet, - Elem: &schema.Resource{Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - Description: "Independent disk name", - }, - "bus_number": { - Type: schema.TypeString, - Required: true, - Description: "Bus number on which to place the disk controller", - }, - "unit_number": { - Type: schema.TypeString, - Required: true, - Description: "Unit number (slot) on the bus specified by BusNumber", - }, - }}, - Optional: true, - Set: resourceVcdVmIndependentDiskHash, - }, - "expose_hardware_virtualization": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: false, - Description: "Expose hardware-assisted CPU virtualization to guest OS.", - }, - "guest_properties": { - Type: schema.TypeMap, - Optional: true, - Description: "Key/value settings for guest properties", + Description: "Storage profile to override the VM default one", }, - "customization": &schema.Schema{ - Optional: true, - MinItems: 1, - MaxItems: 1, - Type: schema.TypeList, - Description: "Guest customization block", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "force": { - ValidateFunc: noopValueWarningValidator(true, - "Using 'true' value for field 'vcd_vapp_vm.customization.force' will reboot VM on every 'terraform apply' operation"), - Type: schema.TypeBool, - Optional: true, - Default: false, - // This settings is used as a 'flag' and it does not matter what is set in the - // state. If it is 'true' - then it means that 'update' procedure must set the - // VM for customization at next boot and reboot it. - DiffSuppressFunc: suppressFalse(), - Description: "'true' value will cause the VM to reboot on every 'apply' operation", - }, - }, + }}, + }, + "expose_hardware_virtualization": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Expose hardware-assisted CPU virtualization to guest OS.", + }, + "guest_properties": { + Type: schema.TypeMap, + Optional: true, + Description: "Key/value settings for guest properties", + }, + "customization": &schema.Schema{ + Optional: true, + MinItems: 1, + MaxItems: 1, + Type: schema.TypeList, + Description: "Guest customization block", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "force": { + ValidateFunc: noopValueWarningValidator(true, + "Using 'true' value for field 'vcd_vapp_vm.customization.force' will reboot VM on every 'terraform apply' operation"), + Type: schema.TypeBool, + Optional: true, + Default: false, + // This settings is used as a 'flag' and it does not matter what is set in the + // state. If it is 'true' - then it means that 'update' procedure must set the + // VM for customization at next boot and reboot it. + DiffSuppressFunc: suppressFalse(), + Description: "'true' value will cause the VM to reboot on every 'apply' operation", }, }, }, + }, +} + +func resourceVcdVAppVm() *schema.Resource { + + return &schema.Resource{ + Create: resourceVcdVAppVmCreate, + Update: resourceVcdVAppVmUpdate, + Read: resourceVcdVAppVmRead, + Delete: resourceVcdVAppVmDelete, + Importer: &schema.ResourceImporter{ + State: resourceVcdVappVmImport, + }, + + Schema: allSchema, } } @@ -417,8 +472,22 @@ func resourceVcdVAppVmCreate(d *schema.ResourceData, meta interface{}) error { d.SetId(vm.VM.ID) + // Add disks new ones or/and update created in template + err = addInternalDisks(d, meta, *vm) + if err != nil { + d.Set("override_template_disk", nil) + return fmt.Errorf("error managing internal disks : %s", err) + } + /* else { + errReadInternalDisk := updateStateOfInternalDisks(d, *vm) + if errReadInternalDisk != nil { + d.Set("internal_disk", nil) + return fmt.Errorf("error reading interal disks : %s", errReadInternalDisk) + } + }*/ + // TODO do not trigger resourceVcdVAppVmUpdate from create. These must be separate actions. - err = resourceVcdVAppVmUpdateExecute(d, meta) + err = resourceVcdVAppVmUpdateExecute(d, meta, true) if err != nil { errAttachedDisk := updateStateOfAttachedDisks(d, *vm, vdc) if errAttachedDisk != nil { @@ -427,6 +496,7 @@ func resourceVcdVAppVmCreate(d *schema.ResourceData, meta interface{}) error { } return err } + return nil } @@ -555,10 +625,10 @@ func resourceVcdVAppVmUpdate(d *schema.ResourceData, meta interface{}) error { vcdClient.lockParentVapp(d) defer vcdClient.unLockParentVapp(d) - return resourceVcdVAppVmUpdateExecute(d, meta) + return resourceVcdVAppVmUpdateExecute(d, meta, false) } -func resourceVcdVAppVmUpdateExecute(d *schema.ResourceData, meta interface{}) error { +func resourceVcdVAppVmUpdateExecute(d *schema.ResourceData, meta interface{}, isCallAfterCreation bool) error { vcdClient := meta.(*VCDClient) @@ -643,10 +713,19 @@ func resourceVcdVAppVmUpdateExecute(d *schema.ResourceData, meta interface{}) er } } + /* if isCallAfterCreation == false && d.HasChange("internal_disk") { + err = updateInternalDisks(d, meta, *vm) + if err != nil { + d.Set("internal_disk", nil) + return fmt.Errorf("error updating itneranl disks : %s", err) + } + }*/ + if d.HasChange("memory") || d.HasChange("cpus") || d.HasChange("cpu_cores") || d.HasChange("power_on") || d.HasChange("disk") || d.HasChange("expose_hardware_virtualization") || d.HasChange("network") || d.HasChange("computer_name") { - log.Printf("[TRACE] VM %s has changes: memory(%t), cpus(%t), cpu_cores(%t), power_on(%t), disk(%t), expose_hardware_virtualization(%t), network(%t), computer_name(%t)", + log.Printf("[TRACE] VM %s has changes: memory(%t), cpus(%t), cpu_cores(%t), power_on(%t), disk(%t), expose_hardware_virtualization(%t),"+ + " network(%t), computer_name(%t)", vm.VM.Name, d.HasChange("memory"), d.HasChange("cpus"), d.HasChange("cpu_cores"), d.HasChange("power_on"), d.HasChange("disk"), d.HasChange("expose_hardware_virtualization"), d.HasChange("network"), d.HasChange("computer_name")) @@ -1003,10 +1082,16 @@ func genericVcdVAppVmRead(d *schema.ResourceData, meta interface{}, origin strin return fmt.Errorf("[VM read] unable to set guest properties in state: %s", err) } + /* err = updateStateOfInternalDisks(d, *vm) + if err != nil { + d.Set("internal_disk", nil) + return fmt.Errorf("[VM read] error reading internal disks : %s", err) + }*/ + err = updateStateOfAttachedDisks(d, *vm, vdc) if err != nil { d.Set("disk", nil) - return fmt.Errorf("[VM read] error updating attached disks : %s", err) + return fmt.Errorf("[VM read] error reading attached disks : %s", err) } guestCustomizationSection, err := vm.GetGuestCustomizationSection() @@ -1042,6 +1127,214 @@ func updateStateOfAttachedDisks(d *schema.ResourceData, vm govcd.VM, vdc *govcd. return d.Set("disk", transformed) } +func updateStateOfInternalDisks(d *schema.ResourceData, vm govcd.VM) error { + + err := vm.Refresh() + if err != nil { + return err + } + + existingInternalDisks := vm.VM.VmSpecSection.DiskSection.DiskSettings + //transformed := schema.NewSet(resourceVcdVmInternalDiskHash, []interface{}{}) + transformed := schema.NewSet(schema.HashResource(allSchema["internal_disk"].Elem.(*schema.Resource)), []interface{}{}) + + str2 := spew.Sdump(existingInternalDisks) + log.Printf("####UpdateState: %s", str2) + + str3 := spew.Sdump(d.State()) + log.Printf("####State-updateStateOfInternalDisks1: %s", str3) + log.Printf("####State-updateStateOfInternalDisks2: %#v", d.State().Attributes) + + for _, internalDisk := range existingInternalDisks { + + busType, err := strconv.Atoi(internalDisk.AdapterType) + if err != nil { + return err + } + + newValues := map[string]interface{}{ + "id": internalDisk.DiskId, + "bus_type": busType, + "size_in_mb": int(internalDisk.SizeMb), + "bus_number": internalDisk.BusNumber, + "unit_number": internalDisk.UnitNumber, + "iops": int(*internalDisk.Iops), + "thin_provisioned": strconv.FormatBool(*internalDisk.ThinProvisioned), + "storage_profile": internalDisk.StorageProfile.Name, + } + + //internalDiskProvidedConfig := internalDisk.(map[string]interface{}) + /* a1, a2 := d.GetOk("storage_profile") + util.Logger.Printf("##################3 %#v, %#v", a1, a2) + if value, ok := d.GetOk("storage_profile"); ok { + newValues["storage_profile"] = value + } + a1, a2 = d.GetOk("thin_provisioned") + util.Logger.Printf("##################3 %#v, %#v", a1, a2) + if value, ok := d.GetOk("thin_provisioned"); ok { + newValues["thin_provisioned"] = value + } + */ + transformed.Add(newValues) + } + + return d.Set("internal_disk", transformed) +} + +func addInternalDisks(d *schema.ResourceData, meta interface{}, vm govcd.VM) error { + vcdClient := meta.(*VCDClient) + + _, vdc, err := vcdClient.GetOrgAndVdcFromResource(d) + if err != nil { + return fmt.Errorf(errorRetrievingOrgAndVdc, err) + } + + diskSettings := vm.VM.VmSpecSection.DiskSection.DiskSettings + + var storageProfilePrt *types.Reference + var overrideVmDefault bool + + internalDisksList := d.Get("override_template_disk").(*schema.Set).List() + + if len(internalDisksList) == 0 { + return nil + } + + for _, internalDisk := range internalDisksList { + internalDiskProvidedConfig := internalDisk.(map[string]interface{}) + diskCreatedByTemplate := getMatchedDisk(internalDiskProvidedConfig, diskSettings) + + storageProfileName := internalDiskProvidedConfig["storage_profile"].(string) + if storageProfileName != "" { + storageProfile, err := vdc.FindStorageProfileReference(storageProfileName) + if err != nil { + return fmt.Errorf("[vm creation] error retrieving storage profile %s : %s", storageProfileName, err) + } + storageProfilePrt = &storageProfile + overrideVmDefault = true + } else { + storageProfilePrt = vm.VM.StorageProfile + overrideVmDefault = false + } + + if diskCreatedByTemplate == nil { + return fmt.Errorf("[vm creation] disk with bus type %d, bust number %d and unit number %d not found", + internalDiskProvidedConfig["bus_type"].(string), internalDiskProvidedConfig["bus_number"].(int), internalDiskProvidedConfig["unit_number"].(int)) + } + + // Update details of internal disk for disk existing in template + if value, ok := internalDiskProvidedConfig["iops"]; ok { + iops := int64(value.(int)) + diskCreatedByTemplate.Iops = &iops + } + + if value, ok := internalDiskProvidedConfig["thin_provisioned"]; ok { + thinProvisioned := value.(bool) + diskCreatedByTemplate.ThinProvisioned = &thinProvisioned + } + + diskCreatedByTemplate.SizeMb = int64(internalDiskProvidedConfig["size_in_mb"].(int)) + diskCreatedByTemplate.StorageProfile = storageProfilePrt + diskCreatedByTemplate.OverrideVmDefault = overrideVmDefault + } + + vmSpecSection := vm.VM.VmSpecSection + vmSpecSection.DiskSection.DiskSettings = diskSettings + _, err = vm.UpdateDisks(vmSpecSection) + if err != nil { + return fmt.Errorf("error updating VM disks: %s", err) + } + + return nil +} + +func updateInternalDisks(d *schema.ResourceData, meta interface{}, vm govcd.VM) error { + vcdClient := meta.(*VCDClient) + + _, vdc, err := vcdClient.GetOrgAndVdcFromResource(d) + if err != nil { + return fmt.Errorf(errorRetrievingOrgAndVdc, err) + } + + var diskSettings []*types.DiskSettings + vm.VM.VmSpecSection.DiskSection = &types.DiskSection{} + diskSettings = []*types.DiskSettings{} + + var storageProfilePrt *types.Reference + var overrideVmDefault bool + + internalDisksList := d.Get("internal_disk").(*schema.Set).List() + str2 := spew.Sdump(internalDisksList) + log.Printf("####Update: %s", str2) + + str3 := spew.Sdump(d.State()) + log.Printf("####StateInUpdate: %s", str3) + log.Printf("####StateInUpdate: %#v", d.State().Attributes) + for _, internalDisk := range internalDisksList { + internalDiskProvidedConfig := internalDisk.(map[string]interface{}) + + storageProfileName := internalDiskProvidedConfig["storage_profile"].(string) + if storageProfileName != "" { + storageProfile, err := vdc.FindStorageProfileReference(storageProfileName) + if err != nil { + return fmt.Errorf("[vm creation] error retrieving storage profile %s : %s", storageProfileName, err) + } + storageProfilePrt = &storageProfile + overrideVmDefault = true + } else { + storageProfilePrt = vm.VM.StorageProfile + overrideVmDefault = false + } + + iops := int64(internalDiskProvidedConfig["iops"].(int)) + var thinProvisionedPrt *bool + thinProvisionedValue := internalDiskProvidedConfig["thin_provisioned"].(string) + log.Printf("#########44: %#v", thinProvisionedValue) + if thinProvisionedValue != "" { + thinProvisioned, err := strconv.ParseBool(thinProvisionedValue) + if err != nil { + return fmt.Errorf("error converting string to bool when updating disk for VM: %s", err) + } + thinProvisionedPrt = &thinProvisioned + } else { + thinProvisionedPrt = nil + } + + diskSettings = append(diskSettings, &types.DiskSettings{ + DiskId: internalDiskProvidedConfig["id"].(string), + SizeMb: int64(internalDiskProvidedConfig["size_in_mb"].(int)), + UnitNumber: internalDiskProvidedConfig["unit_number"].(int), + BusNumber: internalDiskProvidedConfig["bus_number"].(int), + AdapterType: strconv.Itoa(internalDiskProvidedConfig["bus_type"].(int)), + ThinProvisioned: thinProvisionedPrt, + StorageProfile: storageProfilePrt, + Iops: &iops, + VirtualQuantityUnit: "byte", + OverrideVmDefault: overrideVmDefault, + }) + } + + vmSpecSection := vm.VM.VmSpecSection + vmSpecSection.DiskSection.DiskSettings = diskSettings + _, err = vm.UpdateDisks(vmSpecSection) + if err != nil { + return fmt.Errorf("error updating VM disks: %s", err) + } + + return nil +} + +func getMatchedDisk(internalDiskProvidedConfig map[string]interface{}, diskSettings []*types.DiskSettings) *types.DiskSettings { + for _, diskSetting := range diskSettings { + if diskSetting.AdapterType == internalDiskBusTypes[internalDiskProvidedConfig["bus_type"].(string)] && + diskSetting.BusNumber == internalDiskProvidedConfig["bus_number"].(int) && + diskSetting.UnitNumber == internalDiskProvidedConfig["unit_number"].(int) { + return diskSetting + } + } + return nil +} + func resourceVcdVAppVmDelete(d *schema.ResourceData, meta interface{}) error { vcdClient := meta.(*VCDClient) @@ -1132,6 +1425,24 @@ func resourceVcdVmIndependentDiskHash(v interface{}) int { return hashcode.String(buf.String()) } +func resourceVcdVmInternalDiskHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + //buf.WriteString(fmt.Sprintf("%s-", m["id"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["bus_type"].(int))) + buf.WriteString(fmt.Sprintf("%s-", m["bus_number"].(int))) + buf.WriteString(fmt.Sprintf("%s-", m["unit_number"].(int))) + buf.WriteString(fmt.Sprintf("%s-", m["storage_profile"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["thin_provisioned"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["iops"].(int))) + buf.WriteString(fmt.Sprintf("%s-", m["size_in_mb"].(int))) + // We use the name and no other identifier to calculate the hash + // With the VM resource, we assume that disks have a unique name. + // In the event that this is not true, we return an error + + return hashcode.String(buf.String()) +} + // networksToConfig converts terraform schema for 'networks' and converts to types.NetworkConnectionSection // which is used for creating new VM func networksToConfig(networks []interface{}, vdc *govcd.Vdc, vapp govcd.VApp, vcdClient *VCDClient) (types.NetworkConnectionSection, error) { diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go new file mode 100644 index 000000000..cd283c168 --- /dev/null +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -0,0 +1,401 @@ +// /***************************************************************** +// * terraform-provider-vcloud-director +// * Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// * SPDX-License-Identifier: BSD-2-Clause +// ******************************************************************/ + +package vcd + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/vmware/go-vcloud-director/v2/govcd" + "github.com/vmware/go-vcloud-director/v2/types/v56" + "log" + "strings" + "text/tabwriter" +) + +func resourceVmInternalDisk() *schema.Resource { + return &schema.Resource{ + Create: resourceVmInternalDiskCreate, + Read: resourceVmInternalDiskRead, + Update: resourceVmInternalDiskUpdate, + Delete: resourceVmInternalDiskDelete, + Importer: &schema.ResourceImporter{ + State: resourceVcdVmInternalDiskImport, + }, + Schema: map[string]*schema.Schema{ + "org": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "The name of organization to use, optional if defined at provider " + + "level. Useful when connected as sysadmin working across different organizations", + }, + "vdc": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "The name of VDC to use, optional if defined at provider level", + }, + "vapp_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Storage profile to override the VM default one", + }, + "vm_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Storage profile to override the VM default one", + }, + "allow_power_off_on": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Specifies whether the disk storage is pre-allocated or allocated on demand.", + }, + "bus_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"ide", "parallel", "sas", "paravirtual", "sata"}, true), + Description: "The type of disk controller. Possible values: ide, parallel( LSI Logic Parallel SCSI), sas(LSI Logic SAS (SCSI)), paravirtual(Paravirtual (SCSI)), sata", + }, + "size_in_mb": { + Type: schema.TypeInt, + Required: true, + Description: "The size of the disk in MB.", + }, + "bus_number": { + Type: schema.TypeInt, + Required: true, + Description: "The number of the SCSI or IDE controller itself.", + }, + "unit_number": { + Type: schema.TypeInt, + Required: true, + Description: "The device number on the SCSI or IDE controller of the disk.", + }, + "thin_provisioned": { + Type: schema.TypeBool, + + Optional: true, + Default: true, + Description: "Specifies whether the disk storage is pre-allocated or allocated on demand. Default - true.", + }, + "iops": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Specifies the IOPS for the disk.", + }, + "storage_profile": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Storage profile to override the VM default one", + }, + }, + } +} + +var internalDiskBusTypes = map[string]string{ + "ide": "1", + "parallel": "3", + "sas": "4", + "paravirtual": "5", + "sata": "6", +} +var internalDiskBusTypesFromValues = map[string]string{ + "1": "ide", + "3": "parallel", + "4": "sas", + "5": "paravirtual", + "6": "sata", +} + +// creates an internal disk for VM +func resourceVmInternalDiskCreate(d *schema.ResourceData, meta interface{}) error { + vcdClient := meta.(*VCDClient) + + vcdClient.lockParentVm(d) + defer vcdClient.unLockParentVm(d) + + vm, vdc, err := getVm(vcdClient, d) + if err != nil { + return err + } + + var storageProfilePrt *types.Reference + var overrideVmDefault bool + + if storageProfileName, ok := d.GetOk("storage_profile"); ok { + storageProfile, err := vdc.FindStorageProfileReference(storageProfileName.(string)) + if err != nil { + return fmt.Errorf("[vm creation] error retrieving storage profile %s : %s", storageProfileName, err) + } + storageProfilePrt = &storageProfile + overrideVmDefault = true + } else { + storageProfilePrt = vm.VM.StorageProfile + overrideVmDefault = false + } + + iops := int64(d.Get("iops").(int)) + isThinProvisioned := d.Get("thin_provisioned").(bool) + + diskSetting := &types.DiskSettings{ + SizeMb: int64(d.Get("size_in_mb").(int)), + UnitNumber: d.Get("unit_number").(int), + BusNumber: d.Get("bus_number").(int), + AdapterType: internalDiskBusTypes[d.Get("bus_type").(string)], + ThinProvisioned: &isThinProvisioned, + StorageProfile: storageProfilePrt, + Iops: &iops, + VirtualQuantityUnit: "byte", + OverrideVmDefault: overrideVmDefault, + } + + diskId, err := vm.AddDisk(diskSetting) + if err != nil { + return err + fmt.Errorf("error updating VM disks: %s", err) + } + + d.SetId(diskId) + return resourceVmInternalDiskRead(d, meta) +} + +// Deletes disk from VM +func resourceVmInternalDiskDelete(d *schema.ResourceData, m interface{}) error { + vcdClient := m.(*VCDClient) + + vcdClient.lockParentVm(d) + defer vcdClient.unLockParentVm(d) + + vm, _, err := getVm(vcdClient, d) + if err != nil { + return err + } + + err = vm.DeleteDisk(d.Id()) + if err != nil { + return fmt.Errorf("[Error] failed to delete internal disk: %s", err) + } + + log.Printf("[TRACE] VM internal disk %s deleted", d.Id()) + d.SetId("") + return nil +} + +func getVm(vcdClient *VCDClient, d *schema.ResourceData) (*govcd.VM, *govcd.Vdc, error) { + _, vdc, err := vcdClient.GetOrgAndVdcFromResource(d) + if err != nil { + return nil, nil, fmt.Errorf(errorRetrievingOrgAndVdc, err) + } + vapp, err := vdc.GetVAppByName(d.Get("vapp_name").(string), false) + if err != nil { + return nil, nil, fmt.Errorf("[Error] failed to get vApp: %s", err) + } + vm, err := vapp.GetVMByName(d.Get("vm_name").(string), false) + if err != nil { + return nil, nil, fmt.Errorf("[Error] failed to get VM: %s", err) + } + return vm, vdc, err +} + +// Update the resource +func resourceVmInternalDiskUpdate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[TRACE] Update Internal Disk with ID: %s started.", d.Id()) + vcdClient := meta.(*VCDClient) + + vcdClient.lockParentVm(d) + defer vcdClient.unLockParentVm(d) + + vm, vdc, err := getVm(vcdClient, d) + if err != nil { + return err + } + + diskSettingsToUpdate, err := vm.GetDisk(d.Id()) + if err != nil { + return err + } + log.Printf("[TRACE] Internal Disk with id %s found", d.Id()) + iops := int64(d.Get("iops").(int)) + isThinProvisioned := d.Get("thin_provisioned").(bool) + diskSettingsToUpdate.Iops = &iops + diskSettingsToUpdate.ThinProvisioned = &isThinProvisioned + diskSettingsToUpdate.UnitNumber = d.Get("unit_number").(int) + diskSettingsToUpdate.BusNumber = d.Get("bus_number").(int) + diskSettingsToUpdate.AdapterType = internalDiskBusTypes[d.Get("bus_type").(string)] + diskSettingsToUpdate.SizeMb = int64(d.Get("size_in_mb").(int)) + + var storageProfilePrt *types.Reference + var overrideVmDefault bool + + storageProfileName := d.Get("storage_profile").(string) + if storageProfileName != "" { + storageProfile, err := vdc.FindStorageProfileReference(storageProfileName) + if err != nil { + return fmt.Errorf("[Error] error retrieving storage profile %s : %s", storageProfileName, err) + } + storageProfilePrt = &storageProfile + overrideVmDefault = true + } else { + storageProfilePrt = vm.VM.StorageProfile + overrideVmDefault = false + } + + diskSettingsToUpdate.StorageProfile = storageProfilePrt + diskSettingsToUpdate.OverrideVmDefault = overrideVmDefault + + _, err = vm.UpdateDisks(vm.VM.VmSpecSection) + if err != nil { + return err + } + + log.Printf("[TRACE] Inernal Disk %s updated", d.Id()) + return nil +} + +// Retrieves an Org resource from vCD +func resourceVmInternalDiskRead(d *schema.ResourceData, m interface{}) error { + vcdClient := m.(*VCDClient) + + vm, _, err := getVm(vcdClient, d) + if err != nil { + return err + } + + diskSettings, err := vm.GetDisk(d.Id()) + if err == govcd.ErrorEntityNotFound { + log.Printf("[DEBUG] Unable to find disk with Id: %s. Removing from tfstate", d.Id()) + d.SetId("") + return nil + } + if err != nil { + return err + } + + _ = d.Set("bus_type", internalDiskBusTypesFromValues[strings.ToLower(diskSettings.AdapterType)]) + _ = d.Set("size_in_mb", diskSettings.SizeMb) + _ = d.Set("bus_number", diskSettings.BusNumber) + _ = d.Set("unit_number", diskSettings.UnitNumber) + _ = d.Set("thin_provisioned", diskSettings.ThinProvisioned) + _ = d.Set("iops", diskSettings.Iops) + _ = d.Set("storage_profile", diskSettings.StorageProfile.Name) + + return nil +} + +var errHelpInternalDiskImport = fmt.Errorf(`resource id must be specified in one of these formats: +'org-name.vdc-name.vapp-name.vm-name.my-internal-disk-id' to import by rule id +'list@org-name.vdc-name.vapp-name.vm-name' to get a list of internal disks with their IDs`) + +// resourceVcdIndependentDiskImport is responsible for importing the resource. +// The following steps happen as part of import +// 1. The user supplies `terraform import _resource_name_ _the_id_string_` command +// 2a. If the `_the_id_string_` contains a dot formatted path to resource as in the example below +// it will try to import it. If it is found - the ID is set +// 2b. If the `_the_id_string_` starts with `list@` and contains path to VM name similar to +// `list@org-name.vdc-name.vapp-name.vm-name` then the function lists all internal disks and their IDs in that VM +// 3. The functions splits the dot-formatted path and tries to lookup the object +// 4. If the lookup succeeds it sets the ID field for `_resource_name_` resource in statefile +// (the resource must be already defined in .tf config otherwise `terraform import` will complain) +// 5. `terraform refresh` is being implicitly launched. The Read method looks up all other fields +// based on the known ID of object. +// +// Example resource name (_resource_name_): vcd_vm_internal_disk.my-disk +// Example import path (_the_id_string_): org-name.vdc-name.vapp-name.vm-name.my-internal-disk-id +// Example list path (_the_id_string_): list@org-name.vdc-name.vapp-name.vm-name +func resourceVcdVmInternalDiskImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + var commandOrgName, orgName, vdcName, vappName, vmName, diskId string + + resourceURI := strings.Split(d.Id(), ImportSeparator) + + log.Printf("[DEBUG] importing vcd_vm_internal_disk resource with provided id %s", d.Id()) + + if len(resourceURI) != 4 && len(resourceURI) != 5 { + return nil, errHelpInternalDiskImport + } + + if strings.Contains(d.Id(), "list@") { + commandOrgName, vdcName, vappName, vmName = resourceURI[0], resourceURI[1], resourceURI[2], resourceURI[3] + commandOrgNameSplit := strings.Split(commandOrgName, "@") + if len(commandOrgNameSplit) != 2 { + return nil, errHelpDiskImport + } + orgName = commandOrgNameSplit[1] + return listInternalDisksForImport(meta, orgName, vdcName, vappName, vmName) + } else { + orgName, vdcName, vappName, vmName, diskId = resourceURI[0], resourceURI[1], resourceURI[2], resourceURI[3], resourceURI[4] + return getInternalDiskForImport(d, meta, orgName, vdcName, vappName, vmName, diskId) + } +} + +func listInternalDisksForImport(meta interface{}, orgName, vdcName, vappName, vmName string) ([]*schema.ResourceData, error) { + + vcdClient := meta.(*VCDClient) + _, vdc, err := vcdClient.GetOrgAndVdc(orgName, vdcName) + if err != nil { + return nil, fmt.Errorf(errorRetrievingOrgAndVdc, err) + } + vapp, err := vdc.GetVAppByName(vappName, false) + if err != nil { + return nil, fmt.Errorf("[Error] failed to get vApp: %s", err) + } + vm, err := vapp.GetVMByName(vmName, false) + if err != nil { + return nil, fmt.Errorf("[Error] failed to get VM: %s", err) + } + + _, _ = fmt.Fprintln(getTerraformStdout(), "Retrieving all disks by name") + if vm.VM.VmSpecSection.DiskSection == nil || vm.VM.VmSpecSection.DiskSection.DiskSettings == nil || + len(vm.VM.VmSpecSection.DiskSection.DiskSettings) == 0 { + return nil, fmt.Errorf("no internal disks found on VM: %s", vmName) + } + + writer := tabwriter.NewWriter(getTerraformStdout(), 0, 8, 1, '\t', tabwriter.AlignRight) + + fmt.Fprintln(writer, "No\tID\tBusType\tBusNumber\tUnitNumber\tSize\tStoragePofile\tIops\tThinProvisioned") + fmt.Fprintln(writer, "--\t--\t-------\t---------\t----------\t----\t-------------\t----\t---------------") + for index, disk := range vm.VM.VmSpecSection.DiskSection.DiskSettings { + fmt.Fprintf(writer, "%d\t%s\t%s\t%d\t%d\t%d\t%s\t%d\t%t\n", (index + 1), disk.DiskId, internalDiskBusTypesFromValues[disk.AdapterType], disk.BusNumber, disk.UnitNumber, disk.SizeMb, + disk.StorageProfile.Name, *disk.Iops, *disk.ThinProvisioned) + } + writer.Flush() + + return nil, fmt.Errorf("resource was not imported! %s", errHelpInternalDiskImport) +} + +func getInternalDiskForImport(d *schema.ResourceData, meta interface{}, orgName, vdcName, vappName, vmName, diskId string) ([]*schema.ResourceData, error) { + vcdClient := meta.(*VCDClient) + _, vdc, err := vcdClient.GetOrgAndVdc(orgName, vdcName) + if err != nil { + return nil, fmt.Errorf(errorRetrievingOrgAndVdc, err) + } + vapp, err := vdc.GetVAppByName(vappName, false) + if err != nil { + return nil, fmt.Errorf("[Error] failed to get vApp: %s", err) + } + vm, err := vapp.GetVMByName(vmName, false) + if err != nil { + return nil, fmt.Errorf("[Error] failed to get VM: %s", err) + } + + disk, err := vm.GetDisk(diskId) + if err != nil { + return []*schema.ResourceData{}, fmt.Errorf("unable to find internal disk with id %s: %s", + d.Id(), err) + } + + d.SetId(disk.DiskId) + d.Set("vapp_name", vappName) + d.Set("vm_name", vmName) + return []*schema.ResourceData{d}, nil +} diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd.go index 04b48f2c4..752c0b6e6 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd.go @@ -92,7 +92,7 @@ func NewVCDClient(vcdEndpoint url.URL, insecure bool, options ...VCDClientOption // Setting defaults vcdClient := &VCDClient{ Client: Client{ - APIVersion: "27.0", // supported by vCD 8.20, 9.0, 9.1, 9.5, 9.7 + APIVersion: "29.0", // supported by vCD 9.0, 9.1, 9.5, 9.7, 10.0 VCDHREF: vcdEndpoint, Http: http.Client{ Transport: &http.Transport{ diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/system.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/system.go index d106986fe..578de301a 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/system.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/system.go @@ -132,7 +132,7 @@ func CreateEdgeGatewayAsync(vcdClient *VCDClient, egwc EdgeGatewayCreation) (Tas HaEnabled: egwc.HAEnabled, GatewayBackingConfig: egwc.BackingConfiguration, AdvancedNetworkingEnabled: egwc.AdvancedNetworkingEnabled, - DistributedRoutingEnabled: distributed, + DistributedRoutingEnabled: &distributed, GatewayInterfaces: &types.GatewayInterfaces{ GatewayInterface: []*types.GatewayInterface{}, }, diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go index 21d0e5587..1172227a2 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go @@ -5,6 +5,7 @@ package govcd import ( + "encoding/xml" "fmt" "net" "net/http" @@ -850,3 +851,144 @@ func (vm *VM) SetGuestCustomizationSection(guestCustomizationSection *types.Gues return vm.GetGuestCustomizationSection() } + +func (vm *VM) AddDisk(diskData *types.DiskSettings) (string, error) { + if vm.VM.HREF == "" { + return "", fmt.Errorf("cannot add internal disks - VM HREF is unset") + } + + //TODO add validation + + diskSettings := vm.VM.VmSpecSection.DiskSection.DiskSettings + if diskSettings == nil { + diskSettings = []*types.DiskSettings{} + } + + diskSettings = append(diskSettings, diskData) + vmSpecSection := vm.VM.VmSpecSection + vmSpecSection.DiskSection.DiskSettings = diskSettings + + vmSpecSection, err := vm.UpdateDisks(vmSpecSection) + if err != nil { + return "", err + } + + for _, diskSetting := range vmSpecSection.DiskSection.DiskSettings { + if diskSetting.AdapterType == diskData.AdapterType && + diskSetting.BusNumber == diskData.BusNumber && + diskSetting.UnitNumber == diskData.UnitNumber { + return diskSetting.DiskId, nil + } + } + + return "", fmt.Errorf("created disk wasn't in list VM internal disks") +} + +func (vm *VM) GetDisk(diskId string) (*types.DiskSettings, error) { + if vm.VM.HREF == "" { + return nil, fmt.Errorf("cannot get internal disk - VM HREF is unset") + } + + if diskId == "" { + return nil, fmt.Errorf("cannot get internal disk - provided diskId is empty") + } + + if vm.VM.VmSpecSection.DiskSection == nil || vm.VM.VmSpecSection.DiskSection.DiskSettings == nil || + len(vm.VM.VmSpecSection.DiskSection.DiskSettings) == 0 { + return nil, fmt.Errorf("cannot get internal disk - VM don't have internal disks") + } + + for _, diskSetting := range vm.VM.VmSpecSection.DiskSection.DiskSettings { + if diskSetting.DiskId == diskId { + return diskSetting, nil + } + } + + return nil, ErrorEntityNotFound +} + +func (vm *VM) DeleteDisk(diskId string) error { + if vm.VM.HREF == "" { + return fmt.Errorf("cannot delete internal disks - VM HREF is unset") + } + + diskSettings := vm.VM.VmSpecSection.DiskSection.DiskSettings + if diskSettings == nil { + diskSettings = []*types.DiskSettings{} + } + + diskPlacement := -1 + for i, diskSetting := range vm.VM.VmSpecSection.DiskSection.DiskSettings { + if diskSetting.DiskId == diskId { + diskPlacement = i + } + } + + if diskPlacement == -1 { + return fmt.Errorf("cannot find VM internal disk with Id: %s", diskId) + } + + // remove disk in slice + diskSettings = append(diskSettings[:diskPlacement], diskSettings[diskPlacement+1:]...) + + vmSpecSection := vm.VM.VmSpecSection + vmSpecSection.DiskSection.DiskSettings = diskSettings + + vmSpecSection, err := vm.UpdateDisks(vmSpecSection) + if err != nil { + return err + } + + err = vm.Refresh() + if err != nil { + return fmt.Errorf("error refresing vm %s: %s", vm.VM.Name, err) + } + + return nil +} + +// UpdateDisks applies disks configuration for the VM. +// types.VmSpecSection requires consist of all disk entities which exist and not updated, +// as also new ones or changed ones. Returns new disk ID and error. +// Runs synchronously, VM is ready for another operation after this function returns. +func (vm *VM) UpdateDisks(disksSettingToUpdate *types.VmSpecSection) (*types.VmSpecSection, error) { + if vm.VM.HREF == "" { + return nil, fmt.Errorf("cannot update internal disks - VM HREF is unset") + } + + task, err := vm.UpdateDisksAsync(disksSettingToUpdate) + if err != nil { + return nil, err + } + err = task.WaitTaskCompletion() + if err != nil { + return nil, fmt.Errorf("error waiting for task completion after internal disks update for vm %s: %s", vm.VM.Name, err) + } + err = vm.Refresh() + if err != nil { + return nil, fmt.Errorf("error refresing vm %s: %s", vm.VM.Name, err) + } + return vm.VM.VmSpecSection, nil +} + +// UpdateDisks applies disks configuration and return task or err +// types.VmSpecSection requires consist of all disk entities which exist and not updated, +// as also new ones or changed ones. +func (vm *VM) UpdateDisksAsync(disksSettingToUpdate *types.VmSpecSection) (Task, error) { + if vm.VM.HREF == "" { + return Task{}, fmt.Errorf("cannot update disks, VM HREF is unset") + } + + disksSettingToUpdate.Info = "Virtual hardware requirements (simplified)" + vmSpecSectionModified := true + disksSettingToUpdate.Modified = &vmSpecSectionModified + + return vm.client.ExecuteTaskRequest(vm.VM.HREF+"/action/reconfigureVm", http.MethodPost, + types.MimeVM, "error updating VM disks: %s", &types.VMDiskChange{ + XMLName: xml.Name{}, + Xmlns: types.XMLNamespaceVCloud, + Ovf: types.XMLNamespaceOVF, + Name: vm.VM.Name, + VmSpecSection: disksSettingToUpdate, + }) +} diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go index aa434921c..8ca037450 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go @@ -211,6 +211,7 @@ type NetworkConfiguration struct { RetainNetInfoAcrossDeployments bool `xml:"RetainNetInfoAcrossDeployments,omitempty"` Features *NetworkFeatures `xml:"Features,omitempty"` GuestVlanAllowed *bool `xml:"GuestVlanAllowed,omitempty"` + DistributedInterface *bool `xml:"DistributedInterface,omitempty"` // TODO: Not Implemented // RouterInfo RouterInfo `xml:"RouterInfo,omitempty"` // SyslogServerSettings SyslogServerSettings `xml:"SyslogServerSettings,omitempty"` @@ -1134,6 +1135,7 @@ type VMGeneralParams struct { Name string `xml:"Name,omitempty"` // Name of VM Description string `xml:"Description,omitempty"` // VM description NeedsCustomization bool `xml:"NeedsCustomization,omitempty"` // True if this VM needs guest customization + RegenerateBiosUuid bool `xml:"RegenerateBiosUuid,omitempty"` // True if BIOS UUID of the virtual machine should be regenerated so that it is unique, and not the same as the source virtual machine's BIOS UUID. } // VApp represents a vApp @@ -1350,11 +1352,106 @@ type VM struct { // TODO: OVF Sections to be implemented // Environment OVF_Environment `xml:"Environment,omitempty" + VmSpecSection *VmSpecSection `xml:"VmSpecSection,omitempty"` + VMCapabilities *VMCapabilities `xml:"VmCapabilities,omitempty"` // Allows you to specify certain capabilities of this virtual machine. StorageProfile *Reference `xml:"StorageProfile,omitempty"` // A reference to a storage profile to be used for this object. The specified storage profile must exist in the organization vDC that contains the object. If not specified, the default storage profile for the vDC is used. ProductSection *ProductSection `xml:"ProductSection,omitempty"` } +// VM represents a virtual machine only with Disk setting update part +type VMDiskChange struct { + // Attributes + XMLName xml.Name `xml:"Vm"` + Ovf string `xml:"xmlns:ovf,attr,omitempty"` + Xsi string `xml:"xmlns:xsi,attr,omitempty"` + Xmlns string `xml:"xmlns,attr,omitempty"` + + HREF string `xml:"href,attr,omitempty"` + Type string `xml:"type,attr,omitempty"` + Name string `xml:"name,attr"` + ID string `xml:"id,attr,omitempty"` + + VmSpecSection *VmSpecSection `xml:"VmSpecSection,omitempty"` +} + +// VmSpecSection from VM struct +type VmSpecSection struct { + Modified *bool `xml:"Modified,attr,omitempty"` + Info string `xml:"ovf:Info"` + OsType string `xml:"OsType,omitempty"` // The type of the OS. This parameter may be omitted when using the VmSpec to update the contents of an existing VM. + NumCpus *int `xml:"NumCpus,omitempty"` // Number of CPUs. This parameter may be omitted when using the VmSpec to update the contents of an existing VM. + NumCoresPerSocket *int `xml:"NumCoresPerSocket,omitempty"` // Number of cores among which to distribute CPUs in this virtual machine.. This parameter may be omitted when using the VmSpec to update the contents of an existing VM. + CpuResourceMhz *CpuResourceMhz `xml:"CpuResourceMhz,omitempty"` // CPU compute resources. This parameter may be omitted when using the VmSpec to update the contents of an existing VM. + MemoryResourceMb *MemoryResourceMb `xml:"MemoryResourceMb"` // Memory compute resources. This parameter may be omitted when using the VmSpec to update the contents of an existing VM. + MediaSection *MediaSection `xml:"MediaSection,omitempty"` // The media devices of this VM. + DiskSection *DiskSection `xml:"DiskSection,omitempty"` // virtual disks of this VM. + HardwareVersion *HardwareVersion `xml:"HardwareVersion"` // vSphere name of Virtual Hardware Version of this VM. Example: vmx-13 This parameter may be omitted when using the VmSpec to update the contents of an existing VM. + VmToolsVersion string `xml:"VmToolsVersion,omitempty"` // VMware tools version of this VM. + VirtualCpuType string `xml:"VirtualCpuType,omitempty"` // The capabilities settings for this VM. This parameter may be omitted when using the VmSpec to update the contents of an existing VM. + TimeSyncWithHost *bool `xml:"TimeSyncWithHost,omitempty"` // Synchronize the VM's time with the host. +} + +// DiskSection from VM/VmSpecSection struct +type DiskSection struct { + DiskSettings []*DiskSettings `xml:"DiskSettings"` +} + +// DiskSettings from VM/VmSpecSection/DiskSection struct +type DiskSettings struct { + DiskId string `xml:"DiskId,omitempty"` // Specifies a unique identifier for this disk in the scope of the corresponding VM. This element is optional when creating a VM, but if it is provided it should be unique. This element is mandatory when updating an existing disk. + SizeMb int64 `xml:"SizeMb"` // The size of the disk in MB. + UnitNumber int `xml:"UnitNumber"` // The device number on the SCSI or IDE controller of the disk. + BusNumber int `xml:"BusNumber"` // The number of the SCSI or IDE controller itself. + AdapterType string `xml:"AdapterType"` // The type of disk controller, e.g. IDE vs SCSI and if SCSI bus-logic vs LSI logic. + ThinProvisioned *bool `xml:"ThinProvisioned,omitempty"` // Specifies whether the disk storage is pre-allocated or allocated on demand. + StorageProfile *Reference `xml:"StorageProfile,omitempty"` // Specifies reference to a storage profile to be associated with the disk. + OverrideVmDefault bool `xml:"overrideVmDefault"` // Specifies that the disk storage profile overrides the VM's default storage profile. + Iops *int64 `xml:"iops,omitempty"` // Specifies the IOPS for the disk. + VirtualQuantity *int64 `xml:"VirtualQuantity,omitempty"` // The actual size of the disk. + VirtualQuantityUnit string `xml:"VirtualQuantityUnit,omitempty"` // The units in which VirtualQuantity is measured. +} + +// MediaSection from VM/VmSpecSection struct +type MediaSection struct { + MediaSettings []*MediaSettings `xml:"MediaSettings"` +} + +// MediaSettings from VM/VmSpecSection/MediaSection struct +type MediaSettings struct { + DeviceId string `xml:"DeviceId,omitempty"` // Describes the media device whose media mount is being specified here. This deviceId must match the RASD.InstanceID attribute in the VirtualHardwareSection of the vApp's OVF description. + MediaType string `xml:"MediaType,omitempty"` // Specified the type of media that is mounted onto the device. + MediaState string `xml:"MediaState,omitempty"` // Specifies the state of the media device. + MediaImage *Reference `xml:"MediaImage,omitempty"` // The media image that is mounted onto the device. This property can be 'null' which represents that no media is mounted on the device. + UnitNumber int `xml:"UnitNumber"` // Specified the type of media that is mounted onto the device. + BusNumber int `xml:"BusNumber"` // The bus number of the media device controller. + AdapterType string `xml:"AdapterType,omitempty"` // The type of controller, e.g. IDE vs SCSI and if SCSI bus-logic vs LSI logic +} + +// CpuResourceMhz from VM/VmSpecSection struct +type CpuResourceMhz struct { + Configured int64 `xml:"Configured` // The amount of resource configured on the virtual machine. + Limit *int64 `xml:"Limit,omitempty"` // The limit for how much of this resource can be consumed on the underlying virtualization infrastructure. This is only valid when the resource allocation is not unlimited. + Reservation *int64 `xml:"Reservation,omitempty"` // The amount of reservation of this resource on the underlying virtualization infrastructure. + SharesLevel string `xml:"SharesLevel,omitempty"` // Pre-determined relative priorities according to which the non-reserved portion of this resource is made available to the virtualized workload. + Shares *int `xml:"Shares,omitempty"` // Custom priority for the resource. This is a read-only, unless the share level is CUSTOM. +} + +// MemoryResourceMb from VM/VmSpecSection struct +type MemoryResourceMb struct { + Configured int64 `xml:"Configured` // The amount of resource configured on the virtual machine. + Limit *int64 `xml:"Limit,omitempty"` // The limit for how much of this resource can be consumed on the underlying virtualization infrastructure. This is only valid when the resource allocation is not unlimited. + Reservation *int64 `xml:"Reservation,omitempty"` // The amount of reservation of this resource on the underlying virtualization infrastructure. + SharesLevel string `xml:"SharesLevel,omitempty"` // Pre-determined relative priorities according to which the non-reserved portion of this resource is made available to the virtualized workload. + Shares *int `xml:"Shares,omitempty"` // Custom priority for the resource. This is a read-only, unless the share level is CUSTOM. +} + +type HardwareVersion struct { + HREF string `xml:"href,attr"` + Type string `xml:"type,attr,omitempty"` + Value string `xml:",chardata"` +} + // ovf:VirtualHardwareSection from VM struct type VirtualHardwareSection struct { // Extends OVF Section_Type @@ -1367,6 +1464,18 @@ type VirtualHardwareSection struct { Item []*VirtualHardwareItem `xml:"Item,omitempty"` } +// RasdItemsList from VirtualHardware +type RasdItemsList struct { + // Extends OVF Section_Type + XMLName xml.Name `xml:"RasdItemsList"` + Xmlns string `xml:"xmlns,attr,omitempty"` + RasdXmlns string `xml:"xmlns:rasd,attr,omitempty"` + + HREF string `xml:"href,attr,omitempty"` + Type string `xml:"type,attr,omitempty"` + Items []*VirtualHardwareItem `xml:"Item,omitempty"` +} + // Each ovf:Item parsed from the ovf:VirtualHardwareSection type VirtualHardwareItem struct { XMLName xml.Name `xml:"Item"` @@ -1572,7 +1681,7 @@ type GatewayConfiguration struct { EdgeGatewayServiceConfiguration *GatewayFeatures `xml:"EdgeGatewayServiceConfiguration,omitempty"` // Represents Gateway Features. HaEnabled bool `xml:"HaEnabled,omitempty"` // True if this gateway is highly available. (Requires two vShield edge VMs.) AdvancedNetworkingEnabled bool `xml:"AdvancedNetworkingEnabled,omitempty"` // True if the gateway uses advanced networking - DistributedRoutingEnabled bool `xml:"DistributedRoutingEnabled,omitempty"` // True if gateway is attached to a Distributed Logical Router + DistributedRoutingEnabled *bool `xml:"DistributedRoutingEnabled,omitempty"` // True if gateway is attached to a Distributed Logical Router UseDefaultRouteForDNSRelay bool `xml:"UseDefaultRouteForDnsRelay,omitempty"` // True if the default gateway on the external network selected for default route should be used as the DNS relay. } @@ -2240,31 +2349,40 @@ type QueryResultEdgeGatewayRecordType struct { // QueryResultVMRecordType represents a VM record as query result. type QueryResultVMRecordType struct { // Attributes - HREF string `xml:"href,attr,omitempty"` // The URI of the entity. - Name string `xml:"name,attr,omitempty"` // VM name. - Deployed bool `xml:"isDeployed,attr,omitempty"` // True if the virtual machine is deployed. - Status string `xml:"status,attr,omitempty"` - Busy bool `xml:"isBusy,attr,omitempty"` - Deleted bool `xml:"isDeleted,attr,omitempty"` - MaintenanceMode bool `xml:"isInMaintenanceMode,attr,omitempty"` - Published bool `xml:"isPublished,attr,omitempty"` - VAppTemplate bool `xml:"isVAppTemplate,attr,omitempty"` - VdcEnabled bool `xml:"isVdcEnabled,attr,omitempty"` - VdcHREF string `xml:"vdc,attr,omitempty"` - VAppParentHREF string `xml:"container,attr,omitempty"` - VAppParentName string `xml:"containerName,attr,omitempty"` - HardwareVersion int `xml:"hardwareVersion,attr,omitempty"` - HighestSupportedVersion int `xml:"pvdcHighestSupportedHardwareVersion,attr,omitempty"` - VmToolsVersion string `xml:"vmToolsVersion,attr,omitempty"` - GuestOS string `xml:"guestOs,attr,omitempty"` - MemoryMB int `xml:"memoryMB,attr,omitempty"` - Cpus int `xml:"numberOfCpus,attr,omitempty"` - StorageProfileName string `xml:"storageProfileName,attr,omitempty"` - NetworkName string `xml:"networkName,attr,omitempty"` - TaskHREF string `xml:"task,attr,omitempty"` - TaskStatusName string `xml:"taskStatusName,attr,omitempty"` - TaskDetails string `xml:"taskDetails,attr,omitempty"` - TaskStatus string `xml:"TaskStatus,attr,omitempty"` + HREF string `xml:"href,attr,omitempty"` // The URI of the entity. + ID string `xml:"id,attr,omitempty"` + Name string `xml:"name,attr,omitempty"` // VM name. + Type string `xml:"type,attr,omitempty"` // Contains the type of the resource. + ContainerName string `xml:"containerName,attr,omitempty"` // The name of the vApp or vApp template that contains this VM. + ContainerID string `xml:"container,attr,omitempty"` // The ID of the vApp or vApp template that contains this VM. + OwnerName string `xml:"ownerName,attr,omitempty"` + Owner string `xml:"owner,attr,omitempty"` + VdcHREF string `xml:"vdc,attr,omitempty"` + VAppTemplate bool `xml:"isVAppTemplate,attr,omitempty"` + Deleted bool `xml:"isDeleted,attr,omitempty"` + GuestOS string `xml:"guestOs,attr,omitempty"` + Cpus int `xml:"numberOfCpus,attr,omitempty"` + MemoryMB int `xml:"memoryMB,attr,omitempty"` + Status string `xml:"status,attr,omitempty"` + NetworkName string `xml:"networkName,attr,omitempty"` + NetworkHref string `xml:"network,attr,omitempty"` + IpAddress string `xml:"ipAddress,attr,omitempty"` // If configured, the IP Address of the VM on the primary network, otherwise empty. + Busy bool `xml:"isBusy,attr,omitempty"` + Deployed bool `xml:"isDeployed,attr,omitempty"` // True if the virtual machine is deployed. + Published bool `xml:"isPublished,attr,omitempty"` + CatalogName string `xml:"catalogName,attr,omitempty"` + HardwareVersion int `xml:"hardwareVersion,attr,omitempty"` + VmToolsStatus string `xml:"vmToolsStatus,attr,omitempty"` + MaintenanceMode bool `xml:"isInMaintenanceMode,attr,omitempty"` + AutoNature bool `xml:"isAutoNature,attr,omitempty"` // True if the parent vApp is a managed vApp + StorageProfileName string `xml:"storageProfileName,attr,omitempty"` + GcStatus string `xml:"gcStatus,attr,omitempty"` // GC status of this VM. + AutoUndeployDate string `xml:"autoUndeployDate,attr,omitempty"` + AutoDeleteDate string `xml:"autoDeleteDate,attr,omitempty"` + AutoUndeployNotified bool `xml:"isAutoUndeployNotified,attr,omitempty"` + AutoDeleteNotified bool `xml:"isAutoDeleteNotified,attr,omitempty"` + Link []*Link `xml:"Link,omitempty"` + MetaData *Metadata `xml:"Metadata,omitempty"` } // QueryResultVAppRecordType represents a VM record as query result. diff --git a/vendor/modules.txt b/vendor/modules.txt index 29f1f326a..03e87b7fa 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -212,7 +212,7 @@ github.com/ulikunitz/xz/lzma # github.com/vmihailenco/msgpack v3.3.3+incompatible github.com/vmihailenco/msgpack github.com/vmihailenco/msgpack/codes -# github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.5 +# github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.5 => ../go-vcloud-director github.com/vmware/go-vcloud-director/v2/govcd github.com/vmware/go-vcloud-director/v2/types/v56 github.com/vmware/go-vcloud-director/v2/util From 8a257d6b0677c2a1b243ef057970c18dce52dd07 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 2 Dec 2019 16:31:54 +0200 Subject: [PATCH 03/80] missing file Signed-off-by: Vaidotas Bauzys --- .../github.com/mattn/go-isatty/isatty_others.go | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 vendor/github.com/mattn/go-isatty/isatty_others.go diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go deleted file mode 100644 index f02849c56..000000000 --- a/vendor/github.com/mattn/go-isatty/isatty_others.go +++ /dev/null @@ -1,15 +0,0 @@ -// +build appengine js - -package isatty - -// IsTerminal returns true if the file descriptor is terminal which -// is always false on js and appengine classic which is a sandboxed PaaS. -func IsTerminal(fd uintptr) bool { - return false -} - -// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} From 5d829580c93c4537190af3e0e161313680ad812f Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 2 Dec 2019 16:32:07 +0200 Subject: [PATCH 04/80] missing file Signed-off-by: Vaidotas Bauzys --- .../v2/govcd/govcd_test_config.yaml | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 vendor/github.com/vmware/go-vcloud-director/v2/govcd/govcd_test_config.yaml diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/govcd_test_config.yaml b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/govcd_test_config.yaml new file mode 100644 index 000000000..a31d3ab7b --- /dev/null +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/govcd_test_config.yaml @@ -0,0 +1,149 @@ +# COPY THIS FILE to govcd_test_config.yaml +# in the same directory and change the values +# to match your environment. +# +# All items in this file must exist already +# (They will not be removed or left altered) +# The test will create a vApp and remove it at the end +# +provider: + # vCD administrator credentials + # (Providing org credentials will skip some tests) + user: administrator + #user: datacloud-admin + #password: ca$hc0w + password: Darbas34` + #password: CHANGE-ME + #user: vbauzys + #password: Admin!23 + + # + # The vCD address, in the format https://vCD_IP/api + # or https://vCD_host_name/api + #mine vcd 10 + #url: https://bos1-vcloud-static-170-20.eng.vmware.com/api + #url: https://bos1-vcloud-static-170-30.eng.vmware.com/api + url: https://192.168.1.109/api + + # + # The organization you are authenticating with + sysOrg: System + #sysOrg: datacloud + maxRetryTimeout: 60 +vcd: + # Name of the organization (mandatory) + #org: datacloud + org: my1 + # + # The virtual data center (mandatory) + # The tests will create a vApp here + # + #vdc: manoVdc + vdc: vbVdc + #vdc: vdc-datacloud + # An Org catalog, possibly containing at least one item + # Provider VDC; if omitted, some tests will be skipped + provider_vdc: + name: providerVbVdc + storage_profile: Development + network_pool: providerVbVdc-VXLAN-NP + catalog: + name: testCatalog + # + # One item in the catalog. It will be used to compose test vApps + catalogItem: photon-hw11 + # + # An optional description for the catalog. Its test will be skipped if omitted. + # If provided, it must be the current description of the catalog + description: Fresh OS templates + # + # An optional description for the catalog item + catalogItemDescription: wow some huge + # + network: + # First vdc network (mandatory) + network1: "myOrgNet1" + # Second vdc network. If omitted, some tests will be skipped. + #network2: "" + # Storage profiles used in the vDC + # One or two can be listed + # + # Storage profiles used in the vDC + # One or two can be listed + storageProfile: + # First storage profile (mandatory) + storageProfile1: Development + # Second storage profile. If omitted, some tests will be skipped. + storageProfile2: "*" + # An edge gateway + # (see https://pubs.vmware.com/vca/topic/com.vmware.vcloud.api.doc_56/GUID-18B0FB8B-385C-4B6D-982C-4B24D271C646.html) + edgeGateway: vb_edge2 + # + # The IP of the gateway (must exist) + externalIp: 192.168.1.10 + externalNetmask: 255.255.255.0 + # + # A free IP in the Org vDC network + internalIp: 192.168.2.10 + internalNetmask: 255.255.255.0 + # An external Network name + externalNetwork: vb_externalNet2 + # + # A port group name for creating an external network + externalNetworkPortGroup: ForTestingPG + # + # A port group type for creating an external network. Can be DV_PORTGROUP or NETWORK + externalNetworkPortGroupType: DV_PORTGROUP + # + # A vSphere server name for creating an external network + vimServer: vC1 + # + # Independent disk parameters for testing + disk: + # + # Disk size (bytes) for create disk, skip disk tests if it is less than or equal to 0 + size: 2048576 + # + # Disk size (bytes) for update disk, skip some disk tests if it is less than or equal to 0 + sizeForUpdate: 3048576 +logging: + # All items in this section are optional + # Logging is disabled by default. + # See ./util/LOGGING.md for more info + # + # Enables or disables logs + enabled: true + verboseCleanup: true + # + # changes the log name + logFileName: "go-vcloud-director2.log" + # + # Defines whether we log the requests in HTTP operations + logHttpRequests: true + # + # Defines whether we log the responses in HTTP operations + logHttpResponses: true + # + # Comma-separated list of XML tags to skip from the API logs + skipResponseTags: SupportedVersions,VAppTemplate + # + # Comma-separated list of functions from where we log the API calls. + # When this is set, we only log API requests and responses if the name + # of the function matches any of the names in this list. + logFunctions: +ova: + # The ova for uploading catalog item for tests. + # Default paths are simple ova provided by project + # Empty values skips the tests + # Absolute or relative path + ovaPath: ../test-resources/test_vapp_template.ova + # + # The chunked ova (vmdk inside are split) for tests + ovaChunkedPath: ../test-resources/template_with_custom_chunk_size.ova +media: + # The iso for uploading media item for tests. + # Default paths are simple iso provided by project + # Empty values skips the tests + # Absolute or relative path + mediaPath: ../test-resources/test.iso + mediaName: vaido2 From ed2cf5d0aeef51fb93647572579780e7f4f458d2 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 2 Dec 2019 16:32:18 +0200 Subject: [PATCH 05/80] missing file Signed-off-by: Vaidotas Bauzys --- .../github.com/mattn/go-isatty/isatty_others.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 vendor/github.com/mattn/go-isatty/isatty_others.go diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go new file mode 100644 index 000000000..f02849c56 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_others.go @@ -0,0 +1,15 @@ +// +build appengine js + +package isatty + +// IsTerminal returns true if the file descriptor is terminal which +// is always false on js and appengine classic which is a sandboxed PaaS. +func IsTerminal(fd uintptr) bool { + return false +} + +// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} From 2584e4458c761c44144015a048582a131e8fc60d Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 2 Dec 2019 16:35:07 +0200 Subject: [PATCH 06/80] latest govcd with local changes Signed-off-by: Vaidotas Bauzys --- go.mod | 3 + go.sum | 2 - .../v2/govcd/edgegateway.go | 4 +- .../v2/govcd/lbserverpool.go | 38 +-- .../v2/govcd/lbservicemonitor.go | 36 +- .../v2/govcd/lbvirtualserver.go | 42 +-- .../go-vcloud-director/v2/govcd/nsxv_ipset.go | 256 --------------- .../go-vcloud-director/v2/govcd/nsxv_nat.go | 192 ----------- .../v2/govcd/orgvdcnetwork.go | 41 --- .../go-vcloud-director/v2/govcd/system.go | 6 +- .../vmware/go-vcloud-director/v2/govcd/vdc.go | 17 - .../v2/types/v56/constants.go | 5 - .../v2/types/v56/nsxv_types.go | 286 ---------------- .../go-vcloud-director/v2/types/v56/types.go | 309 +++++++++++++++--- vendor/modules.txt | 2 +- 15 files changed, 308 insertions(+), 931 deletions(-) delete mode 100644 vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_ipset.go delete mode 100644 vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_nat.go delete mode 100644 vendor/github.com/vmware/go-vcloud-director/v2/types/v56/nsxv_types.go diff --git a/go.mod b/go.mod index c6e9c9ba1..15b5c611b 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,10 @@ module github.com/terraform-providers/terraform-provider-vcd/v2 go 1.13 require ( + github.com/davecgh/go-spew v1.1.1 github.com/hashicorp/go-version v1.2.0 github.com/hashicorp/terraform-plugin-sdk v1.3.0 github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 ) + +replace github.com/vmware/go-vcloud-director/v2 => ../go-vcloud-director diff --git a/go.sum b/go.sum index 853f90b81..47b6cb9d1 100644 --- a/go.sum +++ b/go.sum @@ -201,8 +201,6 @@ github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvc github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU= github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 h1:jHcYDaWZtSbHdgO6Q/th0fDZxP86S0oRgM79r02BPno= -github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9/go.mod h1:zjondbeyTfZlzhwxOzyF4K2sWWYgMEv5H91dp5dPbU8= github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.1.0 h1:uJwc9HiBOCpoKIObTQaLR+tsEXx1HBHnOsOOpcdhZgw= github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go index 02f1adef8..bdd195162 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go @@ -1059,9 +1059,7 @@ func (egw *EdgeGateway) HasDefaultGateway() bool { // HasAdvancedNetworking returns true if the edge gateway has advanced network configuration enabled func (egw *EdgeGateway) HasAdvancedNetworking() bool { - return egw.EdgeGateway.Configuration != nil && - egw.EdgeGateway.Configuration.AdvancedNetworkingEnabled != nil && - *egw.EdgeGateway.Configuration.AdvancedNetworkingEnabled + return egw.EdgeGateway.Configuration != nil && egw.EdgeGateway.Configuration.AdvancedNetworkingEnabled } // buildProxiedEdgeEndpointURL helps to get root endpoint for Edge Gateway using the diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbserverpool.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbserverpool.go index 631736dfe..cf2c7b335 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbserverpool.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbserverpool.go @@ -52,13 +52,25 @@ func (egw *EdgeGateway) getLbServerPool(lbPoolConfig *types.LbPool) (*types.LbPo return nil, err } - pools, err := egw.GetLbServerPools() + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbServerPoolPath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // Anonymous struct to unwrap "server pool response" + lbPoolResponse := &struct { + LBPools []*types.LbPool `xml:"pool"` + }{} + + // This query returns all server pools as the API does not have filtering options + _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, + "unable to read load lalancer server pool: %s", nil, lbPoolResponse) if err != nil { return nil, err } // Search for pool by ID or by Name - for _, pool := range pools { + for _, pool := range lbPoolResponse.LBPools { // If ID was specified for lookup - look for the same ID if lbPoolConfig.ID != "" && pool.ID == lbPoolConfig.ID { return pool, nil @@ -78,28 +90,6 @@ func (egw *EdgeGateway) getLbServerPool(lbPoolConfig *types.LbPool) (*types.LbPo return nil, ErrorEntityNotFound } -// GetLbServerPools return all created server pools without filtering. -func (egw *EdgeGateway) GetLbServerPools() ([]*types.LbPool, error) { - httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbServerPoolPath) - if err != nil { - return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) - } - - // Anonymous struct to unwrap "server pool response" - lbPoolResponse := &struct { - LBPools []*types.LbPool `xml:"pool"` - }{} - - // This query returns all server pools as the API does not have filtering options - _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, - "unable to read load lalancer server pool: %s", nil, lbPoolResponse) - if err != nil { - return nil, err - } - - return lbPoolResponse.LBPools, nil -} - // GetLbServerPoolByName wraps getLbServerPool and needs only an ID for lookup func (egw *EdgeGateway) GetLbServerPoolById(id string) (*types.LbPool, error) { return egw.getLbServerPool(&types.LbPool{ID: id}) diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbservicemonitor.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbservicemonitor.go index bf100e4f8..72dbc4699 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbservicemonitor.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbservicemonitor.go @@ -55,13 +55,24 @@ func (egw *EdgeGateway) getLbServiceMonitor(lbMonitorConfig *types.LbMonitor) (* return nil, err } - serviceMonitors, err := egw.GetLbServiceMonitors() + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbMonitorPath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // Anonymous struct to unwrap "monitor response" + lbMonitorResponse := &struct { + LBMonitors []*types.LbMonitor `xml:"monitor"` + }{} + + // This query returns all service monitors as the API does not have filtering options + _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, "unable to read Load Balancer monitor: %s", nil, lbMonitorResponse) if err != nil { return nil, err } // Search for monitor by ID or by Name - for _, monitor := range serviceMonitors { + for _, monitor := range lbMonitorResponse.LBMonitors { // If ID was specified for lookup - look for the same ID if lbMonitorConfig.ID != "" && monitor.ID == lbMonitorConfig.ID { return monitor, nil @@ -81,27 +92,6 @@ func (egw *EdgeGateway) getLbServiceMonitor(lbMonitorConfig *types.LbMonitor) (* return nil, ErrorEntityNotFound } -// GetLbServiceMonitors return all service monitors without filtering -func (egw *EdgeGateway) GetLbServiceMonitors() ([]*types.LbMonitor, error) { - httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbMonitorPath) - if err != nil { - return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) - } - - // Anonymous struct to unwrap "monitor response" - lbMonitorResponse := &struct { - LBMonitors []*types.LbMonitor `xml:"monitor"` - }{} - - // This query returns all service monitors as the API does not have filtering options - _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, "unable to read Load Balancer monitor: %s", nil, lbMonitorResponse) - if err != nil { - return nil, err - } - - return lbMonitorResponse.LBMonitors, nil -} - // GetLbServiceMonitorById wraps getLbServiceMonitor and needs only an ID for lookup func (egw *EdgeGateway) GetLbServiceMonitorById(id string) (*types.LbMonitor, error) { return egw.getLbServiceMonitor(&types.LbMonitor{ID: id}) diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbvirtualserver.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbvirtualserver.go index e12115a9d..f0a703d30 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbvirtualserver.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbvirtualserver.go @@ -54,13 +54,25 @@ func (egw *EdgeGateway) getLbVirtualServer(lbVirtualServerConfig *types.LbVirtua return nil, err } - vs, err := egw.GetLbVirtualServers() + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbVirtualServerPath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // Anonymous struct to unwrap "virtual server response" + lbVirtualServerResponse := &struct { + LBVirtualServers []*types.LbVirtualServer `xml:"virtualServer"` + }{} + + // This query returns all virtual servers as the API does not have filtering options + _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, + "unable to read load balancer virtual server: %s", nil, lbVirtualServerResponse) if err != nil { return nil, err } // Search for virtual server by ID or by Name - for _, virtualServer := range vs { + for _, virtualServer := range lbVirtualServerResponse.LBVirtualServers { // If ID was specified for lookup - look for the same ID if lbVirtualServerConfig.ID != "" && virtualServer.ID == lbVirtualServerConfig.ID { return virtualServer, nil @@ -81,34 +93,12 @@ func (egw *EdgeGateway) getLbVirtualServer(lbVirtualServerConfig *types.LbVirtua return nil, ErrorEntityNotFound } -// GetLbVirtualServers is getting all virtual servers without filtering anything -func (egw *EdgeGateway) GetLbVirtualServers() ([]*types.LbVirtualServer, error) { - httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbVirtualServerPath) - if err != nil { - return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) - } - - // Anonymous struct to unwrap "virtual server response" - lbVirtualServerResponse := &struct { - LBVirtualServers []*types.LbVirtualServer `xml:"virtualServer"` - }{} - - // This query returns all virtual servers as the API does not have filtering options - _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, - "unable to read load balancer virtual server: %s", nil, lbVirtualServerResponse) - if err != nil { - return nil, err - } - - return lbVirtualServerResponse.LBVirtualServers, nil -} - -// GetLbVirtualServerById wraps getLbVirtualServers and needs only an ID for lookup +// GetLbVirtualServerById wraps getLbVirtualServer and needs only an ID for lookup func (egw *EdgeGateway) GetLbVirtualServerById(id string) (*types.LbVirtualServer, error) { return egw.getLbVirtualServer(&types.LbVirtualServer{ID: id}) } -// GetLbVirtualServerByName wraps getLbVirtualServers and needs only a Name for lookup +// GetLbVirtualServerByName wraps getLbVirtualServer and needs only a Name for lookup func (egw *EdgeGateway) GetLbVirtualServerByName(name string) (*types.LbVirtualServer, error) { return egw.getLbVirtualServer(&types.LbVirtualServer{Name: name}) } diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_ipset.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_ipset.go deleted file mode 100644 index 7754eebf5..000000000 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_ipset.go +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. - */ - -package govcd - -import ( - "encoding/xml" - "fmt" - "net/http" - - "github.com/vmware/go-vcloud-director/v2/types/v56" - "github.com/vmware/go-vcloud-director/v2/util" -) - -// CreateNsxvIpSet creates an IP set from *types.EdgeIpSet. IP set defines a group of IP addresses -// that you can add as the source or destination in a firewall rule or in DHCP relay configuration. -func (vdc *Vdc) CreateNsxvIpSet(ipSetConfig *types.EdgeIpSet) (*types.EdgeIpSet, error) { - if err := validateCreateNsxvIpSet(ipSetConfig); err != nil { - return nil, err - } - - vdcId, err := getUuidFromHref(vdc.Vdc.HREF) - if err != nil { - return nil, fmt.Errorf("unable to get vdc ID from HREF: %s", err) - } - - // build a path for IP set creation. The endpoint should look like: - // https://_hostname_/network/services/ipset/f9daf2da-b4f9-4921-a2f4-d77a943a381c where the - // trailing UUID is vDC ID - httpPath, err := vdc.buildNsxvNetworkServiceEndpointURL(types.NsxvIpSetServicePath + "/" + vdcId) - if err != nil { - return nil, fmt.Errorf("could not get network services API endpoint for IP set: %s", err) - } - - // Success or an error of type types.NSXError is expected - _, err = vdc.client.ExecuteParamRequestWithCustomError(httpPath, nil, http.MethodPost, types.AnyXMLMime, - "error creating IP set: %s", ipSetConfig, &types.NSXError{}) - if err != nil { - return nil, err - } - - createdIpSet, err := vdc.GetNsxvIpSetByName(ipSetConfig.Name) - if err != nil { - return nil, fmt.Errorf("could not lookup newly created IP set with name %s: %s", ipSetConfig.Name, err) - } - - return createdIpSet, nil -} - -// UpdateNsxvIpSet sends all fields of ipSetConfig. Omiting a value may reset it. ID is mandatory to -// perform update. -// Because the API always requires a Revision to be sent - the update fetches latest revision number -// automatically and embeds into the update structure. -func (vdc *Vdc) UpdateNsxvIpSet(ipSetConfig *types.EdgeIpSet) (*types.EdgeIpSet, error) { - err := validateUpdateNsxvIpSet(ipSetConfig) - if err != nil { - return nil, err - } - - // Inject latest Revision for this IP set so that API accepts change - currentIpSet, err := vdc.GetNsxvIpSetById(ipSetConfig.ID) - if err != nil { - return nil, fmt.Errorf("could not fetch current IP set: %s", err) - } - ipSetConfig.Revision = currentIpSet.Revision - - httpPath, err := vdc.buildNsxvNetworkServiceEndpointURL(types.NsxvIpSetServicePath + "/" + ipSetConfig.ID) - if err != nil { - return nil, fmt.Errorf("could not get network services API endpoint for IP set: %s", err) - } - - // Result is either 204 for success, or an error of type types.NSXError - errString := fmt.Sprintf("error while updating IP set with ID %s :%%s", ipSetConfig.ID) - _, err = vdc.client.ExecuteRequestWithCustomError(httpPath, http.MethodPut, types.AnyXMLMime, - errString, ipSetConfig, &types.NSXError{}) - if err != nil { - return nil, err - } - - updatedIpSet, err := vdc.GetNsxvIpSetById(ipSetConfig.ID) - if err != nil { - return nil, fmt.Errorf("could not lookup updated IP set with ID %s: %s", ipSetConfig.ID, err) - } - - return updatedIpSet, nil -} - -// GetNsxvIpSetByName searches for IP set by name. Names are unique therefore it can find only one. -// Returns ErrorEntityNotFound if an IP set is not found -func (vdc *Vdc) GetNsxvIpSetByName(name string) (*types.EdgeIpSet, error) { - if err := validateGetNsxvIpSet("", name); err != nil { - return nil, err - } - - allIpSets, err := vdc.GetAllNsxvIpSets() - if err != nil { - return nil, err - } - - util.Logger.Printf("[DEBUG] Searching for IP set with name: %s", name) - for _, ipSet := range allIpSets { - util.Logger.Printf("[DEBUG] Checking IP set: %#+v", ipSet) - if ipSet.Name != "" && ipSet.Name == name { - return ipSet, nil - } - } - - return nil, ErrorEntityNotFound -} - -// GetNsxvIpSetById searches for IP set by ID. Returns ErrorEntityNotFound if an IP set is not found -func (vdc *Vdc) GetNsxvIpSetById(id string) (*types.EdgeIpSet, error) { - if err := validateGetNsxvIpSet(id, ""); err != nil { - return nil, err - } - - allIpSets, err := vdc.GetAllNsxvIpSets() - if err != nil { - return nil, err - } - - util.Logger.Printf("[DEBUG] Searching for IP set with id: %s", id) - for _, ipSet := range allIpSets { - util.Logger.Printf("[DEBUG] Checking IP set: %#+v", ipSet) - if ipSet.ID != "" && ipSet.ID == id { - return ipSet, nil - } - } - - return nil, ErrorEntityNotFound -} - -// GetNsxvIpSetByNameOrId uses the same identifier to search by name and by ID. Priority is to try -// and find the IP set by ID. If it is not found - then a search by name is performed. -func (vdc *Vdc) GetNsxvIpSetByNameOrId(identifier string) (*types.EdgeIpSet, error) { - getByName := func(name string, refresh bool) (interface{}, error) { return vdc.GetNsxvIpSetByName(name) } - getById := func(id string, refresh bool) (interface{}, error) { return vdc.GetNsxvIpSetById(id) } - entity, err := getEntityByNameOrId(getByName, getById, identifier, true) - if entity == nil { - return nil, err - } - return entity.(*types.EdgeIpSet), err -} - -// GetAllNsxvIpSets retrieves all IP sets and returns []*types.EdgeIpSet or an -// error of type ErrorEntityNotFound if there are no IP sets -func (vdc *Vdc) GetAllNsxvIpSets() ([]*types.EdgeIpSet, error) { - vdcId, err := getUuidFromHref(vdc.Vdc.HREF) - if err != nil { - return nil, fmt.Errorf("unable to get vdc ID from HREF: %s", err) - } - - // build a path for to read all IP sets in a scope. A scope is defined by vDC ID. The endpoint - // should look like: - // https://192.168.1.109/network/services/ipset/scope/f9daf2da-b4f9-4921-a2f4-d77a943a381c where - // the trailing UUID is vDC ID - httpPath, err := vdc.buildNsxvNetworkServiceEndpointURL(types.NsxvIpSetServicePath + "/scope/" + vdcId) - if err != nil { - return nil, fmt.Errorf("could not get network services API endpoint for IP set: %s", err) - } - - // Anonymous struct to unwrap list of IP sets - ipSetsResponse := &struct { - XMLName xml.Name `xml:"list"` - types.EdgeIpSets `xml:"ipset"` - }{} - - // This query returns all IP sets on the scope (scoped by vDC ID) - errString := fmt.Sprintf("unable to read IP sets for scope %s: %%s", vdcId) - _, err = vdc.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, errString, nil, ipSetsResponse) - if err != nil { - return nil, err - } - - if len(ipSetsResponse.EdgeIpSets) == 0 { - return nil, ErrorEntityNotFound - } - - return ipSetsResponse.EdgeIpSets, nil -} - -// DeleteNsxvIpSetById deletes IP set by its ID which is formatted as -// f9daf2da-b4f9-4921-a2f4-d77a943a381c:ipset-9 -func (vdc *Vdc) DeleteNsxvIpSetById(id string) error { - err := validateDeleteNsxvIpSet(id, "") - if err != nil { - return err - } - - // build a path for to delete exact IP set sample path is: DELETE API-URL/services/ipset/id:ipset-# - // https://192.168.1.109/network/services/ipset/f9daf2da-b4f9-4921-a2f4-d77a943a381c:ipset-9 - httpPath, err := vdc.buildNsxvNetworkServiceEndpointURL(types.NsxvIpSetServicePath + "/" + id) - if err != nil { - return fmt.Errorf("could not get network services API endpoint for IP set: %s", err) - } - - errString := fmt.Sprintf("unable to delete IP set with ID %s: %%s", id) - _, err = vdc.client.ExecuteRequestWithCustomError(httpPath, http.MethodDelete, types.AnyXMLMime, - errString, nil, &types.NSXError{}) - if err != nil { - return err - } - - return nil -} - -// DeleteNsxvIpSetById deletes IP set by its name -func (vdc *Vdc) DeleteNsxvIpSetByName(name string) error { - err := validateDeleteNsxvIpSet("", name) - if err != nil { - return err - } - - // Get IP set by name - ipSet, err := vdc.GetNsxvIpSetByName(name) - if err != nil { - return err - } - - return vdc.DeleteNsxvIpSetById(ipSet.ID) -} - -func validateCreateNsxvIpSet(ipSetConfig *types.EdgeIpSet) error { - - if ipSetConfig.Name == "" { - return fmt.Errorf("IP set must have name defined") - } - - if ipSetConfig.IPAddresses == "" { - return fmt.Errorf("IP set must IP addresses defined") - } - - return nil -} - -func validateUpdateNsxvIpSet(ipSetConfig *types.EdgeIpSet) error { - - if ipSetConfig.ID == "" { - return fmt.Errorf("IP set ID must be set for update") - } - - return validateCreateNsxvIpSet(ipSetConfig) -} - -func validateGetNsxvIpSet(id, name string) error { - if id == "" && name == "" { - return fmt.Errorf("at least name or ID must be provided") - } - - return nil -} - -func validateDeleteNsxvIpSet(id, name string) error { - return validateGetNsxvIpSet(id, name) -} diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_nat.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_nat.go deleted file mode 100644 index 669a5d757..000000000 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_nat.go +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. - */ - -package govcd - -import ( - "encoding/xml" - "fmt" - "net/http" - - "github.com/vmware/go-vcloud-director/v2/types/v56" -) - -// requestEdgeNatRules nests EdgeNatRule as a convenience for unmarshalling POST requests -type requestEdgeNatRules struct { - XMLName xml.Name `xml:"natRules"` - EdgeNatRules []*types.EdgeNatRule `xml:"natRule"` -} - -// responseEdgeNatRules is used to unwrap response when retrieving -type responseEdgeNatRules struct { - XMLName xml.Name `xml:"nat"` - Version string `xml:"version"` - NatRules requestEdgeNatRules `xml:"natRules"` -} - -// CreateNsxvNatRule creates NAT rule using proxied NSX-V API. It is a synchronuous operation. -// It returns an object with all fields populated (including ID) -func (egw *EdgeGateway) CreateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types.EdgeNatRule, error) { - if err := validateCreateNsxvNatRule(natRuleConfig, egw); err != nil { - return nil, err - } - - // Wrap the provided rule for POST request - natRuleRequest := requestEdgeNatRules{ - EdgeNatRules: []*types.EdgeNatRule{natRuleConfig}, - } - - httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath) - if err != nil { - return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) - } - // We expect to get http.StatusCreated or if not an error of type types.NSXError - resp, err := egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPost, types.AnyXMLMime, - "error creating NAT rule: %s", natRuleRequest, &types.NSXError{}) - if err != nil { - return nil, err - } - - // Location header should look similar to: - // [/network/edges/edge-1/nat/config/rules/197157] - natRuleId, err := extractNsxObjectIdFromPath(resp.Header.Get("Location")) - if err != nil { - return nil, err - } - - readNatRule, err := egw.GetNsxvNatRuleById(natRuleId) - if err != nil { - return nil, fmt.Errorf("unable to retrieve NAT rule with ID (%s) after creation: %s", - natRuleId, err) - } - return readNatRule, nil -} - -// UpdateNsxvNatRule updates types.EdgeNatRule with all fields using proxied NSX-V API. ID is -// mandatory to perform the update. -func (egw *EdgeGateway) UpdateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types.EdgeNatRule, error) { - err := validateUpdateNsxvNatRule(natRuleConfig, egw) - if err != nil { - return nil, err - } - - httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath + "/" + natRuleConfig.ID) - if err != nil { - return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) - } - - // Result should be 204, if not we expect an error of type types.NSXError - _, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPut, types.AnyXMLMime, - "error while updating NAT rule : %s", natRuleConfig, &types.NSXError{}) - if err != nil { - return nil, err - } - - readNatRule, err := egw.GetNsxvNatRuleById(natRuleConfig.ID) - if err != nil { - return nil, fmt.Errorf("unable to retrieve NAT rule with ID (%s) after update: %s", - readNatRule.ID, err) - } - return readNatRule, nil -} - -// GetNsxvNatRuleById retrieves types.EdgeNatRule by NAT rule ID as shown in the UI using proxied -// NSX-V API. -// It returns and error `ErrorEntityNotFound` if the NAT rule is now found. -func (egw *EdgeGateway) GetNsxvNatRuleById(id string) (*types.EdgeNatRule, error) { - if err := validateGetNsxvNatRule(id, egw); err != nil { - return nil, err - } - - httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeNatPath) - if err != nil { - return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) - } - - natRuleResponse := &responseEdgeNatRules{} - - // This query returns all application rules as the API does not have filtering options - _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, - "unable to read NAT rule: %s", nil, natRuleResponse) - if err != nil { - return nil, err - } - - for _, rule := range natRuleResponse.NatRules.EdgeNatRules { - if rule.ID != "" && rule.ID == id { - return rule, nil - } - } - - return nil, ErrorEntityNotFound -} - -// DeleteNsxvNatRuleById deletes types.EdgeNatRule by NAT rule ID as shown in the UI using proxied -// NSX-V API. -// It returns and error `ErrorEntityNotFound` if the NAT rule is now found. -func (egw *EdgeGateway) DeleteNsxvNatRuleById(id string) error { - err := validateDeleteNsxvNatRule(id, egw) - if err != nil { - return err - } - - httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath + "/" + id) - if err != nil { - return fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) - } - - // check if the rule exists and pass back the error at it may be 'ErrorEntityNotFound' - _, err = egw.GetNsxvNatRuleById(id) - if err != nil { - return err - } - - _, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodDelete, types.AnyXMLMime, - "unable to delete nat rule: %s", nil, &types.NSXError{}) - if err != nil { - return err - } - - return nil -} - -func validateCreateNsxvNatRule(natRuleConfig *types.EdgeNatRule, egw *EdgeGateway) error { - if !egw.HasAdvancedNetworking() { - return fmt.Errorf("only advanced edge gateways support NAT rules") - } - - if natRuleConfig.Action == "" { - return fmt.Errorf("NAT rule must have an action") - } - - if natRuleConfig.TranslatedAddress == "" { - return fmt.Errorf("NAT rule must translated address specified") - } - - return nil -} - -func validateUpdateNsxvNatRule(natRuleConfig *types.EdgeNatRule, egw *EdgeGateway) error { - if natRuleConfig.ID == "" { - return fmt.Errorf("NAT rule must ID must be set for update") - } - - return validateCreateNsxvNatRule(natRuleConfig, egw) -} - -func validateGetNsxvNatRule(id string, egw *EdgeGateway) error { - if !egw.HasAdvancedNetworking() { - return fmt.Errorf("only advanced edge gateways support NAT rules") - } - - if id == "" { - return fmt.Errorf("unable to retrieve NAT rule without ID") - } - - return nil -} - -func validateDeleteNsxvNatRule(id string, egw *EdgeGateway) error { - return validateGetNsxvNatRule(id, egw) -} diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/orgvdcnetwork.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/orgvdcnetwork.go index 6708ff2a6..81e5ade62 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/orgvdcnetwork.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/orgvdcnetwork.go @@ -193,44 +193,3 @@ func (vdc *Vdc) CreateOrgVDCNetwork(networkConfig *types.OrgVDCNetwork) (Task, e } return Task{}, fmt.Errorf("network creation failed: no operational link found") } - -// GetNetworkList returns a list of networks for the VDC -func (vdc *Vdc) GetNetworkList() ([]*types.QueryResultOrgVdcNetworkRecordType, error) { - // Find the list of networks with the wanted name - result, err := vdc.client.QueryWithNotEncodedParams(nil, map[string]string{ - "type": "orgVdcNetwork", - "filter": fmt.Sprintf("vdc==%s", url.QueryEscape(vdc.Vdc.ID)), - }) - if err != nil { - return nil, fmt.Errorf("[findEdgeGatewayConnection] error returning the list of networks for VDC: %s", err) - } - return result.Results.OrgVdcNetworkRecord, nil -} - -// FindEdgeGatewayNameByNetwork searches the VDC for a connection between an edge gateway and a given network. -// On success, returns the name of the edge gateway -func (vdc *Vdc) FindEdgeGatewayNameByNetwork(networkName string) (string, error) { - - // Find the list of networks with the wanted name - result, err := vdc.client.QueryWithNotEncodedParams(nil, map[string]string{ - "type": "orgVdcNetwork", - "filter": fmt.Sprintf("name==%s;vdc==%s", url.QueryEscape(networkName), url.QueryEscape(vdc.Vdc.ID)), - }) - if err != nil { - return "", fmt.Errorf("[findEdgeGatewayConnection] error returning the list of networks for VDC: %s", err) - } - netList := result.Results.OrgVdcNetworkRecord - - for _, net := range netList { - if net.Name == networkName { - // linkType is not well documented, but empiric tests show that: - // 0 = direct - // 1 = routed - // 2 = isolated - if net.ConnectedTo != "" && net.LinkType == 1 { // We only want routed networks - return net.ConnectedTo, nil - } - } - } - return "", fmt.Errorf("no edge gateway connection found") -} diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/system.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/system.go index a912f65fd..578de301a 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/system.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/system.go @@ -128,10 +128,10 @@ func CreateEdgeGatewayAsync(vcdClient *VCDClient, egwc EdgeGatewayCreation) (Tas Name: egwc.Name, Description: egwc.Description, Configuration: &types.GatewayConfiguration{ - UseDefaultRouteForDNSRelay: &egwc.UseDefaultRouteForDNSRelay, - HaEnabled: &egwc.HAEnabled, + UseDefaultRouteForDNSRelay: egwc.UseDefaultRouteForDNSRelay, + HaEnabled: egwc.HAEnabled, GatewayBackingConfig: egwc.BackingConfiguration, - AdvancedNetworkingEnabled: &egwc.AdvancedNetworkingEnabled, + AdvancedNetworkingEnabled: egwc.AdvancedNetworkingEnabled, DistributedRoutingEnabled: &distributed, GatewayInterfaces: &types.GatewayInterfaces{ GatewayInterface: []*types.GatewayInterface{}, diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vdc.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vdc.go index 868d3b51e..454396db6 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vdc.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vdc.go @@ -820,20 +820,3 @@ func (vdc *Vdc) GetVAppByNameOrId(identifier string, refresh bool) (*VApp, error } return entity.(*VApp), err } - -// buildNsxvNetworkServiceEndpointURL uses vDC HREF as a base to derive NSX-V based "network -// services" endpoint (eg: https://_hostname_or_ip_/network/services + optionalSuffix) -func (vdc *Vdc) buildNsxvNetworkServiceEndpointURL(optionalSuffix string) (string, error) { - apiEndpoint, err := url.ParseRequestURI(vdc.Vdc.HREF) - if err != nil { - return "", fmt.Errorf("unable to process vDC URL: %s", err) - } - - hostname := apiEndpoint.Scheme + "://" + apiEndpoint.Host + "/network/services" - - if optionalSuffix != "" { - return hostname + optionalSuffix, nil - } - - return hostname, nil -} diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go index 85bd1e94d..47990b03f 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go @@ -169,11 +169,6 @@ const ( LbVirtualServerPath = "/loadbalancer/config/virtualservers/" ) -// NSX-V proxied services API endpoints -const ( - NsxvIpSetServicePath = "/ipset" -) - // Guest customization statuses. These are all known possible statuses const ( GuestCustStatusPending = "GC_PENDING" diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/nsxv_types.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/nsxv_types.go deleted file mode 100644 index 645306df3..000000000 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/nsxv_types.go +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. - */ - -package types - -import "encoding/xml" - -// FirewallConfigWithXml allows to enable/disable firewall on a specific edge gateway -// Reference: vCloud Director API for NSX Programming Guide -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -// -// Warning. It nests all firewall rules because Edge Gateway API is done so that if this data is not -// sent while enabling it would wipe all firewall rules. InnerXML type field is used with struct tag -//`innerxml` to prevent any manipulation of configuration and sending it verbatim -type FirewallConfigWithXml struct { - XMLName xml.Name `xml:"firewall"` - Enabled bool `xml:"enabled"` - DefaultPolicy FirewallDefaultPolicy `xml:"defaultPolicy"` - - // Each configuration change has a version number - Version string `xml:"version,omitempty"` - - // The below field has `innerxml` tag so that it is not processed but instead - // sent verbatim - FirewallRules InnerXML `xml:"firewallRules,omitempty"` - GlobalConfig InnerXML `xml:"globalConfig,omitempty"` -} - -// FirewallDefaultPolicy represent default rule -type FirewallDefaultPolicy struct { - LoggingEnabled bool `xml:"loggingEnabled"` - Action string `xml:"action"` -} - -// LbGeneralParamsWithXml allows to enable/disable load balancing capabilities on specific edge gateway -// Reference: vCloud Director API for NSX Programming Guide -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -// -// Warning. It nests all components (LbMonitor, LbPool, LbAppProfile, LbAppRule, LbVirtualServer) -// because Edge Gateway API is done so that if this data is not sent while enabling it would wipe -// all load balancer configurations. InnerXML type fields are used with struct tag `innerxml` to -// prevent any manipulation of configuration and sending it verbatim -type LbGeneralParamsWithXml struct { - XMLName xml.Name `xml:"loadBalancer"` - Enabled bool `xml:"enabled"` - AccelerationEnabled bool `xml:"accelerationEnabled"` - Logging *LbLogging `xml:"logging"` - - // This field is not used anywhere but needs to be passed through - EnableServiceInsertion bool `xml:"enableServiceInsertion"` - // Each configuration change has a version number - Version string `xml:"version,omitempty"` - - // The below fields have `innerxml` tag so that they are not processed but instead - // sent verbatim - VirtualServers []InnerXML `xml:"virtualServer,omitempty"` - Pools []InnerXML `xml:"pool,omitempty"` - AppProfiles []InnerXML `xml:"applicationProfile,omitempty"` - Monitors []InnerXML `xml:"monitor,omitempty"` - AppRules []InnerXML `xml:"applicationRule,omitempty"` -} - -// LbLogging represents logging configuration for load balancer -type LbLogging struct { - Enable bool `xml:"enable"` - LogLevel string `xml:"logLevel"` -} - -// InnerXML is meant to be used when unmarshaling a field into text rather than struct -// It helps to avoid missing out any fields which may not have been specified in the struct. -type InnerXML struct { - Text string `xml:",innerxml"` -} - -// LbMonitor defines health check parameters for a particular type of network traffic -// Reference: vCloud Director API for NSX Programming Guide -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -type LbMonitor struct { - XMLName xml.Name `xml:"monitor"` - ID string `xml:"monitorId,omitempty"` - Type string `xml:"type"` - Interval int `xml:"interval,omitempty"` - Timeout int `xml:"timeout,omitempty"` - MaxRetries int `xml:"maxRetries,omitempty"` - Method string `xml:"method,omitempty"` - URL string `xml:"url,omitempty"` - Expected string `xml:"expected,omitempty"` - Name string `xml:"name,omitempty"` - Send string `xml:"send,omitempty"` - Receive string `xml:"receive,omitempty"` - Extension string `xml:"extension,omitempty"` -} - -type LbMonitors []LbMonitor - -// LbPool represents a load balancer server pool as per "vCloud Director API for NSX Programming Guide" -// Type: LBPoolHealthCheckType -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -type LbPool struct { - XMLName xml.Name `xml:"pool"` - ID string `xml:"poolId,omitempty"` - Name string `xml:"name"` - Description string `xml:"description,omitempty"` - Algorithm string `xml:"algorithm"` - AlgorithmParameters string `xml:"algorithmParameters,omitempty"` - Transparent bool `xml:"transparent"` - MonitorId string `xml:"monitorId,omitempty"` - Members LbPoolMembers `xml:"member,omitempty"` -} - -type LbPools []LbPool - -// LbPoolMember represents a single member inside LbPool -type LbPoolMember struct { - ID string `xml:"memberId,omitempty"` - Name string `xml:"name"` - IpAddress string `xml:"ipAddress"` - Weight int `xml:"weight,omitempty"` - MonitorPort int `xml:"monitorPort,omitempty"` - Port int `xml:"port"` - MaxConn int `xml:"maxConn,omitempty"` - MinConn int `xml:"minConn,omitempty"` - Condition string `xml:"condition,omitempty"` -} - -type LbPoolMembers []LbPoolMember - -// LbAppProfile represents a load balancer application profile as per "vCloud Director API for NSX -// Programming Guide" -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -type LbAppProfile struct { - XMLName xml.Name `xml:"applicationProfile"` - ID string `xml:"applicationProfileId,omitempty"` - Name string `xml:"name,omitempty"` - SslPassthrough bool `xml:"sslPassthrough"` - Template string `xml:"template,omitempty"` - HttpRedirect *LbAppProfileHttpRedirect `xml:"httpRedirect,omitempty"` - Persistence *LbAppProfilePersistence `xml:"persistence,omitempty"` - InsertXForwardedForHttpHeader bool `xml:"insertXForwardedFor"` - ServerSslEnabled bool `xml:"serverSslEnabled"` -} - -type LbAppProfiles []LbAppProfile - -// LbAppProfilePersistence defines persistence profile settings in LbAppProfile -type LbAppProfilePersistence struct { - XMLName xml.Name `xml:"persistence"` - Method string `xml:"method,omitempty"` - CookieName string `xml:"cookieName,omitempty"` - CookieMode string `xml:"cookieMode,omitempty"` - Expire int `xml:"expire,omitempty"` -} - -// LbAppProfileHttpRedirect defines http redirect settings in LbAppProfile -type LbAppProfileHttpRedirect struct { - XMLName xml.Name `xml:"httpRedirect"` - To string `xml:"to,omitempty"` -} - -// LbAppRule represents a load balancer application rule as per "vCloud Director API for NSX -// Programming Guide" -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -type LbAppRule struct { - XMLName xml.Name `xml:"applicationRule"` - ID string `xml:"applicationRuleId,omitempty"` - Name string `xml:"name,omitempty"` - Script string `xml:"script,omitempty"` -} - -type LbAppRules []LbAppRule - -// LbVirtualServer represents a load balancer virtual server as per "vCloud Director API for NSX -// Programming Guide" -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -type LbVirtualServer struct { - XMLName xml.Name `xml:"virtualServer"` - ID string `xml:"virtualServerId,omitempty"` - Name string `xml:"name,omitempty"` - Description string `xml:"description,omitempty"` - Enabled bool `xml:"enabled"` - IpAddress string `xml:"ipAddress"` - Protocol string `xml:"protocol"` - Port int `xml:"port"` - AccelerationEnabled bool `xml:"accelerationEnabled"` - ConnectionLimit int `xml:"connectionLimit,omitempty"` - ConnectionRateLimit int `xml:"connectionRateLimit,omitempty"` - ApplicationProfileId string `xml:"applicationProfileId,omitempty"` - DefaultPoolId string `xml:"defaultPoolId,omitempty"` - ApplicationRuleIds []string `xml:"applicationRuleId,omitempty"` -} - -// EdgeNatRule contains shared structure for SNAT and DNAT rule configuration using -// NSX-V proxied edge gateway endpoint -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -type EdgeNatRule struct { - XMLName xml.Name `xml:"natRule"` - ID string `xml:"ruleId,omitempty"` - RuleType string `xml:"ruleType,omitempty"` - RuleTag string `xml:"ruleTag,omitempty"` - Action string `xml:"action"` - Vnic *int `xml:"vnic,omitempty"` - OriginalAddress string `xml:"originalAddress"` - TranslatedAddress string `xml:"translatedAddress"` - LoggingEnabled bool `xml:"loggingEnabled"` - Enabled bool `xml:"enabled"` - Description string `xml:"description,omitempty"` - Protocol string `xml:"protocol,omitempty"` - OriginalPort string `xml:"originalPort,omitempty"` - TranslatedPort string `xml:"translatedPort,omitempty"` - IcmpType string `xml:"icmpType,omitempty"` -} - -// EdgeFirewall holds data for creating firewall rule using proxied NSX-V API -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -type EdgeFirewallRule struct { - XMLName xml.Name `xml:"firewallRule" ` - ID string `xml:"id,omitempty"` - Name string `xml:"name,omitempty"` - RuleType string `xml:"ruleType,omitempty"` - RuleTag string `xml:"ruleTag,omitempty"` - Source EdgeFirewallEndpoint `xml:"source" ` - Destination EdgeFirewallEndpoint `xml:"destination"` - Application EdgeFirewallApplication `xml:"application"` - MatchTranslated *bool `xml:"matchTranslated,omitempty"` - Direction string `xml:"direction,omitempty"` - Action string `xml:"action,omitempty"` - Enabled bool `xml:"enabled"` - LoggingEnabled bool `xml:"loggingEnabled"` -} - -// EdgeFirewallEndpoint can contains slices of objects for source or destination in EdgeFirewall -type EdgeFirewallEndpoint struct { - Exclude bool `xml:"exclude"` - VnicGroupIds []string `xml:"vnicGroupId,omitempty"` - GroupingObjectIds []string `xml:"groupingObjectId,omitempty"` - IpAddresses []string `xml:"ipAddress,omitempty"` -} - -// EdgeFirewallApplication Wraps []EdgeFirewallApplicationService for multiple protocol/port specification -type EdgeFirewallApplication struct { - ID string `xml:"applicationId,omitempty"` - Services []EdgeFirewallApplicationService `xml:"service,omitempty"` -} - -// EdgeFirewallApplicationService defines port/protocol details for one service in EdgeFirewallRule -type EdgeFirewallApplicationService struct { - Protocol string `xml:"protocol,omitempty"` - Port string `xml:"port,omitempty"` - SourcePort string `xml:"sourcePort,omitempty"` -} - -// EdgeIpSet defines a group of IP addresses that you can add as the source or destination in a -// firewall rule or in DHCP relay configuration. The object itself has more fields in API response, -// however vCD UI only uses the below mentioned. It looks as if the other fields are used in NSX -// internally and are simply proxied back. -// -// Note. Only advanced edge gateways support IP sets -type EdgeIpSet struct { - XMLName xml.Name `xml:"ipset"` - // ID holds composite ID of IP set which is formatted as - // 'f9daf2da-b4f9-4921-a2f4-d77a943a381c:ipset-4' where the first segment before colon is vDC id - // and the second one is IP set ID - ID string `xml:"objectId,omitempty"` - // Name is mandatory and must be unique - Name string `xml:"name"` - // Description - optional - Description string `xml:"description,omitempty"` - // IPAddresses is a mandatory field with comma separated values. The API is known to re-order - // data after submiting and may shuffle components even if re-submitted as it was return from - // API itself - // (eg: "192.168.200.1,192.168.200.1/24,192.168.200.1-192.168.200.24") - IPAddresses string `xml:"value"` - // InheritanceAllowed defines visibility at underlying scopes - InheritanceAllowed *bool `xml:"inheritanceAllowed"` - // Revision is a "version" of IP set configuration. During read current revision is being - // returned and when update is performed this latest version must be sent as it validates if no - // updates ocurred in between. When not the latest version is being sent during update one can - // expect similar error response from API: "The object ipset-27 used in this operation has an - // older version 0 than the current system version 1. Refresh UI or fetch the latest copy of the - // object and retry operation." - Revision *int `xml:"revision,omitempty"` -} - -// EdgeIpSets is a slice of pointers to EdgeIpSet -type EdgeIpSets []*EdgeIpSet diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go index 7a289bf2c..8ca037450 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go @@ -2,8 +2,6 @@ * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. */ -// Package types/v56 provider all types which are used by govcd package in order to perform API -// requests and parse responses package types import ( @@ -213,13 +211,7 @@ type NetworkConfiguration struct { RetainNetInfoAcrossDeployments bool `xml:"RetainNetInfoAcrossDeployments,omitempty"` Features *NetworkFeatures `xml:"Features,omitempty"` GuestVlanAllowed *bool `xml:"GuestVlanAllowed,omitempty"` - - // SubInterface and DistributedInterface are mutually exclusive - // When they are both nil, it means the "internal" interface (the default) will be used. - // When one of them is set, the corresponding interface will be used. - // They cannot be both set (we'll get an API error if we do). - SubInterface *bool `xml:"SubInterface,omitempty"` - DistributedInterface *bool `xml:"DistributedInterface,omitempty"` + DistributedInterface *bool `xml:"DistributedInterface,omitempty"` // TODO: Not Implemented // RouterInfo RouterInfo `xml:"RouterInfo,omitempty"` // SyslogServerSettings SyslogServerSettings `xml:"SyslogServerSettings,omitempty"` @@ -339,8 +331,8 @@ type OrgVDCNetwork struct { OperationKey string `xml:"operationKey,attr,omitempty"` Name string `xml:"name,attr"` Status string `xml:"status,attr,omitempty"` - Description string `xml:"Description,omitempty"` Configuration *NetworkConfiguration `xml:"Configuration,omitempty"` + Description string `xml:"Description,omitempty"` EdgeGateway *Reference `xml:"EdgeGateway,omitempty"` ServiceConfig *GatewayFeatures `xml:"ServiceConfig,omitempty"` // Specifies the service configuration for an isolated Org VDC networks IsShared bool `xml:"IsShared"` @@ -1682,35 +1674,15 @@ type EdgeGateway struct { // Since: 5.1 type GatewayConfiguration struct { Xmlns string `xml:"xmlns,attr,omitempty"` - // BackwardCompatibilityMode. Default is false. If set to true, will allow users to write firewall - // rules in the old 1.5 format. The new format does not require to use direction in firewall - // rules. Also, for firewall rules to allow NAT traffic the filter is applied on the original IP - // addresses. Once set to true cannot be reverted back to false. - BackwardCompatibilityMode bool `xml:"BackwardCompatibilityMode,omitempty"` - // GatewayBackingConfig defines configuration of the vShield edge VM for this gateway. One of: - // compact, full. - GatewayBackingConfig string `xml:"GatewayBackingConfig"` - // GatewayInterfaces holds configuration for edge gateway interfaces, ip allocations, traffic - // rate limits and ip sub-allocations - GatewayInterfaces *GatewayInterfaces `xml:"GatewayInterfaces"` - // EdgeGatewayServiceConfiguration represents Gateway Features. - EdgeGatewayServiceConfiguration *GatewayFeatures `xml:"EdgeGatewayServiceConfiguration,omitempty"` - // True if this gateway is highly available. (Requires two vShield edge VMs.) - HaEnabled *bool `xml:"HaEnabled,omitempty"` - // UseDefaultRouteForDNSRelay defines if the default gateway on the external network selected - // for default route should be used as the DNS relay. - UseDefaultRouteForDNSRelay *bool `xml:"UseDefaultRouteForDnsRelay,omitempty"` - // AdvancedNetworkingEnabled allows to use NSX capabilities such dynamic routing (BGP, OSPF), - // zero trust networking (DLR), enchanced VPN support (IPsec VPN, SSL VPN-Plus). - AdvancedNetworkingEnabled *bool `xml:"AdvancedNetworkingEnabled,omitempty"` - // DistributedRoutingEnabled enables distributed routing on the gateway to allow creation of - // many more organization VDC networks. Traffic in those networks is optimized for VM-to-VM - // communication. - DistributedRoutingEnabled *bool `xml:"DistributedRoutingEnabled,omitempty"` - // FipsModeEnabled allows any secure communication to or from the NSX Edge uses cryptographic - // algorithms or protocols that are allowed by United States Federal Information Processing - // Standards (FIPS). FIPS mode turns on the cipher suites that comply with FIPS. - FipsModeEnabled *bool `xml:"FipsModeEnabled,omitempty"` + // Elements + BackwardCompatibilityMode bool `xml:"BackwardCompatibilityMode,omitempty"` // Compatibility mode. Default is false. If set to true, will allow users to write firewall rules in the old 1.5 format. The new format does not require to use direction in firewall rules. Also, for firewall rules to allow NAT traffic the filter is applied on the original IP addresses. Once set to true cannot be reverted back to false. + GatewayBackingConfig string `xml:"GatewayBackingConfig"` // Configuration of the vShield edge VM for this gateway. One of: compact, full. + GatewayInterfaces *GatewayInterfaces `xml:"GatewayInterfaces"` // List of Gateway interfaces. + EdgeGatewayServiceConfiguration *GatewayFeatures `xml:"EdgeGatewayServiceConfiguration,omitempty"` // Represents Gateway Features. + HaEnabled bool `xml:"HaEnabled,omitempty"` // True if this gateway is highly available. (Requires two vShield edge VMs.) + AdvancedNetworkingEnabled bool `xml:"AdvancedNetworkingEnabled,omitempty"` // True if the gateway uses advanced networking + DistributedRoutingEnabled *bool `xml:"DistributedRoutingEnabled,omitempty"` // True if gateway is attached to a Distributed Logical Router + UseDefaultRouteForDNSRelay bool `xml:"UseDefaultRouteForDnsRelay,omitempty"` // True if the default gateway on the external network selected for default route should be used as the DNS relay. } // GatewayInterfaces is a list of Gateway Interfaces. @@ -1739,27 +1711,16 @@ type GatewayInterface struct { UseForDefaultRoute bool `xml:"UseForDefaultRoute,omitempty"` // True if this network is default route for the gateway. } -// SortBySubnetParticipationGateway allows to sort SubnetParticipation property slice by gateway -// address -func (g *GatewayInterface) SortBySubnetParticipationGateway() { - sort.SliceStable(g.SubnetParticipation, func(i, j int) bool { - return g.SubnetParticipation[i].Gateway < g.SubnetParticipation[j].Gateway - }) -} - // SubnetParticipation allows to chose which subnets a gateway can be a part of // Type: SubnetParticipationType // Namespace: http://www.vmware.com/vcloud/v1.5 // Description: Allows to chose which subnets a gateway can be part of // Since: 5.1 -// -// Note. Field order is important and should not be changed as API returns errors if IPRanges come -// before Gateway and Netmask type SubnetParticipation struct { Gateway string `xml:"Gateway"` // Gateway for subnet - Netmask string `xml:"Netmask"` // Netmask for the subnet. IPAddress string `xml:"IpAddress,omitempty"` // Ip Address to be assigned. Keep empty or omit element for auto assignment IPRanges *IPRanges `xml:"IpRanges,omitempty"` // Range of IP addresses available for external interfaces. + Netmask string `xml:"Netmask"` // Netmask for the subnet UseForDefaultRoute bool `xml:"UseForDefaultRoute,omitempty"` // True if this network is default route for the gateway. } @@ -1811,6 +1772,250 @@ type StaticRoute struct { GatewayInterface *Reference `xml:"GatewayInterface,omitempty"` // Gateway interface to which static route is bound. } +// FirewallConfigWithXml allows to enable/disable firewall on a specific edge gateway +// Reference: vCloud Director API for NSX Programming Guide +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +// +// Warning. It nests all firewall rules because Edge Gateway API is done so that if this data is not +// sent while enabling it would wipe all firewall rules. InnerXML type field is used with struct tag +//`innerxml` to prevent any manipulation of configuration and sending it verbatim +type FirewallConfigWithXml struct { + XMLName xml.Name `xml:"firewall"` + Enabled bool `xml:"enabled"` + DefaultPolicy FirewallDefaultPolicy `xml:"defaultPolicy"` + + // Each configuration change has a version number + Version string `xml:"version,omitempty"` + + // The below field has `innerxml` tag so that it is not processed but instead + // sent verbatim + FirewallRules InnerXML `xml:"firewallRules,omitempty"` + GlobalConfig InnerXML `xml:"globalConfig,omitempty"` +} + +// FirewallDefaultPolicy represent default rule +type FirewallDefaultPolicy struct { + LoggingEnabled bool `xml:"loggingEnabled"` + Action string `xml:"action"` +} + +// LbGeneralParamsWithXml allows to enable/disable load balancing capabilities on specific edge gateway +// Reference: vCloud Director API for NSX Programming Guide +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +// +// Warning. It nests all components (LbMonitor, LbPool, LbAppProfile, LbAppRule, LbVirtualServer) +// because Edge Gateway API is done so that if this data is not sent while enabling it would wipe +// all load balancer configurations. InnerXML type fields are used with struct tag `innerxml` to +// prevent any manipulation of configuration and sending it verbatim +type LbGeneralParamsWithXml struct { + XMLName xml.Name `xml:"loadBalancer"` + Enabled bool `xml:"enabled"` + AccelerationEnabled bool `xml:"accelerationEnabled"` + Logging *LbLogging `xml:"logging"` + + // This field is not used anywhere but needs to be passed through + EnableServiceInsertion bool `xml:"enableServiceInsertion"` + // Each configuration change has a version number + Version string `xml:"version,omitempty"` + + // The below fields have `innerxml` tag so that they are not processed but instead + // sent verbatim + VirtualServers []InnerXML `xml:"virtualServer,omitempty"` + Pools []InnerXML `xml:"pool,omitempty"` + AppProfiles []InnerXML `xml:"applicationProfile,omitempty"` + Monitors []InnerXML `xml:"monitor,omitempty"` + AppRules []InnerXML `xml:"applicationRule,omitempty"` +} + +// LbLogging represents logging configuration for load balancer +type LbLogging struct { + Enable bool `xml:"enable"` + LogLevel string `xml:"logLevel"` +} + +// InnerXML is meant to be used when unmarshaling a field into text rather than struct +// It helps to avoid missing out any fields which may not have been specified in the struct. +type InnerXML struct { + Text string `xml:",innerxml"` +} + +// LbMonitor defines health check parameters for a particular type of network traffic +// Reference: vCloud Director API for NSX Programming Guide +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +type LbMonitor struct { + XMLName xml.Name `xml:"monitor"` + ID string `xml:"monitorId,omitempty"` + Type string `xml:"type"` + Interval int `xml:"interval,omitempty"` + Timeout int `xml:"timeout,omitempty"` + MaxRetries int `xml:"maxRetries,omitempty"` + Method string `xml:"method,omitempty"` + URL string `xml:"url,omitempty"` + Expected string `xml:"expected,omitempty"` + Name string `xml:"name,omitempty"` + Send string `xml:"send,omitempty"` + Receive string `xml:"receive,omitempty"` + Extension string `xml:"extension,omitempty"` +} + +type LbMonitors []LbMonitor + +// LbPool represents a load balancer server pool as per "vCloud Director API for NSX Programming Guide" +// Type: LBPoolHealthCheckType +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +type LbPool struct { + XMLName xml.Name `xml:"pool"` + ID string `xml:"poolId,omitempty"` + Name string `xml:"name"` + Description string `xml:"description,omitempty"` + Algorithm string `xml:"algorithm"` + AlgorithmParameters string `xml:"algorithmParameters,omitempty"` + Transparent bool `xml:"transparent"` + MonitorId string `xml:"monitorId,omitempty"` + Members LbPoolMembers `xml:"member,omitempty"` +} + +type LbPools []LbPool + +// LbPoolMember represents a single member inside LbPool +type LbPoolMember struct { + ID string `xml:"memberId,omitempty"` + Name string `xml:"name"` + IpAddress string `xml:"ipAddress"` + Weight int `xml:"weight,omitempty"` + MonitorPort int `xml:"monitorPort,omitempty"` + Port int `xml:"port"` + MaxConn int `xml:"maxConn,omitempty"` + MinConn int `xml:"minConn,omitempty"` + Condition string `xml:"condition,omitempty"` +} + +type LbPoolMembers []LbPoolMember + +// LbAppProfile represents a load balancer application profile as per "vCloud Director API for NSX +// Programming Guide" +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +type LbAppProfile struct { + XMLName xml.Name `xml:"applicationProfile"` + ID string `xml:"applicationProfileId,omitempty"` + Name string `xml:"name,omitempty"` + SslPassthrough bool `xml:"sslPassthrough"` + Template string `xml:"template,omitempty"` + HttpRedirect *LbAppProfileHttpRedirect `xml:"httpRedirect,omitempty"` + Persistence *LbAppProfilePersistence `xml:"persistence,omitempty"` + InsertXForwardedForHttpHeader bool `xml:"insertXForwardedFor"` + ServerSslEnabled bool `xml:"serverSslEnabled"` +} + +type LbAppProfiles []LbAppProfile + +// LbAppProfilePersistence defines persistence profile settings in LbAppProfile +type LbAppProfilePersistence struct { + XMLName xml.Name `xml:"persistence"` + Method string `xml:"method,omitempty"` + CookieName string `xml:"cookieName,omitempty"` + CookieMode string `xml:"cookieMode,omitempty"` + Expire int `xml:"expire,omitempty"` +} + +// LbAppProfileHttpRedirect defines http redirect settings in LbAppProfile +type LbAppProfileHttpRedirect struct { + XMLName xml.Name `xml:"httpRedirect"` + To string `xml:"to,omitempty"` +} + +// LbAppRule represents a load balancer application rule as per "vCloud Director API for NSX +// Programming Guide" +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +type LbAppRule struct { + XMLName xml.Name `xml:"applicationRule"` + ID string `xml:"applicationRuleId,omitempty"` + Name string `xml:"name,omitempty"` + Script string `xml:"script,omitempty"` +} + +type LbAppRules []LbAppRule + +// LbVirtualServer represents a load balancer virtual server as per "vCloud Director API for NSX +// Programming Guide" +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +type LbVirtualServer struct { + XMLName xml.Name `xml:"virtualServer"` + ID string `xml:"virtualServerId,omitempty"` + Name string `xml:"name,omitempty"` + Description string `xml:"description,omitempty"` + Enabled bool `xml:"enabled"` + IpAddress string `xml:"ipAddress"` + Protocol string `xml:"protocol"` + Port int `xml:"port"` + AccelerationEnabled bool `xml:"accelerationEnabled"` + ConnectionLimit int `xml:"connectionLimit,omitempty"` + ConnectionRateLimit int `xml:"connectionRateLimit,omitempty"` + ApplicationProfileId string `xml:"applicationProfileId,omitempty"` + DefaultPoolId string `xml:"defaultPoolId,omitempty"` + ApplicationRuleIds []string `xml:"applicationRuleId,omitempty"` +} + +// EdgeNatRule contains shared structure for SNAT and DNAT rule configuration using +// NSX-V proxied edge gateway endpoint +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +type EdgeNatRule struct { + XMLName xml.Name `xml:"natRule"` + ID string `xml:"ruleId,omitempty"` + RuleType string `xml:"ruleType,omitempty"` + RuleTag string `xml:"ruleTag,omitempty"` + Action string `xml:"action"` + Vnic *int `xml:"vnic,omitempty"` + OriginalAddress string `xml:"originalAddress"` + TranslatedAddress string `xml:"translatedAddress"` + LoggingEnabled bool `xml:"loggingEnabled"` + Enabled bool `xml:"enabled"` + Description string `xml:"description,omitempty"` + Protocol string `xml:"protocol,omitempty"` + OriginalPort string `xml:"originalPort,omitempty"` + TranslatedPort string `xml:"translatedPort,omitempty"` + IcmpType string `xml:"icmpType,omitempty"` +} + +// EdgeFirewall holds data for creating firewall rule using proxied NSX-V API +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +type EdgeFirewallRule struct { + XMLName xml.Name `xml:"firewallRule" ` + ID string `xml:"id,omitempty"` + Name string `xml:"name,omitempty"` + RuleType string `xml:"ruleType,omitempty"` + RuleTag string `xml:"ruleTag,omitempty"` + Source EdgeFirewallEndpoint `xml:"source" ` + Destination EdgeFirewallEndpoint `xml:"destination"` + Application EdgeFirewallApplication `xml:"application"` + MatchTranslated *bool `xml:"matchTranslated,omitempty"` + Direction string `xml:"direction,omitempty"` + Action string `xml:"action,omitempty"` + Enabled bool `xml:"enabled"` + LoggingEnabled bool `xml:"loggingEnabled"` +} + +// EdgeFirewallEndpoint can contains slices of objects for source or destination in EdgeFirewall +type EdgeFirewallEndpoint struct { + Exclude bool `xml:"exclude"` + VnicGroupIds []string `xml:"vnicGroupId,omitempty"` + GroupingObjectIds []string `xml:"groupingObjectId,omitempty"` + IpAddresses []string `xml:"ipAddress,omitempty"` +} + +// EdgeFirewallApplication Wraps []EdgeFirewallApplicationService for multiple protocol/port specification +type EdgeFirewallApplication struct { + ID string `xml:"applicationId,omitempty"` + Services []EdgeFirewallApplicationService `xml:"service,omitempty"` +} + +// EdgeFirewallApplicationService defines port/protocol details for one service in EdgeFirewallRule +type EdgeFirewallApplicationService struct { + Protocol string `xml:"protocol,omitempty"` + Port string `xml:"port,omitempty"` + SourcePort string `xml:"sourcePort,omitempty"` +} + // VendorTemplate is information about a vendor service template. This is optional. // Type: VendorTemplateType // Namespace: http://www.vmware.com/vcloud/v1.5 @@ -2587,7 +2792,7 @@ type QueryResultOrgVdcNetworkRecordType struct { Dns1 string `xml:"dns1,attr,omitempty"` Dns2 string `xml:"dns2,attr,omitempty"` DnsSuffix string `xml:"dnsSuffix,attr,omitempty"` - LinkType int `xml:"linkType,attr,omitempty"` // 0 = direct, 1 = routed, 2 = isolated + LinkType int `xml:"linkType,attr,omitempty"` ConnectedTo string `xml:"connectedTo,attr,omitempty"` Vdc string `xml:"vdc,attr,omitempty"` IsBusy bool `xml:"isBusy,attr,omitempty"` diff --git a/vendor/modules.txt b/vendor/modules.txt index 46f312d09..2b4e0b352 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -224,7 +224,7 @@ github.com/ulikunitz/xz/lzma # github.com/vmihailenco/msgpack v4.0.1+incompatible github.com/vmihailenco/msgpack github.com/vmihailenco/msgpack/codes -# github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 +# github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 => ../go-vcloud-director github.com/vmware/go-vcloud-director/v2/govcd github.com/vmware/go-vcloud-director/v2/types/v56 github.com/vmware/go-vcloud-director/v2/util From 6719df7b7480539525d6bdf1c6b28daba14cea50 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 2 Dec 2019 16:37:31 +0200 Subject: [PATCH 07/80] latest govcd with local changes Signed-off-by: Vaidotas Bauzys --- .../v2/govcd/edgegateway.go | 4 +- .../v2/govcd/lbserverpool.go | 38 ++- .../v2/govcd/lbservicemonitor.go | 36 +- .../v2/govcd/lbvirtualserver.go | 42 ++- .../v2/govcd/orgvdcnetwork.go | 41 +++ .../go-vcloud-director/v2/govcd/system.go | 6 +- .../vmware/go-vcloud-director/v2/govcd/vdc.go | 17 + .../v2/types/v56/constants.go | 5 + .../go-vcloud-director/v2/types/v56/types.go | 309 +++--------------- 9 files changed, 194 insertions(+), 304 deletions(-) diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go index bdd195162..02f1adef8 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go @@ -1059,7 +1059,9 @@ func (egw *EdgeGateway) HasDefaultGateway() bool { // HasAdvancedNetworking returns true if the edge gateway has advanced network configuration enabled func (egw *EdgeGateway) HasAdvancedNetworking() bool { - return egw.EdgeGateway.Configuration != nil && egw.EdgeGateway.Configuration.AdvancedNetworkingEnabled + return egw.EdgeGateway.Configuration != nil && + egw.EdgeGateway.Configuration.AdvancedNetworkingEnabled != nil && + *egw.EdgeGateway.Configuration.AdvancedNetworkingEnabled } // buildProxiedEdgeEndpointURL helps to get root endpoint for Edge Gateway using the diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbserverpool.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbserverpool.go index cf2c7b335..631736dfe 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbserverpool.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbserverpool.go @@ -52,25 +52,13 @@ func (egw *EdgeGateway) getLbServerPool(lbPoolConfig *types.LbPool) (*types.LbPo return nil, err } - httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbServerPoolPath) - if err != nil { - return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) - } - - // Anonymous struct to unwrap "server pool response" - lbPoolResponse := &struct { - LBPools []*types.LbPool `xml:"pool"` - }{} - - // This query returns all server pools as the API does not have filtering options - _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, - "unable to read load lalancer server pool: %s", nil, lbPoolResponse) + pools, err := egw.GetLbServerPools() if err != nil { return nil, err } // Search for pool by ID or by Name - for _, pool := range lbPoolResponse.LBPools { + for _, pool := range pools { // If ID was specified for lookup - look for the same ID if lbPoolConfig.ID != "" && pool.ID == lbPoolConfig.ID { return pool, nil @@ -90,6 +78,28 @@ func (egw *EdgeGateway) getLbServerPool(lbPoolConfig *types.LbPool) (*types.LbPo return nil, ErrorEntityNotFound } +// GetLbServerPools return all created server pools without filtering. +func (egw *EdgeGateway) GetLbServerPools() ([]*types.LbPool, error) { + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbServerPoolPath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // Anonymous struct to unwrap "server pool response" + lbPoolResponse := &struct { + LBPools []*types.LbPool `xml:"pool"` + }{} + + // This query returns all server pools as the API does not have filtering options + _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, + "unable to read load lalancer server pool: %s", nil, lbPoolResponse) + if err != nil { + return nil, err + } + + return lbPoolResponse.LBPools, nil +} + // GetLbServerPoolByName wraps getLbServerPool and needs only an ID for lookup func (egw *EdgeGateway) GetLbServerPoolById(id string) (*types.LbPool, error) { return egw.getLbServerPool(&types.LbPool{ID: id}) diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbservicemonitor.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbservicemonitor.go index 72dbc4699..bf100e4f8 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbservicemonitor.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbservicemonitor.go @@ -55,24 +55,13 @@ func (egw *EdgeGateway) getLbServiceMonitor(lbMonitorConfig *types.LbMonitor) (* return nil, err } - httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbMonitorPath) - if err != nil { - return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) - } - - // Anonymous struct to unwrap "monitor response" - lbMonitorResponse := &struct { - LBMonitors []*types.LbMonitor `xml:"monitor"` - }{} - - // This query returns all service monitors as the API does not have filtering options - _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, "unable to read Load Balancer monitor: %s", nil, lbMonitorResponse) + serviceMonitors, err := egw.GetLbServiceMonitors() if err != nil { return nil, err } // Search for monitor by ID or by Name - for _, monitor := range lbMonitorResponse.LBMonitors { + for _, monitor := range serviceMonitors { // If ID was specified for lookup - look for the same ID if lbMonitorConfig.ID != "" && monitor.ID == lbMonitorConfig.ID { return monitor, nil @@ -92,6 +81,27 @@ func (egw *EdgeGateway) getLbServiceMonitor(lbMonitorConfig *types.LbMonitor) (* return nil, ErrorEntityNotFound } +// GetLbServiceMonitors return all service monitors without filtering +func (egw *EdgeGateway) GetLbServiceMonitors() ([]*types.LbMonitor, error) { + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbMonitorPath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // Anonymous struct to unwrap "monitor response" + lbMonitorResponse := &struct { + LBMonitors []*types.LbMonitor `xml:"monitor"` + }{} + + // This query returns all service monitors as the API does not have filtering options + _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, "unable to read Load Balancer monitor: %s", nil, lbMonitorResponse) + if err != nil { + return nil, err + } + + return lbMonitorResponse.LBMonitors, nil +} + // GetLbServiceMonitorById wraps getLbServiceMonitor and needs only an ID for lookup func (egw *EdgeGateway) GetLbServiceMonitorById(id string) (*types.LbMonitor, error) { return egw.getLbServiceMonitor(&types.LbMonitor{ID: id}) diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbvirtualserver.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbvirtualserver.go index f0a703d30..e12115a9d 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbvirtualserver.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbvirtualserver.go @@ -54,25 +54,13 @@ func (egw *EdgeGateway) getLbVirtualServer(lbVirtualServerConfig *types.LbVirtua return nil, err } - httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbVirtualServerPath) - if err != nil { - return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) - } - - // Anonymous struct to unwrap "virtual server response" - lbVirtualServerResponse := &struct { - LBVirtualServers []*types.LbVirtualServer `xml:"virtualServer"` - }{} - - // This query returns all virtual servers as the API does not have filtering options - _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, - "unable to read load balancer virtual server: %s", nil, lbVirtualServerResponse) + vs, err := egw.GetLbVirtualServers() if err != nil { return nil, err } // Search for virtual server by ID or by Name - for _, virtualServer := range lbVirtualServerResponse.LBVirtualServers { + for _, virtualServer := range vs { // If ID was specified for lookup - look for the same ID if lbVirtualServerConfig.ID != "" && virtualServer.ID == lbVirtualServerConfig.ID { return virtualServer, nil @@ -93,12 +81,34 @@ func (egw *EdgeGateway) getLbVirtualServer(lbVirtualServerConfig *types.LbVirtua return nil, ErrorEntityNotFound } -// GetLbVirtualServerById wraps getLbVirtualServer and needs only an ID for lookup +// GetLbVirtualServers is getting all virtual servers without filtering anything +func (egw *EdgeGateway) GetLbVirtualServers() ([]*types.LbVirtualServer, error) { + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.LbVirtualServerPath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // Anonymous struct to unwrap "virtual server response" + lbVirtualServerResponse := &struct { + LBVirtualServers []*types.LbVirtualServer `xml:"virtualServer"` + }{} + + // This query returns all virtual servers as the API does not have filtering options + _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, + "unable to read load balancer virtual server: %s", nil, lbVirtualServerResponse) + if err != nil { + return nil, err + } + + return lbVirtualServerResponse.LBVirtualServers, nil +} + +// GetLbVirtualServerById wraps getLbVirtualServers and needs only an ID for lookup func (egw *EdgeGateway) GetLbVirtualServerById(id string) (*types.LbVirtualServer, error) { return egw.getLbVirtualServer(&types.LbVirtualServer{ID: id}) } -// GetLbVirtualServerByName wraps getLbVirtualServer and needs only a Name for lookup +// GetLbVirtualServerByName wraps getLbVirtualServers and needs only a Name for lookup func (egw *EdgeGateway) GetLbVirtualServerByName(name string) (*types.LbVirtualServer, error) { return egw.getLbVirtualServer(&types.LbVirtualServer{Name: name}) } diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/orgvdcnetwork.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/orgvdcnetwork.go index 81e5ade62..6708ff2a6 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/orgvdcnetwork.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/orgvdcnetwork.go @@ -193,3 +193,44 @@ func (vdc *Vdc) CreateOrgVDCNetwork(networkConfig *types.OrgVDCNetwork) (Task, e } return Task{}, fmt.Errorf("network creation failed: no operational link found") } + +// GetNetworkList returns a list of networks for the VDC +func (vdc *Vdc) GetNetworkList() ([]*types.QueryResultOrgVdcNetworkRecordType, error) { + // Find the list of networks with the wanted name + result, err := vdc.client.QueryWithNotEncodedParams(nil, map[string]string{ + "type": "orgVdcNetwork", + "filter": fmt.Sprintf("vdc==%s", url.QueryEscape(vdc.Vdc.ID)), + }) + if err != nil { + return nil, fmt.Errorf("[findEdgeGatewayConnection] error returning the list of networks for VDC: %s", err) + } + return result.Results.OrgVdcNetworkRecord, nil +} + +// FindEdgeGatewayNameByNetwork searches the VDC for a connection between an edge gateway and a given network. +// On success, returns the name of the edge gateway +func (vdc *Vdc) FindEdgeGatewayNameByNetwork(networkName string) (string, error) { + + // Find the list of networks with the wanted name + result, err := vdc.client.QueryWithNotEncodedParams(nil, map[string]string{ + "type": "orgVdcNetwork", + "filter": fmt.Sprintf("name==%s;vdc==%s", url.QueryEscape(networkName), url.QueryEscape(vdc.Vdc.ID)), + }) + if err != nil { + return "", fmt.Errorf("[findEdgeGatewayConnection] error returning the list of networks for VDC: %s", err) + } + netList := result.Results.OrgVdcNetworkRecord + + for _, net := range netList { + if net.Name == networkName { + // linkType is not well documented, but empiric tests show that: + // 0 = direct + // 1 = routed + // 2 = isolated + if net.ConnectedTo != "" && net.LinkType == 1 { // We only want routed networks + return net.ConnectedTo, nil + } + } + } + return "", fmt.Errorf("no edge gateway connection found") +} diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/system.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/system.go index 578de301a..a912f65fd 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/system.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/system.go @@ -128,10 +128,10 @@ func CreateEdgeGatewayAsync(vcdClient *VCDClient, egwc EdgeGatewayCreation) (Tas Name: egwc.Name, Description: egwc.Description, Configuration: &types.GatewayConfiguration{ - UseDefaultRouteForDNSRelay: egwc.UseDefaultRouteForDNSRelay, - HaEnabled: egwc.HAEnabled, + UseDefaultRouteForDNSRelay: &egwc.UseDefaultRouteForDNSRelay, + HaEnabled: &egwc.HAEnabled, GatewayBackingConfig: egwc.BackingConfiguration, - AdvancedNetworkingEnabled: egwc.AdvancedNetworkingEnabled, + AdvancedNetworkingEnabled: &egwc.AdvancedNetworkingEnabled, DistributedRoutingEnabled: &distributed, GatewayInterfaces: &types.GatewayInterfaces{ GatewayInterface: []*types.GatewayInterface{}, diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vdc.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vdc.go index 454396db6..868d3b51e 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vdc.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vdc.go @@ -820,3 +820,20 @@ func (vdc *Vdc) GetVAppByNameOrId(identifier string, refresh bool) (*VApp, error } return entity.(*VApp), err } + +// buildNsxvNetworkServiceEndpointURL uses vDC HREF as a base to derive NSX-V based "network +// services" endpoint (eg: https://_hostname_or_ip_/network/services + optionalSuffix) +func (vdc *Vdc) buildNsxvNetworkServiceEndpointURL(optionalSuffix string) (string, error) { + apiEndpoint, err := url.ParseRequestURI(vdc.Vdc.HREF) + if err != nil { + return "", fmt.Errorf("unable to process vDC URL: %s", err) + } + + hostname := apiEndpoint.Scheme + "://" + apiEndpoint.Host + "/network/services" + + if optionalSuffix != "" { + return hostname + optionalSuffix, nil + } + + return hostname, nil +} diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go index 47990b03f..85bd1e94d 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go @@ -169,6 +169,11 @@ const ( LbVirtualServerPath = "/loadbalancer/config/virtualservers/" ) +// NSX-V proxied services API endpoints +const ( + NsxvIpSetServicePath = "/ipset" +) + // Guest customization statuses. These are all known possible statuses const ( GuestCustStatusPending = "GC_PENDING" diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go index 8ca037450..7a289bf2c 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go @@ -2,6 +2,8 @@ * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. */ +// Package types/v56 provider all types which are used by govcd package in order to perform API +// requests and parse responses package types import ( @@ -211,7 +213,13 @@ type NetworkConfiguration struct { RetainNetInfoAcrossDeployments bool `xml:"RetainNetInfoAcrossDeployments,omitempty"` Features *NetworkFeatures `xml:"Features,omitempty"` GuestVlanAllowed *bool `xml:"GuestVlanAllowed,omitempty"` - DistributedInterface *bool `xml:"DistributedInterface,omitempty"` + + // SubInterface and DistributedInterface are mutually exclusive + // When they are both nil, it means the "internal" interface (the default) will be used. + // When one of them is set, the corresponding interface will be used. + // They cannot be both set (we'll get an API error if we do). + SubInterface *bool `xml:"SubInterface,omitempty"` + DistributedInterface *bool `xml:"DistributedInterface,omitempty"` // TODO: Not Implemented // RouterInfo RouterInfo `xml:"RouterInfo,omitempty"` // SyslogServerSettings SyslogServerSettings `xml:"SyslogServerSettings,omitempty"` @@ -331,8 +339,8 @@ type OrgVDCNetwork struct { OperationKey string `xml:"operationKey,attr,omitempty"` Name string `xml:"name,attr"` Status string `xml:"status,attr,omitempty"` - Configuration *NetworkConfiguration `xml:"Configuration,omitempty"` Description string `xml:"Description,omitempty"` + Configuration *NetworkConfiguration `xml:"Configuration,omitempty"` EdgeGateway *Reference `xml:"EdgeGateway,omitempty"` ServiceConfig *GatewayFeatures `xml:"ServiceConfig,omitempty"` // Specifies the service configuration for an isolated Org VDC networks IsShared bool `xml:"IsShared"` @@ -1674,15 +1682,35 @@ type EdgeGateway struct { // Since: 5.1 type GatewayConfiguration struct { Xmlns string `xml:"xmlns,attr,omitempty"` - // Elements - BackwardCompatibilityMode bool `xml:"BackwardCompatibilityMode,omitempty"` // Compatibility mode. Default is false. If set to true, will allow users to write firewall rules in the old 1.5 format. The new format does not require to use direction in firewall rules. Also, for firewall rules to allow NAT traffic the filter is applied on the original IP addresses. Once set to true cannot be reverted back to false. - GatewayBackingConfig string `xml:"GatewayBackingConfig"` // Configuration of the vShield edge VM for this gateway. One of: compact, full. - GatewayInterfaces *GatewayInterfaces `xml:"GatewayInterfaces"` // List of Gateway interfaces. - EdgeGatewayServiceConfiguration *GatewayFeatures `xml:"EdgeGatewayServiceConfiguration,omitempty"` // Represents Gateway Features. - HaEnabled bool `xml:"HaEnabled,omitempty"` // True if this gateway is highly available. (Requires two vShield edge VMs.) - AdvancedNetworkingEnabled bool `xml:"AdvancedNetworkingEnabled,omitempty"` // True if the gateway uses advanced networking - DistributedRoutingEnabled *bool `xml:"DistributedRoutingEnabled,omitempty"` // True if gateway is attached to a Distributed Logical Router - UseDefaultRouteForDNSRelay bool `xml:"UseDefaultRouteForDnsRelay,omitempty"` // True if the default gateway on the external network selected for default route should be used as the DNS relay. + // BackwardCompatibilityMode. Default is false. If set to true, will allow users to write firewall + // rules in the old 1.5 format. The new format does not require to use direction in firewall + // rules. Also, for firewall rules to allow NAT traffic the filter is applied on the original IP + // addresses. Once set to true cannot be reverted back to false. + BackwardCompatibilityMode bool `xml:"BackwardCompatibilityMode,omitempty"` + // GatewayBackingConfig defines configuration of the vShield edge VM for this gateway. One of: + // compact, full. + GatewayBackingConfig string `xml:"GatewayBackingConfig"` + // GatewayInterfaces holds configuration for edge gateway interfaces, ip allocations, traffic + // rate limits and ip sub-allocations + GatewayInterfaces *GatewayInterfaces `xml:"GatewayInterfaces"` + // EdgeGatewayServiceConfiguration represents Gateway Features. + EdgeGatewayServiceConfiguration *GatewayFeatures `xml:"EdgeGatewayServiceConfiguration,omitempty"` + // True if this gateway is highly available. (Requires two vShield edge VMs.) + HaEnabled *bool `xml:"HaEnabled,omitempty"` + // UseDefaultRouteForDNSRelay defines if the default gateway on the external network selected + // for default route should be used as the DNS relay. + UseDefaultRouteForDNSRelay *bool `xml:"UseDefaultRouteForDnsRelay,omitempty"` + // AdvancedNetworkingEnabled allows to use NSX capabilities such dynamic routing (BGP, OSPF), + // zero trust networking (DLR), enchanced VPN support (IPsec VPN, SSL VPN-Plus). + AdvancedNetworkingEnabled *bool `xml:"AdvancedNetworkingEnabled,omitempty"` + // DistributedRoutingEnabled enables distributed routing on the gateway to allow creation of + // many more organization VDC networks. Traffic in those networks is optimized for VM-to-VM + // communication. + DistributedRoutingEnabled *bool `xml:"DistributedRoutingEnabled,omitempty"` + // FipsModeEnabled allows any secure communication to or from the NSX Edge uses cryptographic + // algorithms or protocols that are allowed by United States Federal Information Processing + // Standards (FIPS). FIPS mode turns on the cipher suites that comply with FIPS. + FipsModeEnabled *bool `xml:"FipsModeEnabled,omitempty"` } // GatewayInterfaces is a list of Gateway Interfaces. @@ -1711,16 +1739,27 @@ type GatewayInterface struct { UseForDefaultRoute bool `xml:"UseForDefaultRoute,omitempty"` // True if this network is default route for the gateway. } +// SortBySubnetParticipationGateway allows to sort SubnetParticipation property slice by gateway +// address +func (g *GatewayInterface) SortBySubnetParticipationGateway() { + sort.SliceStable(g.SubnetParticipation, func(i, j int) bool { + return g.SubnetParticipation[i].Gateway < g.SubnetParticipation[j].Gateway + }) +} + // SubnetParticipation allows to chose which subnets a gateway can be a part of // Type: SubnetParticipationType // Namespace: http://www.vmware.com/vcloud/v1.5 // Description: Allows to chose which subnets a gateway can be part of // Since: 5.1 +// +// Note. Field order is important and should not be changed as API returns errors if IPRanges come +// before Gateway and Netmask type SubnetParticipation struct { Gateway string `xml:"Gateway"` // Gateway for subnet + Netmask string `xml:"Netmask"` // Netmask for the subnet. IPAddress string `xml:"IpAddress,omitempty"` // Ip Address to be assigned. Keep empty or omit element for auto assignment IPRanges *IPRanges `xml:"IpRanges,omitempty"` // Range of IP addresses available for external interfaces. - Netmask string `xml:"Netmask"` // Netmask for the subnet UseForDefaultRoute bool `xml:"UseForDefaultRoute,omitempty"` // True if this network is default route for the gateway. } @@ -1772,250 +1811,6 @@ type StaticRoute struct { GatewayInterface *Reference `xml:"GatewayInterface,omitempty"` // Gateway interface to which static route is bound. } -// FirewallConfigWithXml allows to enable/disable firewall on a specific edge gateway -// Reference: vCloud Director API for NSX Programming Guide -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -// -// Warning. It nests all firewall rules because Edge Gateway API is done so that if this data is not -// sent while enabling it would wipe all firewall rules. InnerXML type field is used with struct tag -//`innerxml` to prevent any manipulation of configuration and sending it verbatim -type FirewallConfigWithXml struct { - XMLName xml.Name `xml:"firewall"` - Enabled bool `xml:"enabled"` - DefaultPolicy FirewallDefaultPolicy `xml:"defaultPolicy"` - - // Each configuration change has a version number - Version string `xml:"version,omitempty"` - - // The below field has `innerxml` tag so that it is not processed but instead - // sent verbatim - FirewallRules InnerXML `xml:"firewallRules,omitempty"` - GlobalConfig InnerXML `xml:"globalConfig,omitempty"` -} - -// FirewallDefaultPolicy represent default rule -type FirewallDefaultPolicy struct { - LoggingEnabled bool `xml:"loggingEnabled"` - Action string `xml:"action"` -} - -// LbGeneralParamsWithXml allows to enable/disable load balancing capabilities on specific edge gateway -// Reference: vCloud Director API for NSX Programming Guide -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -// -// Warning. It nests all components (LbMonitor, LbPool, LbAppProfile, LbAppRule, LbVirtualServer) -// because Edge Gateway API is done so that if this data is not sent while enabling it would wipe -// all load balancer configurations. InnerXML type fields are used with struct tag `innerxml` to -// prevent any manipulation of configuration and sending it verbatim -type LbGeneralParamsWithXml struct { - XMLName xml.Name `xml:"loadBalancer"` - Enabled bool `xml:"enabled"` - AccelerationEnabled bool `xml:"accelerationEnabled"` - Logging *LbLogging `xml:"logging"` - - // This field is not used anywhere but needs to be passed through - EnableServiceInsertion bool `xml:"enableServiceInsertion"` - // Each configuration change has a version number - Version string `xml:"version,omitempty"` - - // The below fields have `innerxml` tag so that they are not processed but instead - // sent verbatim - VirtualServers []InnerXML `xml:"virtualServer,omitempty"` - Pools []InnerXML `xml:"pool,omitempty"` - AppProfiles []InnerXML `xml:"applicationProfile,omitempty"` - Monitors []InnerXML `xml:"monitor,omitempty"` - AppRules []InnerXML `xml:"applicationRule,omitempty"` -} - -// LbLogging represents logging configuration for load balancer -type LbLogging struct { - Enable bool `xml:"enable"` - LogLevel string `xml:"logLevel"` -} - -// InnerXML is meant to be used when unmarshaling a field into text rather than struct -// It helps to avoid missing out any fields which may not have been specified in the struct. -type InnerXML struct { - Text string `xml:",innerxml"` -} - -// LbMonitor defines health check parameters for a particular type of network traffic -// Reference: vCloud Director API for NSX Programming Guide -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -type LbMonitor struct { - XMLName xml.Name `xml:"monitor"` - ID string `xml:"monitorId,omitempty"` - Type string `xml:"type"` - Interval int `xml:"interval,omitempty"` - Timeout int `xml:"timeout,omitempty"` - MaxRetries int `xml:"maxRetries,omitempty"` - Method string `xml:"method,omitempty"` - URL string `xml:"url,omitempty"` - Expected string `xml:"expected,omitempty"` - Name string `xml:"name,omitempty"` - Send string `xml:"send,omitempty"` - Receive string `xml:"receive,omitempty"` - Extension string `xml:"extension,omitempty"` -} - -type LbMonitors []LbMonitor - -// LbPool represents a load balancer server pool as per "vCloud Director API for NSX Programming Guide" -// Type: LBPoolHealthCheckType -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -type LbPool struct { - XMLName xml.Name `xml:"pool"` - ID string `xml:"poolId,omitempty"` - Name string `xml:"name"` - Description string `xml:"description,omitempty"` - Algorithm string `xml:"algorithm"` - AlgorithmParameters string `xml:"algorithmParameters,omitempty"` - Transparent bool `xml:"transparent"` - MonitorId string `xml:"monitorId,omitempty"` - Members LbPoolMembers `xml:"member,omitempty"` -} - -type LbPools []LbPool - -// LbPoolMember represents a single member inside LbPool -type LbPoolMember struct { - ID string `xml:"memberId,omitempty"` - Name string `xml:"name"` - IpAddress string `xml:"ipAddress"` - Weight int `xml:"weight,omitempty"` - MonitorPort int `xml:"monitorPort,omitempty"` - Port int `xml:"port"` - MaxConn int `xml:"maxConn,omitempty"` - MinConn int `xml:"minConn,omitempty"` - Condition string `xml:"condition,omitempty"` -} - -type LbPoolMembers []LbPoolMember - -// LbAppProfile represents a load balancer application profile as per "vCloud Director API for NSX -// Programming Guide" -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -type LbAppProfile struct { - XMLName xml.Name `xml:"applicationProfile"` - ID string `xml:"applicationProfileId,omitempty"` - Name string `xml:"name,omitempty"` - SslPassthrough bool `xml:"sslPassthrough"` - Template string `xml:"template,omitempty"` - HttpRedirect *LbAppProfileHttpRedirect `xml:"httpRedirect,omitempty"` - Persistence *LbAppProfilePersistence `xml:"persistence,omitempty"` - InsertXForwardedForHttpHeader bool `xml:"insertXForwardedFor"` - ServerSslEnabled bool `xml:"serverSslEnabled"` -} - -type LbAppProfiles []LbAppProfile - -// LbAppProfilePersistence defines persistence profile settings in LbAppProfile -type LbAppProfilePersistence struct { - XMLName xml.Name `xml:"persistence"` - Method string `xml:"method,omitempty"` - CookieName string `xml:"cookieName,omitempty"` - CookieMode string `xml:"cookieMode,omitempty"` - Expire int `xml:"expire,omitempty"` -} - -// LbAppProfileHttpRedirect defines http redirect settings in LbAppProfile -type LbAppProfileHttpRedirect struct { - XMLName xml.Name `xml:"httpRedirect"` - To string `xml:"to,omitempty"` -} - -// LbAppRule represents a load balancer application rule as per "vCloud Director API for NSX -// Programming Guide" -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -type LbAppRule struct { - XMLName xml.Name `xml:"applicationRule"` - ID string `xml:"applicationRuleId,omitempty"` - Name string `xml:"name,omitempty"` - Script string `xml:"script,omitempty"` -} - -type LbAppRules []LbAppRule - -// LbVirtualServer represents a load balancer virtual server as per "vCloud Director API for NSX -// Programming Guide" -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -type LbVirtualServer struct { - XMLName xml.Name `xml:"virtualServer"` - ID string `xml:"virtualServerId,omitempty"` - Name string `xml:"name,omitempty"` - Description string `xml:"description,omitempty"` - Enabled bool `xml:"enabled"` - IpAddress string `xml:"ipAddress"` - Protocol string `xml:"protocol"` - Port int `xml:"port"` - AccelerationEnabled bool `xml:"accelerationEnabled"` - ConnectionLimit int `xml:"connectionLimit,omitempty"` - ConnectionRateLimit int `xml:"connectionRateLimit,omitempty"` - ApplicationProfileId string `xml:"applicationProfileId,omitempty"` - DefaultPoolId string `xml:"defaultPoolId,omitempty"` - ApplicationRuleIds []string `xml:"applicationRuleId,omitempty"` -} - -// EdgeNatRule contains shared structure for SNAT and DNAT rule configuration using -// NSX-V proxied edge gateway endpoint -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -type EdgeNatRule struct { - XMLName xml.Name `xml:"natRule"` - ID string `xml:"ruleId,omitempty"` - RuleType string `xml:"ruleType,omitempty"` - RuleTag string `xml:"ruleTag,omitempty"` - Action string `xml:"action"` - Vnic *int `xml:"vnic,omitempty"` - OriginalAddress string `xml:"originalAddress"` - TranslatedAddress string `xml:"translatedAddress"` - LoggingEnabled bool `xml:"loggingEnabled"` - Enabled bool `xml:"enabled"` - Description string `xml:"description,omitempty"` - Protocol string `xml:"protocol,omitempty"` - OriginalPort string `xml:"originalPort,omitempty"` - TranslatedPort string `xml:"translatedPort,omitempty"` - IcmpType string `xml:"icmpType,omitempty"` -} - -// EdgeFirewall holds data for creating firewall rule using proxied NSX-V API -// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide -type EdgeFirewallRule struct { - XMLName xml.Name `xml:"firewallRule" ` - ID string `xml:"id,omitempty"` - Name string `xml:"name,omitempty"` - RuleType string `xml:"ruleType,omitempty"` - RuleTag string `xml:"ruleTag,omitempty"` - Source EdgeFirewallEndpoint `xml:"source" ` - Destination EdgeFirewallEndpoint `xml:"destination"` - Application EdgeFirewallApplication `xml:"application"` - MatchTranslated *bool `xml:"matchTranslated,omitempty"` - Direction string `xml:"direction,omitempty"` - Action string `xml:"action,omitempty"` - Enabled bool `xml:"enabled"` - LoggingEnabled bool `xml:"loggingEnabled"` -} - -// EdgeFirewallEndpoint can contains slices of objects for source or destination in EdgeFirewall -type EdgeFirewallEndpoint struct { - Exclude bool `xml:"exclude"` - VnicGroupIds []string `xml:"vnicGroupId,omitempty"` - GroupingObjectIds []string `xml:"groupingObjectId,omitempty"` - IpAddresses []string `xml:"ipAddress,omitempty"` -} - -// EdgeFirewallApplication Wraps []EdgeFirewallApplicationService for multiple protocol/port specification -type EdgeFirewallApplication struct { - ID string `xml:"applicationId,omitempty"` - Services []EdgeFirewallApplicationService `xml:"service,omitempty"` -} - -// EdgeFirewallApplicationService defines port/protocol details for one service in EdgeFirewallRule -type EdgeFirewallApplicationService struct { - Protocol string `xml:"protocol,omitempty"` - Port string `xml:"port,omitempty"` - SourcePort string `xml:"sourcePort,omitempty"` -} - // VendorTemplate is information about a vendor service template. This is optional. // Type: VendorTemplateType // Namespace: http://www.vmware.com/vcloud/v1.5 @@ -2792,7 +2587,7 @@ type QueryResultOrgVdcNetworkRecordType struct { Dns1 string `xml:"dns1,attr,omitempty"` Dns2 string `xml:"dns2,attr,omitempty"` DnsSuffix string `xml:"dnsSuffix,attr,omitempty"` - LinkType int `xml:"linkType,attr,omitempty"` + LinkType int `xml:"linkType,attr,omitempty"` // 0 = direct, 1 = routed, 2 = isolated ConnectedTo string `xml:"connectedTo,attr,omitempty"` Vdc string `xml:"vdc,attr,omitempty"` IsBusy bool `xml:"isBusy,attr,omitempty"` From f00f7010a08e3b9c58c868e6cdf4e0f6ec5cc277 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Tue, 3 Dec 2019 12:08:35 +0200 Subject: [PATCH 08/80] missing files Signed-off-by: Vaidotas Bauzys --- .../go-vcloud-director/v2/govcd/nsxv_ipset.go | 256 ++++++++++++++++ .../go-vcloud-director/v2/govcd/nsxv_nat.go | 192 ++++++++++++ .../v2/types/v56/nsxv_types.go | 286 ++++++++++++++++++ 3 files changed, 734 insertions(+) create mode 100644 vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_ipset.go create mode 100644 vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_nat.go create mode 100644 vendor/github.com/vmware/go-vcloud-director/v2/types/v56/nsxv_types.go diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_ipset.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_ipset.go new file mode 100644 index 000000000..7754eebf5 --- /dev/null +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_ipset.go @@ -0,0 +1,256 @@ +/* + * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "encoding/xml" + "fmt" + "net/http" + + "github.com/vmware/go-vcloud-director/v2/types/v56" + "github.com/vmware/go-vcloud-director/v2/util" +) + +// CreateNsxvIpSet creates an IP set from *types.EdgeIpSet. IP set defines a group of IP addresses +// that you can add as the source or destination in a firewall rule or in DHCP relay configuration. +func (vdc *Vdc) CreateNsxvIpSet(ipSetConfig *types.EdgeIpSet) (*types.EdgeIpSet, error) { + if err := validateCreateNsxvIpSet(ipSetConfig); err != nil { + return nil, err + } + + vdcId, err := getUuidFromHref(vdc.Vdc.HREF) + if err != nil { + return nil, fmt.Errorf("unable to get vdc ID from HREF: %s", err) + } + + // build a path for IP set creation. The endpoint should look like: + // https://_hostname_/network/services/ipset/f9daf2da-b4f9-4921-a2f4-d77a943a381c where the + // trailing UUID is vDC ID + httpPath, err := vdc.buildNsxvNetworkServiceEndpointURL(types.NsxvIpSetServicePath + "/" + vdcId) + if err != nil { + return nil, fmt.Errorf("could not get network services API endpoint for IP set: %s", err) + } + + // Success or an error of type types.NSXError is expected + _, err = vdc.client.ExecuteParamRequestWithCustomError(httpPath, nil, http.MethodPost, types.AnyXMLMime, + "error creating IP set: %s", ipSetConfig, &types.NSXError{}) + if err != nil { + return nil, err + } + + createdIpSet, err := vdc.GetNsxvIpSetByName(ipSetConfig.Name) + if err != nil { + return nil, fmt.Errorf("could not lookup newly created IP set with name %s: %s", ipSetConfig.Name, err) + } + + return createdIpSet, nil +} + +// UpdateNsxvIpSet sends all fields of ipSetConfig. Omiting a value may reset it. ID is mandatory to +// perform update. +// Because the API always requires a Revision to be sent - the update fetches latest revision number +// automatically and embeds into the update structure. +func (vdc *Vdc) UpdateNsxvIpSet(ipSetConfig *types.EdgeIpSet) (*types.EdgeIpSet, error) { + err := validateUpdateNsxvIpSet(ipSetConfig) + if err != nil { + return nil, err + } + + // Inject latest Revision for this IP set so that API accepts change + currentIpSet, err := vdc.GetNsxvIpSetById(ipSetConfig.ID) + if err != nil { + return nil, fmt.Errorf("could not fetch current IP set: %s", err) + } + ipSetConfig.Revision = currentIpSet.Revision + + httpPath, err := vdc.buildNsxvNetworkServiceEndpointURL(types.NsxvIpSetServicePath + "/" + ipSetConfig.ID) + if err != nil { + return nil, fmt.Errorf("could not get network services API endpoint for IP set: %s", err) + } + + // Result is either 204 for success, or an error of type types.NSXError + errString := fmt.Sprintf("error while updating IP set with ID %s :%%s", ipSetConfig.ID) + _, err = vdc.client.ExecuteRequestWithCustomError(httpPath, http.MethodPut, types.AnyXMLMime, + errString, ipSetConfig, &types.NSXError{}) + if err != nil { + return nil, err + } + + updatedIpSet, err := vdc.GetNsxvIpSetById(ipSetConfig.ID) + if err != nil { + return nil, fmt.Errorf("could not lookup updated IP set with ID %s: %s", ipSetConfig.ID, err) + } + + return updatedIpSet, nil +} + +// GetNsxvIpSetByName searches for IP set by name. Names are unique therefore it can find only one. +// Returns ErrorEntityNotFound if an IP set is not found +func (vdc *Vdc) GetNsxvIpSetByName(name string) (*types.EdgeIpSet, error) { + if err := validateGetNsxvIpSet("", name); err != nil { + return nil, err + } + + allIpSets, err := vdc.GetAllNsxvIpSets() + if err != nil { + return nil, err + } + + util.Logger.Printf("[DEBUG] Searching for IP set with name: %s", name) + for _, ipSet := range allIpSets { + util.Logger.Printf("[DEBUG] Checking IP set: %#+v", ipSet) + if ipSet.Name != "" && ipSet.Name == name { + return ipSet, nil + } + } + + return nil, ErrorEntityNotFound +} + +// GetNsxvIpSetById searches for IP set by ID. Returns ErrorEntityNotFound if an IP set is not found +func (vdc *Vdc) GetNsxvIpSetById(id string) (*types.EdgeIpSet, error) { + if err := validateGetNsxvIpSet(id, ""); err != nil { + return nil, err + } + + allIpSets, err := vdc.GetAllNsxvIpSets() + if err != nil { + return nil, err + } + + util.Logger.Printf("[DEBUG] Searching for IP set with id: %s", id) + for _, ipSet := range allIpSets { + util.Logger.Printf("[DEBUG] Checking IP set: %#+v", ipSet) + if ipSet.ID != "" && ipSet.ID == id { + return ipSet, nil + } + } + + return nil, ErrorEntityNotFound +} + +// GetNsxvIpSetByNameOrId uses the same identifier to search by name and by ID. Priority is to try +// and find the IP set by ID. If it is not found - then a search by name is performed. +func (vdc *Vdc) GetNsxvIpSetByNameOrId(identifier string) (*types.EdgeIpSet, error) { + getByName := func(name string, refresh bool) (interface{}, error) { return vdc.GetNsxvIpSetByName(name) } + getById := func(id string, refresh bool) (interface{}, error) { return vdc.GetNsxvIpSetById(id) } + entity, err := getEntityByNameOrId(getByName, getById, identifier, true) + if entity == nil { + return nil, err + } + return entity.(*types.EdgeIpSet), err +} + +// GetAllNsxvIpSets retrieves all IP sets and returns []*types.EdgeIpSet or an +// error of type ErrorEntityNotFound if there are no IP sets +func (vdc *Vdc) GetAllNsxvIpSets() ([]*types.EdgeIpSet, error) { + vdcId, err := getUuidFromHref(vdc.Vdc.HREF) + if err != nil { + return nil, fmt.Errorf("unable to get vdc ID from HREF: %s", err) + } + + // build a path for to read all IP sets in a scope. A scope is defined by vDC ID. The endpoint + // should look like: + // https://192.168.1.109/network/services/ipset/scope/f9daf2da-b4f9-4921-a2f4-d77a943a381c where + // the trailing UUID is vDC ID + httpPath, err := vdc.buildNsxvNetworkServiceEndpointURL(types.NsxvIpSetServicePath + "/scope/" + vdcId) + if err != nil { + return nil, fmt.Errorf("could not get network services API endpoint for IP set: %s", err) + } + + // Anonymous struct to unwrap list of IP sets + ipSetsResponse := &struct { + XMLName xml.Name `xml:"list"` + types.EdgeIpSets `xml:"ipset"` + }{} + + // This query returns all IP sets on the scope (scoped by vDC ID) + errString := fmt.Sprintf("unable to read IP sets for scope %s: %%s", vdcId) + _, err = vdc.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, errString, nil, ipSetsResponse) + if err != nil { + return nil, err + } + + if len(ipSetsResponse.EdgeIpSets) == 0 { + return nil, ErrorEntityNotFound + } + + return ipSetsResponse.EdgeIpSets, nil +} + +// DeleteNsxvIpSetById deletes IP set by its ID which is formatted as +// f9daf2da-b4f9-4921-a2f4-d77a943a381c:ipset-9 +func (vdc *Vdc) DeleteNsxvIpSetById(id string) error { + err := validateDeleteNsxvIpSet(id, "") + if err != nil { + return err + } + + // build a path for to delete exact IP set sample path is: DELETE API-URL/services/ipset/id:ipset-# + // https://192.168.1.109/network/services/ipset/f9daf2da-b4f9-4921-a2f4-d77a943a381c:ipset-9 + httpPath, err := vdc.buildNsxvNetworkServiceEndpointURL(types.NsxvIpSetServicePath + "/" + id) + if err != nil { + return fmt.Errorf("could not get network services API endpoint for IP set: %s", err) + } + + errString := fmt.Sprintf("unable to delete IP set with ID %s: %%s", id) + _, err = vdc.client.ExecuteRequestWithCustomError(httpPath, http.MethodDelete, types.AnyXMLMime, + errString, nil, &types.NSXError{}) + if err != nil { + return err + } + + return nil +} + +// DeleteNsxvIpSetById deletes IP set by its name +func (vdc *Vdc) DeleteNsxvIpSetByName(name string) error { + err := validateDeleteNsxvIpSet("", name) + if err != nil { + return err + } + + // Get IP set by name + ipSet, err := vdc.GetNsxvIpSetByName(name) + if err != nil { + return err + } + + return vdc.DeleteNsxvIpSetById(ipSet.ID) +} + +func validateCreateNsxvIpSet(ipSetConfig *types.EdgeIpSet) error { + + if ipSetConfig.Name == "" { + return fmt.Errorf("IP set must have name defined") + } + + if ipSetConfig.IPAddresses == "" { + return fmt.Errorf("IP set must IP addresses defined") + } + + return nil +} + +func validateUpdateNsxvIpSet(ipSetConfig *types.EdgeIpSet) error { + + if ipSetConfig.ID == "" { + return fmt.Errorf("IP set ID must be set for update") + } + + return validateCreateNsxvIpSet(ipSetConfig) +} + +func validateGetNsxvIpSet(id, name string) error { + if id == "" && name == "" { + return fmt.Errorf("at least name or ID must be provided") + } + + return nil +} + +func validateDeleteNsxvIpSet(id, name string) error { + return validateGetNsxvIpSet(id, name) +} diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_nat.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_nat.go new file mode 100644 index 000000000..669a5d757 --- /dev/null +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_nat.go @@ -0,0 +1,192 @@ +/* + * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "encoding/xml" + "fmt" + "net/http" + + "github.com/vmware/go-vcloud-director/v2/types/v56" +) + +// requestEdgeNatRules nests EdgeNatRule as a convenience for unmarshalling POST requests +type requestEdgeNatRules struct { + XMLName xml.Name `xml:"natRules"` + EdgeNatRules []*types.EdgeNatRule `xml:"natRule"` +} + +// responseEdgeNatRules is used to unwrap response when retrieving +type responseEdgeNatRules struct { + XMLName xml.Name `xml:"nat"` + Version string `xml:"version"` + NatRules requestEdgeNatRules `xml:"natRules"` +} + +// CreateNsxvNatRule creates NAT rule using proxied NSX-V API. It is a synchronuous operation. +// It returns an object with all fields populated (including ID) +func (egw *EdgeGateway) CreateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types.EdgeNatRule, error) { + if err := validateCreateNsxvNatRule(natRuleConfig, egw); err != nil { + return nil, err + } + + // Wrap the provided rule for POST request + natRuleRequest := requestEdgeNatRules{ + EdgeNatRules: []*types.EdgeNatRule{natRuleConfig}, + } + + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + // We expect to get http.StatusCreated or if not an error of type types.NSXError + resp, err := egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPost, types.AnyXMLMime, + "error creating NAT rule: %s", natRuleRequest, &types.NSXError{}) + if err != nil { + return nil, err + } + + // Location header should look similar to: + // [/network/edges/edge-1/nat/config/rules/197157] + natRuleId, err := extractNsxObjectIdFromPath(resp.Header.Get("Location")) + if err != nil { + return nil, err + } + + readNatRule, err := egw.GetNsxvNatRuleById(natRuleId) + if err != nil { + return nil, fmt.Errorf("unable to retrieve NAT rule with ID (%s) after creation: %s", + natRuleId, err) + } + return readNatRule, nil +} + +// UpdateNsxvNatRule updates types.EdgeNatRule with all fields using proxied NSX-V API. ID is +// mandatory to perform the update. +func (egw *EdgeGateway) UpdateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types.EdgeNatRule, error) { + err := validateUpdateNsxvNatRule(natRuleConfig, egw) + if err != nil { + return nil, err + } + + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath + "/" + natRuleConfig.ID) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // Result should be 204, if not we expect an error of type types.NSXError + _, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPut, types.AnyXMLMime, + "error while updating NAT rule : %s", natRuleConfig, &types.NSXError{}) + if err != nil { + return nil, err + } + + readNatRule, err := egw.GetNsxvNatRuleById(natRuleConfig.ID) + if err != nil { + return nil, fmt.Errorf("unable to retrieve NAT rule with ID (%s) after update: %s", + readNatRule.ID, err) + } + return readNatRule, nil +} + +// GetNsxvNatRuleById retrieves types.EdgeNatRule by NAT rule ID as shown in the UI using proxied +// NSX-V API. +// It returns and error `ErrorEntityNotFound` if the NAT rule is now found. +func (egw *EdgeGateway) GetNsxvNatRuleById(id string) (*types.EdgeNatRule, error) { + if err := validateGetNsxvNatRule(id, egw); err != nil { + return nil, err + } + + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeNatPath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + natRuleResponse := &responseEdgeNatRules{} + + // This query returns all application rules as the API does not have filtering options + _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, + "unable to read NAT rule: %s", nil, natRuleResponse) + if err != nil { + return nil, err + } + + for _, rule := range natRuleResponse.NatRules.EdgeNatRules { + if rule.ID != "" && rule.ID == id { + return rule, nil + } + } + + return nil, ErrorEntityNotFound +} + +// DeleteNsxvNatRuleById deletes types.EdgeNatRule by NAT rule ID as shown in the UI using proxied +// NSX-V API. +// It returns and error `ErrorEntityNotFound` if the NAT rule is now found. +func (egw *EdgeGateway) DeleteNsxvNatRuleById(id string) error { + err := validateDeleteNsxvNatRule(id, egw) + if err != nil { + return err + } + + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath + "/" + id) + if err != nil { + return fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // check if the rule exists and pass back the error at it may be 'ErrorEntityNotFound' + _, err = egw.GetNsxvNatRuleById(id) + if err != nil { + return err + } + + _, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodDelete, types.AnyXMLMime, + "unable to delete nat rule: %s", nil, &types.NSXError{}) + if err != nil { + return err + } + + return nil +} + +func validateCreateNsxvNatRule(natRuleConfig *types.EdgeNatRule, egw *EdgeGateway) error { + if !egw.HasAdvancedNetworking() { + return fmt.Errorf("only advanced edge gateways support NAT rules") + } + + if natRuleConfig.Action == "" { + return fmt.Errorf("NAT rule must have an action") + } + + if natRuleConfig.TranslatedAddress == "" { + return fmt.Errorf("NAT rule must translated address specified") + } + + return nil +} + +func validateUpdateNsxvNatRule(natRuleConfig *types.EdgeNatRule, egw *EdgeGateway) error { + if natRuleConfig.ID == "" { + return fmt.Errorf("NAT rule must ID must be set for update") + } + + return validateCreateNsxvNatRule(natRuleConfig, egw) +} + +func validateGetNsxvNatRule(id string, egw *EdgeGateway) error { + if !egw.HasAdvancedNetworking() { + return fmt.Errorf("only advanced edge gateways support NAT rules") + } + + if id == "" { + return fmt.Errorf("unable to retrieve NAT rule without ID") + } + + return nil +} + +func validateDeleteNsxvNatRule(id string, egw *EdgeGateway) error { + return validateGetNsxvNatRule(id, egw) +} diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/nsxv_types.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/nsxv_types.go new file mode 100644 index 000000000..645306df3 --- /dev/null +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/nsxv_types.go @@ -0,0 +1,286 @@ +/* + * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package types + +import "encoding/xml" + +// FirewallConfigWithXml allows to enable/disable firewall on a specific edge gateway +// Reference: vCloud Director API for NSX Programming Guide +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +// +// Warning. It nests all firewall rules because Edge Gateway API is done so that if this data is not +// sent while enabling it would wipe all firewall rules. InnerXML type field is used with struct tag +//`innerxml` to prevent any manipulation of configuration and sending it verbatim +type FirewallConfigWithXml struct { + XMLName xml.Name `xml:"firewall"` + Enabled bool `xml:"enabled"` + DefaultPolicy FirewallDefaultPolicy `xml:"defaultPolicy"` + + // Each configuration change has a version number + Version string `xml:"version,omitempty"` + + // The below field has `innerxml` tag so that it is not processed but instead + // sent verbatim + FirewallRules InnerXML `xml:"firewallRules,omitempty"` + GlobalConfig InnerXML `xml:"globalConfig,omitempty"` +} + +// FirewallDefaultPolicy represent default rule +type FirewallDefaultPolicy struct { + LoggingEnabled bool `xml:"loggingEnabled"` + Action string `xml:"action"` +} + +// LbGeneralParamsWithXml allows to enable/disable load balancing capabilities on specific edge gateway +// Reference: vCloud Director API for NSX Programming Guide +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +// +// Warning. It nests all components (LbMonitor, LbPool, LbAppProfile, LbAppRule, LbVirtualServer) +// because Edge Gateway API is done so that if this data is not sent while enabling it would wipe +// all load balancer configurations. InnerXML type fields are used with struct tag `innerxml` to +// prevent any manipulation of configuration and sending it verbatim +type LbGeneralParamsWithXml struct { + XMLName xml.Name `xml:"loadBalancer"` + Enabled bool `xml:"enabled"` + AccelerationEnabled bool `xml:"accelerationEnabled"` + Logging *LbLogging `xml:"logging"` + + // This field is not used anywhere but needs to be passed through + EnableServiceInsertion bool `xml:"enableServiceInsertion"` + // Each configuration change has a version number + Version string `xml:"version,omitempty"` + + // The below fields have `innerxml` tag so that they are not processed but instead + // sent verbatim + VirtualServers []InnerXML `xml:"virtualServer,omitempty"` + Pools []InnerXML `xml:"pool,omitempty"` + AppProfiles []InnerXML `xml:"applicationProfile,omitempty"` + Monitors []InnerXML `xml:"monitor,omitempty"` + AppRules []InnerXML `xml:"applicationRule,omitempty"` +} + +// LbLogging represents logging configuration for load balancer +type LbLogging struct { + Enable bool `xml:"enable"` + LogLevel string `xml:"logLevel"` +} + +// InnerXML is meant to be used when unmarshaling a field into text rather than struct +// It helps to avoid missing out any fields which may not have been specified in the struct. +type InnerXML struct { + Text string `xml:",innerxml"` +} + +// LbMonitor defines health check parameters for a particular type of network traffic +// Reference: vCloud Director API for NSX Programming Guide +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +type LbMonitor struct { + XMLName xml.Name `xml:"monitor"` + ID string `xml:"monitorId,omitempty"` + Type string `xml:"type"` + Interval int `xml:"interval,omitempty"` + Timeout int `xml:"timeout,omitempty"` + MaxRetries int `xml:"maxRetries,omitempty"` + Method string `xml:"method,omitempty"` + URL string `xml:"url,omitempty"` + Expected string `xml:"expected,omitempty"` + Name string `xml:"name,omitempty"` + Send string `xml:"send,omitempty"` + Receive string `xml:"receive,omitempty"` + Extension string `xml:"extension,omitempty"` +} + +type LbMonitors []LbMonitor + +// LbPool represents a load balancer server pool as per "vCloud Director API for NSX Programming Guide" +// Type: LBPoolHealthCheckType +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +type LbPool struct { + XMLName xml.Name `xml:"pool"` + ID string `xml:"poolId,omitempty"` + Name string `xml:"name"` + Description string `xml:"description,omitempty"` + Algorithm string `xml:"algorithm"` + AlgorithmParameters string `xml:"algorithmParameters,omitempty"` + Transparent bool `xml:"transparent"` + MonitorId string `xml:"monitorId,omitempty"` + Members LbPoolMembers `xml:"member,omitempty"` +} + +type LbPools []LbPool + +// LbPoolMember represents a single member inside LbPool +type LbPoolMember struct { + ID string `xml:"memberId,omitempty"` + Name string `xml:"name"` + IpAddress string `xml:"ipAddress"` + Weight int `xml:"weight,omitempty"` + MonitorPort int `xml:"monitorPort,omitempty"` + Port int `xml:"port"` + MaxConn int `xml:"maxConn,omitempty"` + MinConn int `xml:"minConn,omitempty"` + Condition string `xml:"condition,omitempty"` +} + +type LbPoolMembers []LbPoolMember + +// LbAppProfile represents a load balancer application profile as per "vCloud Director API for NSX +// Programming Guide" +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +type LbAppProfile struct { + XMLName xml.Name `xml:"applicationProfile"` + ID string `xml:"applicationProfileId,omitempty"` + Name string `xml:"name,omitempty"` + SslPassthrough bool `xml:"sslPassthrough"` + Template string `xml:"template,omitempty"` + HttpRedirect *LbAppProfileHttpRedirect `xml:"httpRedirect,omitempty"` + Persistence *LbAppProfilePersistence `xml:"persistence,omitempty"` + InsertXForwardedForHttpHeader bool `xml:"insertXForwardedFor"` + ServerSslEnabled bool `xml:"serverSslEnabled"` +} + +type LbAppProfiles []LbAppProfile + +// LbAppProfilePersistence defines persistence profile settings in LbAppProfile +type LbAppProfilePersistence struct { + XMLName xml.Name `xml:"persistence"` + Method string `xml:"method,omitempty"` + CookieName string `xml:"cookieName,omitempty"` + CookieMode string `xml:"cookieMode,omitempty"` + Expire int `xml:"expire,omitempty"` +} + +// LbAppProfileHttpRedirect defines http redirect settings in LbAppProfile +type LbAppProfileHttpRedirect struct { + XMLName xml.Name `xml:"httpRedirect"` + To string `xml:"to,omitempty"` +} + +// LbAppRule represents a load balancer application rule as per "vCloud Director API for NSX +// Programming Guide" +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +type LbAppRule struct { + XMLName xml.Name `xml:"applicationRule"` + ID string `xml:"applicationRuleId,omitempty"` + Name string `xml:"name,omitempty"` + Script string `xml:"script,omitempty"` +} + +type LbAppRules []LbAppRule + +// LbVirtualServer represents a load balancer virtual server as per "vCloud Director API for NSX +// Programming Guide" +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +type LbVirtualServer struct { + XMLName xml.Name `xml:"virtualServer"` + ID string `xml:"virtualServerId,omitempty"` + Name string `xml:"name,omitempty"` + Description string `xml:"description,omitempty"` + Enabled bool `xml:"enabled"` + IpAddress string `xml:"ipAddress"` + Protocol string `xml:"protocol"` + Port int `xml:"port"` + AccelerationEnabled bool `xml:"accelerationEnabled"` + ConnectionLimit int `xml:"connectionLimit,omitempty"` + ConnectionRateLimit int `xml:"connectionRateLimit,omitempty"` + ApplicationProfileId string `xml:"applicationProfileId,omitempty"` + DefaultPoolId string `xml:"defaultPoolId,omitempty"` + ApplicationRuleIds []string `xml:"applicationRuleId,omitempty"` +} + +// EdgeNatRule contains shared structure for SNAT and DNAT rule configuration using +// NSX-V proxied edge gateway endpoint +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +type EdgeNatRule struct { + XMLName xml.Name `xml:"natRule"` + ID string `xml:"ruleId,omitempty"` + RuleType string `xml:"ruleType,omitempty"` + RuleTag string `xml:"ruleTag,omitempty"` + Action string `xml:"action"` + Vnic *int `xml:"vnic,omitempty"` + OriginalAddress string `xml:"originalAddress"` + TranslatedAddress string `xml:"translatedAddress"` + LoggingEnabled bool `xml:"loggingEnabled"` + Enabled bool `xml:"enabled"` + Description string `xml:"description,omitempty"` + Protocol string `xml:"protocol,omitempty"` + OriginalPort string `xml:"originalPort,omitempty"` + TranslatedPort string `xml:"translatedPort,omitempty"` + IcmpType string `xml:"icmpType,omitempty"` +} + +// EdgeFirewall holds data for creating firewall rule using proxied NSX-V API +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +type EdgeFirewallRule struct { + XMLName xml.Name `xml:"firewallRule" ` + ID string `xml:"id,omitempty"` + Name string `xml:"name,omitempty"` + RuleType string `xml:"ruleType,omitempty"` + RuleTag string `xml:"ruleTag,omitempty"` + Source EdgeFirewallEndpoint `xml:"source" ` + Destination EdgeFirewallEndpoint `xml:"destination"` + Application EdgeFirewallApplication `xml:"application"` + MatchTranslated *bool `xml:"matchTranslated,omitempty"` + Direction string `xml:"direction,omitempty"` + Action string `xml:"action,omitempty"` + Enabled bool `xml:"enabled"` + LoggingEnabled bool `xml:"loggingEnabled"` +} + +// EdgeFirewallEndpoint can contains slices of objects for source or destination in EdgeFirewall +type EdgeFirewallEndpoint struct { + Exclude bool `xml:"exclude"` + VnicGroupIds []string `xml:"vnicGroupId,omitempty"` + GroupingObjectIds []string `xml:"groupingObjectId,omitempty"` + IpAddresses []string `xml:"ipAddress,omitempty"` +} + +// EdgeFirewallApplication Wraps []EdgeFirewallApplicationService for multiple protocol/port specification +type EdgeFirewallApplication struct { + ID string `xml:"applicationId,omitempty"` + Services []EdgeFirewallApplicationService `xml:"service,omitempty"` +} + +// EdgeFirewallApplicationService defines port/protocol details for one service in EdgeFirewallRule +type EdgeFirewallApplicationService struct { + Protocol string `xml:"protocol,omitempty"` + Port string `xml:"port,omitempty"` + SourcePort string `xml:"sourcePort,omitempty"` +} + +// EdgeIpSet defines a group of IP addresses that you can add as the source or destination in a +// firewall rule or in DHCP relay configuration. The object itself has more fields in API response, +// however vCD UI only uses the below mentioned. It looks as if the other fields are used in NSX +// internally and are simply proxied back. +// +// Note. Only advanced edge gateways support IP sets +type EdgeIpSet struct { + XMLName xml.Name `xml:"ipset"` + // ID holds composite ID of IP set which is formatted as + // 'f9daf2da-b4f9-4921-a2f4-d77a943a381c:ipset-4' where the first segment before colon is vDC id + // and the second one is IP set ID + ID string `xml:"objectId,omitempty"` + // Name is mandatory and must be unique + Name string `xml:"name"` + // Description - optional + Description string `xml:"description,omitempty"` + // IPAddresses is a mandatory field with comma separated values. The API is known to re-order + // data after submiting and may shuffle components even if re-submitted as it was return from + // API itself + // (eg: "192.168.200.1,192.168.200.1/24,192.168.200.1-192.168.200.24") + IPAddresses string `xml:"value"` + // InheritanceAllowed defines visibility at underlying scopes + InheritanceAllowed *bool `xml:"inheritanceAllowed"` + // Revision is a "version" of IP set configuration. During read current revision is being + // returned and when update is performed this latest version must be sent as it validates if no + // updates ocurred in between. When not the latest version is being sent during update one can + // expect similar error response from API: "The object ipset-27 used in this operation has an + // older version 0 than the current system version 1. Refresh UI or fetch the latest copy of the + // object and retry operation." + Revision *int `xml:"revision,omitempty"` +} + +// EdgeIpSets is a slice of pointers to EdgeIpSet +type EdgeIpSets []*EdgeIpSet From 4ecf4d334df05d7d1872eba713b369e5da16d275 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Tue, 3 Dec 2019 16:57:51 +0200 Subject: [PATCH 09/80] Added computed structure for iternal disk, to be readable in state file Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vapp_vm.go | 773 ++++++++++++++++-------------------- 1 file changed, 347 insertions(+), 426 deletions(-) diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index b301db684..507c127c5 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -3,7 +3,6 @@ package vcd import ( "bytes" "fmt" - "github.com/davecgh/go-spew/spew" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "log" "net" @@ -17,320 +16,365 @@ import ( "github.com/vmware/go-vcloud-director/v2/types/v56" ) -var allSchema = map[string]*schema.Schema{ - "vapp_name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "The vApp this VM belongs to", - }, - "name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "A name for the VM, unique within the vApp", - }, - "computer_name": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "Computer name to assign to this virtual machine", - }, - "org": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Description: "The name of organization to use, optional if defined at provider " + - "level. Useful when connected as sysadmin working across different organizations", - }, - "vdc": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Description: "The name of VDC to use, optional if defined at provider level", - }, - "template_name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "The name of the vApp Template to use", - }, - "catalog_name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "The catalog name in which to find the given vApp Template", - }, - "description": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - Description: "The VM description", - // Note: description is read only, as we lack the needed fields to set it at creation. - // Currently, this field has the description of the OVA used to create the VM - }, - "memory": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - Description: "The amount of RAM (in MB) to allocate to the VM", - ValidateFunc: validateMultipleOf4(), - }, - "cpus": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - Default: 1, - Description: "The number of virtual CPUs to allocate to the VM", - }, - "cpu_cores": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - Default: 1, - Description: "The number of cores per socket", - }, - "ip": &schema.Schema{ - Computed: true, - ConflictsWith: []string{"network"}, - Deprecated: "In favor of network", - DiffSuppressFunc: suppressIfIPIsOneOf(), - ForceNew: true, - Optional: true, - Type: schema.TypeString, - }, - "mac": { - Computed: true, - ConflictsWith: []string{"network"}, - Deprecated: "In favor of network", - Optional: true, - Type: schema.TypeString, - }, - "initscript": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Description: "Script to run on initial boot or with customization.force=true set", - }, - "metadata": { - Type: schema.TypeMap, - Optional: true, - // For now underlying go-vcloud-director repo only supports - // a value of type String in this map. - Description: "Key value map of metadata to assign to this VM", - }, - "href": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "VM Hyper Reference", - }, - "accept_all_eulas": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: true, - Description: "Automatically accept EULA if OVA has it", - }, - "power_on": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: true, - Description: "A boolean value stating if this VM should be powered on", - }, - "storage_profile": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "Storage profile to override the default one", - }, - "network_href": &schema.Schema{ - ConflictsWith: []string{"network"}, - Deprecated: "In favor of network", - Type: schema.TypeString, - Optional: true, - }, - "network": { - ConflictsWith: []string{"ip", "network_name", "vapp_network_name", "network_href"}, - Optional: true, - Type: schema.TypeList, - Description: " A block to define network interface. Multiple can be used.", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "type": { - Required: true, - Type: schema.TypeString, - ValidateFunc: validation.StringInSlice([]string{"vapp", "org", "none"}, false), - Description: "Network type to use: 'vapp', 'org' or 'none'. Use 'vapp' for vApp network, 'org' to attach Org VDC network. 'none' for empty NIC.", - }, - "ip_allocation_mode": { - Optional: true, - Type: schema.TypeString, - ValidateFunc: validation.StringInSlice([]string{"POOL", "DHCP", "MANUAL", "NONE"}, false), - Description: "IP address allocation mode. One of POOL, DHCP, MANUAL, NONE", - }, - "name": { - ForceNew: false, - Optional: true, // In case of type = none it is not required - Type: schema.TypeString, - Description: "Name of the network this VM should connect to. Always required except for `type` `NONE`", - }, - "ip": { - Computed: true, - Optional: true, - Type: schema.TypeString, - ValidateFunc: checkEmptyOrSingleIP(), // Must accept empty string to ease using HCL interpolation - Description: "IP of the VM. Settings depend on `ip_allocation_mode`. Omitted or empty for DHCP, POOL, NONE. Required for MANUAL", - }, - "is_primary": { - Default: false, - Optional: true, - // By default if the value is omitted it will report schema change - // on every terraform operation. The below function - // suppresses such cases "" => "false" when applying. - DiffSuppressFunc: falseBoolSuppress(), - Type: schema.TypeBool, - Description: "Set to true if network interface should be primary. First network card in the list will be primary by default", - }, - "mac": { - Computed: true, - Optional: true, - Type: schema.TypeString, - Description: "Mac address of network interface", - }, - }, +func resourceVcdVAppVm() *schema.Resource { + + return &schema.Resource{ + Create: resourceVcdVAppVmCreate, + Update: resourceVcdVAppVmUpdate, + Read: resourceVcdVAppVmRead, + Delete: resourceVcdVAppVmDelete, + Importer: &schema.ResourceImporter{ + State: resourceVcdVappVmImport, }, - }, - "network_name": &schema.Schema{ - ConflictsWith: []string{"network"}, - Deprecated: "In favor of network", - ForceNew: true, - Optional: true, - Type: schema.TypeString, - }, - "vapp_network_name": &schema.Schema{ - ConflictsWith: []string{"network"}, - Deprecated: "In favor of network", - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - "disk": { - Type: schema.TypeSet, - Elem: &schema.Resource{Schema: map[string]*schema.Schema{ - "name": { + + Schema: map[string]*schema.Schema{ + "vapp_name": &schema.Schema{ Type: schema.TypeString, Required: true, - Description: "Independent disk name", + ForceNew: true, + Description: "The vApp this VM belongs to", }, - "bus_number": { + "name": &schema.Schema{ Type: schema.TypeString, Required: true, - Description: "Bus number on which to place the disk controller", + ForceNew: true, + Description: "A name for the VM, unique within the vApp", }, - "unit_number": { + "computer_name": &schema.Schema{ Type: schema.TypeString, - Required: true, - Description: "Unit number (slot) on the bus specified by BusNumber", + Optional: true, + Computed: true, + Description: "Computer name to assign to this virtual machine", }, - }}, - Optional: true, - Set: resourceVcdVmIndependentDiskHash, - }, - "override_template_disk": { - Type: schema.TypeSet, - Optional: true, - ForceNew: true, - Description: " A block to match internal_disk interface in template. Multiple can be used. Disk will be matched by bus_type, bus_number and unit_number.", - Elem: &schema.Resource{Schema: map[string]*schema.Schema{ - "bus_type": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{"ide", "parallel", "sas", "paravirtual", "sata"}, true), - Description: "The type of disk controller. Possible values: ide, parallel( LSI Logic Parallel SCSI), sas(LSI Logic SAS (SCSI)), paravirtual(Paravirtual (SCSI)), sata", + "org": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "The name of organization to use, optional if defined at provider " + + "level. Useful when connected as sysadmin working across different organizations", }, - "size_in_mb": { - Type: schema.TypeInt, + "vdc": { + Type: schema.TypeString, + Optional: true, ForceNew: true, - Required: true, - Description: "The size of the disk in MB.", + Description: "The name of VDC to use, optional if defined at provider level", }, - "bus_number": { - Type: schema.TypeInt, + "template_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, ForceNew: true, + Description: "The name of the vApp Template to use", + }, + "catalog_name": &schema.Schema{ + Type: schema.TypeString, Required: true, - Description: "The number of the SCSI or IDE controller itself.", + ForceNew: true, + Description: "The catalog name in which to find the given vApp Template", + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The VM description", + // Note: description is read only, as we lack the needed fields to set it at creation. + // Currently, this field has the description of the OVA used to create the VM }, - "unit_number": { + "memory": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Description: "The amount of RAM (in MB) to allocate to the VM", + ValidateFunc: validateMultipleOf4(), + }, + "cpus": &schema.Schema{ Type: schema.TypeInt, + Optional: true, + Default: 1, + Description: "The number of virtual CPUs to allocate to the VM", + }, + "cpu_cores": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 1, + Description: "The number of cores per socket", + }, + "ip": &schema.Schema{ + Computed: true, + ConflictsWith: []string{"network"}, + Deprecated: "In favor of network", + DiffSuppressFunc: suppressIfIPIsOneOf(), + ForceNew: true, + Optional: true, + Type: schema.TypeString, + }, + "mac": { + Computed: true, + ConflictsWith: []string{"network"}, + Deprecated: "In favor of network", + Optional: true, + Type: schema.TypeString, + }, + "initscript": &schema.Schema{ + Type: schema.TypeString, + Optional: true, ForceNew: true, - Required: true, - Description: "The device number on the SCSI or IDE controller of the disk.", + Description: "Script to run on initial boot or with customization.force=true set", }, - "thin_provisioned": { + "metadata": { + Type: schema.TypeMap, + Optional: true, + // For now underlying go-vcloud-director repo only supports + // a value of type String in this map. + Description: "Key value map of metadata to assign to this VM", + }, + "href": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "VM Hyper Reference", + }, + "accept_all_eulas": &schema.Schema{ Type: schema.TypeBool, - ForceNew: true, Optional: true, - Description: "Specifies whether the disk storage is pre-allocated or allocated on demand.", + Default: true, + Description: "Automatically accept EULA if OVA has it", }, - "iops": { - Type: schema.TypeInt, - ForceNew: true, + "power_on": &schema.Schema{ + Type: schema.TypeBool, Optional: true, - Description: "Specifies the IOPS for the disk. Default - 0.", + Default: true, + Description: "A boolean value stating if this VM should be powered on", }, "storage_profile": &schema.Schema{ Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Storage profile to override the default one", + }, + "network_href": &schema.Schema{ + ConflictsWith: []string{"network"}, + Deprecated: "In favor of network", + Type: schema.TypeString, + Optional: true, + }, + "network": { + ConflictsWith: []string{"ip", "network_name", "vapp_network_name", "network_href"}, + Optional: true, + Type: schema.TypeList, + Description: " A block to define network interface. Multiple can be used.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Required: true, + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"vapp", "org", "none"}, false), + Description: "Network type to use: 'vapp', 'org' or 'none'. Use 'vapp' for vApp network, 'org' to attach Org VDC network. 'none' for empty NIC.", + }, + "ip_allocation_mode": { + Optional: true, + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"POOL", "DHCP", "MANUAL", "NONE"}, false), + Description: "IP address allocation mode. One of POOL, DHCP, MANUAL, NONE", + }, + "name": { + ForceNew: false, + Optional: true, // In case of type = none it is not required + Type: schema.TypeString, + Description: "Name of the network this VM should connect to. Always required except for `type` `NONE`", + }, + "ip": { + Computed: true, + Optional: true, + Type: schema.TypeString, + ValidateFunc: checkEmptyOrSingleIP(), // Must accept empty string to ease using HCL interpolation + Description: "IP of the VM. Settings depend on `ip_allocation_mode`. Omitted or empty for DHCP, POOL, NONE. Required for MANUAL", + }, + "is_primary": { + Default: false, + Optional: true, + // By default if the value is omitted it will report schema change + // on every terraform operation. The below function + // suppresses such cases "" => "false" when applying. + DiffSuppressFunc: falseBoolSuppress(), + Type: schema.TypeBool, + Description: "Set to true if network interface should be primary. First network card in the list will be primary by default", + }, + "mac": { + Computed: true, + Optional: true, + Type: schema.TypeString, + Description: "Mac address of network interface", + }, + }, + }, + }, + "network_name": &schema.Schema{ + ConflictsWith: []string{"network"}, + Deprecated: "In favor of network", + ForceNew: true, + Optional: true, + Type: schema.TypeString, + }, + "vapp_network_name": &schema.Schema{ + ConflictsWith: []string{"network"}, + Deprecated: "In favor of network", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "disk": { + Type: schema.TypeSet, + Elem: &schema.Resource{Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "Independent disk name", + }, + "bus_number": { + Type: schema.TypeString, + Required: true, + Description: "Bus number on which to place the disk controller", + }, + "unit_number": { + Type: schema.TypeString, + Required: true, + Description: "Unit number (slot) on the bus specified by BusNumber", + }, + }}, + Optional: true, + Set: resourceVcdVmIndependentDiskHash, + }, + "override_template_disk": { + Type: schema.TypeSet, + Optional: true, ForceNew: true, + Description: " A block to match internal_disk interface in template. Multiple can be used. Disk will be matched by bus_type, bus_number and unit_number.", + Elem: &schema.Resource{Schema: map[string]*schema.Schema{ + "bus_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"ide", "parallel", "sas", "paravirtual", "sata"}, true), + Description: "The type of disk controller. Possible values: ide, parallel( LSI Logic Parallel SCSI), sas(LSI Logic SAS (SCSI)), paravirtual(Paravirtual (SCSI)), sata", + }, + "size_in_mb": { + Type: schema.TypeInt, + ForceNew: true, + Required: true, + Description: "The size of the disk in MB.", + }, + "bus_number": { + Type: schema.TypeInt, + ForceNew: true, + Required: true, + Description: "The number of the SCSI or IDE controller itself.", + }, + "unit_number": { + Type: schema.TypeInt, + ForceNew: true, + Required: true, + Description: "The device number on the SCSI or IDE controller of the disk.", + }, + "thin_provisioned": { + Type: schema.TypeBool, + ForceNew: true, + Optional: true, + Description: "Specifies whether the disk storage is pre-allocated or allocated on demand.", + }, + "iops": { + Type: schema.TypeInt, + ForceNew: true, + Optional: true, + Description: "Specifies the IOPS for the disk. Default - 0.", + }, + "storage_profile": &schema.Schema{ + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Description: "Storage profile to override the VM default one", + }, + }}, + }, + "internal_disk": { + Type: schema.TypeList, + Computed: true, + Description: " A block will show internal disk details", + Elem: &schema.Resource{Schema: map[string]*schema.Schema{ + "disk_id": { + Type: schema.TypeString, + Computed: true, + Description: "The disk ID.", + }, + "bus_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of disk controller. Possible values: ide, parallel( LSI Logic Parallel SCSI), sas(LSI Logic SAS (SCSI)), paravirtual(Paravirtual (SCSI)), sata", + }, + "size_in_mb": { + Type: schema.TypeInt, + Computed: true, + Description: "The size of the disk in MB.", + }, + "bus_number": { + Type: schema.TypeInt, + Computed: true, + Description: "The number of the SCSI or IDE controller itself.", + }, + "unit_number": { + Type: schema.TypeInt, + Computed: true, + Description: "The device number on the SCSI or IDE controller of the disk.", + }, + "thin_provisioned": { + Type: schema.TypeBool, + Computed: true, + Description: "Specifies whether the disk storage is pre-allocated or allocated on demand.", + }, + "iops": { + Type: schema.TypeInt, + Computed: true, + Description: "Specifies the IOPS for the disk. Default - 0.", + }, + "storage_profile": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "Storage profile to override the VM default one", + }, + }}, + }, + "expose_hardware_virtualization": &schema.Schema{ + Type: schema.TypeBool, Optional: true, - Description: "Storage profile to override the VM default one", + Default: false, + Description: "Expose hardware-assisted CPU virtualization to guest OS.", }, - }}, - }, - "expose_hardware_virtualization": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: false, - Description: "Expose hardware-assisted CPU virtualization to guest OS.", - }, - "guest_properties": { - Type: schema.TypeMap, - Optional: true, - Description: "Key/value settings for guest properties", - }, - "customization": &schema.Schema{ - Optional: true, - MinItems: 1, - MaxItems: 1, - Type: schema.TypeList, - Description: "Guest customization block", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "force": { - ValidateFunc: noopValueWarningValidator(true, - "Using 'true' value for field 'vcd_vapp_vm.customization.force' will reboot VM on every 'terraform apply' operation"), - Type: schema.TypeBool, - Optional: true, - Default: false, - // This settings is used as a 'flag' and it does not matter what is set in the - // state. If it is 'true' - then it means that 'update' procedure must set the - // VM for customization at next boot and reboot it. - DiffSuppressFunc: suppressFalse(), - Description: "'true' value will cause the VM to reboot on every 'apply' operation", + "guest_properties": { + Type: schema.TypeMap, + Optional: true, + Description: "Key/value settings for guest properties", + }, + "customization": &schema.Schema{ + Optional: true, + MinItems: 1, + MaxItems: 1, + Type: schema.TypeList, + Description: "Guest customization block", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "force": { + ValidateFunc: noopValueWarningValidator(true, + "Using 'true' value for field 'vcd_vapp_vm.customization.force' will reboot VM on every 'terraform apply' operation"), + Type: schema.TypeBool, + Optional: true, + Default: false, + // This settings is used as a 'flag' and it does not matter what is set in the + // state. If it is 'true' - then it means that 'update' procedure must set the + // VM for customization at next boot and reboot it. + DiffSuppressFunc: suppressFalse(), + Description: "'true' value will cause the VM to reboot on every 'apply' operation", + }, + }, }, }, }, - }, -} - -func resourceVcdVAppVm() *schema.Resource { - - return &schema.Resource{ - Create: resourceVcdVAppVmCreate, - Update: resourceVcdVAppVmUpdate, - Read: resourceVcdVAppVmRead, - Delete: resourceVcdVAppVmDelete, - Importer: &schema.ResourceImporter{ - State: resourceVcdVappVmImport, - }, - - Schema: allSchema, } } @@ -472,19 +516,19 @@ func resourceVcdVAppVmCreate(d *schema.ResourceData, meta interface{}) error { d.SetId(vm.VM.ID) - // Add disks new ones or/and update created in template - err = addInternalDisks(d, meta, *vm) + // update existing internal disks in template + err = updateTemplateInternalDisks(d, meta, *vm) if err != nil { d.Set("override_template_disk", nil) return fmt.Errorf("error managing internal disks : %s", err) - } - /* else { + } else { + // add details of internal disk to state errReadInternalDisk := updateStateOfInternalDisks(d, *vm) if errReadInternalDisk != nil { d.Set("internal_disk", nil) - return fmt.Errorf("error reading interal disks : %s", errReadInternalDisk) + log.Printf("error reading interal disks : %s", errReadInternalDisk) } - }*/ + } // TODO do not trigger resourceVcdVAppVmUpdate from create. These must be separate actions. err = resourceVcdVAppVmUpdateExecute(d, meta, true) @@ -1082,11 +1126,11 @@ func genericVcdVAppVmRead(d *schema.ResourceData, meta interface{}, origin strin return fmt.Errorf("[VM read] unable to set guest properties in state: %s", err) } - /* err = updateStateOfInternalDisks(d, *vm) - if err != nil { - d.Set("internal_disk", nil) - return fmt.Errorf("[VM read] error reading internal disks : %s", err) - }*/ + err = updateStateOfInternalDisks(d, *vm) + if err != nil { + d.Set("internal_disk", nil) + return fmt.Errorf("[VM read] error reading internal disks : %s", err) + } err = updateStateOfAttachedDisks(d, *vm, vdc) if err != nil { @@ -1128,60 +1172,31 @@ func updateStateOfAttachedDisks(d *schema.ResourceData, vm govcd.VM, vdc *govcd. } func updateStateOfInternalDisks(d *schema.ResourceData, vm govcd.VM) error { - err := vm.Refresh() if err != nil { return err } existingInternalDisks := vm.VM.VmSpecSection.DiskSection.DiskSettings - //transformed := schema.NewSet(resourceVcdVmInternalDiskHash, []interface{}{}) - transformed := schema.NewSet(schema.HashResource(allSchema["internal_disk"].Elem.(*schema.Resource)), []interface{}{}) - - str2 := spew.Sdump(existingInternalDisks) - log.Printf("####UpdateState: %s", str2) - - str3 := spew.Sdump(d.State()) - log.Printf("####State-updateStateOfInternalDisks1: %s", str3) - log.Printf("####State-updateStateOfInternalDisks2: %#v", d.State().Attributes) - + var internalDiskList []map[string]interface{} for _, internalDisk := range existingInternalDisks { - - busType, err := strconv.Atoi(internalDisk.AdapterType) - if err != nil { - return err - } - - newValues := map[string]interface{}{ - "id": internalDisk.DiskId, - "bus_type": busType, + newValue := map[string]interface{}{ + "disk_id": internalDisk.DiskId, + "bus_type": internalDiskBusTypesFromValues[internalDisk.AdapterType], "size_in_mb": int(internalDisk.SizeMb), "bus_number": internalDisk.BusNumber, "unit_number": internalDisk.UnitNumber, "iops": int(*internalDisk.Iops), - "thin_provisioned": strconv.FormatBool(*internalDisk.ThinProvisioned), + "thin_provisioned": *internalDisk.ThinProvisioned, "storage_profile": internalDisk.StorageProfile.Name, } - - //internalDiskProvidedConfig := internalDisk.(map[string]interface{}) - /* a1, a2 := d.GetOk("storage_profile") - util.Logger.Printf("##################3 %#v, %#v", a1, a2) - if value, ok := d.GetOk("storage_profile"); ok { - newValues["storage_profile"] = value - } - a1, a2 = d.GetOk("thin_provisioned") - util.Logger.Printf("##################3 %#v, %#v", a1, a2) - if value, ok := d.GetOk("thin_provisioned"); ok { - newValues["thin_provisioned"] = value - } - */ - transformed.Add(newValues) + internalDiskList = append(internalDiskList, newValue) } - return d.Set("internal_disk", transformed) + return d.Set("internal_disk", internalDiskList) } -func addInternalDisks(d *schema.ResourceData, meta interface{}, vm govcd.VM) error { +func updateTemplateInternalDisks(d *schema.ResourceData, meta interface{}, vm govcd.VM) error { vcdClient := meta.(*VCDClient) _, vdc, err := vcdClient.GetOrgAndVdcFromResource(d) @@ -1248,82 +1263,6 @@ func addInternalDisks(d *schema.ResourceData, meta interface{}, vm govcd.VM) err return nil } -func updateInternalDisks(d *schema.ResourceData, meta interface{}, vm govcd.VM) error { - vcdClient := meta.(*VCDClient) - - _, vdc, err := vcdClient.GetOrgAndVdcFromResource(d) - if err != nil { - return fmt.Errorf(errorRetrievingOrgAndVdc, err) - } - - var diskSettings []*types.DiskSettings - vm.VM.VmSpecSection.DiskSection = &types.DiskSection{} - diskSettings = []*types.DiskSettings{} - - var storageProfilePrt *types.Reference - var overrideVmDefault bool - - internalDisksList := d.Get("internal_disk").(*schema.Set).List() - str2 := spew.Sdump(internalDisksList) - log.Printf("####Update: %s", str2) - - str3 := spew.Sdump(d.State()) - log.Printf("####StateInUpdate: %s", str3) - log.Printf("####StateInUpdate: %#v", d.State().Attributes) - for _, internalDisk := range internalDisksList { - internalDiskProvidedConfig := internalDisk.(map[string]interface{}) - - storageProfileName := internalDiskProvidedConfig["storage_profile"].(string) - if storageProfileName != "" { - storageProfile, err := vdc.FindStorageProfileReference(storageProfileName) - if err != nil { - return fmt.Errorf("[vm creation] error retrieving storage profile %s : %s", storageProfileName, err) - } - storageProfilePrt = &storageProfile - overrideVmDefault = true - } else { - storageProfilePrt = vm.VM.StorageProfile - overrideVmDefault = false - } - - iops := int64(internalDiskProvidedConfig["iops"].(int)) - var thinProvisionedPrt *bool - thinProvisionedValue := internalDiskProvidedConfig["thin_provisioned"].(string) - log.Printf("#########44: %#v", thinProvisionedValue) - if thinProvisionedValue != "" { - thinProvisioned, err := strconv.ParseBool(thinProvisionedValue) - if err != nil { - return fmt.Errorf("error converting string to bool when updating disk for VM: %s", err) - } - thinProvisionedPrt = &thinProvisioned - } else { - thinProvisionedPrt = nil - } - - diskSettings = append(diskSettings, &types.DiskSettings{ - DiskId: internalDiskProvidedConfig["id"].(string), - SizeMb: int64(internalDiskProvidedConfig["size_in_mb"].(int)), - UnitNumber: internalDiskProvidedConfig["unit_number"].(int), - BusNumber: internalDiskProvidedConfig["bus_number"].(int), - AdapterType: strconv.Itoa(internalDiskProvidedConfig["bus_type"].(int)), - ThinProvisioned: thinProvisionedPrt, - StorageProfile: storageProfilePrt, - Iops: &iops, - VirtualQuantityUnit: "byte", - OverrideVmDefault: overrideVmDefault, - }) - } - - vmSpecSection := vm.VM.VmSpecSection - vmSpecSection.DiskSection.DiskSettings = diskSettings - _, err = vm.UpdateDisks(vmSpecSection) - if err != nil { - return fmt.Errorf("error updating VM disks: %s", err) - } - - return nil -} - func getMatchedDisk(internalDiskProvidedConfig map[string]interface{}, diskSettings []*types.DiskSettings) *types.DiskSettings { for _, diskSetting := range diskSettings { if diskSetting.AdapterType == internalDiskBusTypes[internalDiskProvidedConfig["bus_type"].(string)] && @@ -1425,24 +1364,6 @@ func resourceVcdVmIndependentDiskHash(v interface{}) int { return hashcode.String(buf.String()) } -func resourceVcdVmInternalDiskHash(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - //buf.WriteString(fmt.Sprintf("%s-", m["id"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["bus_type"].(int))) - buf.WriteString(fmt.Sprintf("%s-", m["bus_number"].(int))) - buf.WriteString(fmt.Sprintf("%s-", m["unit_number"].(int))) - buf.WriteString(fmt.Sprintf("%s-", m["storage_profile"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["thin_provisioned"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["iops"].(int))) - buf.WriteString(fmt.Sprintf("%s-", m["size_in_mb"].(int))) - // We use the name and no other identifier to calculate the hash - // With the VM resource, we assume that disks have a unique name. - // In the event that this is not true, we return an error - - return hashcode.String(buf.String()) -} - // networksToConfig converts terraform schema for 'networks' and converts to types.NetworkConnectionSection // which is used for creating new VM func networksToConfig(networks []interface{}, vdc *govcd.Vdc, vapp govcd.VApp, vcdClient *VCDClient) (types.NetworkConnectionSection, error) { From b53ff76827ac2bad9d86545e344d6dffcbf93768 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 4 Dec 2019 08:50:37 +0200 Subject: [PATCH 10/80] Small improvements Signed-off-by: Vaidotas Bauzys --- go.mod | 1 - vcd/resource_vcd_vapp_vm.go | 2 +- vcd/resource_vcd_vm_internal_disk.go | 6 +++--- .../vmware/go-vcloud-director/v2/types/v56/types.go | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 15b5c611b..9617e4ce8 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/terraform-providers/terraform-provider-vcd/v2 go 1.13 require ( - github.com/davecgh/go-spew v1.1.1 github.com/hashicorp/go-version v1.2.0 github.com/hashicorp/terraform-plugin-sdk v1.3.0 github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index 507c127c5..a134dbafc 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -1233,7 +1233,7 @@ func updateTemplateInternalDisks(d *schema.ResourceData, meta interface{}, vm go } if diskCreatedByTemplate == nil { - return fmt.Errorf("[vm creation] disk with bus type %d, bust number %d and unit number %d not found", + return fmt.Errorf("[vm creation] disk with bus type %s, bust number %d and unit number %d not found", internalDiskProvidedConfig["bus_type"].(string), internalDiskProvidedConfig["bus_number"].(int), internalDiskProvidedConfig["unit_number"].(int)) } diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index cd283c168..bf69cf6d5 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -55,8 +55,8 @@ func resourceVmInternalDisk() *schema.Resource { "allow_power_off_on": { Type: schema.TypeBool, Optional: true, - Computed: true, - Description: "Specifies whether the disk storage is pre-allocated or allocated on demand.", + Default: false, + Description: "Specifies whether VM off/on allowed adding or changing internal disk.", }, "bus_type": { Type: schema.TypeString, @@ -263,7 +263,7 @@ func resourceVmInternalDiskUpdate(d *schema.ResourceData, meta interface{}) erro return nil } -// Retrieves an Org resource from vCD +// Retrieves internal disk from VM and updates terraform state func resourceVmInternalDiskRead(d *schema.ResourceData, m interface{}) error { vcdClient := m.(*VCDClient) diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go index 7a289bf2c..bc528bc95 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go @@ -1439,8 +1439,8 @@ type MediaSettings struct { // CpuResourceMhz from VM/VmSpecSection struct type CpuResourceMhz struct { Configured int64 `xml:"Configured` // The amount of resource configured on the virtual machine. - Limit *int64 `xml:"Limit,omitempty"` // The limit for how much of this resource can be consumed on the underlying virtualization infrastructure. This is only valid when the resource allocation is not unlimited. Reservation *int64 `xml:"Reservation,omitempty"` // The amount of reservation of this resource on the underlying virtualization infrastructure. + Limit *int64 `xml:"Limit,omitempty"` // The limit for how much of this resource can be consumed on the underlying virtualization infrastructure. This is only valid when the resource allocation is not unlimited. SharesLevel string `xml:"SharesLevel,omitempty"` // Pre-determined relative priorities according to which the non-reserved portion of this resource is made available to the virtualized workload. Shares *int `xml:"Shares,omitempty"` // Custom priority for the resource. This is a read-only, unless the share level is CUSTOM. } @@ -1448,8 +1448,8 @@ type CpuResourceMhz struct { // MemoryResourceMb from VM/VmSpecSection struct type MemoryResourceMb struct { Configured int64 `xml:"Configured` // The amount of resource configured on the virtual machine. - Limit *int64 `xml:"Limit,omitempty"` // The limit for how much of this resource can be consumed on the underlying virtualization infrastructure. This is only valid when the resource allocation is not unlimited. Reservation *int64 `xml:"Reservation,omitempty"` // The amount of reservation of this resource on the underlying virtualization infrastructure. + Limit *int64 `xml:"Limit,omitempty"` // The limit for how much of this resource can be consumed on the underlying virtualization infrastructure. This is only valid when the resource allocation is not unlimited. SharesLevel string `xml:"SharesLevel,omitempty"` // Pre-determined relative priorities according to which the non-reserved portion of this resource is made available to the virtualized workload. Shares *int `xml:"Shares,omitempty"` // Custom priority for the resource. This is a read-only, unless the share level is CUSTOM. } From 5da5f4232391e89f2e7d99b36ac67e3d8527918d Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 4 Dec 2019 13:46:00 +0200 Subject: [PATCH 11/80] Added vm reboot support Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk.go | 82 ++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index bf69cf6d5..69a5ee8d0 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -52,11 +52,11 @@ func resourceVmInternalDisk() *schema.Resource { ForceNew: true, Description: "Storage profile to override the VM default one", }, - "allow_power_off_on": { + "allow_vm_reboot": { Type: schema.TypeBool, Optional: true, Default: false, - Description: "Specifies whether VM off/on allowed adding or changing internal disk.", + Description: "Specifies whether reboot allowed for adding or changing internal disk.", }, "bus_type": { Type: schema.TypeString, @@ -160,16 +160,70 @@ func resourceVmInternalDiskCreate(d *schema.ResourceData, meta interface{}) erro OverrideVmDefault: overrideVmDefault, } - diskId, err := vm.AddDisk(diskSetting) + err = powerOffIfNeeded(d, vm) if err != nil { return err - fmt.Errorf("error updating VM disks: %s", err) + } + + diskId, err := vm.AddDisk(diskSetting) + if err != nil { + return fmt.Errorf("error updating VM disks: %s", err) } d.SetId(diskId) + + err = powerOnIfNeeded(d, vm) + if err != nil { + return err + } + return resourceVmInternalDiskRead(d, meta) } +func powerOnIfNeeded(d *schema.ResourceData, vm *govcd.VM) error { + vmStatus, err := vm.GetStatus() + if err != nil { + return fmt.Errorf("error getting VM status before ensuring it is powered on: %s", err) + } + + if (vmStatus != "POWERED_ON" && d.Get("bus_type").(string) == "ide" && d.Get("allow_vm_reboot").(bool) == true) || + (vmStatus != "POWERED_ON" && d.Get("allow_vm_reboot").(bool) == true && (d.HasChange("bus_number") || d.HasChange("unit_number"))) { + log.Printf("[DEBUG] Powering on VM %s after adding internal disk.", vm.VM.Name) + + task, err := vm.PowerOn() + if err != nil { + return fmt.Errorf("error powering on VM for adding/updating internal disk: %s", err) + } + err = task.WaitTaskCompletion() + if err != nil { + return fmt.Errorf(errorCompletingTask, err) + } + } + return nil +} + +func powerOffIfNeeded(d *schema.ResourceData, vm *govcd.VM) error { + vmStatus, err := vm.GetStatus() + if err != nil { + return fmt.Errorf("error getting VM status before ensuring it is powered on: %s", err) + } + + if (vmStatus != "POWERED_OFF" && d.Get("bus_type").(string) == "ide" && d.Get("allow_vm_reboot").(bool) == true) || + (vmStatus != "POWERED_OFF" && d.Get("allow_vm_reboot").(bool) == true && (d.HasChange("bus_number") || d.HasChange("unit_number"))) { + log.Printf("[DEBUG] Powering off VM %s for adding/updating internal disk.", vm.VM.Name) + + task, err := vm.PowerOff() + if err != nil { + return fmt.Errorf("error powering off VM for adding internal disk: %s", err) + } + err = task.WaitTaskCompletion() + if err != nil { + return fmt.Errorf(errorCompletingTask, err) + } + } + return nil +} + // Deletes disk from VM func resourceVmInternalDiskDelete(d *schema.ResourceData, m interface{}) error { vcdClient := m.(*VCDClient) @@ -182,11 +236,21 @@ func resourceVmInternalDiskDelete(d *schema.ResourceData, m interface{}) error { return err } + err = powerOffIfNeeded(d, vm) + if err != nil { + return err + } + err = vm.DeleteDisk(d.Id()) if err != nil { return fmt.Errorf("[Error] failed to delete internal disk: %s", err) } + err = powerOnIfNeeded(d, vm) + if err != nil { + return err + } + log.Printf("[TRACE] VM internal disk %s deleted", d.Id()) d.SetId("") return nil @@ -254,11 +318,21 @@ func resourceVmInternalDiskUpdate(d *schema.ResourceData, meta interface{}) erro diskSettingsToUpdate.StorageProfile = storageProfilePrt diskSettingsToUpdate.OverrideVmDefault = overrideVmDefault + err = powerOffIfNeeded(d, vm) + if err != nil { + return err + } + _, err = vm.UpdateDisks(vm.VM.VmSpecSection) if err != nil { return err } + err = powerOnIfNeeded(d, vm) + if err != nil { + return err + } + log.Printf("[TRACE] Inernal Disk %s updated", d.Id()) return nil } From f611a0206eead37b04861cfefcdb1097b389e4ee Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 4 Dec 2019 15:18:29 +0200 Subject: [PATCH 12/80] Improve docs Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index 69a5ee8d0..09af66aad 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -56,7 +56,7 @@ func resourceVmInternalDisk() *schema.Resource { Type: schema.TypeBool, Optional: true, Default: false, - Description: "Specifies whether reboot allowed for adding or changing internal disk.", + Description: "Powers off VM when changing any attribute of an IDE disk or unit/bus number of other disk types, after the change is complete VM is powered back on. Without this setting enabled, such changes on a powered on VM would fail.", }, "bus_type": { Type: schema.TypeString, From 3d00b87f49cbf413618f8e840cda05053042df56 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 4 Dec 2019 17:00:30 +0200 Subject: [PATCH 13/80] Added test for vm Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vapp_vm_test.go | 91 ++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 3 deletions(-) diff --git a/vcd/resource_vcd_vapp_vm_test.go b/vcd/resource_vcd_vapp_vm_test.go index df60d2d43..a2e207ee9 100644 --- a/vcd/resource_vcd_vapp_vm_test.go +++ b/vcd/resource_vcd_vapp_vm_test.go @@ -3,6 +3,8 @@ package vcd import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/terraform" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" @@ -17,6 +19,7 @@ func TestAccVcdVAppVm_Basic(t *testing.T) { var vm govcd.VM var diskResourceName = "TestAccVcdVAppVm_Basic_1" var diskName = "TestAccVcdIndependentDiskBasic" + var internalDiskSize = 20000 var params = StringMap{ "Org": testConfig.VCD.Org, @@ -35,6 +38,7 @@ func TestAccVcdVAppVm_Basic(t *testing.T) { "storageProfileName": "*", "diskResourceName": diskResourceName, "Tags": "vapp vm", + "InternalDiskSize": internalDiskSize, } configText := templateFill(testAccCheckVcdVAppVm_basic, params) @@ -64,6 +68,7 @@ func TestAccVcdVAppVm_Basic(t *testing.T) { resource.TestCheckResourceAttr( "vcd_vapp_vm."+vmName, "metadata.vm_metadata", "VM Metadata."), resource.TestCheckOutput("disk", diskName), + testCheckInternalDiskNonStringOutputs(internalDiskSize), ), }, resource.TestStep{ @@ -73,12 +78,48 @@ func TestAccVcdVAppVm_Basic(t *testing.T) { ImportStateIdFunc: importStateIdVappObject(testConfig, vappName2, vmName), // These fields can't be retrieved from user data ImportStateVerifyIgnore: []string{"template_name", "catalog_name", "network_name", - "initscript", "accept_all_eulas", "power_on", "computer_name"}, + "initscript", "accept_all_eulas", "power_on", "computer_name", "override_template_disk"}, }, }, }) } +func testCheckInternalDiskNonStringOutputs(internalDiskSize int) resource.TestCheckFunc { + return func(s *terraform.State) error { + outputs := s.RootModule().Outputs + + if outputs["internal_disk_size"].Value != internalDiskSize { + return fmt.Errorf("internal disk size value didn't match") + } + + if outputs["internal_disk_iops"].Value != 0 { + return fmt.Errorf("internal disk iops value didn't match") + } + + if outputs["internal_disk_bus_type"].Value != "paravirtual" { + return fmt.Errorf("internal disk bus type value didn't match") + } + + if outputs["internal_disk_bus_number"].Value != 0 { + return fmt.Errorf("internal disk bus number value didn't match") + } + + if outputs["internal_disk_unit_number"].Value != 0 { + return fmt.Errorf("internal disk unit number value didn't match") + } + + if outputs["internal_disk_thin_provisioned"].Value != true { + return fmt.Errorf("internal disk thin provisioned value didn't match") + } + + if outputs["internal_disk_storage_profile"].Value != "*" { + return fmt.Errorf("internal disk storage profile value didn't match") + } + + return nil + } +} + func TestAccVcdVAppVm_Clone(t *testing.T) { var vapp govcd.VApp var vm govcd.VM @@ -199,7 +240,17 @@ resource "vcd_vapp_vm" "{{.VmName}}" { metadata = { vm_metadata = "VM Metadata." } - + + override_template_disk { + bus_type = "paravirtual" + size_in_mb = "{{.InternalDiskSize}}" + bus_number = 0 + unit_number = 0 + iops = 0 + thin_provisioned = true + storage_profile = "{{.storageProfileName}}" + } + network { name = vcd_network_routed.{{.NetworkName}}.name ip = "10.10.102.161" @@ -217,6 +268,41 @@ resource "vcd_vapp_vm" "{{.VmName}}" { output "disk" { value = tolist(vcd_vapp_vm.{{.VmName}}.disk)[0].name } + +output "internal_disk_size" { + value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].size_in_mb + depends_on = [vcd_vapp_vm.{{.VmName}}] +} + +output "internal_disk_iops" { + value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].iops + depends_on = [vcd_vapp_vm.{{.VmName}}] +} + +output "internal_disk_bus_type" { + value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].bus_type + depends_on = [vcd_vapp_vm.{{.VmName}}] +} + +output "internal_disk_bus_number" { + value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].bus_number + depends_on = [vcd_vapp_vm.{{.VmName}}] +} + +output "internal_disk_unit_number" { + value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].unit_number + depends_on = [vcd_vapp_vm.{{.VmName}}] +} + +output "internal_disk_thin_provisioned" { + value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].thin_provisioned + depends_on = [vcd_vapp_vm.{{.VmName}}] +} + +output "internal_disk_storage_profile" { + value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].storage_profile + depends_on = [vcd_vapp_vm.{{.VmName}}] +} ` const testAccCheckVcdVAppVm_clone = ` @@ -287,5 +373,4 @@ resource "vcd_vapp_vm" "{{.VmName2}}" { } } - ` From 422b288f664aaebad20dd71e9af7e8dae8c28be7 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 5 Dec 2019 08:56:03 +0200 Subject: [PATCH 14/80] Added test for vm internal disk Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk_test.go | 97 +++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 vcd/resource_vcd_vm_internal_disk_test.go diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go new file mode 100644 index 000000000..7175979a2 --- /dev/null +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -0,0 +1,97 @@ +// +build vapp vm ALL functional + +package vcd + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/vmware/go-vcloud-director/v2/govcd" +) + +func TestAccVcdVmInternalDisk(t *testing.T) { + // This test requires access to the vCD before filling templates + // Thus it won't run in the short test + if vcdShortTest { + t.Skip(acceptanceTestsSkipped) + return + } + + vapp, err := getAvailableVapp() + if err != nil { + t.Skip("No suitable vApp found for this test") + return + } + var vm *govcd.VM + + if vapp.VApp.Children != nil && len(vapp.VApp.Children.VM) > 0 { + vm, err = vapp.GetVMById(vapp.VApp.Children.VM[0].ID, false) + if err != nil { + t.Skip(fmt.Sprintf("error retrieving VM %s", vapp.VApp.Children.VM[0].Name)) + return + } + } + if vm == nil { + t.Skip(fmt.Sprintf("No VM available in vApp %s", vapp.VApp.Name)) + return + } + + diskResourceName := "disk1" + diskSize := "13333" + busType := "sata" + busNumber := "1" + unitNumber := "0" + allowReboot := true + + var params = StringMap{ + "Org": testConfig.VCD.Org, + "VDC": testConfig.VCD.Vdc, + "VappName": vapp.VApp.Name, + "VmName": vm.VM.Name, + "FuncName": "TestVappVmDS", + "Tags": "vm", + "DiskResourceName": diskResourceName, + "Size": diskSize, + "BusType": busType, + "BusNumber": busNumber, + "UnitNumber": unitNumber, + "StorageProfileName": testConfig.VCD.ProviderVdc.StorageProfile, + "AllowReboot": allowReboot, + } + configText := templateFill(sourceTestVmInternalDisk, params) + debugPrintf("#[DEBUG] CONFIGURATION: %s", configText) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: configText, + Check: resource.ComposeTestCheckFunc(resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "size_in_mb", diskSize), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "bus_type", busType), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "bus_number", busNumber), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "unit_number", unitNumber), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "storage_profile", testConfig.VCD.ProviderVdc.StorageProfile), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "allow_vm_reboot", strconv.FormatBool(allowReboot)), + ), + }, + }, + }) +} + +const sourceTestVmInternalDisk = ` +resource "vcd_vm_internal_disk" "{{.DiskResourceName}}" { + org = "{{.Org}}" + vdc = "{{.VDC}}" + vapp_name = "{{.VappName}}" + vm_name = "{{.VmName}}" + bus_type = "{{.BusType}}" + size_in_mb = "{{.Size}}" + bus_number = "{{.BusNumber}}" + unit_number = "{{.UnitNumber}}" + storage_profile = "{{.StorageProfileName}}" + allow_vm_reboot = "{{.AllowReboot}}" +} +` From cde0d91aa9a3cd5a0b6734c713e2fa485fd7d485 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 5 Dec 2019 11:36:30 +0200 Subject: [PATCH 15/80] Add latest changes from govcd Signed-off-by: Vaidotas Bauzys --- go.mod | 2 +- go.sum | 2 + vcd/resource_vcd_vapp_vm.go | 2 +- vcd/resource_vcd_vm_internal_disk.go | 12 +- .../v2/govcd/govcd_test_config.yaml | 149 ------------------ .../vmware/go-vcloud-director/v2/govcd/vm.go | 66 ++++++-- .../go-vcloud-director/v2/types/v56/types.go | 12 -- vendor/modules.txt | 2 +- 8 files changed, 65 insertions(+), 182 deletions(-) delete mode 100644 vendor/github.com/vmware/go-vcloud-director/v2/govcd/govcd_test_config.yaml diff --git a/go.mod b/go.mod index 9617e4ce8..0fce11662 100644 --- a/go.mod +++ b/go.mod @@ -8,4 +8,4 @@ require ( github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 ) -replace github.com/vmware/go-vcloud-director/v2 => ../go-vcloud-director +replace github.com/vmware/go-vcloud-director/v2 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205092948-006b5b528722 diff --git a/go.sum b/go.sum index 47b6cb9d1..57972a532 100644 --- a/go.sum +++ b/go.sum @@ -197,6 +197,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205092948-006b5b528722 h1:tDbMYaO0kQcSbAG4sVOqcg4qwgyO19CE5nWdql6t+4I= +github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205092948-006b5b528722/go.mod h1:zjondbeyTfZlzhwxOzyF4K2sWWYgMEv5H91dp5dPbU8= github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU= diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index a134dbafc..a8553b870 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -1255,7 +1255,7 @@ func updateTemplateInternalDisks(d *schema.ResourceData, meta interface{}, vm go vmSpecSection := vm.VM.VmSpecSection vmSpecSection.DiskSection.DiskSettings = diskSettings - _, err = vm.UpdateDisks(vmSpecSection) + _, err = vm.UpdateInternalDisks(vmSpecSection) if err != nil { return fmt.Errorf("error updating VM disks: %s", err) } diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index 09af66aad..45890705e 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -165,7 +165,7 @@ func resourceVmInternalDiskCreate(d *schema.ResourceData, meta interface{}) erro return err } - diskId, err := vm.AddDisk(diskSetting) + diskId, err := vm.AddInternalDisk(diskSetting) if err != nil { return fmt.Errorf("error updating VM disks: %s", err) } @@ -241,7 +241,7 @@ func resourceVmInternalDiskDelete(d *schema.ResourceData, m interface{}) error { return err } - err = vm.DeleteDisk(d.Id()) + err = vm.DeleteInternalDisk(d.Id()) if err != nil { return fmt.Errorf("[Error] failed to delete internal disk: %s", err) } @@ -285,7 +285,7 @@ func resourceVmInternalDiskUpdate(d *schema.ResourceData, meta interface{}) erro return err } - diskSettingsToUpdate, err := vm.GetDisk(d.Id()) + diskSettingsToUpdate, err := vm.GetInternalDisk(d.Id()) if err != nil { return err } @@ -323,7 +323,7 @@ func resourceVmInternalDiskUpdate(d *schema.ResourceData, meta interface{}) erro return err } - _, err = vm.UpdateDisks(vm.VM.VmSpecSection) + _, err = vm.UpdateInternalDisks(vm.VM.VmSpecSection) if err != nil { return err } @@ -346,7 +346,7 @@ func resourceVmInternalDiskRead(d *schema.ResourceData, m interface{}) error { return err } - diskSettings, err := vm.GetDisk(d.Id()) + diskSettings, err := vm.GetInternalDisk(d.Id()) if err == govcd.ErrorEntityNotFound { log.Printf("[DEBUG] Unable to find disk with Id: %s. Removing from tfstate", d.Id()) d.SetId("") @@ -462,7 +462,7 @@ func getInternalDiskForImport(d *schema.ResourceData, meta interface{}, orgName, return nil, fmt.Errorf("[Error] failed to get VM: %s", err) } - disk, err := vm.GetDisk(diskId) + disk, err := vm.GetInternalDisk(diskId) if err != nil { return []*schema.ResourceData{}, fmt.Errorf("unable to find internal disk with id %s: %s", d.Id(), err) diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/govcd_test_config.yaml b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/govcd_test_config.yaml deleted file mode 100644 index a31d3ab7b..000000000 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/govcd_test_config.yaml +++ /dev/null @@ -1,149 +0,0 @@ -# COPY THIS FILE to govcd_test_config.yaml -# in the same directory and change the values -# to match your environment. -# -# All items in this file must exist already -# (They will not be removed or left altered) -# The test will create a vApp and remove it at the end -# -provider: - # vCD administrator credentials - # (Providing org credentials will skip some tests) - user: administrator - #user: datacloud-admin - #password: ca$hc0w - password: Darbas34` - #password: CHANGE-ME - #user: vbauzys - #password: Admin!23 - - # - # The vCD address, in the format https://vCD_IP/api - # or https://vCD_host_name/api - #mine vcd 10 - #url: https://bos1-vcloud-static-170-20.eng.vmware.com/api - #url: https://bos1-vcloud-static-170-30.eng.vmware.com/api - url: https://192.168.1.109/api - - # - # The organization you are authenticating with - sysOrg: System - #sysOrg: datacloud - maxRetryTimeout: 60 -vcd: - # Name of the organization (mandatory) - #org: datacloud - org: my1 - # - # The virtual data center (mandatory) - # The tests will create a vApp here - # - #vdc: manoVdc - vdc: vbVdc - #vdc: vdc-datacloud - # An Org catalog, possibly containing at least one item - # Provider VDC; if omitted, some tests will be skipped - provider_vdc: - name: providerVbVdc - storage_profile: Development - network_pool: providerVbVdc-VXLAN-NP - catalog: - name: testCatalog - # - # One item in the catalog. It will be used to compose test vApps - catalogItem: photon-hw11 - # - # An optional description for the catalog. Its test will be skipped if omitted. - # If provided, it must be the current description of the catalog - description: Fresh OS templates - # - # An optional description for the catalog item - catalogItemDescription: wow some huge - # - network: - # First vdc network (mandatory) - network1: "myOrgNet1" - # Second vdc network. If omitted, some tests will be skipped. - #network2: "" - # Storage profiles used in the vDC - # One or two can be listed - # - # Storage profiles used in the vDC - # One or two can be listed - storageProfile: - # First storage profile (mandatory) - storageProfile1: Development - # Second storage profile. If omitted, some tests will be skipped. - storageProfile2: "*" - # An edge gateway - # (see https://pubs.vmware.com/vca/topic/com.vmware.vcloud.api.doc_56/GUID-18B0FB8B-385C-4B6D-982C-4B24D271C646.html) - edgeGateway: vb_edge2 - # - # The IP of the gateway (must exist) - externalIp: 192.168.1.10 - externalNetmask: 255.255.255.0 - # - # A free IP in the Org vDC network - internalIp: 192.168.2.10 - internalNetmask: 255.255.255.0 - # An external Network name - externalNetwork: vb_externalNet2 - # - # A port group name for creating an external network - externalNetworkPortGroup: ForTestingPG - # - # A port group type for creating an external network. Can be DV_PORTGROUP or NETWORK - externalNetworkPortGroupType: DV_PORTGROUP - # - # A vSphere server name for creating an external network - vimServer: vC1 - # - # Independent disk parameters for testing - disk: - # - # Disk size (bytes) for create disk, skip disk tests if it is less than or equal to 0 - size: 2048576 - # - # Disk size (bytes) for update disk, skip some disk tests if it is less than or equal to 0 - sizeForUpdate: 3048576 -logging: - # All items in this section are optional - # Logging is disabled by default. - # See ./util/LOGGING.md for more info - # - # Enables or disables logs - enabled: true - verboseCleanup: true - # - # changes the log name - logFileName: "go-vcloud-director2.log" - # - # Defines whether we log the requests in HTTP operations - logHttpRequests: true - # - # Defines whether we log the responses in HTTP operations - logHttpResponses: true - # - # Comma-separated list of XML tags to skip from the API logs - skipResponseTags: SupportedVersions,VAppTemplate - # - # Comma-separated list of functions from where we log the API calls. - # When this is set, we only log API requests and responses if the name - # of the function matches any of the names in this list. - logFunctions: -ova: - # The ova for uploading catalog item for tests. - # Default paths are simple ova provided by project - # Empty values skips the tests - # Absolute or relative path - ovaPath: ../test-resources/test_vapp_template.ova - # - # The chunked ova (vmdk inside are split) for tests - ovaChunkedPath: ../test-resources/template_with_custom_chunk_size.ova -media: - # The iso for uploading media item for tests. - # Default paths are simple iso provided by project - # Empty values skips the tests - # Absolute or relative path - mediaPath: ../test-resources/test.iso - mediaName: vaido2 diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go index 1172227a2..5a26a3648 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go @@ -852,12 +852,18 @@ func (vm *VM) SetGuestCustomizationSection(guestCustomizationSection *types.Gues return vm.GetGuestCustomizationSection() } -func (vm *VM) AddDisk(diskData *types.DiskSettings) (string, error) { +// AddInternalDisk creates disk type *types.DiskSettings to the VM. +// Returns new disk ID and error. +// Runs synchronously, VM is ready for another operation after this function returns. +func (vm *VM) AddInternalDisk(diskData *types.DiskSettings) (string, error) { if vm.VM.HREF == "" { return "", fmt.Errorf("cannot add internal disks - VM HREF is unset") } - //TODO add validation + err := vm.validateInternalDiskInput(diskData) + if err != nil { + return "", err + } diskSettings := vm.VM.VmSpecSection.DiskSection.DiskSettings if diskSettings == nil { @@ -868,7 +874,7 @@ func (vm *VM) AddDisk(diskData *types.DiskSettings) (string, error) { vmSpecSection := vm.VM.VmSpecSection vmSpecSection.DiskSection.DiskSettings = diskSettings - vmSpecSection, err := vm.UpdateDisks(vmSpecSection) + vmSpecSection, err = vm.UpdateInternalDisks(vmSpecSection) if err != nil { return "", err } @@ -881,10 +887,44 @@ func (vm *VM) AddDisk(diskData *types.DiskSettings) (string, error) { } } - return "", fmt.Errorf("created disk wasn't in list VM internal disks") + return "", fmt.Errorf("created disk wasn't in list of returned VM internal disks") +} + +func (vm *VM) validateInternalDiskInput(diskData *types.DiskSettings) error { + if diskData.AdapterType == "" { + return fmt.Errorf("disk settings missing required field: adapter type") + } + + if diskData.BusNumber < 0 { + return fmt.Errorf("disk settings bus number has to be 0 or higher") + } + + if diskData.UnitNumber < 0 { + return fmt.Errorf("disk settings unit number has to be 0 or higher") + } + + if diskData.SizeMb < int64(0) { + return fmt.Errorf("disk settings size MB has to be 0 or higher") + } + + if diskData.Iops != nil && *diskData.Iops < int64(0) { + return fmt.Errorf("disk settings iops has to be 0 or higher") + } + + if diskData.ThinProvisioned != nil { + return fmt.Errorf("disk settings missing required field: thin provisioned") + } + + if diskData.StorageProfile != nil { + return fmt.Errorf("disk settings missing required field: storage profile") + } + + return nil } -func (vm *VM) GetDisk(diskId string) (*types.DiskSettings, error) { +// GetInternalDisk returns a valid *types.DiskSettings if it exists. +// If it doesn't, returns nil and ErrorEntityNotFound or other err. +func (vm *VM) GetInternalDisk(diskId string) (*types.DiskSettings, error) { if vm.VM.HREF == "" { return nil, fmt.Errorf("cannot get internal disk - VM HREF is unset") } @@ -907,7 +947,9 @@ func (vm *VM) GetDisk(diskId string) (*types.DiskSettings, error) { return nil, ErrorEntityNotFound } -func (vm *VM) DeleteDisk(diskId string) error { +// DeleteInternalDisk delete disk using provided disk ID. +// Runs synchronously, VM is ready for another operation after this function returns. +func (vm *VM) DeleteInternalDisk(diskId string) error { if vm.VM.HREF == "" { return fmt.Errorf("cannot delete internal disks - VM HREF is unset") } @@ -934,7 +976,7 @@ func (vm *VM) DeleteDisk(diskId string) error { vmSpecSection := vm.VM.VmSpecSection vmSpecSection.DiskSection.DiskSettings = diskSettings - vmSpecSection, err := vm.UpdateDisks(vmSpecSection) + _, err := vm.UpdateInternalDisks(vmSpecSection) if err != nil { return err } @@ -947,16 +989,16 @@ func (vm *VM) DeleteDisk(diskId string) error { return nil } -// UpdateDisks applies disks configuration for the VM. +// UpdateInternalDisks applies disks configuration for the VM. // types.VmSpecSection requires consist of all disk entities which exist and not updated, // as also new ones or changed ones. Returns new disk ID and error. // Runs synchronously, VM is ready for another operation after this function returns. -func (vm *VM) UpdateDisks(disksSettingToUpdate *types.VmSpecSection) (*types.VmSpecSection, error) { +func (vm *VM) UpdateInternalDisks(disksSettingToUpdate *types.VmSpecSection) (*types.VmSpecSection, error) { if vm.VM.HREF == "" { return nil, fmt.Errorf("cannot update internal disks - VM HREF is unset") } - task, err := vm.UpdateDisksAsync(disksSettingToUpdate) + task, err := vm.UpdateInternalDisksAsync(disksSettingToUpdate) if err != nil { return nil, err } @@ -971,10 +1013,10 @@ func (vm *VM) UpdateDisks(disksSettingToUpdate *types.VmSpecSection) (*types.VmS return vm.VM.VmSpecSection, nil } -// UpdateDisks applies disks configuration and return task or err +// UpdateInternalDisksAsync applies disks configuration and return task or err // types.VmSpecSection requires consist of all disk entities which exist and not updated, // as also new ones or changed ones. -func (vm *VM) UpdateDisksAsync(disksSettingToUpdate *types.VmSpecSection) (Task, error) { +func (vm *VM) UpdateInternalDisksAsync(disksSettingToUpdate *types.VmSpecSection) (Task, error) { if vm.VM.HREF == "" { return Task{}, fmt.Errorf("cannot update disks, VM HREF is unset") } diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go index bc528bc95..d5ec2621b 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go @@ -1472,18 +1472,6 @@ type VirtualHardwareSection struct { Item []*VirtualHardwareItem `xml:"Item,omitempty"` } -// RasdItemsList from VirtualHardware -type RasdItemsList struct { - // Extends OVF Section_Type - XMLName xml.Name `xml:"RasdItemsList"` - Xmlns string `xml:"xmlns,attr,omitempty"` - RasdXmlns string `xml:"xmlns:rasd,attr,omitempty"` - - HREF string `xml:"href,attr,omitempty"` - Type string `xml:"type,attr,omitempty"` - Items []*VirtualHardwareItem `xml:"Item,omitempty"` -} - // Each ovf:Item parsed from the ovf:VirtualHardwareSection type VirtualHardwareItem struct { XMLName xml.Name `xml:"Item"` diff --git a/vendor/modules.txt b/vendor/modules.txt index 2b4e0b352..f04fb2583 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -224,7 +224,7 @@ github.com/ulikunitz/xz/lzma # github.com/vmihailenco/msgpack v4.0.1+incompatible github.com/vmihailenco/msgpack github.com/vmihailenco/msgpack/codes -# github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 => ../go-vcloud-director +# github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205092948-006b5b528722 github.com/vmware/go-vcloud-director/v2/govcd github.com/vmware/go-vcloud-director/v2/types/v56 github.com/vmware/go-vcloud-director/v2/util From 0ddb0149889d72f7c3469231096a0d4a602d46ba Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 5 Dec 2019 14:45:17 +0200 Subject: [PATCH 16/80] Improvements Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index 45890705e..1606f1710 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -186,8 +186,8 @@ func powerOnIfNeeded(d *schema.ResourceData, vm *govcd.VM) error { return fmt.Errorf("error getting VM status before ensuring it is powered on: %s", err) } - if (vmStatus != "POWERED_ON" && d.Get("bus_type").(string) == "ide" && d.Get("allow_vm_reboot").(bool) == true) || - (vmStatus != "POWERED_ON" && d.Get("allow_vm_reboot").(bool) == true && (d.HasChange("bus_number") || d.HasChange("unit_number"))) { + if (vmStatus != "POWERED_ON" && d.Get("bus_type").(string) == "ide" && d.Get("allow_vm_reboot").(bool)) || + (vmStatus != "POWERED_ON" && d.Get("allow_vm_reboot").(bool) && (d.HasChange("bus_number") || d.HasChange("unit_number"))) { log.Printf("[DEBUG] Powering on VM %s after adding internal disk.", vm.VM.Name) task, err := vm.PowerOn() @@ -208,8 +208,8 @@ func powerOffIfNeeded(d *schema.ResourceData, vm *govcd.VM) error { return fmt.Errorf("error getting VM status before ensuring it is powered on: %s", err) } - if (vmStatus != "POWERED_OFF" && d.Get("bus_type").(string) == "ide" && d.Get("allow_vm_reboot").(bool) == true) || - (vmStatus != "POWERED_OFF" && d.Get("allow_vm_reboot").(bool) == true && (d.HasChange("bus_number") || d.HasChange("unit_number"))) { + if (vmStatus != "POWERED_OFF" && d.Get("bus_type").(string) == "ide" && d.Get("allow_vm_reboot").(bool)) || + (vmStatus != "POWERED_OFF" && d.Get("allow_vm_reboot").(bool) && (d.HasChange("bus_number") || d.HasChange("unit_number"))) { log.Printf("[DEBUG] Powering off VM %s for adding/updating internal disk.", vm.VM.Name) task, err := vm.PowerOff() From 535b1ba2b4df3ffee593415110b4b187585ab584 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 5 Dec 2019 16:22:08 +0200 Subject: [PATCH 17/80] Add documentation Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vapp_vm.go | 2 +- vcd/resource_vcd_vm_internal_disk.go | 4 +-- website/docs/r/vapp_vm.html.markdown | 54 ++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index a8553b870..efaf19747 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -296,7 +296,7 @@ func resourceVcdVAppVm() *schema.Resource { "internal_disk": { Type: schema.TypeList, Computed: true, - Description: " A block will show internal disk details", + Description: "A block will show internal disk details", Elem: &schema.Resource{Schema: map[string]*schema.Schema{ "disk_id": { Type: schema.TypeString, diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index 1606f1710..4e6182327 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -44,13 +44,13 @@ func resourceVmInternalDisk() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - Description: "Storage profile to override the VM default one", + Description: "The vApp this VM internal disk belongs to", }, "vm_name": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, - Description: "Storage profile to override the VM default one", + Description: "VM in vApp in which internal disk created", }, "allow_vm_reboot": { Type: schema.TypeBool, diff --git a/website/docs/r/vapp_vm.html.markdown b/website/docs/r/vapp_vm.html.markdown index aea4a6561..181291a90 100644 --- a/website/docs/r/vapp_vm.html.markdown +++ b/website/docs/r/vapp_vm.html.markdown @@ -114,6 +114,28 @@ resource "vcd_vapp_vm" "web2" { depends_on = ["vcd_vapp.web"] } +resource "vcd_vapp_vm" "internalDiskOverride" { + vapp_name = "${vcd_vapp.web.name}" + name = "internalDiskOverride" + catalog_name = "Boxes" + template_name = "lampstack-1.10.1-ubuntu-10.04" + memory = 2048 + cpus = 2 + cpu_cores = 1 + + override_template_disk { + bus_type = "paravirtual" + size_in_mb = "22384" + bus_number = 0 + unit_number = 0 + iops = 0 + thin_provisioned = true + storage_profile = "*" + } + + depends_on = ["vcd_vapp.web"] +} + ``` ## Argument Reference @@ -152,6 +174,7 @@ example for usage details. **Deprecates**: `network_name`, `ip`, `vapp_network_n * `guest_properties` - (Optional; *v2.5+*) Key value map of guest properties * `description` - (Computed; *v2.6+*) The VM description. Note: description is read only. Currently, this field has the description of the OVA used to create the VM +* `override_template_disk` - (Optional; *v2.6+*) Allows to update internal disk in template before first VM boot. Disk are matched by `bus_type`, `bus_number` and `unit_number`. See [Override template Disk](#override_template_disk) below for details. ## Disk @@ -191,6 +214,18 @@ example for usage details. **Deprecates**: `network_name`, `ip`, `vapp_network_n * `ip_allocation_mode=NONE` - **`ip`** field can be omitted or set to an empty string "". Empty string may be useful when doing HCL variable interpolation. + +## Override template disk + +* `bus_type` (Required) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. +* `size_in_mb` (Required) The size of the disk in MB. +* `bus_number` (Required) The number of the SCSI or IDE controller itself. +* `unit_number` (Required) The device number on the SCSI or IDE controller of the disk. +* `thin_provisioned` (Optional) Specifies whether the disk storage is pre-allocated or allocated on demand. +* `iops` (Optional) Specifies the IOPS for the disk. Default - 0. +* `storage_profile` (Optional) Storage profile which overrides the VM default one. + + ## Customization @@ -258,6 +293,25 @@ resource "vcd_vapp_vm" "web2" { } ``` +## Attribute Reference + +The following additional attributes are exported: + +* `internal_disk` - (*v2.6+*) A block provides internal disk of VM details. See [Internal Disk](#internalDisk) below for details. + + +## Internal disk + +* `disk_id` (*v2.6+*) Specifies a unique identifier for this disk in the scope of the corresponding VM. +* `bus_type` (*v2.6+*) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. +* `size_in_mb` (*v2.6+*) The size of the disk in MB. +* `bus_number` (*v2.6+*) The number of the SCSI or IDE controller itself. +* `unit_number` (*v2.6+*) The device number on the SCSI or IDE controller of the disk. +* `thin_provisioned` (*v2.6+*) Specifies whether the disk storage is pre-allocated or allocated on demand. +* `iops` (*v2.6+*) Specifies the IOPS for the disk. Default - 0. +* `storage_profile` (*v2.6+*) Storage profile which overrides the VM default one. + + ## Importing Supported in provider *v2.6+* From 993c1b7a6cfb8c4bd7b058d89fc238b80b446324 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 5 Dec 2019 16:59:36 +0200 Subject: [PATCH 18/80] Add documentation Signed-off-by: Vaidotas Bauzys --- website/docs/r/vapp_vm.html.markdown | 30 ++--- website/docs/r/vm_internal_disk.html.markdown | 105 ++++++++++++++++++ website/vcd.erb | 3 + 3 files changed, 123 insertions(+), 15 deletions(-) create mode 100644 website/docs/r/vm_internal_disk.html.markdown diff --git a/website/docs/r/vapp_vm.html.markdown b/website/docs/r/vapp_vm.html.markdown index 181291a90..bc22d0f5d 100644 --- a/website/docs/r/vapp_vm.html.markdown +++ b/website/docs/r/vapp_vm.html.markdown @@ -217,13 +217,13 @@ example for usage details. **Deprecates**: `network_name`, `ip`, `vapp_network_n ## Override template disk -* `bus_type` (Required) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. -* `size_in_mb` (Required) The size of the disk in MB. -* `bus_number` (Required) The number of the SCSI or IDE controller itself. -* `unit_number` (Required) The device number on the SCSI or IDE controller of the disk. -* `thin_provisioned` (Optional) Specifies whether the disk storage is pre-allocated or allocated on demand. -* `iops` (Optional) Specifies the IOPS for the disk. Default - 0. -* `storage_profile` (Optional) Storage profile which overrides the VM default one. +* `bus_type` - (Required) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. +* `size_in_mb` - (Required) The size of the disk in MB. +* `bus_number` - (Required) The number of the SCSI or IDE controller itself. +* `unit_number` - (Required) The device number on the SCSI or IDE controller of the disk. +* `thin_provisioned` - (Optional) Specifies whether the disk storage is pre-allocated or allocated on demand. +* `iops` - (Optional) Specifies the IOPS for the disk. Default - 0. +* `storage_profile` - (Optional) Storage profile which overrides the VM default one. @@ -302,14 +302,14 @@ The following additional attributes are exported: ## Internal disk -* `disk_id` (*v2.6+*) Specifies a unique identifier for this disk in the scope of the corresponding VM. -* `bus_type` (*v2.6+*) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. -* `size_in_mb` (*v2.6+*) The size of the disk in MB. -* `bus_number` (*v2.6+*) The number of the SCSI or IDE controller itself. -* `unit_number` (*v2.6+*) The device number on the SCSI or IDE controller of the disk. -* `thin_provisioned` (*v2.6+*) Specifies whether the disk storage is pre-allocated or allocated on demand. -* `iops` (*v2.6+*) Specifies the IOPS for the disk. Default - 0. -* `storage_profile` (*v2.6+*) Storage profile which overrides the VM default one. +* `disk_id` - (*v2.6+*) Specifies a unique identifier for this disk in the scope of the corresponding VM. +* `bus_type` - (*v2.6+*) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. +* `size_in_mb` - (*v2.6+*) The size of the disk in MB. +* `bus_number` - (*v2.6+*) The number of the SCSI or IDE controller itself. +* `unit_number` - (*v2.6+*) The device number on the SCSI or IDE controller of the disk. +* `thin_provisioned` - (*v2.6+*) Specifies whether the disk storage is pre-allocated or allocated on demand. +* `iops` - (*v2.6+*) Specifies the IOPS for the disk. Default - 0. +* `storage_profile` - (*v2.6+*) Storage profile which overrides the VM default one. ## Importing diff --git a/website/docs/r/vm_internal_disk.html.markdown b/website/docs/r/vm_internal_disk.html.markdown new file mode 100644 index 000000000..8a4b2a9cf --- /dev/null +++ b/website/docs/r/vm_internal_disk.html.markdown @@ -0,0 +1,105 @@ +--- +layout: "vcd" +page_title: "vCloudDirector: vcd_vm_internal_disk" +sidebar_current: "docs-vcd-vm-internal-disk" +description: |- + Provides a vCloud Director VM internal disk resource. This can be used to create and delete VM internal disks. +--- + +# vcd\_vm\_internal\_disk + +Provides a vCloud Director VM internal disk resource. This can be used to create and delete VM internal disks. + +Supported in provider *v2.6+* + +## Example Usage + +``` +resource "vcd_vm_internal_disk" "disk1" { + vapp_name = "my-vapp" + vm_name = "my-vm1" + bus_type = "sata" + size_in_mb = "13333" + bus_number = 0 + unit_number = 1 + storage_profile = "Development" + allow_vm_reboot = true + depends_on = ["vcd_vapp_vm.web1"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `org` - (Optional) The name of organization to use, optional if defined at provider level. Useful when connected as sysadmin working across different organisations +* `vdc` - (Optional) The name of VDC to use, optional if defined at provider level +* `vapp_name` - (Required) The vApp this VM internal disk belongs to. +* `vm_name` - (Required) VM in vApp in which internal disk created. +* `allow_vm_reboot` - (Optional) Powers off VM when changing any attribute of an IDE disk or unit/bus number of other disk types, after the change is complete VM is powered back on. Without this setting enabled, such changes on a powered on VM would fail. +* `bus_type` - (Required) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. +* `size_in_mb` - (Required) The size of the disk in MB. +* `bus_number` - (Required) The number of the SCSI or IDE controller itself. +* `unit_number` - (Required) The device number on the SCSI or IDE controller of the disk. +* `thin_provisioned` - (Optional) Specifies whether the disk storage is pre-allocated or allocated on demand. +* `iops` - (Optional) Specifies the IOPS for the disk. Default - 0. +* `storage_profile` - (Optional) Storage profile which overrides the VM default one. + + +## Importing + +~> **Note:** The current implementation of Terraform import can only import resources into the state. It does not generate +configuration. [More information.][docs-import] + +An existing VM internal disk can be [imported][docs-import] into this resource via supplying its path. +The path for this resource is made of org-name.vdc-name.vapp-name.vm-name.disk-id +For example, using this structure, representing a VM internal disk that was **not** created using Terraform: + +```hcl +resource "vcd_vm_internal_disk" "tf-myInternalDisk" { + org = "my-org" + vdc = "my-vdc" + vapp_name = "my-vapp" + vm_name = "my-vm" +} +``` + +You can import such VM internal disk into terraform state using this command + +``` +terraform import vcd_vm_internal_disk.tf-myInternalDisk my-org.my-vdc.my-vapp.my-vm.my-disk-id +``` + +[docs-import]:https://www.terraform.io/docs/import/ + +After importing, if you run `terraform plan` you will see the rest of the values and modify the script accordingly for +further operations. + +### Listing VM internal disk IDs + +If you want to list IDs there is a special command **`terraform import vcd_vm_internal_disk.imported list@org-name.vcd-name.vapp-name.vm-name`** +where `org-name` is the organization used, `vdc-name` is vDC name, `vapp-name` is vAPP name and `vm_name` is VM name in that vAPP. +The output for this command should look similar to below one: + +```shell +$ terraform import vcd_vm_internal_disk.imported list@org-name.vdc-name.vapp-name.vm-name +vcd_vm_internal_disk.imported: Importing from ID "list@org-name.vdc-name.vapp-name.vm-name"... +Retrieving all disks by name +No ID BusType BusNumber UnitNumber Size StoragePofile Iops ThinProvisioned +-- -- ------- --------- ---------- ---- ------------- ---- --------------- +1 2000 paravirtual 0 0 16384 * 0 true +2 3001 ide 0 1 17384 * 0 true +3 16000 sata 0 0 18384 * 0 true +4 16001 sata 0 1 13333 Development 0 true + +Error: resource was not imported! resource id must be specified in one of these formats: +'org-name.vdc-name.vapp-name.vm-name.my-internal-disk-id' to import by rule id +'list@org-name.vdc-name.vapp-name.vm-name' to get a list of internal disks with their IDs + +``` + +Now to import disk with ID urn:vcloud:disk:1bbc273d-7701-4f06-97be-428b46b0805e one could supply this command: + +```shell +$ terraform import vcd_vm_internal_disk.imported org-name.vdc-name.vapp-name.vm-name.3001 +``` \ No newline at end of file diff --git a/website/vcd.erb b/website/vcd.erb index 2f3d91de0..5e3cec6f9 100644 --- a/website/vcd.erb +++ b/website/vcd.erb @@ -175,6 +175,9 @@ > vcd_ipset + > + vcd_vm_internal_disk + From e2a282dcbef27e1fd0e7f0c4bcb4da7b910dd039 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 5 Dec 2019 17:02:49 +0200 Subject: [PATCH 19/80] Update from latest govcd Signed-off-by: Vaidotas Bauzys --- go.mod | 2 +- go.sum | 4 +- .../vmware/go-vcloud-director/v2/govcd/vm.go | 56 +++++++++---------- .../go-vcloud-director/v2/types/v56/types.go | 13 +++-- vendor/modules.txt | 2 +- 5 files changed, 39 insertions(+), 38 deletions(-) diff --git a/go.mod b/go.mod index 0fce11662..e4faba2b9 100644 --- a/go.mod +++ b/go.mod @@ -8,4 +8,4 @@ require ( github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 ) -replace github.com/vmware/go-vcloud-director/v2 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205092948-006b5b528722 +replace github.com/vmware/go-vcloud-director/v2 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205131915-f724347daa56 diff --git a/go.sum b/go.sum index 57972a532..124a61c29 100644 --- a/go.sum +++ b/go.sum @@ -197,8 +197,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205092948-006b5b528722 h1:tDbMYaO0kQcSbAG4sVOqcg4qwgyO19CE5nWdql6t+4I= -github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205092948-006b5b528722/go.mod h1:zjondbeyTfZlzhwxOzyF4K2sWWYgMEv5H91dp5dPbU8= +github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205131915-f724347daa56 h1:GOGIPzQ3Uckwe7jxqh8eDvMOQUp6cgd/uqt6zMMzy6s= +github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205131915-f724347daa56/go.mod h1:zjondbeyTfZlzhwxOzyF4K2sWWYgMEv5H91dp5dPbU8= github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU= diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go index 5a26a3648..e86214661 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go @@ -856,18 +856,19 @@ func (vm *VM) SetGuestCustomizationSection(guestCustomizationSection *types.Gues // Returns new disk ID and error. // Runs synchronously, VM is ready for another operation after this function returns. func (vm *VM) AddInternalDisk(diskData *types.DiskSettings) (string, error) { - if vm.VM.HREF == "" { - return "", fmt.Errorf("cannot add internal disks - VM HREF is unset") + err := vm.Refresh() + if err != nil { + return "", fmt.Errorf("error refreshing VM: %s", err) } - err := vm.validateInternalDiskInput(diskData) + err = vm.validateInternalDiskInput(diskData) if err != nil { return "", err } - diskSettings := vm.VM.VmSpecSection.DiskSection.DiskSettings - if diskSettings == nil { - diskSettings = []*types.DiskSettings{} + var diskSettings []*types.DiskSettings + if vm.VM.VmSpecSection != nil && vm.VM.VmSpecSection.DiskSection != nil && vm.VM.VmSpecSection.DiskSection.DiskSettings != nil { + diskSettings = vm.VM.VmSpecSection.DiskSection.DiskSettings } diskSettings = append(diskSettings, diskData) @@ -911,31 +912,34 @@ func (vm *VM) validateInternalDiskInput(diskData *types.DiskSettings) error { return fmt.Errorf("disk settings iops has to be 0 or higher") } - if diskData.ThinProvisioned != nil { + if diskData.ThinProvisioned == nil { return fmt.Errorf("disk settings missing required field: thin provisioned") } - if diskData.StorageProfile != nil { + if diskData.StorageProfile == nil { return fmt.Errorf("disk settings missing required field: storage profile") } return nil } -// GetInternalDisk returns a valid *types.DiskSettings if it exists. +// GetInternalDiskId returns a valid *types.DiskSettings if it exists. // If it doesn't, returns nil and ErrorEntityNotFound or other err. -func (vm *VM) GetInternalDisk(diskId string) (*types.DiskSettings, error) { - if vm.VM.HREF == "" { - return nil, fmt.Errorf("cannot get internal disk - VM HREF is unset") +func (vm *VM) GetInternalDiskId(diskId string, refresh bool) (*types.DiskSettings, error) { + if refresh { + err := vm.Refresh() + if err != nil { + return nil, fmt.Errorf("error refreshing VM: %s", err) + } } if diskId == "" { - return nil, fmt.Errorf("cannot get internal disk - provided diskId is empty") + return nil, fmt.Errorf("cannot get internal disk - provided disk Id is empty") } if vm.VM.VmSpecSection.DiskSection == nil || vm.VM.VmSpecSection.DiskSection.DiskSettings == nil || len(vm.VM.VmSpecSection.DiskSection.DiskSettings) == 0 { - return nil, fmt.Errorf("cannot get internal disk - VM don't have internal disks") + return nil, fmt.Errorf("cannot get internal disk - VM doesn't have internal disks") } for _, diskSetting := range vm.VM.VmSpecSection.DiskSection.DiskSettings { @@ -947,11 +951,12 @@ func (vm *VM) GetInternalDisk(diskId string) (*types.DiskSettings, error) { return nil, ErrorEntityNotFound } -// DeleteInternalDisk delete disk using provided disk ID. +// DeleteInternalDiskById delete disk using provided disk ID. // Runs synchronously, VM is ready for another operation after this function returns. -func (vm *VM) DeleteInternalDisk(diskId string) error { - if vm.VM.HREF == "" { - return fmt.Errorf("cannot delete internal disks - VM HREF is unset") +func (vm *VM) DeleteInternalDiskById(diskId string) error { + err := vm.Refresh() + if err != nil { + return fmt.Errorf("error refreshing VM: %s", err) } diskSettings := vm.VM.VmSpecSection.DiskSection.DiskSettings @@ -967,7 +972,7 @@ func (vm *VM) DeleteInternalDisk(diskId string) error { } if diskPlacement == -1 { - return fmt.Errorf("cannot find VM internal disk with Id: %s", diskId) + return ErrorEntityNotFound } // remove disk in slice @@ -976,14 +981,9 @@ func (vm *VM) DeleteInternalDisk(diskId string) error { vmSpecSection := vm.VM.VmSpecSection vmSpecSection.DiskSection.DiskSettings = diskSettings - _, err := vm.UpdateInternalDisks(vmSpecSection) - if err != nil { - return err - } - - err = vm.Refresh() + _, err = vm.UpdateInternalDisks(vmSpecSection) if err != nil { - return fmt.Errorf("error refresing vm %s: %s", vm.VM.Name, err) + return fmt.Errorf("error deleting VM %s internal disk %s: %s", vm.VM.Name, diskId, err) } return nil @@ -1004,11 +1004,11 @@ func (vm *VM) UpdateInternalDisks(disksSettingToUpdate *types.VmSpecSection) (*t } err = task.WaitTaskCompletion() if err != nil { - return nil, fmt.Errorf("error waiting for task completion after internal disks update for vm %s: %s", vm.VM.Name, err) + return nil, fmt.Errorf("error waiting for task completion after internal disks update for VM %s: %s", vm.VM.Name, err) } err = vm.Refresh() if err != nil { - return nil, fmt.Errorf("error refresing vm %s: %s", vm.VM.Name, err) + return nil, fmt.Errorf("error refreshing VM %s: %s", vm.VM.Name, err) } return vm.VM.VmSpecSection, nil } diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go index d5ec2621b..c9d228307 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go @@ -1367,7 +1367,7 @@ type VM struct { ProductSection *ProductSection `xml:"ProductSection,omitempty"` } -// VM represents a virtual machine only with Disk setting update part +// VMDiskChange represents a virtual machine only with Disk setting update part type VMDiskChange struct { // Attributes XMLName xml.Name `xml:"Vm"` @@ -1375,12 +1375,12 @@ type VMDiskChange struct { Xsi string `xml:"xmlns:xsi,attr,omitempty"` Xmlns string `xml:"xmlns,attr,omitempty"` - HREF string `xml:"href,attr,omitempty"` - Type string `xml:"type,attr,omitempty"` - Name string `xml:"name,attr"` - ID string `xml:"id,attr,omitempty"` + HREF string `xml:"href,attr,omitempty"` // The URI of the VM entity. + Type string `xml:"type,attr,omitempty"` // The MIME type of the entity - application/vnd.vmware.vcloud.vm+xml + Name string `xml:"name,attr"` // VM name + ID string `xml:"id,attr,omitempty"` // VM ID. The entity identifier, expressed in URN format. The value of this attribute uniquely identifies the entity, persists for the life of the entity, and is never reused. - VmSpecSection *VmSpecSection `xml:"VmSpecSection,omitempty"` + VmSpecSection *VmSpecSection `xml:"VmSpecSection,omitempty"` // Container for the specification of this virtual machine. This is an alternate to using ovf:VirtualHardwareSection + ovf:OperatingSystemSection } // VmSpecSection from VM struct @@ -1454,6 +1454,7 @@ type MemoryResourceMb struct { Shares *int `xml:"Shares,omitempty"` // Custom priority for the resource. This is a read-only, unless the share level is CUSTOM. } +// HardwareVersion from VM/VmSpecSection struct type HardwareVersion struct { HREF string `xml:"href,attr"` Type string `xml:"type,attr,omitempty"` diff --git a/vendor/modules.txt b/vendor/modules.txt index f04fb2583..183779a3c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -224,7 +224,7 @@ github.com/ulikunitz/xz/lzma # github.com/vmihailenco/msgpack v4.0.1+incompatible github.com/vmihailenco/msgpack github.com/vmihailenco/msgpack/codes -# github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205092948-006b5b528722 +# github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205131915-f724347daa56 github.com/vmware/go-vcloud-director/v2/govcd github.com/vmware/go-vcloud-director/v2/types/v56 github.com/vmware/go-vcloud-director/v2/util From 2e93c71195fd03db608b4af457daf45ecec2e180 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 5 Dec 2019 17:11:36 +0200 Subject: [PATCH 20/80] Fixes Signed-off-by: Vaidotas Bauzys --- go.mod | 2 +- go.sum | 4 ++-- vcd/resource_vcd_vm_internal_disk.go | 8 ++++---- .../github.com/vmware/go-vcloud-director/v2/govcd/vm.go | 4 ++-- vendor/modules.txt | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index e4faba2b9..ff9e358c8 100644 --- a/go.mod +++ b/go.mod @@ -8,4 +8,4 @@ require ( github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 ) -replace github.com/vmware/go-vcloud-director/v2 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205131915-f724347daa56 +replace github.com/vmware/go-vcloud-director/v2 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205150825-aeb30347581d diff --git a/go.sum b/go.sum index 124a61c29..d1196ab92 100644 --- a/go.sum +++ b/go.sum @@ -197,8 +197,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205131915-f724347daa56 h1:GOGIPzQ3Uckwe7jxqh8eDvMOQUp6cgd/uqt6zMMzy6s= -github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205131915-f724347daa56/go.mod h1:zjondbeyTfZlzhwxOzyF4K2sWWYgMEv5H91dp5dPbU8= +github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205150825-aeb30347581d h1:RyfWNunUDWXkIhX/FOUUBGOdRqoRUaLGj5Dzo/K88io= +github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205150825-aeb30347581d/go.mod h1:zjondbeyTfZlzhwxOzyF4K2sWWYgMEv5H91dp5dPbU8= github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU= diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index 4e6182327..a3ee43bda 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -241,7 +241,7 @@ func resourceVmInternalDiskDelete(d *schema.ResourceData, m interface{}) error { return err } - err = vm.DeleteInternalDisk(d.Id()) + err = vm.DeleteInternalDiskById(d.Id()) if err != nil { return fmt.Errorf("[Error] failed to delete internal disk: %s", err) } @@ -285,7 +285,7 @@ func resourceVmInternalDiskUpdate(d *schema.ResourceData, meta interface{}) erro return err } - diskSettingsToUpdate, err := vm.GetInternalDisk(d.Id()) + diskSettingsToUpdate, err := vm.GetInternalDiskById(d.Id(), false) if err != nil { return err } @@ -346,7 +346,7 @@ func resourceVmInternalDiskRead(d *schema.ResourceData, m interface{}) error { return err } - diskSettings, err := vm.GetInternalDisk(d.Id()) + diskSettings, err := vm.GetInternalDiskById(d.Id(), false) if err == govcd.ErrorEntityNotFound { log.Printf("[DEBUG] Unable to find disk with Id: %s. Removing from tfstate", d.Id()) d.SetId("") @@ -462,7 +462,7 @@ func getInternalDiskForImport(d *schema.ResourceData, meta interface{}, orgName, return nil, fmt.Errorf("[Error] failed to get VM: %s", err) } - disk, err := vm.GetInternalDisk(diskId) + disk, err := vm.GetInternalDiskById(diskId, false) if err != nil { return []*schema.ResourceData{}, fmt.Errorf("unable to find internal disk with id %s: %s", d.Id(), err) diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go index e86214661..cd6c5ca4e 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go @@ -923,9 +923,9 @@ func (vm *VM) validateInternalDiskInput(diskData *types.DiskSettings) error { return nil } -// GetInternalDiskId returns a valid *types.DiskSettings if it exists. +// GetInternalDiskById returns a valid *types.DiskSettings if it exists. // If it doesn't, returns nil and ErrorEntityNotFound or other err. -func (vm *VM) GetInternalDiskId(diskId string, refresh bool) (*types.DiskSettings, error) { +func (vm *VM) GetInternalDiskById(diskId string, refresh bool) (*types.DiskSettings, error) { if refresh { err := vm.Refresh() if err != nil { diff --git a/vendor/modules.txt b/vendor/modules.txt index 183779a3c..74495e2b5 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -224,7 +224,7 @@ github.com/ulikunitz/xz/lzma # github.com/vmihailenco/msgpack v4.0.1+incompatible github.com/vmihailenco/msgpack github.com/vmihailenco/msgpack/codes -# github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205131915-f724347daa56 +# github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205150825-aeb30347581d github.com/vmware/go-vcloud-director/v2/govcd github.com/vmware/go-vcloud-director/v2/types/v56 github.com/vmware/go-vcloud-director/v2/util From 2243df65641ebf20715eb2dfeae7f087415a85b7 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 5 Dec 2019 17:13:06 +0200 Subject: [PATCH 21/80] Remove commented code Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vapp_vm.go | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index efaf19747..88a389c24 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -669,10 +669,10 @@ func resourceVcdVAppVmUpdate(d *schema.ResourceData, meta interface{}) error { vcdClient.lockParentVapp(d) defer vcdClient.unLockParentVapp(d) - return resourceVcdVAppVmUpdateExecute(d, meta, false) + return resourceVcdVAppVmUpdateExecute(d, meta) } -func resourceVcdVAppVmUpdateExecute(d *schema.ResourceData, meta interface{}, isCallAfterCreation bool) error { +func resourceVcdVAppVmUpdateExecute(d *schema.ResourceData, meta interface{}) error { vcdClient := meta.(*VCDClient) @@ -757,14 +757,6 @@ func resourceVcdVAppVmUpdateExecute(d *schema.ResourceData, meta interface{}, is } } - /* if isCallAfterCreation == false && d.HasChange("internal_disk") { - err = updateInternalDisks(d, meta, *vm) - if err != nil { - d.Set("internal_disk", nil) - return fmt.Errorf("error updating itneranl disks : %s", err) - } - }*/ - if d.HasChange("memory") || d.HasChange("cpus") || d.HasChange("cpu_cores") || d.HasChange("power_on") || d.HasChange("disk") || d.HasChange("expose_hardware_virtualization") || d.HasChange("network") || d.HasChange("computer_name") { From f5a90475ec987706eef9c494fdf6e93720353be7 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 5 Dec 2019 17:39:43 +0200 Subject: [PATCH 22/80] Fix Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vapp_vm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index 88a389c24..d79f0fd13 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -531,7 +531,7 @@ func resourceVcdVAppVmCreate(d *schema.ResourceData, meta interface{}) error { } // TODO do not trigger resourceVcdVAppVmUpdate from create. These must be separate actions. - err = resourceVcdVAppVmUpdateExecute(d, meta, true) + err = resourceVcdVAppVmUpdateExecute(d, meta) if err != nil { errAttachedDisk := updateStateOfAttachedDisks(d, *vm, vdc) if errAttachedDisk != nil { From 670d980b8b3be6b4395e80418f1abe2c1fdfa7a3 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 6 Dec 2019 12:57:21 +0200 Subject: [PATCH 23/80] Add small fixes Signed-off-by: Vaidotas Bauzys --- vcd/config.go | 2 +- vcd/resource_vcd_vm_internal_disk.go | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/vcd/config.go b/vcd/config.go index 1e0a10606..f6d61210a 100644 --- a/vcd/config.go +++ b/vcd/config.go @@ -165,7 +165,7 @@ func (cli *VCDClient) unLockParentVapp(d *schema.ResourceData) { vcdMutexKV.Unlock(key) } -// function lockParentVm locks using vapp_name and vm_name names existing in resource parameters. +// lockParentVm locks using vapp_name and vm_name names existing in resource parameters. // Parent means the resource belongs to the VM being locked func (cli *VCDClient) lockParentVm(d *schema.ResourceData) { vappName := d.Get("vapp_name").(string) diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index a3ee43bda..f4b4e3911 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -1,9 +1,3 @@ -// /***************************************************************** -// * terraform-provider-vcloud-director -// * Copyright (c) 2017 VMware, Inc. All Rights Reserved. -// * SPDX-License-Identifier: BSD-2-Clause -// ******************************************************************/ - package vcd import ( @@ -118,7 +112,7 @@ var internalDiskBusTypesFromValues = map[string]string{ "6": "sata", } -// creates an internal disk for VM +// resourceVmInternalDiskCreate creates an internal disk for VM func resourceVmInternalDiskCreate(d *schema.ResourceData, meta interface{}) error { vcdClient := meta.(*VCDClient) @@ -224,7 +218,7 @@ func powerOffIfNeeded(d *schema.ResourceData, vm *govcd.VM) error { return nil } -// Deletes disk from VM +// resourceVmInternalDiskDelete deletes disk from VM func resourceVmInternalDiskDelete(d *schema.ResourceData, m interface{}) error { vcdClient := m.(*VCDClient) From e314843aec54911a4e2663fedf5232b148184312 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 18 Dec 2019 14:48:19 +0200 Subject: [PATCH 24/80] Improve test, update according latest findings Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk.go | 44 +++-- vcd/resource_vcd_vm_internal_disk_test.go | 203 +++++++++++++++++++++- 2 files changed, 220 insertions(+), 27 deletions(-) diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index f4b4e3911..1a468fa47 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -67,19 +67,19 @@ func resourceVmInternalDisk() *schema.Resource { "bus_number": { Type: schema.TypeInt, Required: true, + ForceNew: true, Description: "The number of the SCSI or IDE controller itself.", }, "unit_number": { Type: schema.TypeInt, Required: true, + ForceNew: true, Description: "The device number on the SCSI or IDE controller of the disk.", }, "thin_provisioned": { - Type: schema.TypeBool, - - Optional: true, - Default: true, - Description: "Specifies whether the disk storage is pre-allocated or allocated on demand. Default - true.", + Type: schema.TypeBool, + Computed: true, + Description: "Specifies whether the disk storage is pre-allocated or allocated on demand.", }, "iops": { Type: schema.TypeInt, @@ -140,7 +140,8 @@ func resourceVmInternalDiskCreate(d *schema.ResourceData, meta interface{}) erro } iops := int64(d.Get("iops").(int)) - isThinProvisioned := d.Get("thin_provisioned").(bool) + // value is required but not treated. + isThinProvisioned := true diskSetting := &types.DiskSettings{ SizeMb: int64(d.Get("size_in_mb").(int)), @@ -180,8 +181,7 @@ func powerOnIfNeeded(d *schema.ResourceData, vm *govcd.VM) error { return fmt.Errorf("error getting VM status before ensuring it is powered on: %s", err) } - if (vmStatus != "POWERED_ON" && d.Get("bus_type").(string) == "ide" && d.Get("allow_vm_reboot").(bool)) || - (vmStatus != "POWERED_ON" && d.Get("allow_vm_reboot").(bool) && (d.HasChange("bus_number") || d.HasChange("unit_number"))) { + if vmStatus != "POWERED_ON" && d.Get("bus_type").(string) == "ide" && d.Get("allow_vm_reboot").(bool) { log.Printf("[DEBUG] Powering on VM %s after adding internal disk.", vm.VM.Name) task, err := vm.PowerOn() @@ -202,8 +202,7 @@ func powerOffIfNeeded(d *schema.ResourceData, vm *govcd.VM) error { return fmt.Errorf("error getting VM status before ensuring it is powered on: %s", err) } - if (vmStatus != "POWERED_OFF" && d.Get("bus_type").(string) == "ide" && d.Get("allow_vm_reboot").(bool)) || - (vmStatus != "POWERED_OFF" && d.Get("allow_vm_reboot").(bool) && (d.HasChange("bus_number") || d.HasChange("unit_number"))) { + if vmStatus != "POWERED_OFF" && d.Get("bus_type").(string) == "ide" && d.Get("allow_vm_reboot").(bool) { log.Printf("[DEBUG] Powering off VM %s for adding/updating internal disk.", vm.VM.Name) task, err := vm.PowerOff() @@ -279,18 +278,22 @@ func resourceVmInternalDiskUpdate(d *schema.ResourceData, meta interface{}) erro return err } + // has refresh inside + err = powerOffIfNeeded(d, vm) + if err != nil { + return err + } + diskSettingsToUpdate, err := vm.GetInternalDiskById(d.Id(), false) if err != nil { return err } log.Printf("[TRACE] Internal Disk with id %s found", d.Id()) iops := int64(d.Get("iops").(int)) - isThinProvisioned := d.Get("thin_provisioned").(bool) diskSettingsToUpdate.Iops = &iops - diskSettingsToUpdate.ThinProvisioned = &isThinProvisioned - diskSettingsToUpdate.UnitNumber = d.Get("unit_number").(int) - diskSettingsToUpdate.BusNumber = d.Get("bus_number").(int) - diskSettingsToUpdate.AdapterType = internalDiskBusTypes[d.Get("bus_type").(string)] + //diskSettingsToUpdate.UnitNumber = d.Get("unit_number").(int) + //diskSettingsToUpdate.BusNumber = d.Get("bus_number").(int) + //diskSettingsToUpdate.AdapterType = internalDiskBusTypes[d.Get("bus_type").(string)] diskSettingsToUpdate.SizeMb = int64(d.Get("size_in_mb").(int)) var storageProfilePrt *types.Reference @@ -312,11 +315,6 @@ func resourceVmInternalDiskUpdate(d *schema.ResourceData, meta interface{}) erro diskSettingsToUpdate.StorageProfile = storageProfilePrt diskSettingsToUpdate.OverrideVmDefault = overrideVmDefault - err = powerOffIfNeeded(d, vm) - if err != nil { - return err - } - _, err = vm.UpdateInternalDisks(vm.VM.VmSpecSection) if err != nil { return err @@ -328,7 +326,7 @@ func resourceVmInternalDiskUpdate(d *schema.ResourceData, meta interface{}) erro } log.Printf("[TRACE] Inernal Disk %s updated", d.Id()) - return nil + return resourceVmInternalDiskRead(d, meta) } // Retrieves internal disk from VM and updates terraform state @@ -340,7 +338,7 @@ func resourceVmInternalDiskRead(d *schema.ResourceData, m interface{}) error { return err } - diskSettings, err := vm.GetInternalDiskById(d.Id(), false) + diskSettings, err := vm.GetInternalDiskById(d.Id(), true) if err == govcd.ErrorEntityNotFound { log.Printf("[DEBUG] Unable to find disk with Id: %s. Removing from tfstate", d.Id()) d.SetId("") @@ -354,7 +352,7 @@ func resourceVmInternalDiskRead(d *schema.ResourceData, m interface{}) error { _ = d.Set("size_in_mb", diskSettings.SizeMb) _ = d.Set("bus_number", diskSettings.BusNumber) _ = d.Set("unit_number", diskSettings.UnitNumber) - _ = d.Set("thin_provisioned", diskSettings.ThinProvisioned) + _ = d.Set("thin_provisioned", *diskSettings.ThinProvisioned) _ = d.Set("iops", diskSettings.Iops) _ = d.Set("storage_profile", diskSettings.StorageProfile.Name) diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go index 7175979a2..0e08bb9d3 100644 --- a/vcd/resource_vcd_vm_internal_disk_test.go +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -4,6 +4,8 @@ package vcd import ( "fmt" + "github.com/vmware/go-vcloud-director/v2/types/v56" + "regexp" "strconv" "testing" @@ -19,6 +21,12 @@ func TestAccVcdVmInternalDisk(t *testing.T) { return } + adminVdc, err := getAdminVdc() + if err != nil { + t.Skip(fmt.Sprintf("error retrieving if VDC is thing provisioned %s", err)) + return + } + vapp, err := getAvailableVapp() if err != nil { t.Skip("No suitable vApp found for this test") @@ -38,8 +46,15 @@ func TestAccVcdVmInternalDisk(t *testing.T) { return } + storageProfile := testConfig.VCD.ProviderVdc.StorageProfile + if *adminVdc.UsesFastProvisioning { + // to avoid `Cannot use multiple storage profiles in a fast-provisioned VDC` we need to reuse VM storage profile + storageProfile = vm.VM.StorageProfile.Name + } + diskResourceName := "disk1" diskSize := "13333" + biggerDiskSize := "14333" busType := "sata" busNumber := "1" unitNumber := "0" @@ -54,34 +69,141 @@ func TestAccVcdVmInternalDisk(t *testing.T) { "Tags": "vm", "DiskResourceName": diskResourceName, "Size": diskSize, + "SizeBigger": biggerDiskSize, "BusType": busType, "BusNumber": busNumber, "UnitNumber": unitNumber, "StorageProfileName": testConfig.VCD.ProviderVdc.StorageProfile, "AllowReboot": allowReboot, } + params["FuncName"] = t.Name() + "-IdeCreate" + configTextIde := templateFill(sourceTestVmInternalDiskIde, params) + params["FuncName"] = t.Name() + "-CreateALl" configText := templateFill(sourceTestVmInternalDisk, params) - debugPrintf("#[DEBUG] CONFIGURATION: %s", configText) + params["FuncName"] = t.Name() + "-Update1" + configText_update1 := templateFill(sourceTestVmInternalDisk_Update1, params) + params["FuncName"] = t.Name() + "-Update2" + //configText_update2 := templateFill(sourceTestVmInternalDisk_Update2, params) + debugPrintf("#[DEBUG] CONFIGURATION: %s", configText+configText_update1) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ + resource.TestStep{ + Config: configTextIde, + ExpectError: regexp.MustCompile(`.*The attempted operation cannot be performed in the current state \(Powered on\).*`), + Check: resource.ComposeTestCheckFunc(resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "size_in_mb", diskSize), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_type", "ide"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_number", "0"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "unit_number", "1"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "storage_profile", storageProfile), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "allow_vm_reboot", "false"), + ), + }, resource.TestStep{ Config: configText, Check: resource.ComposeTestCheckFunc(resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "size_in_mb", diskSize), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "bus_type", busType), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "bus_number", busNumber), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "unit_number", unitNumber), - resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "storage_profile", testConfig.VCD.ProviderVdc.StorageProfile), - resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "allow_vm_reboot", strconv.FormatBool(allowReboot)), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "storage_profile", storageProfile), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "iops", "0"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "size_in_mb", diskSize), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_type", "ide"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_number", "0"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "unit_number", "1"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "storage_profile", storageProfile), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "iops", "0"), + ), + }, + resource.TestStep{ + Config: configText_update1, + //ExpectError: regexp.MustCompile(`.*You must power off the virtual machine.*to change its hard disks, bus, or unit numbers.*`), + //ExpectNonEmptyPlan: true, + Check: resource.ComposeTestCheckFunc(resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "size_in_mb", biggerDiskSize), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "bus_type", busType), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "bus_number", busNumber), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "unit_number", unitNumber), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "storage_profile", storageProfile), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "iops", "0"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "allow_vm_reboot", "false"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_type", "ide"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_number", "0"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "unit_number", "1"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "storage_profile", storageProfile), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "size_in_mb", biggerDiskSize), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "iops", "0"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "allow_vm_reboot", "true"), ), }, + /*resource.TestStep{ + Config: configText_update2, + Check: resource.ComposeTestCheckFunc(resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "size_in_mb", diskSize), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_type", "ide"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_number", "1"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "unit_number", "0"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "storage_profile", testConfig.VCD.ProviderVdc.StorageProfile), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "allow_vm_reboot", "true"), + ), + },*/ }, }) } +func getAdminVdc() (*types.AdminVdc, error) { + vcdClient, err := getTestVCDFromJson(testConfig) + if err != nil { + return nil, fmt.Errorf("error getting client configuration: %s", err) + } + err = ProviderAuthenticate(vcdClient, testConfig.Provider.User, testConfig.Provider.Password, testConfig.Provider.Token, testConfig.Provider.SysOrg) + if err != nil { + return nil, fmt.Errorf("authentication error: %s", err) + } + org, err := vcdClient.GetAdminOrgByName(testConfig.VCD.Org) + if err != nil { + return nil, fmt.Errorf("org not found : %s", err) + } + adminVdc, err := org.GetAdminVDCByName(testConfig.VCD.Vdc, false) + if err != nil { + return nil, fmt.Errorf("vdc not found : %s", err) + } + return adminVdc.AdminVdc, nil +} + +const sourceTestVmInternalDiskIde = ` +resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { + org = "{{.Org}}" + vdc = "{{.VDC}}" + vapp_name = "{{.VappName}}" + vm_name = "{{.VmName}}" + bus_type = "ide" + size_in_mb = "{{.Size}}" + bus_number = "0" + unit_number = "1" + storage_profile = "{{.StorageProfileName}}" + allow_vm_reboot = "false" +} +` + const sourceTestVmInternalDisk = ` +resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { + org = "{{.Org}}" + vdc = "{{.VDC}}" + vapp_name = "{{.VappName}}" + vm_name = "{{.VmName}}" + bus_type = "ide" + size_in_mb = "{{.Size}}" + bus_number = "0" + unit_number = "1" + storage_profile = "{{.StorageProfileName}}" + allow_vm_reboot = "true" +} + resource "vcd_vm_internal_disk" "{{.DiskResourceName}}" { org = "{{.Org}}" vdc = "{{.VDC}}" @@ -92,6 +214,79 @@ resource "vcd_vm_internal_disk" "{{.DiskResourceName}}" { bus_number = "{{.BusNumber}}" unit_number = "{{.UnitNumber}}" storage_profile = "{{.StorageProfileName}}" - allow_vm_reboot = "{{.AllowReboot}}" + allow_vm_reboot = "false" +} +` + +const sourceTestVmInternalDisk_Update1 = ` +resource "vcd_vm_internal_disk" "{{.DiskResourceName}}" { + org = "{{.Org}}" + vdc = "{{.VDC}}" + vapp_name = "{{.VappName}}" + vm_name = "{{.VmName}}" + bus_type = "{{.BusType}}" + size_in_mb = "{{.SizeBigger}}" + bus_number = "{{.BusNumber}}" + unit_number = "{{.UnitNumber}}" + storage_profile = "{{.StorageProfileName}}" + allow_vm_reboot = "false" +} + +resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { + org = "{{.Org}}" + vdc = "{{.VDC}}" + vapp_name = "{{.VappName}}" + vm_name = "{{.VmName}}" + bus_type = "ide" + size_in_mb = "{{.SizeBigger}}" + bus_number = "0" + unit_number = "1" + storage_profile = "{{.StorageProfileName}}" + allow_vm_reboot = "true" +} +` + +/*const sourceTestVmInternalDisk = sataDisk + ` +resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { + org = "{{.Org}}" + vdc = "{{.VDC}}" + vapp_name = "{{.VappName}}" + vm_name = "{{.VmName}}" + bus_type = "ide" + size_in_mb = "{{.Size}}" + bus_number = "0" + unit_number = "1" + storage_profile = "{{.StorageProfileName}}" + allow_vm_reboot = "true" +} +` + +const sourceTestVmInternalDisk_Update1 = sataDisk + ` +resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { + org = "{{.Org}}" + vdc = "{{.VDC}}" + vapp_name = "{{.VappName}}" + vm_name = "{{.VmName}}" + bus_type = "ide" + size_in_mb = "{{.Size}}" + bus_number = "1" + unit_number = "0" + storage_profile = "{{.StorageProfileName}}" + allow_vm_reboot = "false" } ` + +const sourceTestVmInternalDisk_Update2 = sataDisk + ` +resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { + org = "{{.Org}}" + vdc = "{{.VDC}}" + vapp_name = "{{.VappName}}" + vm_name = "{{.VmName}}" + bus_type = "ide" + size_in_mb = "{{.Size}}" + bus_number = "1" + unit_number = "0" + storage_profile = "{{.StorageProfileName}}" + allow_vm_reboot = "true" +} +`*/ From 87c4fdb4b4be82ac71879c3e0c357cb3eedaca2c Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 18 Dec 2019 15:40:24 +0200 Subject: [PATCH 25/80] Improve test, update according latest findings Signed-off-by: Vaidotas Bauzys --- vcd/config_test.go | 18 ++++++++++++++++++ vcd/resource_vcd_vm_internal_disk_test.go | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/vcd/config_test.go b/vcd/config_test.go index 6f11c3baf..3f5724cf5 100644 --- a/vcd/config_test.go +++ b/vcd/config_test.go @@ -813,3 +813,21 @@ func importStateIdEdgeGatewayObject(vcd TestConfig, edgeGatewayName, objectName objectName, nil } } + +// Used by all entities that depend on Org + VDC + vApp VM (such as VM internal disks) +func importStateIdVmObject(vcd TestConfig, vappName, vmName, objectName string) resource.ImportStateIdFunc { + return func(*terraform.State) (string, error) { + if testConfig.VCD.Org == "" || testConfig.VCD.Vdc == "" || vappName == "" || vmName == "" || objectName == "" { + return "", fmt.Errorf("missing information to generate import path") + } + return testConfig.VCD.Org + + ImportSeparator + + testConfig.VCD.Vdc + + ImportSeparator + + vappName + + ImportSeparator + + vmName + + ImportSeparator + + objectName, nil + } +} diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go index 0e08bb9d3..b4a63611a 100644 --- a/vcd/resource_vcd_vm_internal_disk_test.go +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -141,6 +141,14 @@ func TestAccVcdVmInternalDisk(t *testing.T) { resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "allow_vm_reboot", "true"), ), }, + resource.TestStep{ + ResourceName: "vcd_vm_internal_disk." + TestAccVcdVdc + "-import", + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: importStateIdVmObject(testConfig, vapp.VApp.Name, vm.VM.Name, "3001"), + // These fields can't be retrieved + ImportStateVerifyIgnore: []string{"org", "vdc", "allow_vm_reboot", "thin_provisioned"}, + }, /*resource.TestStep{ Config: configText_update2, Check: resource.ComposeTestCheckFunc(resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "size_in_mb", diskSize), From f36ba2c853e66bb8ddab57405a43ee0f8968a925 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 19 Dec 2019 15:34:54 +0200 Subject: [PATCH 26/80] Add VM status handling and changes according latest govcd Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_edgegateway.go | 2 +- vcd/resource_vcd_vm_internal_disk.go | 9 ++++++--- vcd/resource_vcd_vm_internal_disk_test.go | 8 ++++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/vcd/resource_vcd_edgegateway.go b/vcd/resource_vcd_edgegateway.go index 6e53eb79a..114e47bca 100644 --- a/vcd/resource_vcd_edgegateway.go +++ b/vcd/resource_vcd_edgegateway.go @@ -194,7 +194,7 @@ func resourceVcdEdgeGatewayCreate(d *schema.ResourceData, meta interface{}) erro } // In version 9.7+ the advanced property is true by default - if vcdClient.APIVCDMaxVersionIs(">= 32.0") { + if vcdClient.Client.APIVCDMaxVersionIs(">= 32.0") { if !gwCreation.AdvancedNetworkingEnabled { return fmt.Errorf("'advanced' property for vCD 9.7+ must be set to 'true'") } diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index 1a468fa47..421b06d62 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -112,6 +112,8 @@ var internalDiskBusTypesFromValues = map[string]string{ "6": "sata", } +var vmStatusBefore string + // resourceVmInternalDiskCreate creates an internal disk for VM func resourceVmInternalDiskCreate(d *schema.ResourceData, meta interface{}) error { vcdClient := meta.(*VCDClient) @@ -181,7 +183,7 @@ func powerOnIfNeeded(d *schema.ResourceData, vm *govcd.VM) error { return fmt.Errorf("error getting VM status before ensuring it is powered on: %s", err) } - if vmStatus != "POWERED_ON" && d.Get("bus_type").(string) == "ide" && d.Get("allow_vm_reboot").(bool) { + if vmStatusBefore == "POWERED_ON" && vmStatus != "POWERED_ON" && d.Get("bus_type").(string) == "ide" && d.Get("allow_vm_reboot").(bool) { log.Printf("[DEBUG] Powering on VM %s after adding internal disk.", vm.VM.Name) task, err := vm.PowerOn() @@ -199,8 +201,9 @@ func powerOnIfNeeded(d *schema.ResourceData, vm *govcd.VM) error { func powerOffIfNeeded(d *schema.ResourceData, vm *govcd.VM) error { vmStatus, err := vm.GetStatus() if err != nil { - return fmt.Errorf("error getting VM status before ensuring it is powered on: %s", err) + return fmt.Errorf("error getting VM status before ensuring it is powered off: %s", err) } + vmStatusBefore = vmStatus if vmStatus != "POWERED_OFF" && d.Get("bus_type").(string) == "ide" && d.Get("allow_vm_reboot").(bool) { log.Printf("[DEBUG] Powering off VM %s for adding/updating internal disk.", vm.VM.Name) @@ -234,7 +237,7 @@ func resourceVmInternalDiskDelete(d *schema.ResourceData, m interface{}) error { return err } - err = vm.DeleteInternalDiskById(d.Id()) + err = vm.DeleteInternalDisk(d.Id()) if err != nil { return fmt.Errorf("[Error] failed to delete internal disk: %s", err) } diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go index b4a63611a..e634fcdbd 100644 --- a/vcd/resource_vcd_vm_internal_disk_test.go +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -46,6 +46,14 @@ func TestAccVcdVmInternalDisk(t *testing.T) { return } + // for test to run correctly VM has to be power on + task, err := vm.PowerOn() + if err != nil { + t.Skip(fmt.Sprintf("Error powering up vm %s", err)) + return + } + _ = task.WaitTaskCompletion() + storageProfile := testConfig.VCD.ProviderVdc.StorageProfile if *adminVdc.UsesFastProvisioning { // to avoid `Cannot use multiple storage profiles in a fast-provisioned VDC` we need to reuse VM storage profile From 4d2c5f4312f093f844fc7cdba8273fddcbeb20c9 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 19 Dec 2019 15:43:37 +0200 Subject: [PATCH 27/80] Bom govcd version Signed-off-by: Vaidotas Bauzys --- go.mod | 2 +- go.sum | 4 +- .../vmware/go-vcloud-director/v2/govcd/api.go | 115 +++++++++++++++-- .../go-vcloud-director/v2/govcd/api_vcd.go | 13 +- .../v2/govcd/api_vcd_versions.go | 57 ++++---- .../v2/govcd/edgegateway.go | 53 +++----- .../go-vcloud-director/v2/govcd/vapp.go | 4 +- .../vmware/go-vcloud-director/v2/govcd/vm.go | 56 ++++---- .../v2/types/v56/constants.go | 2 + .../v2/types/v56/nsxv_types.go | 122 ++++++++++++++++++ .../go-vcloud-director/v2/types/v56/types.go | 58 +-------- .../go-vcloud-director/v2/util/logging.go | 2 +- vendor/modules.txt | 2 +- 13 files changed, 327 insertions(+), 163 deletions(-) diff --git a/go.mod b/go.mod index ff9e358c8..7b21709c6 100644 --- a/go.mod +++ b/go.mod @@ -8,4 +8,4 @@ require ( github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 ) -replace github.com/vmware/go-vcloud-director/v2 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205150825-aeb30347581d +replace github.com/vmware/go-vcloud-director/v2 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191219102849-f40088ca976a diff --git a/go.sum b/go.sum index d1196ab92..2cfc0855d 100644 --- a/go.sum +++ b/go.sum @@ -197,8 +197,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205150825-aeb30347581d h1:RyfWNunUDWXkIhX/FOUUBGOdRqoRUaLGj5Dzo/K88io= -github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205150825-aeb30347581d/go.mod h1:zjondbeyTfZlzhwxOzyF4K2sWWYgMEv5H91dp5dPbU8= +github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191219102849-f40088ca976a h1:/Qr8JglTFTyqSPgVgENDDhAUFIQTD0bY+9vEYQMLE38= +github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191219102849-f40088ca976a/go.mod h1:zjondbeyTfZlzhwxOzyF4K2sWWYgMEv5H91dp5dPbU8= github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU= diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api.go index bfcdf8077..6ecb29dc1 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api.go @@ -34,6 +34,8 @@ type Client struct { // where vCloud director may take time to respond and retry mechanism is needed. // This must be >0 to avoid instant timeout errors. MaxRetryTimeout int + + supportedVersions SupportedVersions // Versions from /api/versions endpoint } // The header key used by default to set the authorization token. @@ -127,6 +129,17 @@ func ContainsNotFound(err error) bool { // NewRequestWitNotEncodedParams allows passing complex values params that shouldn't be encoded like for queries. e.g. /query?filter=name=foo func (cli *Client) NewRequestWitNotEncodedParams(params map[string]string, notEncodedParams map[string]string, method string, reqUrl url.URL, body io.Reader) *http.Request { + return cli.NewRequestWitNotEncodedParamsWithApiVersion(params, notEncodedParams, method, reqUrl, body, cli.APIVersion) +} + +// NewRequestWitNotEncodedParamsWithApiVersion allows passing complex values params that shouldn't be encoded like for queries. e.g. /query?filter=name=foo +// * params - request parameters +// * notEncodedParams - request parameters which will be added not encoded +// * method - request type +// * reqUrl - request url +// * body - request body +// * apiVersion - provided Api version overrides default Api version value used in request parameter +func (cli *Client) NewRequestWitNotEncodedParamsWithApiVersion(params map[string]string, notEncodedParams map[string]string, method string, reqUrl url.URL, body io.Reader, apiVersion string) *http.Request { reqValues := url.Values{} // Build up our request parameters @@ -152,7 +165,7 @@ func (cli *Client) NewRequestWitNotEncodedParams(params map[string]string, notEn // Add the authorization header req.Header.Add(cli.VCDAuthHeader, cli.VCDToken) // Add the Accept header for VCD - req.Header.Add("Accept", "application/*+xml;version="+cli.APIVersion) + req.Header.Add("Accept", "application/*+xml;version="+apiVersion) } // Avoids passing data if the logging of requests is disabled @@ -188,6 +201,12 @@ func (cli *Client) NewRequest(params map[string]string, method string, reqUrl ur return cli.NewRequestWitNotEncodedParams(params, nil, method, reqUrl, body) } +// NewRequestWithApiVersion creates a new HTTP request and applies necessary auth headers if set. +// Allows to override default request API Version +func (cli *Client) NewRequestWithApiVersion(params map[string]string, method string, reqUrl url.URL, body io.Reader, apiVersion string) *http.Request { + return cli.NewRequestWitNotEncodedParamsWithApiVersion(params, nil, method, reqUrl, body, apiVersion) +} + // ParseErr takes an error XML resp, error interface for unmarshaling and returns a single string for // use in error messages. func ParseErr(resp *http.Response, errType error) error { @@ -284,12 +303,36 @@ func checkRespWithErrType(resp *http.Response, err, errType error) (*http.Respon // payload - XML struct which will be marshalled and added as body/payload // E.g. client.ExecuteTaskRequest(updateDiskLink.HREF, http.MethodPut, updateDiskLink.Type, "error updating disk: %s", xmlPayload) func (client *Client) ExecuteTaskRequest(pathURL, requestType, contentType, errorMessage string, payload interface{}) (Task, error) { + return client.executeTaskRequest(pathURL, requestType, contentType, errorMessage, payload, client.APIVersion) +} + +// Helper function creates request, runs it, checks response and parses task from response. +// pathURL - request URL +// requestType - HTTP method type +// contentType - value to set for "Content-Type" +// errorMessage - error message to return when error happens +// payload - XML struct which will be marshalled and added as body/payload +// apiVersion - api version which will be used in request +// E.g. client.ExecuteTaskRequest(updateDiskLink.HREF, http.MethodPut, updateDiskLink.Type, "error updating disk: %s", xmlPayload) +func (client *Client) ExecuteTaskRequestWithApiVersion(pathURL, requestType, contentType, errorMessage string, payload interface{}, apiVersion string) (Task, error) { + return client.executeTaskRequest(pathURL, requestType, contentType, errorMessage, payload, apiVersion) +} + +// Helper function creates request, runs it, checks response and parses task from response. +// pathURL - request URL +// requestType - HTTP method type +// contentType - value to set for "Content-Type" +// errorMessage - error message to return when error happens +// payload - XML struct which will be marshalled and added as body/payload +// apiVersion - api version which will be used in request +// E.g. client.ExecuteTaskRequest(updateDiskLink.HREF, http.MethodPut, updateDiskLink.Type, "error updating disk: %s", xmlPayload) +func (client *Client) executeTaskRequest(pathURL, requestType, contentType, errorMessage string, payload interface{}, apiVersion string) (Task, error) { if !isMessageWithPlaceHolder(errorMessage) { return Task{}, fmt.Errorf("error message has to include place holder for error") } - resp, err := executeRequest(pathURL, requestType, contentType, payload, client) + resp, err := executeRequestWithApiVersion(pathURL, requestType, contentType, payload, client, apiVersion) if err != nil { return Task{}, fmt.Errorf(errorMessage, err) } @@ -317,12 +360,36 @@ func (client *Client) ExecuteTaskRequest(pathURL, requestType, contentType, erro // payload - XML struct which will be marshalled and added as body/payload // E.g. client.ExecuteRequestWithoutResponse(catalogItemHREF.String(), http.MethodDelete, "", "error deleting Catalog item: %s", nil) func (client *Client) ExecuteRequestWithoutResponse(pathURL, requestType, contentType, errorMessage string, payload interface{}) error { + return client.executeRequestWithoutResponse(pathURL, requestType, contentType, errorMessage, payload, client.APIVersion) +} + +// Helper function creates request, runs it, checks response and do not expect any values from it. +// pathURL - request URL +// requestType - HTTP method type +// contentType - value to set for "Content-Type" +// errorMessage - error message to return when error happens +// payload - XML struct which will be marshalled and added as body/payload +// apiVersion - api version which will be used in request +// E.g. client.ExecuteRequestWithoutResponse(catalogItemHREF.String(), http.MethodDelete, "", "error deleting Catalog item: %s", nil) +func (client *Client) ExecuteRequestWithoutResponseWithApiVersion(pathURL, requestType, contentType, errorMessage string, payload interface{}, apiVersion string) error { + return client.executeRequestWithoutResponse(pathURL, requestType, contentType, errorMessage, payload, apiVersion) +} + +// Helper function creates request, runs it, checks response and do not expect any values from it. +// pathURL - request URL +// requestType - HTTP method type +// contentType - value to set for "Content-Type" +// errorMessage - error message to return when error happens +// payload - XML struct which will be marshalled and added as body/payload +// apiVersion - api version which will be used in request +// E.g. client.ExecuteRequestWithoutResponse(catalogItemHREF.String(), http.MethodDelete, "", "error deleting Catalog item: %s", nil) +func (client *Client) executeRequestWithoutResponse(pathURL, requestType, contentType, errorMessage string, payload interface{}, apiVersion string) error { if !isMessageWithPlaceHolder(errorMessage) { return fmt.Errorf("error message has to include place holder for error") } - resp, err := executeRequest(pathURL, requestType, contentType, payload, client) + resp, err := executeRequestWithApiVersion(pathURL, requestType, contentType, payload, client, apiVersion) if err != nil { return fmt.Errorf(errorMessage, err) } @@ -350,12 +417,40 @@ func (client *Client) ExecuteRequestWithoutResponse(pathURL, requestType, conten // E.g. unmarshalledAdminOrg := &types.AdminOrg{} // client.ExecuteRequest(adminOrg.AdminOrg.HREF, http.MethodGet, "", "error refreshing organization: %s", nil, unmarshalledAdminOrg) func (client *Client) ExecuteRequest(pathURL, requestType, contentType, errorMessage string, payload, out interface{}) (*http.Response, error) { + return client.executeRequest(pathURL, requestType, contentType, errorMessage, payload, out, client.APIVersion) +} + +// Helper function creates request, runs it, check responses and parses out interface from response. +// pathURL - request URL +// requestType - HTTP method type +// contentType - value to set for "Content-Type" +// errorMessage - error message to return when error happens +// payload - XML struct which will be marshalled and added as body/payload +// out - structure to be used for unmarshalling xml +// apiVersion - api version which will be used in request +// E.g. unmarshalledAdminOrg := &types.AdminOrg{} +// client.ExecuteRequest(adminOrg.AdminOrg.HREF, http.MethodGet, "", "error refreshing organization: %s", nil, unmarshalledAdminOrg) +func (client *Client) ExecuteRequestWithApiVersion(pathURL, requestType, contentType, errorMessage string, payload, out interface{}, apiVersion string) (*http.Response, error) { + return client.executeRequest(pathURL, requestType, contentType, errorMessage, payload, out, apiVersion) +} + +// Helper function creates request, runs it, check responses and parses out interface from response. +// pathURL - request URL +// requestType - HTTP method type +// contentType - value to set for "Content-Type" +// errorMessage - error message to return when error happens +// payload - XML struct which will be marshalled and added as body/payload +// out - structure to be used for unmarshalling xml +// apiVersion - api version which will be used in request +// E.g. unmarshalledAdminOrg := &types.AdminOrg{} +// client.ExecuteRequest(adminOrg.AdminOrg.HREF, http.MethodGet, "", "error refreshing organization: %s", nil, unmarshalledAdminOrg) +func (client *Client) executeRequest(pathURL, requestType, contentType, errorMessage string, payload, out interface{}, apiVersion string) (*http.Response, error) { if !isMessageWithPlaceHolder(errorMessage) { return &http.Response{}, fmt.Errorf("error message has to include place holder for error") } - resp, err := executeRequest(pathURL, requestType, contentType, payload, client) + resp, err := executeRequestWithApiVersion(pathURL, requestType, contentType, payload, client, apiVersion) if err != nil { return resp, fmt.Errorf(errorMessage, err) } @@ -390,7 +485,7 @@ func (client *Client) ExecuteParamRequestWithCustomError(pathURL string, params return &http.Response{}, fmt.Errorf("error message has to include place holder for error") } - resp, err := executeRequestCustomErr(pathURL, params, requestType, contentType, payload, client, errType) + resp, err := executeRequestCustomErr(pathURL, params, requestType, contentType, payload, client, errType, client.APIVersion) if err != nil { return &http.Response{}, fmt.Errorf(errorMessage, err) } @@ -413,12 +508,12 @@ func (client *Client) ExecuteParamRequestWithCustomError(pathURL string, params } // executeRequest does executeRequestCustomErr and checks for vCD errors in API response -func executeRequest(pathURL, requestType, contentType string, payload interface{}, client *Client) (*http.Response, error) { - return executeRequestCustomErr(pathURL, map[string]string{}, requestType, contentType, payload, client, &types.Error{}) +func executeRequestWithApiVersion(pathURL, requestType, contentType string, payload interface{}, client *Client, apiVersion string) (*http.Response, error) { + return executeRequestCustomErr(pathURL, map[string]string{}, requestType, contentType, payload, client, &types.Error{}, apiVersion) } // executeRequestCustomErr performs request and unmarshals API error to errType if not 2xx status was returned -func executeRequestCustomErr(pathURL string, params map[string]string, requestType, contentType string, payload interface{}, client *Client, errType error) (*http.Response, error) { +func executeRequestCustomErr(pathURL string, params map[string]string, requestType, contentType string, payload interface{}, client *Client, errType error, apiVersion string) (*http.Response, error) { url, _ := url.ParseRequestURI(pathURL) var req *http.Request @@ -431,10 +526,10 @@ func executeRequestCustomErr(pathURL string, params map[string]string, requestTy } body := bytes.NewBufferString(xml.Header + string(marshaledXml)) - req = client.NewRequest(params, requestType, *url, body) + req = client.NewRequestWithApiVersion(params, requestType, *url, body, apiVersion) default: - req = client.NewRequest(params, requestType, *url, nil) + req = client.NewRequestWithApiVersion(params, requestType, *url, nil, apiVersion) } if contentType != "" { diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd.go index 752c0b6e6..8c30f119f 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd.go @@ -21,21 +21,20 @@ import ( type VCDClientOption func(*VCDClient) error type VCDClient struct { - Client Client // Client for the underlying VCD instance - sessionHREF url.URL // HREF for the session API - QueryHREF url.URL // HREF for the query API - Mutex sync.Mutex - supportedVersions SupportedVersions // Versions from /api/versions endpoint + Client Client // Client for the underlying VCD instance + sessionHREF url.URL // HREF for the session API + QueryHREF url.URL // HREF for the query API + Mutex sync.Mutex } func (vcdCli *VCDClient) vcdloginurl() error { - if err := vcdCli.validateAPIVersion(); err != nil { + if err := vcdCli.Client.validateAPIVersion(); err != nil { return fmt.Errorf("could not find valid version for login: %s", err) } // find login address matching the API version var neededVersion VersionInfo - for _, versionInfo := range vcdCli.supportedVersions.VersionInfos { + for _, versionInfo := range vcdCli.Client.supportedVersions.VersionInfos { if versionInfo.Version == vcdCli.Client.APIVersion { neededVersion = versionInfo break diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd_versions.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd_versions.go index 6e3d398e5..f853517f3 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd_versions.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd_versions.go @@ -36,21 +36,21 @@ type SupportedVersions struct { // Format: ">= 27.0, < 32.0", ">= 30.0", "= 27.0" // // vCD version mapping to API version support https://code.vmware.com/doc/preview?id=8072 -func (vcdCli *VCDClient) APIVCDMaxVersionIs(versionConstraint string) bool { - err := vcdCli.vcdFetchSupportedVersions() +func (cli *Client) APIVCDMaxVersionIs(versionConstraint string) bool { + err := cli.vcdFetchSupportedVersions() if err != nil { util.Logger.Printf("[ERROR] could not retrieve supported versions: %s", err) return false } util.Logger.Printf("[TRACE] checking max API version against constraints '%s'", versionConstraint) - maxVersion, err := vcdCli.maxSupportedVersion() + maxVersion, err := cli.maxSupportedVersion() if err != nil { util.Logger.Printf("[ERROR] unable to find max supported version : %s", err) return false } - isSupported, err := vcdCli.apiVersionMatchesConstraint(maxVersion, versionConstraint) + isSupported, err := cli.apiVersionMatchesConstraint(maxVersion, versionConstraint) if err != nil { util.Logger.Printf("[ERROR] unable to find max supported version : %s", err) return false @@ -66,11 +66,11 @@ func (vcdCli *VCDClient) APIVCDMaxVersionIs(versionConstraint string) bool { // Format: ">= 27.0, < 32.0", ">= 30.0", "= 27.0" // // vCD version mapping to API version support https://code.vmware.com/doc/preview?id=8072 -func (vcdCli *VCDClient) APIClientVersionIs(versionConstraint string) bool { +func (cli *Client) APIClientVersionIs(versionConstraint string) bool { util.Logger.Printf("[TRACE] checking current API version against constraints '%s'", versionConstraint) - isSupported, err := vcdCli.apiVersionMatchesConstraint(vcdCli.Client.APIVersion, versionConstraint) + isSupported, err := cli.apiVersionMatchesConstraint(cli.APIVersion, versionConstraint) if err != nil { util.Logger.Printf("[ERROR] unable to find cur supported version : %s", err) return false @@ -82,30 +82,30 @@ func (vcdCli *VCDClient) APIClientVersionIs(versionConstraint string) bool { // vcdFetchSupportedVersions retrieves list of supported versions from // /api/versions endpoint and stores them in VCDClient for future uses. // It only does it once. -func (vcdCli *VCDClient) vcdFetchSupportedVersions() error { +func (cli *Client) vcdFetchSupportedVersions() error { // Only fetch /versions if it is not stored already - numVersions := len(vcdCli.supportedVersions.VersionInfos) + numVersions := len(cli.supportedVersions.VersionInfos) if numVersions > 0 { util.Logger.Printf("[TRACE] skipping fetch of versions because %d are stored", numVersions) return nil } - apiEndpoint := vcdCli.Client.VCDHREF + apiEndpoint := cli.VCDHREF apiEndpoint.Path += "/versions" suppVersions := new(SupportedVersions) - _, err := vcdCli.Client.ExecuteRequest(apiEndpoint.String(), http.MethodGet, + _, err := cli.ExecuteRequest(apiEndpoint.String(), http.MethodGet, "", "error fetching versions: %s", nil, suppVersions) - vcdCli.supportedVersions = *suppVersions + cli.supportedVersions = *suppVersions return err } // maxSupportedVersion parses supported version list and returns the highest version in string format. -func (vcdCli *VCDClient) maxSupportedVersion() (string, error) { - versions := make([]*semver.Version, len(vcdCli.supportedVersions.VersionInfos)) - for index, versionInfo := range vcdCli.supportedVersions.VersionInfos { +func (cli *Client) maxSupportedVersion() (string, error) { + versions := make([]*semver.Version, len(cli.supportedVersions.VersionInfos)) + for index, versionInfo := range cli.supportedVersions.VersionInfos { version, _ := semver.NewVersion(versionInfo.Version) versions[index] = version } @@ -124,15 +124,15 @@ func (vcdCli *VCDClient) maxSupportedVersion() (string, error) { // vcdCheckSupportedVersion checks if there is at least one specified version exactly matching listed ones. // Format example "27.0" -func (vcdCli *VCDClient) vcdCheckSupportedVersion(version string) (bool, error) { - return vcdCli.checkSupportedVersionConstraint(fmt.Sprintf("= %s", version)) +func (cli *Client) vcdCheckSupportedVersion(version string) (bool, error) { + return cli.checkSupportedVersionConstraint(fmt.Sprintf("= %s", version)) } // Checks if there is at least one specified version matching the list returned by vCD. // Constraint format can be in format ">= 27.0, < 32",">= 30" ,"= 27.0". -func (vcdCli *VCDClient) checkSupportedVersionConstraint(versionConstraint string) (bool, error) { - for _, versionInfo := range vcdCli.supportedVersions.VersionInfos { - versionMatch, err := vcdCli.apiVersionMatchesConstraint(versionInfo.Version, versionConstraint) +func (cli *Client) checkSupportedVersionConstraint(versionConstraint string) (bool, error) { + for _, versionInfo := range cli.supportedVersions.VersionInfos { + versionMatch, err := cli.apiVersionMatchesConstraint(versionInfo.Version, versionConstraint) if err != nil { return false, fmt.Errorf("cannot match version: %s", err) } @@ -144,7 +144,7 @@ func (vcdCli *VCDClient) checkSupportedVersionConstraint(versionConstraint strin return false, fmt.Errorf("version %s is not supported", versionConstraint) } -func (vcdCli *VCDClient) apiVersionMatchesConstraint(version, versionConstraint string) (bool, error) { +func (cli *Client) apiVersionMatchesConstraint(version, versionConstraint string) (bool, error) { checkVer, err := semver.NewVersion(version) if err != nil { @@ -165,16 +165,25 @@ func (vcdCli *VCDClient) apiVersionMatchesConstraint(version, versionConstraint } // validateAPIVersion fetches API versions -func (vcdCli *VCDClient) validateAPIVersion() error { - err := vcdCli.vcdFetchSupportedVersions() +func (cli *Client) validateAPIVersion() error { + err := cli.vcdFetchSupportedVersions() if err != nil { return fmt.Errorf("could not retrieve supported versions: %s", err) } // Check if version is supported - if ok, err := vcdCli.vcdCheckSupportedVersion(vcdCli.Client.APIVersion); !ok || err != nil { - return fmt.Errorf("API version %s is not supported: %s", vcdCli.Client.APIVersion, err) + if ok, err := cli.vcdCheckSupportedVersion(cli.APIVersion); !ok || err != nil { + return fmt.Errorf("API version %s is not supported: %s", cli.APIVersion, err) } return nil } + +// GetDefaultAPIVersionOrLaterThan32 returns default version or 32 if it is connected to version vCD 9.7 or later +func (cli *Client) GetDefaultAPIVersionOrLaterThan32() string { + apiVersion := cli.APIVersion + if cli.APIVCDMaxVersionIs(">= 32.0") { + apiVersion = "32.0" + } + return apiVersion +} diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go index 02f1adef8..c28e01922 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go @@ -1254,19 +1254,19 @@ func validateUpdateLBGeneralParams(logLevel string) error { return nil } -// getVnics retrieves a structure of type EdgeGatewayVnics which contains network interfaces -// available in Edge Gateway -func (egw *EdgeGateway) getVnics() (*types.EdgeGatewayVnics, error) { +// getVdcNetworks retrieves a structure of type EdgeGatewayInterfaces which contains network +// interfaces available in Edge Gateway (uses "/vdcNetworks" endpoint) +func (egw *EdgeGateway) getVdcNetworks() (*types.EdgeGatewayInterfaces, error) { if !egw.HasAdvancedNetworking() { return nil, fmt.Errorf("only advanced edge gateway supports vNics") } - httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeVnicConfig) + httpPath, err := egw.buildProxiedEdgeEndpointURL("/vdcNetworks") if err != nil { return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) } - vnicConfig := &types.EdgeGatewayVnics{} + vnicConfig := &types.EdgeGatewayInterfaces{} _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, "unable to edge gateway vnic configuration: %s", nil, vnicConfig) @@ -1281,7 +1281,7 @@ func (egw *EdgeGateway) getVnics() (*types.EdgeGatewayVnics, error) { // networkType one of: 'internal', 'uplink', 'trunk', 'subinterface' // networkName cannot be empty func (egw *EdgeGateway) GetVnicIndexByNetworkNameAndType(networkName, networkType string) (*int, error) { - vnics, err := egw.getVnics() + vnics, err := egw.getVdcNetworks() if err != nil { return nil, fmt.Errorf("cannot retrieve vNic configuration: %s", err) } @@ -1296,7 +1296,7 @@ func (egw *EdgeGateway) GetVnicIndexByNetworkNameAndType(networkName, networkTyp // Warning: this function assumes that there are no duplicate network names attached. If it is so // this function will return the first network func (egw *EdgeGateway) GetAnyVnicIndexByNetworkName(networkName string) (*int, string, error) { - vnics, err := egw.getVnics() + vnics, err := egw.getVdcNetworks() if err != nil { return nil, "", fmt.Errorf("cannot retrieve vNic configuration: %s", err) } @@ -1325,7 +1325,7 @@ func (egw *EdgeGateway) GetAnyVnicIndexByNetworkName(networkName string) (*int, // GetNetworkNameAndTypeByVnicIndex returns network name and network type for given vNic index // returned networkType can be one of: 'internal', 'uplink', 'trunk', 'subinterface' func (egw *EdgeGateway) GetNetworkNameAndTypeByVnicIndex(vNicIndex int) (string, string, error) { - vnics, err := egw.getVnics() + vnics, err := egw.getVdcNetworks() if err != nil { return "", "", fmt.Errorf("cannot retrieve vNic configuration: %s", err) } @@ -1333,7 +1333,7 @@ func (egw *EdgeGateway) GetNetworkNameAndTypeByVnicIndex(vNicIndex int) (string, } // getVnicIndexByNetworkNameAndType is wrapped and used by public function GetVnicIndexByNetworkNameAndType -func getVnicIndexByNetworkNameAndType(networkName, networkType string, vnics *types.EdgeGatewayVnics) (*int, error) { +func getVnicIndexByNetworkNameAndType(networkName, networkType string, vnics *types.EdgeGatewayInterfaces) (*int, error) { if networkName == "" { return nil, fmt.Errorf("network name cannot be empty") } @@ -1347,22 +1347,14 @@ func getVnicIndexByNetworkNameAndType(networkName, networkType string, vnics *ty var foundIndex *int foundCount := 0 - for _, vnic := range vnics.Vnic { - // Look for matching portgroup name and network type - if networkType != types.EdgeGatewayVnicTypeSubinterface && vnic.PortgroupName == networkName && vnic.Type == networkType { + for _, vnic := range vnics.EdgeInterface { + // Look for matching portgroup name and network type. If the PortgroupName is not empty - + // check that it contains network name as well. + if vnic.Name == networkName && vnic.Type == networkType && + (vnic.PortgroupName == networkName || vnic.PortgroupName == "") { foundIndex = vnic.Index foundCount++ } - - // if looking for subinterface - check if they are defined and search for logicalSwitchName - if networkType == types.EdgeGatewayVnicTypeSubinterface && len(vnic.SubInterfaces.SubInterface) > 0 { - for _, subInterface := range vnic.SubInterfaces.SubInterface { - if subInterface.LogicalSwitchName == networkName { - foundIndex = subInterface.Index - foundCount++ - } - } - } } if foundCount > 1 { @@ -1378,7 +1370,7 @@ func getVnicIndexByNetworkNameAndType(networkName, networkType string, vnics *ty } // getNetworkNameAndTypeByVnicIndex looks up network type and name in list of edge gateway interfaces -func getNetworkNameAndTypeByVnicIndex(vNicIndex int, vnics *types.EdgeGatewayVnics) (string, string, error) { +func getNetworkNameAndTypeByVnicIndex(vNicIndex int, vnics *types.EdgeGatewayInterfaces) (string, string, error) { if vNicIndex < 0 { return "", "", fmt.Errorf("vNic index cannot be negative") } @@ -1386,23 +1378,12 @@ func getNetworkNameAndTypeByVnicIndex(vNicIndex int, vnics *types.EdgeGatewayVni foundCount := 0 var networkName, networkType string - for _, vnic := range vnics.Vnic { + for _, vnic := range vnics.EdgeInterface { if vnic.Index != nil && *vnic.Index == vNicIndex { foundCount++ - networkName = vnic.PortgroupName + networkName = vnic.Name networkType = vnic.Type } - - // Search inside "subinterface tree" - if vnic.Type == types.EdgeGatewayVnicTypeTrunk && len(vnic.SubInterfaces.SubInterface) > 0 { - for _, subInterface := range vnic.SubInterfaces.SubInterface { - if subInterface.Index != nil && *subInterface.Index == vNicIndex { - foundCount++ - networkName = subInterface.LogicalSwitchName - networkType = types.EdgeGatewayVnicTypeSubinterface - } - } - } } if foundCount > 1 { diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vapp.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vapp.go index e40937fc2..1431e5446 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vapp.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vapp.go @@ -892,8 +892,8 @@ func (client *Client) GetVMByHref(vmHref string) (*VM, error) { newVm := NewVM(client) - _, err := client.ExecuteRequest(vmHref, http.MethodGet, - "", "error retrieving vm: %s", nil, newVm.VM) + _, err := client.ExecuteRequestWithApiVersion(vmHref, http.MethodGet, + "", "error retrieving vm: %s", nil, newVm.VM, client.GetDefaultAPIVersionOrLaterThan32()) if err != nil { diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go index cd6c5ca4e..a488f3b36 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go @@ -71,8 +71,8 @@ func (vm *VM) Refresh() error { // elements in slices. vm.VM = &types.VM{} - _, err := vm.client.ExecuteRequest(refreshUrl, http.MethodGet, - "", "error refreshing VM: %s", nil, vm.VM) + _, err := vm.client.ExecuteRequestWithApiVersion(refreshUrl, http.MethodGet, + "", "error refreshing VM: %s", nil, vm.VM, vm.client.GetDefaultAPIVersionOrLaterThan32()) // The request was successful return err @@ -861,7 +861,7 @@ func (vm *VM) AddInternalDisk(diskData *types.DiskSettings) (string, error) { return "", fmt.Errorf("error refreshing VM: %s", err) } - err = vm.validateInternalDiskInput(diskData) + err = vm.validateInternalDiskInput(diskData, vm.VM.Name, vm.VM.ID) if err != nil { return "", err } @@ -891,41 +891,45 @@ func (vm *VM) AddInternalDisk(diskData *types.DiskSettings) (string, error) { return "", fmt.Errorf("created disk wasn't in list of returned VM internal disks") } -func (vm *VM) validateInternalDiskInput(diskData *types.DiskSettings) error { +func (vm *VM) validateInternalDiskInput(diskData *types.DiskSettings, vmName, vmId string) error { if diskData.AdapterType == "" { - return fmt.Errorf("disk settings missing required field: adapter type") + return fmt.Errorf("[VM %s Id %s] disk settings missing required field: adapter type", vmName, vmId) } if diskData.BusNumber < 0 { - return fmt.Errorf("disk settings bus number has to be 0 or higher") + return fmt.Errorf("[VM %s Id %s] disk settings bus number has to be 0 or higher", vmName, vmId) } if diskData.UnitNumber < 0 { - return fmt.Errorf("disk settings unit number has to be 0 or higher") + return fmt.Errorf("[VM %s Id %s] disk settings unit number has to be 0 or higher", vmName, vmId) } if diskData.SizeMb < int64(0) { - return fmt.Errorf("disk settings size MB has to be 0 or higher") + return fmt.Errorf("[VM %s Id %s] disk settings size MB has to be 0 or higher", vmName, vmId) } if diskData.Iops != nil && *diskData.Iops < int64(0) { - return fmt.Errorf("disk settings iops has to be 0 or higher") + return fmt.Errorf("[VM %s Id %s] disk settings iops has to be 0 or higher", vmName, vmId) } if diskData.ThinProvisioned == nil { - return fmt.Errorf("disk settings missing required field: thin provisioned") + return fmt.Errorf("[VM %s Id %s] disk settings missing required field: thin provisioned", vmName, vmId) } if diskData.StorageProfile == nil { - return fmt.Errorf("disk settings missing required field: storage profile") + return fmt.Errorf("[VM %s Id %s]disk settings missing required field: storage profile", vmName, vmId) } return nil } -// GetInternalDiskById returns a valid *types.DiskSettings if it exists. +// GetInternalDiskById returns a *types.DiskSettings if one exists. // If it doesn't, returns nil and ErrorEntityNotFound or other err. func (vm *VM) GetInternalDiskById(diskId string, refresh bool) (*types.DiskSettings, error) { + if diskId == "" { + return nil, fmt.Errorf("cannot get internal disk - provided disk Id is empty") + } + if refresh { err := vm.Refresh() if err != nil { @@ -933,10 +937,6 @@ func (vm *VM) GetInternalDiskById(diskId string, refresh bool) (*types.DiskSetti } } - if diskId == "" { - return nil, fmt.Errorf("cannot get internal disk - provided disk Id is empty") - } - if vm.VM.VmSpecSection.DiskSection == nil || vm.VM.VmSpecSection.DiskSection.DiskSettings == nil || len(vm.VM.VmSpecSection.DiskSection.DiskSettings) == 0 { return nil, fmt.Errorf("cannot get internal disk - VM doesn't have internal disks") @@ -951,9 +951,9 @@ func (vm *VM) GetInternalDiskById(diskId string, refresh bool) (*types.DiskSetti return nil, ErrorEntityNotFound } -// DeleteInternalDiskById delete disk using provided disk ID. +// DeleteInternalDisk delete disk using provided disk ID. // Runs synchronously, VM is ready for another operation after this function returns. -func (vm *VM) DeleteInternalDiskById(diskId string) error { +func (vm *VM) DeleteInternalDisk(diskId string) error { err := vm.Refresh() if err != nil { return fmt.Errorf("error refreshing VM: %s", err) @@ -975,7 +975,7 @@ func (vm *VM) DeleteInternalDiskById(diskId string) error { return ErrorEntityNotFound } - // remove disk in slice + // remove disk from slice diskSettings = append(diskSettings[:diskPlacement], diskSettings[diskPlacement+1:]...) vmSpecSection := vm.VM.VmSpecSection @@ -990,8 +990,9 @@ func (vm *VM) DeleteInternalDiskById(diskId string) error { } // UpdateInternalDisks applies disks configuration for the VM. -// types.VmSpecSection requires consist of all disk entities which exist and not updated, -// as also new ones or changed ones. Returns new disk ID and error. +// types.VmSpecSection has to have all internal disk state. Disks which don't match provided ones in types.VmSpecSection +// will be deleted. Matched internal disk will be updated. New internal disk description found +// in types.VmSpecSection will be created. Returns updated types.VmSpecSection and error. // Runs synchronously, VM is ready for another operation after this function returns. func (vm *VM) UpdateInternalDisks(disksSettingToUpdate *types.VmSpecSection) (*types.VmSpecSection, error) { if vm.VM.HREF == "" { @@ -1013,9 +1014,11 @@ func (vm *VM) UpdateInternalDisks(disksSettingToUpdate *types.VmSpecSection) (*t return vm.VM.VmSpecSection, nil } -// UpdateInternalDisksAsync applies disks configuration and return task or err -// types.VmSpecSection requires consist of all disk entities which exist and not updated, -// as also new ones or changed ones. +// UpdateInternalDisksAsync applies disks configuration for the VM. +// types.VmSpecSection has to have all internal disk state. Disks which don't match provided ones in types.VmSpecSection +// will be deleted. Matched internal disk will be updated. New internal disk description found +// in types.VmSpecSection will be created. +// Returns Task and error. func (vm *VM) UpdateInternalDisksAsync(disksSettingToUpdate *types.VmSpecSection) (Task, error) { if vm.VM.HREF == "" { return Task{}, fmt.Errorf("cannot update disks, VM HREF is unset") @@ -1025,12 +1028,13 @@ func (vm *VM) UpdateInternalDisksAsync(disksSettingToUpdate *types.VmSpecSection vmSpecSectionModified := true disksSettingToUpdate.Modified = &vmSpecSectionModified - return vm.client.ExecuteTaskRequest(vm.VM.HREF+"/action/reconfigureVm", http.MethodPost, + return vm.client.ExecuteTaskRequestWithApiVersion(vm.VM.HREF+"/action/reconfigureVm", http.MethodPost, types.MimeVM, "error updating VM disks: %s", &types.VMDiskChange{ XMLName: xml.Name{}, Xmlns: types.XMLNamespaceVCloud, Ovf: types.XMLNamespaceOVF, Name: vm.VM.Name, VmSpecSection: disksSettingToUpdate, - }) + }, vm.client.GetDefaultAPIVersionOrLaterThan32()) + } diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go index 85bd1e94d..f5a1a1163 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go @@ -161,6 +161,8 @@ const ( EdgeFirewallPath = "/firewall/config" EdgeCreateFirewallPath = "/firewall/config/rules" EdgeVnicConfig = "/vnics" + EdgeVdcVnicConfig = "/vdcNetworks" + EdgeDhcpRelayPath = "/dhcp/config/relay" LbConfigPath = "/loadbalancer/config/" LbMonitorPath = "/loadbalancer/config/monitors/" LbServerPoolPath = "/loadbalancer/config/pools/" diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/nsxv_types.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/nsxv_types.go index 645306df3..5ff411ab1 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/nsxv_types.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/nsxv_types.go @@ -284,3 +284,125 @@ type EdgeIpSet struct { // EdgeIpSets is a slice of pointers to EdgeIpSet type EdgeIpSets []*EdgeIpSet + +// EdgeGatewayVnics is a data structure holding information of vNic configuration in NSX-V edge +// gateway using "/network/edges/edge_id/vnics" endpoint +type EdgeGatewayVnics struct { + XMLName xml.Name `xml:"vnics"` + Vnic []struct { + Label string `xml:"label"` + Name string `xml:"name"` + AddressGroups struct { + AddressGroup struct { + PrimaryAddress string `xml:"primaryAddress,omitempty"` + SecondaryAddresses struct { + IpAddress []string `xml:"ipAddress,omitempty"` + } `xml:"secondaryAddresses,omitempty"` + SubnetMask string `xml:"subnetMask,omitempty"` + SubnetPrefixLength string `xml:"subnetPrefixLength,omitempty"` + } `xml:"addressGroup,omitempty"` + } `xml:"addressGroups,omitempty"` + Mtu string `xml:"mtu,omitempty"` + Type string `xml:"type,omitempty"` + IsConnected string `xml:"isConnected,omitempty"` + Index *int `xml:"index"` + PortgroupId string `xml:"portgroupId,omitempty"` + PortgroupName string `xml:"portgroupName,omitempty"` + EnableProxyArp string `xml:"enableProxyArp,omitempty"` + EnableSendRedirects string `xml:"enableSendRedirects,omitempty"` + SubInterfaces struct { + SubInterface []struct { + IsConnected string `xml:"isConnected,omitempty"` + Label string `xml:"label,omitempty"` + Name string `xml:"name,omitempty"` + Index *int `xml:"index,omitempty"` + TunnelId string `xml:"tunnelId,omitempty"` + LogicalSwitchId string `xml:"logicalSwitchId,omitempty"` + LogicalSwitchName string `xml:"logicalSwitchName,omitempty"` + EnableSendRedirects string `xml:"enableSendRedirects,omitempty"` + Mtu string `xml:"mtu,omitempty"` + AddressGroups struct { + AddressGroup struct { + PrimaryAddress string `xml:"primaryAddress,omitempty"` + SubnetMask string `xml:"subnetMask,omitempty"` + SubnetPrefixLength string `xml:"subnetPrefixLength,omitempty"` + } `xml:"addressGroup,omitempty"` + } `xml:"addressGroups,omitempty"` + } `xml:"subInterface,omitempty"` + } `xml:"subInterfaces,omitempty"` + } `xml:"vnic,omitempty"` +} + +// EdgeGatewayInterfaces is a data structure holding information of vNic configuration in NSX-V edge +// gateway using "/network/edges/edge_id/vdcNetworks" endpoint +type EdgeGatewayInterfaces struct { + XMLName xml.Name `xml:"edgeInterfaces"` + EdgeInterface []struct { + Name string `xml:"name"` + Type string `xml:"type"` + Index *int `xml:"index"` + NetworkReference struct { + ID string `xml:"id"` + Name string `xml:"name"` + Type string `xml:"type"` + } `xml:"networkReference"` + AddressGroups struct { + AddressGroup struct { + PrimaryAddress string `xml:"primaryAddress"` + SubnetMask string `xml:"subnetMask"` + SubnetPrefixLength string `xml:"subnetPrefixLength"` + SecondaryAddresses struct { + IpAddress []string `xml:"ipAddress"` + } `xml:"secondaryAddresses"` + } `xml:"addressGroup"` + } `xml:"addressGroups"` + PortgroupId string `xml:"portgroupId"` + PortgroupName string `xml:"portgroupName"` + IsConnected string `xml:"isConnected"` + TunnelId string `xml:"tunnelId"` + } `xml:"edgeInterface"` +} + +// EdgeDhcpRelay - Dynamic Host Configuration Protocol (DHCP) relay enables you to leverage your +// existing DHCP infrastructure from within NSX without any interruption to the IP address +// management in your environment. DHCP messages are relayed from virtual machine(s) to the +// designated DHCP server(s) in the physical world. This enables IP addresses within NSX to continue +// to be in sync with IP addresses in other environments. +type EdgeDhcpRelay struct { + XMLName xml.Name `xml:"relay"` + // RelayServer specifies external relay server(s) to which DHCP messages are to be relayed to. + // The relay server can be an IP set, IP address block, domain, or a combination of all of + // these. Messages are relayed to each listed DHCP server. + RelayServer *EdgeDhcpRelayServer `xml:"relayServer"` + // EdgeDhcRelayAgents specifies a list of edge gateway interfaces (vNics) from which DHCP + // messages are to be relayed to the external DHCP relay server(s) with optional gateway + // interface addresses. + RelayAgents *EdgeDhcpRelayAgents `xml:"relayAgents"` +} + +type EdgeDhcpRelayServer struct { + // GroupingObjectIds is a general concept in NSX which allows to pass in many types of objects + // (like VM IDs, IP set IDs, org networks, security groups) howether in this case it accepts + // only IP sets which have IDs specified as 'f9daf2da-b4f9-4921-a2f4-d77a943a381c:ipset-2' where + // first part is vDC ID and the second part is unique IP set ID + GroupingObjectId []string `xml:"groupingObjectId,omitempty"` + // IpAddresses holds a list of IP addresses for DHCP servers + IpAddress []string `xml:"ipAddress,omitempty"` + // Fqdn holds a list of FQDNs (fully qualified domain names) + Fqdns []string `xml:"fqdn,omitempty"` +} + +// EdgeDhcpRelayAgent specifies which edge gateway interface (vNic) from which DHCP messages are to +// be relayed to the external DHCP relay server(s) with an optional gateway interface address. +type EdgeDhcpRelayAgent struct { + // VnicIndex must specify vNic adapter index on the edge gateway + VnicIndex *int `xml:"vnicIndex"` + // GatewayInterfaceAddress holds a gateway interface address. Optional, defaults to the vNic + // primary address. + GatewayInterfaceAddress string `xml:"giAddress,omitempty"` +} + +// EdgeDhcpRelayAgents holds a slice of EdgeDhcpRelayAgent +type EdgeDhcpRelayAgents struct { + Agents []EdgeDhcpRelayAgent `xml:"relayAgent"` +} diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go index c9d228307..9d186088c 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go @@ -1369,7 +1369,6 @@ type VM struct { // VMDiskChange represents a virtual machine only with Disk setting update part type VMDiskChange struct { - // Attributes XMLName xml.Name `xml:"Vm"` Ovf string `xml:"xmlns:ovf,attr,omitempty"` Xsi string `xml:"xmlns:xsi,attr,omitempty"` @@ -1380,7 +1379,7 @@ type VMDiskChange struct { Name string `xml:"name,attr"` // VM name ID string `xml:"id,attr,omitempty"` // VM ID. The entity identifier, expressed in URN format. The value of this attribute uniquely identifies the entity, persists for the life of the entity, and is never reused. - VmSpecSection *VmSpecSection `xml:"VmSpecSection,omitempty"` // Container for the specification of this virtual machine. This is an alternate to using ovf:VirtualHardwareSection + ovf:OperatingSystemSection + VmSpecSection *VmSpecSection `xml:"VmSpecSection,omitempty"` // Container for the specification of this virtual machine. This is an alternative to using ovf:VirtualHardwareSection + ovf:OperatingSystemSection } // VmSpecSection from VM struct @@ -1389,12 +1388,12 @@ type VmSpecSection struct { Info string `xml:"ovf:Info"` OsType string `xml:"OsType,omitempty"` // The type of the OS. This parameter may be omitted when using the VmSpec to update the contents of an existing VM. NumCpus *int `xml:"NumCpus,omitempty"` // Number of CPUs. This parameter may be omitted when using the VmSpec to update the contents of an existing VM. - NumCoresPerSocket *int `xml:"NumCoresPerSocket,omitempty"` // Number of cores among which to distribute CPUs in this virtual machine.. This parameter may be omitted when using the VmSpec to update the contents of an existing VM. + NumCoresPerSocket *int `xml:"NumCoresPerSocket,omitempty"` // Number of cores among which to distribute CPUs in this virtual machine. This parameter may be omitted when using the VmSpec to update the contents of an existing VM. CpuResourceMhz *CpuResourceMhz `xml:"CpuResourceMhz,omitempty"` // CPU compute resources. This parameter may be omitted when using the VmSpec to update the contents of an existing VM. MemoryResourceMb *MemoryResourceMb `xml:"MemoryResourceMb"` // Memory compute resources. This parameter may be omitted when using the VmSpec to update the contents of an existing VM. MediaSection *MediaSection `xml:"MediaSection,omitempty"` // The media devices of this VM. DiskSection *DiskSection `xml:"DiskSection,omitempty"` // virtual disks of this VM. - HardwareVersion *HardwareVersion `xml:"HardwareVersion"` // vSphere name of Virtual Hardware Version of this VM. Example: vmx-13 This parameter may be omitted when using the VmSpec to update the contents of an existing VM. + HardwareVersion *HardwareVersion `xml:"HardwareVersion"` // vSphere name of Virtual Hardware Version of this VM. Example: vmx-13 - This parameter may be omitted when using the VmSpec to update the contents of an existing VM. VmToolsVersion string `xml:"VmToolsVersion,omitempty"` // VMware tools version of this VM. VirtualCpuType string `xml:"VirtualCpuType,omitempty"` // The capabilities settings for this VM. This parameter may be omitted when using the VmSpec to update the contents of an existing VM. TimeSyncWithHost *bool `xml:"TimeSyncWithHost,omitempty"` // Synchronize the VM's time with the host. @@ -1442,7 +1441,7 @@ type CpuResourceMhz struct { Reservation *int64 `xml:"Reservation,omitempty"` // The amount of reservation of this resource on the underlying virtualization infrastructure. Limit *int64 `xml:"Limit,omitempty"` // The limit for how much of this resource can be consumed on the underlying virtualization infrastructure. This is only valid when the resource allocation is not unlimited. SharesLevel string `xml:"SharesLevel,omitempty"` // Pre-determined relative priorities according to which the non-reserved portion of this resource is made available to the virtualized workload. - Shares *int `xml:"Shares,omitempty"` // Custom priority for the resource. This is a read-only, unless the share level is CUSTOM. + Shares *int `xml:"Shares,omitempty"` // Custom priority for the resource. This field is read-only, unless the shares level is CUSTOM. } // MemoryResourceMb from VM/VmSpecSection struct @@ -1973,6 +1972,7 @@ type FirewallService struct { type NatService struct { Xmlns string `xml:"xmlns,attr,omitempty"` // Elements + IsEnabled bool `xml:"IsEnabled"` // Enable or disable the service using this flag NatType string `xml:"NatType,omitempty"` // One of: ipTranslation (use IP translation), portForwarding (use port forwarding) Policy string `xml:"Policy,omitempty"` // One of: allowTraffic (Allow all traffic), allowTrafficIn (Allow inbound traffic only) @@ -2662,51 +2662,3 @@ type AdminCatalogRecord struct { Link *Link `xml:"Link,omitempty"` Vdc *Metadata `xml:"Metadata,omitempty"` } - -// EdgeGatewayVnics is a data structure holding information of vNic configuration in NSX-V edge -// gateway -type EdgeGatewayVnics struct { - XMLName xml.Name `xml:"vnics"` - Vnic []struct { - Label string `xml:"label"` - Name string `xml:"name"` - AddressGroups struct { - AddressGroup struct { - PrimaryAddress string `xml:"primaryAddress,omitempty"` - SecondaryAddresses struct { - IpAddress []string `xml:"ipAddress,omitempty"` - } `xml:"secondaryAddresses,omitempty"` - SubnetMask string `xml:"subnetMask,omitempty"` - SubnetPrefixLength string `xml:"subnetPrefixLength,omitempty"` - } `xml:"addressGroup,omitempty"` - } `xml:"addressGroups,omitempty"` - Mtu string `xml:"mtu,omitempty"` - Type string `xml:"type,omitempty"` - IsConnected string `xml:"isConnected,omitempty"` - Index *int `xml:"index"` - PortgroupId string `xml:"portgroupId,omitempty"` - PortgroupName string `xml:"portgroupName,omitempty"` - EnableProxyArp string `xml:"enableProxyArp,omitempty"` - EnableSendRedirects string `xml:"enableSendRedirects,omitempty"` - SubInterfaces struct { - SubInterface []struct { - IsConnected string `xml:"isConnected,omitempty"` - Label string `xml:"label,omitempty"` - Name string `xml:"name,omitempty"` - Index *int `xml:"index,omitempty"` - TunnelId string `xml:"tunnelId,omitempty"` - LogicalSwitchId string `xml:"logicalSwitchId,omitempty"` - LogicalSwitchName string `xml:"logicalSwitchName,omitempty"` - EnableSendRedirects string `xml:"enableSendRedirects,omitempty"` - Mtu string `xml:"mtu,omitempty"` - AddressGroups struct { - AddressGroup struct { - PrimaryAddress string `xml:"primaryAddress,omitempty"` - SubnetMask string `xml:"subnetMask,omitempty"` - SubnetPrefixLength string `xml:"subnetPrefixLength,omitempty"` - } `xml:"addressGroup,omitempty"` - } `xml:"addressGroups,omitempty"` - } `xml:"subInterface,omitempty"` - } `xml:"subInterfaces,omitempty"` - } `xml:"vnic,omitempty"` -} diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/util/logging.go b/vendor/github.com/vmware/go-vcloud-director/v2/util/logging.go index 06f5a3a45..b454456a1 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/util/logging.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/util/logging.go @@ -357,7 +357,7 @@ func FuncNameCallStack() string { fpcs := make([]uintptr, 10) runtime.Callers(0, fpcs) // Removes the function names from the reflect stack itself and the ones from the API management - removeReflect := regexp.MustCompile(`^ runtime.call|reflect.Value|\bNewRequest\b|NewRequestWitNotEncodedParams|ExecuteRequest|ExecuteRequestWithoutResponse|ExecuteTaskRequest`) + removeReflect := regexp.MustCompile(`^ runtime.call|reflect.Value|\bNewRequest\b|NewRequestWitNotEncodedParamsWithApiVersion|NewRequestWitNotEncodedParams|ExecuteRequest|ExecuteRequestWithoutResponse|ExecuteTaskRequest`) var stackStr []string // Gets up to 10 functions from the stack for N := 0; N < len(fpcs) && N < 10; N++ { diff --git a/vendor/modules.txt b/vendor/modules.txt index 74495e2b5..c2597e276 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -224,7 +224,7 @@ github.com/ulikunitz/xz/lzma # github.com/vmihailenco/msgpack v4.0.1+incompatible github.com/vmihailenco/msgpack github.com/vmihailenco/msgpack/codes -# github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191205150825-aeb30347581d +# github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191219102849-f40088ca976a github.com/vmware/go-vcloud-director/v2/govcd github.com/vmware/go-vcloud-director/v2/types/v56 github.com/vmware/go-vcloud-director/v2/util From 3f3798bc12d27fa46b8733c7fbc756f27620bb4c Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 19 Dec 2019 15:44:32 +0200 Subject: [PATCH 28/80] Merge Signed-off-by: Vaidotas Bauzys --- .../v2/govcd/nsxv_dhcprelay.go | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_dhcprelay.go diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_dhcprelay.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_dhcprelay.go new file mode 100644 index 000000000..f1511043a --- /dev/null +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/nsxv_dhcprelay.go @@ -0,0 +1,77 @@ +/* + * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "fmt" + "net/http" + + "github.com/vmware/go-vcloud-director/v2/types/v56" +) + +// UpdateDhcpRelay updates DHCP relay settings for a particular edge gateway and returns them. The +// feature itself enables you to leverage your existing DHCP infrastructure from within NSX without +// any interruption to the IP address management in your environment. DHCP messages are relayed from +// virtual machine(s) to the designated DHCP server(s) in the physical world. This enables IP +// addresses within NSX to continue to be in sync with IP addresses in other environments. +func (egw *EdgeGateway) UpdateDhcpRelay(dhcpRelayConfig *types.EdgeDhcpRelay) (*types.EdgeDhcpRelay, error) { + if !egw.HasAdvancedNetworking() { + return nil, fmt.Errorf("only advanced edge gateways support DHCP relay") + } + + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeDhcpRelayPath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + // We expect to get http.StatusNoContent or if not an error of type types.NSXError + _, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPut, types.AnyXMLMime, + "error setting DHCP relay settings: %s", dhcpRelayConfig, &types.NSXError{}) + if err != nil { + return nil, err + } + + return egw.GetDhcpRelay() +} + +// GetDhcpRelay retrieves a structure of *types.EdgeDhcpRelay with all DHCP relay settings present +// on a particular edge gateway. +func (egw *EdgeGateway) GetDhcpRelay() (*types.EdgeDhcpRelay, error) { + if !egw.HasAdvancedNetworking() { + return nil, fmt.Errorf("only advanced edge gateways support DHCP relay") + } + response := &types.EdgeDhcpRelay{} + + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeDhcpRelayPath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // This query Edge gaateway DHCP relay using proxied NSX-V API + _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, + "unable to read edge gateway DHCP relay configuration: %s", nil, response) + if err != nil { + return nil, err + } + + return response, nil +} + +// ResetDhcpRelay removes all configuration by sending a DELETE request for DHCP relay configuration +// endpoint +func (egw *EdgeGateway) ResetDhcpRelay() error { + if !egw.HasAdvancedNetworking() { + return fmt.Errorf("only advanced edge gateways support DHCP relay") + } + + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeDhcpRelayPath) + if err != nil { + return fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // Send a DELETE request to DHCP relay configuration endpoint + _, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodDelete, types.AnyXMLMime, + "unable to reset edge gateway DHCP relay configuration: %s", nil, &types.NSXError{}) + return err +} From 66ad708d539a0ba62ac82350290559904a8b1330 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 19 Dec 2019 15:51:36 +0200 Subject: [PATCH 29/80] Fix after merge Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_nsxv_firewall_rule_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcd/resource_vcd_nsxv_firewall_rule_test.go b/vcd/resource_vcd_nsxv_firewall_rule_test.go index d74d6712f..223926fa3 100644 --- a/vcd/resource_vcd_nsxv_firewall_rule_test.go +++ b/vcd/resource_vcd_nsxv_firewall_rule_test.go @@ -1057,7 +1057,7 @@ func TestAccVcdNsxvEdgeFirewallRuleVms(t *testing.T) { if err != nil { t.Skip("unable to validate vCD version - skipping test") } - if vcdClient.APIVCDMaxVersionIs("= 29.0") { + if vcdClient.Client.APIVCDMaxVersionIs("= 29.0") { t.Skip("Skipping this test for vCD 9.0 because of a known issue: " + "https://github.com/terraform-providers/terraform-provider-vcd/issues/420") } From 9398399026c7a6d2099e252ad0d7ec232a63b8b7 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 19 Dec 2019 16:08:27 +0200 Subject: [PATCH 30/80] Fix for vendor Signed-off-by: Vaidotas Bauzys --- vendor/modules.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/modules.txt b/vendor/modules.txt index c2597e276..b302ee6a3 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -224,7 +224,7 @@ github.com/ulikunitz/xz/lzma # github.com/vmihailenco/msgpack v4.0.1+incompatible github.com/vmihailenco/msgpack github.com/vmihailenco/msgpack/codes -# github.com/vmware/go-vcloud-director/v2 v2.5.0-alpha.9 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191219102849-f40088ca976a +# github.com/vmware/go-vcloud-director/v2 v2.5.1 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191219102849-f40088ca976a github.com/vmware/go-vcloud-director/v2/govcd github.com/vmware/go-vcloud-director/v2/types/v56 github.com/vmware/go-vcloud-director/v2/util From c29c06fa5e8144190d0802d7e4a8acd30a084339 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 20 Dec 2019 10:31:05 +0200 Subject: [PATCH 31/80] Improved doc Signed-off-by: Vaidotas Bauzys --- website/docs/r/vm_internal_disk.html.markdown | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/docs/r/vm_internal_disk.html.markdown b/website/docs/r/vm_internal_disk.html.markdown index 8a4b2a9cf..935d6955a 100644 --- a/website/docs/r/vm_internal_disk.html.markdown +++ b/website/docs/r/vm_internal_disk.html.markdown @@ -41,10 +41,12 @@ The following arguments are supported: * `size_in_mb` - (Required) The size of the disk in MB. * `bus_number` - (Required) The number of the SCSI or IDE controller itself. * `unit_number` - (Required) The device number on the SCSI or IDE controller of the disk. -* `thin_provisioned` - (Optional) Specifies whether the disk storage is pre-allocated or allocated on demand. * `iops` - (Optional) Specifies the IOPS for the disk. Default - 0. * `storage_profile` - (Optional) Storage profile which overrides the VM default one. +## Attribute reference + +* `thin_provisioned` - Specifies whether the disk storage is pre-allocated or allocated on demand. ## Importing From 7fe4e2fe177b67c4a6c3753eb331f5e0f2f156e4 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 27 Dec 2019 14:33:27 +0200 Subject: [PATCH 32/80] Update changelog Signed-off-by: Vaidotas Bauzys --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9e5b57a4..881cbe272 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ ## 2.7.0 (Unreleased) + +* Drop support for vCD 9.0 +* **New Resource:** `vcd_vm_internal_disk` VM internal disk configuration +* `vcd_vapp_vm` Internal disks in VM template allows to be edited by `override_template_disk` field + ## 2.6.0 (December 13, 2019) FEATURES: From 0ffff89c116ba057dfd6c3ad7e81abeb5a6bcbeb Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 3 Jan 2020 14:08:36 +0200 Subject: [PATCH 33/80] Improving test Signed-off-by: Vaidotas Bauzys --- vcd/config_test.go | 8 +- vcd/resource_vcd_vm_internal_disk.go | 2 + vcd/resource_vcd_vm_internal_disk_test.go | 210 +++++++++++++++------- 3 files changed, 151 insertions(+), 69 deletions(-) diff --git a/vcd/config_test.go b/vcd/config_test.go index 832e1ef4c..0d723957b 100644 --- a/vcd/config_test.go +++ b/vcd/config_test.go @@ -815,14 +815,14 @@ func importStateIdEdgeGatewayObject(vcd TestConfig, edgeGatewayName, objectName } // Used by all entities that depend on Org + VDC + vApp VM (such as VM internal disks) -func importStateIdVmObject(vcd TestConfig, vappName, vmName, objectName string) resource.ImportStateIdFunc { +func importStateIdVmObject(orgName, vdcName, vappName, vmName, objectName string) resource.ImportStateIdFunc { return func(*terraform.State) (string, error) { - if testConfig.VCD.Org == "" || testConfig.VCD.Vdc == "" || vappName == "" || vmName == "" || objectName == "" { + if orgName == "" || vdcName == "" || vappName == "" || vmName == "" || objectName == "" { return "", fmt.Errorf("missing information to generate import path") } - return testConfig.VCD.Org + + return orgName + ImportSeparator + - testConfig.VCD.Vdc + + vdcName + ImportSeparator + vappName + ImportSeparator + diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index 421b06d62..d40932fe2 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -464,6 +464,8 @@ func getInternalDiskForImport(d *schema.ResourceData, meta interface{}, orgName, } d.SetId(disk.DiskId) + d.Set("org", orgName) + d.Set("vdc", vdcName) d.Set("vapp_name", vappName) d.Set("vm_name", vmName) return []*schema.ResourceData{d}, nil diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go index e634fcdbd..b460c4909 100644 --- a/vcd/resource_vcd_vm_internal_disk_test.go +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -6,11 +6,9 @@ import ( "fmt" "github.com/vmware/go-vcloud-director/v2/types/v56" "regexp" - "strconv" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" - "github.com/vmware/go-vcloud-director/v2/govcd" ) func TestAccVcdVmInternalDisk(t *testing.T) { @@ -21,44 +19,44 @@ func TestAccVcdVmInternalDisk(t *testing.T) { return } - adminVdc, err := getAdminVdc() - if err != nil { - t.Skip(fmt.Sprintf("error retrieving if VDC is thing provisioned %s", err)) - return - } - - vapp, err := getAvailableVapp() - if err != nil { - t.Skip("No suitable vApp found for this test") - return - } - var vm *govcd.VM + /* adminVdc, err := getAdminVdc() + if err != nil { + t.Skip(fmt.Sprintf("error retrieving if VDC is thing provisioned %s", err)) + return + } - if vapp.VApp.Children != nil && len(vapp.VApp.Children.VM) > 0 { - vm, err = vapp.GetVMById(vapp.VApp.Children.VM[0].ID, false) + vapp, err := getAvailableVapp() if err != nil { - t.Skip(fmt.Sprintf("error retrieving VM %s", vapp.VApp.Children.VM[0].Name)) + t.Skip("No suitable vApp found for this test") return } - } - if vm == nil { - t.Skip(fmt.Sprintf("No VM available in vApp %s", vapp.VApp.Name)) - return - } + var vm *govcd.VM - // for test to run correctly VM has to be power on - task, err := vm.PowerOn() - if err != nil { - t.Skip(fmt.Sprintf("Error powering up vm %s", err)) - return - } - _ = task.WaitTaskCompletion() + if vapp.VApp.Children != nil && len(vapp.VApp.Children.VM) > 0 { + vm, err = vapp.GetVMById(vapp.VApp.Children.VM[0].ID, false) + if err != nil { + t.Skip(fmt.Sprintf("error retrieving VM %s", vapp.VApp.Children.VM[0].Name)) + return + } + } + if vm == nil { + t.Skip(fmt.Sprintf("No VM available in vApp %s", vapp.VApp.Name)) + return + } + + // for test to run correctly VM has to be power on + task, err := vm.PowerOn() + if err != nil { + t.Skip(fmt.Sprintf("Error powering up vm %s", err)) + return + } + _ = task.WaitTaskCompletion()*/ storageProfile := testConfig.VCD.ProviderVdc.StorageProfile - if *adminVdc.UsesFastProvisioning { + /* if *adminVdc.UsesFastProvisioning { // to avoid `Cannot use multiple storage profiles in a fast-provisioned VDC` we need to reuse VM storage profile storageProfile = vm.VM.StorageProfile.Name - } + }*/ diskResourceName := "disk1" diskSize := "13333" @@ -68,11 +66,14 @@ func TestAccVcdVmInternalDisk(t *testing.T) { unitNumber := "0" allowReboot := true + vappName := "TestInternalDiskVapp" + vmName := "TestInternalDiskVm" + vdcName := "ForInternalDiskTest" var params = StringMap{ - "Org": testConfig.VCD.Org, - "VDC": testConfig.VCD.Vdc, - "VappName": vapp.VApp.Name, - "VmName": vm.VM.Name, + "Org": testConfig.VCD.Org, + //"VDC": testConfig.VCD.Vdc, + // "VappName": vapp.VApp.Name, + // "VmName": vm.VM.Name, "FuncName": "TestVappVmDS", "Tags": "vm", "DiskResourceName": diskResourceName, @@ -83,6 +84,25 @@ func TestAccVcdVmInternalDisk(t *testing.T) { "UnitNumber": unitNumber, "StorageProfileName": testConfig.VCD.ProviderVdc.StorageProfile, "AllowReboot": allowReboot, + + "VdcName": vdcName, + "OrgName": testConfig.VCD.Org, + "AllocationModel": "ReservationPool", + "ProviderVdc": testConfig.VCD.ProviderVdc.Name, + "NetworkPool": testConfig.VCD.ProviderVdc.NetworkPool, + "Allocated": "1024", + "Reserved": "1024", + "Limit": "1024", + "ProviderVdcStorageProfile": testConfig.VCD.ProviderVdc.StorageProfile, + // cause vDC ignores empty values and use default + "MemoryGuaranteed": "1", + "CpuGuaranteed": "1", + + "Catalog": testSuiteCatalogName, + "CatalogItem": testSuiteCatalogOVAItem, + "VappName": vappName, + "VmName": vmName, + "ComputerName": vmName + "Unique", } params["FuncName"] = t.Name() + "-IdeCreate" configTextIde := templateFill(sourceTestVmInternalDiskIde, params) @@ -104,7 +124,7 @@ func TestAccVcdVmInternalDisk(t *testing.T) { Check: resource.ComposeTestCheckFunc(resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "size_in_mb", diskSize), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_type", "ide"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_number", "0"), - resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "unit_number", "1"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "unit_number", "0"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "storage_profile", storageProfile), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "allow_vm_reboot", "false"), ), @@ -116,14 +136,16 @@ func TestAccVcdVmInternalDisk(t *testing.T) { resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "bus_number", busNumber), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "unit_number", unitNumber), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "storage_profile", storageProfile), - resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), + //resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "thin_provisioned", "true"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "iops", "0"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "size_in_mb", diskSize), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_type", "ide"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_number", "0"), - resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "unit_number", "1"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "unit_number", "0"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "storage_profile", storageProfile), - resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), + //resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "thin_provisioned", "true"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "iops", "0"), ), }, @@ -136,24 +158,26 @@ func TestAccVcdVmInternalDisk(t *testing.T) { resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "bus_number", busNumber), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "unit_number", unitNumber), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "storage_profile", storageProfile), - resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), + //resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "thin_provisioned", "true"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "iops", "0"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "allow_vm_reboot", "false"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_type", "ide"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_number", "0"), - resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "unit_number", "1"), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "unit_number", "0"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "storage_profile", storageProfile), - resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), + //resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), + resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "thin_provisioned", "true"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "size_in_mb", biggerDiskSize), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "iops", "0"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "allow_vm_reboot", "true"), ), }, resource.TestStep{ - ResourceName: "vcd_vm_internal_disk." + TestAccVcdVdc + "-import", + ResourceName: "vcd_vm_internal_disk." + diskResourceName + "-import", ImportState: true, ImportStateVerify: true, - ImportStateIdFunc: importStateIdVmObject(testConfig, vapp.VApp.Name, vm.VM.Name, "3001"), + ImportStateIdFunc: importStateIdVmObject(testConfig.VCD.Org, vdcName, vappName, vmName, "3000"), // These fields can't be retrieved ImportStateVerifyIgnore: []string{"org", "vdc", "allow_vm_reboot", "thin_provisioned"}, }, @@ -191,40 +215,96 @@ func getAdminVdc() (*types.AdminVdc, error) { return adminVdc.AdminVdc, nil } -const sourceTestVmInternalDiskIde = ` +// we need VDC with disabled fast provisioning to edit disks +const sourceTestVmInternalDiskOrgVdcAndVM = ` +resource "vcd_org_vdc" "{{.VdcName}}" { + org = "{{.OrgName}}" + name = "{{.VdcName}}" + + allocation_model = "{{.AllocationModel}}" + network_pool_name = "{{.NetworkPool}}" + provider_vdc_name = "{{.ProviderVdc}}" + + compute_capacity { + cpu { + allocated = "{{.Allocated}}" + limit = "{{.Limit}}" + } + + memory { + allocated = "{{.Allocated}}" + limit = "{{.Limit}}" + } + } + + storage_profile { + name = "{{.ProviderVdcStorageProfile}}" + enabled = true + limit = 102400 + default = true + } + + enabled = true + enable_thin_provisioning = true + enable_fast_provisioning = false + delete_force = true + delete_recursive = true +} + +resource "vcd_vapp" "{{.VappName}}" { + org = "{{.Org}}" + vdc = vcd_org_vdc.{{.VdcName}}.name + name = "{{.VappName}}" +} + +resource "vcd_vapp_vm" "{{.VmName}}" { + org = "{{.Org}}" + vdc = vcd_org_vdc.{{.VdcName}}.name + vapp_name = vcd_vapp.{{.VappName}}.name + name = "{{.VmName}}" + computer_name = "{{.ComputerName}}" + catalog_name = "{{.Catalog}}" + template_name = "{{.CatalogItem}}" + memory = 1024 + cpus = 1 + cpu_cores = 1 +} +` + +const sourceTestVmInternalDiskIde = sourceTestVmInternalDiskOrgVdcAndVM + ` resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { org = "{{.Org}}" - vdc = "{{.VDC}}" - vapp_name = "{{.VappName}}" - vm_name = "{{.VmName}}" + vdc = vcd_org_vdc.{{.VdcName}}.name + vapp_name = vcd_vapp.{{.VappName}}.name + vm_name = vcd_vapp_vm.{{.VmName}}.name bus_type = "ide" size_in_mb = "{{.Size}}" bus_number = "0" - unit_number = "1" + unit_number = "0" storage_profile = "{{.StorageProfileName}}" allow_vm_reboot = "false" } ` -const sourceTestVmInternalDisk = ` +const sourceTestVmInternalDisk = sourceTestVmInternalDiskOrgVdcAndVM + ` resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { org = "{{.Org}}" - vdc = "{{.VDC}}" - vapp_name = "{{.VappName}}" - vm_name = "{{.VmName}}" + vdc = vcd_org_vdc.{{.VdcName}}.name + vapp_name = vcd_vapp.{{.VappName}}.name + vm_name = vcd_vapp_vm.{{.VmName}}.name bus_type = "ide" size_in_mb = "{{.Size}}" bus_number = "0" - unit_number = "1" + unit_number = "0" storage_profile = "{{.StorageProfileName}}" allow_vm_reboot = "true" } resource "vcd_vm_internal_disk" "{{.DiskResourceName}}" { org = "{{.Org}}" - vdc = "{{.VDC}}" - vapp_name = "{{.VappName}}" - vm_name = "{{.VmName}}" + vdc = vcd_org_vdc.{{.VdcName}}.name + vapp_name = vcd_vapp.{{.VappName}}.name + vm_name = vcd_vapp_vm.{{.VmName}}.name bus_type = "{{.BusType}}" size_in_mb = "{{.Size}}" bus_number = "{{.BusNumber}}" @@ -234,12 +314,12 @@ resource "vcd_vm_internal_disk" "{{.DiskResourceName}}" { } ` -const sourceTestVmInternalDisk_Update1 = ` +const sourceTestVmInternalDisk_Update1 = sourceTestVmInternalDiskOrgVdcAndVM + ` resource "vcd_vm_internal_disk" "{{.DiskResourceName}}" { org = "{{.Org}}" - vdc = "{{.VDC}}" - vapp_name = "{{.VappName}}" - vm_name = "{{.VmName}}" + vdc = vcd_org_vdc.{{.VdcName}}.name + vapp_name = vcd_vapp.{{.VappName}}.name + vm_name = vcd_vapp_vm.{{.VmName}}.name bus_type = "{{.BusType}}" size_in_mb = "{{.SizeBigger}}" bus_number = "{{.BusNumber}}" @@ -250,13 +330,13 @@ resource "vcd_vm_internal_disk" "{{.DiskResourceName}}" { resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { org = "{{.Org}}" - vdc = "{{.VDC}}" - vapp_name = "{{.VappName}}" - vm_name = "{{.VmName}}" + vdc = vcd_org_vdc.{{.VdcName}}.name + vapp_name = vcd_vapp.{{.VappName}}.name + vm_name = vcd_vapp_vm.{{.VmName}}.name bus_type = "ide" size_in_mb = "{{.SizeBigger}}" bus_number = "0" - unit_number = "1" + unit_number = "0" storage_profile = "{{.StorageProfileName}}" allow_vm_reboot = "true" } From 90244668996a022e535df35c81e7c380aaa8f5e3 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 6 Jan 2020 08:20:33 +0200 Subject: [PATCH 34/80] Comment out code Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go index b460c4909..76b89714c 100644 --- a/vcd/resource_vcd_vm_internal_disk_test.go +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -3,8 +3,6 @@ package vcd import ( - "fmt" - "github.com/vmware/go-vcloud-director/v2/types/v56" "regexp" "testing" @@ -195,7 +193,7 @@ func TestAccVcdVmInternalDisk(t *testing.T) { }) } -func getAdminVdc() (*types.AdminVdc, error) { +/*func getAdminVdc() (*types.AdminVdc, error) { vcdClient, err := getTestVCDFromJson(testConfig) if err != nil { return nil, fmt.Errorf("error getting client configuration: %s", err) @@ -213,7 +211,7 @@ func getAdminVdc() (*types.AdminVdc, error) { return nil, fmt.Errorf("vdc not found : %s", err) } return adminVdc.AdminVdc, nil -} +}*/ // we need VDC with disabled fast provisioning to edit disks const sourceTestVmInternalDiskOrgVdcAndVM = ` From 7d87bcbe988151a0ec67967c6afd2dea1242141f Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 6 Jan 2020 09:34:50 +0200 Subject: [PATCH 35/80] Increase version Signed-off-by: Vaidotas Bauzys --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 8a965c116..873ca0fa6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.6.0 +v2.7.0 From 2dcdeaef364b98d6e4677b186b5810a5ee0fdbaa Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 6 Jan 2020 09:39:16 +0200 Subject: [PATCH 36/80] Increase version Signed-off-by: Vaidotas Bauzys --- website/docs/index.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index cee9a922e..14b58322f 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -6,7 +6,7 @@ description: |- The VMware vCloud Director provider is used to interact with the resources supported by VMware vCloud Director. The provider needs to be configured with the proper credentials before it can be used. --- -# VMware vCloud Director Provider 2.6 +# VMware vCloud Director Provider 2.7 The VMware vCloud Director provider is used to interact with the resources supported by VMware vCloud Director. The provider needs to be configured with the proper credentials before it can be used. From 84db69d7c90870b57f42af67e10e694ad55362ac Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 6 Jan 2020 13:43:21 +0200 Subject: [PATCH 37/80] Increase version Signed-off-by: Vaidotas Bauzys --- PREVIOUS_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PREVIOUS_VERSION b/PREVIOUS_VERSION index 21222ceed..8a965c116 100644 --- a/PREVIOUS_VERSION +++ b/PREVIOUS_VERSION @@ -1 +1 @@ -v2.5.0 +v2.6.0 From 45fd197495ebc60492b9746a5f06b7ca947d708a Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Tue, 7 Jan 2020 12:29:08 +0200 Subject: [PATCH 38/80] Add sys check Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go index 76b89714c..cd5ab7558 100644 --- a/vcd/resource_vcd_vm_internal_disk_test.go +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -16,6 +16,11 @@ func TestAccVcdVmInternalDisk(t *testing.T) { t.Skip(acceptanceTestsSkipped) return } + // In general VM internal disks works with Org users, but due we need create VDC with disabled fast provisioning value, we have to be sys admins + if !usingSysAdmin() { + t.Skip("VM internal disks tests requires system admin privileges") + return + } /* adminVdc, err := getAdminVdc() if err != nil { From 94703ff8b400b6fb288ffc71f143669f8f517bbf Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Tue, 7 Jan 2020 12:53:32 +0200 Subject: [PATCH 39/80] Improved import handling org and vdc Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index d40932fe2..7ec2f7a00 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -464,8 +464,12 @@ func getInternalDiskForImport(d *schema.ResourceData, meta interface{}, orgName, } d.SetId(disk.DiskId) - d.Set("org", orgName) - d.Set("vdc", vdcName) + if vcdClient.Org != orgName { + d.Set("org", orgName) + } + if vcdClient.Vdc != vdcName { + d.Set("vdc", vdcName) + } d.Set("vapp_name", vappName) d.Set("vm_name", vmName) return []*schema.ResourceData{d}, nil From 863eb457c0fdf993c24b366168c6955c8e1a1814 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Tue, 7 Jan 2020 14:02:11 +0200 Subject: [PATCH 40/80] Merge branch 'master' of github.com:terraform-providers/terraform-provider-vcd into vm-disk-feature Signed-off-by: Vaidotas Bauzys # Conflicts: # CHANGELOG.md --- CHANGELOG.md | 8 ++++++-- website/docs/index.html.markdown | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 881cbe272..d64130afa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,13 @@ ## 2.7.0 (Unreleased) -* Drop support for vCD 9.0 +FEATURES: + * **New Resource:** `vcd_vm_internal_disk` VM internal disk configuration * `vcd_vapp_vm` Internal disks in VM template allows to be edited by `override_template_disk` field - + +NOTES: +* Drop support for vCD 9.0 + ## 2.6.0 (December 13, 2019) FEATURES: diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index 14b58322f..a28f7bd3d 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -20,7 +20,6 @@ When upgrading the provider please check for such labels for the resources you a The following vCloud Director versions are supported by this provider: -* 9.0 * 9.1 * 9.5 * 9.7 From 3c736abc7057a6370882e102080edb1c9ce449b7 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Tue, 7 Jan 2020 14:07:36 +0200 Subject: [PATCH 41/80] upgrade govcd Signed-off-by: Vaidotas Bauzys --- go.mod | 2 +- go.sum | 4 ++-- .../v2/govcd/api_vcd_versions.go | 10 ++++---- .../go-vcloud-director/v2/govcd/vapp.go | 2 +- .../vmware/go-vcloud-director/v2/govcd/vdc.go | 2 +- .../vmware/go-vcloud-director/v2/govcd/vm.go | 5 ++-- .../go-vcloud-director/v2/types/v56/types.go | 24 ++++++++++--------- vendor/modules.txt | 2 +- 8 files changed, 27 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 2640e700e..16a089930 100644 --- a/go.mod +++ b/go.mod @@ -8,4 +8,4 @@ require ( github.com/vmware/go-vcloud-director/v2 v2.5.1 ) -replace github.com/vmware/go-vcloud-director/v2 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191219102849-f40088ca976a +replace github.com/vmware/go-vcloud-director/v2 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200107102938-ea4fad99620e diff --git a/go.sum b/go.sum index 2cfc0855d..444c290d5 100644 --- a/go.sum +++ b/go.sum @@ -197,8 +197,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191219102849-f40088ca976a h1:/Qr8JglTFTyqSPgVgENDDhAUFIQTD0bY+9vEYQMLE38= -github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191219102849-f40088ca976a/go.mod h1:zjondbeyTfZlzhwxOzyF4K2sWWYgMEv5H91dp5dPbU8= +github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200107102938-ea4fad99620e h1:QctEt3maK7Mrf+IYtZ9liCfcQl0x88J3EPeTzbswvQw= +github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200107102938-ea4fad99620e/go.mod h1:zjondbeyTfZlzhwxOzyF4K2sWWYgMEv5H91dp5dPbU8= github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU= diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd_versions.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd_versions.go index f853517f3..0b15851b4 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd_versions.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/api_vcd_versions.go @@ -179,11 +179,13 @@ func (cli *Client) validateAPIVersion() error { return nil } -// GetDefaultAPIVersionOrLaterThan32 returns default version or 32 if it is connected to version vCD 9.7 or later -func (cli *Client) GetDefaultAPIVersionOrLaterThan32() string { +// GetSpecificApiVersionOnCondition returns default version or wantedApiVersion if it is connected to version +// described in vcdApiVersionCondition +// f.e. values ">= 32.0", "32.0" returns 32.0 if vCD version is above or 9.7 +func (cli *Client) GetSpecificApiVersionOnCondition(vcdApiVersionCondition, wantedApiVersion string) string { apiVersion := cli.APIVersion - if cli.APIVCDMaxVersionIs(">= 32.0") { - apiVersion = "32.0" + if cli.APIVCDMaxVersionIs(vcdApiVersionCondition) { + apiVersion = wantedApiVersion } return apiVersion } diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vapp.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vapp.go index 1431e5446..709d7002d 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vapp.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vapp.go @@ -893,7 +893,7 @@ func (client *Client) GetVMByHref(vmHref string) (*VM, error) { newVm := NewVM(client) _, err := client.ExecuteRequestWithApiVersion(vmHref, http.MethodGet, - "", "error retrieving vm: %s", nil, newVm.VM, client.GetDefaultAPIVersionOrLaterThan32()) + "", "error retrieving vm: %s", nil, newVm.VM, client.GetSpecificApiVersionOnCondition(">= 32.0", "32.0")) if err != nil { diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vdc.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vdc.go index 868d3b51e..67c94af94 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vdc.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vdc.go @@ -272,7 +272,7 @@ func (vdc *Vdc) FindStorageProfileReference(name string) (types.Reference, error } for _, sp := range vdc.Vdc.VdcStorageProfiles.VdcStorageProfile { if sp.Name == name { - return types.Reference{HREF: sp.HREF, Name: sp.Name}, nil + return types.Reference{HREF: sp.HREF, Name: sp.Name, ID: sp.ID}, nil } } return types.Reference{}, fmt.Errorf("can't find any VDC Storage_profiles") diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go index a488f3b36..b25db45f2 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/vm.go @@ -72,7 +72,7 @@ func (vm *VM) Refresh() error { vm.VM = &types.VM{} _, err := vm.client.ExecuteRequestWithApiVersion(refreshUrl, http.MethodGet, - "", "error refreshing VM: %s", nil, vm.VM, vm.client.GetDefaultAPIVersionOrLaterThan32()) + "", "error refreshing VM: %s", nil, vm.VM, vm.client.GetSpecificApiVersionOnCondition(">= 32.0", "32.0")) // The request was successful return err @@ -1024,7 +1024,6 @@ func (vm *VM) UpdateInternalDisksAsync(disksSettingToUpdate *types.VmSpecSection return Task{}, fmt.Errorf("cannot update disks, VM HREF is unset") } - disksSettingToUpdate.Info = "Virtual hardware requirements (simplified)" vmSpecSectionModified := true disksSettingToUpdate.Modified = &vmSpecSectionModified @@ -1035,6 +1034,6 @@ func (vm *VM) UpdateInternalDisksAsync(disksSettingToUpdate *types.VmSpecSection Ovf: types.XMLNamespaceOVF, Name: vm.VM.Name, VmSpecSection: disksSettingToUpdate, - }, vm.client.GetDefaultAPIVersionOrLaterThan32()) + }, vm.client.GetSpecificApiVersionOnCondition(">= 32.0", "32.0")) } diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go index 9d186088c..645b757aa 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go @@ -382,6 +382,7 @@ type Vdc struct { Link LinkList `xml:"Link,omitempty"` Description string `xml:"Description,omitempty"` + Tasks *TasksInProgress `xml:"Tasks,omitempty"` AllocationModel string `xml:"AllocationModel"` ComputeCapacity []*ComputeCapacity `xml:"ComputeCapacity"` ResourceEntities []*ResourceEntities `xml:"ResourceEntities,omitempty"` @@ -389,10 +390,9 @@ type Vdc struct { Capabilities []*Capabilities `xml:"Capabilities,omitempty"` NicQuota int `xml:"NicQuota"` NetworkQuota int `xml:"NetworkQuota"` + UsedNetworkCount int `xml:"UsedNetworkCount,omitempty"` VMQuota int `xml:"VmQuota"` IsEnabled bool `xml:"IsEnabled"` - Tasks *TasksInProgress `xml:"Tasks,omitempty"` - UsedNetworkCount int `xml:"UsedNetworkCount,omitempty"` VdcStorageProfiles *VdcStorageProfiles `xml:"VdcStorageProfiles"` } @@ -405,15 +405,17 @@ type AdminVdc struct { Xmlns string `xml:"xmlns,attr"` Vdc - ResourceGuaranteedMemory *float64 `xml:"ResourceGuaranteedMemory,omitempty"` - ResourceGuaranteedCpu *float64 `xml:"ResourceGuaranteedCpu,omitempty"` - VCpuInMhz *int64 `xml:"VCpuInMhz,omitempty"` - IsThinProvision *bool `xml:"IsThinProvision,omitempty"` - NetworkPoolReference *Reference `xml:"NetworkPoolReference,omitempty"` - ProviderVdcReference *Reference `xml:"ProviderVdcReference"` - UsesFastProvisioning *bool `xml:"UsesFastProvisioning,omitempty"` - OverCommitAllowed bool `xml:"OverCommitAllowed,omitempty"` - VmDiscoveryEnabled bool `xml:"VmDiscoveryEnabled,omitempty"` + VCpuInMhz2 *int64 `xml:"VCpuInMhz2,omitempty"` + ResourceGuaranteedMemory *float64 `xml:"ResourceGuaranteedMemory,omitempty"` + ResourceGuaranteedCpu *float64 `xml:"ResourceGuaranteedCpu,omitempty"` + VCpuInMhz *int64 `xml:"VCpuInMhz,omitempty"` + IsThinProvision *bool `xml:"IsThinProvision,omitempty"` + NetworkPoolReference *Reference `xml:"NetworkPoolReference,omitempty"` + ProviderVdcReference *Reference `xml:"ProviderVdcReference"` + ResourcePoolRefs *VimObjectRefs `xml:"vmext:ResourcePoolRefs,omitempty"` + UsesFastProvisioning *bool `xml:"UsesFastProvisioning,omitempty"` + OverCommitAllowed bool `xml:"OverCommitAllowed,omitempty"` + VmDiscoveryEnabled bool `xml:"VmDiscoveryEnabled,omitempty"` } // VdcStorageProfile represents the parameters to create a storage profile in an organization vDC. diff --git a/vendor/modules.txt b/vendor/modules.txt index b302ee6a3..6354b6cce 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -224,7 +224,7 @@ github.com/ulikunitz/xz/lzma # github.com/vmihailenco/msgpack v4.0.1+incompatible github.com/vmihailenco/msgpack github.com/vmihailenco/msgpack/codes -# github.com/vmware/go-vcloud-director/v2 v2.5.1 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20191219102849-f40088ca976a +# github.com/vmware/go-vcloud-director/v2 v2.5.1 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200107102938-ea4fad99620e github.com/vmware/go-vcloud-director/v2/govcd github.com/vmware/go-vcloud-director/v2/types/v56 github.com/vmware/go-vcloud-director/v2/util From 07aeb351f8d41d273179081cbd5f62bf46730707 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Tue, 7 Jan 2020 15:47:19 +0200 Subject: [PATCH 42/80] Improve unit tests Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vapp_vm_test.go | 91 +---------------------- vcd/resource_vcd_vm_internal_disk_test.go | 90 +++++++++++++++++++++- 2 files changed, 91 insertions(+), 90 deletions(-) diff --git a/vcd/resource_vcd_vapp_vm_test.go b/vcd/resource_vcd_vapp_vm_test.go index a2e207ee9..dd13355c8 100644 --- a/vcd/resource_vcd_vapp_vm_test.go +++ b/vcd/resource_vcd_vapp_vm_test.go @@ -3,8 +3,6 @@ package vcd import ( - "fmt" - "github.com/hashicorp/terraform-plugin-sdk/terraform" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" @@ -19,7 +17,6 @@ func TestAccVcdVAppVm_Basic(t *testing.T) { var vm govcd.VM var diskResourceName = "TestAccVcdVAppVm_Basic_1" var diskName = "TestAccVcdIndependentDiskBasic" - var internalDiskSize = 20000 var params = StringMap{ "Org": testConfig.VCD.Org, @@ -38,7 +35,6 @@ func TestAccVcdVAppVm_Basic(t *testing.T) { "storageProfileName": "*", "diskResourceName": diskResourceName, "Tags": "vapp vm", - "InternalDiskSize": internalDiskSize, } configText := templateFill(testAccCheckVcdVAppVm_basic, params) @@ -68,7 +64,6 @@ func TestAccVcdVAppVm_Basic(t *testing.T) { resource.TestCheckResourceAttr( "vcd_vapp_vm."+vmName, "metadata.vm_metadata", "VM Metadata."), resource.TestCheckOutput("disk", diskName), - testCheckInternalDiskNonStringOutputs(internalDiskSize), ), }, resource.TestStep{ @@ -78,48 +73,12 @@ func TestAccVcdVAppVm_Basic(t *testing.T) { ImportStateIdFunc: importStateIdVappObject(testConfig, vappName2, vmName), // These fields can't be retrieved from user data ImportStateVerifyIgnore: []string{"template_name", "catalog_name", "network_name", - "initscript", "accept_all_eulas", "power_on", "computer_name", "override_template_disk"}, + "initscript", "accept_all_eulas", "power_on", "computer_name"}, }, }, }) } -func testCheckInternalDiskNonStringOutputs(internalDiskSize int) resource.TestCheckFunc { - return func(s *terraform.State) error { - outputs := s.RootModule().Outputs - - if outputs["internal_disk_size"].Value != internalDiskSize { - return fmt.Errorf("internal disk size value didn't match") - } - - if outputs["internal_disk_iops"].Value != 0 { - return fmt.Errorf("internal disk iops value didn't match") - } - - if outputs["internal_disk_bus_type"].Value != "paravirtual" { - return fmt.Errorf("internal disk bus type value didn't match") - } - - if outputs["internal_disk_bus_number"].Value != 0 { - return fmt.Errorf("internal disk bus number value didn't match") - } - - if outputs["internal_disk_unit_number"].Value != 0 { - return fmt.Errorf("internal disk unit number value didn't match") - } - - if outputs["internal_disk_thin_provisioned"].Value != true { - return fmt.Errorf("internal disk thin provisioned value didn't match") - } - - if outputs["internal_disk_storage_profile"].Value != "*" { - return fmt.Errorf("internal disk storage profile value didn't match") - } - - return nil - } -} - func TestAccVcdVAppVm_Clone(t *testing.T) { var vapp govcd.VApp var vm govcd.VM @@ -240,17 +199,7 @@ resource "vcd_vapp_vm" "{{.VmName}}" { metadata = { vm_metadata = "VM Metadata." } - - override_template_disk { - bus_type = "paravirtual" - size_in_mb = "{{.InternalDiskSize}}" - bus_number = 0 - unit_number = 0 - iops = 0 - thin_provisioned = true - storage_profile = "{{.storageProfileName}}" - } - + network { name = vcd_network_routed.{{.NetworkName}}.name ip = "10.10.102.161" @@ -268,41 +217,6 @@ resource "vcd_vapp_vm" "{{.VmName}}" { output "disk" { value = tolist(vcd_vapp_vm.{{.VmName}}.disk)[0].name } - -output "internal_disk_size" { - value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].size_in_mb - depends_on = [vcd_vapp_vm.{{.VmName}}] -} - -output "internal_disk_iops" { - value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].iops - depends_on = [vcd_vapp_vm.{{.VmName}}] -} - -output "internal_disk_bus_type" { - value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].bus_type - depends_on = [vcd_vapp_vm.{{.VmName}}] -} - -output "internal_disk_bus_number" { - value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].bus_number - depends_on = [vcd_vapp_vm.{{.VmName}}] -} - -output "internal_disk_unit_number" { - value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].unit_number - depends_on = [vcd_vapp_vm.{{.VmName}}] -} - -output "internal_disk_thin_provisioned" { - value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].thin_provisioned - depends_on = [vcd_vapp_vm.{{.VmName}}] -} - -output "internal_disk_storage_profile" { - value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].storage_profile - depends_on = [vcd_vapp_vm.{{.VmName}}] -} ` const testAccCheckVcdVAppVm_clone = ` @@ -372,5 +286,4 @@ resource "vcd_vapp_vm" "{{.VmName2}}" { ip_allocation_mode = vcd_vapp_vm.{{.VmName}}.network.0.ip_allocation_mode } } - ` diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go index cd5ab7558..a26739dc7 100644 --- a/vcd/resource_vcd_vm_internal_disk_test.go +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -3,6 +3,8 @@ package vcd import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/terraform" "regexp" "testing" @@ -54,6 +56,7 @@ func TestAccVcdVmInternalDisk(t *testing.T) { return } _ = task.WaitTaskCompletion()*/ + internalDiskSize := 20000 storageProfile := testConfig.VCD.ProviderVdc.StorageProfile /* if *adminVdc.UsesFastProvisioning { @@ -85,7 +88,7 @@ func TestAccVcdVmInternalDisk(t *testing.T) { "BusType": busType, "BusNumber": busNumber, "UnitNumber": unitNumber, - "StorageProfileName": testConfig.VCD.ProviderVdc.StorageProfile, + "StorageProfileName": storageProfile, "AllowReboot": allowReboot, "VdcName": vdcName, @@ -106,6 +109,8 @@ func TestAccVcdVmInternalDisk(t *testing.T) { "VappName": vappName, "VmName": vmName, "ComputerName": vmName + "Unique", + + "InternalDiskSize": internalDiskSize, } params["FuncName"] = t.Name() + "-IdeCreate" configTextIde := templateFill(sourceTestVmInternalDiskIde, params) @@ -130,6 +135,7 @@ func TestAccVcdVmInternalDisk(t *testing.T) { resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "unit_number", "0"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "storage_profile", storageProfile), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "allow_vm_reboot", "false"), + testCheckInternalDiskNonStringOutputs(internalDiskSize), ), }, resource.TestStep{ @@ -198,6 +204,42 @@ func TestAccVcdVmInternalDisk(t *testing.T) { }) } +func testCheckInternalDiskNonStringOutputs(internalDiskSize int) resource.TestCheckFunc { + return func(s *terraform.State) error { + outputs := s.RootModule().Outputs + + if outputs["internal_disk_size"].Value != internalDiskSize { + return fmt.Errorf("internal disk size value didn't match") + } + + if outputs["internal_disk_iops"].Value != 0 { + return fmt.Errorf("internal disk iops value didn't match") + } + + if outputs["internal_disk_bus_type"].Value != "paravirtual" { + return fmt.Errorf("internal disk bus type value didn't match") + } + + if outputs["internal_disk_bus_number"].Value != 0 { + return fmt.Errorf("internal disk bus number value didn't match") + } + + if outputs["internal_disk_unit_number"].Value != 0 { + return fmt.Errorf("internal disk unit number value didn't match") + } + + if outputs["internal_disk_thin_provisioned"].Value != true { + return fmt.Errorf("internal disk thin provisioned value didn't match") + } + + if outputs["internal_disk_storage_profile"].Value != "*" { + return fmt.Errorf("internal disk storage profile value didn't match") + } + + return nil + } +} + /*func getAdminVdc() (*types.AdminVdc, error) { vcdClient, err := getTestVCDFromJson(testConfig) if err != nil { @@ -271,7 +313,53 @@ resource "vcd_vapp_vm" "{{.VmName}}" { memory = 1024 cpus = 1 cpu_cores = 1 + + override_template_disk { + bus_type = "paravirtual" + size_in_mb = "{{.InternalDiskSize}}" + bus_number = 0 + unit_number = 0 + iops = 0 + thin_provisioned = true + storage_profile = "{{.StorageProfileName}}" + } +} + +output "internal_disk_size" { + value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].size_in_mb + depends_on = [vcd_vapp_vm.{{.VmName}}] +} + +output "internal_disk_iops" { + value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].iops + depends_on = [vcd_vapp_vm.{{.VmName}}] +} + +output "internal_disk_bus_type" { + value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].bus_type + depends_on = [vcd_vapp_vm.{{.VmName}}] } + +output "internal_disk_bus_number" { + value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].bus_number + depends_on = [vcd_vapp_vm.{{.VmName}}] +} + +output "internal_disk_unit_number" { + value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].unit_number + depends_on = [vcd_vapp_vm.{{.VmName}}] +} + +output "internal_disk_thin_provisioned" { + value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].thin_provisioned + depends_on = [vcd_vapp_vm.{{.VmName}}] +} + +output "internal_disk_storage_profile" { + value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].storage_profile + depends_on = [vcd_vapp_vm.{{.VmName}}] +} + ` const sourceTestVmInternalDiskIde = sourceTestVmInternalDiskOrgVdcAndVM + ` From f0507435bc12d213fdc52cb2623d20e67619ffc4 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Tue, 7 Jan 2020 16:12:52 +0200 Subject: [PATCH 43/80] Improve documentation Signed-off-by: Vaidotas Bauzys --- website/docs/r/vm_internal_disk.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/vm_internal_disk.html.markdown b/website/docs/r/vm_internal_disk.html.markdown index 935d6955a..b5282aae0 100644 --- a/website/docs/r/vm_internal_disk.html.markdown +++ b/website/docs/r/vm_internal_disk.html.markdown @@ -14,7 +14,7 @@ Supported in provider *v2.6+* ## Example Usage -``` +```hcl resource "vcd_vm_internal_disk" "disk1" { vapp_name = "my-vapp" vm_name = "my-vm1" From d7468a7cb3d0b13c488a0f86a55cace995a02fba Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Tue, 7 Jan 2020 16:23:23 +0200 Subject: [PATCH 44/80] Improve documentation Signed-off-by: Vaidotas Bauzys --- website/docs/r/vapp_vm.html.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/docs/r/vapp_vm.html.markdown b/website/docs/r/vapp_vm.html.markdown index bc22d0f5d..fe062c228 100644 --- a/website/docs/r/vapp_vm.html.markdown +++ b/website/docs/r/vapp_vm.html.markdown @@ -216,6 +216,8 @@ example for usage details. **Deprecates**: `network_name`, `ip`, `vapp_network_n ## Override template disk +Allows to update internal disk in template before first VM boot. Disk are matched by `bus_type`, `bus_number` and `unit_number` +Changes are ignored on update. To manage internal disk later please use [`vcd_vm_internal_disk`](/docs/providers/vcd/r/vm_internal_disk.html) resource. * `bus_type` - (Required) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. * `size_in_mb` - (Required) The size of the disk in MB. From c328f91bc8be5801c04883630f686e3e05c61e94 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Tue, 7 Jan 2020 16:28:19 +0200 Subject: [PATCH 45/80] Improve documentation Signed-off-by: Vaidotas Bauzys --- website/docs/r/vm_internal_disk.html.markdown | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/website/docs/r/vm_internal_disk.html.markdown b/website/docs/r/vm_internal_disk.html.markdown index b5282aae0..2ab931f80 100644 --- a/website/docs/r/vm_internal_disk.html.markdown +++ b/website/docs/r/vm_internal_disk.html.markdown @@ -16,15 +16,15 @@ Supported in provider *v2.6+* ```hcl resource "vcd_vm_internal_disk" "disk1" { - vapp_name = "my-vapp" - vm_name = "my-vm1" - bus_type = "sata" - size_in_mb = "13333" - bus_number = 0 - unit_number = 1 + vapp_name = "my-vapp" + vm_name = "my-vm1" + bus_type = "sata" + size_in_mb = "13333" + bus_number = 0 + unit_number = 1 storage_profile = "Development" allow_vm_reboot = true - depends_on = ["vcd_vapp_vm.web1"] + depends_on = ["vcd_vapp_vm.web1"] } ``` From 0782758723cc55c3048479118a48454618a54abf Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 8 Jan 2020 09:01:08 +0200 Subject: [PATCH 46/80] Add supress func for override_template_disk Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vapp_vm.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index d79f0fd13..a978add50 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -243,10 +243,11 @@ func resourceVcdVAppVm() *schema.Resource { Set: resourceVcdVmIndependentDiskHash, }, "override_template_disk": { - Type: schema.TypeSet, - Optional: true, - ForceNew: true, - Description: " A block to match internal_disk interface in template. Multiple can be used. Disk will be matched by bus_type, bus_number and unit_number.", + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Description: " A block to match internal_disk interface in template. Multiple can be used. Disk will be matched by bus_type, bus_number and unit_number.", + DiffSuppressFunc: suppressAlways(), Elem: &schema.Resource{Schema: map[string]*schema.Schema{ "bus_type": { Type: schema.TypeString, From 7895068695e657190bb9f3465ee7f0b3bef159da Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 8 Jan 2020 09:11:42 +0200 Subject: [PATCH 47/80] Reverted supress Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vapp_vm.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index a978add50..d79f0fd13 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -243,11 +243,10 @@ func resourceVcdVAppVm() *schema.Resource { Set: resourceVcdVmIndependentDiskHash, }, "override_template_disk": { - Type: schema.TypeSet, - Optional: true, - ForceNew: true, - Description: " A block to match internal_disk interface in template. Multiple can be used. Disk will be matched by bus_type, bus_number and unit_number.", - DiffSuppressFunc: suppressAlways(), + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Description: " A block to match internal_disk interface in template. Multiple can be used. Disk will be matched by bus_type, bus_number and unit_number.", Elem: &schema.Resource{Schema: map[string]*schema.Schema{ "bus_type": { Type: schema.TypeString, From bd603795bf4647cc1be623a7c3645597ccb7a834 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 8 Jan 2020 13:33:28 +0200 Subject: [PATCH 48/80] Small improvements Signed-off-by: Vaidotas Bauzys --- vcd/config_test.go | 6 +- vcd/provider.go | 2 +- vcd/resource_vcd_edgegateway.go | 1 + vcd/resource_vcd_nsxv_firewall_rule.go | 1 + vcd/resource_vcd_nsxv_firewall_rule_test.go | 1 + vcd/resource_vcd_vapp_vm.go | 1 + vcd/resource_vcd_vm_internal_disk.go | 11 +-- vcd/resource_vcd_vm_internal_disk_test.go | 90 +++++++++---------- website/docs/r/independent_disk.html.markdown | 2 +- website/docs/r/vapp_vm.html.markdown | 20 ++--- website/docs/r/vm_internal_disk.html.markdown | 6 +- 11 files changed, 72 insertions(+), 69 deletions(-) diff --git a/vcd/config_test.go b/vcd/config_test.go index 0d723957b..933e6fe76 100644 --- a/vcd/config_test.go +++ b/vcd/config_test.go @@ -815,9 +815,9 @@ func importStateIdEdgeGatewayObject(vcd TestConfig, edgeGatewayName, objectName } // Used by all entities that depend on Org + VDC + vApp VM (such as VM internal disks) -func importStateIdVmObject(orgName, vdcName, vappName, vmName, objectName string) resource.ImportStateIdFunc { +func importStateIdVmObject(orgName, vdcName, vappName, vmName, objectIdentifier string) resource.ImportStateIdFunc { return func(*terraform.State) (string, error) { - if orgName == "" || vdcName == "" || vappName == "" || vmName == "" || objectName == "" { + if orgName == "" || vdcName == "" || vappName == "" || vmName == "" || objectIdentifier == "" { return "", fmt.Errorf("missing information to generate import path") } return orgName + @@ -828,6 +828,6 @@ func importStateIdVmObject(orgName, vdcName, vappName, vmName, objectName string ImportSeparator + vmName + ImportSeparator + - objectName, nil + objectIdentifier, nil } } diff --git a/vcd/provider.go b/vcd/provider.go index 774fe3921..c8c3a7f4a 100644 --- a/vcd/provider.go +++ b/vcd/provider.go @@ -130,7 +130,7 @@ func Provider() terraform.ResourceProvider { "vcd_nsxv_firewall_rule": resourceVcdNsxvFirewallRule(), // 2.5 "vcd_nsxv_dhcp_relay": resourceVcdNsxvDhcpRelay(), // 2.6 "vcd_nsxv_ip_set": resourceVcdIpSet(), // 2.6 - "vcd_vm_internal_disk": resourceVmInternalDisk(), // 2.6 + "vcd_vm_internal_disk": resourceVmInternalDisk(), // 2.7 "vcd_ipset": resourceVcdIpSet(), // 2.6 }, diff --git a/vcd/resource_vcd_edgegateway.go b/vcd/resource_vcd_edgegateway.go index afe4a0bad..0192bcdde 100644 --- a/vcd/resource_vcd_edgegateway.go +++ b/vcd/resource_vcd_edgegateway.go @@ -790,6 +790,7 @@ func getExternalNetworkData(vcdClient *VCDClient, d *schema.ResourceData, gatewa // we check what the user has set in the config and passing through the same value. // Note. If it wasn't "TypeSet" with more values - it would be possible to simply omit // setting the field. + // "<= 29" -> if vCD older or 9.0 if vcdClient.Client.APIVCDMaxVersionIs("<= 29") && origin == "resource" { log.Printf("[TRACE] edge gateway - skipping read of external networks on vCD 9.0 "+ "because for network %s it does not return these values", extNetwork.Network.Name) diff --git a/vcd/resource_vcd_nsxv_firewall_rule.go b/vcd/resource_vcd_nsxv_firewall_rule.go index 0d114cff1..25e682e44 100644 --- a/vcd/resource_vcd_nsxv_firewall_rule.go +++ b/vcd/resource_vcd_nsxv_firewall_rule.go @@ -301,6 +301,7 @@ func resourceVcdNsxvFirewallRuleUpdate(d *schema.ResourceData, meta interface{}) // IP set IDs (ipset-454 insted of a0c3c92a-a180-48fb-96ec-91c610c7c254:ipset-460) for update // operations only. Otherwise update operation fails with exception. var shortIds bool + // if vCD older or 9.0 if vcdClient.Client.APIVCDMaxVersionIs("<= 29") { log.Println("[DEBUG] vcd_nsxv_firewall_rule update - using short IDs for update operations because vCD is <= 9.0 ") shortIds = true diff --git a/vcd/resource_vcd_nsxv_firewall_rule_test.go b/vcd/resource_vcd_nsxv_firewall_rule_test.go index 223926fa3..e6a5f9298 100644 --- a/vcd/resource_vcd_nsxv_firewall_rule_test.go +++ b/vcd/resource_vcd_nsxv_firewall_rule_test.go @@ -1057,6 +1057,7 @@ func TestAccVcdNsxvEdgeFirewallRuleVms(t *testing.T) { if err != nil { t.Skip("unable to validate vCD version - skipping test") } + // if vCD older or 9.0 if vcdClient.Client.APIVCDMaxVersionIs("= 29.0") { t.Skip("Skipping this test for vCD 9.0 because of a known issue: " + "https://github.com/terraform-providers/terraform-provider-vcd/issues/420") diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index d79f0fd13..64c14da90 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -1255,6 +1255,7 @@ func updateTemplateInternalDisks(d *schema.ResourceData, meta interface{}, vm go return nil } +// getMatchedDisk returns matched disk by adapter type, bus number and unit number func getMatchedDisk(internalDiskProvidedConfig map[string]interface{}, diskSettings []*types.DiskSettings) *types.DiskSettings { for _, diskSetting := range diskSettings { if diskSetting.AdapterType == internalDiskBusTypes[internalDiskProvidedConfig["bus_type"].(string)] && diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index 7ec2f7a00..aadec878b 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -44,13 +44,13 @@ func resourceVmInternalDisk() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - Description: "VM in vApp in which internal disk created", + Description: "VM in vApp in which internal disk is created", }, "allow_vm_reboot": { - Type: schema.TypeBool, - Optional: true, - Default: false, - Description: "Powers off VM when changing any attribute of an IDE disk or unit/bus number of other disk types, after the change is complete VM is powered back on. Without this setting enabled, such changes on a powered on VM would fail.", + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: " Powers off VM when changing any attribute of an IDE disk or unit/bus number of other disk types, after the change is complete VM is powered back on. Without this setting enabled, such changes on a powered-on VM would fail.", }, "bus_type": { Type: schema.TypeString, @@ -298,6 +298,7 @@ func resourceVmInternalDiskUpdate(d *schema.ResourceData, meta interface{}) erro //diskSettingsToUpdate.BusNumber = d.Get("bus_number").(int) //diskSettingsToUpdate.AdapterType = internalDiskBusTypes[d.Get("bus_type").(string)] diskSettingsToUpdate.SizeMb = int64(d.Get("size_in_mb").(int)) + // Note can't change adapter type, bus number, unit number as vSphere changes diskId var storageProfilePrt *types.Reference var overrideVmDefault bool diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go index a26739dc7..630de4433 100644 --- a/vcd/resource_vcd_vm_internal_disk_test.go +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -12,13 +12,12 @@ import ( ) func TestAccVcdVmInternalDisk(t *testing.T) { - // This test requires access to the vCD before filling templates // Thus it won't run in the short test if vcdShortTest { t.Skip(acceptanceTestsSkipped) return } - // In general VM internal disks works with Org users, but due we need create VDC with disabled fast provisioning value, we have to be sys admins + // In general VM internal disks works with Org users, but since we need to create VDC with disabled fast provisioning value, we have to be sys admins if !usingSysAdmin() { t.Skip("VM internal disks tests requires system admin privileges") return @@ -100,7 +99,7 @@ func TestAccVcdVmInternalDisk(t *testing.T) { "Reserved": "1024", "Limit": "1024", "ProviderVdcStorageProfile": testConfig.VCD.ProviderVdc.StorageProfile, - // cause vDC ignores empty values and use default + // because vDC ignores empty values and use default "MemoryGuaranteed": "1", "CpuGuaranteed": "1", @@ -363,43 +362,42 @@ output "internal_disk_storage_profile" { ` const sourceTestVmInternalDiskIde = sourceTestVmInternalDiskOrgVdcAndVM + ` -resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { - org = "{{.Org}}" - vdc = vcd_org_vdc.{{.VdcName}}.name - vapp_name = vcd_vapp.{{.VappName}}.name - vm_name = vcd_vapp_vm.{{.VmName}}.name - bus_type = "ide" - size_in_mb = "{{.Size}}" - bus_number = "0" - unit_number = "0" - storage_profile = "{{.StorageProfileName}}" - allow_vm_reboot = "false" +resource "vcd_vm_internal_disk" "imported" { + vapp_name = "vApp_system_1" + vm_name = "TerraformDisk1" + bus_type = "paravirtual" + size_in_mb = "22384" + bus_number = 0 + unit_number = 0 + #storage_profile = "Development" + #allow_vm_reboot = true + #depends_on = ["vcd_vapp_vm.Override3Disks3"] } ` const sourceTestVmInternalDisk = sourceTestVmInternalDiskOrgVdcAndVM + ` resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { - org = "{{.Org}}" - vdc = vcd_org_vdc.{{.VdcName}}.name - vapp_name = vcd_vapp.{{.VappName}}.name - vm_name = vcd_vapp_vm.{{.VmName}}.name - bus_type = "ide" - size_in_mb = "{{.Size}}" - bus_number = "0" - unit_number = "0" + org = "{{.Org}}" + vdc = vcd_org_vdc.{{.VdcName}}.name + vapp_name = vcd_vapp.{{.VappName}}.name + vm_name = vcd_vapp_vm.{{.VmName}}.name + bus_type = "ide" + size_in_mb = "{{.Size}}" + bus_number = "0" + unit_number = "0" storage_profile = "{{.StorageProfileName}}" allow_vm_reboot = "true" } resource "vcd_vm_internal_disk" "{{.DiskResourceName}}" { - org = "{{.Org}}" - vdc = vcd_org_vdc.{{.VdcName}}.name - vapp_name = vcd_vapp.{{.VappName}}.name - vm_name = vcd_vapp_vm.{{.VmName}}.name - bus_type = "{{.BusType}}" - size_in_mb = "{{.Size}}" - bus_number = "{{.BusNumber}}" - unit_number = "{{.UnitNumber}}" + org = "{{.Org}}" + vdc = vcd_org_vdc.{{.VdcName}}.name + vapp_name = vcd_vapp.{{.VappName}}.name + vm_name = vcd_vapp_vm.{{.VmName}}.name + bus_type = "{{.BusType}}" + size_in_mb = "{{.Size}}" + bus_number = "{{.BusNumber}}" + unit_number = "{{.UnitNumber}}" storage_profile = "{{.StorageProfileName}}" allow_vm_reboot = "false" } @@ -407,27 +405,27 @@ resource "vcd_vm_internal_disk" "{{.DiskResourceName}}" { const sourceTestVmInternalDisk_Update1 = sourceTestVmInternalDiskOrgVdcAndVM + ` resource "vcd_vm_internal_disk" "{{.DiskResourceName}}" { - org = "{{.Org}}" - vdc = vcd_org_vdc.{{.VdcName}}.name - vapp_name = vcd_vapp.{{.VappName}}.name - vm_name = vcd_vapp_vm.{{.VmName}}.name - bus_type = "{{.BusType}}" - size_in_mb = "{{.SizeBigger}}" - bus_number = "{{.BusNumber}}" - unit_number = "{{.UnitNumber}}" + org = "{{.Org}}" + vdc = vcd_org_vdc.{{.VdcName}}.name + vapp_name = vcd_vapp.{{.VappName}}.name + vm_name = vcd_vapp_vm.{{.VmName}}.name + bus_type = "{{.BusType}}" + size_in_mb = "{{.SizeBigger}}" + bus_number = "{{.BusNumber}}" + unit_number = "{{.UnitNumber}}" storage_profile = "{{.StorageProfileName}}" allow_vm_reboot = "false" } resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { - org = "{{.Org}}" - vdc = vcd_org_vdc.{{.VdcName}}.name - vapp_name = vcd_vapp.{{.VappName}}.name - vm_name = vcd_vapp_vm.{{.VmName}}.name - bus_type = "ide" - size_in_mb = "{{.SizeBigger}}" - bus_number = "0" - unit_number = "0" + org = "{{.Org}}" + vdc = vcd_org_vdc.{{.VdcName}}.name + vapp_name = vcd_vapp.{{.VappName}}.name + vm_name = vcd_vapp_vm.{{.VmName}}.name + bus_type = "ide" + size_in_mb = "{{.SizeBigger}}" + bus_number = "0" + unit_number = "0" storage_profile = "{{.StorageProfileName}}" allow_vm_reboot = "true" } diff --git a/website/docs/r/independent_disk.html.markdown b/website/docs/r/independent_disk.html.markdown index c742b9e3a..0159f3c31 100644 --- a/website/docs/r/independent_disk.html.markdown +++ b/website/docs/r/independent_disk.html.markdown @@ -94,7 +94,7 @@ further operations. If you want to list IDs there is a special command **`terraform import vcd_independent_disk.imported list@org-name.vdc-name.my-independent-disk-name`** where `org-name` is the organization used, `vdc-name` is vDC name and `my-independent-disk-name` -is independent disk name. The output for this command should look similar to below one: +is independent disk name. The output for this command should look similar to the one below: ```shell $ terraform import vcd_independent_disk.imported list@org-name.vdc-name.my-independent-disk-name diff --git a/website/docs/r/vapp_vm.html.markdown b/website/docs/r/vapp_vm.html.markdown index fe062c228..4a26670ee 100644 --- a/website/docs/r/vapp_vm.html.markdown +++ b/website/docs/r/vapp_vm.html.markdown @@ -174,7 +174,7 @@ example for usage details. **Deprecates**: `network_name`, `ip`, `vapp_network_n * `guest_properties` - (Optional; *v2.5+*) Key value map of guest properties * `description` - (Computed; *v2.6+*) The VM description. Note: description is read only. Currently, this field has the description of the OVA used to create the VM -* `override_template_disk` - (Optional; *v2.6+*) Allows to update internal disk in template before first VM boot. Disk are matched by `bus_type`, `bus_number` and `unit_number`. See [Override template Disk](#override_template_disk) below for details. +* `override_template_disk` - (Optional; *v2.7+*) Allows to update internal disk in template before first VM boot. Disk are matched by `bus_type`, `bus_number` and `unit_number`. See [Override template Disk](#override_template_disk) below for details. ## Disk @@ -299,19 +299,19 @@ resource "vcd_vapp_vm" "web2" { The following additional attributes are exported: -* `internal_disk` - (*v2.6+*) A block provides internal disk of VM details. See [Internal Disk](#internalDisk) below for details. +* `internal_disk` - (*v2.7+*) A block providing internal disk of VM details. See [Internal Disk](#internalDisk) below for details. ## Internal disk -* `disk_id` - (*v2.6+*) Specifies a unique identifier for this disk in the scope of the corresponding VM. -* `bus_type` - (*v2.6+*) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. -* `size_in_mb` - (*v2.6+*) The size of the disk in MB. -* `bus_number` - (*v2.6+*) The number of the SCSI or IDE controller itself. -* `unit_number` - (*v2.6+*) The device number on the SCSI or IDE controller of the disk. -* `thin_provisioned` - (*v2.6+*) Specifies whether the disk storage is pre-allocated or allocated on demand. -* `iops` - (*v2.6+*) Specifies the IOPS for the disk. Default - 0. -* `storage_profile` - (*v2.6+*) Storage profile which overrides the VM default one. +* `disk_id` - (*v2.7+*) Specifies a unique identifier for this disk in the scope of the corresponding VM. +* `bus_type` - (*v2.7+*) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. +* `size_in_mb` - (*v2.7+*) The size of the disk in MB. +* `bus_number` - (*v2.7+*) The number of the SCSI or IDE controller itself. +* `unit_number` - (*v2.7+*) The device number on the SCSI or IDE controller of the disk. +* `thin_provisioned` - (*v2.7+*) Specifies whether the disk storage is pre-allocated or allocated on demand. +* `iops` - (*v2.7+*) Specifies the IOPS for the disk. Default - 0. +* `storage_profile` - (*v2.7+*) Storage profile which overrides the VM default one. ## Importing diff --git a/website/docs/r/vm_internal_disk.html.markdown b/website/docs/r/vm_internal_disk.html.markdown index 2ab931f80..02276eeab 100644 --- a/website/docs/r/vm_internal_disk.html.markdown +++ b/website/docs/r/vm_internal_disk.html.markdown @@ -10,7 +10,7 @@ description: |- Provides a vCloud Director VM internal disk resource. This can be used to create and delete VM internal disks. -Supported in provider *v2.6+* +Supported in provider *v2.7+* ## Example Usage @@ -35,8 +35,8 @@ The following arguments are supported: * `org` - (Optional) The name of organization to use, optional if defined at provider level. Useful when connected as sysadmin working across different organisations * `vdc` - (Optional) The name of VDC to use, optional if defined at provider level * `vapp_name` - (Required) The vApp this VM internal disk belongs to. -* `vm_name` - (Required) VM in vApp in which internal disk created. -* `allow_vm_reboot` - (Optional) Powers off VM when changing any attribute of an IDE disk or unit/bus number of other disk types, after the change is complete VM is powered back on. Without this setting enabled, such changes on a powered on VM would fail. +* `vm_name` - (Required) VM in vApp in which internal disk is created. +* `allow_vm_reboot` - (Optional) Powers off VM when changing any attribute of an IDE disk or unit/bus number of other disk types, after the change is complete VM is powered back on. Without this setting enabled, such changes on a powered-on VM would fail. * `bus_type` - (Required) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. * `size_in_mb` - (Required) The size of the disk in MB. * `bus_number` - (Required) The number of the SCSI or IDE controller itself. From 8de260047cbdaf6700f194d55a70ecff9bd26e63 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 8 Jan 2020 13:37:02 +0200 Subject: [PATCH 49/80] Small improvement in doc Signed-off-by: Vaidotas Bauzys --- website/docs/r/vm_internal_disk.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/vm_internal_disk.html.markdown b/website/docs/r/vm_internal_disk.html.markdown index 02276eeab..f4f27d617 100644 --- a/website/docs/r/vm_internal_disk.html.markdown +++ b/website/docs/r/vm_internal_disk.html.markdown @@ -81,7 +81,7 @@ further operations. If you want to list IDs there is a special command **`terraform import vcd_vm_internal_disk.imported list@org-name.vcd-name.vapp-name.vm-name`** where `org-name` is the organization used, `vdc-name` is vDC name, `vapp-name` is vAPP name and `vm_name` is VM name in that vAPP. -The output for this command should look similar to below one: +The output for this command should look similar to the one below: ```shell $ terraform import vcd_vm_internal_disk.imported list@org-name.vdc-name.vapp-name.vm-name From 4451115848df96809cee57067341a5b74e2c1801 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 8 Jan 2020 15:45:39 +0200 Subject: [PATCH 50/80] Add improvements to doc Signed-off-by: Vaidotas Bauzys --- website/docs/r/vapp_vm.html.markdown | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/website/docs/r/vapp_vm.html.markdown b/website/docs/r/vapp_vm.html.markdown index 4a26670ee..325bd1445 100644 --- a/website/docs/r/vapp_vm.html.markdown +++ b/website/docs/r/vapp_vm.html.markdown @@ -114,8 +114,19 @@ resource "vcd_vapp_vm" "web2" { depends_on = ["vcd_vapp.web"] } +## Example Usage (Override Template Disk) +resource "vcd_network_direct" "net" { + name = "net" + external_network = "corp-network" +} + +resource "vcd_vapp" "web" { + name = "web" + depends_on = ["vcd_network_direct.net"] +} + resource "vcd_vapp_vm" "internalDiskOverride" { - vapp_name = "${vcd_vapp.web.name}" + vapp_name = vcd_vapp.web.name name = "internalDiskOverride" catalog_name = "Boxes" template_name = "lampstack-1.10.1-ubuntu-10.04" @@ -132,8 +143,6 @@ resource "vcd_vapp_vm" "internalDiskOverride" { thin_provisioned = true storage_profile = "*" } - - depends_on = ["vcd_vapp.web"] } ``` @@ -218,6 +227,7 @@ example for usage details. **Deprecates**: `network_name`, `ip`, `vapp_network_n ## Override template disk Allows to update internal disk in template before first VM boot. Disk are matched by `bus_type`, `bus_number` and `unit_number` Changes are ignored on update. To manage internal disk later please use [`vcd_vm_internal_disk`](/docs/providers/vcd/r/vm_internal_disk.html) resource. +Managing disks in VM allowed if vDC isn't fast provisioned. * `bus_type` - (Required) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. * `size_in_mb` - (Required) The size of the disk in MB. From 97da6e442a78ef0adda000d179cf536ead1ffca6 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 8 Jan 2020 16:18:54 +0200 Subject: [PATCH 51/80] Add improvements to doc Signed-off-by: Vaidotas Bauzys --- website/docs/r/vapp_vm.html.markdown | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/website/docs/r/vapp_vm.html.markdown b/website/docs/r/vapp_vm.html.markdown index 325bd1445..53037588a 100644 --- a/website/docs/r/vapp_vm.html.markdown +++ b/website/docs/r/vapp_vm.html.markdown @@ -113,8 +113,11 @@ resource "vcd_vapp_vm" "web2" { depends_on = ["vcd_vapp.web"] } +``` ## Example Usage (Override Template Disk) + +```hcl resource "vcd_network_direct" "net" { name = "net" external_network = "corp-network" @@ -183,7 +186,7 @@ example for usage details. **Deprecates**: `network_name`, `ip`, `vapp_network_n * `guest_properties` - (Optional; *v2.5+*) Key value map of guest properties * `description` - (Computed; *v2.6+*) The VM description. Note: description is read only. Currently, this field has the description of the OVA used to create the VM -* `override_template_disk` - (Optional; *v2.7+*) Allows to update internal disk in template before first VM boot. Disk are matched by `bus_type`, `bus_number` and `unit_number`. See [Override template Disk](#override_template_disk) below for details. +* `override_template_disk` - (Optional; *v2.7+*) Allows to update internal disk in template before first VM boot. Disk are matched by `bus_type`, `bus_number` and `unit_number`. See [Override template Disk](#override-template-disk) below for details. ## Disk @@ -223,7 +226,7 @@ example for usage details. **Deprecates**: `network_name`, `ip`, `vapp_network_n * `ip_allocation_mode=NONE` - **`ip`** field can be omitted or set to an empty string "". Empty string may be useful when doing HCL variable interpolation. - + ## Override template disk Allows to update internal disk in template before first VM boot. Disk are matched by `bus_type`, `bus_number` and `unit_number` Changes are ignored on update. To manage internal disk later please use [`vcd_vm_internal_disk`](/docs/providers/vcd/r/vm_internal_disk.html) resource. From ec0eac7047d85a89db2ba716cce157d291d35a06 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 8 Jan 2020 16:46:48 +0200 Subject: [PATCH 52/80] Removed thin provisioning from override template disk Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vapp_vm.go | 13 ++------ vcd/resource_vcd_vm_internal_disk_test.go | 31 +++++++------------ website/docs/r/vapp_vm.html.markdown | 2 -- website/docs/r/vm_internal_disk.html.markdown | 2 +- 4 files changed, 15 insertions(+), 33 deletions(-) diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index 64c14da90..e47f4a97d 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -273,12 +273,6 @@ func resourceVcdVAppVm() *schema.Resource { Required: true, Description: "The device number on the SCSI or IDE controller of the disk.", }, - "thin_provisioned": { - Type: schema.TypeBool, - ForceNew: true, - Optional: true, - Description: "Specifies whether the disk storage is pre-allocated or allocated on demand.", - }, "iops": { Type: schema.TypeInt, ForceNew: true, @@ -1235,10 +1229,9 @@ func updateTemplateInternalDisks(d *schema.ResourceData, meta interface{}, vm go diskCreatedByTemplate.Iops = &iops } - if value, ok := internalDiskProvidedConfig["thin_provisioned"]; ok { - thinProvisioned := value.(bool) - diskCreatedByTemplate.ThinProvisioned = &thinProvisioned - } + // value is required but not treated. + isThinProvisioned := true + diskCreatedByTemplate.ThinProvisioned = &isThinProvisioned diskCreatedByTemplate.SizeMb = int64(internalDiskProvidedConfig["size_in_mb"].(int)) diskCreatedByTemplate.StorageProfile = storageProfilePrt diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go index 630de4433..24d6c8168 100644 --- a/vcd/resource_vcd_vm_internal_disk_test.go +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -227,10 +227,6 @@ func testCheckInternalDiskNonStringOutputs(internalDiskSize int) resource.TestCh return fmt.Errorf("internal disk unit number value didn't match") } - if outputs["internal_disk_thin_provisioned"].Value != true { - return fmt.Errorf("internal disk thin provisioned value didn't match") - } - if outputs["internal_disk_storage_profile"].Value != "*" { return fmt.Errorf("internal disk storage profile value didn't match") } @@ -319,7 +315,6 @@ resource "vcd_vapp_vm" "{{.VmName}}" { bus_number = 0 unit_number = 0 iops = 0 - thin_provisioned = true storage_profile = "{{.StorageProfileName}}" } } @@ -349,11 +344,6 @@ output "internal_disk_unit_number" { depends_on = [vcd_vapp_vm.{{.VmName}}] } -output "internal_disk_thin_provisioned" { - value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].thin_provisioned - depends_on = [vcd_vapp_vm.{{.VmName}}] -} - output "internal_disk_storage_profile" { value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].storage_profile depends_on = [vcd_vapp_vm.{{.VmName}}] @@ -362,16 +352,17 @@ output "internal_disk_storage_profile" { ` const sourceTestVmInternalDiskIde = sourceTestVmInternalDiskOrgVdcAndVM + ` -resource "vcd_vm_internal_disk" "imported" { - vapp_name = "vApp_system_1" - vm_name = "TerraformDisk1" - bus_type = "paravirtual" - size_in_mb = "22384" - bus_number = 0 - unit_number = 0 - #storage_profile = "Development" - #allow_vm_reboot = true - #depends_on = ["vcd_vapp_vm.Override3Disks3"] +resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { + org = "{{.Org}}" + vdc = vcd_org_vdc.{{.VdcName}}.name + vapp_name = vcd_vapp.{{.VappName}}.name + vm_name = vcd_vapp_vm.{{.VmName}}.name + bus_type = "ide" + size_in_mb = "{{.Size}}" + bus_number = "0" + unit_number = "0" + storage_profile = "{{.StorageProfileName}}" + allow_vm_reboot = "false" } ` diff --git a/website/docs/r/vapp_vm.html.markdown b/website/docs/r/vapp_vm.html.markdown index 53037588a..eabfda5f5 100644 --- a/website/docs/r/vapp_vm.html.markdown +++ b/website/docs/r/vapp_vm.html.markdown @@ -143,7 +143,6 @@ resource "vcd_vapp_vm" "internalDiskOverride" { bus_number = 0 unit_number = 0 iops = 0 - thin_provisioned = true storage_profile = "*" } } @@ -236,7 +235,6 @@ Managing disks in VM allowed if vDC isn't fast provisioned. * `size_in_mb` - (Required) The size of the disk in MB. * `bus_number` - (Required) The number of the SCSI or IDE controller itself. * `unit_number` - (Required) The device number on the SCSI or IDE controller of the disk. -* `thin_provisioned` - (Optional) Specifies whether the disk storage is pre-allocated or allocated on demand. * `iops` - (Optional) Specifies the IOPS for the disk. Default - 0. * `storage_profile` - (Optional) Storage profile which overrides the VM default one. diff --git a/website/docs/r/vm_internal_disk.html.markdown b/website/docs/r/vm_internal_disk.html.markdown index f4f27d617..1f1ad7454 100644 --- a/website/docs/r/vm_internal_disk.html.markdown +++ b/website/docs/r/vm_internal_disk.html.markdown @@ -36,7 +36,7 @@ The following arguments are supported: * `vdc` - (Optional) The name of VDC to use, optional if defined at provider level * `vapp_name` - (Required) The vApp this VM internal disk belongs to. * `vm_name` - (Required) VM in vApp in which internal disk is created. -* `allow_vm_reboot` - (Optional) Powers off VM when changing any attribute of an IDE disk or unit/bus number of other disk types, after the change is complete VM is powered back on. Without this setting enabled, such changes on a powered-on VM would fail. +* `allow_vm_reboot` - (Optional) Powers off VM when changing any attribute of an IDE disk or unit/bus number of other disk types, after the change is complete VM is powered back on. Without this setting enabled, such changes on a powered-on VM would fail. Defaults to false. * `bus_type` - (Required) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. * `size_in_mb` - (Required) The size of the disk in MB. * `bus_number` - (Required) The number of the SCSI or IDE controller itself. From 803f2a88b7c763f66914ba4c955f903d49836216 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 9 Jan 2020 09:38:59 +0200 Subject: [PATCH 53/80] Changes for binary test to work Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk_test.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go index 24d6c8168..d6e5426b9 100644 --- a/vcd/resource_vcd_vm_internal_disk_test.go +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -12,11 +12,7 @@ import ( ) func TestAccVcdVmInternalDisk(t *testing.T) { - // Thus it won't run in the short test - if vcdShortTest { - t.Skip(acceptanceTestsSkipped) - return - } + // In general VM internal disks works with Org users, but since we need to create VDC with disabled fast provisioning value, we have to be sys admins if !usingSysAdmin() { t.Skip("VM internal disks tests requires system admin privileges") @@ -121,6 +117,12 @@ func TestAccVcdVmInternalDisk(t *testing.T) { //configText_update2 := templateFill(sourceTestVmInternalDisk_Update2, params) debugPrintf("#[DEBUG] CONFIGURATION: %s", configText+configText_update1) + // Thus it won't run in the short test + if vcdShortTest { + t.Skip(acceptanceTestsSkipped) + return + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -352,6 +354,7 @@ output "internal_disk_storage_profile" { ` const sourceTestVmInternalDiskIde = sourceTestVmInternalDiskOrgVdcAndVM + ` +# skip-binary-test: expected to fail for allow_vm_reboot=false and bus_type = "ide" resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { org = "{{.Org}}" vdc = vcd_org_vdc.{{.VdcName}}.name From 734559aa53d00e5dad07ab186f3aa23ec825195d Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 9 Jan 2020 10:04:07 +0200 Subject: [PATCH 54/80] Improve comments Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk_test.go | 3 ++- website/docs/r/vapp_vm.html.markdown | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go index d6e5426b9..d7bd5fafe 100644 --- a/vcd/resource_vcd_vm_internal_disk_test.go +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -128,7 +128,8 @@ func TestAccVcdVmInternalDisk(t *testing.T) { Providers: testAccProviders, Steps: []resource.TestStep{ resource.TestStep{ - Config: configTextIde, + Config: configTextIde, + // expected to fail for allow_vm_reboot=false and bus_type = "ide" (VM needs to be power off to add IDE disk) ExpectError: regexp.MustCompile(`.*The attempted operation cannot be performed in the current state \(Powered on\).*`), Check: resource.ComposeTestCheckFunc(resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "size_in_mb", diskSize), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_type", "ide"), diff --git a/website/docs/r/vapp_vm.html.markdown b/website/docs/r/vapp_vm.html.markdown index eabfda5f5..b64e6202c 100644 --- a/website/docs/r/vapp_vm.html.markdown +++ b/website/docs/r/vapp_vm.html.markdown @@ -229,7 +229,7 @@ example for usage details. **Deprecates**: `network_name`, `ip`, `vapp_network_n ## Override template disk Allows to update internal disk in template before first VM boot. Disk are matched by `bus_type`, `bus_number` and `unit_number` Changes are ignored on update. To manage internal disk later please use [`vcd_vm_internal_disk`](/docs/providers/vcd/r/vm_internal_disk.html) resource. -Managing disks in VM allowed if vDC isn't fast provisioned. +Managing disks in VM allowed if VDC isn't fast provisioned. * `bus_type` - (Required) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. * `size_in_mb` - (Required) The size of the disk in MB. From 6643176e040d0df467cf6d9b8af02f4be05b5ea3 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 9 Jan 2020 11:40:30 +0200 Subject: [PATCH 55/80] Improve docs Signed-off-by: Vaidotas Bauzys --- website/docs/r/vapp_vm.html.markdown | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/website/docs/r/vapp_vm.html.markdown b/website/docs/r/vapp_vm.html.markdown index b64e6202c..c0b6c3c87 100644 --- a/website/docs/r/vapp_vm.html.markdown +++ b/website/docs/r/vapp_vm.html.markdown @@ -118,16 +118,6 @@ resource "vcd_vapp_vm" "web2" { ## Example Usage (Override Template Disk) ```hcl -resource "vcd_network_direct" "net" { - name = "net" - external_network = "corp-network" -} - -resource "vcd_vapp" "web" { - name = "web" - depends_on = ["vcd_network_direct.net"] -} - resource "vcd_vapp_vm" "internalDiskOverride" { vapp_name = vcd_vapp.web.name name = "internalDiskOverride" From 54759fb562e55f28e07fedf67572ecc0335c3b83 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 9 Jan 2020 11:46:26 +0200 Subject: [PATCH 56/80] Improve docs Signed-off-by: Vaidotas Bauzys --- website/docs/r/vapp_vm.html.markdown | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/website/docs/r/vapp_vm.html.markdown b/website/docs/r/vapp_vm.html.markdown index c0b6c3c87..ceeb221d5 100644 --- a/website/docs/r/vapp_vm.html.markdown +++ b/website/docs/r/vapp_vm.html.markdown @@ -218,8 +218,9 @@ example for usage details. **Deprecates**: `network_name`, `ip`, `vapp_network_n ## Override template disk Allows to update internal disk in template before first VM boot. Disk are matched by `bus_type`, `bus_number` and `unit_number` -Changes are ignored on update. To manage internal disk later please use [`vcd_vm_internal_disk`](/docs/providers/vcd/r/vm_internal_disk.html) resource. -Managing disks in VM allowed if VDC isn't fast provisioned. +Changes are ignored on update. To manage internal disk later please use [`vcd_vm_internal_disk`](/docs/providers/vcd/r/vm_internal_disk.html) resource. + +~> **Note:** Managing disks in VM is possible only when VDC fast provisioned is disabled. * `bus_type` - (Required) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. * `size_in_mb` - (Required) The size of the disk in MB. From 35a9b084857e04371e60a0c58bd23854cefd1227 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 9 Jan 2020 11:52:25 +0200 Subject: [PATCH 57/80] Improve docs Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk.go | 2 +- website/docs/r/vm_internal_disk.html.markdown | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index aadec878b..03409c722 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -424,7 +424,7 @@ func listInternalDisksForImport(meta interface{}, orgName, vdcName, vappName, vm return nil, fmt.Errorf("[Error] failed to get VM: %s", err) } - _, _ = fmt.Fprintln(getTerraformStdout(), "Retrieving all disks by name") + _, _ = fmt.Fprintln(getTerraformStdout(), "Retrieving all disks") if vm.VM.VmSpecSection.DiskSection == nil || vm.VM.VmSpecSection.DiskSection.DiskSettings == nil || len(vm.VM.VmSpecSection.DiskSection.DiskSettings) == 0 { return nil, fmt.Errorf("no internal disks found on VM: %s", vmName) diff --git a/website/docs/r/vm_internal_disk.html.markdown b/website/docs/r/vm_internal_disk.html.markdown index 1f1ad7454..b9abf5559 100644 --- a/website/docs/r/vm_internal_disk.html.markdown +++ b/website/docs/r/vm_internal_disk.html.markdown @@ -34,8 +34,8 @@ The following arguments are supported: * `org` - (Optional) The name of organization to use, optional if defined at provider level. Useful when connected as sysadmin working across different organisations * `vdc` - (Optional) The name of VDC to use, optional if defined at provider level -* `vapp_name` - (Required) The vApp this VM internal disk belongs to. -* `vm_name` - (Required) VM in vApp in which internal disk is created. +* `vapp_name` - (Required) The vAPP this VM internal disk belongs to. +* `vm_name` - (Required) VM in vAPP in which internal disk is created. * `allow_vm_reboot` - (Optional) Powers off VM when changing any attribute of an IDE disk or unit/bus number of other disk types, after the change is complete VM is powered back on. Without this setting enabled, such changes on a powered-on VM would fail. Defaults to false. * `bus_type` - (Required) The type of disk controller. Possible values: `ide`, `parallel`( LSI Logic Parallel SCSI), `sas`(LSI Logic SAS (SCSI)), `paravirtual`(Paravirtual (SCSI)), `sata`. * `size_in_mb` - (Required) The size of the disk in MB. @@ -86,7 +86,7 @@ The output for this command should look similar to the one below: ```shell $ terraform import vcd_vm_internal_disk.imported list@org-name.vdc-name.vapp-name.vm-name vcd_vm_internal_disk.imported: Importing from ID "list@org-name.vdc-name.vapp-name.vm-name"... -Retrieving all disks by name +Retrieving all disks No ID BusType BusNumber UnitNumber Size StoragePofile Iops ThinProvisioned -- -- ------- --------- ---------- ---- ------------- ---- --------------- 1 2000 paravirtual 0 0 16384 * 0 true From bc20d55c991dfb7c3747ed1488462bad431816e3 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 9 Jan 2020 12:10:33 +0200 Subject: [PATCH 58/80] Improve comments Signed-off-by: Vaidotas Bauzys --- vcd/provider.go | 1 - vcd/resource_vcd_vapp_vm.go | 2 +- vcd/resource_vcd_vm_internal_disk.go | 10 +++++----- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/vcd/provider.go b/vcd/provider.go index c8c3a7f4a..06730abdc 100644 --- a/vcd/provider.go +++ b/vcd/provider.go @@ -131,7 +131,6 @@ func Provider() terraform.ResourceProvider { "vcd_nsxv_dhcp_relay": resourceVcdNsxvDhcpRelay(), // 2.6 "vcd_nsxv_ip_set": resourceVcdIpSet(), // 2.6 "vcd_vm_internal_disk": resourceVmInternalDisk(), // 2.7 - "vcd_ipset": resourceVcdIpSet(), // 2.6 }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index e47f4a97d..fc174b12e 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -246,7 +246,7 @@ func resourceVcdVAppVm() *schema.Resource { Type: schema.TypeSet, Optional: true, ForceNew: true, - Description: " A block to match internal_disk interface in template. Multiple can be used. Disk will be matched by bus_type, bus_number and unit_number.", + Description: "A block to match internal_disk interface in template. Multiple can be used. Disk will be matched by bus_type, bus_number and unit_number.", Elem: &schema.Resource{Schema: map[string]*schema.Schema{ "bus_type": { Type: schema.TypeString, diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index 03409c722..d2582f25d 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -47,10 +47,10 @@ func resourceVmInternalDisk() *schema.Resource { Description: "VM in vApp in which internal disk is created", }, "allow_vm_reboot": { - Type: schema.TypeBool, - Optional: true, - Default: false, - Description: " Powers off VM when changing any attribute of an IDE disk or unit/bus number of other disk types, after the change is complete VM is powered back on. Without this setting enabled, such changes on a powered-on VM would fail.", + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Powers off VM when changing any attribute of an IDE disk or unit/bus number of other disk types, after the change is complete VM is powered back on. Without this setting enabled, such changes on a powered-on VM would fail.", }, "bus_type": { Type: schema.TypeString, @@ -367,7 +367,7 @@ var errHelpInternalDiskImport = fmt.Errorf(`resource id must be specified in one 'org-name.vdc-name.vapp-name.vm-name.my-internal-disk-id' to import by rule id 'list@org-name.vdc-name.vapp-name.vm-name' to get a list of internal disks with their IDs`) -// resourceVcdIndependentDiskImport is responsible for importing the resource. +// resourceVcdVmInternalDiskImport is responsible for importing the resource. // The following steps happen as part of import // 1. The user supplies `terraform import _resource_name_ _the_id_string_` command // 2a. If the `_the_id_string_` contains a dot formatted path to resource as in the example below From d0b7480c7204fe9f16576d8113ab5c64e29669d9 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 9 Jan 2020 15:21:20 +0200 Subject: [PATCH 59/80] Improve test Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk_test.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go index d7bd5fafe..a25031ab1 100644 --- a/vcd/resource_vcd_vm_internal_disk_test.go +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -324,34 +324,27 @@ resource "vcd_vapp_vm" "{{.VmName}}" { output "internal_disk_size" { value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].size_in_mb - depends_on = [vcd_vapp_vm.{{.VmName}}] } output "internal_disk_iops" { value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].iops - depends_on = [vcd_vapp_vm.{{.VmName}}] } output "internal_disk_bus_type" { value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].bus_type - depends_on = [vcd_vapp_vm.{{.VmName}}] } output "internal_disk_bus_number" { value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].bus_number - depends_on = [vcd_vapp_vm.{{.VmName}}] } output "internal_disk_unit_number" { value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].unit_number - depends_on = [vcd_vapp_vm.{{.VmName}}] } output "internal_disk_storage_profile" { value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].storage_profile - depends_on = [vcd_vapp_vm.{{.VmName}}] } - ` const sourceTestVmInternalDiskIde = sourceTestVmInternalDiskOrgVdcAndVM + ` From 8e95632c850931c67decd962f83eb9c826690585 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 10 Jan 2020 10:40:03 +0200 Subject: [PATCH 60/80] bum govcd version Signed-off-by: Vaidotas Bauzys --- go.mod | 2 +- go.sum | 4 ++-- .../vmware/go-vcloud-director/v2/types/v56/types.go | 1 + vendor/modules.txt | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 16a089930..ca65b189c 100644 --- a/go.mod +++ b/go.mod @@ -8,4 +8,4 @@ require ( github.com/vmware/go-vcloud-director/v2 v2.5.1 ) -replace github.com/vmware/go-vcloud-director/v2 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200107102938-ea4fad99620e +replace github.com/vmware/go-vcloud-director/v2 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200110083753-3629a5d04125 diff --git a/go.sum b/go.sum index 444c290d5..7e8d615ad 100644 --- a/go.sum +++ b/go.sum @@ -197,8 +197,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200107102938-ea4fad99620e h1:QctEt3maK7Mrf+IYtZ9liCfcQl0x88J3EPeTzbswvQw= -github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200107102938-ea4fad99620e/go.mod h1:zjondbeyTfZlzhwxOzyF4K2sWWYgMEv5H91dp5dPbU8= +github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200110083753-3629a5d04125 h1:jKohfHJfJN7wsxN2qy0+NcoqoEACn3Ue8YTfnVEh1cs= +github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200110083753-3629a5d04125/go.mod h1:zjondbeyTfZlzhwxOzyF4K2sWWYgMEv5H91dp5dPbU8= github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU= diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go index 645b757aa..ee051b5a8 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go @@ -1414,6 +1414,7 @@ type DiskSettings struct { BusNumber int `xml:"BusNumber"` // The number of the SCSI or IDE controller itself. AdapterType string `xml:"AdapterType"` // The type of disk controller, e.g. IDE vs SCSI and if SCSI bus-logic vs LSI logic. ThinProvisioned *bool `xml:"ThinProvisioned,omitempty"` // Specifies whether the disk storage is pre-allocated or allocated on demand. + Disk *Reference `xml:"Disk,omitempty"` // Specifies reference to a named disk. StorageProfile *Reference `xml:"StorageProfile,omitempty"` // Specifies reference to a storage profile to be associated with the disk. OverrideVmDefault bool `xml:"overrideVmDefault"` // Specifies that the disk storage profile overrides the VM's default storage profile. Iops *int64 `xml:"iops,omitempty"` // Specifies the IOPS for the disk. diff --git a/vendor/modules.txt b/vendor/modules.txt index 6354b6cce..c1180e917 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -224,7 +224,7 @@ github.com/ulikunitz/xz/lzma # github.com/vmihailenco/msgpack v4.0.1+incompatible github.com/vmihailenco/msgpack github.com/vmihailenco/msgpack/codes -# github.com/vmware/go-vcloud-director/v2 v2.5.1 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200107102938-ea4fad99620e +# github.com/vmware/go-vcloud-director/v2 v2.5.1 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200110083753-3629a5d04125 github.com/vmware/go-vcloud-director/v2/govcd github.com/vmware/go-vcloud-director/v2/types/v56 github.com/vmware/go-vcloud-director/v2/util From 820c92e856d2459f27c0b3ea20fc62c2f0e4975a Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 10 Jan 2020 11:43:01 +0200 Subject: [PATCH 61/80] Improve handling resource after import, now ignores allow_vm_reboot Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index d2582f25d..1d89bb3a8 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -46,12 +46,6 @@ func resourceVmInternalDisk() *schema.Resource { ForceNew: true, Description: "VM in vApp in which internal disk is created", }, - "allow_vm_reboot": { - Type: schema.TypeBool, - Optional: true, - Default: false, - Description: "Powers off VM when changing any attribute of an IDE disk or unit/bus number of other disk types, after the change is complete VM is powered back on. Without this setting enabled, such changes on a powered-on VM would fail.", - }, "bus_type": { Type: schema.TypeString, Required: true, @@ -93,6 +87,12 @@ func resourceVmInternalDisk() *schema.Resource { Computed: true, Description: "Storage profile to override the VM default one", }, + "allow_vm_reboot": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Powers off VM when changing any attribute of an IDE disk or unit/bus number of other disk types, after the change is complete VM is powered back on. Without this setting enabled, such changes on a powered-on VM would fail.", + }, }, } } @@ -276,6 +276,10 @@ func resourceVmInternalDiskUpdate(d *schema.ResourceData, meta interface{}) erro vcdClient.lockParentVm(d) defer vcdClient.unLockParentVm(d) + // ignore only allow_vm_reboot change, allows to avoid empty update + if d.HasChange("allow_vm_reboot") && !d.HasChange("iops") && !d.HasChange("size_in_mb") && !d.HasChange("storage_profile") { + return nil + } vm, vdc, err := getVm(vcdClient, d) if err != nil { return err @@ -473,5 +477,6 @@ func getInternalDiskForImport(d *schema.ResourceData, meta interface{}, orgName, } d.Set("vapp_name", vappName) d.Set("vm_name", vmName) + d.Set("allow_vm_reboot", false) return []*schema.ResourceData{d}, nil } From 70d8e524f8689aa3e499c75e766fc7a8063058d4 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 10 Jan 2020 12:51:14 +0200 Subject: [PATCH 62/80] Improve vm status before handling Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk.go | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index 1d89bb3a8..76cfb89fd 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -112,8 +112,6 @@ var internalDiskBusTypesFromValues = map[string]string{ "6": "sata", } -var vmStatusBefore string - // resourceVmInternalDiskCreate creates an internal disk for VM func resourceVmInternalDiskCreate(d *schema.ResourceData, meta interface{}) error { vcdClient := meta.(*VCDClient) @@ -157,7 +155,7 @@ func resourceVmInternalDiskCreate(d *schema.ResourceData, meta interface{}) erro OverrideVmDefault: overrideVmDefault, } - err = powerOffIfNeeded(d, vm) + vmStatusBefore, err := powerOffIfNeeded(d, vm) if err != nil { return err } @@ -169,7 +167,7 @@ func resourceVmInternalDiskCreate(d *schema.ResourceData, meta interface{}) erro d.SetId(diskId) - err = powerOnIfNeeded(d, vm) + err = powerOnIfNeeded(d, vm, vmStatusBefore) if err != nil { return err } @@ -177,7 +175,7 @@ func resourceVmInternalDiskCreate(d *schema.ResourceData, meta interface{}) erro return resourceVmInternalDiskRead(d, meta) } -func powerOnIfNeeded(d *schema.ResourceData, vm *govcd.VM) error { +func powerOnIfNeeded(d *schema.ResourceData, vm *govcd.VM, vmStatusBefore string) error { vmStatus, err := vm.GetStatus() if err != nil { return fmt.Errorf("error getting VM status before ensuring it is powered on: %s", err) @@ -198,26 +196,26 @@ func powerOnIfNeeded(d *schema.ResourceData, vm *govcd.VM) error { return nil } -func powerOffIfNeeded(d *schema.ResourceData, vm *govcd.VM) error { +func powerOffIfNeeded(d *schema.ResourceData, vm *govcd.VM) (string, error) { vmStatus, err := vm.GetStatus() if err != nil { - return fmt.Errorf("error getting VM status before ensuring it is powered off: %s", err) + return "", fmt.Errorf("error getting VM status before ensuring it is powered off: %s", err) } - vmStatusBefore = vmStatus + vmStatusBefore := vmStatus if vmStatus != "POWERED_OFF" && d.Get("bus_type").(string) == "ide" && d.Get("allow_vm_reboot").(bool) { log.Printf("[DEBUG] Powering off VM %s for adding/updating internal disk.", vm.VM.Name) task, err := vm.PowerOff() if err != nil { - return fmt.Errorf("error powering off VM for adding internal disk: %s", err) + return vmStatusBefore, fmt.Errorf("error powering off VM for adding internal disk: %s", err) } err = task.WaitTaskCompletion() if err != nil { - return fmt.Errorf(errorCompletingTask, err) + return vmStatusBefore, fmt.Errorf(errorCompletingTask, err) } } - return nil + return vmStatusBefore, nil } // resourceVmInternalDiskDelete deletes disk from VM @@ -232,7 +230,7 @@ func resourceVmInternalDiskDelete(d *schema.ResourceData, m interface{}) error { return err } - err = powerOffIfNeeded(d, vm) + vmStatusBefore, err := powerOffIfNeeded(d, vm) if err != nil { return err } @@ -242,7 +240,7 @@ func resourceVmInternalDiskDelete(d *schema.ResourceData, m interface{}) error { return fmt.Errorf("[Error] failed to delete internal disk: %s", err) } - err = powerOnIfNeeded(d, vm) + err = powerOnIfNeeded(d, vm, vmStatusBefore) if err != nil { return err } @@ -286,7 +284,7 @@ func resourceVmInternalDiskUpdate(d *schema.ResourceData, meta interface{}) erro } // has refresh inside - err = powerOffIfNeeded(d, vm) + vmStatusBefore, err := powerOffIfNeeded(d, vm) if err != nil { return err } @@ -328,7 +326,7 @@ func resourceVmInternalDiskUpdate(d *schema.ResourceData, meta interface{}) erro return err } - err = powerOnIfNeeded(d, vm) + err = powerOnIfNeeded(d, vm, vmStatusBefore) if err != nil { return err } From 55d6340845c89349e966f5329e78d8990cca9996 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 10 Jan 2020 13:12:28 +0200 Subject: [PATCH 63/80] Change bus type validation setting, always accept low case Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vapp_vm.go | 2 +- vcd/resource_vcd_vm_internal_disk.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index fc174b12e..e81bdcc0f 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -252,7 +252,7 @@ func resourceVcdVAppVm() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{"ide", "parallel", "sas", "paravirtual", "sata"}, true), + ValidateFunc: validation.StringInSlice([]string{"ide", "parallel", "sas", "paravirtual", "sata"}, false), Description: "The type of disk controller. Possible values: ide, parallel( LSI Logic Parallel SCSI), sas(LSI Logic SAS (SCSI)), paravirtual(Paravirtual (SCSI)), sata", }, "size_in_mb": { diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index 76cfb89fd..a30e3d12e 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -50,7 +50,7 @@ func resourceVmInternalDisk() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{"ide", "parallel", "sas", "paravirtual", "sata"}, true), + ValidateFunc: validation.StringInSlice([]string{"ide", "parallel", "sas", "paravirtual", "sata"}, false), Description: "The type of disk controller. Possible values: ide, parallel( LSI Logic Parallel SCSI), sas(LSI Logic SAS (SCSI)), paravirtual(Paravirtual (SCSI)), sata", }, "size_in_mb": { From 5eb531baf79ad9344386f8e6fa0874a66839feea Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 10 Jan 2020 14:21:43 +0200 Subject: [PATCH 64/80] Improve doc Signed-off-by: Vaidotas Bauzys --- website/docs/r/vm_internal_disk.html.markdown | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/website/docs/r/vm_internal_disk.html.markdown b/website/docs/r/vm_internal_disk.html.markdown index b9abf5559..30d16d2e3 100644 --- a/website/docs/r/vm_internal_disk.html.markdown +++ b/website/docs/r/vm_internal_disk.html.markdown @@ -8,7 +8,11 @@ description: |- # vcd\_vm\_internal\_disk -Provides a vCloud Director VM internal disk resource. This can be used to create and delete VM internal disks. +This can be used to create, update and delete VM internal disks on already created VMs. + +~> **Note:** To adjust disk parameters when creating a new VM, please use [override_template_disk](/docs/providers/vcd/r/vapp_vm.html#override-template-disk). + +To manage disks which already exist inside a VM, please [import](#importing) them first. Supported in provider *v2.7+* From 1c0d2d283173863f8f812b34570e64d75bec01c1 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 10 Jan 2020 14:22:40 +0200 Subject: [PATCH 65/80] Improve doc Signed-off-by: Vaidotas Bauzys --- website/docs/r/vm_internal_disk.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/vm_internal_disk.html.markdown b/website/docs/r/vm_internal_disk.html.markdown index 30d16d2e3..e1173b9f5 100644 --- a/website/docs/r/vm_internal_disk.html.markdown +++ b/website/docs/r/vm_internal_disk.html.markdown @@ -104,7 +104,7 @@ Error: resource was not imported! resource id must be specified in one of these ``` -Now to import disk with ID urn:vcloud:disk:1bbc273d-7701-4f06-97be-428b46b0805e one could supply this command: +Now to import disk with ID 301 one could supply this command: ```shell $ terraform import vcd_vm_internal_disk.imported org-name.vdc-name.vapp-name.vm-name.3001 From 757a03dc99fbdbe3ca79962b0b11a3ed1974309f Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 10 Jan 2020 14:23:56 +0200 Subject: [PATCH 66/80] Improve doc Signed-off-by: Vaidotas Bauzys --- website/docs/r/vapp_vm.html.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/r/vapp_vm.html.markdown b/website/docs/r/vapp_vm.html.markdown index ceeb221d5..b23ddaa73 100644 --- a/website/docs/r/vapp_vm.html.markdown +++ b/website/docs/r/vapp_vm.html.markdown @@ -116,6 +116,7 @@ resource "vcd_vapp_vm" "web2" { ``` ## Example Usage (Override Template Disk) +This example shows how to [change VM template's disk properties](#override-template-disk) when the VM is created. ```hcl resource "vcd_vapp_vm" "internalDiskOverride" { From 30904daed28ef521958865a30cda7756507621fb Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 10 Jan 2020 14:28:07 +0200 Subject: [PATCH 67/80] Improve doc Signed-off-by: Vaidotas Bauzys --- website/docs/r/vapp_vm.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/vapp_vm.html.markdown b/website/docs/r/vapp_vm.html.markdown index b23ddaa73..6b88db475 100644 --- a/website/docs/r/vapp_vm.html.markdown +++ b/website/docs/r/vapp_vm.html.markdown @@ -219,7 +219,7 @@ example for usage details. **Deprecates**: `network_name`, `ip`, `vapp_network_n ## Override template disk Allows to update internal disk in template before first VM boot. Disk are matched by `bus_type`, `bus_number` and `unit_number` -Changes are ignored on update. To manage internal disk later please use [`vcd_vm_internal_disk`](/docs/providers/vcd/r/vm_internal_disk.html) resource. +Changes are ignored on update. This part isn't reread on refresh. To manage internal disk later please use [`vcd_vm_internal_disk`](/docs/providers/vcd/r/vm_internal_disk.html) resource. ~> **Note:** Managing disks in VM is possible only when VDC fast provisioned is disabled. From 8e54b3e57332cc4bb1899a38d6b917493ec80104 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 10 Jan 2020 16:55:29 +0200 Subject: [PATCH 68/80] Add filtering out independent disk from internal disks in listing and state file Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vapp_vm.go | 25 ++++++++++++++----------- vcd/resource_vcd_vm_internal_disk.go | 7 +++++-- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index e81bdcc0f..e6fe7df1a 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -1166,17 +1166,20 @@ func updateStateOfInternalDisks(d *schema.ResourceData, vm govcd.VM) error { existingInternalDisks := vm.VM.VmSpecSection.DiskSection.DiskSettings var internalDiskList []map[string]interface{} for _, internalDisk := range existingInternalDisks { - newValue := map[string]interface{}{ - "disk_id": internalDisk.DiskId, - "bus_type": internalDiskBusTypesFromValues[internalDisk.AdapterType], - "size_in_mb": int(internalDisk.SizeMb), - "bus_number": internalDisk.BusNumber, - "unit_number": internalDisk.UnitNumber, - "iops": int(*internalDisk.Iops), - "thin_provisioned": *internalDisk.ThinProvisioned, - "storage_profile": internalDisk.StorageProfile.Name, - } - internalDiskList = append(internalDiskList, newValue) + // API shows internal disk and independent disks in one list. If disk.Disk != nil then it's independent disk + if internalDisk.Disk == nil { + newValue := map[string]interface{}{ + "disk_id": internalDisk.DiskId, + "bus_type": internalDiskBusTypesFromValues[internalDisk.AdapterType], + "size_in_mb": int(internalDisk.SizeMb), + "bus_number": internalDisk.BusNumber, + "unit_number": internalDisk.UnitNumber, + "iops": int(*internalDisk.Iops), + "thin_provisioned": *internalDisk.ThinProvisioned, + "storage_profile": internalDisk.StorageProfile.Name, + } + internalDiskList = append(internalDiskList, newValue) + } } return d.Set("internal_disk", internalDiskList) diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index a30e3d12e..e516959fb 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -437,8 +437,11 @@ func listInternalDisksForImport(meta interface{}, orgName, vdcName, vappName, vm fmt.Fprintln(writer, "No\tID\tBusType\tBusNumber\tUnitNumber\tSize\tStoragePofile\tIops\tThinProvisioned") fmt.Fprintln(writer, "--\t--\t-------\t---------\t----------\t----\t-------------\t----\t---------------") for index, disk := range vm.VM.VmSpecSection.DiskSection.DiskSettings { - fmt.Fprintf(writer, "%d\t%s\t%s\t%d\t%d\t%d\t%s\t%d\t%t\n", (index + 1), disk.DiskId, internalDiskBusTypesFromValues[disk.AdapterType], disk.BusNumber, disk.UnitNumber, disk.SizeMb, - disk.StorageProfile.Name, *disk.Iops, *disk.ThinProvisioned) + // API shows internal disk and independent disks in one list. If disk.Disk != nil then it's independent disk + if disk.Disk == nil { + fmt.Fprintf(writer, "%d\t%s\t%s\t%d\t%d\t%d\t%s\t%d\t%t\n", (index + 1), disk.DiskId, internalDiskBusTypesFromValues[disk.AdapterType], disk.BusNumber, disk.UnitNumber, disk.SizeMb, + disk.StorageProfile.Name, *disk.Iops, *disk.ThinProvisioned) + } } writer.Flush() From 0aa3325b365740b9f5d59f58e05790590dd7bad6 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 10 Jan 2020 17:04:02 +0200 Subject: [PATCH 69/80] Improve docs Signed-off-by: Vaidotas Bauzys --- website/docs/r/vapp_vm.html.markdown | 4 ++-- website/docs/r/vm_internal_disk.html.markdown | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/website/docs/r/vapp_vm.html.markdown b/website/docs/r/vapp_vm.html.markdown index 6b88db475..cdb1ff10e 100644 --- a/website/docs/r/vapp_vm.html.markdown +++ b/website/docs/r/vapp_vm.html.markdown @@ -176,7 +176,7 @@ example for usage details. **Deprecates**: `network_name`, `ip`, `vapp_network_n * `guest_properties` - (Optional; *v2.5+*) Key value map of guest properties * `description` - (Computed; *v2.6+*) The VM description. Note: description is read only. Currently, this field has the description of the OVA used to create the VM -* `override_template_disk` - (Optional; *v2.7+*) Allows to update internal disk in template before first VM boot. Disk are matched by `bus_type`, `bus_number` and `unit_number`. See [Override template Disk](#override-template-disk) below for details. +* `override_template_disk` - (Optional; *v2.7+*) Allows to update internal disk in template before first VM boot. Disk is matched by `bus_type`, `bus_number` and `unit_number`. See [Override template Disk](#override-template-disk) below for details. ## Disk @@ -218,7 +218,7 @@ example for usage details. **Deprecates**: `network_name`, `ip`, `vapp_network_n ## Override template disk -Allows to update internal disk in template before first VM boot. Disk are matched by `bus_type`, `bus_number` and `unit_number` +Allows to update internal disk in template before first VM boot. Disk is matched by `bus_type`, `bus_number` and `unit_number`. Changes are ignored on update. This part isn't reread on refresh. To manage internal disk later please use [`vcd_vm_internal_disk`](/docs/providers/vcd/r/vm_internal_disk.html) resource. ~> **Note:** Managing disks in VM is possible only when VDC fast provisioned is disabled. diff --git a/website/docs/r/vm_internal_disk.html.markdown b/website/docs/r/vm_internal_disk.html.markdown index e1173b9f5..13efbb0e3 100644 --- a/website/docs/r/vm_internal_disk.html.markdown +++ b/website/docs/r/vm_internal_disk.html.markdown @@ -14,6 +14,8 @@ This can be used to create, update and delete VM internal disks on already creat To manage disks which already exist inside a VM, please [import](#importing) them first. +~> **Note:** Managing disks in VM is possible only when VDC fast provisioned is disabled. + Supported in provider *v2.7+* ## Example Usage @@ -104,7 +106,7 @@ Error: resource was not imported! resource id must be specified in one of these ``` -Now to import disk with ID 301 one could supply this command: +Now to import disk with ID 3001 one could supply this command: ```shell $ terraform import vcd_vm_internal_disk.imported org-name.vdc-name.vapp-name.vm-name.3001 From 5615855285613b759a48a0080843bec91dd86a2a Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 10 Jan 2020 17:34:38 +0200 Subject: [PATCH 70/80] Improve test Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go index a25031ab1..d10498898 100644 --- a/vcd/resource_vcd_vm_internal_disk_test.go +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -322,6 +322,16 @@ resource "vcd_vapp_vm" "{{.VmName}}" { } } +resource "vcd_independent_disk" "IndependentDisk1" { + org = "{{.Org}}" + vdc = vcd_org_vdc.{{.VdcName}}.name + name = "TestAccVcdVmInternalDiskTest" + size = "5" + bus_type = "SCSI" + bus_sub_type = "lsilogicsas" + storage_profile = "{{.StorageProfileName}}" +} + output "internal_disk_size" { value = vcd_vapp_vm.{{.VmName}}.internal_disk[0].size_in_mb } From 543bf7fa7eaecb496ea79c0860fa50480780151a Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 10 Jan 2020 17:41:15 +0200 Subject: [PATCH 71/80] Add comments Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vapp_vm.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index e6fe7df1a..8fb123136 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -643,6 +643,7 @@ func expandDisksProperties(v interface{}) ([]diskParams, error) { func getVmIndependentDisks(vm govcd.VM) []string { var disks []string + // We use VirtualHardwareSection due in time of implementation we didn't have access to VmSpecSection which we used for internal disks. for _, item := range vm.VM.VirtualHardwareSection.Item { // disk resource type is 17 if item.ResourceType == 17 && item.HostResource[0].Disk != "" { @@ -1167,6 +1168,8 @@ func updateStateOfInternalDisks(d *schema.ResourceData, vm govcd.VM) error { var internalDiskList []map[string]interface{} for _, internalDisk := range existingInternalDisks { // API shows internal disk and independent disks in one list. If disk.Disk != nil then it's independent disk + // We use VmSpecSection as it is newer type than VirtualHardwareSection. It is used by HTML5 vCD client, has easy understandable structure. + // VirtualHardwareSection is a mess, has undocumented relationships between elements and very hard to use without issues for internal disks. if internalDisk.Disk == nil { newValue := map[string]interface{}{ "disk_id": internalDisk.DiskId, From e7eaafe89d865a326c093b353646f79ae6c174af Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 13 Jan 2020 07:26:18 +0200 Subject: [PATCH 72/80] Add missing element internal_disk to datasource Signed-off-by: Vaidotas Bauzys --- vcd/datasource_vcd_vapp_vm.go | 47 ++++++++++++++++++++++++++++ website/docs/d/vapp_vm.html.markdown | 1 + 2 files changed, 48 insertions(+) diff --git a/vcd/datasource_vcd_vapp_vm.go b/vcd/datasource_vcd_vapp_vm.go index 8192897e6..d70d727bc 100644 --- a/vcd/datasource_vcd_vapp_vm.go +++ b/vcd/datasource_vcd_vapp_vm.go @@ -122,6 +122,53 @@ func datasourceVcdVAppVm() *schema.Resource { Computed: true, Set: resourceVcdVmIndependentDiskHash, }, + "internal_disk": { + Type: schema.TypeList, + Computed: true, + Description: "A block will show internal disk details", + Elem: &schema.Resource{Schema: map[string]*schema.Schema{ + "disk_id": { + Type: schema.TypeString, + Computed: true, + Description: "The disk ID.", + }, + "bus_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of disk controller. Possible values: ide, parallel( LSI Logic Parallel SCSI), sas(LSI Logic SAS (SCSI)), paravirtual(Paravirtual (SCSI)), sata", + }, + "size_in_mb": { + Type: schema.TypeInt, + Computed: true, + Description: "The size of the disk in MB.", + }, + "bus_number": { + Type: schema.TypeInt, + Computed: true, + Description: "The number of the SCSI or IDE controller itself.", + }, + "unit_number": { + Type: schema.TypeInt, + Computed: true, + Description: "The device number on the SCSI or IDE controller of the disk.", + }, + "thin_provisioned": { + Type: schema.TypeBool, + Computed: true, + Description: "Specifies whether the disk storage is pre-allocated or allocated on demand.", + }, + "iops": { + Type: schema.TypeInt, + Computed: true, + Description: "Specifies the IOPS for the disk. Default - 0.", + }, + "storage_profile": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "Storage profile to override the VM default one", + }, + }}, + }, "expose_hardware_virtualization": &schema.Schema{ Type: schema.TypeBool, Computed: true, diff --git a/website/docs/d/vapp_vm.html.markdown b/website/docs/d/vapp_vm.html.markdown index 67a5fc956..0235bc511 100644 --- a/website/docs/d/vapp_vm.html.markdown +++ b/website/docs/d/vapp_vm.html.markdown @@ -87,5 +87,6 @@ The following arguments are supported: * `description` - The VM description. Note: description is read only. Currently, this field has the description of the OVA used to create the VM * `expose_hardware_virtualization` - Expose hardware-assisted CPU virtualization to guest OS +* `internal_disk` - (*v2.7+*) A block providing internal disk of VM details See [VM resource](/docs/providers/vcd/r/vapp_vm.html#attribute-reference) for more info about VM attributes. From 584b61576a006da7fffc1371a44b8275b37c0f0c Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 13 Jan 2020 08:50:00 +0200 Subject: [PATCH 73/80] Add missing attach independent disk in test Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vm_internal_disk_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go index d10498898..0b2191ca0 100644 --- a/vcd/resource_vcd_vm_internal_disk_test.go +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -320,6 +320,12 @@ resource "vcd_vapp_vm" "{{.VmName}}" { iops = 0 storage_profile = "{{.StorageProfileName}}" } + + disk { + name = vcd_independent_disk.IndependentDisk1.name + bus_number = 3 + unit_number = 0 + } } resource "vcd_independent_disk" "IndependentDisk1" { From f5f4d82f6605f0c13c14e56897feb69e5d63d32e Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 13 Jan 2020 10:00:04 +0200 Subject: [PATCH 74/80] Improve comment Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vapp_vm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index 8fb123136..2f27c14dc 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -1169,7 +1169,7 @@ func updateStateOfInternalDisks(d *schema.ResourceData, vm govcd.VM) error { for _, internalDisk := range existingInternalDisks { // API shows internal disk and independent disks in one list. If disk.Disk != nil then it's independent disk // We use VmSpecSection as it is newer type than VirtualHardwareSection. It is used by HTML5 vCD client, has easy understandable structure. - // VirtualHardwareSection is a mess, has undocumented relationships between elements and very hard to use without issues for internal disks. + // VirtualHardwareSection has undocumented relationships between elements and very hard to use without issues for internal disks. if internalDisk.Disk == nil { newValue := map[string]interface{}{ "disk_id": internalDisk.DiskId, From 624adf47d4e12e41128c8705cc404e54ad6e10da Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 13 Jan 2020 10:58:54 +0200 Subject: [PATCH 75/80] Removed not needed parts Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_independent_disk_test.go | 28 ----- vcd/resource_vcd_vm_internal_disk.go | 3 - vcd/resource_vcd_vm_internal_disk_test.go | 126 +--------------------- 3 files changed, 1 insertion(+), 156 deletions(-) diff --git a/vcd/resource_vcd_independent_disk_test.go b/vcd/resource_vcd_independent_disk_test.go index e7db75c9b..179a53e63 100644 --- a/vcd/resource_vcd_independent_disk_test.go +++ b/vcd/resource_vcd_independent_disk_test.go @@ -38,9 +38,6 @@ func TestAccVcdIndependentDiskBasic(t *testing.T) { params["FuncName"] = t.Name() + "-Compatibility" configTextForCompatibility := templateFill(testAccCheckVcdIndependentDiskForCompatibility, params) - /* params["FuncName"] = t.Name() - configText := templateFill(testAccCheckVcdIndependentDiskBasic, params) - */ params["FuncName"] = t.Name() + "-WithoutOptionals" configTextWithoutOptionals := templateFill(testAccCheckVcdIndependentDiskWithoutOptionals, params) @@ -60,7 +57,6 @@ func TestAccVcdIndependentDiskBasic(t *testing.T) { Config: configTextForCompatibility, Check: resource.ComposeTestCheckFunc( testAccCheckDiskCreated("vcd_independent_disk."+resourceName), - //resource.TestCheckResourceAttr("vcd_independent_disk."+resourceName, "size_in_bytes", "5242880000"), resource.TestMatchResourceAttr("vcd_independent_disk."+resourceName, "owner_name", regexp.MustCompile(`^\S+`)), resource.TestMatchResourceAttr("vcd_independent_disk."+resourceName, "datastore_name", regexp.MustCompile(`^\S+`)), resource.TestMatchResourceAttr("vcd_independent_disk."+resourceName, "iops", regexp.MustCompile(`^\d+$`)), @@ -74,17 +70,6 @@ func TestAccVcdIndependentDiskBasic(t *testing.T) { ImportStateIdFunc: importStateIdByDisk("vcd_independent_disk." + resourceName), ImportStateVerifyIgnore: []string{"org", "vdc", "size"}, }, - /* resource.TestStep{ - Config: configText, - Check: resource.ComposeTestCheckFunc( - testAccCheckDiskCreated("vcd_independent_disk."+resourceName+"second"), - resource.TestCheckResourceAttr("vcd_independent_disk."+resourceName+"second", "size_in_bytes", "5242880000"), - resource.TestMatchResourceAttr("vcd_independent_disk."+resourceName+"second", "owner_name", regexp.MustCompile(`^\S+`)), - resource.TestMatchResourceAttr("vcd_independent_disk."+resourceName+"second", "datastore_name", regexp.MustCompile(`^\S+`)), - resource.TestMatchResourceAttr("vcd_independent_disk."+resourceName+"second", "iops", regexp.MustCompile(`^\d+$`)), - resource.TestCheckResourceAttr("vcd_independent_disk."+resourceName+"second", "is_attached", "false"), - ), - },*/ resource.TestStep{ Config: configTextWithoutOptionals, Check: resource.ComposeTestCheckFunc( @@ -192,16 +177,3 @@ resource "vcd_independent_disk" "{{.secondResourceName}}" { size = "{{.size}}" } ` - -/*const testAccCheckVcdIndependentDiskBasic = ` -resource "vcd_independent_disk" "{{.secondResourceName}}" { - org = "{{.Org}}" - vdc = "{{.Vdc}}" - name = "{{.secondName}}" - size_in_bytes = "{{.sizeInBytes}}" - bus_type = "{{.busType}}" - bus_sub_type = "{{.busSubType}}" - storage_profile = "{{.storageProfileName}}" -} -` -*/ diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index e516959fb..dde06f05f 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -296,9 +296,6 @@ func resourceVmInternalDiskUpdate(d *schema.ResourceData, meta interface{}) erro log.Printf("[TRACE] Internal Disk with id %s found", d.Id()) iops := int64(d.Get("iops").(int)) diskSettingsToUpdate.Iops = &iops - //diskSettingsToUpdate.UnitNumber = d.Get("unit_number").(int) - //diskSettingsToUpdate.BusNumber = d.Get("bus_number").(int) - //diskSettingsToUpdate.AdapterType = internalDiskBusTypes[d.Get("bus_type").(string)] diskSettingsToUpdate.SizeMb = int64(d.Get("size_in_mb").(int)) // Note can't change adapter type, bus number, unit number as vSphere changes diskId diff --git a/vcd/resource_vcd_vm_internal_disk_test.go b/vcd/resource_vcd_vm_internal_disk_test.go index 0b2191ca0..d88dedd4c 100644 --- a/vcd/resource_vcd_vm_internal_disk_test.go +++ b/vcd/resource_vcd_vm_internal_disk_test.go @@ -18,47 +18,8 @@ func TestAccVcdVmInternalDisk(t *testing.T) { t.Skip("VM internal disks tests requires system admin privileges") return } - - /* adminVdc, err := getAdminVdc() - if err != nil { - t.Skip(fmt.Sprintf("error retrieving if VDC is thing provisioned %s", err)) - return - } - - vapp, err := getAvailableVapp() - if err != nil { - t.Skip("No suitable vApp found for this test") - return - } - var vm *govcd.VM - - if vapp.VApp.Children != nil && len(vapp.VApp.Children.VM) > 0 { - vm, err = vapp.GetVMById(vapp.VApp.Children.VM[0].ID, false) - if err != nil { - t.Skip(fmt.Sprintf("error retrieving VM %s", vapp.VApp.Children.VM[0].Name)) - return - } - } - if vm == nil { - t.Skip(fmt.Sprintf("No VM available in vApp %s", vapp.VApp.Name)) - return - } - - // for test to run correctly VM has to be power on - task, err := vm.PowerOn() - if err != nil { - t.Skip(fmt.Sprintf("Error powering up vm %s", err)) - return - } - _ = task.WaitTaskCompletion()*/ internalDiskSize := 20000 - storageProfile := testConfig.VCD.ProviderVdc.StorageProfile - /* if *adminVdc.UsesFastProvisioning { - // to avoid `Cannot use multiple storage profiles in a fast-provisioned VDC` we need to reuse VM storage profile - storageProfile = vm.VM.StorageProfile.Name - }*/ - diskResourceName := "disk1" diskSize := "13333" biggerDiskSize := "14333" @@ -71,10 +32,7 @@ func TestAccVcdVmInternalDisk(t *testing.T) { vmName := "TestInternalDiskVm" vdcName := "ForInternalDiskTest" var params = StringMap{ - "Org": testConfig.VCD.Org, - //"VDC": testConfig.VCD.Vdc, - // "VappName": vapp.VApp.Name, - // "VmName": vm.VM.Name, + "Org": testConfig.VCD.Org, "FuncName": "TestVappVmDS", "Tags": "vm", "DiskResourceName": diskResourceName, @@ -114,7 +72,6 @@ func TestAccVcdVmInternalDisk(t *testing.T) { params["FuncName"] = t.Name() + "-Update1" configText_update1 := templateFill(sourceTestVmInternalDisk_Update1, params) params["FuncName"] = t.Name() + "-Update2" - //configText_update2 := templateFill(sourceTestVmInternalDisk_Update2, params) debugPrintf("#[DEBUG] CONFIGURATION: %s", configText+configText_update1) // Thus it won't run in the short test @@ -147,7 +104,6 @@ func TestAccVcdVmInternalDisk(t *testing.T) { resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "bus_number", busNumber), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "unit_number", unitNumber), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "storage_profile", storageProfile), - //resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "thin_provisioned", "true"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "iops", "0"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "size_in_mb", diskSize), @@ -155,21 +111,17 @@ func TestAccVcdVmInternalDisk(t *testing.T) { resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_number", "0"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "unit_number", "0"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "storage_profile", storageProfile), - //resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "thin_provisioned", "true"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "iops", "0"), ), }, resource.TestStep{ Config: configText_update1, - //ExpectError: regexp.MustCompile(`.*You must power off the virtual machine.*to change its hard disks, bus, or unit numbers.*`), - //ExpectNonEmptyPlan: true, Check: resource.ComposeTestCheckFunc(resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "size_in_mb", biggerDiskSize), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "bus_type", busType), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "bus_number", busNumber), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "unit_number", unitNumber), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "storage_profile", storageProfile), - //resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "thin_provisioned", "true"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "iops", "0"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "allow_vm_reboot", "false"), @@ -177,7 +129,6 @@ func TestAccVcdVmInternalDisk(t *testing.T) { resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_number", "0"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "unit_number", "0"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "storage_profile", storageProfile), - //resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "thin_provisioned", strconv.FormatBool(*adminVdc.IsThinProvision)), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName, "thin_provisioned", "true"), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "size_in_mb", biggerDiskSize), resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "iops", "0"), @@ -192,16 +143,6 @@ func TestAccVcdVmInternalDisk(t *testing.T) { // These fields can't be retrieved ImportStateVerifyIgnore: []string{"org", "vdc", "allow_vm_reboot", "thin_provisioned"}, }, - /*resource.TestStep{ - Config: configText_update2, - Check: resource.ComposeTestCheckFunc(resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "size_in_mb", diskSize), - resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_type", "ide"), - resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "bus_number", "1"), - resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "unit_number", "0"), - resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "storage_profile", testConfig.VCD.ProviderVdc.StorageProfile), - resource.TestCheckResourceAttr("vcd_vm_internal_disk."+diskResourceName+"_ide", "allow_vm_reboot", "true"), - ), - },*/ }, }) } @@ -238,26 +179,6 @@ func testCheckInternalDiskNonStringOutputs(internalDiskSize int) resource.TestCh } } -/*func getAdminVdc() (*types.AdminVdc, error) { - vcdClient, err := getTestVCDFromJson(testConfig) - if err != nil { - return nil, fmt.Errorf("error getting client configuration: %s", err) - } - err = ProviderAuthenticate(vcdClient, testConfig.Provider.User, testConfig.Provider.Password, testConfig.Provider.Token, testConfig.Provider.SysOrg) - if err != nil { - return nil, fmt.Errorf("authentication error: %s", err) - } - org, err := vcdClient.GetAdminOrgByName(testConfig.VCD.Org) - if err != nil { - return nil, fmt.Errorf("org not found : %s", err) - } - adminVdc, err := org.GetAdminVDCByName(testConfig.VCD.Vdc, false) - if err != nil { - return nil, fmt.Errorf("vdc not found : %s", err) - } - return adminVdc.AdminVdc, nil -}*/ - // we need VDC with disabled fast provisioning to edit disks const sourceTestVmInternalDiskOrgVdcAndVM = ` resource "vcd_org_vdc" "{{.VdcName}}" { @@ -434,48 +355,3 @@ resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { allow_vm_reboot = "true" } ` - -/*const sourceTestVmInternalDisk = sataDisk + ` -resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { - org = "{{.Org}}" - vdc = "{{.VDC}}" - vapp_name = "{{.VappName}}" - vm_name = "{{.VmName}}" - bus_type = "ide" - size_in_mb = "{{.Size}}" - bus_number = "0" - unit_number = "1" - storage_profile = "{{.StorageProfileName}}" - allow_vm_reboot = "true" -} -` - -const sourceTestVmInternalDisk_Update1 = sataDisk + ` -resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { - org = "{{.Org}}" - vdc = "{{.VDC}}" - vapp_name = "{{.VappName}}" - vm_name = "{{.VmName}}" - bus_type = "ide" - size_in_mb = "{{.Size}}" - bus_number = "1" - unit_number = "0" - storage_profile = "{{.StorageProfileName}}" - allow_vm_reboot = "false" -} -` - -const sourceTestVmInternalDisk_Update2 = sataDisk + ` -resource "vcd_vm_internal_disk" "{{.DiskResourceName}}_ide" { - org = "{{.Org}}" - vdc = "{{.VDC}}" - vapp_name = "{{.VappName}}" - vm_name = "{{.VmName}}" - bus_type = "ide" - size_in_mb = "{{.Size}}" - bus_number = "1" - unit_number = "0" - storage_profile = "{{.StorageProfileName}}" - allow_vm_reboot = "true" -} -`*/ From c238651e802db723b02ad3b2665f87e132e829a4 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 13 Jan 2020 11:32:05 +0200 Subject: [PATCH 76/80] Improve docs Signed-off-by: Vaidotas Bauzys --- website/vcd.erb | 84 ++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/website/vcd.erb b/website/vcd.erb index 6506feb10..5f2995269 100644 --- a/website/vcd.erb +++ b/website/vcd.erb @@ -13,30 +13,6 @@ > Data Sources From a669fddb180301e39271c45a9e2b9e7284ec4a5a Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 13 Jan 2020 11:38:14 +0200 Subject: [PATCH 77/80] Improvements Signed-off-by: Vaidotas Bauzys --- vcd/resource_vcd_vapp_vm.go | 9 ++++++++- vcd/resource_vcd_vm_internal_disk.go | 14 ++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/vcd/resource_vcd_vapp_vm.go b/vcd/resource_vcd_vapp_vm.go index 2f27c14dc..52d0c59d7 100644 --- a/vcd/resource_vcd_vapp_vm.go +++ b/vcd/resource_vcd_vapp_vm.go @@ -643,7 +643,7 @@ func expandDisksProperties(v interface{}) ([]diskParams, error) { func getVmIndependentDisks(vm govcd.VM) []string { var disks []string - // We use VirtualHardwareSection due in time of implementation we didn't have access to VmSpecSection which we used for internal disks. + // We use VirtualHardwareSection because in time of implementation we didn't have access to VmSpecSection which we used for internal disks. for _, item := range vm.VM.VirtualHardwareSection.Item { // disk resource type is 17 if item.ResourceType == 17 && item.HostResource[0].Disk != "" { @@ -1164,6 +1164,9 @@ func updateStateOfInternalDisks(d *schema.ResourceData, vm govcd.VM) error { return err } + if vm.VM.VmSpecSection == nil || vm.VM.VmSpecSection.DiskSection == nil { + return fmt.Errorf("[updateStateOfInternalDisks] VmSpecSection part is missing") + } existingInternalDisks := vm.VM.VmSpecSection.DiskSection.DiskSettings var internalDiskList []map[string]interface{} for _, internalDisk := range existingInternalDisks { @@ -1196,6 +1199,10 @@ func updateTemplateInternalDisks(d *schema.ResourceData, meta interface{}, vm go return fmt.Errorf(errorRetrievingOrgAndVdc, err) } + if vm.VM.VmSpecSection == nil || vm.VM.VmSpecSection.DiskSection == nil { + return fmt.Errorf("[updateTemplateInternalDisks] VmSpecSection part is missing") + } + diskSettings := vm.VM.VmSpecSection.DiskSection.DiskSettings var storageProfilePrt *types.Reference diff --git a/vcd/resource_vcd_vm_internal_disk.go b/vcd/resource_vcd_vm_internal_disk.go index dde06f05f..831c6cd97 100644 --- a/vcd/resource_vcd_vm_internal_disk.go +++ b/vcd/resource_vcd_vm_internal_disk.go @@ -130,7 +130,7 @@ func resourceVmInternalDiskCreate(d *schema.ResourceData, meta interface{}) erro if storageProfileName, ok := d.GetOk("storage_profile"); ok { storageProfile, err := vdc.FindStorageProfileReference(storageProfileName.(string)) if err != nil { - return fmt.Errorf("[vm creation] error retrieving storage profile %s : %s", storageProfileName, err) + return fmt.Errorf("[internal disk creation] error retrieving storage profile %s : %s", storageProfileName, err) } storageProfilePrt = &storageProfile overrideVmDefault = true @@ -237,7 +237,7 @@ func resourceVmInternalDiskDelete(d *schema.ResourceData, m interface{}) error { err = vm.DeleteInternalDisk(d.Id()) if err != nil { - return fmt.Errorf("[Error] failed to delete internal disk: %s", err) + return fmt.Errorf("[resourceVmInternalDiskDelete] failed to delete internal disk: %s", err) } err = powerOnIfNeeded(d, vm, vmStatusBefore) @@ -257,11 +257,11 @@ func getVm(vcdClient *VCDClient, d *schema.ResourceData) (*govcd.VM, *govcd.Vdc, } vapp, err := vdc.GetVAppByName(d.Get("vapp_name").(string), false) if err != nil { - return nil, nil, fmt.Errorf("[Error] failed to get vApp: %s", err) + return nil, nil, fmt.Errorf("[getVm] failed to get vApp: %s", err) } vm, err := vapp.GetVMByName(d.Get("vm_name").(string), false) if err != nil { - return nil, nil, fmt.Errorf("[Error] failed to get VM: %s", err) + return nil, nil, fmt.Errorf("[getVm] failed to get VM: %s", err) } return vm, vdc, err } @@ -355,7 +355,9 @@ func resourceVmInternalDiskRead(d *schema.ResourceData, m interface{}) error { _ = d.Set("size_in_mb", diskSettings.SizeMb) _ = d.Set("bus_number", diskSettings.BusNumber) _ = d.Set("unit_number", diskSettings.UnitNumber) - _ = d.Set("thin_provisioned", *diskSettings.ThinProvisioned) + if diskSettings.ThinProvisioned != nil { + _ = d.Set("thin_provisioned", *diskSettings.ThinProvisioned) + } _ = d.Set("iops", diskSettings.Iops) _ = d.Set("storage_profile", diskSettings.StorageProfile.Name) @@ -431,7 +433,7 @@ func listInternalDisksForImport(meta interface{}, orgName, vdcName, vappName, vm writer := tabwriter.NewWriter(getTerraformStdout(), 0, 8, 1, '\t', tabwriter.AlignRight) - fmt.Fprintln(writer, "No\tID\tBusType\tBusNumber\tUnitNumber\tSize\tStoragePofile\tIops\tThinProvisioned") + fmt.Fprintln(writer, "No\tID\tBusType\tBusNumber\tUnitNumber\tSize\tStorageProfile\tIops\tThinProvisioned") fmt.Fprintln(writer, "--\t--\t-------\t---------\t----------\t----\t-------------\t----\t---------------") for index, disk := range vm.VM.VmSpecSection.DiskSection.DiskSettings { // API shows internal disk and independent disks in one list. If disk.Disk != nil then it's independent disk From 36d7b9011893564da32bdb34ba036606eccd1b63 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 13 Jan 2020 11:53:47 +0200 Subject: [PATCH 78/80] Fix doc Signed-off-by: Vaidotas Bauzys --- website/docs/d/org_vdc.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/d/org_vdc.html.markdown b/website/docs/d/org_vdc.html.markdown index ee510771b..d0ccdbf12 100644 --- a/website/docs/d/org_vdc.html.markdown +++ b/website/docs/d/org_vdc.html.markdown @@ -6,7 +6,7 @@ description: |- Provides an organization VDC data source. --- -# vcd\_org\_vcd +# vcd\_org\_vdc Provides a vCloud Director Organization VDC data source. An Organization VDC can be used to reference a VCD and use its data within other resources or data sources. From 3b47d98522d0336bb243c5bb234e0389af19dca7 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 13 Jan 2020 11:59:46 +0200 Subject: [PATCH 79/80] Improve doc Signed-off-by: Vaidotas Bauzys --- website/vcd.erb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/website/vcd.erb b/website/vcd.erb index 5f2995269..80357c38c 100644 --- a/website/vcd.erb +++ b/website/vcd.erb @@ -109,15 +109,6 @@ > vcd_external_network - > - vcd_firewall_rules - - > - vcd_dnat - - > - vcd_snat - > vcd_network @@ -136,6 +127,15 @@ > vcd_edgegateway_vpn + > + vcd_firewall_rules + + > + vcd_dnat + + > + vcd_snat + > vcd_vapp From a0e4aa39bd2b341cbd000641a604aae55c79bbe8 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 13 Jan 2020 12:48:44 +0200 Subject: [PATCH 80/80] bump govcd Signed-off-by: Vaidotas Bauzys --- go.mod | 4 +--- go.sum | 4 ++-- .../vmware/go-vcloud-director/v2/types/v56/types.go | 2 +- vendor/modules.txt | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index ca65b189c..9fd4d04d4 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,5 @@ go 1.13 require ( github.com/hashicorp/go-version v1.2.0 github.com/hashicorp/terraform-plugin-sdk v1.3.0 - github.com/vmware/go-vcloud-director/v2 v2.5.1 + github.com/vmware/go-vcloud-director/v2 v2.6.0-alpha.1 ) - -replace github.com/vmware/go-vcloud-director/v2 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200110083753-3629a5d04125 diff --git a/go.sum b/go.sum index 7e8d615ad..c6bed210d 100644 --- a/go.sum +++ b/go.sum @@ -197,12 +197,12 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200110083753-3629a5d04125 h1:jKohfHJfJN7wsxN2qy0+NcoqoEACn3Ue8YTfnVEh1cs= -github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200110083753-3629a5d04125/go.mod h1:zjondbeyTfZlzhwxOzyF4K2sWWYgMEv5H91dp5dPbU8= github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU= github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmware/go-vcloud-director/v2 v2.6.0-alpha.1 h1:tplMa0oMs/qBviLsvrxszpQb0v5j7QO86wkOML5VqjQ= +github.com/vmware/go-vcloud-director/v2 v2.6.0-alpha.1/go.mod h1:zjondbeyTfZlzhwxOzyF4K2sWWYgMEv5H91dp5dPbU8= github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.1.0 h1:uJwc9HiBOCpoKIObTQaLR+tsEXx1HBHnOsOOpcdhZgw= github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go index ee051b5a8..a101cb9bc 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go @@ -1396,7 +1396,7 @@ type VmSpecSection struct { MediaSection *MediaSection `xml:"MediaSection,omitempty"` // The media devices of this VM. DiskSection *DiskSection `xml:"DiskSection,omitempty"` // virtual disks of this VM. HardwareVersion *HardwareVersion `xml:"HardwareVersion"` // vSphere name of Virtual Hardware Version of this VM. Example: vmx-13 - This parameter may be omitted when using the VmSpec to update the contents of an existing VM. - VmToolsVersion string `xml:"VmToolsVersion,omitempty"` // VMware tools version of this VM. + VmToolsVersion string `xml:"VmToolsVersion,omitempty"` // VMware tools version of this VM. VirtualCpuType string `xml:"VirtualCpuType,omitempty"` // The capabilities settings for this VM. This parameter may be omitted when using the VmSpec to update the contents of an existing VM. TimeSyncWithHost *bool `xml:"TimeSyncWithHost,omitempty"` // Synchronize the VM's time with the host. } diff --git a/vendor/modules.txt b/vendor/modules.txt index c1180e917..89b1f2c9a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -224,7 +224,7 @@ github.com/ulikunitz/xz/lzma # github.com/vmihailenco/msgpack v4.0.1+incompatible github.com/vmihailenco/msgpack github.com/vmihailenco/msgpack/codes -# github.com/vmware/go-vcloud-director/v2 v2.5.1 => github.com/vbauzysvmware/go-vcloud-director/v2 v2.2.0-alpha.3.0.20200110083753-3629a5d04125 +# github.com/vmware/go-vcloud-director/v2 v2.6.0-alpha.1 github.com/vmware/go-vcloud-director/v2/govcd github.com/vmware/go-vcloud-director/v2/types/v56 github.com/vmware/go-vcloud-director/v2/util