Changeset - a255abd6e6f8
[Not reviewed]
default
0 9 0
ethanzonca - 16 years ago 2009-12-06 21:51:48
ez@ethanzonca.com
Conf replacer made more flexible, conf issue fixed (causes segfault now), fixed style, updated comments
9 files changed with 85 insertions and 74 deletions:
0 comments (0 inline, 0 general)
TODO
Show inline comments
 
@@ -20,26 +20,26 @@ Master
 
 -update frame assigner to distribute some frames for lower-priority jobs when there's a higher priority job
 
?-make frame assigner not assign frames beyond the amount of frames in a single job
 
B-Add calls to remotio -> make API for sending messages ~= a function for every type of message defined in protocol.h
 
 -Write a stub for getting info from the tarball/validifying the tarball. Read distren-job.xml, a file in the tarball, to find out 1. which rendering system to use (that system, e.g. blender/povray, can read more specifics, such as name of file to pass to blender and frames. Options common between different systems will be handled in common as best as possible)
 
 -Write a stub for publishing file and constructing job description so that the job can be shared
 
*-Other XML-writing/reading such as current stats / jobs / data / etc, xml writing for job creation, ...
 
=-XML-RPC cgi interface for obtaining info
 
=-charsets: the program should operate in UTF-8. This is partially required by libxml2's (and XML's) usage of UTF-8. We want the program's internal charset to be UTF-8 and figure out if we need w_char/whatever to fulfill UTF-8. I don't believe we do... mabye we just need a declaration that al data stored must be in UTF-8 format. -> we need to consider how charsets work when and if issues come up
 
M-explain members of struct general_info; e.g., what is timestamp used for, what's the point of the hibernate var, etc.
 
 
 
Slave
 
 -Fix current bugs
 
*-Currently distrencommon.conf is not created slave-side
 
*-Add calls to remoteio, once it works or even before it works -> write message API functions and API
 
?-Other XML-writing/reading: Update exec_blender() to use some struct info, maybe. The struct is being passed to it.
 
=-Make code more flexible for different types of jobs and operating systems (such as paths, libs, etc)
 
=-Add code to allow pausing/resuming of the slave (including a running blender process) -> signal handling (SIGTSTP vs. SIGSTOP), distrenslave_control cmdline interface
 
B-Add tmpdir variable (just like the datadir variable) that is compile-time or whatever -> simple API to allow configuration-file override while taking into account defaults set by ./configure
 
*-Finish stub to delete old job data and output
 
=-Upload finished frames' output files while rendering subsequent frames
 

	
 
Options
 
=-Rewrite some stuff, try to make it simpler
 
x-Move server and client confuse code into their individual files, rather than in the common file. -- note: there wasn't any client code in options.c. There was the ``server'' section. That section is passed multiple times and provides information to remoteio on how to connect to servers
 
=-Review all confuse interfacing
 
B-Push patch for relative includes in confuse to the confuse developers -- windows/mingw32 testing required first
 
?-Expand execio() to support stderr
etc/distrenclient.conf
Show inline comments
 
/*
 
 A client is essentially the libdistren library or any app that is submitting 
 
 files. Thus, it should only need to know which server to access. Credentials
 
 specific to a server will be specified per-server for now.
 
*/
 
client
 
{
 
  server = "ohnopublishing"
 
  server = "protofusion"
 
}
 

	
 
include("distrencommon.conf")
etc/distrencommon.conf
Show inline comments
 
/*
 
  Configuration file for distren.
 
  Currently, this file is being prepared as the goal for this project. For instance, the ability to support connecting and communicating with servers is suggested by the server sections.
 
*/
 

	
 
