예제 #1
0
파일: Get.php 프로젝트: shanehill00/webdfs
 /**
  * handle a GET. Since any node can handle a GET request, we need to gracefully handle misses.
  * the algorithm follows
  *
  * ("target storage node" and "target node" refer to a node that is supposed to store the requested data)
  *
  * if we are NOT a target storage node for the file being requested
  *      send a redirect to a node that supposedly contains the data
  *
  * else if we are a target storage node
  *      if we contain the file
  *          send the file to the client
  *      else if we do not contain the file we might need to self heal
  *          if we are configured to self heal
  *              call the self healing function (described below)
  *          else
  *              send a 404 to the client
  *          endif
  *      endif
  * endif
  *
  */
 public function handle()
 {
     $iAmATarget = $this->iAmATarget();
     if ($iAmATarget) {
         if (file_exists($this->finalPath)) {
             $this->sendFile();
         } else {
             if ($this->canSelfHeal()) {
                 try {
                     if (!$this->selfHeal()) {
                         WebDFS_Helper::send404($this->params['name']);
                     }
                 } catch (Exception $e) {
                     $this->errorLog('selfHeal', $e->getMessage(), $e->getTraceAsString());
                     WebDFS_Helper::send500();
                 }
             } else {
                 WebDFS_Helper::send404($this->params['name']);
             }
         }
     } else {
         // get the paths, choose one, and print a 301 redirect
         $nodes = $this->getTargetNodes();
         if ($nodes) {
             WebDFS_Helper::send301(join('/', array($nodes[0]['staticUrl'], $this->params['pathHash'], $this->params['name'])));
         }
     }
 }
예제 #2
0
파일: Put.php 프로젝트: shanehill00/webdfs
 public function handle()
 {
     if ($this->iAmATarget()) {
         set_error_handler(array($this, "handleSpoolError"));
         try {
             // get the data from stdin and put it in a temp file
             // we will disconnect the client at this point if we are configured to do so
             // otherwise we hang on to the client, which in most cases is really bad
             // because you might stay connected until the replication chain is completed
             $this->spoolData();
             // save the data to the appropriate directory and remove the spooled file
             // but only if we are a targetNode, otherwise DO NOTHING
             $this->saveData();
         } catch (WebDFS_Exception_PutException $e) {
             $this->errorLog('putData', $this->params['action'], $this->params['name'], $e->getMessage(), $e->getTraceAsString());
             WebDFS_Helper::send500();
             // we want to be sure to exit here because we have errored
             // and the state of the file upload is unknown
             exit;
         }
         restore_error_handler();
     }
     // forward the data on to the next node
     // the reasons for forwarding are:
     //
     // we are NOT a targetNode and are just the first node
     // to receive the upload, so we forward the data to the first targetNode
     // and remove the spooled file
     //
     // OR we are a targetNode and need to fulfill the replication requirements
     // so, we forward data to the next targetNode in our list.
     // however, if we are the last replication targetNode, we DO NOTHING.
     set_error_handler(array($this, "handleForwardDataError"));
     try {
         $this->forwardDataForPut();
     } catch (WebDFS_Exception_PutException $e) {
         $this->errorLog('putForward', $this->params['action'], $this->params['name'], $e->getMessage(), $e->getTraceAsString());
         WebDFS_Helper::send500();
     } catch (WebDFS_Exception $e) {
         $this->errorLog('putForward', $this->params['action'], $this->params['name'], $e->getMessage(), $e->getTraceAsString());
         WebDFS_Helper::send500();
     }
     restore_error_handler();
 }
예제 #3
0
 public function handle()
 {
     set_error_handler(array($this, "handleDeleteDataError"));
     try {
         $this->_deleteData();
     } catch (WebDFS_Exception_DeleteException $e) {
         $this->errorLog('deleteData', $this->params['action'], $this->params['name'], $e->getMessage(), $e->getTraceAsString());
         WebDFS_Helper::send500($this->config['errMsgs']['delete500'], $this->params['name']);
     }
     restore_error_handler();
     set_error_handler(array($this, "handleForwardDeleteError"));
     try {
         $this->sendDelete();
     } catch (WebDFS_Exception_DeleteException $e) {
         $this->errorLog('deleteForward', $this->params['action'], $this->params['name'], $e->getMessage(), $e->getTraceAsString());
         WebDFS_Helper::send500($error500Msg);
     }
     restore_error_handler();
 }
예제 #4
0
파일: Move.php 프로젝트: shanehill00/webdfs
 /**
  *  called when we are in start context for a move operation
  *
  *  we check to see if we were a target node
  *  that needs to be moved.  it checks using the configIndex value in the params name 'moveConfigIndex'.
  *  if we are an owning node, we set the context to 'create' and we send the data to be moved
  *  to the first node of the current config that should have the data.
  *
  *  if we are not the owning node, we get the list of possible nodes that are owners
  *  and send the move command to the owner node with a context of 'start'
  */
 protected function doStartForMove()
 {
     // here we make a new locator instance and use it to locate the old data
     require_once $this->dataConfig['locatorClassPath'];
     set_error_handler(array($this, "handleMoveStartError"));
     try {
         $locClass = $this->dataConfig['locatorClassName'];
         $locator = new $locClass($this->config['data'][$this->params['moveConfigIndex']]);
         $thisProxyUrl = $this->config['thisProxyUrl'];
         $objKey = $this->params['name'];
         if ($this->iAmATarget($locator->findNodes($objKey))) {
             $this->sendDataToStartMove();
         } else {
             $this->sendStartMove($locator);
         }
     } catch (Exception $e) {
         $this->errorLog('doStartForMove', $this->params['action'], $this->params['moveContext'], $this->params['name'], $e->getMessage(), $e->getTraceAsString());
         WebDFS_Helper::send500();
     }
     restore_error_handler();
 }
