From 9e2f2e2e8de284024b98b7b413c55af0075b47fb Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Tue, 10 May 2022 15:09:08 -0600 Subject: [PATCH 1/6] Add deprecation notices, remove v4 handling, fix #6800 --- .../traffic_ops_golang/routing/routes.go | 3 - .../traffic_ops_golang/server/detail.go | 178 ++++++++++-------- 2 files changed, 104 insertions(+), 77 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go b/traffic_ops/traffic_ops_golang/routing/routes.go index cbfa250307..452ab76157 100644 --- a/traffic_ops/traffic_ops_golang/routing/routes.go +++ b/traffic_ops/traffic_ops_golang/routing/routes.go @@ -311,9 +311,6 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodGet, Path: `servercheck/extensions$`, Handler: extensions.Get, RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: []string{"SERVER-CHECK:READ", "SERVER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4834985993}, {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodDelete, Path: `servercheck/extensions/{id}$`, Handler: extensions.Delete, RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: []string{"SERVER-CHECK:DELETE", "SERVER-CHECK:READ", "SERVER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4804982993}, - //Server Details - {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodGet, Path: `servers/details/?$`, Handler: server.GetDetailParamHandler, RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: []string{"SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 42612647143}, - //Server status {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}/status$`, Handler: server.UpdateStatusHandler, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ", "STATUS:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4766638513}, {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodPost, Path: `servers/{id}/queue_update$`, Handler: server.QueueUpdateHandler, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:QUEUE", "SERVER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 41894713}, diff --git a/traffic_ops/traffic_ops_golang/server/detail.go b/traffic_ops/traffic_ops_golang/server/detail.go index 287e0e4011..c90ebaf6bf 100644 --- a/traffic_ops/traffic_ops_golang/server/detail.go +++ b/traffic_ops/traffic_ops_golang/server/detail.go @@ -28,6 +28,7 @@ import ( "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers" + "github.com/apache/trafficcontrol/lib/go-log" "github.com/apache/trafficcontrol/lib/go-tc" "github.com/apache/trafficcontrol/lib/go-util" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api" @@ -36,27 +37,39 @@ import ( "github.com/lib/pq" ) +type serverDetailResponse struct { + Limit int `json:"limit"` + OrderBy string `json:"orderby"` + Response interface{} `json:"response"` + Size int `json:"size"` +} + +// GetDetailParamHandler handles GET requests to /servers/details (the name +// includes "Param" for legacy reasons). +// +// Deprecated: This endpoint has been removed from APIv4. func GetDetailParamHandler(w http.ResponseWriter, r *http.Request) { + alt := "/servers" inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) + api.HandleDeprecatedErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr, &alt) return } defer inf.Close() hostName := inf.Params["hostName"] physLocationIDStr := inf.Params["physLocationID"] - physLocationID := -1 + var physLocationID int if physLocationIDStr != "" { - err := error(nil) + var err error physLocationID, err = strconv.Atoi(physLocationIDStr) if err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("physLocationID parameter is not an integer"), nil) + api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("physLocationID parameter is not an integer"), err, &alt) return } } if hostName == "" && physLocationIDStr == "" { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("missing required fields: 'hostName' or 'physLocationID'"), nil) + api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("missing required fields: 'hostName' or 'physLocationID'"), nil, &alt) return } orderBy := "hostName" @@ -65,22 +78,25 @@ func GetDetailParamHandler(w http.ResponseWriter, r *http.Request) { } limit := 1000 if limitStr, ok := inf.Params["limit"]; ok { - err := error(nil) + var err error limit, err = strconv.Atoi(limitStr) if err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("limit parameter is not an integer"), nil) + api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("limit parameter is not an integer"), err, &alt) return } } servers, err := getDetailServers(inf.Tx.Tx, inf.User, hostName, physLocationID, util.CamelToSnakeCase(orderBy), limit, *inf.Version) - respVals := map[string]interface{}{ - "orderby": orderBy, - "limit": limit, - "size": len(servers), + if err != nil { + api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err, &alt) + } + resp := serverDetailResponse{ + Limit: limit, + OrderBy: orderBy, + Size: len(servers), } if inf.Version.Major <= 2 { - v11ServerList := []tc.ServerDetailV11{} + v11ServerList := make([]tc.ServerDetailV11, 0, resp.Size) for _, server := range servers { interfaces := server.ServerInterfaces routerHostName := "" @@ -93,24 +109,23 @@ func GetDetailParamHandler(w http.ResponseWriter, r *http.Request) { v11server := tc.ServerDetailV11{} v11server.ServerDetail, err = dbhelpers.GetServerDetailFromV4(server, inf.Tx.Tx) if err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to GetServerDetailFromV4: %w", err)) + api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to GetServerDetailFromV4: %w", err), &alt) return } v11server.RouterHostName = &routerHostName v11server.RouterPortName = &routerPortName legacyInterface, err := tc.V4InterfaceInfoToLegacyInterfaces(interfaces) if err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("converting to server detail v11: "+err.Error())) + api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, fmt.Errorf("converting to server detail v11: %w", err), &alt) return } v11server.LegacyInterfaceDetails = legacyInterface v11ServerList = append(v11ServerList, v11server) } - api.RespWriterVals(w, r, inf.Tx.Tx, respVals)(v11ServerList, err) - return + resp.Response = v11ServerList } else if inf.Version.Major <= 3 { - v3ServerList := []tc.ServerDetailV30{} + v3ServerList := make([]tc.ServerDetailV30, 0, resp.Size) for _, server := range servers { v3Server := tc.ServerDetailV30{} interfaces := server.ServerInterfaces @@ -123,25 +138,36 @@ func GetDetailParamHandler(w http.ResponseWriter, r *http.Request) { } v3Server.ServerDetail, err = dbhelpers.GetServerDetailFromV4(server, inf.Tx.Tx) if err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to GetServerDetailFromV4: %w", err)) + api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to GetServerDetailFromV4: %w", err), &alt) return } v3Server.RouterHostName = &routerHostName v3Server.RouterPortName = &routerPortName v3Interfaces, err := tc.V4InterfaceInfoToV3Interfaces(interfaces) if err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("converting to server detail v3: "+err.Error())) + api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, fmt.Errorf("converting to server detail v3: %w", err), &alt) return } v3Server.ServerInterfaces = &v3Interfaces v3ServerList = append(v3ServerList, v3Server) } - api.RespWriterVals(w, r, inf.Tx.Tx, respVals)(v3ServerList, err) - return + resp.Response = v3ServerList + } else { + api.WriteRespAlertNotFound(w, r) } - api.RespWriterVals(w, r, inf.Tx.Tx, respVals)(servers, err) + + alerts := api.CreateDeprecationAlerts(&alt) + api.WriteAlertsObj(w, r, http.StatusOK, alerts, resp) } +// AddWhereClauseAndQuery adds a WHERE clause to the query given in `q` (does +// NOT check for existing WHERE clauses or that the end of the string is the +// proper place to put one!) that limits the query results to those with the +// given hostname and/or Physical Location ID and, with orderByStr and limitStr +// appended (in that order), returns the result of querying the given +// transaction. +// Use an empty string for the hostname to not filter by hostname, use -1 as +// physLocationID to not filter by Physical Location. func AddWhereClauseAndQuery(tx *sql.Tx, q string, hostName string, physLocationID int, orderByStr string, limitStr string) (*sql.Rows, error) { if hostName != "" && physLocationID != -1 { q += ` WHERE server.host_name = $1::text AND server.phys_location = $2::bigint` + orderByStr + limitStr @@ -158,6 +184,40 @@ func AddWhereClauseAndQuery(tx *sql.Tx, q string, hostName string, physLocationI } } +const dataFetchQuery = `, +cg.name AS cachegroup, +cdn.name AS cdn_name, +ARRAY(select deliveryservice from deliveryservice_server where server = server.id), +server.domain_name, +server.guid, +server.host_name, +server.https_port, +server.ilo_ip_address, +server.ilo_ip_gateway, +server.ilo_ip_netmask, +server.ilo_password, +server.ilo_username, +(SELECT address FROM ip_address WHERE service_address = true AND family(address) = 4 AND server = server.id) AS service_ip, +(SELECT address FROM ip_address WHERE service_address = true AND family(address) = 6 AND server = server.id) AS service_ip6, +(SELECT gateway FROM ip_address WHERE service_address = true AND family(address) = 4 AND server = server.id) AS service_gateway, +(SELECT gateway FROM ip_address WHERE service_address = true AND family(address) = 6 AND server = server.id) AS service_gateway6, +(SELECT host(netmask(ip_address.address)) FROM ip_address WHERE service_address = true AND family(address) = 4 AND server = server.id) AS service_netmask, +(SELECT interface FROM ip_address WHERE service_address = true AND family(address) = 4 AND server = server.id) AS interface_name, +(SELECT mtu FROM interface WHERE server.id = interface.server AND interface.name = (SELECT interface FROM ip_address WHERE service_address = true AND family(address) = 4 AND server = server.id)) AS interface_mtu, +server.mgmt_ip_address, +server.mgmt_ip_gateway, +server.mgmt_ip_netmask, +server.offline_reason, +pl.name as phys_location, +(SELECT ARRAY_AGG(profile_name) FROM server_profile WHERE server_profile.server=server.id) AS profile_name, +server.rack, +st.name as status, +server.tcp_port, +t.name as server_type, +server.xmpp_id, +server.xmpp_passwd +` + func getDetailServers(tx *sql.Tx, user *auth.CurrentUser, hostName string, physLocationID int, orderBy string, limit int, reqVersion api.Version) ([]tc.ServerDetailV40, error) { allowedOrderByCols := map[string]string{ "": "", @@ -192,39 +252,6 @@ func getDetailServers(tx *sql.Tx, user *auth.CurrentUser, hostName string, physL return nil, errors.New("orderBy '" + orderBy + "' not permitted") } - dataFetchQuery := `, -cg.name AS cachegroup, -cdn.name AS cdn_name, -ARRAY(select deliveryservice from deliveryservice_server where server = server.id), -server.domain_name, -server.guid, -server.host_name, -server.https_port, -server.ilo_ip_address, -server.ilo_ip_gateway, -server.ilo_ip_netmask, -server.ilo_password, -server.ilo_username, -(SELECT address FROM ip_address WHERE service_address = true AND family(address) = 4 AND server = server.id) AS service_ip, -(SELECT address FROM ip_address WHERE service_address = true AND family(address) = 6 AND server = server.id) AS service_ip6, -(SELECT gateway FROM ip_address WHERE service_address = true AND family(address) = 4 AND server = server.id) AS service_gateway, -(SELECT gateway FROM ip_address WHERE service_address = true AND family(address) = 6 AND server = server.id) AS service_gateway6, -(SELECT host(netmask(ip_address.address)) FROM ip_address WHERE service_address = true AND family(address) = 4 AND server = server.id) AS service_netmask, -(SELECT interface FROM ip_address WHERE service_address = true AND family(address) = 4 AND server = server.id) AS interface_name, -(SELECT mtu FROM interface WHERE server.id = interface.server AND interface.name = (SELECT interface FROM ip_address WHERE service_address = true AND family(address) = 4 AND server = server.id)) AS interface_mtu, -server.mgmt_ip_address, -server.mgmt_ip_gateway, -server.mgmt_ip_netmask, -server.offline_reason, -pl.name as phys_location, -(SELECT ARRAY_AGG(profile_name) FROM server_profile WHERE server_profile.server=server.id) AS profile_name, -server.rack, -st.name as status, -server.tcp_port, -t.name as server_type, -server.xmpp_id, -server.xmpp_passwd -` queryFormatString := ` SELECT server.id @@ -247,42 +274,43 @@ JOIN type t ON server.type = t.id } idRows, err := AddWhereClauseAndQuery(tx, fmt.Sprintf(queryFormatString, ""), hostName, physLocationID, orderByStr, limitStr) if err != nil { - return nil, errors.New("querying delivery service eligible servers: " + err.Error()) + return nil, fmt.Errorf("querying delivery service eligible servers: %w", err) } - defer idRows.Close() + defer log.Close(idRows, "getting IDs for server details names") var serverIDs []int for idRows.Next() { var serverID *int err := idRows.Scan(&serverID) if err != nil { - return nil, errors.New("querying delivery service eligible server ids: " + err.Error()) + return nil, fmt.Errorf("querying delivery service eligible server ids: %w", err) } serverIDs = append(serverIDs, *serverID) } serversMap, err := dbhelpers.GetServersInterfaces(serverIDs, tx) if err != nil { - return nil, errors.New("unable to get server interfaces: " + err.Error()) + return nil, fmt.Errorf("unable to get server interfaces: %w", err) } rows, err := AddWhereClauseAndQuery(tx, fmt.Sprintf(queryFormatString, dataFetchQuery), hostName, physLocationID, orderByStr, limitStr) if err != nil { - return nil, errors.New("Error querying detail servers: " + err.Error()) + return nil, fmt.Errorf("querying detail servers: %w", err) } - defer rows.Close() + defer log.Close(rows, "getting server details data") sIDs := []int{} servers := []tc.ServerDetailV40{} - serviceAddress := util.StrPtr("") - service6Address := util.StrPtr("") - serviceGateway := util.StrPtr("") - service6Gateway := util.StrPtr("") - serviceNetmask := util.StrPtr("") - serviceInterface := util.StrPtr("") - serviceMtu := util.StrPtr("") + serviceAddress := new(string) + service6Address := new(string) + serviceGateway := new(string) + service6Gateway := new(string) + serviceNetmask := new(string) + serviceInterface := new(string) + serviceMtu := new(string) for rows.Next() { s := tc.ServerDetailV40{} - if err := rows.Scan(&s.ID, + err = rows.Scan( + &s.ID, &s.CacheGroup, &s.CDNName, pq.Array(&s.DeliveryServiceIDs), @@ -313,8 +341,10 @@ JOIN type t ON server.type = t.id &s.TCPPort, &s.Type, &s.XMPPID, - &s.XMPPPasswd); err != nil { - return nil, errors.New("Error scanning detail server: " + err.Error()) + &s.XMPPPasswd, + ) + if err != nil { + return nil, fmt.Errorf("srror scanning detail server: %w", err) } s.ServerInterfaces = []tc.ServerInterfaceInfoV40{} if interfacesMap, ok := serversMap[*s.ID]; ok { @@ -335,16 +365,16 @@ JOIN type t ON server.type = t.id rows, err = tx.Query(`SELECT serverid, description, val from hwinfo where serverid = ANY($1);`, pq.Array(sIDs)) if err != nil { - return nil, errors.New("Error querying detail servers hardware info: " + err.Error()) + return nil, fmt.Errorf("querying detail servers hardware info: %w", err) } - defer rows.Close() + defer log.Close(rows, "getting hwinfo data") hwInfos := map[int]map[string]string{} for rows.Next() { serverID := 0 desc := "" val := "" if err := rows.Scan(&serverID, &desc, &val); err != nil { - return nil, errors.New("Error scanning detail server hardware info: " + err.Error()) + return nil, fmt.Errorf("scanning detail server hardware info: %w", err) } hwInfo, ok := hwInfos[serverID] From f92f1e32cf0faab5e45e72b968d3811e288daacd Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Tue, 10 May 2022 15:12:45 -0600 Subject: [PATCH 2/6] Update documentation - add deprecation notices to v3, remove from v4 --- docs/source/api/v3/servers_details.rst | 3 + docs/source/api/v4/servers_details.rst | 192 ------------------ .../clients/python/trafficops/tosession.py | 2 +- 3 files changed, 4 insertions(+), 193 deletions(-) delete mode 100644 docs/source/api/v4/servers_details.rst diff --git a/docs/source/api/v3/servers_details.rst b/docs/source/api/v3/servers_details.rst index 4dfe2e96ec..f1e746aa45 100644 --- a/docs/source/api/v3/servers_details.rst +++ b/docs/source/api/v3/servers_details.rst @@ -20,6 +20,9 @@ ******************* Retrieves details of :ref:`tp-configure-servers`. +.. deprecated:: 3.1 + This endpoint has been removed from the latest version of the API, and clients are advised to use :ref:`to-api-v3-servers` instead. + ``GET`` ======= diff --git a/docs/source/api/v4/servers_details.rst b/docs/source/api/v4/servers_details.rst deleted file mode 100644 index dc0a917b50..0000000000 --- a/docs/source/api/v4/servers_details.rst +++ /dev/null @@ -1,192 +0,0 @@ -.. -.. -.. Licensed under the Apache License, Version 2.0 (the "License"); -.. you may not use this file except in compliance with the License. -.. You may obtain a copy of the License at -.. -.. http://www.apache.org/licenses/LICENSE-2.0 -.. -.. Unless required by applicable law or agreed to in writing, software -.. distributed under the License is distributed on an "AS IS" BASIS, -.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -.. See the License for the specific language governing permissions and -.. limitations under the License. -.. - -.. _to-api-servers-details: - -******************* -``servers/details`` -******************* -Retrieves details of :ref:`tp-configure-servers`. - - -``GET`` -======= -:Auth. Required: Yes -:Roles Required: None -:Permissions Required: SERVER:READ, DELIVERY-SERVICE:READ, CDN:READ, PHYSICAL-LOCATION:READ, CACHE-GROUP:READ, TYPE:READ, PROFILE:READ -:Response Type: Array - -.. note:: On top of the response including the response key that is of type array it will also include the keys ``limit``, ``orderby``, and ``size``. - -Request Structure ------------------ -.. table:: Request Query Parameters - - +----------------+----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | Name | Required | Description | - +================+========================================+================================================================================================================================================================+ - | hostName | Required if no physLocationID provided | Return only the servers with this (short) hostname. Capitalization of "hostName" is important. | - +----------------+----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | physLocationID | Required if no hostName provided | Return only servers with this integral, unique identifier for the physical location where the server resides. Capitalization of "physLocationID" is important. | - +----------------+----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+ - -.. code-block:: http - :caption: Request Example - - GET /api/4.0/servers/details?hostName=edge HTTP/1.1 - User-Agent: python-requests/2.22.0 - Accept-Encoding: gzip, deflate - Accept: */* - Connection: keep-alive - Cookie: mojolicious=... - -Response Structure ------------------- -:limit: The maximum size of the ``response`` array, also indicative of the number of results per page using the pagination requested by the query parameters (if any) - this should be the same as the ``limit`` query parameter (if given) -:orderby: A string that names the field by which the elements of the ``response`` array are ordered - should be the same as the ``orderby`` request query parameter (if given) -:response: An array of objects, each of which represents the details of a given :ref:`Server `. - - :cachegroup: A string that is the :ref:`name of the Cache Group ` to which the server belongs - :cdnName: Name of the CDN to which the server belongs - :deliveryservices: An array of integral, unique identifiers for :term:`Delivery Services` to which this server belongs - :domainName: The domain part of the server's :abbr:`FQDN (Fully Qualified Domain Name)` - :guid: An identifier used to uniquely identify the server - - .. note:: This is a legacy key which only still exists for compatibility reasons - it should always be ``null`` - - :hostName: The (short) hostname of the server - :httpsPort: The port on which the server listens for incoming HTTPS connections/requests - :id: An integral, unique identifier for this server - :iloIpAddress: The IPv4 address of the server's :abbr:`ILO (Integrated Lights-Out)` service\ [1]_ - :iloIpGateway: The IPv4 gateway address of the server's :abbr:`ILO (Integrated Lights-Out)` service\ [1]_ - :iloIpNetmask: The IPv4 subnet mask of the server's :abbr:`ILO (Integrated Lights-Out)` service\ [1]_ - :iloPassword: The password of the of the server's :abbr:`ILO (Integrated Lights-Out)` service user\ [1]_ - displays as simply ``******`` if the currently logged-in user does not have the 'admin' or 'operations' :term:`Role(s) ` - :iloUsername: The user name for the server's :abbr:`ILO (Integrated Lights-Out)` service\ [1]_ - :interfaces: An array of interface and IP address information - - :max_bandwidth: The maximum allowed bandwidth for this interface to be considered "healthy" by Traffic Monitor. This has no effect if `monitor` is not true. Values are in kb/s. The value `0` means "no limit". - :monitor: A boolean indicating if Traffic Monitor should monitor this interface - :mtu: The :abbr:`MTU (Maximum Transmission Unit)` to configure for ``interfaceName`` - - .. seealso:: `The Wikipedia article on Maximum Transmission Unit `_ - - :name: The network interface name used by the server. - - :ipAddresses: An array of the IP address information for the interface - - :address: The IPv4 or IPv6 address and subnet mask of the server - applicable for the interface ``name`` - :gateway: The IPv4 or IPv6 gateway address of the server - applicable for the interface ``name`` - :service_address: A boolean determining if content will be routed to the IP address - - :routerHostName: The human-readable name of the router responsible for reaching this server's interface. - :routerPortName: The human-readable name of the port used by the router responsible for reaching this server's interface. - - :mgmtIpAddress: The IPv4 address of the server's management port - :mgmtIpGateway: The IPv4 gateway of the server's management port - :mgmtIpNetmask: The IPv4 subnet mask of the server's management port - :offlineReason: A user-entered reason why the server is in ADMIN_DOWN or OFFLINE status - :physLocation: The name of the physical location where the server resides - :profileNames: List of :ref:`profile-name` of the :term:`Profiles` used by this server - :rack: A string indicating "server rack" location - :status: The status of the server - - .. seealso:: :ref:`health-proto` - - :tcpPort: The port on which this server listens for incoming TCP connections - - .. note:: This is typically thought of as synonymous with "HTTP port", as the port specified by ``httpsPort`` may also be used for incoming TCP connections. - - :type: The name of the 'type' of this server - :xmppId: A system-generated UUID used to generate a server hashId for use in Traffic Router's consistent hashing algorithm. This value is set when a server is created and cannot be changed afterwards. - :xmppPasswd: The password used in XMPP communications with the server - -:size: The page number - if pagination was requested in the query parameters, else ``0`` to indicate no pagination - of the results represented by the ``response`` array. This is named "size" for legacy reasons - -.. code-block:: http - :caption: Response Example - - HTTP/1.1 200 OK - Access-Control-Allow-Credentials: true - Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Set-Cookie, Cookie - Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE - Access-Control-Allow-Origin: * - Content-Encoding: gzip - Content-Type: application/json - Set-Cookie: mojolicious=...; Path=/; Expires=Mon, 24 Feb 2020 01:27:36 GMT; Max-Age=3600; HttpOnly - Whole-Content-Sha512: HW2F3CEpohNAvNlEDhUfXmtwpEka4dwUWFVUSSjW98aXiv10vI6ysRIcC2P9huabCz5fdHqY3tp0LR4ekwEHqw== - X-Server-Name: traffic_ops_golang/ - Date: Mon, 24 Feb 2020 00:27:36 GMT - Content-Length: 493 - - { - "limit": 1000, - "orderby": "hostName", - "response": [ - { - "cachegroup": "CDN_in_a_Box_Edge", - "cdnName": "CDN-in-a-Box", - "deliveryservices": [ - 1 - ], - "domainName": "infra.ciab.test", - "guid": null, - "hardwareInfo": null, - "hostName": "edge", - "httpsPort": 443, - "id": 5, - "iloIpAddress": "", - "iloIpGateway": "", - "iloIpNetmask": "", - "iloPassword": "", - "iloUsername": "", - "mgmtIpAddress": "", - "mgmtIpGateway": "", - "mgmtIpNetmask": "", - "offlineReason": "", - "physLocation": "Apachecon North America 2018", - "profileNames": ["ATS_EDGE_TIER_CACHE"], - "rack": "", - "status": "REPORTED", - "tcpPort": 80, - "type": "EDGE", - "xmppId": "edge", - "xmppPasswd": "", - "interfaces": [ - { "ipAddresses": [ - { - "address": "172.16.239.100", - "gateway": "172.16.239.1", - "service_address": true - }, - { - "address": "fc01:9400:1000:8::100", - "gateway": "fc01:9400:1000:8::1", - "service_address": true - } - ], - "max_bandwidth": 0, - "monitor": true, - "mtu": 1500, - "name": "eth0", - "routerHostName": "", - "routerPortName": "" - } - ] - } - ], - "size": 1 - } - -.. [1] For more information see the `Wikipedia page on Lights-Out management `_\ . diff --git a/traffic_control/clients/python/trafficops/tosession.py b/traffic_control/clients/python/trafficops/tosession.py index f907911f04..b9bafb4e8a 100644 --- a/traffic_control/clients/python/trafficops/tosession.py +++ b/traffic_control/clients/python/trafficops/tosession.py @@ -1652,7 +1652,7 @@ def get_server_delivery_services(self, server_id=None): def get_server_details(self, name=None): """ Get servers/details - :ref:`to-api-servers-details` + :ref:`to-api-v3-servers-details` :rtype: Tuple[Union[Dict[str, Any], List[Dict[str, Any]]], requests.Response] :raises: Union[LoginError, OperationError] """ From f4ba16dbab0048f0a025043a6bde2efc16b306b7 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Tue, 10 May 2022 15:25:07 -0600 Subject: [PATCH 3/6] Update clients - remove from v4, add deprecation notices to v3 --- .../clients/python/trafficops/tosession.py | 5 ++- traffic_ops/testing/api/v4/servers_test.go | 33 ------------------- traffic_ops/v3-client/server.go | 4 +++ traffic_ops/v4-client/server.go | 11 ------- 4 files changed, 8 insertions(+), 45 deletions(-) diff --git a/traffic_control/clients/python/trafficops/tosession.py b/traffic_control/clients/python/trafficops/tosession.py index b9bafb4e8a..150b51311d 100644 --- a/traffic_control/clients/python/trafficops/tosession.py +++ b/traffic_control/clients/python/trafficops/tosession.py @@ -1648,13 +1648,16 @@ def get_server_delivery_services(self, server_id=None): :raises: Union[LoginError, OperationError] """ - @api_request('get', 'servers/details?hostName={name}', ('3.0','4.0',)) + @api_request('get', 'servers/details?hostName={name}', ('3.0',)) def get_server_details(self, name=None): """ Get servers/details :ref:`to-api-v3-servers-details` :rtype: Tuple[Union[Dict[str, Any], List[Dict[str, Any]]], requests.Response] :raises: Union[LoginError, OperationError] + + .. deprecated:: 3.0 + The endpoint this represents has been removed from APIv4 and clients should use get_servers instead. """ @api_request('post', 'servercheck', ('3.0',)) diff --git a/traffic_ops/testing/api/v4/servers_test.go b/traffic_ops/testing/api/v4/servers_test.go index 4adf0d8345..a6f1680a4d 100644 --- a/traffic_ops/testing/api/v4/servers_test.go +++ b/traffic_ops/testing/api/v4/servers_test.go @@ -42,7 +42,6 @@ func TestServers(t *testing.T) { header.Set(rfc.IfUnmodifiedSince, time) UpdateTestServers(t) UpdateTestServersWithHeaders(t, header) - GetTestServersDetails(t) GetTestServers(t) GetTestServersIMSAfterChange(t, header) GetTestServersQueryParameters(t) @@ -657,38 +656,6 @@ func GetTestServers(t *testing.T) { } } -func GetTestServersDetails(t *testing.T) { - opts := client.NewRequestOptions() - for _, server := range testData.Servers { - if server.HostName == nil { - t.Errorf("found server with nil hostname: %+v", server) - continue - } - opts.QueryParameters.Set("hostName", *server.HostName) - resp, _, err := TOSession.GetServersDetails(opts) - if err != nil { - t.Errorf("cannot get Server Details: %v - alerts: %+v", err, resp.Alerts) - } - if len(resp.Response) == 0 { - t.Fatal("no servers in response, quitting") - } - if len(resp.Response[0].ServerInterfaces) == 0 { - t.Fatalf("no interfaces to check, quitting") - } - if len(server.Interfaces) == 0 { - t.Fatalf("no interfaces to check, quitting") - } - - // just check the first interface for noe - if resp.Response[0].ServerInterfaces[0].RouterHostName != server.Interfaces[0].RouterHostName { - t.Errorf("expected router host name to be %s, but got %s", server.Interfaces[0].RouterHostName, resp.Response[0].ServerInterfaces[0].RouterHostName) - } - if resp.Response[0].ServerInterfaces[0].RouterPortName != server.Interfaces[0].RouterPortName { - t.Errorf("expected router port to be %s, but got %s", server.Interfaces[0].RouterPortName, resp.Response[0].ServerInterfaces[0].RouterPortName) - } - } -} - func GetTestServersQueryParameters(t *testing.T) { dses, _, err := TOSession.GetDeliveryServices(client.RequestOptions{QueryParameters: url.Values{"xmlId": []string{"ds1"}}}) if err != nil { diff --git a/traffic_ops/v3-client/server.go b/traffic_ops/v3-client/server.go index 13b2fad97c..c7d026c3e6 100644 --- a/traffic_ops/v3-client/server.go +++ b/traffic_ops/v3-client/server.go @@ -204,6 +204,10 @@ func (to *Session) GetFirstServer(params *url.Values, header http.Header) (tc.Se return firstServer, reqInf, err } +// GetServerDetailsByHostNameWithHdr retrieves the "details" of all servers with +// the given hostname. +// Deprecated: Server "details" as a concept have been removed from the latest +// version of the API, and clients should use GetServersWithHdr instead. func (to *Session) GetServerDetailsByHostNameWithHdr(hostName string, header http.Header) ([]tc.ServerDetailV30, toclientlib.ReqInf, error) { v := url.Values{} v.Add("hostName", hostName) diff --git a/traffic_ops/v4-client/server.go b/traffic_ops/v4-client/server.go index 59d3009488..ab56c42c47 100644 --- a/traffic_ops/v4-client/server.go +++ b/traffic_ops/v4-client/server.go @@ -29,9 +29,6 @@ const ( // apiServers is the API version-relative path to the /servers API // endpoint. apiServers = "/servers" - // apiServersDetails is the API version-relative path to the - // /servers/details API endpoint. - apiServersDetails = "/servers/details" ) func needAndCanFetch(id *int, name *string) bool { @@ -127,14 +124,6 @@ func (to *Session) GetServers(opts RequestOptions) (tc.ServersV4Response, toclie return data, reqInf, err } -// GetServersDetails retrieves the Server Details of the Server with the given -// (short) Hostname. -func (to *Session) GetServersDetails(opts RequestOptions) (tc.ServersV4DetailResponse, toclientlib.ReqInf, error) { - var data tc.ServersV4DetailResponse - reqInf, err := to.get(apiServersDetails, opts, &data) - return data, reqInf, err -} - // DeleteServer deletes the Server with the given ID. func (to *Session) DeleteServer(id int, opts RequestOptions) (tc.Alerts, toclientlib.ReqInf, error) { route := fmt.Sprintf("%s/%d", apiServers, id) From ac52f9838d5f7d594c1f2c569699258eee42b8e3 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Tue, 10 May 2022 15:26:58 -0600 Subject: [PATCH 4/6] Update CHANGELOG --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db7aa0e5de..dc0d645e74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,11 +17,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Traffic Monitor: Add support for `access.log` to TM. - Added functionality for login to provide a Bearer token and for that token to be later used for authorization. - [Traffic Ops] Added support for backend configurations so that Traffic Ops can act as a reverse proxy for these services [#6754](https://github.com/apache/trafficcontrol/pull/6754). -- Added functionality for CDN locks, so that they can be shared amongst a list of specified usernames. +- Added functionality for CDN locks, so that they can be shared amongst a list of specified usernames. - [Traffic Ops | Traffic Go Clients | T3C] Add additional timestamp fields to server for queuing and dequeueing config and revalidate updates. - Added layered profile feature to 4.0 for `GET` /servers/, `POST` /servers/, `PUT` /servers/{id} and `DELETE` /servers/{id}. - Added a Traffic Ops endpoint and Traffic Portal page to view all CDNi configuration update requests and approve or deny. -- Added layered profile feature to 4.0 for `GET` /servers/details. - Added layered profile feature to 4.0 for `GET` /deliveryservices/{id}/servers/ and /deliveryservices/{id}/servers/eligible. ### Fixed @@ -56,6 +55,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Replace `forever` with `pm2` for process management of the traffic portal node server to remediate security issues. - Removed the Traffic Monitor `peer_polling_protocol` option. Traffic Monitor now just uses hostnames to request peer states, which can be handled via IPv4 or IPv6 depending on the underlying IP version in use. - Dropped CentOS 8 support +- The `/servers/details` endpoint of the Traffic Ops API has been dropped in version 4.0, and marked deprecated in earlier versions. ### Changed - [#6654](https://github.com/apache/trafficcontrol/issues/6654) Traffic Monitor now uses the TO API 4.0 by default and falls back to 3.1 From b524bc8fbe5a3257fa212f4b78df0e5ed536f7d2 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Fri, 13 May 2022 15:34:20 -0600 Subject: [PATCH 5/6] fix unable to get server details by hostname if physloc not also provided --- traffic_ops/traffic_ops_golang/server/detail.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/server/detail.go b/traffic_ops/traffic_ops_golang/server/detail.go index c90ebaf6bf..eb1c749a98 100644 --- a/traffic_ops/traffic_ops_golang/server/detail.go +++ b/traffic_ops/traffic_ops_golang/server/detail.go @@ -169,13 +169,13 @@ func GetDetailParamHandler(w http.ResponseWriter, r *http.Request) { // Use an empty string for the hostname to not filter by hostname, use -1 as // physLocationID to not filter by Physical Location. func AddWhereClauseAndQuery(tx *sql.Tx, q string, hostName string, physLocationID int, orderByStr string, limitStr string) (*sql.Rows, error) { - if hostName != "" && physLocationID != -1 { + if hostName != "" && physLocationID != 0 { q += ` WHERE server.host_name = $1::text AND server.phys_location = $2::bigint` + orderByStr + limitStr return tx.Query(q, hostName, physLocationID) } else if hostName != "" { q += ` WHERE server.host_name = $1::text` + orderByStr + limitStr return tx.Query(q, hostName) - } else if physLocationID != -1 { + } else if physLocationID != 0 { q += ` WHERE server.phys_location = $1::int` + orderByStr + limitStr return tx.Query(q, physLocationID) } else { @@ -344,7 +344,7 @@ JOIN type t ON server.type = t.id &s.XMPPPasswd, ) if err != nil { - return nil, fmt.Errorf("srror scanning detail server: %w", err) + return nil, fmt.Errorf("scanning detail server: %w", err) } s.ServerInterfaces = []tc.ServerInterfaceInfoV40{} if interfacesMap, ok := serversMap[*s.ID]; ok { From 6fd1093b20af4737646e1771df541b831f757a41 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Tue, 17 May 2022 13:17:54 -0600 Subject: [PATCH 6/6] Fix embedded 'response' object --- .../traffic_ops_golang/server/detail.go | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/server/detail.go b/traffic_ops/traffic_ops_golang/server/detail.go index eb1c749a98..a327a40595 100644 --- a/traffic_ops/traffic_ops_golang/server/detail.go +++ b/traffic_ops/traffic_ops_golang/server/detail.go @@ -37,13 +37,6 @@ import ( "github.com/lib/pq" ) -type serverDetailResponse struct { - Limit int `json:"limit"` - OrderBy string `json:"orderby"` - Response interface{} `json:"response"` - Size int `json:"size"` -} - // GetDetailParamHandler handles GET requests to /servers/details (the name // includes "Param" for legacy reasons). // @@ -89,14 +82,11 @@ func GetDetailParamHandler(w http.ResponseWriter, r *http.Request) { if err != nil { api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err, &alt) } - resp := serverDetailResponse{ - Limit: limit, - OrderBy: orderBy, - Size: len(servers), - } + var resp interface{} + size := len(servers) if inf.Version.Major <= 2 { - v11ServerList := make([]tc.ServerDetailV11, 0, resp.Size) + v11ServerList := make([]tc.ServerDetailV11, 0, size) for _, server := range servers { interfaces := server.ServerInterfaces routerHostName := "" @@ -123,9 +113,9 @@ func GetDetailParamHandler(w http.ResponseWriter, r *http.Request) { v11ServerList = append(v11ServerList, v11server) } - resp.Response = v11ServerList + resp = v11ServerList } else if inf.Version.Major <= 3 { - v3ServerList := make([]tc.ServerDetailV30, 0, resp.Size) + v3ServerList := make([]tc.ServerDetailV30, 0, size) for _, server := range servers { v3Server := tc.ServerDetailV30{} interfaces := server.ServerInterfaces @@ -151,13 +141,18 @@ func GetDetailParamHandler(w http.ResponseWriter, r *http.Request) { v3Server.ServerInterfaces = &v3Interfaces v3ServerList = append(v3ServerList, v3Server) } - resp.Response = v3ServerList + resp = v3ServerList } else { api.WriteRespAlertNotFound(w, r) + return } - alerts := api.CreateDeprecationAlerts(&alt) - api.WriteAlertsObj(w, r, http.StatusOK, alerts, resp) + api.WriteRespVals(w, r, resp, map[string]interface{}{ + "alerts": api.CreateDeprecationAlerts(&alt).Alerts, + "limit": limit, + "orderby": orderBy, + "size": size, + }) } // AddWhereClauseAndQuery adds a WHERE clause to the query given in `q` (does