github.com/vmware/govmomi@v0.43.0/simulator/dvs.go (about)

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