github.com/vmware/govmomi@v0.51.0/simulator/dvs.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 simulator
     6  
     7  import (
     8  	"fmt"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/google/uuid"
    13  
    14  	"github.com/vmware/govmomi/vim25/methods"
    15  	"github.com/vmware/govmomi/vim25/mo"
    16  	"github.com/vmware/govmomi/vim25/soap"
    17  	"github.com/vmware/govmomi/vim25/types"
    18  )
    19  
    20  type DistributedVirtualSwitch struct {
    21  	mo.DistributedVirtualSwitch
    22  
    23  	types.FetchDVPortsResponse
    24  }
    25  
    26  func (s *DistributedVirtualSwitch) eventArgument() *types.DvsEventArgument {
    27  	return &types.DvsEventArgument{
    28  		EntityEventArgument: types.EntityEventArgument{
    29  			Name: s.Name,
    30  		},
    31  		Dvs: s.Self,
    32  	}
    33  }
    34  
    35  func (s *DistributedVirtualSwitch) event(ctx *Context) types.DvsEvent {
    36  	return types.DvsEvent{
    37  		Event: types.Event{
    38  			Datacenter: datacenterEventArgument(ctx, s),
    39  			Dvs:        s.eventArgument(),
    40  		},
    41  	}
    42  }
    43  
    44  func (s *DistributedVirtualSwitch) AddDVPortgroupTask(ctx *Context, c *types.AddDVPortgroup_Task) soap.HasFault {
    45  	task := CreateTask(s, "addDVPortgroup", func(t *Task) (types.AnyType, types.BaseMethodFault) {
    46  		f := ctx.Map.getEntityParent(s, "Folder").(*Folder)
    47  
    48  		portgroups := s.Portgroup
    49  		portgroupNames := s.Summary.PortgroupName
    50  
    51  		for _, spec := range c.Spec {
    52  			pg := &DistributedVirtualPortgroup{}
    53  			pg.Name = spec.Name
    54  			pg.Entity().Name = pg.Name
    55  
    56  			// Standard AddDVPortgroupTask() doesn't allow duplicate names, but NSX 3.0 does create some DVPGs with the same name.
    57  			// Allow duplicate names using this prefix so we can reproduce and test this condition.
    58  			if strings.HasPrefix(pg.Name, "NSX-") || spec.BackingType == string(types.DistributedVirtualPortgroupBackingTypeNsx) {
    59  				if spec.LogicalSwitchUuid == "" {
    60  					spec.LogicalSwitchUuid = uuid.New().String()
    61  				}
    62  				if spec.SegmentId == "" {
    63  					spec.SegmentId = fmt.Sprintf("/infra/segments/vnet_%s", uuid.New().String())
    64  				}
    65  
    66  			} else {
    67  				if obj := ctx.Map.FindByName(pg.Name, f.ChildEntity); obj != nil {
    68  					return nil, &types.DuplicateName{
    69  						Name:   pg.Name,
    70  						Object: obj.Reference(),
    71  					}
    72  				}
    73  			}
    74  
    75  			folderPutChild(ctx, &f.Folder, pg)
    76  
    77  			pg.Key = pg.Self.Value
    78  			pg.Config = types.DVPortgroupConfigInfo{
    79  				Key:                          pg.Key,
    80  				Name:                         pg.Name,
    81  				NumPorts:                     spec.NumPorts,
    82  				DistributedVirtualSwitch:     &s.Self,
    83  				DefaultPortConfig:            spec.DefaultPortConfig,
    84  				Description:                  spec.Description,
    85  				Type:                         spec.Type,
    86  				Policy:                       spec.Policy,
    87  				PortNameFormat:               spec.PortNameFormat,
    88  				Scope:                        spec.Scope,
    89  				VendorSpecificConfig:         spec.VendorSpecificConfig,
    90  				ConfigVersion:                spec.ConfigVersion,
    91  				AutoExpand:                   spec.AutoExpand,
    92  				VmVnicNetworkResourcePoolKey: spec.VmVnicNetworkResourcePoolKey,
    93  				LogicalSwitchUuid:            spec.LogicalSwitchUuid,
    94  				SegmentId:                    spec.SegmentId,
    95  				BackingType:                  spec.BackingType,
    96  			}
    97  
    98  			if pg.Config.LogicalSwitchUuid != "" {
    99  				if pg.Config.BackingType == "" {
   100  					pg.Config.BackingType = "nsx"
   101  				}
   102  			}
   103  
   104  			if pg.Config.DefaultPortConfig == nil {
   105  				pg.Config.DefaultPortConfig = &types.VMwareDVSPortSetting{
   106  					Vlan: new(types.VmwareDistributedVirtualSwitchVlanIdSpec),
   107  					UplinkTeamingPolicy: &types.VmwareUplinkPortTeamingPolicy{
   108  						Policy: &types.StringPolicy{
   109  							Value: "loadbalance_srcid",
   110  						},
   111  						ReversePolicy: &types.BoolPolicy{
   112  							Value: types.NewBool(true),
   113  						},
   114  						NotifySwitches: &types.BoolPolicy{
   115  							Value: types.NewBool(true),
   116  						},
   117  						RollingOrder: &types.BoolPolicy{
   118  							Value: types.NewBool(true),
   119  						},
   120  					},
   121  				}
   122  			}
   123  
   124  			if pg.Config.Policy == nil {
   125  				pg.Config.Policy = &types.VMwareDVSPortgroupPolicy{
   126  					DVPortgroupPolicy: types.DVPortgroupPolicy{
   127  						BlockOverrideAllowed:               true,
   128  						ShapingOverrideAllowed:             false,
   129  						VendorConfigOverrideAllowed:        false,
   130  						LivePortMovingAllowed:              false,
   131  						PortConfigResetAtDisconnect:        true,
   132  						NetworkResourcePoolOverrideAllowed: types.NewBool(false),
   133  						TrafficFilterOverrideAllowed:       types.NewBool(false),
   134  					},
   135  					VlanOverrideAllowed:           false,
   136  					UplinkTeamingOverrideAllowed:  false,
   137  					SecurityPolicyOverrideAllowed: false,
   138  					IpfixOverrideAllowed:          types.NewBool(false),
   139  				}
   140  			}
   141  
   142  			for i := 0; i < int(spec.NumPorts); i++ {
   143  				pg.PortKeys = append(pg.PortKeys, strconv.Itoa(i))
   144  			}
   145  
   146  			portgroups = append(portgroups, pg.Self)
   147  			portgroupNames = append(portgroupNames, pg.Name)
   148  
   149  			for _, h := range s.Summary.HostMember {
   150  				pg.Host = append(pg.Host, h)
   151  
   152  				host := ctx.Map.Get(h).(*HostSystem)
   153  				ctx.Map.AppendReference(ctx, host, &host.Network, pg.Reference())
   154  
   155  				parent := ctx.Map.Get(*host.HostSystem.Parent)
   156  				computeNetworks := append(hostParent(ctx, &host.HostSystem).Network, pg.Reference())
   157  				ctx.Update(parent, []types.PropertyChange{
   158  					{Name: "network", Val: computeNetworks},
   159  				})
   160  			}
   161  
   162  			ctx.postEvent(&types.DVPortgroupCreatedEvent{
   163  				DVPortgroupEvent: pg.event(ctx),
   164  			})
   165  		}
   166  
   167  		ctx.Update(s, []types.PropertyChange{
   168  			{Name: "portgroup", Val: portgroups},
   169  			{Name: "summary.portgroupName", Val: portgroupNames},
   170  		})
   171  
   172  		return nil, nil
   173  	})
   174  
   175  	return &methods.AddDVPortgroup_TaskBody{
   176  		Res: &types.AddDVPortgroup_TaskResponse{
   177  			Returnval: task.Run(ctx),
   178  		},
   179  	}
   180  }
   181  
   182  func (s *DistributedVirtualSwitch) ReconfigureDvsTask(ctx *Context, req *types.ReconfigureDvs_Task) soap.HasFault {
   183  	task := CreateTask(s, "reconfigureDvs", func(t *Task) (types.AnyType, types.BaseMethodFault) {
   184  		spec := req.Spec.GetDVSConfigSpec()
   185  
   186  		members := s.Summary.HostMember
   187  
   188  		for _, member := range spec.Host {
   189  			h := ctx.Map.Get(member.Host)
   190  			if h == nil {
   191  				return nil, &types.ManagedObjectNotFound{Obj: member.Host}
   192  			}
   193  
   194  			host := h.(*HostSystem)
   195  
   196  			switch types.ConfigSpecOperation(member.Operation) {
   197  			case types.ConfigSpecOperationAdd:
   198  				if FindReference(s.Summary.HostMember, member.Host) != nil {
   199  					return nil, &types.AlreadyExists{Name: host.Name}
   200  				}
   201  
   202  				hostNetworks := append(host.Network, s.Portgroup...)
   203  				ctx.Update(host, []types.PropertyChange{
   204  					{Name: "network", Val: hostNetworks},
   205  				})
   206  				members = append(members, member.Host)
   207  				parent := ctx.Map.Get(*host.HostSystem.Parent)
   208  
   209  				var pgs []types.ManagedObjectReference
   210  				for _, ref := range s.Portgroup {
   211  					pg := ctx.Map.Get(ref).(*DistributedVirtualPortgroup)
   212  					pgs = append(pgs, ref)
   213  
   214  					pgHosts := append(pg.Host, member.Host)
   215  					ctx.Update(pg, []types.PropertyChange{
   216  						{Name: "host", Val: pgHosts},
   217  					})
   218  
   219  					cr := hostParent(ctx, &host.HostSystem)
   220  					if FindReference(cr.Network, ref) == nil {
   221  						computeNetworks := append(cr.Network, ref)
   222  						ctx.Update(parent, []types.PropertyChange{
   223  							{Name: "network", Val: computeNetworks},
   224  						})
   225  					}
   226  				}
   227  
   228  				ctx.postEvent(&types.DvsHostJoinedEvent{
   229  					DvsEvent:   s.event(ctx),
   230  					HostJoined: *host.eventArgument(),
   231  				})
   232  			case types.ConfigSpecOperationRemove:
   233  				for _, ref := range host.Vm {
   234  					vm := ctx.Map.Get(ref).(*VirtualMachine)
   235  					if pg := FindReference(vm.Network, s.Portgroup...); pg != nil {
   236  						return nil, &types.ResourceInUse{
   237  							Type: pg.Type,
   238  							Name: pg.Value,
   239  						}
   240  					}
   241  				}
   242  
   243  				RemoveReference(&members, member.Host)
   244  
   245  				ctx.postEvent(&types.DvsHostLeftEvent{
   246  					DvsEvent: s.event(ctx),
   247  					HostLeft: *host.eventArgument(),
   248  				})
   249  			case types.ConfigSpecOperationEdit:
   250  				return nil, &types.NotSupported{}
   251  			}
   252  		}
   253  
   254  		ctx.Update(s, []types.PropertyChange{
   255  			{Name: "summary.hostMember", Val: members},
   256  		})
   257  
   258  		ctx.postEvent(&types.DvsReconfiguredEvent{
   259  			DvsEvent:   s.event(ctx),
   260  			ConfigSpec: spec,
   261  		})
   262  
   263  		return nil, nil
   264  	})
   265  
   266  	return &methods.ReconfigureDvs_TaskBody{
   267  		Res: &types.ReconfigureDvs_TaskResponse{
   268  			Returnval: task.Run(ctx),
   269  		},
   270  	}
   271  }
   272  
   273  func (s *DistributedVirtualSwitch) FetchDVPorts(ctx *Context, req *types.FetchDVPorts) soap.HasFault {
   274  	body := &methods.FetchDVPortsBody{}
   275  	body.Res = &types.FetchDVPortsResponse{
   276  		Returnval: s.dvPortgroups(ctx, req.Criteria),
   277  	}
   278  	return body
   279  }
   280  
   281  func (s *DistributedVirtualSwitch) DestroyTask(ctx *Context, req *types.Destroy_Task) soap.HasFault {
   282  	task := CreateTask(s, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
   283  		// TODO: should return ResourceInUse fault if any VM is using a port on this switch
   284  		// and past that, remove refs from each host.Network, etc
   285  		f := ctx.Map.getEntityParent(s, "Folder").(*Folder)
   286  		folderRemoveChild(ctx, &f.Folder, s.Reference())
   287  		ctx.postEvent(&types.DvsDestroyedEvent{DvsEvent: s.event(ctx)})
   288  		return nil, nil
   289  	})
   290  
   291  	return &methods.Destroy_TaskBody{
   292  		Res: &types.Destroy_TaskResponse{
   293  			Returnval: task.Run(ctx),
   294  		},
   295  	}
   296  }
   297  
   298  func (s *DistributedVirtualSwitch) dvPortgroups(ctx *Context, criteria *types.DistributedVirtualSwitchPortCriteria) []types.DistributedVirtualPort {
   299  	res := s.FetchDVPortsResponse.Returnval
   300  	if len(res) != 0 {
   301  		return res
   302  	}
   303  
   304  	for _, ref := range s.Portgroup {
   305  		pg := ctx.Map.Get(ref).(*DistributedVirtualPortgroup)
   306  
   307  		for _, key := range pg.PortKeys {
   308  			res = append(res, types.DistributedVirtualPort{
   309  				DvsUuid:      s.Uuid,
   310  				Key:          key,
   311  				PortgroupKey: pg.Key,
   312  				Config: types.DVPortConfigInfo{
   313  					Setting: pg.Config.DefaultPortConfig,
   314  				},
   315  			})
   316  		}
   317  	}
   318  
   319  	// filter ports by criteria
   320  	res = s.filterDVPorts(res, criteria)
   321  
   322  	return res
   323  }
   324  
   325  func (s *DistributedVirtualSwitch) filterDVPorts(
   326  	ports []types.DistributedVirtualPort,
   327  	criteria *types.DistributedVirtualSwitchPortCriteria,
   328  ) []types.DistributedVirtualPort {
   329  	if criteria == nil {
   330  		return ports
   331  	}
   332  
   333  	ports = s.filterDVPortsByPortgroupKey(ports, criteria)
   334  	ports = s.filterDVPortsByPortKey(ports, criteria)
   335  	ports = s.filterDVPortsByConnected(ports, criteria)
   336  
   337  	return ports
   338  }
   339  
   340  func (s *DistributedVirtualSwitch) filterDVPortsByPortgroupKey(
   341  	ports []types.DistributedVirtualPort,
   342  	criteria *types.DistributedVirtualSwitchPortCriteria,
   343  ) []types.DistributedVirtualPort {
   344  	if len(criteria.PortgroupKey) == 0 || criteria.Inside == nil {
   345  		return ports
   346  	}
   347  
   348  	// inside portgroup keys
   349  	if *criteria.Inside {
   350  		filtered := []types.DistributedVirtualPort{}
   351  
   352  		for _, p := range ports {
   353  			for _, pgk := range criteria.PortgroupKey {
   354  				if p.PortgroupKey == pgk {
   355  					filtered = append(filtered, p)
   356  					break
   357  				}
   358  			}
   359  		}
   360  		return filtered
   361  	}
   362  
   363  	// outside portgroup keys
   364  	filtered := []types.DistributedVirtualPort{}
   365  
   366  	for _, p := range ports {
   367  		found := false
   368  		for _, pgk := range criteria.PortgroupKey {
   369  			if p.PortgroupKey == pgk {
   370  				found = true
   371  				break
   372  			}
   373  		}
   374  
   375  		if !found {
   376  			filtered = append(filtered, p)
   377  		}
   378  	}
   379  	return filtered
   380  }
   381  
   382  func (s *DistributedVirtualSwitch) filterDVPortsByPortKey(
   383  	ports []types.DistributedVirtualPort,
   384  	criteria *types.DistributedVirtualSwitchPortCriteria,
   385  ) []types.DistributedVirtualPort {
   386  	if len(criteria.PortKey) == 0 {
   387  		return ports
   388  	}
   389  
   390  	filtered := []types.DistributedVirtualPort{}
   391  
   392  	for _, p := range ports {
   393  		for _, pk := range criteria.PortKey {
   394  			if p.Key == pk {
   395  				filtered = append(filtered, p)
   396  				break
   397  			}
   398  		}
   399  	}
   400  
   401  	return filtered
   402  }
   403  
   404  func (s *DistributedVirtualSwitch) filterDVPortsByConnected(
   405  	ports []types.DistributedVirtualPort,
   406  	criteria *types.DistributedVirtualSwitchPortCriteria,
   407  ) []types.DistributedVirtualPort {
   408  	if criteria.Connected == nil {
   409  		return ports
   410  	}
   411  
   412  	filtered := []types.DistributedVirtualPort{}
   413  
   414  	for _, p := range ports {
   415  		connected := p.Connectee != nil
   416  		if connected == *criteria.Connected {
   417  			filtered = append(filtered, p)
   418  		}
   419  	}
   420  
   421  	return filtered
   422  }