Skip to content

Commit

Permalink
Add asn field to server (#7023)
Browse files Browse the repository at this point in the history
* Adding ASN to Server struct

* doc changes

* fix typo

* add godoc

* address code review comments

* address code review changes

* address code review comments

* remove extra doc line
  • Loading branch information
srijeet0406 authored Sep 7, 2022
1 parent eeaee06 commit 5e8255f
Show file tree
Hide file tree
Showing 15 changed files with 177 additions and 65 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
- [#7032](https://github.com/apache/trafficcontrol/issues/7032) Add t3c-apply flag to use local ATS version for config generation rather than Server package Parameter, to allow managing the ATS OS package via external tools. See 'man t3c-apply' and 'man t3c-generate' for details.

- [Traffic Monitor] Added logging for `ipv4Availability` and `ipv6Availability` in TM.
- [Traffic Ops] Added the `ASN` field in TO Server struct, which provides the ability to query servers by `ASN`.

### Changed
- Traffic Portal now obscures sensitive text in Delivery Service "Raw Remap" fields, private SSL keys, "Header Rewrite" rules, and ILO interface passwords by default.
Expand Down
2 changes: 1 addition & 1 deletion cache-config/t3cutil/toreq/clientfuncs.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func (cl *TOClient) GetServerByHostName(serverHostName string, reqHdr http.Heade
if len(toServers.Response) < 1 {
return errors.New("getting server name '" + serverHostName + "' from Traffic Ops '" + torequtil.MaybeIPStr(reqInf.RemoteAddr) + "': no servers returned")
}
asv, err := serverToLatest(&toServers.Response[0])
asv, err := serverToLatest(&toServers.Response[0].ServerV40)
if err != nil {
return errors.New("converting server to latest version: " + err.Error())
}
Expand Down
8 changes: 6 additions & 2 deletions cache-config/t3cutil/toreq/conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ import (
)

func serversToLatest(svs tc.ServersV4Response) ([]atscfg.Server, error) {
return atscfg.ToServers(svs.Response), nil
serversV40 := make([]tc.ServerV40, 0)
for _, srv := range svs.Response {
serversV40 = append(serversV40, srv.ServerV40)
}
return atscfg.ToServers(serversV40), nil
}

func serverToLatest(oldSv *tc.ServerV40) (*atscfg.Server, error) {
Expand Down Expand Up @@ -218,7 +222,7 @@ func (cl *TOClient) GetServersCompat(opts toclient.RequestOptions) (tc.ServersV4
if err != nil {
return tc.ServersV4Response{}, reqInf, errors.New("converting server from possible legacy format: " + err.Error())
}
resp.Response = append(resp.Response, newSv)
resp.Response = append(resp.Response, tc.ServerV41{ServerV40: newSv})
}
return resp, reqInf, nil
}
Expand Down
12 changes: 12 additions & 0 deletions docs/source/api/v4/servers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ Request Structure
| | | the first page is 1. If ``offset`` was defined, this query parameter has no effect. ``limit`` must be defined to |
| | | make use of ``page``. |
+----------------+----------+-------------------------------------------------------------------------------------------------------------------+
| asn | no | Return only the servers that have a cachegroup matching the provided ASN. |
| | | |
| | | .. versionadded:: 4.1 |
+----------------+----------+-------------------------------------------------------------------------------------------------------------------+


.. code-block:: http
:caption: Request Example
Expand All @@ -81,6 +86,9 @@ Request Structure
Response Structure
------------------
:asns: The :abbr:`ASN (Autonomous System Number)` associated with the cachegroups of the current server.

.. versionadded:: 4.1
:cachegroup: A string that is the :ref:`name of the Cache Group <cache-group-name>` to which the server belongs
:cachegroupId: An integer that is the :ref:`ID of the Cache Group <cache-group-id>` to which the server belongs
:cdnId: The integral, unique identifier of the CDN to which the server belongs
Expand Down Expand Up @@ -225,6 +233,10 @@ Response Structure
"routerHostName": "",
"routerPortName": ""
}
],
"asns": [
1,
2
]
}],
"summary": {
Expand Down
2 changes: 1 addition & 1 deletion infrastructure/cdn-in-a-box/enroller/enroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ func enrollServer(toSession *session, r io.Reader) error {
return err
}

alerts, _, err := toSession.CreateServer(s, client.RequestOptions{})
alerts, _, err := toSession.CreateServer(tc.ServerV4{ServerV40: s}, client.RequestOptions{})
if err != nil {
err = fmt.Errorf("error creating Server: %v - alerts: %+v", err, alerts.Alerts)
log.Infoln(err)
Expand Down
10 changes: 8 additions & 2 deletions lib/go-tc/servers.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import (

// ServersV4Response is the format of a response to a GET request for API v4.x /servers.
type ServersV4Response struct {
Response []ServerV40 `json:"response"`
Response []ServerV41 `json:"response"`
Summary struct {
Count uint64 `json:"count"`
} `json:"summary"`
Expand Down Expand Up @@ -1019,6 +1019,12 @@ func UpdateServerPropertiesV40(profileNames []string, properties CommonServerPro
}
}

// ServerV41 is the representation of a Server in version 4.1 of the Traffic Ops API.
type ServerV41 struct {
ServerV40
ASNs []int64 `json:"asns"`
}

// ServerV40 is the representation of a Server in version 4.0 of the Traffic Ops API.
type ServerV40 struct {
Cachegroup *string `json:"cachegroup" db:"cachegroup"`
Expand Down Expand Up @@ -1066,7 +1072,7 @@ type ServerV40 struct {

// ServerV4 is the representation of a Server in the latest minor version of
// version 4 of the Traffic Ops API.
type ServerV4 = ServerV40
type ServerV4 = ServerV41

// ServerV30 is the representation of a Server in version 3 of the Traffic Ops API.
type ServerV30 struct {
Expand Down
2 changes: 1 addition & 1 deletion traffic_monitor/towrap/towrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ func (s TrafficOpsSessionThreadsafe) fetchServerByHostname(hostName string) (tc.
for i, srv := range resp.Response {
num = i
if srv.CDNName != nil && srv.HostName != nil && *srv.HostName == hostName {
server = srv
server = srv.ServerV40
found = true
break
}
Expand Down
65 changes: 53 additions & 12 deletions traffic_ops/testing/api/v4/servers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,24 @@ import (
)

func TestServers(t *testing.T) {
WithObjs(t, []TCObj{CDNs, Types, Tenants, Users, Parameters, Profiles, Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers, Topologies, ServiceCategories, DeliveryServices, DeliveryServiceServerAssignments}, func() {
WithObjs(t, []TCObj{CDNs, Types, Tenants, Users, Parameters, Profiles, Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers, Topologies, ServiceCategories, DeliveryServices, DeliveryServiceServerAssignments, ASN}, func() {

currentTime := time.Now().UTC().Add(-15 * time.Second)
currentTimeRFC := currentTime.Format(time.RFC1123)
tomorrow := currentTime.AddDate(0, 0, 1).Format(time.RFC1123)

setupCacheGroupWithASN(t)
methodTests := utils.V4TestCase{
"GET": {
"NOT MODIFIED when NO CHANGES made": {
ClientSession: TOSession,
RequestOpts: client.RequestOptions{Header: http.Header{rfc.IfModifiedSince: {tomorrow}}},
Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusNotModified)),
},
"OK when CORRECT ASN parameter": {
ClientSession: TOSession,
RequestOpts: client.RequestOptions{QueryParameters: url.Values{"asn": {"1111"}}},
Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseLengthGreaterOrEqual(1)),
},
"OK when VALID HOSTNAME parameter": {
ClientSession: TOSession,
RequestOpts: client.RequestOptions{QueryParameters: url.Values{"hostName": {"atlanta-edge-01"}}},
Expand All @@ -58,6 +63,17 @@ func TestServers(t *testing.T) {
Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseLengthGreaterOrEqual(1),
validateServerFields(map[string]interface{}{"CachegroupID": GetCacheGroupId(t, "cachegroup1")()})),
},
"OK when VALID ASN parameter": {
ClientSession: TOSession,
RequestOpts: client.RequestOptions{QueryParameters: url.Values{"asn": {"1111"}}},
Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseLengthGreaterOrEqual(1),
validateServerFields(map[string]interface{}{"Cachegroup": "topology-mid-cg-01"})),
},
"EMPTY RESPONSE when INVALID ASN parameter": {
ClientSession: TOSession,
RequestOpts: client.RequestOptions{QueryParameters: url.Values{"asn": {"5555"}}},
Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseHasLength(0)),
},
"OK when VALID CACHEGROUPNAME parameter": {
ClientSession: TOSession,
RequestOpts: client.RequestOptions{QueryParameters: url.Values{"cachegroupName": {"topology-mid-cg-01"}}},
Expand Down Expand Up @@ -384,10 +400,35 @@ func TestServers(t *testing.T) {
})
}

func setupCacheGroupWithASN(t *testing.T) {
// Add a new asn for one of the cachegroups
cgResp, _, err := TOSession.GetCacheGroups(client.RequestOptions{QueryParameters: url.Values{"name": {"topology-mid-cg-01"}}})
if err != nil {
t.Fatalf("couldn't get cachegroups: %v", err)
}
if len(cgResp.Response) != 1 {
t.Fatalf("expected 1 cachegroup, but got %d", len(cgResp.Response))
}
if cgResp.Response[0].ID == nil {
t.Fatalf("ID of cachegroup is nil")
}

asn := tc.ASN{
ASN: 1111,
Cachegroup: "topology-mid-cg-01",
CachegroupID: *cgResp.Response[0].ID,
}

_, _, err = TOSession.CreateASN(asn, client.NewRequestOptions())
if err != nil {
t.Fatalf("couldn't create ASN: %v", err)
}
}

func validateServerFields(expectedResp map[string]interface{}) utils.CkReqFunc {
return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
assert.RequireNotNil(t, resp, "Expected response to not be nil.")
serverResp := resp.([]tc.ServerV40)
serverResp := resp.([]tc.ServerV41)
for field, expected := range expectedResp {
for _, server := range serverResp {
switch field {
Expand Down Expand Up @@ -460,7 +501,7 @@ func validateServerFieldsForUpdate(hostname string, expectedResp map[string]inte
func validateExpectedServers(expectedHostnames []string) utils.CkReqFunc {
return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
assert.RequireNotNil(t, resp, "Expected response to not be nil.")
serverResp := resp.([]tc.ServerV40)
serverResp := resp.([]tc.ServerV41)
var notInResponse []string
serverMap := make(map[string]struct{})
for _, server := range serverResp {
Expand All @@ -479,7 +520,7 @@ func validateExpectedServers(expectedHostnames []string) utils.CkReqFunc {
func validateServerTypeIsNotMid() utils.CkReqFunc {
return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
assert.RequireNotNil(t, resp, "Expected response to not be nil.")
serverResp := resp.([]tc.ServerV40)
serverResp := resp.([]tc.ServerV41)
for _, server := range serverResp {
assert.RequireNotNil(t, server.HostName, "Expected server host name to not be nil.")
assert.NotEqual(t, server.Type, tc.CacheTypeMid.String(), "Expected to find no %s-typed servers but found server %s", tc.CacheTypeMid, *server.HostName)
Expand All @@ -490,7 +531,7 @@ func validateServerTypeIsNotMid() utils.CkReqFunc {
func validateServerPagination(paginationParam string) utils.CkReqFunc {
return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
assert.RequireNotNil(t, resp, "Expected response to not be nil.")
paginationResp := resp.([]tc.ServerV40)
paginationResp := resp.([]tc.ServerV41)
opts := client.NewRequestOptions()
opts.QueryParameters.Set("orderby", "id")
respBase, _, err := TOSession.GetServers(opts)
Expand Down Expand Up @@ -556,16 +597,16 @@ func UpdateTestServerStatusLastUpdated(t *testing.T) {
assert.RequireNoError(t, err, "Cannot get Server by hostname '%s': %v - alerts %+v", hostName, err, resp.Alerts)
assert.RequireGreaterOrEqual(t, len(resp.Response), 1, "Expected at least one server to exist by hostname '%s'", hostName)
assert.RequireNotNil(t, resp.Response[0].StatusLastUpdated, "Traffic Ops returned a representation for a server with null or undefined Status Last Updated time")
originalServer := resp.Response[0]
originalServer := resp.Response[0].ServerV40

// Perform an update with no changes to status
alerts, _, err := TOSession.UpdateServer(*originalServer.ID, originalServer, client.RequestOptions{})
alerts, _, err := TOSession.UpdateServer(*originalServer.ID, tc.ServerV4{ServerV40: originalServer}, client.RequestOptions{})
assert.RequireNoError(t, err, "Cannot UPDATE Server by ID %d (hostname '%s'): %v - alerts: %+v", *originalServer.ID, hostName, err, alerts)

resp, _, err = TOSession.GetServers(opts)
assert.RequireNoError(t, err, "Cannot get Server by hostname '%s': %v - alerts %+v", hostName, err, resp.Alerts)
assert.RequireGreaterOrEqual(t, len(resp.Response), 1, "Expected at least one server to exist by hostname '%s'", hostName)
respServer := resp.Response[0]
respServer := resp.Response[0].ServerV40
assert.RequireNotNil(t, respServer.StatusLastUpdated, "Traffic Ops returned a representation for a server with null or undefined Status Last Updated time")
assert.Equal(t, *originalServer.StatusLastUpdated, *respServer.StatusLastUpdated, "Since status didnt change, no change in 'StatusLastUpdated' time was expected. "+
"old value: %v, new value: %v", *originalServer.StatusLastUpdated, *respServer.StatusLastUpdated)
Expand All @@ -574,13 +615,13 @@ func UpdateTestServerStatusLastUpdated(t *testing.T) {
newStatusID := GetStatusID(t, "ONLINE")()
originalServer.StatusID = &newStatusID

alerts, _, err = TOSession.UpdateServer(*originalServer.ID, originalServer, client.RequestOptions{})
alerts, _, err = TOSession.UpdateServer(*originalServer.ID, tc.ServerV4{ServerV40: originalServer}, client.RequestOptions{})
assert.RequireNoError(t, err, "Cannot UPDATE Server by ID %d (hostname '%s'): %v - alerts: %+v", *originalServer.ID, hostName, err, alerts)

resp, _, err = TOSession.GetServers(opts)
assert.RequireNoError(t, err, "Cannot get Server by hostname '%s': %v - alerts %+v", hostName, err, resp.Alerts)
assert.RequireGreaterOrEqual(t, len(resp.Response), 1, "Expected at least one server to exist by hostname '%s'", hostName)
respServer = resp.Response[0]
respServer = resp.Response[0].ServerV40
assert.RequireNotNil(t, respServer.StatusLastUpdated, "Traffic Ops returned a representation for a server with null or undefined Status Last Updated time")
assert.NotEqual(t, *originalServer.StatusLastUpdated, *respServer.StatusLastUpdated, "Since status changed, expected 'StatusLastUpdated' to change. "+
"old value: %v, new value: %v", *originalServer.StatusLastUpdated, *respServer.StatusLastUpdated)
Expand Down Expand Up @@ -628,7 +669,7 @@ func UpdateDSGetServerDSID(t *testing.T) {

func CreateTestServers(t *testing.T) {
for _, server := range testData.Servers {
resp, _, err := TOSession.CreateServer(server, client.RequestOptions{})
resp, _, err := TOSession.CreateServer(tc.ServerV4{ServerV40: server}, client.RequestOptions{})
assert.RequireNoError(t, err, "Could not create server '%s': %v - alerts: %+v", *server.HostName, err, resp.Alerts)
}
}
Expand Down
10 changes: 5 additions & 5 deletions traffic_ops/testing/api/v4/serverupdatestatus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,19 @@ func TestServerUpdateStatus(t *testing.T) {
}{
{
"atlanta-edge-01",
&edge1cdn1,
&edge1cdn1.ServerV40,
},
{
"atlanta-edge-03",
&edge2cdn1,
&edge2cdn1.ServerV40,
},
{
"atlanta-mid-16",
&mid1cdn1,
&mid1cdn1.ServerV40,
},
{
"edge1-cdn2",
&edge1cdn2,
&edge1cdn2.ServerV40,
},
} {
opts.QueryParameters.Set("hostName", s.name)
Expand All @@ -130,7 +130,7 @@ func TestServerUpdateStatus(t *testing.T) {
t.Errorf("Expected exactly one server named '%s' to exist - actual: %d", s.name, len(resp.Response))
t.Logf("Testing will proceed with server: %+v", resp.Response[0])
}
*s.server = resp.Response[0]
*s.server = resp.Response[0].ServerV40
if s.server.ID == nil {
t.Fatalf("server '%s' was returned with nil ID", s.name)
}
Expand Down
2 changes: 1 addition & 1 deletion traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2024,7 +2024,7 @@ WHERE server.id = $2;`
}

// GetCommonServerPropertiesFromV4 converts ServerV40 to CommonServerProperties struct.
func GetCommonServerPropertiesFromV4(s tc.ServerV40, tx *sql.Tx) (tc.CommonServerProperties, error) {
func GetCommonServerPropertiesFromV4(s tc.ServerV41, tx *sql.Tx) (tc.CommonServerProperties, error) {
var id int
var desc string
if len(s.ProfileNames) == 0 {
Expand Down
2 changes: 2 additions & 0 deletions traffic_ops/traffic_ops_golang/routing/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
* 4.x API
*/

// GET servers
{Version: api.Version{Major: 4, Minor: 1}, Method: http.MethodGet, Path: `servers/?$`, Handler: server.Read, 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: 47219592853},
// Assign Multiple Server Capabilities
{Version: api.Version{Major: 4, Minor: 1}, Method: http.MethodPut, Path: `multiple_server_capabilities/?$`, Handler: server.AssignMultipleServerCapabilities, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ", "SERVER-CAPABILITY:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 40792419258},

Expand Down
Loading

0 comments on commit 5e8255f

Please sign in to comment.