Skip to content

Commit

Permalink
Merge pull request #406 from terraform-providers/port_tagging
Browse files Browse the repository at this point in the history
Support tagging VM segment port
  • Loading branch information
annakhm authored Jul 29, 2020
2 parents ca6f484 + c8a3ac5 commit afcfae6
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 7 deletions.
27 changes: 27 additions & 0 deletions nsxt/policy_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,33 @@ func newUUID() string {
return uuid.String()
}

func getPolicyTagsFromSet(tagSet *schema.Set) []model.Tag {
tags := tagSet.List()
var tagList []model.Tag
for _, tag := range tags {
data := tag.(map[string]interface{})
tagScope := data["scope"].(string)
tagTag := data["tag"].(string)
elem := model.Tag{
Scope: &tagScope,
Tag: &tagTag}

tagList = append(tagList, elem)
}
return tagList
}

func initPolicyTagsSet(tags []model.Tag) []map[string]interface{} {
var tagList []map[string]interface{}
for _, tag := range tags {
elem := make(map[string]interface{})
elem["scope"] = tag.Scope
elem["tag"] = tag.Tag
tagList = append(tagList, elem)
}
return tagList
}

func getCustomizedPolicyTagsFromSchema(d *schema.ResourceData, schemaName string) []model.Tag {
tags := d.Get(schemaName).(*schema.Set).List()
var tagList []model.Tag
Expand Down
189 changes: 183 additions & 6 deletions nsxt/resource_nsxt_policy_vm_tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/vmware/vsphere-automation-sdk-go/runtime/protocol/client"
"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra/realized_state"
updateClient "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra/realized_state/enforcement_points"
"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra/realized_state/enforcement_points"
"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra/segments"
"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model"
"log"
"strings"
Expand All @@ -35,7 +36,18 @@ func resourceNsxtPolicyVMTags() *schema.Resource {
Description: "Instance id",
Required: true,
},
"tag": getRequiredTagsSchema(),
"tag": getTagsSchema(),
"port": {
Type: schema.TypeList,
Description: "Tag specificiation for corresponding segment port",
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"segment_path": getPolicyPathSchema(true, true, "Segment path where VM port should be tagged"),
"tag": getTagsSchema(),
},
},
},
},
}
}
Expand All @@ -47,9 +59,9 @@ func listAllPolicyVirtualMachines(connector *client.RestConnector, m interface{}
var cursor *string
total := 0

enforcementPointPath := getPolicyEnforcementPointPath(m)
for {
// NOTE: the search API does not return resource_type of VirtualMachine
enforcementPointPath := getPolicyEnforcementPointPath(m)
// NOTE: Search API doesn't filter by realized state resources
vms, err := client.List(cursor, &enforcementPointPath, &boolFalse, nil, nil, &boolFalse, nil)
if err != nil {
return results, err
Expand All @@ -66,6 +78,58 @@ func listAllPolicyVirtualMachines(connector *client.RestConnector, m interface{}
}
}

func listAllPolicySegmentPorts(connector *client.RestConnector, segmentPath string) ([]model.SegmentPort, error) {
client := segments.NewDefaultPortsClient(connector)
segmentID := getPolicyIDFromPath(segmentPath)
var results []model.SegmentPort
boolFalse := false
var cursor *string
total := 0

for {
vms, err := client.List(segmentID, cursor, &boolFalse, nil, nil, &boolFalse, nil)
if err != nil {
return results, err
}
results = append(results, vms.Results...)
if total == 0 && vms.ResultCount != nil {
// first response
total = int(*vms.ResultCount)
}
cursor = vms.Cursor
if len(results) >= total {
log.Printf("[DEBUG] Found %d ports for segment %s", len(results), segmentID)
return results, nil
}
}
}

func listAllPolicyVifs(m interface{}) ([]model.VirtualNetworkInterface, error) {

client := enforcement_points.NewDefaultVifsClient(getPolicyConnector(m))
var results []model.VirtualNetworkInterface
var cursor *string
total := 0

enforcementPointPath := getPolicyEnforcementPoint(m)
for {
// NOTE: Search API doesn't filter by realized state resources
vifs, err := client.List(enforcementPointPath, cursor, nil, nil, nil, nil, nil)
if err != nil {
return results, err
}
results = append(results, vifs.Results...)
if total == 0 && vifs.ResultCount != nil {
// first response
total = int(*vifs.ResultCount)
}
cursor = vifs.Cursor
if len(results) >= total {
return results, nil
}
}
}

func findNsxtPolicyVMByNamePrefix(connector *client.RestConnector, namePrefix string, m interface{}) ([]model.VirtualMachine, []model.VirtualMachine, error) {
var perfectMatch, prefixMatch []model.VirtualMachine

Expand Down Expand Up @@ -109,7 +173,7 @@ func findNsxtPolicyVMByID(connector *client.RestConnector, vmID string, m interf
}

func updateNsxtPolicyVMTags(connector *client.RestConnector, externalID string, tags []model.Tag, m interface{}) error {
client := updateClient.NewDefaultVirtualMachinesClient(connector)
client := enforcement_points.NewDefaultVirtualMachinesClient(connector)

tagUpdate := model.VirtualMachineTagsUpdate{
Tags: tags,
Expand All @@ -118,6 +182,105 @@ func updateNsxtPolicyVMTags(connector *client.RestConnector, externalID string,
return client.Updatetags(getPolicyEnforcementPoint(m), tagUpdate)
}

func listPolicyVifAttachmentsForVM(m interface{}, externalID string) ([]string, error) {
var vifAttachmentIds []string
vifs, err := listAllPolicyVifs(m)
if err != nil {
return vifAttachmentIds, err
}

for _, vif := range vifs {
if (vif.LportAttachmentId != nil) && (vif.OwnerVmId != nil) && *vif.OwnerVmId == externalID {
vifAttachmentIds = append(vifAttachmentIds, *vif.LportAttachmentId)
}
}

return vifAttachmentIds, nil
}

func updateNsxtPolicyVMPortTags(connector *client.RestConnector, externalID string, portTags []interface{}, m interface{}, isDelete bool) error {

client := segments.NewDefaultPortsClient(connector)

vifAttachmentIds, err := listPolicyVifAttachmentsForVM(m, externalID)
if err != nil {
return err
}

for _, portTag := range portTags {
data := portTag.(map[string]interface{})
segmentPath := data["segment_path"].(string)
var tags []model.Tag
if !isDelete {
tags = getPolicyTagsFromSet(data["tag"].(*schema.Set))
}

ports, portsErr := listAllPolicySegmentPorts(connector, segmentPath)
if portsErr != nil {
return portsErr
}
for _, port := range ports {
if port.Attachment == nil || port.Attachment.Id == nil {
continue
}

for _, attachment := range vifAttachmentIds {
if attachment == *port.Attachment.Id {
port.Tags = tags
log.Printf("[DEBUG] Updating port %s with %d tags", *port.Path, len(tags))
segmentID := getPolicyIDFromPath(segmentPath)
_, err = client.Update(segmentID, *port.Id, port)
if err != nil {
return err
}
break
}
}
}
}

return nil
}

func setPolicyVMPortTagsInSchema(d *schema.ResourceData, m interface{}, externalID string) error {

connector := getPolicyConnector(m)
vifAttachmentIds, err := listPolicyVifAttachmentsForVM(m, externalID)
if err != nil {
return err
}

portTags := d.Get("port").([]interface{})
var actualPortTags []map[string]interface{}
for _, portTag := range portTags {
data := portTag.(map[string]interface{})
segmentPath := data["segment_path"].(string)

ports, portsErr := listAllPolicySegmentPorts(connector, segmentPath)
if portsErr != nil {
return portsErr
}
for _, port := range ports {
if port.Attachment == nil || port.Attachment.Id == nil {
continue
}

for _, attachment := range vifAttachmentIds {
if attachment == *port.Attachment.Id {
tags := make(map[string]interface{})
tags["segment_path"] = segmentPath
tags["tag"] = initPolicyTagsSet(port.Tags)
actualPortTags = append(actualPortTags, tags)
}
}
}
}

d.Set("port", actualPortTags)

return nil
}

func resourceNsxtPolicyVMTagsRead(d *schema.ResourceData, m interface{}) error {
connector := getPolicyConnector(m)

Expand All @@ -138,7 +301,7 @@ func resourceNsxtPolicyVMTagsRead(d *schema.ResourceData, m interface{}) error {
d.Set("instance_id", vm.ExternalId)
}

return nil
return setPolicyVMPortTagsInSchema(d, m, *vm.ExternalId)
}

func resourceNsxtPolicyVMTagsCreate(d *schema.ResourceData, m interface{}) error {
Expand All @@ -154,12 +317,20 @@ func resourceNsxtPolicyVMTagsCreate(d *schema.ResourceData, m interface{}) error
if tags == nil {
tags = make([]model.Tag, 0)
}

err = updateNsxtPolicyVMTags(connector, *vm.ExternalId, tags, m)
if err != nil {
return handleCreateError("Virtual Machine Tag", *vm.ExternalId, err)
}

portTags := d.Get("port").([]interface{})
err = updateNsxtPolicyVMPortTags(connector, *vm.ExternalId, portTags, m, false)
if err != nil {
return handleCreateError("Segment Port Tag", *vm.ExternalId, err)
}

d.SetId(*vm.ExternalId)
d.Set("port", portTags)

return resourceNsxtPolicyVMTagsRead(d, m)
}
Expand All @@ -184,5 +355,11 @@ func resourceNsxtPolicyVMTagsDelete(d *schema.ResourceData, m interface{}) error
return handleDeleteError("Virtual Machine Tag", *vm.ExternalId, err)
}

portTags := d.Get("port").([]interface{})
err = updateNsxtPolicyVMPortTags(connector, *vm.ExternalId, portTags, m, true)
if err != nil {
return handleCreateError("Segment Port Tag", *vm.ExternalId, err)
}

return err
}
84 changes: 84 additions & 0 deletions nsxt/resource_nsxt_policy_vm_tags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,51 @@ func TestAccResourceNsxtPolicyVMTags_basic(t *testing.T) {
})
}

func TestAccResourceNsxtPolicyVMTags_withPorts(t *testing.T) {
vmID := getTestVMID()
testResourceName := "nsxt_policy_vm_tags.test"

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccOnlyLocalManager(t)
testAccPreCheck(t)
testAccEnvDefined(t, "NSXT_TEST_VM_ID")
testAccEnvDefined(t, "NSXT_TEST_VM_SEGMENT_ID")
},
Providers: testAccProviders,
CheckDestroy: func(state *terraform.State) error {
return testAccNSXPolicyVMTagsCheckDestroy(state)
},
Steps: []resource.TestStep{
{
Config: testAccNSXPolicyVMPortTagsCreateTemplate(vmID),
Check: resource.ComposeTestCheckFunc(
testAccNSXPolicyVMTagsCheckExists(testResourceName),
resource.TestCheckResourceAttr(testResourceName, "tag.#", "1"),
resource.TestCheckResourceAttr(testResourceName, "port.#", "1"),
resource.TestCheckResourceAttrSet(testResourceName, "port.0.segment_path"),
resource.TestCheckResourceAttr(testResourceName, "port.0.tag.#", "1"),
resource.TestCheckResourceAttr(testResourceName, "instance_id", vmID),
),
},
{
Config: testAccNSXPolicyVMPortTagsUpdateTemplate(vmID),
Check: resource.ComposeTestCheckFunc(
testAccNSXPolicyVMTagsCheckExists(testResourceName),
resource.TestCheckResourceAttr(testResourceName, "tag.#", "0"),
resource.TestCheckResourceAttr(testResourceName, "port.#", "1"),
resource.TestCheckResourceAttrSet(testResourceName, "port.0.segment_path"),
resource.TestCheckResourceAttr(testResourceName, "port.0.tag.#", "2"),
resource.TestCheckResourceAttr(testResourceName, "instance_id", vmID),
),
},
{
Config: testAccNsxtPolicyEmptyTemplate(),
},
},
})
}

