Exemple #1
0
class AccountPoolTest(unittest.TestCase):
    CORRELATE = AccountPool

    def setUp(self):
        self.user1     = 'testuser1'
        self.password1 = 'test1'
        self.account1  = Account(self.user1, self.password1)
        self.user2     = 'testuser2'
        self.password2 = 'test2'
        self.account2  = Account(self.user2, self.password2)
        self.accm      = AccountPool()

    def testConstructor(self):
        accm = AccountPool()
        self.assertEqual(accm.n_accounts(), 0)

        accm = AccountPool([self.account1, self.account2])
        self.assertEqual(accm.n_accounts(), 2)

    def testAddAccount(self):
        self.assertEqual(self.accm.n_accounts(), 0)
        self.accm.add_account(self.account1)
        self.assertEqual(self.accm.n_accounts(), 1)

        self.accm.add_account(self.account2)
        self.assertEqual(self.accm.n_accounts(), 2)

    def testReset(self):
        self.testAddAccount()
        self.accm.reset()
        self.assertEqual(self.accm.n_accounts(), 0)

    def testHasAccount(self):
        self.assertEqual(self.accm.has_account(self.account1), False)
        self.accm.add_account(self.account1)
        self.assertEqual(self.accm.has_account(self.account1), True)

    def testGetAccountFromHash(self):
        account = Account('user', 'test')
        thehash = account.__hash__()
        self.accm.add_account(account)
        self.assertEqual(self.accm.get_account_from_hash(thehash), account)

    def testGetAccountFromName(self):
        self.testAddAccount()
        self.assertEqual(self.account2,
                         self.accm.get_account_from_name(self.user2))

    def testNAccounts(self):
        self.testAddAccount()

    def testAcquireAccount(self):
        self.testAddAccount()
        self.accm.acquire_account(self.account1)
        self.account1.release()
        self.accm.acquire_account(self.account1)
        self.account1.release()

        # Add three more accounts.
        filename = os.path.join(os.path.dirname(__file__), 'account_pool.cfg')
        self.accm.add_account(get_accounts_from_file(filename))
        self.assert_(self.accm.n_accounts() == 5)

        for i in range(0, 2000):
            # Each time an account is acquired a different one should be 
            # returned.
            acquired = {}
            for n in range(0, 5):
                account = self.accm.acquire_account()
                self.assert_(account is not None)
                self.assert_(not acquired.has_key(account.get_name()))
                acquired[account.get_name()] = account

            # Release one account.
            acquired['abc'].release()

            # Acquire one account.
            account = self.accm.acquire_account()
            self.assert_(account.get_name() == 'abc')

            # Release all accounts.
            for account in acquired.itervalues():
                account.release()

    def testReleaseAccounts(self):
        account1 = Account('foo')
        account2 = Account('bar')
        pool = AccountPool()
        pool.add_account(account1)
        pool.add_account(account2)
        pool.acquire_account(account1, 'one')
        pool.acquire_account(account2, 'two')

        self.assert_(account1 not in pool.unlocked_accounts)
        self.assert_(account2 not in pool.unlocked_accounts)
        pool.release_accounts('one')
        self.assert_(account1 in pool.unlocked_accounts)
        self.assert_(account2 not in pool.unlocked_accounts)
        pool.release_accounts('one')
        self.assert_(account1 in pool.unlocked_accounts)
        self.assert_(account2 not in pool.unlocked_accounts)
        pool.release_accounts('two')
        self.assert_(account1 in pool.unlocked_accounts)
        self.assert_(account2 in pool.unlocked_accounts)
