// configureSharedPortGroups sets VCH static IP for networks that share a // portgroup with another network that has a configured static IP func (v *Validator) configureSharedPortGroups(input *data.Data, counts map[string]int, ips map[string][]data.NetworkConfig) error { defer trace.End(trace.Begin("")) // find other networks using same portgroup and copy the NetworkConfig to them for name, config := range ips { if len(config) != 1 { return fmt.Errorf("Failed to configure static IP for additional networks using port group %q", name) } log.Infof("Configuring static IP for additional networks using port group %q", name) if input.ClientNetwork.Name == name && input.ClientNetwork.Empty() { input.ClientNetwork = config[0] } if input.ExternalNetwork.Name == name && input.ExternalNetwork.Empty() { input.ExternalNetwork = config[0] } if input.ManagementNetwork.Name == name && input.ManagementNetwork.Empty() { input.ManagementNetwork = config[0] } } return nil }
func testCompute(v *Validator, input *data.Data, t *testing.T) *config.VirtualContainerHostConfigSpec { tests := []struct { path string vc bool hasErr bool }{ {"DC0_C0/Resources/validator", true, false}, {"DC0_C0/validator", true, false}, {"validator", true, true}, {"DC0_C0/test", true, true}, {"/DC0_C1/test", true, true}, {"/DC0_C1/test", true, true}, {"/DC0/host/DC0_C1/Resources/validator", true, true}, {"/DC1/host/DC0_C1/Resources/validator", true, true}, {"DC0_H0/Resources", true, false}, {"DC0_H0", true, false}, {"/DC0/host/DC0_C0/Resources/validator", true, false}, {"localhost.localdomain/Resources/validator", false, false}, {"validator", false, false}, {"test", false, true}, {"/ha-datacenter/host/localhost.localdomain/Resources/validator", false, false}, } conf := &config.VirtualContainerHostConfigSpec{} for _, test := range tests { if v.isVC && !test.vc { continue } if !v.isVC && test.vc { continue } t.Logf("%+v", test) input.ComputeResourcePath = test.path v.compute(v.Context, input, conf) v.ListIssues() if !test.hasErr { assert.Equal(t, 0, len(v.issues)) } else { assert.True(t, len(v.issues) > 0, "Should have errors") } v.issues = nil } return conf }
func (v *Validator) network(ctx context.Context, input *data.Data, conf *config.VirtualContainerHostConfigSpec) { defer trace.End(trace.Begin("")) // External net // external network is default for appliance err := v.addNetworkHelper(ctx, conf, input.ExternalNetworkName, "external", "external", true) if err != nil { v.NoteIssue(fmt.Errorf("Error checking network for --external-network: %s", err)) v.suggestNetwork("--external-network", true) } // Bridge network should be different than all other networks v.checkNetworkConflict(input.BridgeNetworkName, input.ExternalNetworkName, "external") // Client net if input.ClientNetworkName == "" { input.ClientNetworkName = input.ExternalNetworkName } err = v.addNetworkHelper(ctx, conf, input.ClientNetworkName, "client", "client", false) if err != nil { v.NoteIssue(fmt.Errorf("Error checking network for --client-network: %s", err)) v.suggestNetwork("--client-network", true) } v.checkNetworkConflict(input.BridgeNetworkName, input.ClientNetworkName, "client") // Management net if input.ManagementNetworkName == "" { input.ManagementNetworkName = input.ClientNetworkName } err = v.addNetworkHelper(ctx, conf, input.ManagementNetworkName, "", "management", false) if err != nil { v.NoteIssue(fmt.Errorf("Error checking network for --management-network: %s", err)) v.suggestNetwork("--management-network", true) } v.checkNetworkConflict(input.BridgeNetworkName, input.ManagementNetworkName, "management") // Bridge net - // vCenter: must exist and must be a DPG // ESX: doesn't need to exist - we will create with default value // // for now we're hardcoded to "bridge" for the container host name conf.BridgeNetwork = "bridge" endpointMoref, err := v.dpgHelper(ctx, input.BridgeNetworkName) var bridgeID, netMoid string if err != nil { bridgeID = "" netMoid = "" } else { bridgeID = endpointMoref.String() netMoid = endpointMoref.String() } checkBridgeVDS := true if err != nil { if _, ok := err.(*find.NotFoundError); !ok || v.IsVC() { v.NoteIssue(fmt.Errorf("An existing distributed port group must be specified for bridge network on vCenter: %s", err)) v.suggestNetwork("--bridge-network", false) checkBridgeVDS = false // prevent duplicate error output } // this allows the dispatcher to create the network with corresponding name // if BridgeNetworkName doesn't already exist then we set the ContainerNetwork // ID to the name, but leaving the NetworkEndpoint moref as "" netMoid = input.BridgeNetworkName } bridgeNet := &executor.NetworkEndpoint{ Common: executor.Common{ Name: "bridge", ID: bridgeID, }, Static: &net.IPNet{IP: net.IPv4zero}, // static but managed externally Network: executor.ContainerNetwork{ Common: executor.Common{ Name: "bridge", ID: netMoid, }, }, } // we need to have the bridge network identified as an available container network conf.AddContainerNetwork(&bridgeNet.Network) // we also need to have the appliance attached to the bridge network to allow // port forwarding conf.AddNetwork(bridgeNet) err = v.checkVDSMembership(ctx, endpointMoref, input.BridgeNetworkName) if err != nil && checkBridgeVDS { v.NoteIssue(fmt.Errorf("Unable to check hosts in vDS for %q: %s", input.BridgeNetworkName, err)) } // add mapped networks (from --container-network) // these should be a distributed port groups in vCenter suggestedMapped := false // only suggest mapped nets once for name, net := range input.MappedNetworks { checkMappedVDS := true // "bridge" is reserved if name == "bridge" { v.NoteIssue(fmt.Errorf("Cannot use reserved name \"bridge\" for container network")) continue } gw := input.MappedNetworksGateways[name] pools := input.MappedNetworksIPRanges[name] dns := input.MappedNetworksDNS[name] if len(pools) != 0 && ip.IsUnspecifiedSubnet(&gw) { v.NoteIssue(fmt.Errorf("IP range specified without gateway for container network %q", name)) continue } if !ip.IsUnspecifiedSubnet(&gw) && !ip.IsRoutableIP(gw.IP, &gw) { v.NoteIssue(fmt.Errorf("Gateway %s is not a routable address", gw.IP)) continue } err = nil // verify ip ranges are within subnet, // and don't overlap with each other for i, r := range pools { if !gw.Contains(r.FirstIP) || !gw.Contains(r.LastIP) { err = fmt.Errorf("IP range %q is not in subnet %q", r, gw) break } for _, r2 := range pools[i+1:] { if r2.Overlaps(r) { err = fmt.Errorf("Overlapping ip ranges: %q %q", r2, r) break } } if err != nil { break } } if err != nil { v.NoteIssue(err) continue } moref, err := v.dpgHelper(ctx, net) if err != nil { v.NoteIssue(fmt.Errorf("Error adding container network %q: %s", name, err)) checkMappedVDS = false if !suggestedMapped { v.suggestNetwork("--container-network", true) suggestedMapped = true } } mappedNet := &executor.ContainerNetwork{ Common: executor.Common{ Name: name, ID: moref.String(), }, Gateway: gw, Nameservers: dns, Pools: pools, } if input.BridgeNetworkName == net { v.NoteIssue(errors.Errorf("the bridge network must not be shared with another network role - %q also mapped as container network %q", input.BridgeNetworkName, name)) } err = v.checkVDSMembership(ctx, moref, net) if err != nil && checkMappedVDS { v.NoteIssue(fmt.Errorf("Unable to check hosts in vDS for %q: %s", net, err)) } conf.AddContainerNetwork(mappedNet) } }
func CreateNoDCCheck(ctx context.Context, input *data.Data) (*Validator, error) { defer trace.End(trace.Begin("")) var err error v := &Validator{} v.Context = ctx tURL := input.URL // default to https scheme if tURL.Scheme == "" { tURL.Scheme = "https" } // if they specified only an IP address the parser for some reason considers that a path if tURL.Host == "" { tURL.Host = tURL.Path tURL.Path = "" } if tURL.Scheme == "https" && input.Thumbprint == "" { var cert object.HostCertificateInfo if err = cert.FromURL(tURL, new(tls.Config)); err != nil { return nil, err } if cert.Err != nil { if !input.Force { // TODO: prompt user / check ./known_hosts log.Errorf("Failed to verify certificate for target=%s (thumbprint=%s)", tURL.Host, cert.ThumbprintSHA1) return nil, cert.Err } } input.Thumbprint = cert.ThumbprintSHA1 log.Debugf("Accepting host %q thumbprint %s", tURL.Host, input.Thumbprint) } sessionconfig := &session.Config{ Thumbprint: input.Thumbprint, Insecure: input.Force, } // if a datacenter was specified, set it v.DatacenterPath = tURL.Path if v.DatacenterPath != "" { sessionconfig.DatacenterPath = v.DatacenterPath // path needs to be stripped before we can use it as a service url tURL.Path = "" } sessionconfig.Service = tURL.String() v.Session = session.NewSession(sessionconfig) v.Session, err = v.Session.Connect(v.Context) if err != nil { return nil, err } // cached here to allow a modicum of testing while session is still in use. v.isVC = v.Session.IsVC() finder := find.NewFinder(v.Session.Client.Client, false) v.Session.Finder = finder v.Session.Populate(ctx) // only allow the datacenter to be specified in the taget url, if any pElems := strings.Split(v.DatacenterPath, "/") if len(pElems) > 2 { detail := "--target should only specify datacenter in the path (e.g. https://addr/datacenter) - specify cluster, resource pool, or folder with --compute-resource" log.Error(detail) v.suggestDatacenter() return nil, errors.New(detail) } return v, nil }
func testStorage(v *Validator, input *data.Data, conf *config.VirtualContainerHostConfigSpec, t *testing.T) { tests := []struct { image string volumes map[string]string hasErr bool expectImage string expectVolumes map[string]string }{ {"LocalDS_0", map[string]string{"volume1": "LocalDS_0/volumes/volume1", "volume2": "ds://LocalDS_0/volumes/volume2"}, false, "ds://LocalDS_0/test001", map[string]string{"volume1": "ds://LocalDS_0/volumes/volume1", "volume2": "ds://LocalDS_0/volumes/volume2"}}, {"LocalDS_0/images", map[string]string{"volume1": "LocalDS_0/volumes/volume1", "volume2": "ds://LocalDS_0/volumes/volume2"}, false, "ds://LocalDS_0/images", map[string]string{"volume1": "ds://LocalDS_0/volumes/volume1", "volume2": "ds://LocalDS_0/volumes/volume2"}}, {"ds://LocalDS_0/images", map[string]string{"volume1": "LocalDS_0/volumes/volume1", "volume2": "ds://LocalDS_0/volumes/volume2"}, false, "ds://LocalDS_0/images", map[string]string{"volume1": "ds://LocalDS_0/volumes/volume1", "volume2": "ds://LocalDS_0/volumes/volume2"}}, {"ds://LocalDS_0/images/xyz", map[string]string{"volume1": "LocalDS_0/volumes/volume1", "volume2": "ds://LocalDS_0/volumes/volume2"}, false, "ds://LocalDS_0/images/xyz", map[string]string{"volume1": "ds://LocalDS_0/volumes/volume1", "volume2": "ds://LocalDS_0/volumes/volume2"}}, {"ds://😗", map[string]string{"volume1": "😗/volumes/volume1", "volume2": "ds://😗/volumes/volume2"}, true, "ds://😗/test001", nil}, {"ds://LocalDS_0", map[string]string{"volume1": "LocalDS_1/volumes/volume1", "volume2": "ds://LocalDS_1/volumes/volume2"}, true, "ds://LocalDS_0/test001", nil}, {"LocalDS_0", map[string]string{"volume1": "LocalDS_1/volumes/volume1", "volume2": "ds://LocalDS_1/volumes/volume2"}, true, "ds://LocalDS_0/test001", nil}, {"LocalDS_0", map[string]string{"volume1": "LocalDS_1/volumes/volume1", "volume2": "ds://LocalDS_1/volumes/volume2"}, true, "ds://LocalDS_0/test001", nil}, {"", map[string]string{"volume1": "", "volume2": "ds://"}, true, "", nil}, {"ds://", map[string]string{"volume1": "", "volume2": "ds://"}, true, "", nil}, } for _, test := range tests { t.Logf("%+v", test) input.ImageDatastorePath = test.image input.VolumeLocations = test.volumes v.storage(v.Context, input, conf) v.ListIssues() if !test.hasErr { assert.Equal(t, 0, len(v.issues)) assert.Equal(t, test.expectImage, conf.ImageStores[0].String()) conf.ImageStores = conf.ImageStores[1:] for key, volume := range conf.VolumeLocations { assert.Equal(t, test.expectVolumes[key], volume.String()) } } else { assert.True(t, len(v.issues) > 0, "Should have errors") } v.issues = nil } }