in traffic_ops/traffic_ops_golang/server/servers.go [1276:1564]
func Update(inf *api.Info) (int, error, error) {
id := inf.IntParams["id"]
// Get original server
originals, _, userErr, sysErr, errCode, _ := getServers(inf.RequestHeaders(), inf.Params, inf.Tx, inf.User, false, *inf.Version, inf.Config.RoleBasedPermissions)
if userErr != nil || sysErr != nil {
return errCode, userErr, sysErr
}
if len(originals) < 1 {
return http.StatusNotFound, errors.New("the server doesn't exist, cannot update"), nil
}
if len(originals) > 1 {
return http.StatusInternalServerError, nil, fmt.Errorf("too many servers by ID %d: %d", id, len(originals))
}
original := originals[0]
if original.XMPPID == nil || *original.XMPPID == "" {
log.Warnf("original server %s (#%d) had no XMPPID", original.HostName, original.ID)
}
if original.StatusLastUpdated == nil {
log.Warnln("original server had no Status Last Updated time")
original.StatusLastUpdated = util.Ptr(original.LastUpdated)
}
var originalXMPPID string
if original.XMPPID != nil {
originalXMPPID = *original.XMPPID
}
originalStatusID := original.StatusID
var server tc.ServerV5
var serverV3 tc.ServerV30
var statusLastUpdatedTime time.Time
tx := inf.Tx.Tx
if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 5}) {
server.ID = inf.IntParams["id"]
if err := inf.DecodeBody(&server); err != nil {
return http.StatusBadRequest, err, nil
}
if server.StatusID != originalStatusID {
currentTime := time.Now()
server.StatusLastUpdated = ¤tTime
statusLastUpdatedTime = currentTime
} else {
server.StatusLastUpdated = original.StatusLastUpdated
statusLastUpdatedTime = *original.StatusLastUpdated
}
tmp := server.Downgrade()
_, userErr, sysErr := validateV4(&tmp, tx)
if userErr != nil || sysErr != nil {
if sysErr != nil {
return http.StatusInternalServerError, userErr, sysErr
}
return http.StatusBadRequest, userErr, sysErr
}
server = tmp.Upgrade()
} else if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 4}) {
var serverV4 tc.ServerV4
serverV4.ID = util.Ptr(inf.IntParams["id"])
if err := inf.DecodeBody(&serverV4); err != nil {
return http.StatusBadRequest, err, nil
}
if serverV4.StatusID == nil || *serverV4.StatusID != originalStatusID {
currentTime := time.Now()
server.StatusLastUpdated = ¤tTime
statusLastUpdatedTime = currentTime
} else {
server.StatusLastUpdated = original.StatusLastUpdated
statusLastUpdatedTime = *original.StatusLastUpdated
}
_, userErr, sysErr := validateV4(&serverV4, tx)
if userErr != nil || sysErr != nil {
if sysErr != nil {
return http.StatusInternalServerError, userErr, sysErr
}
return http.StatusBadRequest, userErr, sysErr
}
server = serverV4.Upgrade()
} else {
serverV3.ID = new(int)
*serverV3.ID = inf.IntParams["id"]
if err := inf.DecodeBody(&serverV3); err != nil {
return http.StatusBadRequest, err, nil
}
if serverV3.StatusID != nil && *serverV3.StatusID != originalStatusID {
currentTime := time.Now()
serverV3.StatusLastUpdated = ¤tTime
statusLastUpdatedTime = currentTime
} else {
serverV3.StatusLastUpdated = original.StatusLastUpdated
statusLastUpdatedTime = *original.StatusLastUpdated
}
_, userErr, sysErr := validateV3(&serverV3, tx)
if userErr != nil || sysErr != nil {
if sysErr != nil {
return http.StatusInternalServerError, userErr, sysErr
}
return http.StatusBadRequest, userErr, sysErr
}
profileName, exists, err := dbhelpers.GetProfileNameFromID(*serverV3.ProfileID, tx)
if err != nil {
return http.StatusInternalServerError, nil, err
}
if !exists {
return http.StatusNotFound, errors.New("profile does not exist"), nil
}
profileNames := []string{profileName}
upgraded, err := serverV3.UpgradeToV40(profileNames)
if err != nil {
return http.StatusInternalServerError, nil, fmt.Errorf("error upgrading valid V3 server to V4 structure: %w", err)
}
server = upgraded.Upgrade()
}
if original.CacheGroupID != server.CacheGroupID || original.CDNID != server.CDNID {
hasDSOnCDN, err := dbhelpers.CachegroupHasTopologyBasedDeliveryServicesOnCDN(tx, original.CacheGroupID, original.CDNID)
if err != nil {
return http.StatusInternalServerError, nil, err
}
CDNIDs := []int{}
if hasDSOnCDN {
CDNIDs = append(CDNIDs, original.CDNID)
}
if err = topology_validation.CheckForEmptyCacheGroups(inf.Tx, []int{original.CacheGroupID}, CDNIDs, true, []int{original.ID}); err != nil {
return http.StatusBadRequest, fmt.Errorf("server is the last one in its Cache Group, which is used by a Topology, so it cannot be moved to another Cache Group: %w", err), nil
}
}
status, ok, err := dbhelpers.GetStatusByID(server.StatusID, tx)
if err != nil {
return http.StatusInternalServerError, nil, fmt.Errorf("getting server #%d status (#%d): %v", id, server.StatusID, err)
}
if !ok {
log.Warnf("previously existent status #%d not found when fetching later", server.StatusID)
return http.StatusBadRequest, fmt.Errorf("no such Status: #%d", server.StatusID), nil
}
if status.Name == nil {
return http.StatusInternalServerError, nil, fmt.Errorf("status #%d had no name", server.StatusID)
}
if *status.Name != string(tc.CacheStatusOnline) && *status.Name != string(tc.CacheStatusReported) {
dsIDs, err := getActiveDeliveryServicesThatOnlyHaveThisServerAssigned(id, original.Type, tx)
if err != nil {
return http.StatusInternalServerError,
nil,
fmt.Errorf("getting Delivery Services to which server #%d is assigned that have no other servers: %w", id, err)
}
if len(dsIDs) > 0 {
prefix := fmt.Sprintf("setting server status to '%s' would leave Active Delivery Service", *status.Name)
alertText := InvalidStatusForDeliveryServicesAlertText(prefix, original.Type, dsIDs)
return http.StatusConflict, errors.New(alertText), nil
}
}
if userErr, sysErr, errCode = checkTypeChangeSafety(server, inf.Tx); userErr != nil || sysErr != nil {
return errCode, userErr, sysErr
}
if server.XMPPID != nil && *server.XMPPID != "" && originalXMPPID != "" && *server.XMPPID != originalXMPPID {
return http.StatusBadRequest, errors.New("server cannot be updated due to requested XMPPID change. XMPIDD is immutable"), nil
}
userErr, sysErr, statusCode := api.CheckIfUnModified(inf.RequestHeaders(), inf.Tx, server.ID, "server")
if userErr != nil || sysErr != nil {
return statusCode, userErr, sysErr
}
if server.CDN != "" {
userErr, sysErr, statusCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, server.CDN, inf.User.UserName)
if userErr != nil || sysErr != nil {
return statusCode, userErr, sysErr
}
} else {
userErr, sysErr, statusCode = dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(server.CDNID), inf.User.UserName)
if userErr != nil || sysErr != nil {
return statusCode, userErr, sysErr
}
}
if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 4}) {
if err = dbhelpers.UpdateServerProfilesForV4(server.ID, server.Profiles, tx); err != nil {
userErr, sysErr, errCode := api.ParseDBError(err)
return errCode, userErr, sysErr
}
} else {
if err = dbhelpers.UpdateServerProfileTableForV3(serverV3.ID, serverV3.ProfileID, (original.Profiles)[0], tx); err != nil {
return http.StatusInternalServerError, nil, fmt.Errorf("failed to update server_profile: %w", err)
}
}
serverID, errCode, userErr, sysErr := updateServer(inf.Tx, server)
if userErr != nil || sysErr != nil {
return errCode, userErr, sysErr
}
if userErr, sysErr, errCode = deleteInterfaces(id, tx); userErr != nil || sysErr != nil {
return errCode, userErr, sysErr
}
if userErr, sysErr, errCode = createInterfaces(id, server.Interfaces, tx); userErr != nil || sysErr != nil {
return errCode, userErr, sysErr
}
where := `WHERE s.id = $1`
var selquery string
if inf.Version.Major == 4 || inf.Version.LessThan(&api.Version{Major: 4}) {
selquery = selectQuery + joinProfileV4 + where
} else {
selquery = selectQuery + where
}
err = inf.Tx.QueryRow(selquery, serverID).Scan(
&server.CacheGroup,
&server.CacheGroupID,
&server.CDNID,
&server.CDN,
&server.DomainName,
&server.GUID,
&server.HostName,
&server.HTTPSPort,
&server.ID,
&server.ILOIPAddress,
&server.ILOIPGateway,
&server.ILOIPNetmask,
&server.ILOPassword,
&server.ILOUsername,
&server.LastUpdated,
&server.MgmtIPAddress,
&server.MgmtIPGateway,
&server.MgmtIPNetmask,
&server.OfflineReason,
&server.PhysicalLocation,
&server.PhysicalLocationID,
pq.Array(&server.Profiles),
&server.Rack,
&server.RevalUpdateTime,
&server.RevalApplyTime,
&server.RevalUpdateFailed,
&server.Status,
&server.StatusID,
&server.TCPPort,
&server.Type,
&server.TypeID,
&server.ConfigUpdateTime,
&server.ConfigApplyTime,
&server.ConfigUpdateFailed,
&server.XMPPID,
&server.XMPPPasswd,
&server.StatusLastUpdated,
)
if err != nil {
return http.StatusInternalServerError, nil, err
}
serversInterfaces, err := dbhelpers.GetServersInterfaces([]int{server.ID}, inf.Tx.Tx)
if err != nil {
return http.StatusInternalServerError, nil, err
}
if interfacesMap, ok := serversInterfaces[server.ID]; ok {
for _, intfc := range interfacesMap {
server.Interfaces = append(server.Interfaces, intfc)
}
}
if userErr, sysErr, errCode = updateStatusLastUpdatedTime(id, &statusLastUpdatedTime, tx); userErr != nil || sysErr != nil {
return errCode, userErr, sysErr
}
if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 5}) {
inf.WriteSuccessResponse(server, "Server updated")
} else if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 4}) {
inf.WriteSuccessResponse(server.Downgrade(), "Server updated")
} else if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 3}) {
downgraded := server.Downgrade()
csp, err := dbhelpers.GetCommonServerPropertiesFromV4(downgraded, inf.Tx.Tx)
if err != nil {
return http.StatusInternalServerError, nil, err
}
serverV30, err := downgraded.ToServerV3FromV4(csp)
if err != nil {
return http.StatusInternalServerError, nil, err
}
inf.WriteSuccessResponse(serverV30, "Server updated")
}
inf.CreateChangeLog(fmt.Sprintf("SERVER: %s.%s, ID: %d, ACTION: updated", server.HostName, server.DomainName, server.ID))
return http.StatusOK, nil, nil
}