Exemple #1
0
static int node_md_info(struct node_id *nid)
{
	struct sd_md_info info = {};
	char size_str[UINT64_DECIMAL_SIZE], used_str[UINT64_DECIMAL_SIZE],
	     avail_str[UINT64_DECIMAL_SIZE];
	struct sd_req hdr;
	struct sd_rsp *rsp = (struct sd_rsp *)&hdr;
	int ret, i;

	sd_init_req(&hdr, SD_OP_MD_INFO);
	hdr.data_length = sizeof(info);

	ret = dog_exec_req(nid->addr, nid->port, &hdr, &info);
	if (ret < 0)
		return EXIT_SYSFAIL;

	if (rsp->result != SD_RES_SUCCESS) {
		sd_err("failed to get multi-disk infomation: %s",
		       sd_strerror(rsp->result));
		return EXIT_FAILURE;
	}

	for (i = 0; i < info.nr; i++) {
		uint64_t size = info.disk[i].free + info.disk[i].used;
		int ratio = (int)(((double)info.disk[i].used / size) * 100);

		size_to_str(size, size_str, sizeof(size_str));
		size_to_str(info.disk[i].used, used_str, sizeof(used_str));
		size_to_str(info.disk[i].free, avail_str, sizeof(avail_str));
		fprintf(stdout, "%2d\t%s\t%s\t%s\t%3d%%\t%s\n",
			info.disk[i].idx, size_str, used_str, avail_str, ratio,
			info.disk[i].path);
	}
	return EXIT_SUCCESS;
}
Exemple #2
0
static int node_info(int argc, char **argv)
{
	int i, ret, success = 0;
	uint64_t total_size = 0, total_avail = 0, total_vdi_size = 0;
	char total_str[UINT64_DECIMAL_SIZE], avail_str[UINT64_DECIMAL_SIZE], vdi_size_str[UINT64_DECIMAL_SIZE];

	if (!raw_output)
		printf("Id\tSize\tUsed\tUse%%\n");

	for (i = 0; i < sd_nodes_nr; i++) {
		char host[128];
		struct sd_node_req req;
		struct sd_node_rsp *rsp = (struct sd_node_rsp *)&req;
		char store_str[UINT64_DECIMAL_SIZE], free_str[UINT64_DECIMAL_SIZE];

		addr_to_str(host, sizeof(host), sd_nodes[i].nid.addr, 0);

		sd_init_req((struct sd_req *)&req, SD_OP_STAT_SHEEP);
		req.epoch = sd_epoch;

		ret = send_light_req((struct sd_req *)&req, host,
				     sd_nodes[i].nid.port);

		size_to_str(rsp->store_size, store_str, sizeof(store_str));
		size_to_str(rsp->store_size - rsp->store_free, free_str,
			    sizeof(free_str));
		if (!ret) {
			printf(raw_output ? "%d %s %s %d%%\n" : "%2d\t%s\t%s\t%3d%%\n",
			       i, store_str, free_str,
			       rsp->store_size == 0 ? 0 :
			       (int)(((double)(rsp->store_size - rsp->store_free) / rsp->store_size) * 100));
			success++;
		}

		total_size += rsp->store_size;
		total_avail += rsp->store_free;
	}

	if (success == 0) {
		fprintf(stderr, "Cannot get information from any nodes\n");
		return EXIT_SYSFAIL;
	}

	if (parse_vdi(cal_total_vdi_size, SD_INODE_HEADER_SIZE,
			&total_vdi_size) < 0)
		return EXIT_SYSFAIL;

	size_to_str(total_size, total_str, sizeof(total_str));
	size_to_str(total_size - total_avail, avail_str, sizeof(avail_str));
	size_to_str(total_vdi_size, vdi_size_str, sizeof(vdi_size_str));
	printf(raw_output ? "Total %s %s %d%% %s\n"
			  : "Total\t%s\t%s\t%3d%%\n\nTotal virtual image size\t%s\n",
	       total_str, avail_str,
	       (int)(((double)(total_size - total_avail) / total_size) * 100),
	       vdi_size_str);

	return EXIT_SUCCESS;
}
Exemple #3
0
static void print_vdi_list(uint32_t vid, char *name, char *tag, uint32_t snapid,
			   uint32_t flags, struct sheepdog_inode *i, void *data)
{
	int idx, is_clone = 0;
	uint64_t my_objs, cow_objs;
	char vdi_size_str[16], my_objs_str[16], cow_objs_str[16];
	time_t ti;
	struct tm tm;
	char dbuf[128];
	struct get_vdi_info *info = data;

	if (info && strcmp(name, info->name) != 0)
		return;

	ti = i->create_time >> 32;
	if (raw_output) {
		snprintf(dbuf, sizeof(dbuf), "%" PRIu64, (uint64_t) ti);
	} else {
		localtime_r(&ti, &tm);
		strftime(dbuf, sizeof(dbuf),
			 "%Y-%m-%d %H:%M", &tm);
	}

	my_objs = 0;
	cow_objs = 0;
	for (idx = 0; idx < MAX_DATA_OBJS; idx++) {
		if (!i->data_vdi_id[idx])
			continue;
		if (is_data_obj_writeable(i, idx))
			my_objs++;
		else
			cow_objs++;
	}

	size_to_str(i->vdi_size, vdi_size_str, sizeof(vdi_size_str));
	size_to_str(my_objs * SD_DATA_OBJ_SIZE, my_objs_str, sizeof(my_objs_str));
	size_to_str(cow_objs * SD_DATA_OBJ_SIZE, cow_objs_str, sizeof(cow_objs_str));

	if (i->snap_id == 1 && i->parent_vdi_id != 0)
		is_clone = 1;

	if (raw_output) {
		printf("%c ", is_current(i) ? (is_clone ? 'c' : '=') : 's');
		while (*name) {
			if (isspace(*name) || *name == '\\')
				putchar('\\');
			putchar(*name++);
		}
		printf(" %d %s %s %s %s %" PRIx32 " %d %s\n", snapid,
				vdi_size_str, my_objs_str, cow_objs_str, dbuf, vid,
				i->nr_copies, i->tag);
	} else {
		printf("%c %-8s %5d %7s %7s %7s %s  %7" PRIx32 " %5d %13s\n",
				is_current(i) ? (is_clone ? 'c' : ' ') : 's',
				name, snapid, vdi_size_str, my_objs_str, cow_objs_str,
				dbuf, vid, i->nr_copies, i->tag);
	}
}
Exemple #4
0
/*
 * Show prograss bar as follows.
 *
 *  45.0 % [===============>                  ] 180 MB / 400 MB
 */
