/* Fetch new maps and update the project list. Returns: osack reference map project name to id map id to project name map On error err is set, and nil values returned for conversion maps and the old os_list is returned */ func update_project(os_admin *ostack.Ostack, old_os_refs map[string]*ostack.Ostack, os_projects map[string]*osif_project, old_pname2id map[string]*string, old_id2pname map[string]*string, update_list bool) ( os_refs map[string]*ostack.Ostack, id2pname map[string]*string, pname2id map[string]*string) { new_name2id, new_id2pname, err := os_admin.Map_tenants() // fetch new maps, overwrite only if no errors if err == nil { pname2id = new_name2id id2pname = new_id2pname } else { osif_sheep.Baa(1, "WRN: unable to get tenant name/ID translation data: %s [TGUOSI010]", err) return old_os_refs, old_pname2id, old_id2pname } if update_list { // asked to update the os_refs too os_refs, _ = refresh_creds(os_admin, old_os_refs, id2pname) // periodic update of project cred list add2projects(os_projects, os_refs, pname2id, 2) // add refernces to the projects list if osif_sheep.Would_baa(2) { for _, v := range os_projects { osif_sheep.Baa(2, "update project sees: %s", *v.name) } } } else { os_refs = old_os_refs } osif_sheep.Baa(1, "credentials were updated from openstack") return os_refs, pname2id, id2pname }
/* 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 } } } }