/*
 
  currently, server's are only supported rudimentarily. If a job has n frames and there are s servers, n / (s + 1) frames will be sent to each individual server and also rendered on the machine the job was submitted to. There will be no recursive distribution of jobs, but I want to make that possible in the future. AND, I want job IDs to have significance based on 1: the files, 2: the versions of software used to render just like git, mercurial, or bazaar's commit IDs have significance. This will allow global distribution of renderjobs without requiring central servers to coordinate the jobs - a network only need be distributed. And complex algorithms based on timeouts and completion of jobs should allow slow servers to reassign jobs to fast ones and, possibly, find shorter routes to return the resulting images to the original job submitter.
 
  currently, server's are only supported rudimentarily. If a job has n frames and there are s servers, n / (s + 1) frames will be sent to each individual server and also rendered on the machine the job was submitted to. There will be no recursive distribution of jobs, but I want to make that possible in the future. AND, I want job IDs to have significance based on 1: the files, 2: the versions of software used to render just like git, mercurial, or bazaar's commit IDs have significance. This will allow global distribution of renderjobs without requiring central servers to coordinate the jobs - a network only need be distributed. And complex algorithms based on timeouts and completion of jobs should allow slow servers to reassign jobs to fast ones and, possibly, find shorter routes to return the resulting images to the original job submitter. Multiple server declarations are currently included, but an implementation for multiple servers is lacking.
 
*/
 

	
 
  Just a pointer, for the multiple server architecture, we would need to designate one server as a "master" server to avoid obvious issues. We can code it flexibly though. --Normaldotcom
 
     I don't know what ``obvious'' issues you're talking about ;-) --ohnobinki
 
*/
 
server protofusion
 
{
 
  hostname = "protofusion.org"
 
  username = "distrenc"
 
  /* method's use is not implemented, ssh is the only option atm ' */
 
  method = "ssh"
 
  types = {"submit", "distribution"} /* submit means it accepts jobs, distribution means it can host files */
 
}
 

	
 
server ohnopublishing
 
{
 
  hostname = "ohnopublishing.net"
 
  username = "distrenc"
 
  method = "ssh"
 
  types = {"distribution"}
 
}
 

	
etc/distrendaemon.conf
Show inline comments
 
/*
 
 configuration for _this_ server
 
*/
 
daemon
 
{
 
  datadir = "/home/ohnobinki/var/distren"
 
//  render_types = {"povray", "blender", "inkscape", "imagemagick", "dcraw"}
 

	
 
/*
 
listen {
 
       type = "unix"
 
       path = "/home/ohnobinki/var/run/distrend.sock"
 
       port = "0770"
 
  datadir = "/home/ethanzonca/var/distren"
 
  render_types = {"povray", "blender", "inkscape", "imagemagick", "dcraw"}
 
}
 
*/
 

	
 
listen {
 
       type = "tcp"
 
       path = "*"
 
       port = 4554
 
}
 
} /* daemon */
 
include("distrencommon.conf")
 

	
 

	
 
listen {
 
       type = "unix"
 
       path = "/home/ethanzonca/var/run/distrend.sock"
 
       port = "0770"
 
}
etc/distrenslave.conf
Show inline comments
 
slave
 
{
 
  username = "!username"
 
  datadir = "/home/ethanzonca/var/distren"
 
}
 

	
 
include("distrencommon.conf")
 

	
 
datadir = "/home/ohnobinki/var/distren"
etc/distrenslave.conf.in
Show inline comments
 
slave
 
{
 
  username = "!username"
 
  datadir = "@LOCALSTATEDIR@/@PACKAGE@"
 
}
 

	
 
include("distrencommon.conf")
 

	
 
datadir = "@LOCALSTATEDIR@/@PACKAGE@"
 
include("distrencommon.conf")
 
\ No newline at end of file
src/server/slave.c
Show inline comments
 
@@ -24,36 +24,53 @@
 
#include "remoteio.h"
 
#include "slavefuncs.h"
 

	
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <string.h>
 
#include <sys/stat.h>
 
#include <unistd.h>
 

	
 
int main(int argc, char *argv[])
 