class AccountManager(object):
    """
    Keeps track of available user accounts and assigns them to the
    worker threads.
    """

    def __init__(self):
        """
        Constructor.
        """
        self.default_pool = None
        self.pools = None
        self.reset()

    def reset(self):
        """
        Removes all account pools.
        """
        self.default_pool = AccountPool()
        self.pools = []

    def add_pool(self, pool, match=None):
        """
        Adds a new account pool. If the given match argument is
        None, the pool the default pool. Otherwise, the match argument is
        a callback function that is invoked to decide whether or not the
        given pool should be used for a host.

        When Exscript logs into a host, the account is chosen in the following
        order:

            # Exscript checks whether an account was attached to the
            L{Host} object using L{Host.set_account()}), and uses that.

            # If the L{Host} has no account attached, Exscript walks
            through all pools that were passed to L{Queue.add_account_pool()}.
            For each pool, it passes the L{Host} to the function in the
            given match argument. If the return value is True, the account
            pool is used to acquire an account.
            (Accounts within each pool are taken in a round-robin
            fashion.)

            # If no matching account pool is found, an account is taken
            from the default account pool.

            # Finally, if all that fails and the default account pool
            contains no accounts, an error is raised.

        Example usage::

            def do_nothing(conn):
                conn.autoinit()

            def use_this_pool(host):
                return host.get_name().startswith('foo')

            default_pool = AccountPool()
            default_pool.add_account(Account('default-user', 'password'))

            other_pool = AccountPool()
            other_pool.add_account(Account('user', 'password'))

            queue = Queue()
            queue.account_manager.add_pool(default_pool)
            queue.account_manager.add_pool(other_pool, use_this_pool)

            host = Host('localhost')
            queue.run(host, do_nothing)

        In the example code, the host has no account attached. As a result,
        the queue checks whether use_this_pool() returns True. Because the
        hostname does not start with 'foo', the function returns False, and
        Exscript takes the 'default-user' account from the default pool.

        @type  pool: AccountPool
        @param pool: The account pool that is added.
        @type  match: callable
        @param match: A callback to check if the pool should be used.
        """
        if match is None:
            self.default_pool = pool
        else:
            self.pools.append((match, pool))

    def add_account(self, account):
        """
        Adds the given account to the default account pool that Exscript uses
        to log into all hosts that have no specific L{Account} attached.

        @type  account: Account
        @param account: The account that is added.
        """
        self.default_pool.add_account(account)

    def get_account_from_hash(self, account_hash):
        """
        Returns the account with the given hash, if it is contained in any
        of the pools. Returns None otherwise.

        @type  account_hash: str
        @param account_hash: The hash of an account object.
        """
        for _, pool in self.pools:
            account = pool.get_account_from_hash(account_hash)
            if account is not None:
                return account
        return self.default_pool.get_account_from_hash(account_hash)

    def acquire_account(self, account=None, owner=None):
        """
        Acquires the given account. If no account is given, one is chosen
        from the default pool.

        @type  account: Account
        @param account: The account that is added.
        @type  owner: object
        @param owner: An optional descriptor for the owner.
        @rtype:  L{Account}
        @return: The account that was acquired.
        """
        if account is not None:
            for _, pool in self.pools:
                if pool.has_account(account):
                    return pool.acquire_account(account, owner)

            if not self.default_pool.has_account(account):
                # The account is not in any pool.
                account.acquire()
                return account

        return self.default_pool.acquire_account(account, owner)

    def acquire_account_for(self, host, owner=None):
        """
        Acquires an account for the given host and returns it.
        The host is passed to each of the match functions that were
        passed in when adding the pool. The first pool for which the
        match function returns True is chosen to assign an account.

        @type  host: L{Host}
        @param host: The host for which an account is acquired.
        @type  owner: object
        @param owner: An optional descriptor for the owner.
        @rtype:  L{Account}
        @return: The account that was acquired.
        """
        # Check whether a matching account pool exists.
        for match, pool in self.pools:
            if match(host) is True:
                return pool.acquire_account(owner=owner)

        # Else, choose an account from the default account pool.
        return self.default_pool.acquire_account(owner=owner)

    def release_accounts(self, owner):
        """
        Releases all accounts that were acquired by the given owner.

        @type  owner: object
        @param owner: The owner descriptor as passed to acquire_account().
        """
        for _, pool in self.pools:
            pool.release_accounts(owner)
        self.default_pool.release_accounts(owner)
