]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
crushtool: shell based on spirit example
authorSage Weil <sage@newdream.net>
Thu, 20 Mar 2008 19:19:05 +0000 (12:19 -0700)
committerSage Weil <sage@newdream.net>
Thu, 20 Mar 2008 19:19:05 +0000 (12:19 -0700)
src/Makefile.am
src/crush/grammar.h [new file with mode: 0644]
src/crushtool [deleted file]
src/crushtool.cc [new file with mode: 0644]
src/crushtool.pl [new file with mode: 0755]

index aa87f7130e157923ae406b77250d956c2a944b9a..6b4205db4b3b0f272989d4535a8e8b5d578f44a7 100644 (file)
@@ -15,8 +15,8 @@ mkmonfs_SOURCES = mkmonfs.cc
 mkmonfs_LDADD = libmon.a libcommon.a libcrush.a
 monmaptool_SOURCES = monmaptool.cc
 monmaptool_LDADD = libcommon.a
-#crushtool_SOURCES = crushtool.cc
-#crushtool_LDADD = libcommon.a libcrush.a
+crushtool_SOURCES = crushtool.cc
+crushtool_LDADD = libcommon.a libcrush.a
 osdmaptool_SOURCES = osdmaptool.cc
 osdmaptool_LDADD = libmon.a libcommon.a libcrush.a
 
@@ -89,7 +89,7 @@ AM_LDFLAGS =
 bin_PROGRAMS = \
        cmon cmds cosd csyn \
        cmonctl \
-       mkmonfs monmaptool osdmaptool \
+       mkmonfs monmaptool osdmaptool crushtool \
        fakesyn \
        streamtest dupstore \
        $(FUSEBIN) $(NEWSYN)
@@ -99,7 +99,7 @@ noinst_LIBRARIES = \
        libos.a libebofs.a
 
 # extra bits
-EXTRA_DIST = start.sh stop.sh crushtool
+EXTRA_DIST = start.sh stop.sh crushtool.pl
 
 # cleaning
 clean-local:
diff --git a/src/crush/grammar.h b/src/crush/grammar.h
new file mode 100644 (file)
index 0000000..eaa7ecf
--- /dev/null
@@ -0,0 +1,69 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software 
+ * Foundation.  See file COPYING.
+ * 
+ */
+
+#ifndef __CRUSH_GRAMMAR
+#define __CRUSH_GRAMMAR
+
+using namespace boost::spirit;
+
+struct crush_grammar : public grammar<crush_grammar>
+{
+  static const int integerID = 1;
+  static const int factorID = 2;
+  static const int termID = 3;
+  static const int expressionID = 4;
+  
+  template <typename ScannerT>
+  struct definition
+  {
+    definition(crush_grammar const& /*self*/)
+    {
+      //  Start grammar definition
+      integer     =   leaf_node_d[ lexeme_d[
+                                           (!ch_p('-') >> +digit_p)
+                                           ] ];
+      
+      factor      =   integer
+       |   inner_node_d[ch_p('(') >> expression >> ch_p(')')]
+       |   (root_node_d[ch_p('-')] >> factor);
+      
+      term        =   factor >>
+       *(  (root_node_d[ch_p('*')] >> factor)
+           | (root_node_d[ch_p('/')] >> factor)
+           );
+      
+      expression  =   term >>
+       *(  (root_node_d[ch_p('+')] >> term)
+           | (root_node_d[ch_p('-')] >> term)
+           );
+      //  End grammar definition
+      
+      // turn on the debugging info.
+      BOOST_SPIRIT_DEBUG_RULE(integer);
+      BOOST_SPIRIT_DEBUG_RULE(factor);
+      BOOST_SPIRIT_DEBUG_RULE(term);
+      BOOST_SPIRIT_DEBUG_RULE(expression);
+    }
+    
+    rule<ScannerT, parser_context<>, parser_tag<expressionID> >   expression;
+    rule<ScannerT, parser_context<>, parser_tag<termID> >         term;
+    rule<ScannerT, parser_context<>, parser_tag<factorID> >       factor;
+    rule<ScannerT, parser_context<>, parser_tag<integerID> >      integer;
+    
+    rule<ScannerT, parser_context<>, parser_tag<expressionID> > const&
+    start() const { return expression; }
+  };
+};
+
+#endif
diff --git a/src/crushtool b/src/crushtool
deleted file mode 100755 (executable)
index b7f4d04..0000000
+++ /dev/null
@@ -1,274 +0,0 @@
-#!/usr/bin/perl
-
-use Config::General;
-use Tie::IxHash;
-use Data::Dumper;
-
-use strict;
-
-use CrushWrapper;
-
-my $usage = "crushtool infile\n";
-my $fn = shift @ARGV || die $usage;
-
-my $alg_types = {
-       uniform => 1,
-       list => 2, 
-       tree => 3, 
-       straw => 4
-};
-my $alg_names = {
-    1 => 'uniform',
-    2 => 'list',
-    3 => 'tree',
-    4 => 'straw' };
-
-my $wrap = new CrushWrapper::CrushWrapper;
-
-&compile_crush($fn, "$fn.out");
-&decompile_crush("$fn.out", "$fn.out.out");
-
-# find lowest id number used
-sub get_lowest {
-    my $item = shift;
-    return unless ref $item;
-    
-    my $lowest = 0;
-    
-    if (ref $item eq 'HASH') { 
-       $lowest = $item->{'id'} if $lowest > $item->{'id'};
-       foreach my $key (keys %{$item}) { 
-           #next if grep { $key eq $_ } qw(type rule);
-           my $sublowest = get_lowest($item->{$key});
-           $lowest = $sublowest if $lowest > $sublowest;
-       }
-    } elsif (ref $item eq 'ARRAY') { 
-       foreach my $element (@{$item}) { 
-           my $sublowest = get_lowest($element);
-           $lowest = $sublowest if $lowest > $sublowest;
-       }
-    } 
-    
-    return $lowest;
-}
-
-
-sub decompile_crush {
-    my $infn = shift @_;
-    my $outfn = shift @_;
-
-    $wrap->create();
-
-    print "reading...\n";
-    my $r = $wrap->read_from_file($infn);
-
-    die "can't read file $infn ($r)\n" if ($r != 0);
-
-    my $arr;
-
-    # types
-    my $types = $wrap->get_num_type_names();
-    my %type_map;
-    for (my $id=0; $types > 0; $id++) {
-       my $name = $wrap->get_type_name($id);
-       next if $name eq '';
-       $types--;
-       $type_map{$id} = $name;
-       print "type $id is '$name'\n";
-       $arr->{'types'}->{'type'}->{$name}->{'type_id'} = $id;
-    }
-    
-    # devices
-    my $max_devices = $wrap->get_max_devices();
-    my %device_weight;
-    my %name_map;
-    for (my $id=0; $id < $max_devices; $id++) {
-       my $name = $wrap->get_item_name($id);
-       next if $name eq '';
-       $name_map{$id} = $name;
-       print "device $id '$name'\n";
-       $arr->{'devices'}->{$type_map{0}}->{$name}->{'id'} = $id;
-       my $off = $wrap->get_device_offload($id);
-       if ($off) {
-           my $off = (0x10000 - $off) / 0x10000; 
-           $arr->{'devices'}->{$type_map{0}}->{$name}->{'offload'} = $off;
-       }
-    }
-
-    # get bucket names
-    my $max_buckets = $wrap->get_max_buckets();
-    for (my $id=-1; $id > -1-$max_buckets; $id--) {
-       my $name = $wrap->get_item_name($id);
-       next if $name eq '';
-       $name_map{$id} = $name;
-    }
-
-    # do buckets
-    my $max_buckets = $wrap->get_max_buckets();
-    for (my $id=-1; $id > -1-$max_buckets; $id--) {
-       my $name = $wrap->get_item_name($id);
-       next if $name eq '';
-       print "bucket $id '$name'\n";
-       my $alg = $wrap->get_bucket_alg($id);
-       my $type = $wrap->get_bucket_type($id);
-       $arr->{'buckets'}->{$type_map{$type}}->{$name}->{'id'} = $id;
-       $arr->{'buckets'}->{$type_map{$type}}->{$name}->{'alg'} = $alg_names->{$alg};
-       my $n = $wrap->get_bucket_size($id);
-       for (my $i=0; $i<$n; $i++) {
-           my $item = $wrap->get_bucket_item($id, $i);
-           my $weight = $wrap->get_bucket_item_weight($id, $i);
-           next unless $weight;
-           $weight /= 0x10000;
-           $arr->{'buckets'}->{$type_map{$type}}->{$name}->{'item'}->{$name_map{$item}}->{'weight'} = $weight;
-       }
-    }
-
-    
-
-    print Dumper $arr;
-
-}
-
-sub compile_crush {
-    my $infn = shift @_;
-    my $outfn = shift @_;
-
-    $wrap->create();
-
-    tie my %conf, "Tie::IxHash";
-    %conf = Config::General::ParseConfig( -ConfigFile => $fn,
-                                         -Tie => "Tie::IxHash",
-                                         -MergeDuplicateBlocks => 1 );
-    
-    my $arr = \%conf;
-    print Dumper $arr;
-
-    my $lowest = get_lowest($arr);
-    #print "lowest is $lowest\n";
-    
-    my %weights;  # item id -> weight
-    
-    # types
-    my %type_ids;
-    foreach my $item_type (keys %{$arr->{'types'}->{'type'}}) {
-       my $type_id = $arr->{'types'}->{'type'}->{$item_type}->{'type_id'};
-       print "type $type_id '$item_type'\n";
-       $type_ids{$item_type} = $type_id;
-       $wrap->set_type_name($type_id, $item_type);
-    }
-    
-    # build device table
-    my %device_ids;  # name -> id
-    foreach my $item_type (keys %{$arr->{'devices'}}) {
-       foreach my $name (keys %{$arr->{'devices'}->{$item_type}}) {
-           my $id = $arr->{'devices'}->{$item_type}->{$name}->{'id'};
-           if (!defined $id || $id < 0) { 
-               die "invalid device id for $item_type $name: id is required and must be non-negative";
-           }
-           $wrap->set_item_name($id, $name);
-           
-           my $w = $arr->{'devices'}->{$item_type}->{$name}->{'weight'};
-           $weights{$id} = $w;
-           $device_ids{$name} = $id;
-           print "device $id '$name' weight $w\n";
-       }
-    }
-    
-    # build bucket table
-    my %bucket_ids;
-    foreach my $bucket_type (keys %{$arr->{'buckets'}}) {
-       foreach my $name (keys %{$arr->{'buckets'}->{$bucket_type}}) {
-           # verify type
-           unless (defined $type_ids{$bucket_type}) {
-               die "invalid bucket type $bucket_type\n";
-           }
-           
-           # id
-           my $id = $arr->{'buckets'}->{$bucket_type}->{$name}->{'id'};
-           if (defined $id && $id > -1) { 
-               die "invalid bucket id for $bucket_type $name: id must be negative";
-           } elsif (!defined $id) { 
-               # get the next lower ID number and inject it into the config hash
-               $id = --$lowest;
-               $arr->{'buckets'}->{$bucket_type}->{$name}->{'id'} = $id;
-           }
-           
-           $wrap->set_item_name($id, $name);
-           $bucket_ids{$name} = $id;
-           
-           my @item_ids;
-           my @weights;
-           my $myweight;
-           foreach my $item_name (keys %{$arr->{'buckets'}->{$bucket_type}->{$name}->{'item'}}) {
-               my $id = $wrap->get_item_id($item_name);
-               push @item_ids, $id;
-               my $weight = $arr->{'buckets'}->{$bucket_type}->{$name}->{'item'}->{$item_name}->{'weight'};
-               $weight ||= $weights{$id};
-               push(@weights, $weight * 65536);  # 16.16 fixed point
-               $myweight += $weight;
-           }
-           
-           my $alg = $arr->{'buckets'}->{$bucket_type}->{$name}->{'alg'};
-           $alg = 'straw' if !$alg;
-           die "invalid bucket alg $alg\n"
-               unless $alg_types->{$alg};
-           
-           my $typeid = $type_ids{$bucket_type};
-           my $algid = $alg_types->{$alg};
-           print "\tid $id\n";
-           print "\talg $alg ($algid)\n";
-           print "\ttype $bucket_type ($typeid)\n";
-           print "\titems @item_ids\n";
-           print "\tweights @weights\n";
-           
-           # id, alg, type, size, items, weights
-           #TODO: pass the correct value for type to add_bucket
-           my $result = $wrap->add_bucket($id, $algid, $typeid,
-                                      scalar(@item_ids), \@item_ids, \@weights);
-           #print "\t.. $result\n\n";
-           print "\tweight $myweight\n";
-           $weights{$id} = $myweight;
-       }
-    }
-
-    # rules
-    for my $rule_name (keys %{$arr->{'rules'}->{'rule'}}) {
-       my $r = $arr->{'rules'}->{'rule'}->{$rule_name};
-       my $pool = $r->{'pool'};
-       my $typeid = $rule_types{$r->{'type'}};
-       my $min_size = $r->{'min_size'};
-       my $max_size = $r->{'max_size'};
-               
-    }
-
-    $wrap->finalize;
-    $wrap->write_to_file($outfn);
-    1;
-}
-
-
-
-print "Line: " . __LINE__ ."\n";
-
-
-=item
-
-/*** BUCKETS ***/
-enum {
-    CRUSH_BUCKET_UNIFORM = 1,
-    CRUSH_BUCKET_LIST = 2,
-    CRUSH_BUCKET_TREE = 3,
-    CRUSH_BUCKET_STRAW = 4
-};
-
-/*** RULES ***/
-enum {
-    CRUSH_RULE_NOOP = 0,
-    CRUSH_RULE_TAKE = 1,          /* arg1 = value to start with */
-    CRUSH_RULE_CHOOSE_FIRSTN = 2, /* arg1 = num items to pick */
-                                  /* arg2 = type */
-    CRUSH_RULE_CHOOSE_INDEP = 3,  /* same */
-    CRUSH_RULE_EMIT = 4           /* no args */
-};
-
-=cut
diff --git a/src/crushtool.cc b/src/crushtool.cc
new file mode 100644 (file)
index 0000000..58e936e
--- /dev/null
@@ -0,0 +1,163 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software 
+ * Foundation.  See file COPYING.
+ * 
+ */
+
+#include <boost/spirit/core.hpp>
+#include <boost/spirit/tree/ast.hpp>
+#include <boost/spirit/tree/tree_to_xml.hpp>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <sys/stat.h>
+
+#include "config.h"
+
+#include "crush/CrushWrapper.h"
+#include "crush/grammar.h"
+
+#include <iostream>
+#include <stack>
+#include <functional>
+#include <string>
+#include <cassert>
+#include <map>
+using namespace std;
+
+
+using namespace boost::spirit;
+
+typedef char const*         iterator_t;
+typedef tree_match<iterator_t> parse_tree_match_t;
+typedef parse_tree_match_t::tree_iterator iter_t;
+
+
+//
+long evaluate(parse_tree_match_t hit);
+long eval_expression(iter_t const& i);
+
+long evaluate(tree_parse_info<> info)
+{
+  return eval_expression(info.trees.begin());
+}
+
+long eval_expression(iter_t const& i)
+{
+  cout << "In eval_expression. i->value = "
+       << string(i->value.begin(), i->value.end())
+       << " i->children.size() = " << i->children.size() << std::endl;
+  
+  if (i->value.id() == crush_grammar::integerID)
+    {
+      assert(i->children.size() == 0);
+      
+      // extract integer (not always delimited by '\0')
+      string integer(i->value.begin(), i->value.end());
+      
+      return strtol(integer.c_str(), 0, 10);
+    }
+  else if (i->value.id() == crush_grammar::factorID)
+    {
+      // factor can only be unary minus
+      assert(*i->value.begin() == '-');
+      return - eval_expression(i->children.begin());
+    }
+  else if (i->value.id() == crush_grammar::termID)
+    {
+      if (*i->value.begin() == '*')
+        {
+         assert(i->children.size() == 2);
+         return eval_expression(i->children.begin()) *
+           eval_expression(i->children.begin()+1);
+        }
+      else if (*i->value.begin() == '/')
+        {
+         assert(i->children.size() == 2);
+         return eval_expression(i->children.begin()) /
+           eval_expression(i->children.begin()+1);
+        }
+      else
+       assert(0);
+    }
+  else if (i->value.id() == crush_grammar::expressionID)
+    {
+      if (*i->value.begin() == '+')
+        {
+         assert(i->children.size() == 2);
+         return eval_expression(i->children.begin()) +
+           eval_expression(i->children.begin()+1);
+        }
+      else if (*i->value.begin() == '-')
+        {
+         assert(i->children.size() == 2);
+         return eval_expression(i->children.begin()) -
+           eval_expression(i->children.begin()+1);
+        }
+      else
+       assert(0);
+    }
+  else
+    {
+      assert(0); // error
+    }
+  
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+int main(int argc, char **argv)
+{
+  // look in tree_calc_grammar for the definition of crush_grammar
+  crush_grammar calc;
+  
+  cout << "/////////////////////////////////////////////////////////\n\n";
+  cout << "\t\tThe simplest working crush_grammar...\n\n";
+  cout << "/////////////////////////////////////////////////////////\n\n";
+  cout << "Type an expression...or [q or Q] to quit\n\n";
+  
+  string str;
+  while (getline(cin, str))
+    {
+      if (str.empty() || str[0] == 'q' || str[0] == 'Q')
+       break;
+      
+      tree_parse_info<> info = ast_parse(str.c_str(), calc);
+      
+      if (info.full)
+        {
+#if defined(BOOST_SPIRIT_DUMP_PARSETREE_AS_XML)
+         // dump parse tree as XML
+         std::map<parser_id, std::string> rule_names;
+         rule_names[crush_grammar::integerID] = "integer";
+         rule_names[crush_grammar::factorID] = "factor";
+         rule_names[crush_grammar::termID] = "term";
+         rule_names[crush_grammar::expressionID] = "expression";
+         tree_to_xml(cout, info.trees, str.c_str(), rule_names);
+#endif
+         
+         // print the result
+         cout << "parsing succeeded\n";
+         cout << "result = " << evaluate(info) << "\n\n";
+        }
+      else
+        {
+         cout << "parsing failed\n";
+        }
+    }
+  
+  cout << "Bye... :-) \n\n";
+  return 0;
+}
+
diff --git a/src/crushtool.pl b/src/crushtool.pl
new file mode 100755 (executable)
index 0000000..b7f4d04
--- /dev/null
@@ -0,0 +1,274 @@
+#!/usr/bin/perl
+
+use Config::General;
+use Tie::IxHash;
+use Data::Dumper;
+
+use strict;
+
+use CrushWrapper;
+
+my $usage = "crushtool infile\n";
+my $fn = shift @ARGV || die $usage;
+
+my $alg_types = {
+       uniform => 1,
+       list => 2, 
+       tree => 3, 
+       straw => 4
+};
+my $alg_names = {
+    1 => 'uniform',
+    2 => 'list',
+    3 => 'tree',
+    4 => 'straw' };
+
+my $wrap = new CrushWrapper::CrushWrapper;
+
+&compile_crush($fn, "$fn.out");
+&decompile_crush("$fn.out", "$fn.out.out");
+
+# find lowest id number used
+sub get_lowest {
+    my $item = shift;
+    return unless ref $item;
+    
+    my $lowest = 0;
+    
+    if (ref $item eq 'HASH') { 
+       $lowest = $item->{'id'} if $lowest > $item->{'id'};
+       foreach my $key (keys %{$item}) { 
+           #next if grep { $key eq $_ } qw(type rule);
+           my $sublowest = get_lowest($item->{$key});
+           $lowest = $sublowest if $lowest > $sublowest;
+       }
+    } elsif (ref $item eq 'ARRAY') { 
+       foreach my $element (@{$item}) { 
+           my $sublowest = get_lowest($element);
+           $lowest = $sublowest if $lowest > $sublowest;
+       }
+    } 
+    
+    return $lowest;
+}
+
+
+sub decompile_crush {
+    my $infn = shift @_;
+    my $outfn = shift @_;
+
+    $wrap->create();
+
+    print "reading...\n";
+    my $r = $wrap->read_from_file($infn);
+
+    die "can't read file $infn ($r)\n" if ($r != 0);
+
+    my $arr;
+
+    # types
+    my $types = $wrap->get_num_type_names();
+    my %type_map;
+    for (my $id=0; $types > 0; $id++) {
+       my $name = $wrap->get_type_name($id);
+       next if $name eq '';
+       $types--;
+       $type_map{$id} = $name;
+       print "type $id is '$name'\n";
+       $arr->{'types'}->{'type'}->{$name}->{'type_id'} = $id;
+    }
+    
+    # devices
+    my $max_devices = $wrap->get_max_devices();
+    my %device_weight;
+    my %name_map;
+    for (my $id=0; $id < $max_devices; $id++) {
+       my $name = $wrap->get_item_name($id);
+       next if $name eq '';
+       $name_map{$id} = $name;
+       print "device $id '$name'\n";
+       $arr->{'devices'}->{$type_map{0}}->{$name}->{'id'} = $id;
+       my $off = $wrap->get_device_offload($id);
+       if ($off) {
+           my $off = (0x10000 - $off) / 0x10000; 
+           $arr->{'devices'}->{$type_map{0}}->{$name}->{'offload'} = $off;
+       }
+    }
+
+    # get bucket names
+    my $max_buckets = $wrap->get_max_buckets();
+    for (my $id=-1; $id > -1-$max_buckets; $id--) {
+       my $name = $wrap->get_item_name($id);
+       next if $name eq '';
+       $name_map{$id} = $name;
+    }
+
+    # do buckets
+    my $max_buckets = $wrap->get_max_buckets();
+    for (my $id=-1; $id > -1-$max_buckets; $id--) {
+       my $name = $wrap->get_item_name($id);
+       next if $name eq '';
+       print "bucket $id '$name'\n";
+       my $alg = $wrap->get_bucket_alg($id);
+       my $type = $wrap->get_bucket_type($id);
+       $arr->{'buckets'}->{$type_map{$type}}->{$name}->{'id'} = $id;
+       $arr->{'buckets'}->{$type_map{$type}}->{$name}->{'alg'} = $alg_names->{$alg};
+       my $n = $wrap->get_bucket_size($id);
+       for (my $i=0; $i<$n; $i++) {
+           my $item = $wrap->get_bucket_item($id, $i);
+           my $weight = $wrap->get_bucket_item_weight($id, $i);
+           next unless $weight;
+           $weight /= 0x10000;
+           $arr->{'buckets'}->{$type_map{$type}}->{$name}->{'item'}->{$name_map{$item}}->{'weight'} = $weight;
+       }
+    }
+
+    
+
+    print Dumper $arr;
+
+}
+
+sub compile_crush {
+    my $infn = shift @_;
+    my $outfn = shift @_;
+
+    $wrap->create();
+
+    tie my %conf, "Tie::IxHash";
+    %conf = Config::General::ParseConfig( -ConfigFile => $fn,
+                                         -Tie => "Tie::IxHash",
+                                         -MergeDuplicateBlocks => 1 );
+    
+    my $arr = \%conf;
+    print Dumper $arr;
+
+    my $lowest = get_lowest($arr);
+    #print "lowest is $lowest\n";
+    
+    my %weights;  # item id -> weight
+    
+    # types
+    my %type_ids;
+    foreach my $item_type (keys %{$arr->{'types'}->{'type'}}) {
+       my $type_id = $arr->{'types'}->{'type'}->{$item_type}->{'type_id'};
+       print "type $type_id '$item_type'\n";
+       $type_ids{$item_type} = $type_id;
+       $wrap->set_type_name($type_id, $item_type);
+    }
+    
+    # build device table
+    my %device_ids;  # name -> id
+    foreach my $item_type (keys %{$arr->{'devices'}}) {
+       foreach my $name (keys %{$arr->{'devices'}->{$item_type}}) {
+           my $id = $arr->{'devices'}->{$item_type}->{$name}->{'id'};
+           if (!defined $id || $id < 0) { 
+               die "invalid device id for $item_type $name: id is required and must be non-negative";
+           }
+           $wrap->set_item_name($id, $name);
+           
+           my $w = $arr->{'devices'}->{$item_type}->{$name}->{'weight'};
+           $weights{$id} = $w;
+           $device_ids{$name} = $id;
+           print "device $id '$name' weight $w\n";
+       }
+    }
+    
+    # build bucket table
+    my %bucket_ids;
+    foreach my $bucket_type (keys %{$arr->{'buckets'}}) {
+       foreach my $name (keys %{$arr->{'buckets'}->{$bucket_type}}) {
+           # verify type
+           unless (defined $type_ids{$bucket_type}) {
+               die "invalid bucket type $bucket_type\n";
+           }
+           
+           # id
+           my $id = $arr->{'buckets'}->{$bucket_type}->{$name}->{'id'};
+           if (defined $id && $id > -1) { 
+               die "invalid bucket id for $bucket_type $name: id must be negative";
+           } elsif (!defined $id) { 
+               # get the next lower ID number and inject it into the config hash
+               $id = --$lowest;
+               $arr->{'buckets'}->{$bucket_type}->{$name}->{'id'} = $id;
+           }
+           
+           $wrap->set_item_name($id, $name);
+           $bucket_ids{$name} = $id;
+           
+           my @item_ids;
+           my @weights;
+           my $myweight;
+           foreach my $item_name (keys %{$arr->{'buckets'}->{$bucket_type}->{$name}->{'item'}}) {
+               my $id = $wrap->get_item_id($item_name);
+               push @item_ids, $id;
+               my $weight = $arr->{'buckets'}->{$bucket_type}->{$name}->{'item'}->{$item_name}->{'weight'};
+               $weight ||= $weights{$id};
+               push(@weights, $weight * 65536);  # 16.16 fixed point
+               $myweight += $weight;
+           }
+           
+           my $alg = $arr->{'buckets'}->{$bucket_type}->{$name}->{'alg'};
+           $alg = 'straw' if !$alg;
+           die "invalid bucket alg $alg\n"
+               unless $alg_types->{$alg};
+           
+           my $typeid = $type_ids{$bucket_type};
+           my $algid = $alg_types->{$alg};
+           print "\tid $id\n";
+           print "\talg $alg ($algid)\n";
+           print "\ttype $bucket_type ($typeid)\n";
+           print "\titems @item_ids\n";
+           print "\tweights @weights\n";
+           
+           # id, alg, type, size, items, weights
+           #TODO: pass the correct value for type to add_bucket
+           my $result = $wrap->add_bucket($id, $algid, $typeid,
+                                      scalar(@item_ids), \@item_ids, \@weights);
+           #print "\t.. $result\n\n";
+           print "\tweight $myweight\n";
+           $weights{$id} = $myweight;
+       }
+    }
+
+    # rules
+    for my $rule_name (keys %{$arr->{'rules'}->{'rule'}}) {
+       my $r = $arr->{'rules'}->{'rule'}->{$rule_name};
+       my $pool = $r->{'pool'};
+       my $typeid = $rule_types{$r->{'type'}};
+       my $min_size = $r->{'min_size'};
+       my $max_size = $r->{'max_size'};
+               
+    }
+
+    $wrap->finalize;
+    $wrap->write_to_file($outfn);
+    1;
+}
+
+
+
+print "Line: " . __LINE__ ."\n";
+
+
+=item
+
+/*** BUCKETS ***/
+enum {
+    CRUSH_BUCKET_UNIFORM = 1,
+    CRUSH_BUCKET_LIST = 2,
+    CRUSH_BUCKET_TREE = 3,
+    CRUSH_BUCKET_STRAW = 4
+};
+
+/*** RULES ***/
+enum {
+    CRUSH_RULE_NOOP = 0,
+    CRUSH_RULE_TAKE = 1,          /* arg1 = value to start with */
+    CRUSH_RULE_CHOOSE_FIRSTN = 2, /* arg1 = num items to pick */
+                                  /* arg2 = type */
+    CRUSH_RULE_CHOOSE_INDEP = 3,  /* same */
+    CRUSH_RULE_EMIT = 4           /* no args */
+};
+
+=cut