void show_progress(uint64_t done, uint64_t total, bool raw)
{
	char done_str[256], total_str[256];
	int screen_width = get_screen_width();
	int bar_length = screen_width - 30;
	char *buf;

	if (!is_stdout_console())
		return;
	if (screen_width <= 0)
		return;

	printf("\r"); /* move to the beginning of the line */

	if (raw) {
		snprintf(done_str, sizeof(done_str), "%"PRIu64, done);
		snprintf(total_str, sizeof(total_str), "%"PRIu64, total);
	} else {
		size_to_str(done, done_str, sizeof(done_str));
		size_to_str(total, total_str, sizeof(total_str));
	}

	buf = xmalloc(screen_width + 1);
	snprintf(buf, screen_width, "%5.1lf %% [", (double)done / total * 100);

	for (int i = 0; i < bar_length; i++) {
		if (total * (i + 1) / bar_length <= done)
			strcat(buf, "=");
		else if (total * i / bar_length <= done &&
			 done < total * (i + 1) / bar_length)
			strcat(buf, ">");
		else
			strcat(buf, " ");
	}
	snprintf(buf + strlen(buf), screen_width - strlen(buf),
		 "] %s / %s", done_str, total_str);

	/* fill the rest of buffer with blank characters */
	memset(buf + strlen(buf), ' ', screen_width - strlen(buf));
	buf[screen_width] = '\0';
	printf("%s", buf);

	if (done == total)
		printf("\n");

	fflush(stdout);

	free(buf);
}
Exemple #5
0
static void print_vdi_graph(uint32_t vid, char *name, char * tag, uint32_t snapid,
			    uint32_t flags, struct sheepdog_inode *i, void *data)
{
	time_t ti;
	struct tm tm;
	char dbuf[128], tbuf[128], size_str[128];

	ti = i->ctime >> 32;
	localtime_r(&ti, &tm);

	strftime(dbuf, sizeof(dbuf), "%Y-%m-%d", &tm);
	strftime(tbuf, sizeof(tbuf), "%H:%M:%S", &tm);
	size_to_str(i->vdi_size, size_str, sizeof(size_str));

	printf("  \"%x\" -> \"%x\";\n", i->parent_vdi_id, vid);
	printf("  \"%x\" [\n"
	       "    group = \"%s\",\n"
	       "    label = \"",
	       vid, name);
	printf("name: %10s\\n"
	       "tag : %10x\\n"
	       "size: %10s\\n"
	       "date: %10s\\n"
	       "time: %10s",
	       name, snapid, size_str, dbuf, tbuf);

	if (is_current(i))
		printf("\",\n    color=\"red\"\n  ];\n\n");
	else
		printf("\"\n  ];\n\n");

}
Exemple #6
0
int resource_index_to_string(

  std::string           &output,
  std::vector<resource> &resources,
  size_t                 index)

  {
  if (index >= resources.size())
    return(-1);

  char buf[MAXLINE];
  int  rc = PBSE_NONE;

  output = resources[index].rs_defin->rs_name;
  output += "=";
        
  switch (resources[index].rs_value.at_type)
    {
    case ATR_TYPE_LONG:

      sprintf(buf, "%ld", resources[index].rs_value.at_val.at_long);
      output += buf;

      break;

    case ATR_TYPE_STR:

      output += resources[index].rs_value.at_val.at_str;

      break;

    case ATR_TYPE_SIZE:

      size_to_str(resources[index].rs_value.at_val.at_size, buf, sizeof(buf));
      output += buf;

      break;

    default:

      rc = -1;
      break;

    }

  return(rc);
  } // END resource_index_to_string()
