Example #1
0
// 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
}
Example #2
0
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
}
Example #3
0
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)
	}
}
Example #4
0
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
}
Example #5
0
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
	}
}