Example #1
0
File: osif.go Project: krjoshi/tegu
/*
	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
}
Example #2
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
			}
		}
	}
}