Exemple #7
0
static void print_type_size(Visitor *v, const char *name, uint64_t *obj,
                            Error **errp)
{
    StringOutputVisitor *sov = to_sov(v);
    uint64_t val;
    char *out, *psize;

    if (!sov->human) {
        out = g_strdup_printf("%"PRIu64, *obj);
        string_output_set(sov, out);
        return;
    }

    val = *obj;
    psize = size_to_str(val);
    out = g_strdup_printf("%"PRIu64" (%s)", val, psize);
    string_output_set(sov, out);

    g_free(psize);
}
Exemple #8
0
END_TEST



START_TEST(test_size_to_str)
  {
  struct size_value sv;
  char              buf[1024];

  sv.atsv_num = 2;
  sv.atsv_shift = 50;

  fail_unless(size_to_str(sv, buf, sizeof(buf)) == PBSE_NONE);
  fail_unless(!strcmp(buf, "2pb"));

  sv.atsv_num = 30;
  sv.atsv_shift = 40;
  fail_unless(size_to_str(sv, buf, sizeof(buf)) == PBSE_NONE);
  fail_unless(!strcmp(buf, "30tb"));

  sv.atsv_num = 4;
  sv.atsv_shift = 30;
  fail_unless(size_to_str(sv, buf, sizeof(buf)) == PBSE_NONE);
  fail_unless(!strcmp(buf, "4gb"));

  sv.atsv_num = 500;
  sv.atsv_shift = 20;
  fail_unless(size_to_str(sv, buf, sizeof(buf)) == PBSE_NONE);
  fail_unless(!strcmp(buf, "500mb"));

  sv.atsv_num = 1;
  sv.atsv_shift = 10;
  fail_unless(size_to_str(sv, buf, sizeof(buf)) == PBSE_NONE);
  fail_unless(!strcmp(buf, "1kb"));
  
  fail_unless(size_to_str(sv, buf, 2) != PBSE_NONE);
  }
/*
 * attr_to_str
 *
 * @param ds - the dynamic string we're printing the pbs_attribute into
 * @param aindex - the pbs_attribute's index
 * @param attr - the pbs_attribute
 * @param XML - boolean telling us whether to print XML or not
 */