{
 

	
 
  int counter;
 
  char curopt;
 
  char *username;
 

	
 
  /* Parse arguments */
 
  for(counter = 0; counter < argc; counter ++)
 
    if(strcmp(argv[counter], "-h") == 0)
 
      {
 
	fprintf(stderr, "Usage: distrenslave [option] \nStarts a distren slave\n\t-h\tshow this help\n");
 
	return 2;
 
      }
 
  while(-1 != (curopt = getopt(argc, argv, "u:h")))
 
     {
 
       if(curopt == ':')
 
         {
 
           fprintf(stderr, "-%c: is missing an argument\n", optopt);
 
           return 1;
 
         }
 
       else if(curopt == '?')
 
         {
 
           fprintf(stderr, "-%c: invalid option specified\n", optopt);
 
           return 1;
 
         }
 
       else if(curopt == 'h')
 
         {
 
           fprintf(stderr, "Usage: distrenslave [option] \nStarts a distren slave\n\t-u\tset username (run after fresh install)\n\t-h\tshow this help\n");
 
           return 2;
 
         }
 
       else if(curopt == 'u')
 
         username = strdup(optarg);
 
         fprintf(stderr, "Putting username \"%s\" in distrenslave.conf\n", username);
 
         conf_replace("distrenslave.conf", "!username", username);
 
         fprintf(stderr, "Please invoke distrenslave with no arguments to run with the username you just set\n");
 
         return 0;
 
     }
 

	
 
  /* Get conf data */
 
  char *username;
 
  char *datadir;
 
  cfg_t * my_cfg;
 
  cfg_opt_t myopts[] = {
 
    CFG_SIMPLE_STR("username", &username),
 
    CFG_SIMPLE_STR("datadir", &datadir),
 
    CFG_END()
 
  };
 
  struct options_common *commonopts;
 
  username = NULL;
 
  options_init(argc, argv, &my_cfg, myopts, "slave", &commonopts);
 

	
 

	
 
@@ -94,24 +111,25 @@ int main(int argc, char *argv[])
 
  char *tarcmd;        /* Command to run for tar. Migrate to libtar sometime */
 
  char *outdir;        /* Output Directory for tar */
 
  char *jobdatapath;   /* Path to job data */
 

	
 
  struct distrenjob *myjob; /* Structure to hold data gathered from the XML file */
 

	
 
  int haveWork = 0;
 

	
 
  while(1)
 
    {
 

	
 
    tell_the_server(0); // Give me some work!
 

	
 
    /* If we got a frame */
 
    if(haveWork)
 
      {
 
        /* @TODO: Add remotio hooks */
 
        // jobnum = remoteio_read(jobnum); /* Set jobnum from remoteio (we could use info from struct, but we need this info to download the xmlfile */
 
        //framenum = remoteio_read(jobnum); /* Set framenum from remoteio */
 

	
 
        fprintf(stderr, "Preparing to render frame %d in job %d\n", framenum, jobnum);
 

	
 
        // Variable Preparation
 
        _distren_asprintf(&jobdatapath, "job%d", jobnum);
 
        _distren_asprintf(&urltoTar, "http://protofusion.org/distren/stor/job%d/job%d.tar.gz", jobnum); // Prepares URL to download from
 
@@ -124,91 +142,96 @@ int main(int argc, char *argv[])
 
        // Prepare to download the job tar if it isn't  present
 
        struct stat buffer;
 
        int fstatus = stat(pathtoJobfile, &buffer);
 
        if(fstatus == -1)
 
          {
 
            // Download the Tar
 
            if( curlget(urltoTar, pathtoTar) == 0){
 
              fprintf(stderr, "Job data retrieved successfully\n");
 
              free(urltoTar);
 
            }
 
            else {
 
              fprintf(stderr, "Downloading job data from server failed. Check your network connection.\n");
 
              return 1; // for now
 
              return 1; // Eventually make a retry loop
 
            }
 
          }
 
        else
 
          fprintf(stderr, "Using cached job file...\n");
 

	
 
        _distren_asprintf(&outdir, "/tmp/distren/job%d", jobnum); /*< @TODO: free() */
 
        mkdir("/tmp/distren", 0750); /* @TODO: Make this less *nix-specific */
 
        _distren_asprintf(&outdir, "/tmp/distren/job%d", jobnum);
 
        mkdir("/tmp/distren", 0750); /* @TODO: Change to tmpdir once it exists */
 
        mkdir(outdir, 0750);
 

	
 
        _distren_asprintf(&tarcmd, "tar -xvf \"%s\" -C \"%s\"", pathtoTar, outdir); /* @TODO: Make this portable. Libtar or something? */
 
        _distren_asprintf(&tarcmd, "tar -xvf \"%s\" -C \"%s\"", pathtoTar, outdir); /* @TODO:Use a lib here! */
 
        system(tarcmd);
 
        free(tarcmd);
 
        free(pathtoTar);
 
        free(outdir);
 

	
 
        /* Parses a job's XML file, puts data in the myjob struct */
 
        if(xml2distrenjob(&myjob, pathtoXml) == 0)
 
          {
 
            fprintf(stderr, "Well, the XML craziness may have worked. Maybe. \n");
 
            free(pathtoXml);
 

	
 
            /* Frees things up if it was successful. xml2distrenjob() really (usually) only fails if malloc'ing inside it fails */
 
            distrenjob_free(&myjob);
 
          }
 
        else
 
          {
 
            fprintf(stderr, "I think the XML craziness may have failed, so I'll terminate just for fun.\n");
 
            return 1;
 
          }
 

	
 
        /* Variable-fillers which require XML */
 
        outputExt = myjob->output_format; /* @TODO: FIXME! <-- I WILL! */
 
        outputExt = myjob->output_format;
 
        /* Prepares the path to the jobfile */
 
        _distren_asprintf(&pathtoOutput, "%s/job%d/output/job%d-frame%d.%s", datadir, jobnum, jobnum, framenum, outputExt );
 
        free(outputExt);
 

	
 
        /* Execute blender */
 
        if(exec_blender(pathtoJobfile, pathtoOutput, framenum)){ /* @TODO: This warning should be fixed :D */
 
        if(exec_blender(pathtoJobfile, pathtoOutput, framenum))
 
          {
 
            fprintf(stderr,"Error running Blender. Check your installation and/or your PATH.");
 
            return 1;
 
        }
 
          }
 
        free(pathtoJobfile);
 

	
 
        /* When blender is finished, run this... */
 
        /* Post-execution */
 
        fprintf(stderr, "Finished frame %d in job %d, uploading...", framenum, jobnum);
 
        if( !curlpost(pathtoOutput, urltoOutput)){ // Uploads output
 
          fprintf(stderr,"Upload successful, removing old output...\n");
 
          remove(pathtoOutput); // Delete the file after its uploaded
 
        }
 
        else{
 
          fprintf(stderr,"Upload failed. Check your network connection. Trying again...");
 
          int tries=0;
 
          while(tries<10 && curlpost(pathtoOutput, urltoOutput)){
 
              fprintf(stderr, "Upload failed. Trying again in 10 seconds... (attempt %d of 10)", tries);
 
              tries++;
 
              sleep(10);
 
        if( !curlpost(pathtoOutput, urltoOutput)) // Uploads output
 
          {
 
            fprintf(stderr,"Upload successful, removing old output...\n");
 
            remove(pathtoOutput); // Delete the file after its uploaded
 
          }
 
          // @TODO: Keep track of files that we were unable to upload, and upload them later
 
        }
 
        else
 
          {
 
            fprintf(stderr,"Upload failed. Check your network connection. Trying again...");
 
            int tries=0;
 
            while(tries<10 && curlpost(pathtoOutput, urltoOutput))
 
              {
 
                fprintf(stderr, "Upload failed. Trying again in 10 seconds... (attempt %d of 10)", tries);
 
                tries++;
 
                sleep(10);
 
              }
 
            // @TODO: Keep track of files that we were unable to upload, and upload them later
 
          }
 
        free(urltoOutput);
 
        free(pathtoOutput);
 

	
 
        tell_the_server(DISTREN_REQUEST_DONEFRAME); /* Tells the server that it's done rendering, and upload is done */
 
      }
 
    else
 
      fprintf(stderr,"Nothing to do. Idling...\n");
 

	
 
    // If the server says that every frame for the last jobnum is finished, OR if the data is getting old
 
    if(1 == 0){
 
      // Note: individual frames are already deleted after uploading,
 
      // except for ones that couldn't be uploaded
 
      delete_jobdata(jobnum, datadir);
 
    }
 
    if(1 == 0)
 
      {
 
        // Note: individual frames are already deleted after uploading,
 
        // except for ones that couldn't be uploaded
 
        delete_jobdata(jobnum, datadir);
 
      }
 

	
 
    sleep(5); // Poll 5 seconds. @TODO: Remove all polling
 
  }
 
  return 0;
 
}
src/server/slavefuncs.c
Show inline comments
 
@@ -158,85 +158,86 @@ int curlpost(char *filename, char *url){
 
  }
 
  return res;
 
}
 

	
 

	
 
