[ 
https://issues.apache.org/jira/browse/TS-4723?focusedWorklogId=26920&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-26920
 ]

ASF GitHub Bot logged work on TS-4723:
--------------------------------------

                Author: ASF GitHub Bot
            Created on: 23/Aug/16 14:16
            Start Date: 23/Aug/16 14:16
    Worklog Time Spent: 10m 
      Work Description: Github user SolidWallOfCode commented on a diff in the 
pull request:

    https://github.com/apache/trafficserver/pull/843#discussion_r75874806
  
    --- Diff: plugins/experimental/carp/Common.cc ---
    @@ -0,0 +1,364 @@
    +
    +/** @file
    +
    +  Loads the CARP configuration
    +
    +  @section license License
    +
    +  Licensed to the Apache Software Foundation (ASF) under one
    +  or more contributor license agreements.  See the NOTICE file
    +  distributed with this work for additional information
    +  regarding copyright ownership.  The ASF licenses this file
    +  to you 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.
    + */
    +#include "Common.h"
    +
    +#include <arpa/inet.h>
    +#include <stdlib.h> 
    +#include <stdio.h> 
    +#include <errno.h> 
    +#include <netdb.h>
    +#include <string.h>
    +#include <memory.h>
    +
    +using namespace std;
    +
    +/************************************************************************/
    +void stringExplode(string str, string separator, vector<string>* results) 
    +{
    +    size_t found;
    +    found = str.find_first_of(separator);
    +    while (found != std::string::npos) 
    +    {
    +        if (found > 0) 
    +        {
    +            results->push_back(str.substr(0, found));
    +        }
    +        str = str.substr(found + 1);
    +        found = str.find_first_of(separator);
    +    }
    +    if (str.length() > 0) 
    +    {
    +        results->push_back(str);
    +    }
    +}
    +
    +/************************************************************************/
    +/*
    + Parse /proc/{pid}/net/tcp
    +   sl  local_address rem_address   st tx_queue rx_queue tr tm->when 
retrnsmt   uid  timeout inode                                                   
  
    +   0: 00000000:036B 00000000:0000 0A 00000000:00000000 00:00000000 
00000000     0        0 22272 1 ffff8801b7ecb700 299 0 0 2 -1                   
  
    +   1: 00000000:08AE 00000000:0000 0A 00000000:00000000 00:00000000 
00000000     0        0 10621 1 ffff8801b7b9a080 299 0 0 2 -1                   
  
    +   2: 00000000:006F 00000000:0000 0A 00000000:00000000 00:00000000 
00000000     0        0 22046 1 ffff8801b7b9b400 299 0 0 2 -1                   
  
    + Parse /proc/{pid}/net/tcp6
    + *   sl  local_address                         remote_address              
          st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode
    +   0: 00000000000000000000000000000000:08AE 
00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 
    0        0 10619 1 ffff8801b266d780 299 0 0 2 -1
    +   1: 00000000000000000000000000000000:006F 
00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 
    0        0 22051 1 ffff8801b3a51880 299 0 0 2 -1
    +   2: 00000000000000000000000000000000:0016 
00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 
    0        0 10656 1 ffff8801b266d040 299 0 0 2 -1
    +   3: 00000000000000000000000000000000:8E77 
00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 
    0        0 22300 1 ffff8801b2f5c780 299 0 0 2 -1
    + 
    + looking for local_address:PORT wher st = LISTEN (0x0A)
    + *  ./include/net/tcp_states.h
    + * enum {
    +    TCP_ESTABLISHED = 1,
    +    TCP_SYN_SENT,
    +    TCP_SYN_RECV,
    +    TCP_FIN_WAIT1,
    +    TCP_FIN_WAIT2,
    +    TCP_TIME_WAIT,
    +    TCP_CLOSE,
    +    TCP_CLOSE_WAIT,
    +    TCP_LAST_ACK,
    +    TCP_LISTEN,
    +    TCP_CLOSING,    
    +
    +    TCP_MAX_STATES  
    + */
    +
    +/************************************************************************/
    +
    +/* used internally                                                      */
    +bool
    +scanProcFileForPort(string sFilename, string sPid, unsigned int iPort)
    +{
    +  bool bMatch = false;
    +  TSFile file;
    +  int iLine = 0;
    +  bool bParseDone = false;
    +
    +  // attempt to open file
    +  TSDebug(DEBUG_TAG_INIT, "Trying to open proc file @ %s to determine 
listening ports", sFilename.c_str());
    +  file = TSfopen(sFilename.c_str(), "r");
    +  if (file == NULL) {
    +    TSError("Failed to open proc tcp file of %s.  Error=%s", 
sFilename.c_str(), strerror(errno));
    +    return false;
    +  }
    +
    +  TSDebug(DEBUG_TAG_INIT, "Successfully opened %s file", 
sFilename.c_str());
    +
    +  char buffer[1024];
    +  memset(buffer, 0, sizeof (buffer));
    +
    +  while (TSfgets(file, buffer, sizeof (buffer) - 1) != NULL && 
!bParseDone) {
    +    char *eol = strstr(buffer, "\r\n");
    +    if (!eol)
    +      eol = strchr(buffer, '\n');
    +    if (eol)
    +      *eol = 0; // remove ending LF or CRLF
    +
    +    //    TSDebug(DEBUG_TAG_INIT, "Parsing line: %s", buffer);
    +    ++iLine;
    +    if (iLine == 1) { // ignore header line
    +      continue;
    +    }
    +    vector<string> vParts;
    +    stringExplode(string(buffer), string(" "), &vParts);
    +    // part 3 is status
    +    unsigned int iStatus = 0;
    +    sscanf(vParts[3].c_str(), "%x", &iStatus);
    +    if (iStatus == TCP_LISTEN) {
    +      vector<string> vLocalParts;
    +      stringExplode(vParts[1], string(":"), &vLocalParts); // part 1 is 
localaddr
    +      unsigned int iLPort = 0;
    +      sscanf(vLocalParts[1].c_str(), "%x", &iLPort);
    +      TSDebug(DEBUG_TAG_INIT, "Found listening port %d", iLPort);
    +      if (iLPort == iPort) {
    +        string sLabel = string("socket:[" + vParts[9] + "]");
    +        // find inode in   /proc/{pid}/fd/
    +        string sDir = "/proc/" + string(sPid) + "/fd";
    +
    +        DIR *dp;
    +        struct dirent *dirp;
    +        if ((dp = opendir(sDir.c_str())) == NULL) {
    +          TSDebug(DEBUG_TAG_INIT, "Failed to open directory %s, %s", 
sDir.c_str(), strerror(errno));
    +          continue;
    +        }
    +
    +        while ((dirp = readdir(dp)) != NULL && !bParseDone) {
    +          //TSDebug(DEBUG_TAG_INIT, "File in  %s = %s inode=%d", 
sDir.c_str(),dirp->d_name,dirp->d_ino);
    +          string sPath = sDir + "/" + dirp->d_name;
    +          char sBuf[256];
    +          ssize_t iCount = readlink(sPath.c_str(), sBuf, sizeof (sBuf));
    +          if (iCount > 0) {
    +            string sLink(sBuf, iCount);
    +            if (sLink.compare(sLabel) == 0) {
    +              TSDebug(DEBUG_TAG_INIT, "Found that port %d is opened for 
listening by pid %s", iLPort, sPid.c_str());
    +              bParseDone = true;
    +              bMatch = true;
    +            }
    +          }
    +        }
    +        closedir(dp);
    +      }
    +    }
    +    /*    
    +        for(int i=0;i<vParts.size();i++)
    +        {
    +          TSDebug(DEBUG_TAG_INIT, "part[%d]='%s'", i, vParts[i].c_str());
    +        }
    +     */
    +  }
    +
    +  TSfclose(file);
    +
    +  return bMatch;
    +}
    +
    +/************************************************************************/
    +bool 
    +isPortSelf(unsigned int iPort)
    +{
    +  pid_t pid = getpid();
    +  char sPid[10];
    +  bool bMatch = false;
    +
    +  string sFileName; 
    +  // open file @ /proc/{pid}/net/tcp
    +
    +  
    +  sprintf(sPid,"%d",pid);
    +  
    +  // look for IPv4 listener first
    +  sFileName = "/proc/" + string(sPid) + "/net/tcp";
    +  bMatch = scanProcFileForPort(sFileName,sPid, iPort);
    +  if(!bMatch) { // did not find IPv4 listener, check for IPv6
    +    sFileName = "/proc/" + string(sPid) + "/net/tcp6";
    +    bMatch = scanProcFileForPort(sFileName,sPid, iPort);
    +  }
    +  return bMatch;
    +}
    +
    +/************************************************************************/
    +struct hostent * 
    +getHostIp(string hName, struct hostent *h, char *buf, int buflen)
    +{
    +    int res, err;
    +    struct hostent *hp = NULL;
    +
    +    res = gethostbyname_r(hName.c_str(), h, buf, buflen, &hp, &err);
    +    if ((res == 0) && (hp != NULL)) {
    +      return hp;
    +    } else {
    +      TSDebug(DEBUG_TAG_INIT, "gethostbyname_r failed for %s.  Error=%s", 
hName.c_str(), strerror(err));
    +    }
    +    return NULL;
    +}
    +
    +          
    +/************************************************************************/
    +/*
    + * *** WARNING *** You will need to run the carp plugin with 
traffic_manager for it to detect
    + * itself and to forward directly to the origin.  It will not work by 
running traffic_server directly!
    + */
    +bool
    +isSelf(string sName, int iPort, struct hostent *pSelf)
    +{
    +  bool bMatch = false;
    +  struct hostent other, *pOther;
    +  char buf[1024];
    +  pOther = getHostIp(sName,&other,buf, sizeof(buf));
    +  //uint32_t self_addr = htonl(*(uint32_t *)pSelf->h_addr_list);
    +  //TSDebug(DEBUG_TAG_INIT, "isSelf SELF h_addrtype=%d h_length=%d ip=%s",
    +  //    pSelf->h_addrtype, pSelf->h_length, inet_ntoa(*(struct 
in_addr*)&self_addr));
    +  if(pOther) {
    +    if( (pOther->h_addrtype != pSelf->h_addrtype) ||  // check basics are 
same
    +       (pOther->h_length != pSelf->h_length)) {
    +      return false;
    +    }
    +    
    +    for (int i = 0; pOther->h_addr_list[i] != NULL; i++) {  // loop 
through each in other
    +      for (int j = 0; pSelf->h_addr_list[j] != NULL; j++) { // loop 
through self
    +  //      uint32_t other_addr = htonl(*(uint32_t *)pOther->h_addr_list);
    +  //      TSDebug(DEBUG_TAG_INIT, "isSelf OTHER h_addrtype=%d h_length=%d 
ip=%s",
    +  //          pOther->h_addrtype, pOther->h_length, inet_ntoa(*(struct 
in_addr*)&other_addr));
    +
    +        if(memcmp(pSelf->h_addr_list[j], pOther->h_addr_list[i], 
pOther->h_length) == 0) {
    +          // check for matching ports
    +          // for self, look at the ports we are listening on.
    +          bMatch = isPortSelf(iPort);
    +          TSDebug(DEBUG_TAG_INIT, "port matched %s", bMatch ? 
"true":"false");
    +        }
    +      }
    +    }
    +  }
    +  return bMatch;
    +}
    +
    +/************************************************************************/
    +bool
    +addHeader(TSMBuffer& reqp, TSMLoc& hdr_loc, string header, string value)
    +{
    +  bool bReturn = false;
    +  if (value.size() <= 0) {
    +    TSDebug(DEBUG_TAG_HOOK, "\tWould set header %s to an empty value, 
skipping", header.c_str());
    +  } else {
    +    TSMLoc new_field;
    +
    +    if (TS_SUCCESS == TSMimeHdrFieldCreateNamed(reqp, hdr_loc, 
header.data(), header.size(), &new_field)) {
    +      if (TS_SUCCESS == TSMimeHdrFieldValueStringInsert(reqp, hdr_loc, 
new_field, -1,  value.data(), value.size())) {
    +        if (TS_SUCCESS == TSMimeHdrFieldAppend(reqp, hdr_loc, new_field)) {
    +          TSDebug(DEBUG_TAG_HOOK, "\tAdded header %s: %s", header.c_str(), 
value.c_str());
    +          bReturn = true;
    +        }
    +      }
    +      TSHandleMLocRelease(reqp, hdr_loc, new_field);
    +    }
    +  }
    +  return bReturn;
    +}
    +
    +/************************************************************************/
    +bool
    +getHeader(TSMBuffer& reqp, TSMLoc& hdr_loc, const std::string header, 
std::string& value)
    +{
    +  bool bReturn = false;
    +  TSMLoc fieldLoc = TSMimeHdrFieldFind(reqp, hdr_loc, header.data(), 
header.size());
    +
    +  if (fieldLoc && (fieldLoc != NULL)) {
    +    const char *str;
    +    int strLen = 0;
    +    str = TSMimeHdrFieldValueStringGet(reqp, hdr_loc, fieldLoc, 0, 
&strLen);
    +    if (str && strLen > 0) {
    +      value.assign(str, strLen);
    +      bReturn = true;
    +    } else {
    +      bReturn = false;
    +    }
    +    TSHandleMLocRelease(reqp, hdr_loc, fieldLoc);
    +  }
    +  return bReturn;
    +}
    +
    +/************************************************************************/
    +bool
    +removeHeader(TSMBuffer& reqp, TSMLoc& hdr_loc, string header)
    +{
    +  bool bReturn = false;
    +  TSMLoc fieldLoc = TSMimeHdrFieldFind(reqp, hdr_loc, header.data(), 
header.size());
    +
    +  if (fieldLoc && (fieldLoc != NULL)) {
    +    if(TSMimeHdrFieldRemove(reqp, hdr_loc, fieldLoc) == TS_SUCCESS) {
    +      if(TSMimeHdrFieldDestroy(reqp, hdr_loc, fieldLoc) == TS_SUCCESS) {
    +        TSDebug(DEBUG_TAG_HOOK, "\tRemoved header %s", header.c_str());
    +        bReturn = true;
    +      }
    +    }
    +    TSHandleMLocRelease(reqp, hdr_loc, fieldLoc);
    +  }
    +  return bReturn;
    +}
    +
    +
    +bool
    +setHeader(TSMBuffer& reqp, TSMLoc& hdr_loc, const std::string header, 
const std::string value)
    +{
    +  bool bReturn = false;
    +  TSMLoc fieldLoc = TSMimeHdrFieldFind(reqp, hdr_loc, header.data(), 
header.size());
    +
    +  if (fieldLoc && (fieldLoc != NULL)) {
    +    if (TSMimeHdrFieldValueStringSet(reqp, hdr_loc, fieldLoc, 0, 
value.data(), value.length()) == TS_SUCCESS) {
    +      TSDebug(DEBUG_TAG_HOOK, "\tSet header %s to %s", header.c_str(), 
value.c_str());
    +      bReturn = true;
    +    } else {
    +      bReturn = false;
    +    }
    +    TSHandleMLocRelease(reqp, hdr_loc, fieldLoc);
    +  }
    +  return bReturn;
    +}
    +
    +/************************************************************************/
    +//Convert a struct sockaddr address to a string, IPv4 and IPv6:
    +bool
    +getStringFromSockaddr(const struct sockaddr *sa, string& s)
    +{
    +  char str[INET6_ADDRSTRLEN];
    +  memset(str,sizeof(str),0);
    +  switch (sa->sa_family) {
    +  case AF_INET:
    +    inet_ntop(AF_INET, &(((struct sockaddr_in *) sa)->sin_addr), str, 
sizeof(str));
    +    break;
    +
    +  case AF_INET6:
    +    inet_ntop(AF_INET6, &(((struct sockaddr_in6 *) sa)->sin6_addr), str, 
sizeof(str));
    +    break;
    +
    +  default:
    +    s.assign("Unknown");
    +    return false;
    +  }
    +  s.assign(str);
    +  sprintf(str,":%d",ntohs(((struct sockaddr_in *) sa)->sin_port));
    --- End diff --
    
    You might want to use brackets around IPv6 addresses so the port colon and 
the internal IPv6 colons do not get mixed up.


Issue Time Tracking
-------------------

    Worklog Id:     (was: 26920)
    Time Spent: 6.5h  (was: 6h 20m)

> ATS CARP Plugin
> ---------------
>
>                 Key: TS-4723
>                 URL: https://issues.apache.org/jira/browse/TS-4723
>             Project: Traffic Server
>          Issue Type: New Feature
>          Components: Plugins
>            Reporter: Eric Schwartz
>            Assignee: Eric Schwartz
>             Fix For: 7.0.0
>
>          Time Spent: 6.5h
>  Remaining Estimate: 0h
>
> Open sourcing this plugin we use internally within Yahoo in place of 
> hierarchical caching.
> CARP is a plugin that allows you to group a bunch of ATS hosts into a cluster 
> and share cache space across the entire group. This is done with consistent 
> hashing on the object URL to generate an "owner" node in the cluster. 
> Requests to any other node in the cluster will be forwarded on to the 
> corresponding owner. More info in the README.
> Difference from internal version of note:
> I've ripped out some code we weren't entirely sure we could open source 
> because of a hash function. If it turns out that we can open source this, 
> I'll do so. The CarpHashAlgorithm class is meant to be extensible, so any 
> consistent hash function can replace it. The function included here is pretty 
> straightforward but not what we use in production, so just wanted to use that 
> caveat.
> One last caveat:
> You'll see some code and documentation in here for object replication. This 
> is something I added recently to CARP that allows you to specify an object be 
> replicated a certain number of times in the cluster. This is useful if you 
> have a network partition or if you're performing some sort of update. When an 
> object's primary owner is unreachable, a node in the cluster can go to the 
> secondary owner if it's available rather than having to fall all the way back 
> to origin. While I've done some initial testing on this with my own cluster 
> of hosts, it's not been tested in production so use at your own risk for now. 
> I'll be sure to keep the open source community informed on the progress of 
> our tests with this feature.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to