int attr_to_str(

  std::string&      ds,     /* O */
  attribute_def    *at_def, /* I */
  pbs_attribute     attr,   /* I */
  bool              XML)    /* I */

  {
  /* used to print numbers and chars as strings */
  char local_buf[MAXLINE];

  if ((attr.at_flags & ATR_VFLAG_SET) == FALSE)
    return(NO_ATTR_DATA);

  switch (at_def->at_type)
    {
    case ATR_TYPE_LONG:

      snprintf(local_buf, sizeof(local_buf), "%ld", attr.at_val.at_long);
      ds += local_buf;

      break;

    case ATR_TYPE_CHAR:

      sprintf(local_buf, "%c", attr.at_val.at_char);
      ds += local_buf;

      break;

    case ATR_TYPE_STR:

      if (attr.at_val.at_str == NULL)
        return(NO_ATTR_DATA);

      if (strlen(attr.at_val.at_str) == 0)
        return(NO_ATTR_DATA);

      if (XML == true)
        appendEscapedXML(attr.at_val.at_str,ds);
      else
        ds += attr.at_val.at_str;

      break;

    case ATR_TYPE_ARST:
    case ATR_TYPE_ACL:

      {
      int j;
      struct array_strings *arst = attr.at_val.at_arst;

      if (arst == NULL)
        return(NO_ATTR_DATA);

      /* concatenate all of the array strings into one string */
      for (j = 0; j < arst->as_usedptr; j++)
        {
        if (j > 0)
          ds += ",";

        if (XML == true)
          appendEscapedXML(arst->as_string[j],ds);
        else
          ds += arst->as_string[j];
        }
      }

      break;

    case ATR_TYPE_SIZE:

      size_to_str(attr.at_val.at_size, local_buf, sizeof(local_buf));
      ds += local_buf;

      break;

    case ATR_TYPE_RESC:

      {
      resource *current = (resource *)GET_NEXT(attr.at_val.at_list);

      if (current == NULL)
        return(NO_ATTR_DATA);

      /* print all of the resources */
      while (current != NULL)
        {

        /* there are only 3 resource types used */
        switch (current->rs_value.at_type)
          {
          case ATR_TYPE_LONG:

            ds += "\t\t<";
            ds += current->rs_defin->rs_name;
            ds += ">";

            snprintf(local_buf, sizeof(local_buf), "%ld", current->rs_value.at_val.at_long);
            ds += local_buf;

            ds += "</";
            ds += current->rs_defin->rs_name;
            ds += ">";

            break;

          case ATR_TYPE_STR:

            /* Patch provided by Martin Siegert to fix seg-fault
             * when current->rs_value.at_val.at_str is NULL 
             * Bugzilla bug 101 
             */

            if (current->rs_value.at_val.at_str == NULL)
              break;

            if (strlen(current->rs_value.at_val.at_str) == 0)
              break;

            ds += "\t\t<";
            ds += current->rs_defin->rs_name;
            ds += ">";

            
            if (XML == true)
              appendEscapedXML(current->rs_value.at_val.at_str,ds);
            else
              ds += current->rs_value.at_val.at_str;

            ds += "</";
            ds += current->rs_defin->rs_name;
            ds += ">";

            break;

          case ATR_TYPE_SIZE:

            ds += "\t\t<";
            ds += current->rs_defin->rs_name;
            ds += ">";

            size_to_str(current->rs_value.at_val.at_size, local_buf, sizeof(local_buf));

            ds += local_buf;

            ds += "</";
            ds += current->rs_defin->rs_name;
            ds += ">";

            break;
          }

        current = (resource *)GET_NEXT(current->rs_link);
        ds += "\n";
        }
      }

      break;

    case ATR_TYPE_TV:
      /* Record seconds and milliseconds */
      sprintf(local_buf, "%ld.%ld", attr.at_val.at_timeval.tv_sec, attr.at_val.at_timeval.tv_usec);
      ds += local_buf;
      break;

    /* NYI */
    case ATR_TYPE_LIST:
    case ATR_TYPE_LL:
    case ATR_TYPE_SHORT:
    case ATR_TYPE_JINFOP:

      break;
    } /* END switch pbs_attribute type */

  return(PBSE_NONE);
  } /* END attr_to_str */
