github.com/vmware/govmomi@v0.37.2/govc/dvs/portgroup/info.go (about)

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