Exemple #3
0
class AccountPoolTest(unittest.TestCase):
    CORRELATE = AccountPool

    def setUp(self):
        self.user1 = 'testuser1'
        self.password1 = 'test1'
        self.account1 = Account(self.user1, self.password1)
        self.user2 = 'testuser2'
        self.password2 = 'test2'
        self.account2 = Account(self.user2, self.password2)
        self.accm = AccountPool()

    def testConstructor(self):
        accm = AccountPool()
        self.assertEqual(accm.n_accounts(), 0)

        accm = AccountPool([self.account1, self.account2])
        self.assertEqual(accm.n_accounts(), 2)

    def testAddAccount(self):
        self.assertEqual(self.accm.n_accounts(), 0)
        self.accm.add_account(self.account1)
        self.assertEqual(self.accm.n_accounts(), 1)

        self.accm.add_account(self.account2)
        self.assertEqual(self.accm.n_accounts(), 2)

    def testReset(self):
        self.testAddAccount()
        self.accm.reset()
        self.assertEqual(self.accm.n_accounts(), 0)

    def testHasAccount(self):
        self.assertEqual(self.accm.has_account(self.account1), False)
        self.accm.add_account(self.account1)
        self.assertEqual(self.accm.has_account(self.account1), True)

    def testGetAccountFromHash(self):
        account = Account('user', 'test')
        thehash = account.__hash__()
        self.accm.add_account(account)
        self.assertEqual(self.accm.get_account_from_hash(thehash), account)

    def testGetAccountFromName(self):
        self.testAddAccount()
        self.assertEqual(self.account2,
                         self.accm.get_account_from_name(self.user2))

    def testNAccounts(self):
        self.testAddAccount()

    def testAcquireAccount(self):
        self.testAddAccount()
        self.accm.acquire_account(self.account1)
        self.account1.release()
        self.accm.acquire_account(self.account1)
        self.account1.release()

        # Add three more accounts.
        filename = os.path.join(os.path.dirname(__file__), 'account_pool.cfg')
        self.accm.add_account(get_accounts_from_file(filename))
        self.assert_(self.accm.n_accounts() == 5)

        for i in range(0, 2000):
            # Each time an account is acquired a different one should be
            # returned.
            acquired = {}
            for n in range(0, 5):
                account = self.accm.acquire_account()
                self.assert_(account is not None)
                self.assert_(not acquired.has_key(account.get_name()))
                acquired[account.get_name()] = account

            # Release one account.
            acquired['abc'].release()

            # Acquire one account.
            account = self.accm.acquire_account()
            self.assert_(account.get_name() == 'abc')

            # Release all accounts.
            for account in acquired.itervalues():
                account.release()

    def testReleaseAccounts(self):
        account1 = Account('foo')
        account2 = Account('bar')
        pool = AccountPool()
        pool.add_account(account1)
        pool.add_account(account2)
        pool.acquire_account(account1, 'one')
        pool.acquire_account(account2, 'two')

        self.assert_(account1 not in pool.unlocked_accounts)
        self.assert_(account2 not in pool.unlocked_accounts)
        pool.release_accounts('one')
        self.assert_(account1 in pool.unlocked_accounts)
        self.assert_(account2 not in pool.unlocked_accounts)
        pool.release_accounts('one')
        self.assert_(account1 in pool.unlocked_accounts)
        self.assert_(account2 not in pool.unlocked_accounts)
        pool.release_accounts('two')
        self.assert_(account1 in pool.unlocked_accounts)
        self.assert_(account2 in pool.unlocked_accounts)
