コード例 #1
0
ファイル: network_path.go プロジェクト: att/tegu
/*
	Find a set of connected switches that can be used as a path beteeen
	hosts 1 and 2 (given by name; mac or ip).  Further, all links between from and the final switch must be able to
	support the additional capacity indicated by inc_cap during the time window between
	commence and conclude (unix timestamps).

	If the network is 'split' a host may appear to be attached to multiple switches; one with a real connection and
	the others are edge switches were we see an 'entry' point for the host from the portion of the network that we
	cannot visualise.  We must attempt to find a path between h1 using all of it's attached switches, and thus the
	return is an array of paths rather than a single path.


	h1nm and h2nm are likely going to be ip addresses as the main function translates any names that would have
	come in from the requestor.

	Extip is an external IP address that will need to be associated with the flow-mods and thus needs to be
	added to any path we generate.

	If mlag_paths is true, then we will find shortest path but add usage to all related mlag links in the path.

	If find_all is set, and mlog_paths is false, then we will suss out all possible paths between h1 and h2 and not
	just the shortest path.
*/
func (n *Network) find_paths(h1nm *string, h2nm *string, usr *string, commence int64, conclude int64, inc_cap int64, extip *string, ext_flag *string, find_all bool) (pcount int, path_list []*gizmos.Path, cap_trip bool) {
	var (
		path      *gizmos.Path
		ssw       *gizmos.Switch // starting switch
		h1        *gizmos.Host
		h2        *gizmos.Host
		lnk       *gizmos.Link
		plidx     int = 0
		swidx     int = 0 // index into host's switch list
		err       error
		lcap_trip bool = false // local capacity trip flag; indicates one or more paths blocked by capacity limits
	)

	if h1nm == nil || h2nm == nil {
		net_sheep.Baa(1, "IER:	find_paths: one/both names is/are nil  h1 nil=%v  h2 nil=%v", h1nm == nil, h2nm == nil)
		return 0, nil, false
	}

	h1 = n.hosts[*h1nm]
	if h1 == nil {
		path_list = nil
		net_sheep.Baa(1, "find-path: cannot find host(1) in network -- not reported by SDNC? %s", *h1nm)
		return
	}
	h1nm = h1.Get_mac() // must have the host's mac as our flowmods are at that level

	h2 = n.hosts[*h2nm] // do the same for the second host
	if h2 == nil {
		path_list = nil
		net_sheep.Baa(1, "find-path: cannot find host(2) in network -- not reported by the SDNC? %s", *h2nm)
		return
	}
	h2nm = h2.Get_mac()

	if h1nm == nil || h2nm == nil { // this has never happened, but be parinoid
		pcount = 0
		path_list = nil
		net_sheep.Baa(0, "CRI: find-path: internal error: either h1nm or h2nm was nil after get mac  [TGUNET005]")
		return
	}

	path_list = make([]*gizmos.Path, len(n.links)) // we cannot have more in our path than the number of links (needs to be changed as this isn't good in the long run)
	pcount = 0

	for { // we'll break after we've looked at all of the connection points for h1
		if plidx >= len(path_list) {
			net_sheep.Baa(0, "CRI: find-path: internal error -- path size > num of links.  [TGUNET006]")
			return
		}

		ssw, _ = h1.Get_switch_port(swidx) // get next switch that lists h1 as attached; we'll work 'out' from it toward h2
		if ssw == nil {                    // no more source switches which h1 thinks it's attached to
			pcount = plidx
			if pcount <= 0 || swidx == 0 {
				net_sheep.Baa(1, "find-path: early exit? no switch/port returned for h1 (%s) at index %d captrip=%v", *h1nm, swidx, lcap_trip)
			}
			path_list = path_list[0:pcount] // slice it down to size
			cap_trip = lcap_trip            // set with overall state
			return
		}

		fence := n.get_fence(usr)
		if ssw.Has_host(h1nm) && ssw.Has_host(h2nm) { // if both hosts are on the same switch, there's no path if they both have the same port (both external to our view)
			p1 := h1.Get_port(ssw)
			p2 := h2.Get_port(ssw)
			if p1 < 0 || p1 != p2 { // when ports differ we'll create/find the vlink between them	(in Tegu-lite port == -128 is legit and will dup)
				m1 := h1.Get_mac()
				m2 := h2.Get_mac()

				lnk = n.find_vlink(*(ssw.Get_id()), p1, p2, m1, m2)
				has_room := true // always room if relaxed mode, so start this way
				if n.relaxed {
					has_room = ssw.Has_capacity_out(commence, conclude, inc_cap, fence.Name, fence.Get_limit_max()) // ensure cap on all outbound links from switch
				}

				if has_room { // room for the reservation
					lnk.Add_lbp(*h1nm)
					net_sheep.Baa(1, "path[%d]: found target on same switch, different ports: %s  %d, %d", plidx, ssw.To_str(), h1.Get_port(ssw), h2.Get_port(ssw))
					path = gizmos.Mk_path(h1, h2) // empty path
					path.Set_bandwidth(inc_cap)
					path.Set_extip(extip, ext_flag)
					path.Add_switch(ssw)
					path.Add_link(lnk)

					path_list[plidx] = path
					plidx++
				} else {
					lcap_trip = true
					net_sheep.Baa(1, "path[%d]: hosts on same switch, virtual link cannot support bandwidth increase of %d", plidx, inc_cap)
				}
			} else { // debugging only
				net_sheep.Baa(2, "find-path: path[%d]: found target (%s) on same switch with same port: %s  %d, %d", plidx, *h2nm, ssw.To_str(), p1, p2)
				net_sheep.Baa(2, "find-path: host1-json= %s", h1.To_json())
				net_sheep.Baa(2, "find-path: host2-json= %s", h2.To_json())
			}
		} else { // usual case, two named hosts and hosts are on different switches
			net_sheep.Baa(1, "path[%d]: searching for path starting from switch: %s", plidx, ssw.To_str())

			for sname := range n.switches { // initialise the network for the walk
				n.switches[sname].Cost = 2147483647 // this should be large enough and allows cost to be int32
				n.switches[sname].Prev = nil
				n.switches[sname].Flags &= ^tegu.SWFL_VISITED
			}

			if n.relaxed {
				if !ssw.Has_capacity_out(commence, conclude, inc_cap, fence.Name, fence.Get_limit_max()) { // we do enforce capacity on ingress switch
					err = fmt.Errorf("ingress switch cannot support additional bandwidth (%d) or user max reached (%d)", inc_cap, fence.Get_limit_max())
					net_sheep.Baa(1, "%s", err)
					lcap_trip = true
				} else {
					dsw, _ := h2.Get_switch_port(swidx) // need the switch associated with the second host (dest switch)
					path, err = n.find_relaxed_path(ssw, h1, dsw, h2)
				}

				if err != nil {
					net_sheep.Baa(1, "find_paths: find_relaxed failed: %s", err)
				}
			} else {
				if find_all { // find all possible paths not just shortest
					path, err = n.find_all_paths(ssw, h1, h2, usr, commence, conclude, inc_cap, fence.Get_limit_max()) // find a 'scramble' path
					if err != nil {
						net_sheep.Baa(1, "find_paths: find_all failed: %s", err)
					}
				} else {
					path, cap_trip = n.find_shortest_path(ssw, h1, h2, usr, commence, conclude, inc_cap, fence.Get_limit_max())
					if cap_trip {
						lcap_trip = true
					}
				}
			}

			if path != nil {
				path.Set_extip(extip, ext_flag)
				path_list[plidx] = path
				plidx++
			}
		}

		swidx++
	}

	pcount = plidx // shouldn't get here, but safety first
	cap_trip = lcap_trip
	return
}