/** Logs the user into the server after ensuring that keys exist */
 
int login_user(char *username)
 
{
 
  // @TODO: Put some telnet-style auth code here
 
  return 1; // success
 
}
 

	
 
/** Replaces !username with a username in the slave's conf file */
 
int conf_replace(char *username){
 
/** Replaces wordtoreplace with replacewith in conffile (relative to SYSCONFDIR) */
 
int conf_replace(char *conffile, char *wordtoreplace, char *replacewith){
 
  int maxlinelen = 120;
 
  char *fileOrig = SYSCONFDIR "/distrenslave.conf";
 
  char *fileRepl = SYSCONFDIR "/distrenslave.conf.edited";
 
  char *text2find = "!username";
 
  char *text2repl = username;
 
  char *fileOrig;
 
  char *fileRepl;
 
  _distren_asprintf(&fileOrig, "&s&s", SYSCONFDIR, conffile);
 
  _distren_asprintf(&fileRepl, "&s&s.edited", SYSCONFDIR, conffile);
 
  char buffer[maxlinelen+2];
 
  char *buff_ptr, *find_ptr;
 
  FILE *fp1, *fp2;
 
  size_t find_len = strlen(text2find);
 
  size_t find_len = strlen(wordtoreplace);
 
  fp1 = fopen(fileOrig,"r");
 
  fp2 = fopen(fileRepl,"w");
 
  if (fp1 ==NULL){
 
    fprintf(stderr, "%s doesn't exist\n",fileOrig);
 
    return 0;
 
  }
 
  else if(fp2 ==NULL){
 
    fprintf(stderr, "Can't write a file to disk! Check permissions.\n");
 
    return 0;
 
  }
 
  else{
 
    while(fgets(buffer,maxlinelen+2,fp1))
 
      {
 
        buff_ptr = buffer;
 
        while ((find_ptr = strstr(buff_ptr,text2find)))
 
        while ((find_ptr = strstr(buff_ptr,wordtoreplace)))
 
        {
 
           while(buff_ptr < find_ptr)
 
             fputc((int)*buff_ptr++,fp2);
 
           fputs(text2repl,fp2);
 
           fputs(replacewith,fp2);
 
           buff_ptr += find_len;
 
         }
 
         fputs(buff_ptr,fp2);
 
      }
 
    rename(fileRepl, fileOrig);
 
  }
 
  fclose(fp2);
 
  fclose(fp1);
 
  fprintf(stderr,"Wrote conf file...\n");
 
return 1; // Success
 
}
 

	
 

	
 
/* Executors */
 

	
 
/** Executor function for Blender operations */
 
int exec_blender(char *input, char *output, int frame)
 
{
 
  fprintf(stderr,"Preparing to execute...\n");
 
  int ret;
 
  char *frame_str;
 

	
 
  char *command = "blender"; // @TODO: append .exe if win32? Do we expect this to be in the PATH?
 
  char *cmd[] = { command, "-b", "-o", output, input, "-f", frame_str, (char *)NULL };
 
  char *command = "blender"; // @TODO: We currently expect this to be in PATH
 
  char *cmd[] = { command, "-b", "-o", output, input, "-f", frame_str, (char *)NULL }; // arguments for blender
 

	
 
  char buf[10];
 
  struct execio *testrem;
 
  size_t readlen;
 

	
 
  _distren_asprintf(&frame_str, "%i", frame);
 

	
 
  fprintf(stderr,"Executing: %s\n", frame_str);
 
  ret = execio_open(&testrem, command, cmd);
 
  buf[9] = '\0';
 
  while(!execio_read(testrem, buf, 9, &readlen))
 
    {
 
      if(readlen > 9) {
 
        fprintf(stderr, "Something is terribly wrong!\n");
 
       }
 
       buf[readlen] = '\0';
 
       fprintf(stderr, "read \"%s\"\n", buf);
 
    }
 
  execio_close(testrem);
 

	
src/server/slavefuncs.h
Show inline comments
 
@@ -26,18 +26,18 @@
 
#include <stdio.h>
 

	
 
xmlNodePtr xml_quickxpath(xmlXPathContextPtr xpathctxt, xmlChar *path);
 
int software_updatecheck();
 
void tell_the_server(int stuff);
 
int delete_jobdata(int jobnum, char *datadir);
 
size_t curl_writetodisk(void *ptr, size_t size, size_t nmemb, FILE *stream);
 
int curlget(char *url, char *out);
 
int curlpost(char *filename, char *url);
 
int ssh_keygen();
 
int register_user(char *username, char *email);
 
int login_user(char *username);
 
int conf_replace(char *username);
 
int conf_replace(char *conffile, char *wordtoreplace, char *replacewith);
 
int exec_blender(char *input, char *output, int frame);
 
void xmlinit();
 
void xmlcleanup();
 

	
 
#endif
0 comments (0 inline, 0 general)