github.com/vmware/govmomi@v0.51.0/cli/dvs/portgroup/info.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package portgroup
     6  
     7  import (
     8  	"context"
     9  	"flag"
    10  	"fmt"
    11  	"io"
    12  	"reflect"
    13  	"sort"
    14  	"strings"
    15  
    16  	"github.com/vmware/govmomi/cli"
    17  	"github.com/vmware/govmomi/cli/flags"
    18  	"github.com/vmware/govmomi/object"
    19  	"github.com/vmware/govmomi/vim25/mo"
    20  	"github.com/vmware/govmomi/vim25/types"
    21  )
    22  
    23  type info struct {
    24  	*flags.DatacenterFlag
    25  
    26  	pg         string
    27  	active     bool
    28  	connected  bool
    29  	inside     bool
    30  	uplinkPort bool
    31  	vlanID     int
    32  	count      int
    33  	dvsRules   bool
    34  }
    35  
    36  var protocols = map[int32]string{
    37  	1:  "icmp",
    38  	2:  "igmp",
    39  	6:  "tcp",
    40  	17: "udp",
    41  	58: "ipv6-icmp",
    42  }
    43  
    44  type trafficRule struct {
    45  	Description        string
    46  	Direction          string
    47  	Action             string
    48  	Protocol           string
    49  	SourceAddress      string
    50  	SourceIpPort       string
    51  	DestinationAddress string
    52  	DestinationIpPort  string
    53  }
    54  
    55  func init() {
    56  	cli.Register("dvs.portgroup.info", &info{})
    57  }
    58  
    59  func (cmd *info) Register(ctx context.Context, f *flag.FlagSet) {
    60  	cmd.DatacenterFlag, ctx = flags.NewDatacenterFlag(ctx)
    61  	cmd.DatacenterFlag.Register(ctx, f)
    62  
    63  	f.StringVar(&cmd.pg, "pg", "", "Distributed Virtual Portgroup")
    64  	f.BoolVar(&cmd.active, "active", false, "Filter by port active or inactive status")
    65  	f.BoolVar(&cmd.connected, "connected", false, "Filter by port connected or disconnected status")
    66  	f.BoolVar(&cmd.inside, "inside", true, "Filter by port inside or outside status")
    67  	f.BoolVar(&cmd.uplinkPort, "uplinkPort", false, "Filter for uplink ports")
    68  	f.IntVar(&cmd.vlanID, "vlan", 0, "Filter by VLAN ID (0 = unfiltered)")
    69  	f.IntVar(&cmd.count, "count", 0, "Number of matches to return (0 = unlimited)")
    70  	f.BoolVar(&cmd.dvsRules, "r", false, "Show DVS rules")
    71  }
    72  
    73  func (cmd *info) Usage() string {
    74  	return "DVS"
    75  }
    76  
    77  func (cmd *info) Description() string {
    78  	return `Portgroup info for DVS.
    79  
    80  Examples:
    81    govc dvs.portgroup.info DSwitch
    82    govc dvs.portgroup.info -pg InternalNetwork DSwitch
    83    govc find / -type DistributedVirtualSwitch | xargs -n1 govc dvs.portgroup.info`
    84  }
    85  
    86  type infoResult struct {
    87  	Port []types.DistributedVirtualPort `json:"port"`
    88  	cmd  *info
    89  }
    90  
    91  func printPort(port types.BaseDvsIpPort) string {
    92  	if port != nil {
    93  		switch portType := port.(type) {
    94  		case *types.DvsSingleIpPort:
    95  			return fmt.Sprintf("%d", portType.PortNumber)
    96  		case *types.DvsIpPortRange:
    97  			return fmt.Sprintf("%d-%d", portType.StartPortNumber,
    98  				portType.EndPortNumber)
    99  		}
   100  	}
   101  	return "Any"
   102  }
   103  
   104  func printAddress(address types.BaseIpAddress) string {
   105  	if address != nil {
   106  		switch (address).(type) {
   107  		case *types.SingleIp:
   108  			return address.(*types.SingleIp).Address
   109  		case *types.IpRange:
   110  			return fmt.Sprintf("%s/%d", address.(*types.IpRange).AddressPrefix, address.(*types.IpRange).PrefixLength)
   111  		}
   112  	}
   113  	return "Any"
   114  }
   115  
   116  func printAction(action types.BaseDvsNetworkRuleAction) string {
   117  	if action != nil {
   118  		switch (action).(type) {
   119  		case *types.DvsAcceptNetworkRuleAction:
   120  			return fmt.Sprintf("Accept")
   121  		case *types.DvsDropNetworkRuleAction:
   122  			return fmt.Sprintf("Drop")
   123  		}
   124  	}
   125  	return "n/a"
   126  }
   127  
   128  func printTable(trafficRuleSet map[int]map[int]trafficRule, portID int) {
   129  	if len(trafficRuleSet[portID]) == 0 {
   130  		return
   131  	}
   132  
   133  	keys := []int{}
   134  	for k := range trafficRuleSet[portID] {
   135  		keys = append(keys, k)
   136  	}
   137  	sort.Ints(keys)
   138  	tabWidthInt := 22
   139  	tabWidth := fmt.Sprintf("%d", tabWidthInt)
   140  	headLen := 9*(tabWidthInt+2) - 1
   141  	fmt.Printf("+" + strings.Repeat("-", headLen) + "+\n")
   142  	format := "| %-" + tabWidth +
   143  		"s| %-" + tabWidth +
   144  		"s| %-" + tabWidth +
   145  		"s| %-" + tabWidth +
   146  		"s| %-" + tabWidth +
   147  		"s| %-" + tabWidth +
   148  		"s| %-" + tabWidth +
   149  		"s| %-" + tabWidth +
   150  		"s| %-" + tabWidth +
   151  		"s|\n"
   152  	fmt.Printf(format,
   153  		"Sequence",
   154  		"Description",
   155  		"Direction",
   156  		"Action",
   157  		"Protocol",
   158  		"SourceAddress",
   159  		"SourceIpPort",
   160  		"DestinationAddress",
   161  		"DestinationIpPort")
   162  	fmt.Printf("+" + strings.Repeat("-", headLen) + "+\n")
   163  	for _, id := range keys {
   164  		fmt.Printf(format,
   165  			fmt.Sprintf("%d", id),
   166  			trafficRuleSet[portID][id].Description,
   167  			trafficRuleSet[portID][id].Direction,
   168  			trafficRuleSet[portID][id].Action,
   169  			trafficRuleSet[portID][id].Protocol,
   170  			trafficRuleSet[portID][id].SourceAddress,
   171  			trafficRuleSet[portID][id].SourceIpPort,
   172  			trafficRuleSet[portID][id].DestinationAddress,
   173  			trafficRuleSet[portID][id].DestinationIpPort)
   174  	}
   175  	fmt.Printf("+" + strings.Repeat("-", headLen) + "+\n")
   176  }
   177  
   178  func (r *infoResult) Write(w io.Writer) error {
   179  	trafficRuleSet := make(map[int]map[int]trafficRule)
   180  	for portID, port := range r.Port {
   181  		var vlanID int32
   182  		setting := port.Config.Setting.(*types.VMwareDVSPortSetting)
   183  
   184  		switch vlan := setting.Vlan.(type) {
   185  		case *types.VmwareDistributedVirtualSwitchVlanIdSpec:
   186  			vlanID = vlan.VlanId
   187  		case *types.VmwareDistributedVirtualSwitchTrunkVlanSpec:
   188  		case *types.VmwareDistributedVirtualSwitchPvlanSpec:
   189  			vlanID = vlan.PvlanId
   190  		}
   191  
   192  		// Show port info if: VLAN ID is not defined, or VLAN ID matches requested VLAN
   193  		if r.cmd.vlanID == 0 || vlanID == int32(r.cmd.vlanID) {
   194  			fmt.Printf("PortgroupKey: %s\n", port.PortgroupKey)
   195  			fmt.Printf("DvsUuid:      %s\n", port.DvsUuid)
   196  			fmt.Printf("VlanId:       %d\n", vlanID)
   197  			fmt.Printf("PortKey:      %s\n\n", port.Key)
   198  
   199  			trafficRuleSet[portID] = make(map[int]trafficRule)
   200  
   201  			if r.cmd.dvsRules && setting.FilterPolicy != nil &&
   202  				setting.FilterPolicy.FilterConfig != nil &&
   203  				len(setting.FilterPolicy.FilterConfig) > 0 {
   204  
   205  				rules, ok := setting.FilterPolicy.FilterConfig[0].(*types.DvsTrafficFilterConfig)
   206  				if !ok {
   207  					continue
   208  				}
   209  				if rules != nil && rules.TrafficRuleset != nil {
   210  					for _, rule := range rules.TrafficRuleset.Rules {
   211  						for _, q := range rule.Qualifier {
   212  							var protocol string
   213  							ipr, ok := q.(*types.DvsIpNetworkRuleQualifier)
   214  							if !ok {
   215  								continue
   216  							}
   217  							if val, ok := protocols[ipr.Protocol.Value]; ok {
   218  								protocol = val
   219  							} else {
   220  								protocol = fmt.Sprintf("%d", ipr.Protocol.Value)
   221  							}
   222  
   223  							trafficRuleSet[portID][int(rule.Sequence)] = trafficRule{
   224  								Description:        rule.Description,
   225  								Direction:          rule.Direction,
   226  								Action:             printAction(rule.Action),
   227  								Protocol:           protocol,
   228  								SourceAddress:      printAddress(ipr.SourceAddress),
   229  								SourceIpPort:       printPort(ipr.SourceIpPort),
   230  								DestinationAddress: printAddress(ipr.DestinationAddress),
   231  								DestinationIpPort:  printPort(ipr.DestinationIpPort),
   232  							}
   233  						}
   234  					}
   235  				}
   236  			}
   237  		}
   238  	}
   239  
   240  	if r.cmd.dvsRules && len(r.Port) > 0 {
   241  		eq := 0
   242  		for i := range r.Port {
   243  			if i > 0 {
   244  				reflect.DeepEqual(trafficRuleSet[i-1], trafficRuleSet[i])
   245  				if reflect.DeepEqual(trafficRuleSet[i-1], trafficRuleSet[i]) {
   246  					eq++
   247  				} else {
   248  					fmt.Printf("%s and %s port rules are unequal\n", r.Port[i-1].Key, r.Port[i].Key)
   249  					break
   250  				}
   251  			}
   252  		}
   253  
   254  		if eq == len(trafficRuleSet)-1 {
   255  			printTable(trafficRuleSet, 0)
   256  		} else {
   257  			for portID, port := range r.Port {
   258  				fmt.Printf("\nPortKey:      %s\n", port.Key)
   259  				printTable(trafficRuleSet, portID)
   260  			}
   261  		}
   262  	}
   263  
   264  	return nil
   265  }
   266  
   267  func (cmd *info) Run(ctx context.Context, f *flag.FlagSet) error {
   268  	if f.NArg() != 1 {
   269  		return flag.ErrHelp
   270  	}
   271  
   272  	finder, err := cmd.Finder()
   273  	if err != nil {
   274  		return err
   275  	}
   276  
   277  	// Retrieve DVS reference
   278  	net, err := finder.Network(ctx, f.Arg(0))
   279  	if err != nil {
   280  		return err
   281  	}
   282  
   283  	// Convert to DVS object type
   284  	dvs, ok := net.(*object.DistributedVirtualSwitch)
   285  	if !ok {
   286  		return fmt.Errorf("%s (%s) is not a DVS", f.Arg(0), net.Reference().Type)
   287  	}
   288  
   289  	// Set base search criteria
   290  	criteria := types.DistributedVirtualSwitchPortCriteria{
   291  		Connected:  types.NewBool(cmd.connected),
   292  		Active:     types.NewBool(cmd.active),
   293  		UplinkPort: types.NewBool(cmd.uplinkPort),
   294  		Inside:     types.NewBool(cmd.inside),
   295  	}
   296  
   297  	// If a distributed virtual portgroup path is set, then add its portgroup key to the base criteria
   298  	if len(cmd.pg) > 0 {
   299  		// Retrieve distributed virtual portgroup reference
   300  		net, err = finder.Network(ctx, cmd.pg)
   301  		if err != nil {
   302  			return err
   303  		}
   304  
   305  		// Convert distributed virtual portgroup object type
   306  		dvpg, ok := net.(*object.DistributedVirtualPortgroup)
   307  		if !ok {
   308  			return fmt.Errorf("%s (%s) is not a DVPG", cmd.pg, net.Reference().Type)
   309  		}
   310  
   311  		// Obtain portgroup key property
   312  		var dvp mo.DistributedVirtualPortgroup
   313  		if err := dvpg.Properties(ctx, dvpg.Reference(), []string{"key"}, &dvp); err != nil {
   314  			return err
   315  		}
   316  
   317  		// Add portgroup key to port search criteria
   318  		criteria.PortgroupKey = []string{dvp.Key}
   319  	}
   320  
   321  	res, err := dvs.FetchDVPorts(ctx, &criteria)
   322  	if err != nil {
   323  		return err
   324  	}
   325  
   326  	// Truncate output to -count if specified
   327  	if cmd.count > 0 && cmd.count < len(res) {
   328  		res = res[:cmd.count]
   329  	}
   330  
   331  	info := infoResult{
   332  		cmd:  cmd,
   333  		Port: res,
   334  	}
   335  
   336  	return cmd.WriteResult(&info)
   337  }