Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Support tagging VM segment port #406

Merged
merged 1 commit into from
Jul 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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