class AccountManager(object):
    """
    Keeps track of available user accounts and assigns them to the
    worker threads.
    """

    def __init__(self):
        """
        Constructor.
        """
        self.default_pool = None
        self.pools        = None
        self.reset()

    def reset(self):
        """
        Removes all account pools.
        """
        self.default_pool = AccountPool()
        self.pools        = []

    def add_pool(self, pool, match = None):
        """
        Adds a new account pool. If the given match argument is
        None, the pool the default pool. Otherwise, the match argument is
        a callback function that is invoked to decide whether or not the
        given pool should be used for a host.

        When Exscript logs into a host, the account is chosen in the following
        order:

            # Exscript checks whether an account was attached to the
            L{Host} object using L{Host.set_account()}), and uses that.

            # If the L{Host} has no account attached, Exscript walks
            through all pools that were passed to L{Queue.add_account_pool()}.
            For each pool, it passes the L{Host} to the function in the
            given match argument. If the return value is True, the account
            pool is used to acquire an account.
            (Accounts within each pool are taken in a round-robin
            fashion.)

            # If no matching account pool is found, an account is taken
            from the default account pool.

            # Finally, if all that fails and the default account pool
            contains no accounts, an error is raised.

        Example usage::

            def do_nothing(conn):
                conn.autoinit()

            def use_this_pool(host):
                return host.get_name().startswith('foo')

            default_pool = AccountPool()
            default_pool.add_account(Account('default-user', 'password'))

            other_pool = AccountPool()
            other_pool.add_account(Account('user', 'password'))

            queue = Queue()
            queue.account_manager.add_pool(default_pool)
            queue.account_manager.add_pool(other_pool, use_this_pool)

            host = Host('localhost')
            queue.run(host, do_nothing)

        In the example code, the host has no account attached. As a result,
        the queue checks whether use_this_pool() returns True. Because the
        hostname does not start with 'foo', the function returns False, and
        Exscript takes the 'default-user' account from the default pool.

        @type  pool: AccountPool
        @param pool: The account pool that is added.
        @type  match: callable
        @param match: A callback to check if the pool should be used.
        """
        if match is None:
            self.default_pool = pool
        else:
            self.pools.append((match, pool))

    def add_account(self, account):
        """
        Adds the given account to the default account pool that Exscript uses
        to log into all hosts that have no specific L{Account} attached.

        @type  account: Account
        @param account: The account that is added.
        """
        self.default_pool.add_account(account)

    def get_account_from_hash(self, account_hash):
        """
        Returns the account with the given hash, if it is contained in any
        of the pools. Returns None otherwise.

        @type  account_hash: str
        @param account_hash: The hash of an account object.
        """
        for _, pool in self.pools:
            account = pool.get_account_from_hash(account_hash)
            if account is not None:
                return account
        return self.default_pool.get_account_from_hash(account_hash)

    def acquire_account(self, account = None, owner = None):
        """
        Acquires the given account. If no account is given, one is chosen
        from the default pool.

        @type  account: Account
        @param account: The account that is added.
        @type  owner: object
        @param owner: An optional descriptor for the owner.
        @rtype:  L{Account}
        @return: The account that was acquired.
        """
        if account is not None:
            for _, pool in self.pools:
                if pool.has_account(account):
                    return pool.acquire_account(account, owner)

            if not self.default_pool.has_account(account):
                # The account is not in any pool.
                account.acquire()
                return account

        return self.default_pool.acquire_account(account, owner)

    def acquire_account_for(self, host, owner = None):
        """
        Acquires an account for the given host and returns it.
        The host is passed to each of the match functions that were
        passed in when adding the pool. The first pool for which the
        match function returns True is chosen to assign an account.

        @type  host: L{Host}
        @param host: The host for which an account is acquired.
        @type  owner: object
        @param owner: An optional descriptor for the owner.
        @rtype:  L{Account}
        @return: The account that was acquired.
        """
        # Check whether a matching account pool exists.
        for match, pool in self.pools:
            if match(host) is True:
                return pool.acquire_account(owner = owner)

        # Else, choose an account from the default account pool.
        return self.default_pool.acquire_account(owner = owner)

    def release_accounts(self, owner):
        """
        Releases all accounts that were acquired by the given owner.

        @type  owner: object
        @param owner: The owner descriptor as passed to acquire_account().
        """
        for _, pool in self.pools:
            pool.release_accounts(owner)
        self.default_pool.release_accounts(owner)