func TestAccResourceNsxtPolicyVMTags_import_basic(t *testing.T) {
vmID := getTestVMID()
testResourceName := "nsxt_policy_vm_tags.test"
Expand Down Expand Up @@ -143,3 +188,42 @@ resource "nsxt_policy_vm_tags" "test" {
}
}`, instanceID)
}

func testAccNSXPolicyVMPortTagsCreateTemplate(instanceID string) string {
return fmt.Sprintf(`
resource "nsxt_policy_vm_tags" "test" {
instance_id = "%s"
tag {
scope = "color"
tag = "blue"
}
port {
segment_path = "/infra/segments/%s"
tag {
scope = "color"
tag = "green"
}
}
}`, instanceID, getTestVMSegmentID())
}

func testAccNSXPolicyVMPortTagsUpdateTemplate(instanceID string) string {
return fmt.Sprintf(`
resource "nsxt_policy_vm_tags" "test" {
instance_id = "%s"
port {
segment_path = "/infra/segments/%s"
tag {
scope = "color"
tag = "green"
}
tag {
scope = "shape"
tag = "round"
}
}
}`, instanceID, getTestVMSegmentID())
}
4 changes: 4 additions & 0 deletions nsxt/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ func getTestVMID() string {
return os.Getenv("NSXT_TEST_VM_ID")
}

func getTestVMSegmentID() string {
return os.Getenv("NSXT_TEST_VM_SEGMENT_ID")
}

func getTestVMName() string {
return os.Getenv("NSXT_TEST_VM_NAME")
}
Expand Down
Loading

0 comments on commit afcfae6

Please sign in to comment.