Example #1
0
/*
	Formats v1 and v2 in the {n} format for adding to a host representation which is
	now   token/project/vm:port{vlan}.
*/
func (p *Pledge_bw) bw_vlan2string() (v1 string, v2 string) {
	v1 = ""
	v2 = ""
	if p.vlan1 != nil && clike.Atoi(*p.vlan1) > 0 {
		v1 = "{" + *p.vlan1 + "}"
	}
	if p.vlan2 != nil && clike.Atoi(*p.vlan2) > 0 {
		v2 = "{" + *p.vlan2 + "}"
	}

	return v1, v2
}
Example #2
0
/*
	Returns {ID} if the vlan is defined and > 0. Returns an empty string if not
	defined, or invalid value.
*/
func (p *Pledge_pass) vlan2string() (vlan string) {
	vlan = ""
	if p.vlan != nil && clike.Atoi(*p.vlan) > 0 {
		vlan = "{" + *p.vlan + "}"
	}

	return vlan
}
Example #3
0
/*
	Formats vlan in the {n} format for adding to a host representation which is
	now   token/project/vm:port{vlan}. If vlan is < 0 then the empty string is returned.
*/
func (p *Pledge_bwow) vlan2string() (v string) {
	if p != nil {
		v = ""
		if p.src_vlan != nil && clike.Atoi(*p.src_vlan) > 0 {
			v = "{" + *p.src_vlan + "}"
		}
	}

	return ""
}
Example #4
0
/*
	Sets up the global variables needed by the whole package. This should be invoked by the
	main tegu function (main/tegu.go).

	CAUTION:  this is not implemented as an init() function as we must pass information from the
			main to here.
*/
func Initialise(cfg_fname *string, ver *string, nwch chan *ipc.Chmsg, rmch chan *ipc.Chmsg, rmluch chan *ipc.Chmsg, osifch chan *ipc.Chmsg, fqch chan *ipc.Chmsg, amch chan *ipc.Chmsg) (err error) {
	err = nil

	def_log_dir := "."
	log_dir := &empty_str

	nw_ch = nwch
	rmgr_ch = rmch
	rmgrlu_ch = rmluch
	osif_ch = osifch
	fq_ch = fqch
	am_ch = amch

	if ver != nil {
		version = *ver
	}

	tegu_sheep = bleater.Mk_bleater(1, os.Stderr) // the main (parent) bleater used by libraries and as master 'volume' control
	tegu_sheep.Set_prefix("tegu")

	pid = os.Getpid() // used to keep reservation names unique across invocations

	tklr = ipc.Mk_tickler(30)                   // shouldn't need more than 30 different tickle spots
	tklr.Add_spot(2, rmgr_ch, REQ_NOOP, nil, 1) // a quick burst tickle to prevent a long block if the first goroutine to schedule a tickle schedules a long wait

	if cfg_fname != nil {
		cfg_data, err = config.Parse2strs(nil, *cfg_fname) // capture config data as strings -- referenced as cfg_data["sect"]["key"]
		if err != nil {
			err = fmt.Errorf("unable to parse config file %s: %s", *cfg_fname, err)
			return
		}

		if p := cfg_data["default"]["shell"]; p != nil {
			shell_cmd = *p
		}
		if p := cfg_data["default"]["verbose"]; p != nil {
			tegu_sheep.Set_level(uint(clike.Atoi(*p)))
		}
		if log_dir = cfg_data["default"]["log_dir"]; log_dir == nil {
			log_dir = &def_log_dir
		}
	} else {
		cfg_data = nil
	}

	tegu_sheep.Add_child(gizmos.Get_sheep()) // since we don't directly initialise the gizmo environment we ask for its sheep
	if *log_dir != "stderr" {                // if overriden in config
		lfn := tegu_sheep.Mk_logfile_nm(log_dir, 86400)
		tegu_sheep.Baa(1, "switching to log file: %s", *lfn)
		tegu_sheep.Append_target(*lfn, false)      // switch bleaters to the log file rather than stderr
		go tegu_sheep.Sheep_herder(log_dir, 86400) // start the function that will roll the log now and again
	}

	return
}
Example #5
0
/*
	Accepts a string of space separated dscp values and returns a string with the values
	appropriately shifted so that they can be used by the agent in a flow-mod command.  E.g.
	a dscp value of 40 is shifted to 160.
*/
func shift_values(list string) (new_list string) {
	new_list = ""
	sep := ""
	toks := strings.Split(list, " ")

	for i := range toks {
		n := clike.Atoi(toks[i])
		new_list += fmt.Sprintf("%s%d", sep, n<<2)
		sep = " "
	}

	return
}
Example #6
0
File: res_mgr.go Project: att/tegu
/*
	Given a name, get host info (IP, mac, switch-id, switch-port) from network.
*/
func get_hostinfo( name *string ) ( *string, *string, *string, int ) {

	if name != nil  &&  *name != "" {
		ch := make( chan *ipc.Chmsg );
		req := ipc.Mk_chmsg( )
		req.Send_req( nw_ch, ch, REQ_HOSTINFO, name, nil )		// get host info string (mac, ip, switch)
		req = <- ch
		if req.State == nil {
			htoks := strings.Split( req.Response_data.( string ), "," )					// results are: ip, mac, switch-id, switch-port; all strings
			return &htoks[0], &htoks[1], &htoks[2], clike.Atoi( htoks[3] )
		} else {
			rm_sheep.Baa( 1, "get_hostinfo: error from network mgr: %s", req.State )
		}
	}

	rm_sheep.Baa( 1, "get_hostinfo: no name provided" )
	return nil, nil, nil, 0
}
Example #7
0
File: res_mgr.go Project: att/tegu
/*
	Set the user link capacity and forward it on to the network manager. We expect this
	to be a request from the far side (user/admin) or read from the chkpt file so
	the value is passed as a string (which is also what network wants too.
*/
func (inv *Inventory) add_ulcap( name *string, sval *string ) {
	val := clike.Atoi( *sval )

	pdata := make( []*string, 2 )		// parameters for message to network
	pdata[0] = name
	pdata[1] = sval

	if val >= 0 && val < 101 {
		rm_sheep.Baa( 2, "adding user cap: %s %d", *name, val )
		inv.ulcap_cache[*name] = val

		req := ipc.Mk_chmsg( )
		req.Send_req( nw_ch, nil, REQ_SETULCAP, pdata, nil ) 				// push into the network environment

	} else {
		if val == -1 {
			delete( inv.ulcap_cache, *name )
			req := ipc.Mk_chmsg( )
			req.Send_req( nw_ch, nil, REQ_SETULCAP, pdata, nil ) 				// push into the network environment
		} else {
			rm_sheep.Baa( 1, "user link capacity not set %d is out of range (1-100)", val )
		}
	}
}
Example #8
0
/*
	Given a thing (field value in a struct), set the thing with the element in the map (key)
	the map value to the proper type.
*/
func set_value(thing reflect.Value, kind reflect.Kind, key string, tag_id string, pfx string, annon bool, m map[string]string) {

	if !thing.CanAddr() { // prevent stack dump
		return
	}

	switch kind {
	default:
		fmt.Fprintf(os.Stderr, "transform.mts: tagged sturct member cannot be converted from map: tag=%s kind=%v\n", key, thing.Kind())

	case reflect.String:
		thing.SetString(m[key])

	case reflect.Ptr:
		p := thing.Elem() // get the pointer value; allows us to suss the type
		if !p.IsValid() { // ptr is nill in the struct so we must allocate a pointer to 0 so it can be changed below
			thing.Set(reflect.New(thing.Type().Elem()))
			p = thing.Elem()
		}
		switch p.Kind() {
		case reflect.String:
			s := m[key] // copy it and then point to the copy
			thing.Set(reflect.ValueOf(&s))

		case reflect.Int:
			i := clike.Atoi(m[key]) // convert to integer and then point at the value
			thing.Set(reflect.ValueOf(&i))

		case reflect.Int64:
			i := clike.Atoi64(m[key])
			thing.Set(reflect.ValueOf(&i))

		case reflect.Int32:
			i := clike.Atoi32(m[key])
			thing.Set(reflect.ValueOf(&i))

		case reflect.Int16:
			i := clike.Atoi16(m[key])
			thing.Set(reflect.ValueOf(&i))

		case reflect.Int8:
			i := int8(clike.Atoi16(m[key]))
			thing.Set(reflect.ValueOf(&i))

		case reflect.Uint:
			ui := clike.Atou(m[key])
			thing.Set(reflect.ValueOf(&ui))

		case reflect.Uint64:
			ui := clike.Atou64(m[key])
			thing.Set(reflect.ValueOf(&ui))

		case reflect.Uint32:
			ui := clike.Atou32(m[key])
			thing.Set(reflect.ValueOf(&ui))

		case reflect.Uint16:
			ui := clike.Atou16(m[key])
			thing.Set(reflect.ValueOf(&ui))

		case reflect.Uint8:
			ui := uint8(clike.Atou16(m[key]))
			thing.Set(reflect.ValueOf(&ui))

		case reflect.Float64:
			fv := clike.Atof(m[key])
			thing.Set(reflect.ValueOf(&fv))

		case reflect.Float32:
			fv := float32(clike.Atof(m[key]))
			thing.Set(reflect.ValueOf(&fv))

		case reflect.Bool:
			b := m[key] == "true" || m[key] == "True" || m[key] == "TRUE"
			thing.Set(reflect.ValueOf(&b))

		case reflect.Struct:
			map_to_struct(m, p, p.Type(), tag_id, pfx) // recurse to process
		}

	case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8:
		thing.SetInt(clike.Atoi64(m[key]))

	case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8:
		thing.SetUint(uint64(clike.Atoi64(m[key])))

	case reflect.Float64, reflect.Float32:
		thing.SetFloat(clike.Atof(m[key]))

	case reflect.Bool:
		thing.SetBool(m[key] == "true")

	case reflect.Map:
		new_map := reflect.MakeMap(thing.Type()) // create the map
		thing.Set(new_map)                       // put it in the struct

		idx := key + "/" // now populate the map
		ilen := len(idx)
		for k, _ := range m { // we could keep a separate list of keys, but for now this should do
			if strings.HasPrefix(k, key) {
				tokens := strings.Split(k[ilen:], "/")
				map_key := reflect.ValueOf(tokens[0]) // map key is everything past the tag to the next slant
				map_ele_type := new_map.Type().Elem() // the type of the element that the map references

				mthing := reflect.New(map_ele_type).Elem() // new returns pointer, so dereference with Elem() (value not type!)

				set_value(mthing, mthing.Kind(), idx+tokens[0], tag_id, idx+tokens[0]+"/", false, m) // put the value into the map thing
				new_map.SetMapIndex(map_key, mthing)                                                 // put it into the map (order IS important; add to map after recursion)
				//fmt.Fprintf( os.Stderr, "saving: %s  thing-type=%s mthing=%s\n", map_key, thing.Type(), mthing )
			}
		}

	case reflect.Slice:
		c := clike.Atoi(m[key+".cap"])
		l := clike.Atoi(m[key+".len"])

		thing.Set(reflect.MakeSlice(thing.Type(), l, c)) // create a new slice with the same len/cap that it had
		for j := 0; j < l; j++ {
			idx := fmt.Sprintf("%s/%d", key, j)
			set_value(thing.Index(j), thing.Type().Elem().Kind(), idx, tag_id, idx+"/", false, m) // populate each value of the slice up to len
		}

	case reflect.Struct:
		if annon {
			map_to_struct(m, thing, thing.Type(), tag_id, key) // anon structs share namespace, so prefix is the same
		} else {
			map_to_struct(m, thing, thing.Type(), tag_id, pfx) // dive to get the substruct adding a level to the prefix
		}

	}

}
Example #9
0
File: res_mgr.go Project: att/tegu
/*
	Executes as a goroutine to drive the reservation manager portion of tegu.
*/
func Res_manager( my_chan chan *ipc.Chmsg, cookie *string ) {

	var (
		inv	*Inventory
		msg	*ipc.Chmsg
		ckptd	string
		last_qcheck	int64 = 0			// time that the last queue check was made to set window
		last_chkpt	int64 = 0			// time that the last checkpoint was written
		retry_chkpt bool = false		// checkpoint needs to be retried because of a timing issue
		queue_gen_type = REQ_GEN_EPQMAP
		alt_table = DEF_ALT_TABLE		// table number where meta marking happens
		all_sys_up	bool = false;		// set when we receive the all_up message; some functions (chkpt) must wait for this
		hto_limit 	int = 3600 * 18		// OVS has a size limit to the hard timeout value, this caps it just under the OVS limit
		res_refresh	int64 = 0			// next time when we must force all reservations to refresh flow-mods (hto_limit nonzero)
		rr_rate		int = 3600			// refresh rate (1 hour)
		favour_v6 bool = true			// favour ipv6 addresses if a host has both defined.
	)

	super_cookie = cookie				// global for all methods

	rm_sheep = bleater.Mk_bleater( 0, os.Stderr )		// allocate our bleater and attach it to the master
	rm_sheep.Set_prefix( "res_mgr" )
	tegu_sheep.Add_child( rm_sheep )					// we become a child so that if the master vol is adjusted we'll react too

	p := cfg_data["default"]["queue_type"]				// lives in default b/c used by fq-mgr too
	if p != nil {
		if *p == "endpoint" {
			queue_gen_type = REQ_GEN_EPQMAP
		} else {
			queue_gen_type = REQ_GEN_QMAP
		}
	}

	p = cfg_data["default"]["alttable"]				// alt table for meta marking
	if p != nil {
		alt_table = clike.Atoi( *p )
	}

	p = cfg_data["default"]["favour_ipv6"]
	if p != nil {
		favour_v6 = *p == "true"
	}

	if cfg_data["resmgr"] != nil {
		cdp := cfg_data["resmgr"]["chkpt_dir"]
		if cdp == nil {
			ckptd = "/var/lib/tegu/resmgr"							// default directory and prefix
		} else {
			ckptd = *cdp + "/resmgr"							// add prefix to directory in config
		}

		p = cfg_data["resmgr"]["verbose"]
		if p != nil {
			rm_sheep.Set_level(  uint( clike.Atoi( *p ) ) )
		}

		/*
		p = cfg_data["resmgr"]["set_vlan"]
		if p != nil {
			set_vlan = *p == "true"
		}
		*/

		p = cfg_data["resmgr"]["super_cookie"]
		if p != nil {
			super_cookie = p
			rm_sheep.Baa( 1, "super-cookie was set from config file" )
		}

		p = cfg_data["resmgr"]["hto_limit"]					// if OVS or whatever has a max timeout we can ensure it's not surpassed
		if p != nil {
			hto_limit = clike.Atoi( *p )
		}

		p = cfg_data["resmgr"]["res_refresh"]				// rate that reservations are refreshed if hto_limit is non-zero
		if p != nil {
			rr_rate = clike.Atoi( *p )
			if rr_rate < 900 {
				if rr_rate < 120 {
					rm_sheep.Baa( 0, "NOTICE: reservation refresh rate in config is insanely low (%ds) and was changed to 1800s", rr_rate )
					rr_rate = 1800
				} else {
					rm_sheep.Baa( 0, "NOTICE: reservation refresh rate in config is too low: %ds", rr_rate )
				}
			}
		}
	}

	send_meta_counter := 200;										// send meta f-mods only now and again
	rm_sheep.Baa( 1, "ovs table number %d used for metadata marking", alt_table )

	res_refresh = time.Now().Unix() + int64( rr_rate )				// set first refresh in an hour (ignored if hto_limit not set
	inv = Mk_inventory( )
	inv.chkpt = chkpt.Mk_chkpt( ckptd, 10, 90 )

	last_qcheck = time.Now().Unix()

	tkl_ch := make( chan *ipc.Chmsg, 5 )								// special, short buffer, channel for tickles allows 5 to queue before blocking sender
	tklr.Add_spot( 2, tkl_ch, REQ_PUSH, nil, ipc.FOREVER )				// push reservations to agent just before they go live
	tklr.Add_spot( 1, tkl_ch, REQ_SETQUEUES, nil, ipc.FOREVER )			// drives us to see if queues need to be adjusted
	tklr.Add_spot( 5, tkl_ch, REQ_RTRY_CHKPT, nil, ipc.FOREVER )		// ensures that we retried any missed checkpoints
	tklr.Add_spot( 60, tkl_ch, REQ_VET_RETRY, nil, ipc.FOREVER )		// run the retry queue if it has size

	go rm_lookup( rmgrlu_ch, inv )

	rm_sheep.Baa( 3, "res_mgr is running  %x", my_chan )
	for {
		select {									// select next ready message on either channel
			case msg = <- tkl_ch:					// msg available on tickle channel
				msg.State = nil						// nil state is OK, no error
				my_chan <- msg;						// just pass it through; tkl_ch has a small buffer (blocks quickly) and this prevents filling the main queue w/ tickles if we get busy	

			case msg = <- my_chan:					// process message from the main channel
				rm_sheep.Baa( 3, "processing message: %d", msg.Msg_type )
				switch msg.Msg_type {
					case REQ_NOOP:			// just ignore

					case REQ_ADD:
						msg.State = inv.Add_res( msg.Req_data )			// add will determine the pledge type and do the right thing
						msg.Response_data = nil


					case REQ_ALLUP:			// signals that all initialisation is complete (chkpting etc. can go)
						all_sys_up = true
						// periodic checkpointing turned off with the introduction of tegu_ha
						//tklr.Add_spot( 180, my_chan, REQ_CHKPT, nil, ipc.FOREVER )		// tickle spot to drive us every 180 seconds to checkpoint

					case REQ_RTRY_CHKPT:									// called to attempt to send a queued checkpoint request
						if all_sys_up {
							if retry_chkpt {
								rm_sheep.Baa( 3, "invoking checkpoint (retry)" )
								retry_chkpt, last_chkpt = inv.write_chkpt( last_chkpt )
							}
						}

					case REQ_CHKPT:											// external thread has requested checkpoint
						if all_sys_up {
							rm_sheep.Baa( 3, "invoking checkpoint" )
							retry_chkpt, last_chkpt = inv.write_chkpt( last_chkpt )
						}

					case REQ_DEL:											// user initiated delete -- requires cookie
						data := msg.Req_data.( []*string )					// assume pointers to name and cookie
						if data[0] != nil  &&  *data[0] == "all" {
							inv.Del_all_res( data[1] )
							msg.State = nil
						} else {
							msg.State = inv.Del_res( data[0], data[1] )
						}

						inv.push_reservations( my_chan, alt_table, int64( hto_limit ), favour_v6 )			// must force a push to push augmented (shortened) reservations
						msg.Response_data = nil

					case REQ_DUPCHECK:
						if msg.Req_data != nil {
							msg.Response_data, msg.State = inv.dup_check(  msg.Req_data.( *gizmos.Pledge ) )
						}

					case REQ_GET:											// user initiated get -- requires cookie
						data := msg.Req_data.( []*string )					// assume pointers to name and cookie
						msg.Response_data, msg.State = inv.Get_res( data[0], data[1] )

					case REQ_LIST:											// list reservations	(for a client)
						msg.Response_data, msg.State = inv.res2json( )

					case REQ_LOAD:								// load from a checkpoint file
						data := msg.Req_data.( *string )		// assume pointers to name and cookie
						msg.State = inv.load_chkpt( data )
						msg.Response_data = nil
						rm_sheep.Baa( 1, "checkpoint file loaded" )

					case REQ_PAUSE:
						msg.State = nil							// right now this cannot fail in ways we know about
						msg.Response_data = ""
						inv.pause_on()
						res_refresh = 0;						// must force a push of everything on next push tickle
						rm_sheep.Baa( 1, "pausing..." )

					case REQ_RESUME:
						msg.State = nil							// right now this cannot fail in ways we know about
						msg.Response_data = ""
						res_refresh = 0;						// must force a push of everything on next push tickle
						inv.pause_off()

					case REQ_SETQUEUES:							// driven about every second to reset the queues if a reservation state has changed
						now := time.Now().Unix()
						if now > last_qcheck  &&  inv.any_concluded( now - last_qcheck ) || inv.any_commencing( now - last_qcheck, 0 ) {
							rm_sheep.Baa( 1, "channel states: rm=%d rmlu=%d fq=%d net=%d agent=%d", len( rmgr_ch ), len( rmgrlu_ch ), len( fq_ch ), len( nw_ch ), len( am_ch ) )
							rm_sheep.Baa( 1, "reservation state change detected, requesting queue map from net-mgr" )
							tmsg := ipc.Mk_chmsg( )
							tmsg.Send_req( nw_ch, my_chan, queue_gen_type, time.Now().Unix(), nil )		// get a queue map; when it arrives we'll push to fqmgr and trigger flow-mod push
						}
						last_qcheck = now

					case REQ_PUSH:								// driven every few seconds to check for need to refresh because of switch max timeout setting
						if hto_limit > 0 {						// if reservation flow-mods are capped with a hard timeout limit
							now := time.Now().Unix()
							if now > res_refresh {
								rm_sheep.Baa( 2, "refreshing all reservations" )
								inv.reset_push()							// reset pushed flag on all reservations to cause active ones to be pushed again
								res_refresh = now + int64( rr_rate )		// push everything again in an hour

								inv.push_reservations( my_chan, alt_table, int64( hto_limit ), favour_v6 )			// force a push of all
							}
						}


					case REQ_PLEDGE_LIST:						// generate a list of pledges that are related to the given VM
						msg.Response_data, msg.State = inv.pledge_list(  msg.Req_data.( *string ) )

					case REQ_SETULCAP:							// user link capacity; expect array of two string pointers (name and value)
						data := msg.Req_data.( []*string )
						inv.add_ulcap( data[0], data[1] )
						retry_chkpt, last_chkpt = inv.write_chkpt( last_chkpt )

					// CAUTION: the requests below come back as asynch responses rather than as initial message
					case REQ_IE_RESERVE:						// an IE reservation failed
						msg.Response_ch = nil					// immediately disable to prevent loop
						inv.failed_push( msg )					// suss out the pledge and mark it unpushed

					case REQ_GEN_QMAP:							// response caries the queue map that now should be sent to fq-mgr to drive a queue update
						fallthrough

					case REQ_GEN_EPQMAP:
						rm_sheep.Baa( 1, "received queue map from network manager" )

						qlist := msg.Response_data.( []string )							// get the qulist map for our use first
						if send_meta_counter >= 200 {
							send_meta_fmods( qlist, alt_table )								// push meta rules
							send_meta_counter = 0
						} else {
							send_meta_counter++
						}

						msg.Response_ch = nil											// immediately disable to prevent loop
						fq_data := make( []interface{}, 1 )
						fq_data[FQ_QLIST] = msg.Response_data
						tmsg := ipc.Mk_chmsg( )
						tmsg.Send_req( fq_ch, nil, REQ_SETQUEUES, fq_data, nil )		// send the queue list to fq manager to deal with

						inv.push_reservations( my_chan, alt_table, int64( hto_limit ), favour_v6 )			// now safe to push reservations if any activated

					case REQ_VET_RETRY:
						if inv != nil && len( inv.retry ) > 0 {
							inv.vet_retries( )
						}

					case REQ_YANK_RES:										// yank a reservation from the inventory returning the pledge and allowing flow-mods to purge
						if msg.Response_ch != nil {
							msg.Response_data, msg.State = inv.yank_res( msg.Req_data.( *string ) )
						}

					/* deprecated -- moved to rm_lookup
					case REQ_GET_MIRRORS:									// user initiated get list of mirrors
						t := inv.Get_mirrorlist()
						msg.Response_data = &t;
					*/

					default:
						rm_sheep.Baa( 0, "WRN: res_mgr: unknown message: %d [TGURMG001]", msg.Msg_type )
						msg.Response_data = nil
						msg.State = fmt.Errorf( "res_mgr: unknown message (%d)", msg.Msg_type )
						msg.Response_ch = nil				// we don't respond to these.
				}	// end main channel case

		}		// end select

		rm_sheep.Baa( 3, "processing message complete: %d", msg.Msg_type )
		if msg.Response_ch != nil {			// if a response channel was provided
			msg.Response_ch <- msg			// send our result back to the requester
		}
	}
}
Example #10
0
File: osif.go Project: krjoshi/tegu
/*
	executed as a goroutine this loops waiting for messages from the tickler and takes
	action based on what is needed.
*/
func Osif_mgr(my_chan chan *ipc.Chmsg) {

	var (
		msg           *ipc.Chmsg
		os_list       string                    = ""
		os_sects      []string                       // sections in the config file
		os_refs       map[string]*ostack.Ostack      // creds for each project we need to request info from
		os_projects   map[string]*osif_project       // list of project info (maps)
		os_admin      *ostack.Ostack                 // admin creds
		refresh_delay int                       = 15 // config file can override
		id2pname      map[string]*string             // project id/name translation maps
		pname2id      map[string]*string
		req_token     bool    = false // if set to true in config file the token _must_ be present when called to validate
		def_passwd    *string         // defaults and what we assume are the admin creds
		def_usr       *string
		def_url       *string
		def_project   *string
		def_region    *string
	)

	osif_sheep = bleater.Mk_bleater(0, os.Stderr) // allocate our bleater and attach it to the master
	osif_sheep.Set_prefix("osif_mgr")
	tegu_sheep.Add_child(osif_sheep) // we become a child so that if the master vol is adjusted we'll react too

	//ostack.Set_debugging( 0 );

	// ---- pick up configuration file things of interest --------------------------

	if cfg_data["osif"] != nil { // cannot imagine that this section is missing, but don't fail if it is
		def_passwd = cfg_data["osif"]["passwd"] // defaults applied if non-section given in list, or info omitted from the section
		def_usr = cfg_data["osif"]["usr"]
		def_url = cfg_data["osif"]["url"]
		def_project = cfg_data["osif"]["project"]

		p := cfg_data["osif"]["refresh"]
		if p != nil {
			refresh_delay = clike.Atoi(*p)
			if refresh_delay < 15 {
				osif_sheep.Baa(1, "resresh was too small (%ds), setting to 15", refresh_delay)
				refresh_delay = 15
			}
		}

		p = cfg_data["osif"]["debug"]
		if p != nil {
			v := clike.Atoi(*p)
			if v > -5 {
				ostack.Set_debugging(v)
			}
		}

		p = cfg_data["osif"]["region"]
		if p != nil {
			def_region = p
		}

		p = cfg_data["osif"]["ostack_list"] // preferred placement in osif section
		if p == nil {
			p = cfg_data["default"]["ostack_list"] // originally in default, so backwards compatable
		}
		if p != nil {
			os_list = *p
		}

		p = cfg_data["osif"]["require_token"]
		if p != nil && *p == "true" {
			req_token = true
		}

		p = cfg_data["osif"]["verbose"]
		if p != nil {
			osif_sheep.Set_level(uint(clike.Atoi(*p)))
		}
	}

	if os_list == " " || os_list == "" || os_list == "off" {
		osif_sheep.Baa(0, "osif disabled: no openstack list (ostack_list) defined in configuration file or setting is 'off'")
	} else {
		// TODO -- investigate getting id2pname maps from each specific set of creds defined if an overarching admin name is not given

		os_admin = get_admin_creds(def_url, def_usr, def_passwd, def_project, def_region) // this will block until we authenticate
		if os_admin != nil {
			osif_sheep.Baa(1, "admin creds generated, mapping tenants")
			pname2id, id2pname, _ = os_admin.Map_tenants() // list only projects we belong to
			for k, v := range pname2id {
				osif_sheep.Baa(1, "project known: %s %s", k, *v) // useful to see in log what projects we can see
			}
		} else {
			id2pname = make(map[string]*string) // empty maps and we'll never generate a translation from project name to tenant ID since there are no default admin creds
			pname2id = make(map[string]*string)
			if def_project != nil {
				osif_sheep.Baa(0, "WRN: unable to use admin information (%s, proj=%s, reg=%s) to authorise with openstack  [TGUOSI009]", def_usr, def_project, def_region)
			} else {
				osif_sheep.Baa(0, "WRN: unable to use admin information (%s, proj=no-project, reg=%s) to authorise with openstack  [TGUOSI009]", def_usr, def_region) // YES msg ids are duplicated here
			}
		}

		if os_list == "all" {
			os_refs, _ = refresh_creds(os_admin, os_refs, id2pname) // for each project in id2pname get current ostack struct (auth)
			for k := range os_refs {
				osif_sheep.Baa(1, "inital os_list member: %s", k)
			}
		} else {
			if strings.Index(os_list, ",") > 0 {
				os_sects = strings.Split(os_list, ",")
			} else {
				os_sects = strings.Split(os_list, " ")
			}

			os_refs = make(map[string]*ostack.Ostack, len(os_sects)*2) // length is a guideline, not a hard value
			for i := 0; i < len(os_sects); i++ {
				osif_sheep.Baa(1, "creating openstack interface for %s", os_sects[i])
				url := def_url
				usr := def_usr
				passwd := def_passwd
				project := &os_sects[i]

				if cfg_data[os_sects[i]] != nil { // section name supplied, override defaults with information from the section
					if cfg_data[os_sects[i]]["url"] != nil {
						url = cfg_data[os_sects[i]]["url"]
					}
					if cfg_data[os_sects[i]]["usr"] != nil {
						usr = cfg_data[os_sects[i]]["usr"]
					}
					if cfg_data[os_sects[i]]["passwd"] != nil {
						passwd = cfg_data[os_sects[i]]["passwd"]
					}
					if cfg_data[os_sects[i]]["project"] != nil {
						project = cfg_data[os_sects[i]]["project"]
					}
				}
				os_refs[*project] = ostack.Mk_ostack(url, usr, passwd, project)
				os_refs["_ref_"] = os_refs[*project] // a quick access reference when any one will do
			}
		}

		os_projects = make(map[string]*osif_project)
		add2projects(os_projects, os_refs, pname2id, 0) // add refernces to the projects list
	}

	// ---------------- end config parsing ----------------------------------------

	if os_admin != nil { // only if we are using openstack as a database
		//tklr.Add_spot( 3, my_chan, REQ_GENCREDS, nil, 1 )						// add tickle spot to drive us once in 3s and then another to drive us based on config refresh rate
		tklr.Add_spot(int64(180), my_chan, REQ_GENCREDS, nil, ipc.FOREVER)
	}

	osif_sheep.Baa(2, "osif manager is running  %x", my_chan)
	for {
		msg = <-my_chan // wait for next message from tickler
		msg.State = nil // default to all OK

		osif_sheep.Baa(3, "processing request: %d", msg.Msg_type)
		switch msg.Msg_type {
		case REQ_GENMAPS: // driven by tickler
			// deprecated with switch to lazy update

		case REQ_GENCREDS: // driven by tickler now and then
			if os_admin != nil {
				os_refs, pname2id, id2pname = update_project(os_admin, os_refs, os_projects, pname2id, id2pname, os_list == "all")
			}

			/* ---- before lite ----
			case REQ_VM2IP:														// driven by tickler; gen a new vm translation map and push to net mgr
				m := mapvm2ip( os_refs )
				if m != nil {
					count := 0;
					msg := ipc.Mk_chmsg( )
					msg.Send_req( nw_ch, nil, REQ_VM2IP, m, nil )					// send new map to network as it is managed there
					osif_sheep.Baa( 2, "VM2IP mapping updated from openstack" )
					for k, v := range m {
						osif_sheep.Baa( 3, "VM mapped: %s ==> %s", k, *v )
						count++;
					}
					osif_sheep.Baa( 2, "mapped %d VM names/IDs from openstack (verbose 3 for debug list)", count )
				}
			*/

		case REQ_IP2MACMAP: // generate an ip to mac map and send to those who need it (fq_mgr at this point)
			freq := ipc.Mk_chmsg() // need a new request to pass to fq_mgr
			data, err := get_ip2mac(os_projects)
			if err == nil {
				osif_sheep.Baa(2, "sending ip2mac map to fq_mgr")
				freq.Send_req(fq_ch, nil, REQ_IP2MACMAP, data, nil) // request data forward
				msg.State = nil                                     // response ok back to requestor
			} else {
				msg.State = err // error goes back to requesting process
			}

		case REQ_CHOSTLIST:
			if msg.Response_ch != nil { // no sense going off to ostack if no place to send the list
				osif_sheep.Baa(2, "starting list host")
				msg.Response_data, msg.State = get_hosts(os_refs)
				osif_sheep.Baa(2, "finishing list host")
			} else {
				osif_sheep.Baa(0, "WRN: no response channel for host list request  [TGUOSI012]")
			}

			/* ======= don't think these are needed but holding ======
			case REQ_PROJNAME2ID:					// translate a project name (tenant) to ID
				if msg.Response_ch != nil {
					pname := msg.Req_data.( *string )
					if s, ok := pname2id[*pname]; ok {			// translate if there, else assume it's in it's "final" form
						msg.Response_data = s
					} else {
						msg.Response_data = pname
					}
				}

			*/

		case REQ_VALIDATE_TOKEN: // given token/tenant validate it and translate tenant name to ID if given; returns just ID
			if msg.Response_ch != nil {
				s := msg.Req_data.(*string)
				*s += "/"                                 // add trailing slant to simulate "data"
				if !have_project(s, pname2id, id2pname) { // ensure that we have creds for this project, if not attempt to get
					os_refs, pname2id, id2pname = update_project(os_admin, os_refs, os_projects, pname2id, id2pname, os_list == "all")
				}
				msg.Response_data, msg.State = validate_token(s, os_refs, pname2id, req_token)
			}

		case REQ_GET_HOSTINFO: // dig out all of the bits of host info for a single host from openstack and return in a network update struct
			if msg.Response_ch != nil {
				go get_os_hostinfo(msg, os_refs, os_projects, id2pname, pname2id) // do it asynch and return the result on the message channel
				msg = nil                                                         // prevent early response
			}

		case REQ_GET_PROJ_HOSTS:
			if msg.Response_ch != nil {
				go get_all_osvm_info(msg, os_refs, os_projects, id2pname, pname2id) // do it asynch and return the result on the message channel
				msg = nil                                                           // prevent response from this function
			}

		case REQ_GET_DEFGW: // dig out the default gateway for a project
			if msg.Response_ch != nil {
				go get_os_defgw(msg, os_refs, os_projects, id2pname, pname2id) // do it asynch and return the result on the message channel
				msg = nil                                                      // prevent early response
			}

		case REQ_VALIDATE_HOST: // validate and translate a [token/]project-name/host  string
			if msg.Response_ch != nil {
				if !have_project(msg.Req_data.(*string), pname2id, id2pname) { // ensure that we have creds for this project, if not attempt to get
					os_refs, pname2id, id2pname = update_project(os_admin, os_refs, os_projects, pname2id, id2pname, os_list == "all")
				}
				msg.Response_data, msg.State = validate_token(msg.Req_data.(*string), os_refs, pname2id, req_token)
			}

		case REQ_XLATE_HOST: // accepts a [token/][project/]host name and translate project to an ID
			if msg.Response_ch != nil {
				if !have_project(msg.Req_data.(*string), pname2id, id2pname) { // ensure that we have creds for this project, if not attempt to get
					os_refs, pname2id, id2pname = update_project(os_admin, os_refs, os_projects, pname2id, id2pname, os_list == "all")
				}
				msg.Response_data, msg.State = validate_token(msg.Req_data.(*string), os_refs, pname2id, false) // same process as validation but token not required
			}

		case REQ_VALIDATE_TEGU_ADMIN: // validate that the token is for the tegu user
			if msg.Response_ch != nil {
				if !have_project(msg.Req_data.(*string), pname2id, id2pname) { // ensure that we have creds for this project, if not attempt to get
					os_refs, pname2id, id2pname = update_project(os_admin, os_refs, os_projects, pname2id, id2pname, os_list == "all")
				}
				msg.State = validate_admin_token(os_admin, msg.Req_data.(*string), def_usr)
				msg.Response_data = ""
			}

		case REQ_HAS_ANY_ROLE: // given a token and list of roles, returns true if any role listed is listed by openstack for the token
			if msg.Response_ch != nil {
				d := msg.Req_data.(*string)
				dtoks := strings.Split(*d, " ") // data assumed to be token <space> role[,role...]
				if len(dtoks) > 1 {
					msg.Response_data, msg.State = has_any_role(os_refs, os_admin, &dtoks[0], &dtoks[1])
				} else {
					msg.State = fmt.Errorf("has_any_role: bad input data")
					msg.Response_data = false
				}

			}

		case REQ_PNAME2ID: // user, project, tenant (what ever) name to ID
			if msg.Response_ch != nil {
				msg.Response_data = pname2id[*(msg.Req_data.(*string))]
				if msg.Response_data.(*string) == nil { // maybe it was an ID that came in
					if id2pname[*(msg.Req_data.(*string))] != nil { // if in id map, then return the stirng (the id) they passed (#202)
						msg.Response_data = msg.Req_data.(*string)
					} else {
						msg.Response_data = nil // couldn't translate
					}
				}
			}

		default:
			osif_sheep.Baa(1, "unknown request: %d", msg.Msg_type)
			msg.Response_data = nil
			if msg.Response_ch != nil {
				msg.State = fmt.Errorf("osif: unknown request (%d)", msg.Msg_type)
			}
		}

		if msg != nil { // if msg wasn't passed off to a go routine
			osif_sheep.Baa(3, "processing request complete: %d", msg.Msg_type)

			if msg.Response_ch != nil { // if a reqponse channel was provided
				msg.Response_ch <- msg // send our result back to the requestor
			}
		}
	}
}
Example #11
0
func Agent_mgr(ach chan *ipc.Chmsg) {
	var (
		port      string = "29055" // port we'll listen on for connections
		adata     *agent_data
		host_list string = ""
		dscp_list string = "46 26 18" // list of dscp values that are used to promote a packet to the pri queue in intermed switches
		refresh   int64  = 60
		iqrefresh int64  = 1800 // intermediate queue refresh (this can take a long time, keep from clogging the works)
	)

	adata = &agent_data{}
	adata.agents = make(map[string]*agent)

	am_sheep = bleater.Mk_bleater(0, os.Stderr) // allocate our bleater and attach it to the master
	am_sheep.Set_prefix("agentmgr")
	tegu_sheep.Add_child(am_sheep) // we become a child so that if the master vol is adjusted we'll react too

	// suss out config settings from our section
	if cfg_data["agent"] != nil {
		if p := cfg_data["agent"]["port"]; p != nil {
			port = *p
		}
		if p := cfg_data["agent"]["verbose"]; p != nil {
			am_sheep.Set_level(uint(clike.Atoi(*p)))
		}
		if p := cfg_data["agent"]["refresh"]; p != nil {
			refresh = int64(clike.Atoi(*p))
		}
		if p := cfg_data["agent"]["iqrefresh"]; p != nil {
			iqrefresh = int64(clike.Atoi(*p))
			if iqrefresh < 90 {
				am_sheep.Baa(1, "iqrefresh in configuration file is too small, set to 90 seconds")
				iqrefresh = 90
			}
		}
	}
	if cfg_data["default"] != nil { // we pick some things from the default section too
		if p := cfg_data["default"]["pri_dscp"]; p != nil { // list of dscp (diffserv) values that match for priority promotion
			dscp_list = *p
			am_sheep.Baa(1, "dscp priority list from config file: %s", dscp_list)
		} else {
			am_sheep.Baa(1, "dscp priority list not in config file, using defaults: %s", dscp_list)
		}
	}

	dscp_list = shift_values(dscp_list) // must shift values before giving to agent

	// enforce some sanity on config file settings
	am_sheep.Baa(1, "agent_mgr thread started: listening on port %s", port)

	tklr.Add_spot(2, ach, REQ_MAC2PHOST, nil, 1)                   // tickle once, very soon after starting, to get a mac translation
	tklr.Add_spot(10, ach, REQ_INTERMEDQ, nil, 1)                  // tickle once, very soon, to start an intermediate refresh asap
	tklr.Add_spot(refresh, ach, REQ_MAC2PHOST, nil, ipc.FOREVER)   // reocurring tickle to get host mapping
	tklr.Add_spot(iqrefresh, ach, REQ_INTERMEDQ, nil, ipc.FOREVER) // reocurring tickle to ensure intermediate switches are properly set

	sess_chan := make(chan *connman.Sess_data, 1024) // channel for comm from agents (buffers, disconns, etc)
	smgr := connman.NewManager(port, sess_chan)

	for {
		select { // wait on input from either channel
		case req := <-ach:
			req.State = nil // nil state is OK, no error

			am_sheep.Baa(3, "processing request %d", req.Msg_type)

			switch req.Msg_type {
			case REQ_NOOP: // just ignore -- acts like a ping if there is a return channel

			case REQ_SENDALL: // send request to all agents
				if req.Req_data != nil {
					adata.send2all(smgr, req.Req_data.(string))
				}

			case REQ_SENDLONG: // send a long request to one agent
				if req.Req_data != nil {
					adata.send2one(smgr, req.Req_data.(string))
				}

			case REQ_SENDSHORT: // send a short request to one agent (round robin)
				if req.Req_data != nil {
					adata.send2one(smgr, req.Req_data.(string))
				}

			case REQ_MAC2PHOST: // send a request for agent to generate  mac to phost map
				if host_list != "" {
					adata.send_mac2phost(smgr, &host_list)
				}

			case REQ_CHOSTLIST: // a host list from fq-manager
				if req.Req_data != nil {
					host_list = *(req.Req_data.(*string))
				}

			case REQ_INTERMEDQ:
				req.Response_ch = nil
				if host_list != "" {
					adata.send_intermedq(smgr, &host_list, &dscp_list)
				}

			}

			am_sheep.Baa(3, "processing request finished %d", req.Msg_type) // we seem to wedge in network, this will be chatty, but may help
			if req.Response_ch != nil {                                     // if response needed; send the request (updated) back
				req.Response_ch <- req
			}

		case sreq := <-sess_chan: // data from a connection or TCP listener
			switch sreq.State {
			case connman.ST_ACCEPTED: // newly accepted connection; no action

			case connman.ST_NEW: // new connection
				a := adata.Mk_agent(sreq.Id)
				am_sheep.Baa(1, "new agent: %s [%s]", a.id, sreq.Data)
				if host_list != "" { // immediate request for this
					adata.send_mac2phost(smgr, &host_list)
					adata.send_intermedq(smgr, &host_list, &dscp_list)
				}

			case connman.ST_DISC:
				am_sheep.Baa(1, "agent dropped: %s", sreq.Id)
				if _, not_nil := adata.agents[sreq.Id]; not_nil {
					delete(adata.agents, sreq.Id)
				} else {
					am_sheep.Baa(1, "did not find an agent with the id: %s", sreq.Id)
				}
				adata.build_list() // rebuild the list to drop the agent

			case connman.ST_DATA:
				if _, not_nil := adata.agents[sreq.Id]; not_nil {
					cval := 100
					if len(sreq.Buf) < 100 { // don't try to go beyond if chop value too large
						cval = len(sreq.Buf)
					}
					am_sheep.Baa(2, "data: [%s]  %d bytes received:  first 100b: %s", sreq.Id, len(sreq.Buf), sreq.Buf[0:cval])
					adata.agents[sreq.Id].process_input(sreq.Buf)
				} else {
					am_sheep.Baa(1, "data from unknown agent: [%s]  %d bytes ignored:  %s", sreq.Id, len(sreq.Buf), sreq.Buf)
				}
			}
		} // end select
	}
}
Example #12
0
/*
	the main go routine to act on messages sent to our channel. We expect messages from the
	reservation manager, and from a tickler that causes us to evaluate the need to resize
	ovs queues.

	DSCP values:  Dscp values range from 0-64 decimal, but when described on or by
		flow-mods are shifted two bits to the left. The send flow mod function will
		do the needed shifting so all values outside of that one funciton should assume/use
		decimal values in the range of 0-64.

*/
func Fq_mgr(my_chan chan *ipc.Chmsg, sdn_host *string) {

	var (
		uri_prefix   string = ""
		msg          *ipc.Chmsg
		data         []interface{}      // generic list of data on some requests
		fdata        *Fq_req            // flow-mod request data
		qcheck_freq  int64              = 5
		hcheck_freq  int64              = 180
		host_list    *string                            // current set of openstack real hosts
		ip2mac       map[string]*string                 // translation from ip address to mac
		switch_hosts *string                            // from config file and overrides openstack list if given (mostly testing)
		ssq_cmd      *string                            // command string used to set switch queues (from config file)
		send_all     bool               = false         // send all flow-mods; false means send just ingress/egress and not intermediate switch f-mods
		alt_table    int                = DEF_ALT_TABLE // meta data marking table
		phost_suffix *string            = nil           // physical host suffix added to each host name in the list from openstack (config)

		//max_link_used	int64 = 0			// the current maximum link utilisation
	)

	fq_sheep = bleater.Mk_bleater(0, os.Stderr) // allocate our bleater and attach it to the master
	fq_sheep.Set_prefix("fq_mgr")
	tegu_sheep.Add_child(fq_sheep) // we become a child so that if the master vol is adjusted we'll react too

	// -------------- pick up config file data if there --------------------------------
	if *sdn_host == "" { // not supplied on command line, pull from config
		if sdn_host = cfg_data["default"]["sdn_host"]; sdn_host == nil { // no default; when not in config, then it's turned off and we send to agent
			sdn_host = &empty_str
		}
	}

	if cfg_data["default"]["queue_type"] != nil {
		if *cfg_data["default"]["queue_type"] == "endpoint" {
			send_all = false
		} else {
			send_all = true
		}
	}
	if p := cfg_data["default"]["alttable"]; p != nil { // this is the base; we use alt_table to alt_table + (n-1) when we need more than 1 table
		alt_table = clike.Atoi(*p)
	}

	if cfg_data["fqmgr"] != nil { // pick up things in our specific setion
		if dp := cfg_data["fqmgr"]["ssq_cmd"]; dp != nil { // set switch queue command
			ssq_cmd = dp
		}

		/*
			if p := cfg_data["fqmgr"]["default_dscp"]; p != nil {		// this is a single value and should not be confused with the dscp list in the default section of the config
				dscp = clike.Atoi( *p )
			}
		*/

		if p := cfg_data["fqmgr"]["queue_check"]; p != nil { // queue check frequency from the control file
			qcheck_freq = clike.Atoi64(*p)
			if qcheck_freq < 5 {
				qcheck_freq = 5
			}
		}

		if p := cfg_data["fqmgr"]["host_check"]; p != nil { // frequency of checking for new _real_ hosts from openstack
			hcheck_freq = clike.Atoi64(*p)
			if hcheck_freq < 30 {
				hcheck_freq = 30
			}
		}

		if p := cfg_data["fqmgr"]["switch_hosts"]; p != nil {
			switch_hosts = p
		}

		if p := cfg_data["fqmgr"]["verbose"]; p != nil {
			fq_sheep.Set_level(uint(clike.Atoi(*p)))
		}

		if p := cfg_data["fqmgr"]["phost_suffix"]; p != nil { // suffix added to physical host strings for agent commands
			if *p != "" {
				phost_suffix = p
				fq_sheep.Baa(1, "physical host names will be suffixed with: %s", *phost_suffix)
			}
		}
	}
	// ----- end config file munging ---------------------------------------------------

	//tklr.Add_spot( qcheck_freq, my_chan, REQ_SETQUEUES, nil, ipc.FOREVER );  	// tickle us every few seconds to adjust the ovs queues if needed

	if switch_hosts == nil {
		tklr.Add_spot(2, my_chan, REQ_CHOSTLIST, nil, 1)                     // tickle once, very soon after starting, to get a host list
		tklr.Add_spot(hcheck_freq, my_chan, REQ_CHOSTLIST, nil, ipc.FOREVER) // tickles us every once in a while to update host list
		fq_sheep.Baa(2, "host list will be requested from openstack every %ds", hcheck_freq)
	} else {
		host_list = switch_hosts
		fq_sheep.Baa(0, "static host list from config used for setting OVS queues: %s", *host_list)
	}

	if sdn_host != nil && *sdn_host != "" {
		uri_prefix = fmt.Sprintf("http://%s", *sdn_host)
	}

	fq_sheep.Baa(1, "flowmod-queue manager is running, sdn host: %s", *sdn_host)
	for {
		msg = <-my_chan // wait for next message
		msg.State = nil // default to all OK

		fq_sheep.Baa(3, "processing message: %d", msg.Msg_type)
		switch msg.Msg_type {
		case REQ_GEN_FMOD: // generic fmod; just pass it along w/o any special handling
			if msg.Req_data != nil {
				fdata = msg.Req_data.(*Fq_req) // pointer at struct with all of our expected goodies
				send_gfmod_agent(fdata, ip2mac, host_list, phost_suffix)
			}

		case REQ_BWOW_RESERVE: // oneway bandwidth flow-mod generation
			msg.Response_ch = nil          // nothing goes back from this
			fdata = msg.Req_data.(*Fq_req) // pointer at struct with all of the expected goodies
			send_bwow_fmods(fdata, ip2mac, phost_suffix)

		case REQ_BW_RESERVE: // bandwidth endpoint flow-mod creation; single agent script creates all needed fmods
			fdata = msg.Req_data.(*Fq_req) // pointer at struct with all of the expected goodies
			send_bw_fmods(fdata, ip2mac, phost_suffix)
			msg.Response_ch = nil // nothing goes back from this

		case REQ_IE_RESERVE: // proactive ingress/egress reservation flowmod  (this is likely deprecated as of 3/21/2015 -- resmgr invokes the bw_fmods script via agent)
			fdata = msg.Req_data.(*Fq_req) // user view of what the flow-mod should be

			if uri_prefix != "" { // an sdn controller -- skoogi -- is enabled
				msg.State = gizmos.SK_ie_flowmod(&uri_prefix, *fdata.Match.Ip1, *fdata.Match.Ip2, fdata.Expiry, fdata.Espq.Queuenum, fdata.Espq.Switch, fdata.Espq.Port)

				if msg.State == nil { // no error, no response to requestor
					fq_sheep.Baa(2, "proactive reserve successfully sent: uri=%s h1=%s h2=%s exp=%d qnum=%d swid=%s port=%d dscp=%d",
						uri_prefix, fdata.Match.Ip1, fdata.Match.Ip2, fdata.Expiry, fdata.Espq.Queuenum, fdata.Espq.Switch, fdata.Espq.Port)
					msg.Response_ch = nil
				} else {
					// do we need to suss out the id and mark it failed, or set a timer on it,  so as not to flood reqmgr with errors?
					fq_sheep.Baa(1, "ERR: proactive reserve failed: uri=%s h1=%s h2=%s exp=%d qnum=%d swid=%s port=%d  [TGUFQM008]",
						uri_prefix, fdata.Match.Ip1, fdata.Match.Ip2, fdata.Expiry, fdata.Espq.Queuenum, fdata.Espq.Switch, fdata.Espq.Port)
				}
			} else {
				// q-lite now generates one flowmod  in each direction because of the ITONS requirements
				if send_all || fdata.Espq.Queuenum > 1 { // if sending all fmods, or this has a non-intermediate queue
					cdata := fdata.Clone()       // copy so we can alter w/o affecting sender's copy
					if cdata.Espq.Port == -128 { // we'll assume in this case that the switch given is the host name and we need to set the switch to br-int
						swid := "br-int"
						cdata.Swid = &swid
					}

					if cdata.Resub == nil {
						resub_list := ""                         // resub to alternate table to set a meta mark, then to table 0 to hit openstack junk
						if cdata.Single_switch || fdata.Dir_in { // must use the base table for inbound traffic OR same switch traffic (bug 2015/1/26)
							resub_list = fmt.Sprintf("%d 0", alt_table) // base alt_table is for 'local' traffic (trafic that doesn't go through br-rl
						} else {
							resub_list = fmt.Sprintf("%d 0", alt_table+1) // base+1 is for OUTBOUND only traffic that must go through the rate limiting bridge
						}
						cdata.Resub = &resub_list
					}

					meta := "0x00/0x07" // match-value/mask; match only when meta neither of our two bits, nor the agent bit (0x04) are set
					cdata.Match.Meta = &meta

					if fdata.Dir_in { // inbound to this switch we need to revert dscp from our settings to the 'origianal' settings
						if cdata.Single_switch {
							cdata.Match.Dscp = -1 // there is no match if both on same switch
							send_gfmod_agent(cdata, ip2mac, host_list, phost_suffix)
						} else {
							cdata.Match.Dscp = cdata.Dscp // match the dscp that was added on ingress
							if !cdata.Dscp_koe {          // dropping the value on exit
								cdata.Action.Dscp = 0 // set action to turn it off, otherwise we let it ride (no overt action)
							}

							send_gfmod_agent(cdata, ip2mac, host_list, phost_suffix)
						}
					} else { // outbound from this switch set the dscp value specified on the reservation
						cdata.Match.Dscp = -1 // on outbound there is no dscp match, ensure this is off
						if cdata.Single_switch {
							send_gfmod_agent(cdata, ip2mac, host_list, phost_suffix) // in single switch mode there is no dscp value needed
						} else {
							cdata.Action.Dscp = cdata.Dscp // otherwise set the value and send
							send_gfmod_agent(cdata, ip2mac, host_list, phost_suffix)
						}
					}
				}

				msg.Response_ch = nil
			}

		case REQ_ST_RESERVE: // reservation fmods for traffic steering
			msg.Response_ch = nil // for now, nothing goes back
			if msg.Req_data != nil {
				fq_data := msg.Req_data.(*Fq_req) // request data
				if uri_prefix != "" {             // an sdn controller -- skoogi -- is enabled (not supported)
					fq_sheep.Baa(0, "ERR: steering reservations are not supported with skoogi (SDNC); no flow-mods pushed")
				} else {
					send_stfmod_agent(fq_data, ip2mac, host_list)
				}
			} else {
				fq_sheep.Baa(0, "CRI: missing data on st-reserve request to fq-mgr")
			}

		case REQ_SK_RESERVE: // send a reservation to skoogi
			data = msg.Req_data.([]interface{}) // msg data expected to be array of interface: h1, h2, expiry, queue h1/2 must be IP addresses
			if uri_prefix != "" {
				fq_sheep.Baa(2, "msg to reserve: %s %s %s %d %d", uri_prefix, data[0].(string), data[1].(string), data[2].(int64), data[3].(int))
				msg.State = gizmos.SK_reserve(&uri_prefix, data[0].(string), data[1].(string), data[2].(int64), data[3].(int))
			} else {
				fq_sheep.Baa(1, "reservation not sent, no sdn-host defined:  %s %s %s %d %d", uri_prefix, data[0].(string), data[1].(string), data[2].(int64), data[3].(int))
			}

		case REQ_SETQUEUES: // request from reservation manager which indicates something changed and queues need to be reset
			qlist := msg.Req_data.([]interface{})[0].([]string)
			if ssq_cmd != nil {
				adjust_queues(qlist, ssq_cmd, host_list) // if writing to a file and driving a local script
			} else {
				adjust_queues_agent(qlist, host_list, phost_suffix) // if sending json to an agent
			}

		case REQ_CHOSTLIST: // this is tricky as it comes from tickler as a request, and from osifmgr as a response, be careful!
			msg.Response_ch = nil // regardless of source, we should not reply to this request

			if msg.State != nil || msg.Response_data != nil { // response from ostack if with list or error
				if msg.Response_data.(*string) != nil {
					hls := strings.TrimLeft(*(msg.Response_data.(*string)), " \t") // ditch leading whitespace
					hl := &hls
					if *hl != "" {
						host_list = hl // ok to use it
						if phost_suffix != nil {
							fq_sheep.Baa(2, "host list from osif before suffix added: %s", *host_list)
							host_list = add_phost_suffix(host_list, phost_suffix) // in some cases ostack sends foo, but we really need to use foo-suffix (sigh)
						}
						send_hlist_agent(host_list) // send to agent_manager
						fq_sheep.Baa(2, "host list received from osif: %s", *host_list)
					} else {
						fq_sheep.Baa(1, "host list received from osif was discarded: ()")
					}
				} else {
					fq_sheep.Baa(0, "WRN: no  data from openstack; expected host list string  [TGUFQM009]")
				}
			} else {
				req_hosts(my_chan, fq_sheep) // send requests to osif for data
			}

		case REQ_IP2MACMAP: // a new map from osif
			if msg.Req_data != nil {
				newmap := msg.Req_data.(map[string]*string)
				if len(newmap) > 0 {
					ip2mac = newmap // safe to replace
					fq_sheep.Baa(2, "ip2mac translation received from osif: %d elements", len(ip2mac))
				} else {
					if ip2mac != nil {
						fq_sheep.Baa(2, "ip2mac translation received from osif: 0 elements -- kept old table with %d elements", len(ip2mac))
					} else {
						fq_sheep.Baa(2, "ip2mac translation received from osif: 0 elements -- no existing table to keep")
					}
				}
			} else {
				fq_sheep.Baa(0, "WRN: no  data from osif (nil map); expected ip2mac translation map  [TGUFQM010]")
			}
			msg.State = nil // state is always good

		default:
			fq_sheep.Baa(1, "unknown request: %d", msg.Msg_type)
			msg.Response_data = nil
			if msg.Response_ch != nil {
				msg.State = fmt.Errorf("unknown request (%d)", msg.Msg_type)
			}
		}

		fq_sheep.Baa(3, "processing message complete: %d", msg.Msg_type)
		if msg.Response_ch != nil { // if a reqponse channel was provided
			fq_sheep.Baa(3, "sending response: %d", msg.Msg_type)
			msg.Response_ch <- msg // send our result back to the requestor
		}
	}
}
Example #13
0
/*
	Internal funciton to convert an interface into a supported desired value.
*/
func cvt2desired(v interface{}, desired int) interface{} {
	switch v.(type) {

	case string:
		switch desired {
		case ET_STRING:
			return v
		case ET_STRINGP:
			return &v
		case ET_INT:
			return v.(int)
		case ET_UINT:
			return v.(uint)
		case ET_INT64:
			return v.(int64)
		case ET_FLOAT:
			return v.(float64)
		case ET_BOOL:
			return v == "true"
		}

	case *string:
		switch desired {
		case ET_STRING:
			return *(v.(*string))
		case ET_STRINGP:
			return v
		case ET_INT:
			return clike.Atoi(*(v.(*string)))
		case ET_UINT:
			return clike.Atou(*(v.(*string)))
		case ET_INT64:
			return clike.Atoll(*(v.(*string)))
		case ET_FLOAT:
			return clike.Atof(*(v.(*string)))
		case ET_BOOL:
			return *(v.(*string)) == "true"
		}

	case float64:
		switch desired {
		case ET_STRING:
			return fmt.Sprintf("%.2f", v)
		case ET_STRINGP:
			s := fmt.Sprintf("%.2f", v)
			return &s
		case ET_INT:
			return int(v.(float64))
		case ET_UINT:
			return uint(v.(float64))
		case ET_INT64:
			return int64(v.(float64))
		case ET_FLOAT:
			return v
		case ET_BOOL:
			return v.(float64) != 0.0
		}

	case int:
		switch desired {
		case ET_STRING:
			return fmt.Sprintf("%d", v)
		case ET_STRINGP:
			s := fmt.Sprintf("%d", v)
			return &s
		case ET_INT:
			return v
		case ET_UINT:
			return uint(v.(int))
		case ET_INT64:
			return int64(v.(int))
		case ET_FLOAT:
			return float64(v.(int))
		case ET_BOOL:
			return v.(int) != 0
		}

	case int64:
		switch desired {
		case ET_STRING:
			return fmt.Sprintf("%d", v)
		case ET_STRINGP:
			s := fmt.Sprintf("%d", v)
			return &s
		case ET_INT:
			return int(v.(int64))
		case ET_UINT:
			return uint(v.(int64))
		case ET_INT64:
			return v
		case ET_FLOAT:
			return float64(v.(int64))
		case ET_BOOL:
			return v.(int64) != 0
		}

	case bool:
		switch desired {
		case ET_STRING:
			return fmt.Sprintf("%v", v)
		case ET_STRINGP:
			s := fmt.Sprintf("%v", v)
			return &s
		case ET_INT:
			if v.(bool) {
				return int(1)
			} else {
				return int(0)
			}
		case ET_UINT:
			if v.(bool) {
				return uint(1)
			} else {
				return uint(0)
			}
		case ET_INT64:
			if v.(bool) {
				return int64(1)
			} else {
				return int64(0)
			}
		case ET_FLOAT:
			if v.(bool) {
				return 1.0
			} else {
				return 0.0
			}
		case ET_BOOL:
			return v
		}
	}

	return nil
}