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 } }
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 } }