Exemple #10
0
static int node_info(int argc, char **argv)
{
	int i, ret, success = 0;
	uint64_t total_size = 0, total_avail = 0, total_vdi_size = 0;
	char total_str[21], avail_str[21], vdi_size_str[21];

	if (!raw_output)
		printf("Id\tSize\tUsed\tUse%%\n");

	for (i = 0; i < nr_nodes; i++) {
		char name[128];
		int fd;
		unsigned wlen, rlen;
		struct sd_node_req req;
		struct sd_node_rsp *rsp = (struct sd_node_rsp *)&req;
		char store_str[21], free_str[21];

		addr_to_str(name, sizeof(name), node_list_entries[i].addr, 0);

		fd = connect_to(name, node_list_entries[i].port);
		if (fd < 0)
			return 1;

		memset(&req, 0, sizeof(req));

		req.opcode = SD_OP_STAT_SHEEP;
		req.epoch = node_list_version;

		wlen = 0;
		rlen = 0;
		ret = exec_req(fd, (struct sd_req *)&req, NULL, &wlen, &rlen);
		close(fd);

		size_to_str(rsp->store_size, store_str, sizeof(store_str));
		size_to_str(rsp->store_size - rsp->store_free, free_str,
			    sizeof(free_str));
		if (!ret && rsp->result == SD_RES_SUCCESS) {
			printf(raw_output ? "%d %s %s %d%%\n" : "%2d\t%s\t%s\t%3d%%\n",
			       i, store_str, free_str,
			       (int)(((double)(rsp->store_size - rsp->store_free) / rsp->store_size) * 100));
			success++;
		}

		total_size += rsp->store_size;
		total_avail += rsp->store_free;
	}

	if (success == 0) {
		fprintf(stderr, "Cannot get information from any nodes\n");
		return EXIT_SYSFAIL;
	}

	if (parse_vdi(cal_total_vdi_size, SD_INODE_HEADER_SIZE,
			&total_vdi_size) < 0)
		return EXIT_SYSFAIL;

	size_to_str(total_size, total_str, sizeof(total_str));
	size_to_str(total_size - total_avail, avail_str, sizeof(avail_str));
	size_to_str(total_vdi_size, vdi_size_str, sizeof(vdi_size_str));
	printf(raw_output ? "Total %s %s %d%% %s\n"
			  : "Total\t%s\t%s\t%3d%%\n\nTotal virtual image size\t%s\n",
	       total_str, avail_str,
	       (int)(((double)(total_size - total_avail) / total_size) * 100),
	       vdi_size_str);

	return EXIT_SUCCESS;
}
run_stats run_benchmark(int run_id, benchmark_config* cfg, object_generator* obj_gen)
{
    fprintf(stderr, "[RUN #%u] Preparing benchmark client...\n", run_id);

    // prepare threads data
    std::vector<cg_thread*> threads;
    for (unsigned int i = 0; i < cfg->threads; i++) {
        cg_thread* t = new cg_thread(i, cfg, obj_gen);
        assert(t != NULL);

        if (t->prepare() < 0) {
            benchmark_error_log("error: failed to prepare thread %u for test.\n", i);
            exit(1);
        }
        threads.push_back(t);
    }

    // launch threads
    fprintf(stderr, "[RUN #%u] Launching threads now...\n", run_id);
    for (std::vector<cg_thread*>::iterator i = threads.begin(); i != threads.end(); i++) {
        (*i)->start();
    }

    unsigned long int prev_ops = 0;
    unsigned long int prev_bytes = 0;
    unsigned long int prev_duration = 0;
    double prev_latency = 0, cur_latency = 0;
    unsigned long int cur_ops_sec = 0;
    unsigned long int cur_bytes_sec = 0;

    // provide some feedback...
    unsigned int active_threads = 0;
    do {
        active_threads = 0;
        sleep(1);

        unsigned long int total_ops = 0;
        unsigned long int total_bytes = 0;
        unsigned long int duration = 0;
        unsigned int thread_counter = 0; 
        unsigned long int total_latency = 0;
        
        for (std::vector<cg_thread*>::iterator i = threads.begin(); i != threads.end(); i++) {
            if (!(*i)->m_finished)
                active_threads++;

            total_ops += (*i)->m_cg->get_total_ops();
            total_bytes += (*i)->m_cg->get_total_bytes();
            total_latency += (*i)->m_cg->get_total_latency();
            thread_counter++;
            float factor = ((float)(thread_counter - 1) / thread_counter);
            duration =  factor * duration +  (float)(*i)->m_cg->get_duration_usec() / thread_counter ;
        }
        
        unsigned long int cur_ops = total_ops-prev_ops;
        unsigned long int cur_bytes = total_bytes-prev_bytes;
        unsigned long int cur_duration = duration-prev_duration;
        double cur_total_latency = total_latency-prev_latency;
        prev_ops = total_ops;
        prev_bytes = total_bytes;
        prev_latency = total_latency;
        prev_duration = duration;
        
        unsigned long int ops_sec = 0;
        unsigned long int bytes_sec = 0;
        double avg_latency = 0;
        if (duration > 1000000) {
            ops_sec = (long)( (double)total_ops / duration * 1000000);
            bytes_sec = (long)( (double)total_bytes / duration * 1000000);
            avg_latency = ((double) total_latency / 1000 / total_ops) ;
        }
        if (cur_duration > 1000000 && active_threads == cfg->threads) {
            cur_ops_sec = (long)( (double)cur_ops / cur_duration * 1000000);
            cur_bytes_sec = (long)( (double)cur_bytes / cur_duration * 1000000);
            cur_latency = ((double) cur_total_latency / 1000 / cur_ops) ;
        }

        char bytes_str[40], cur_bytes_str[40];
        size_to_str(bytes_sec, bytes_str, sizeof(bytes_str)-1);
        size_to_str(cur_bytes_sec, cur_bytes_str, sizeof(cur_bytes_str)-1);
        
        double progress = 0;
        if(cfg->requests)
            progress = 100.0 * total_ops / ((double)cfg->requests*cfg->clients*cfg->threads);
        else
            progress = 100.0 * (duration / 1000000.0)/cfg->test_time;
        
        fprintf(stderr, "[RUN #%u %.0f%%, %3u secs] %2u threads: %11lu ops, %7lu (avg: %7lu) ops/sec, %s/sec (avg: %s/sec), %5.2f (avg: %5.2f) msec latency\r",
            run_id, progress, (unsigned int) (duration / 1000000), active_threads, total_ops, cur_ops_sec, ops_sec, cur_bytes_str, bytes_str, cur_latency, avg_latency);
    } while (active_threads > 0);

    fprintf(stderr, "\n\n");

    // join all threads back and unify stats
    run_stats stats;
    for (std::vector<cg_thread*>::iterator i = threads.begin(); i != threads.end(); i++) {
        (*i)->join();
        (*i)->m_cg->merge_run_stats(&stats);
    }

    // Do we need to produce client stats?
    if (cfg->client_stats != NULL) {
        unsigned int cg_id = 0;
        fprintf(stderr, "[RUN %u] Writing client stats files...\n", run_id);
        for (std::vector<cg_thread*>::iterator i = threads.begin(); i != threads.end(); i++) {
            char prefix[PATH_MAX];

            snprintf(prefix, sizeof(prefix)-1, "%s-%u-%u", cfg->client_stats, run_id, cg_id++);
            (*i)->m_cg->write_client_stats(prefix);
        }
    }

    // clean up all client_groups.  the main value of this is to be able to
    // properly look for leaks...
    while (threads.size() > 0) {
        cg_thread* t = *threads.begin();
        threads.erase(threads.begin());
        delete t;
    }
    
    return stats;
}
Exemple #12
0
/*
 * attr_to_str
 *
 * @param ds - the dynamic string we're printing the pbs_attribute into
 * @param aindex - the pbs_attribute's index
 * @param attr - the pbs_attribute
 * @param XML - boolean telling us whether to print XML or not
 */
