#!/usr/bin/python

import os.path

class ClusterNode:
    def __init__(self, spec_string):
        (self.user, self.node) = spec_string.split('@',1)

    def str(self):
        return self.user + "@" + self.node

class ClusterConfig:
    def __init__(self):
        default_path = os.path.expanduser("~/.cluster")
        self.nodes = []
        self.deploy_nodes = []
        self.code_dir = "cbr"
        self.port_base = 6666
        if (os.path.exists(default_path)):
            self.parse(default_path)
        else: # just setup some default nodes that make sense for us
            self.nodes.append( ClusterNode("meru@meru00") )
            self.nodes.append( ClusterNode("meru@meru01") )
            self.nodes.append( ClusterNode("meru@meru02") )
            self.nodes.append( ClusterNode("meru@meru03") )
            self.nodes.append( ClusterNode("meru@meru04") )

    def generate_deployment(self, count, repeat = True):
        if (self.nodes == None or len(self.nodes) == 0):
            return None
        self.deploy_nodes = []
        node_idx = 0
        while( len(self.deploy_nodes) < count ):
            if (node_idx >= len(self.nodes)):
                if (repeat == False):
                    return None
                node_idx = 0
            self.deploy_nodes.append(self.nodes[node_idx])
            node_idx = node_idx + 1
        return self.deploy_nodes

    # Parse options from the given file
    def parse(self, filename):
        fp = open(filename, 'r')
        if (fp == None):
            return
        # clear out any old values
        self.user = None
        self.nodes = []
        # line format is option = value
        for line in fp:
            [opt_name, opt_value] = line.split('=', 1)
            opt_name = opt_name.strip()
            if (opt_name == "node"):
                self.nodes.append( ClusterNode(opt_value.strip()) )
            elif (opt_name == "code_dir"):
                self.code_dir = opt_value.strip()
            elif (opt_name == "port"):
                self.port_base = int(opt_value)
        fp.close()
        return


if __name__ == "__main__":
    testcc = ClusterConfig()
    print "Nodes: ", testcc.nodes
    print "Code Directory: ", testcc.code_dir
    print "2 Deployment: ", testcc.generate_deployment(2)
    print "6 Deployment: ", testcc.generate_deployment(6)
    print "6 Deployment, No Repeat: ", testcc.generate_deployment(6, False)

@@ -104,25 +104,20 @@
 f.user, 
-self.node
+rest
 ) = spec
@@ -141,70 +141,458 @@
 ,1)%0A
-%0A    def str(self):%0A        return self.user + %22@%22 + self.node
+        rest = rest.split(':', 1)%0A        self.node = rest%5B0%5D%0A        if (len(rest) %3E 1):%0A            self.reuse_count = int(rest%5B1%5D)%0A        else:%0A            self.reuse_count = 1%0A%0A    def str(self):%0A        return self.user + %22@%22 + self.node%0A%0A    def __str__(self):%0A        return self.user + %22@%22 + self.node + %22:%22 + str(self.reuse_count)%0A%0A    def __repr__(self):%0A        return %22%3C%22 + self.user + %22@%22 + self.node + %22:%22 + str(self.reuse_count) + %22%3E%22
 %0A%0Acl
@@ -1389,32 +1389,33 @@
 return None%0A
+%0A
         
 self.deploy_
@@ -1406,341 +1406,637 @@
     
-self.deploy_nodes = %5B%5D%0A        node_idx = 0%0A        while( len(self.deploy_nodes) %3C count ):%0A            if (node_idx %3E= len(self.nodes)):%0A                if (repeat == False):%0A                    return None%0A                node_idx = 0%0A            self.deploy_nodes.append(self.nodes%5Bnode_idx%5D)%0A            node_idx = node_idx + 1%0A
+max_copies = max( %5Bx.reuse_count for x in self.nodes%5D )%0A        nodes_by_count = %5B %5B%5D %5D%0A        for reuse in range(1,max_copies+1):%0A            nodes_by_count.append( %5B%5D )%0A        for node in self.nodes:%0A            nodes_by_count%5Bnode.reuse_count%5D.append(node)%0A%0A        self.deploy_nodes = %5B%5D%0A        while( True ):%0A            for used_count in range(1,max_copies+1):%0A                for cur_count in range(used_count,max_copies+1):%0A                    for node in nodes_by_count%5Bcur_count%5D:%0A                        self.deploy_nodes.append(node)%0A                        if len(self.deploy_nodes) == count:%0A                    
     
@@ -2063,16 +2063,36 @@
 oy_nodes
+%0A        return None
 %0A%0A    # 
@@ -3125,8 +3125,66 @@
  False)%0A
+    print %229 Deployment: %22, testcc.generate_deployment(9)%0A

Add support for automatically reusing a node. The .cluster file can have lines such as node=user@host:reuse where reuse specifies the number of times to reuse the server. Currently we still loop around to reuse if we don't have enough nodes even with the reuse values, and the reuse defaults to 1.