Example #1
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 #2
0
func main() {

	home := os.Getenv("HOME")
	def_user := os.Getenv("LOGNAME")
	def_rdir := "/tmp/tegu_b" // rsync directory created on remote hosts
	def_rlist :=              // list of scripts to copy to remote hosts for execution
		"/usr/bin/create_ovs_queues " +
			"/usr/bin/map_mac2phost " +
			"/usr/bin/ovs_sp2uuid " +
			"/usr/bin/purge_ovs_queues " +
			"/usr/bin/ql_setup_irl " +
			"/usr/bin/ql_setup_ipt " +
			"/usr/bin/send_ovs_fmod " +
			"/usr/bin/tegu_add_mirror " +
			"/usr/bin/tegu_del_mirror " +
			"/usr/bin/ql_bw_fmods " +
			"/usr/bin/ql_bwow_fmods " +
			"/usr/bin/ql_set_trunks " +
			"/usr/bin/ql_filter_rtr " +
			"/usr/bin/setup_ovs_intermed "

	if home == "" {
		home = "/home/tegu" // probably bogus, but we'll have something
	}
	def_key := home + "/.ssh/id_rsa," + home + "/.ssh/id_dsa" // default ssh key to use

	needs_help := flag.Bool("?", false, "show usage") // define recognised command line options
	id := flag.Int("i", 0, "id")
	key_files := flag.String("k", def_key, "ssh-key file(s) for broker")
	log_dir := flag.String("l", "stderr", "log_dir")
	parallel := flag.Int("p", 10, "parallel ssh commands")
	no_rsync := flag.Bool("no-rsync", false, "turn off rsync")
	rdir := flag.String("rdir", def_rdir, "rsync remote directory")
	rlist := flag.String("rlist", def_rlist, "rsync file list")
	tegu_host := flag.String("h", "localhost:29055", "tegu_host:port")
	user := flag.String("u", def_user, "ssh user-name")
	verbose := flag.Bool("v", false, "verbose")
	vlevel := flag.Int("V", 1, "verbose-level")
	flag.Parse() // actually parse the commandline

	if *needs_help {
		usage(version)
		os.Exit(0)
	}

	if *id <= 0 {
		fmt.Fprintf(os.Stderr, "ERR: must enter -i id (number) on command line\n")
		os.Exit(1)
	}

	sheep = bleater.Mk_bleater(0, os.Stderr)
	sheep.Set_prefix(fmt.Sprintf("agent-%d", *id)) // append the pid so that if multiple agents are running they'll use different log files

	if *needs_help {
		usage(version)
		os.Exit(0)
	}

	if *verbose {
		sheep.Set_level(1)
	} else {
		if *vlevel > 0 {
			sheep.Set_level(uint(*vlevel))
		}
	}

	if *log_dir != "stderr" { // allow it to stay on stderr
		lfn := sheep.Mk_logfile_nm(log_dir, 86400)
		sheep.Baa(1, "switching to log file: %s", *lfn)
		sheep.Append_target(*lfn, false)      // switch bleaters to the log file rather than stderr
		go sheep.Sheep_herder(log_dir, 86400) // start the function that will roll the log now and again
	}

	sheep.Baa(1, "tegu_agent %s started", version)
	sheep.Baa(1, "will contact tegu on port: %s", *tegu_host)

	jc := jsontools.Mk_jsoncache()                  // create json cache to buffer tegu datagram input
	sess_mgr := make(chan *connman.Sess_data, 1024) // session management to create tegu connections with and drive the session listener(s)
	smgr := connman.NewManager("", sess_mgr)        // get a manager, but no listen port opened

	connect2tegu(smgr, tegu_host, sess_mgr) // establish initial connection

	ntoks, key_toks := token.Tokenise_populated(*key_files, " ,") // allow space or , seps and drop nil tokens
	if ntoks <= 0 {
		sheep.Baa(0, "CRI: no ssh key files given (-k)")
		os.Exit(1)
	}
	keys := make([]string, ntoks)
	for i := range key_toks {
		keys[i] = key_toks[i]
	}
	broker := ssh_broker.Mk_broker(*user, keys)
	if broker == nil {
		sheep.Baa(0, "CRI: unable to create an ssh broker")
		os.Exit(1)
	}
	if !*no_rsync {
		sheep.Baa(1, "will sync these files to remote hosts: %s", *rlist)
		broker.Add_rsync(rlist, rdir)
	}
	sheep.Baa(1, "successfully created ssh_broker for user: %s, command path: %s", *user, *rdir)
	broker.Start_initiators(*parallel)

	for {
		select { // wait on input from any channel -- just one now, but who knows
		case sreq := <-sess_mgr: // data from the network
			switch sreq.State {
			case connman.ST_ACCEPTED: // shouldn't happen
				sheep.Baa(1, "this shouldn't happen; accepted session????")

			case connman.ST_NEW: // new connection; nothing to process here

			case connman.ST_DISC:
				sheep.Baa(1, "session to tegu was lost")
				connect2tegu(smgr, tegu_host, sess_mgr) // blocks until connected and reports on the conn_ch channel when done
				broker.Reset()                          // reset the broker each time we pick up a new tegu connection

			case connman.ST_DATA:
				sheep.Baa(3, "data: [%s]  %d bytes received", sreq.Id, len(sreq.Buf))
				jc.Add_bytes(sreq.Buf)
				jblob := jc.Get_blob() // get next blob if ready
				for jblob != nil {
					resp := handle_blob(jblob, broker, rdir)
					if resp != nil {
						for i := range resp {
							smgr.Write(sreq.Id, resp[i])
						}
					}

					jblob = jc.Get_blob() // get next blob if more than one in the cache
				}
			}
		} // end select
	}
}