int attr_to_str(

  std::string&      ds,     /* O */
  attribute_def    *at_def, /* I */
  pbs_attribute     attr,   /* I */
  bool              XML)    /* I */

  {
  /* used to print numbers and chars as strings */
  char local_buf[MAXLINE];

  if ((attr.at_flags & ATR_VFLAG_SET) == FALSE)
    return(NO_ATTR_DATA);

  switch (at_def->at_type)
    {

    case ATR_TYPE_BOOL:
     
      snprintf(local_buf, sizeof(local_buf), attr.at_val.at_bool ? "true" : "false");
      ds += local_buf;
      
      break;

    case ATR_TYPE_LONG:

      snprintf(local_buf, sizeof(local_buf), "%ld", attr.at_val.at_long);
      ds += local_buf;

      break;

    case ATR_TYPE_CHAR:

      sprintf(local_buf, "%c", attr.at_val.at_char);
      ds += local_buf;

      break;

    case ATR_TYPE_STR:

      if (attr.at_val.at_str == NULL)
        return(NO_ATTR_DATA);

      if (strlen(attr.at_val.at_str) == 0)
        return(NO_ATTR_DATA);

      if (XML == true)
        appendEscapedXML(attr.at_val.at_str,ds);
      else
        ds += attr.at_val.at_str;

      break;

    case ATR_TYPE_ARST:
    case ATR_TYPE_ACL:

      {
      int j;
      struct array_strings *arst = attr.at_val.at_arst;

      if (arst == NULL)
        return(NO_ATTR_DATA);

      /* concatenate all of the array strings into one string */
      for (j = 0; j < arst->as_usedptr; j++)
        {
        if (j > 0)
          ds += ",";

        if (XML == true)
          appendEscapedXML(arst->as_string[j],ds);
        else
          ds += arst->as_string[j];
        }
      }

      break;

    case ATR_TYPE_SIZE:

      size_to_str(attr.at_val.at_size, local_buf, sizeof(local_buf));
      ds += local_buf;

      break;

    case ATR_TYPE_RESC:

      {
      if (attr.at_val.at_ptr == NULL)
        return(NO_ATTR_DATA);

      std::vector<resource> *resources = (std::vector<resource> *)attr.at_val.at_ptr;

      // print all of the resources
      for (size_t i = 0; i < resources->size(); i++)
        {
        resource &r = resources->at(i);

        /* there are only 3 resource types used */
        switch (r.rs_value.at_type)
          {
          case ATR_TYPE_LONG:

            ds += "\t\t<";
            ds += r.rs_defin->rs_name;
            ds += ">";

            snprintf(local_buf, sizeof(local_buf), "%ld", r.rs_value.at_val.at_long);
            ds += local_buf;

            ds += "</";
            ds += r.rs_defin->rs_name;
            ds += ">";

            break;

          case ATR_TYPE_STR:

            /* Patch provided by Martin Siegert to fix seg-fault
             * when r.rs_value.at_val.at_str is NULL 
             * Bugzilla bug 101 
             */

            if (r.rs_value.at_val.at_str == NULL)
              break;

            if (strlen(r.rs_value.at_val.at_str) == 0)
              break;

            ds += "\t\t<";
            ds += r.rs_defin->rs_name;
            ds += ">";

            
            if (XML == true)
              appendEscapedXML(r.rs_value.at_val.at_str,ds);
            else
              ds += r.rs_value.at_val.at_str;

            ds += "</";
            ds += r.rs_defin->rs_name;
            ds += ">";

            break;

          case ATR_TYPE_SIZE:

            ds += "\t\t<";
            ds += r.rs_defin->rs_name;
            ds += ">";

            size_to_str(r.rs_value.at_val.at_size, local_buf, sizeof(local_buf));

            ds += local_buf;

            ds += "</";
            ds += r.rs_defin->rs_name;
            ds += ">";

            break;
          }

        ds += "\n";
        } // END for each resource
      }

      break;

    case ATR_TYPE_ATTR_REQ_INFO:
      {
      std::vector<std::string> names, values;
      attr_req_info *cr = (attr_req_info *)attr.at_val.at_ptr;

      if (cr == NULL)
        break;

      if (!strcmp(ATTR_req_infomin, at_def->at_name))
        cr->get_min_values(names, values);
      else if (!strcmp(ATTR_req_infomax, at_def->at_name))
        cr->get_max_values(names, values);
      else if (!strcmp(ATTR_req_infodefault, at_def->at_name))
        cr->get_default_values(names, values);
      else
        {
        /* something's not right */
        return(PBSE_BAD_PARAMETER);
        }

      for (unsigned int it = 0; it < names.size(); it++)
        {
        ds += "\n\t\t<";
        ds += names[it].c_str();
        ds += ">";

        ds += values[it].c_str();

        ds += "</";
        ds += names[it].c_str();
        ds += ">";
        }
      ds += "\n";

      }

      break;

    case ATR_TYPE_TV:
      /* Record seconds and milliseconds */
      sprintf(local_buf, "%ld.%ld", attr.at_val.at_timeval.tv_sec, attr.at_val.at_timeval.tv_usec);
      ds += local_buf;
      break;

    /* NYI */
    case ATR_TYPE_LIST:
    case ATR_TYPE_LL:
    case ATR_TYPE_SHORT:
    case ATR_TYPE_JINFOP:

      break;
    } /* END switch pbs_attribute type */

  return(PBSE_NONE);
  } /* END attr_to_str */