in traffic_ops/traffic_ops_golang/server/servers.go [738:1043]
func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth.CurrentUser, useIMS bool, version api.Version, roleBasedPerms bool) ([]tc.ServerV5, uint64, error, error, int, *time.Time) {
var maxTime time.Time
var runSecond bool
// Query Parameters to Database Query column mappings
// see the fields mapped in the SQL query
queryParamsToSQLCols := map[string]dbhelpers.WhereColumnInfo{
"cachegroup": {Column: "s.cachegroup", Checker: api.IsInt},
"cacheGroup": {Column: "cg.name", Checker: nil},
"parentCachegroup": {Column: "cg.parent_cachegroup_id", Checker: api.IsInt},
"cachegroupName": {Column: "cg.name", Checker: nil},
"cdn": {Column: "s.cdn_id", Checker: api.IsInt},
"id": {Column: "s.id", Checker: api.IsInt},
"hostName": {Column: "s.host_name", Checker: nil},
"physLocation": {Column: "s.phys_location", Checker: nil},
"physicalLocationID": {Column: "s.phys_location_id", Checker: api.IsInt},
"physicalLocation": {Column: "s.phys_location", Checker: nil},
"status": {Column: "st.name", Checker: nil},
"topology": {Column: "tc.topology", Checker: nil},
"type": {Column: "t.name", Checker: nil},
"dsId": {Column: "dss.deliveryservice", Checker: nil},
}
if version.GreaterThanOrEqualTo(&api.Version{Major: 4}) {
queryParamsToSQLCols["profileName"] = dbhelpers.WhereColumnInfo{
Column: "sp.profile_name",
Checker: nil,
}
} else {
queryParamsToSQLCols["profileId"] = dbhelpers.WhereColumnInfo{
Column: "s.profile",
Checker: api.IsInt,
}
}
usesMids := false
queryAddition := ""
dsHasRequiredCapabilities := false
var requiredCapabilities []string
var dsID int
var cdnID int
var serverCount uint64
var err error
if dsIDStr, ok := params[`dsId`]; ok {
// don't allow query on ds outside user's tenant
dsID, err = strconv.Atoi(dsIDStr)
if err != nil {
return nil, 0, errors.New("dsId must be an integer"), nil, http.StatusNotFound, nil
}
cdnID, _, err = dbhelpers.GetDSCDNIdFromID(tx.Tx, dsID)
if err != nil {
return nil, 0, nil, err, http.StatusInternalServerError, nil
}
userErr, sysErr, _ := tenant.CheckID(tx.Tx, user, dsID)
if userErr != nil || sysErr != nil {
return nil, 0, errors.New("Forbidden"), sysErr, http.StatusForbidden, nil
}
var joinSubQuery string
if err := tx.QueryRow(deliveryservice.GetRequiredCapabilitiesQuery, dsID).Scan(pq.Array(&requiredCapabilities)); err != nil && err != sql.ErrNoRows {
err = fmt.Errorf("unable to get required capabilities for deliveryservice %d: %w", dsID, err)
return nil, 0, nil, err, http.StatusInternalServerError, nil
}
if requiredCapabilities != nil && len(requiredCapabilities) > 0 {
dsHasRequiredCapabilities = true
}
joinSubQuery = dssTopologiesJoinSubquery
// only if dsId is part of params: add join on deliveryservice_server table
queryAddition = fmt.Sprintf(deliveryServiceServersJoin, joinSubQuery)
// depending on ds type, also need to add mids
dsType, _, _, err := dbhelpers.GetDeliveryServiceTypeAndCDNName(dsID, tx.Tx)
if err != nil {
return nil, 0, nil, err, http.StatusInternalServerError, nil
}
usesMids = dsType.UsesMidCache()
log.Debugf("Servers for ds %d; uses mids? %v\n", dsID, usesMids)
}
if _, ok := params[`topology`]; ok {
/* language=SQL */
queryAddition += `
JOIN topology_cachegroup tc ON cg."name" = tc.cachegroup
`
}
where, orderBy, pagination, queryValues, errs := dbhelpers.BuildWhereAndOrderByAndPagination(params, queryParamsToSQLCols)
if dsHasRequiredCapabilities {
where += requiredCapabilitiesCondition
}
if len(errs) > 0 {
return nil, 0, util.JoinErrs(errs), nil, http.StatusBadRequest, nil
}
var queryString, countQueryString string
queryString = selectQuery
countQueryString = serverCountQuery
if version.GreaterThanOrEqualTo(&api.Version{Major: 4}) {
if _, ok := params["profileName"]; ok {
queryString = queryString + `
JOIN server_profile sp ON s.id = sp.server`
countQueryString = countQueryString + `
JOIN server_profile sp ON s.id = sp.server`
} else {
queryString = queryString + ` ` + joinProfileV4
countQueryString = countQueryString + ` ` + joinProfileV4
}
}
countQuery := countQueryString + queryAddition + where
// If we are querying for a DS that has reqd capabilities, we need to make sure that we also include all the ORG servers directly assigned to this DS
if _, ok := params["dsId"]; ok && dsHasRequiredCapabilities {
countQuery = `SELECT (` + countQuery + `) + (` + countQueryString + originServerQuery + `) AS total`
}
serverCount, err = getServerCount(tx, countQuery, queryValues)
if err != nil {
return nil, 0, nil, fmt.Errorf("failed to get servers count: %v", err), http.StatusInternalServerError, nil
}
serversList := []tc.ServerV5{}
if useIMS {
runSecond, maxTime = ims.TryIfModifiedSinceQuery(tx, h, queryValues, selectMaxLastUpdatedQuery(queryAddition, where))
if !runSecond {
log.Debugln("IMS HIT")
return serversList, 0, nil, nil, http.StatusNotModified, &maxTime
}
log.Debugln("IMS MISS")
} else {
log.Debugln("Non IMS request")
}
query := queryString + queryAddition + where + orderBy + pagination
// If you're looking to get the servers for a particular delivery service, make sure you're also querying the ORG servers from the deliveryservice_server table
if _, ok := params[`dsId`]; ok {
query = `(` + queryString + queryAddition + where + orderBy + pagination + `) UNION ` + queryString + originServerQuery
}
log.Debugln("Query is ", query)
rows, err := tx.NamedQuery(query, queryValues)
if err != nil {
return nil, serverCount, nil, errors.New("querying: " + err.Error()), http.StatusInternalServerError, nil
}
defer rows.Close()
HiddenField := "********"
servers := make(map[int]tc.ServerV5)
ids := []int{}
for rows.Next() {
var s tc.ServerV5
err := rows.Scan(
&s.CacheGroup,
&s.CacheGroupID,
&s.CDNID,
&s.CDN,
&s.DomainName,
&s.GUID,
&s.HostName,
&s.HTTPSPort,
&s.ID,
&s.ILOIPAddress,
&s.ILOIPGateway,
&s.ILOIPNetmask,
&s.ILOPassword,
&s.ILOUsername,
&s.LastUpdated,
&s.MgmtIPAddress,
&s.MgmtIPGateway,
&s.MgmtIPNetmask,
&s.OfflineReason,
&s.PhysicalLocation,
&s.PhysicalLocationID,
pq.Array(&s.Profiles),
&s.Rack,
&s.RevalUpdateTime,
&s.RevalApplyTime,
&s.RevalUpdateFailed,
&s.Status,
&s.StatusID,
&s.TCPPort,
&s.Type,
&s.TypeID,
&s.ConfigUpdateTime,
&s.ConfigApplyTime,
&s.ConfigUpdateFailed,
&s.XMPPID,
&s.XMPPPasswd,
&s.StatusLastUpdated,
)
if err != nil {
return nil, serverCount, nil, fmt.Errorf("getting servers: %w", err), http.StatusInternalServerError, nil
}
if (version.GreaterThanOrEqualTo(&api.Version{Major: 4}) && roleBasedPerms) || version.GreaterThanOrEqualTo(&api.Version{Major: 5}) {
if !user.Can(tc.PermSecureServerRead) {
s.ILOPassword = &HiddenField
s.XMPPPasswd = &HiddenField
}
} else if user.PrivLevel < auth.PrivLevelOperations {
s.ILOPassword = &HiddenField
s.XMPPPasswd = &HiddenField
}
if _, ok := servers[s.ID]; ok {
return nil, serverCount, nil, fmt.Errorf("found more than one server with ID #%d", s.ID), http.StatusInternalServerError, nil
}
servers[s.ID] = s
ids = append(ids, s.ID)
}
// if ds requested uses mid-tier caches, add those to the list as well
if usesMids {
midIDs, userErr, sysErr, errCode := getMidServers(ids, servers, dsID, cdnID, tx, dsHasRequiredCapabilities)
log.Debugf("getting mids: %v, %v, %s\n", userErr, sysErr, http.StatusText(errCode))
serverCount = serverCount + uint64(len(midIDs))
if userErr != nil || sysErr != nil {
return nil, serverCount, userErr, sysErr, errCode, nil
}
ids = append(ids, midIDs...)
}
if len(ids) < 1 {
return []tc.ServerV5{}, serverCount, nil, nil, http.StatusOK, nil
}
query, args, err := sqlx.In(`SELECT max_bandwidth, monitor, mtu, name, server, router_host_name, router_port_name FROM interface WHERE server IN (?)`, ids)
if err != nil {
return nil, serverCount, nil, fmt.Errorf("building interfaces query: %v", err), http.StatusInternalServerError, nil
}
query = tx.Rebind(query)
interfaces := map[int]map[string]tc.ServerInterfaceInfoV40{}
interfaceRows, err := tx.Queryx(query, args...)
if err != nil {
return nil, serverCount, nil, fmt.Errorf("querying for interfaces: %v", err), http.StatusInternalServerError, nil
}
defer interfaceRows.Close()
for interfaceRows.Next() {
iface := tc.ServerInterfaceInfoV40{
ServerInterfaceInfo: tc.ServerInterfaceInfo{
IPAddresses: []tc.ServerIPAddress{},
},
}
var server int
var routerHostName string
var routerPort string
if err = interfaceRows.Scan(&iface.MaxBandwidth, &iface.Monitor, &iface.MTU, &iface.Name, &server, &routerHostName, &routerPort); err != nil {
return nil, serverCount, nil, fmt.Errorf("getting server interfaces: %v", err), http.StatusInternalServerError, nil
}
if _, ok := servers[server]; !ok {
continue
}
if _, ok := interfaces[server]; !ok {
interfaces[server] = map[string]tc.ServerInterfaceInfoV40{}
}
iface.RouterHostName = routerHostName
iface.RouterPortName = routerPort
interfaces[server][iface.Name] = iface
}
query, args, err = sqlx.In(`SELECT address, gateway, service_address, server, interface FROM ip_address WHERE server IN (?)`, ids)
if err != nil {
return nil, serverCount, nil, fmt.Errorf("building IP addresses query: %v", err), http.StatusInternalServerError, nil
}
query = tx.Rebind(query)
ipRows, err := tx.Tx.Query(query, args...)
if err != nil {
return nil, serverCount, nil, fmt.Errorf("querying for IP addresses: %v", err), http.StatusInternalServerError, nil
}
defer ipRows.Close()
for ipRows.Next() {
var ip tc.ServerIPAddress
var server int
var iface string
if err = ipRows.Scan(&ip.Address, &ip.Gateway, &ip.ServiceAddress, &server, &iface); err != nil {
return nil, serverCount, nil, fmt.Errorf("getting server IP addresses: %v", err), http.StatusInternalServerError, nil
}
if _, ok := interfaces[server]; !ok {
continue
}
if i, ok := interfaces[server][iface]; !ok {
log.Warnf("IP addresses query returned addresses for an interface that was not found in interfaces query: %s", iface)
} else {
i.IPAddresses = append(i.IPAddresses, ip)
interfaces[server][iface] = i
}
}
returnable := make([]tc.ServerV5, 0, len(ids))
for _, id := range ids {
server := servers[id]
for _, iface := range interfaces[id] {
server.Interfaces = append(server.Interfaces, iface)
}
returnable = append(returnable, server)
}
return returnable, serverCount, nil, nil, http.StatusOK, &maxTime
}