예제 #5
0
 /**
  *
  * self heal
  *
  * Self heal accomplishes one of two things depending on when and why it is called.
  * It can be used to automatically move data from an old config when scaling.
  * And it can be used to fetch and save to disk a copy of some data from a peer server when
  * data has been lost; say, when a server failed.
  *
  * The self healing process is initiated when we have been asked for some data that is supposedly
  * stored on our disk and we cannot find it.
  *
  * When we are asked to fetch data that is supposedly stored on our disk, one of the following things can be true:
  *
  *      1) The data never was put on disk and this is simply servicing a request for data that is non-existent
  *         ( we currently do not have a reliable way to tell what is supposedly on our disk
  *           this could change if we start keeping a partial index in memory of what is supposedly on the disk. )
  *
  *      2) For some reason, the data is missing or corrupted and we need to heal ourselves
  *
  *      3) New servers and disks have been added to the cluster configuration and we are performing
  *         an auto move operation
  *
  * Currently, we have to assume that we "might" or "probably" have been asked to store the data
  * at some point in the past. Therefore we are forced to search for the data before we return a 404 to the client
  *
  * heal is the function that fecthes the file from a peer server
  * and then saves it to the temp path.
  *
  * self heal will:
  *      iterate the all data configs starting with the oldest and look for the old data.
  *      if we locate the data
  *          we download it
  *          save it to disk
  *          fsync the data
  *
  *      The above facilitates self heal and the first part of auto move
  *      To complete the auto move we need to check and see if the data needs to be deleted from the
  *      source.  The source being the server from which we downloaded the file
  *      for the self healing process.  we only delete the source if the server in question
  *      is NOT in the target nodes list we derive from the current data config
  *
  *
  *      If we cannot find that data at all;
  *          remove the tempfile
  *          we send a "404 not found" message back to the client
  *
  * endif
  *
  */
 public function selfHeal()
 {
     $filename = $this->params['name'];
     $tmpPath = $this->tmpPath;
     $fd = fopen($tmpPath, "wb+");
     if (!$fd) {
         $this->errorLog('selfHealNoFile', $tmpPath, $filename);
         WebDFS_Helper::send500();
         return;
     }
     $locator = null;
     $configIdx = null;
     $copiedFrom = null;
     $fileSize = null;
     $nodes = null;
     $healed = false;
     if ($this->params['getContext'] != self::GET_CONTEXT_AUTOMOVE) {
         $headers = array();
         $headers[0] = self::HEADER_GET_CONTEXT . ': ' . self::GET_CONTEXT_AUTOMOVE;
         $curl = curl_init();
         curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
         curl_setopt($curl, CURLOPT_FILE, $fd);
         curl_setopt($curl, CURLOPT_TIMEOUT, 10);
         curl_setopt($curl, CURLOPT_HEADER, false);
         curl_setopt($curl, CURLOPT_FAILONERROR, true);
         curl_setopt($curl, CURLOPT_BINARYTRANSFER, true);
         curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
         $totalConfigs = count($this->config['data']);
         for ($configIdx = $totalConfigs - 1; $configIdx >= 0; $configIdx--) {
             if ($configIdx == 0) {
                 // 0 means we are looking at the most current config
                 $locator = $this->locator;
                 $nodes = $this->getTargetNodes();
             } else {
                 $config = $this->config['data'][$configIdx];
                 $locClass = $config['locatorClassName'];
                 $locator = new $locClass($config);
                 $nodes = $locator->findNodes($filename);
             }
             foreach ($nodes as $node) {
                 // check to see if we are looking at node data for ourselves
                 // in which case we do not want to make a request as that
                 // would be wasted resources and pointless
                 if ($node['proxyUrl'] != $this->config['thisProxyUrl']) {
                     $url = join('/', array($node['staticUrl'], $this->params['pathHash'], $filename));
                     curl_setopt($curl, CURLOPT_URL, $url);
                     curl_exec($curl);
                     $info = curl_getinfo($curl);
                     if (!curl_errno($curl) && $info['http_code'] < 400) {
                         fclose($fd);
                         $copiedFrom = $node;
                         $this->debugLog('autoMove');
                         $healed = true;
                         break 2;
                     }
                     ftruncate($fd, 0);
                 }
             }
         }
     }
     // at this point we have achieved the same effect as a spoolData() call
     // so now we:
     // save the data
     // return the file back to the caller
     // if the source proxy url is NOT in the current target nodes list
     //      we issue a delete command to the source node
     //      and delete the data from the old location
     // endif
     if (!$healed) {
         // we cannot find the data
         // remove the temp file
         // send a 404
         fclose($fd);
         unlink($tmpPath);
         WebDFS_Helper::send404($this->params['name']);
     } else {
         if ($healed) {
             // need to  check to see if we wrote all of the data
             // as dictated by the content length headeer
             $fileSize = filesize($tmpPath);
             if ($fileSize != $info['download_content_length']) {
                 unlink($tmpPath);
                 $msg = sprintf($this->config['exceptionMsgs']['incompleteWrite'], $info['download_content_length'], $fileSize);
                 throw new WebDFS_Exception($msg);
             }
             $this->saveData();
             $this->sendFile();
             // here we check if the source from where we copied
             // is included in the the current target node list
             $position = $this->getTargetNodePosition(null, $copiedFrom['proxyUrl']);
             if ($position == WebDFS::POSITION_NONE) {
                 $this->sendDeleteForHeal($copiedFrom['proxyUrl'] . '/' . $filename);
             }
         }
     }
 }