Executes the ansible playbooks that configure the servers in the stack.

        Assumes that the root playbook directory is ``./playbooks/`` relative
        to the stack configuration file.  Also sets the ansible *module_path*
        to be ``./common_modules/`` relative to the stack configuration file.

        E.g.  If the stack configuration file is::

            $HOME/bang-stacks/my_web_service.yml

        then the root playbook directory is::

            $HOME/bang-stacks/playbooks/

        and the ansible module path is::

            $HOME/bang-stacks/common_modules/

def configure(self):
        """
        Executes the ansible playbooks that configure the servers in the stack.

        Assumes that the root playbook directory is ``./playbooks/`` relative
        to the stack configuration file.  Also sets the ansible *module_path*
        to be ``./common_modules/`` relative to the stack configuration file.

        E.g.  If the stack configuration file is::

            $HOME/bang-stacks/my_web_service.yml

        then the root playbook directory is::

            $HOME/bang-stacks/playbooks/

        and the ansible module path is::

            $HOME/bang-stacks/common_modules/

        """
        cfg = self.config
        bang_config_dir = os.path.abspath(
                os.path.dirname(cfg.filepath)
                )
        playbook_dir = os.path.join(bang_config_dir, 'playbooks')
        creds = cfg.get(A.DEPLOYER_CREDS, {})
        pb_kwargs = {
                # this allows connection reuse using "ControlPersist":
                'transport': 'ssh',
                'module_path': os.path.join(bang_config_dir, 'common_modules'),
                'remote_pass': creds.get(A.creds.SSH_PASS),
                # TODO: determine forks
                # 'forks': options.forks,
                }
        # only add the 'remote_user' kwarg if it's in the config, otherwise use
        # ansible's default behaviour.
        ssh_user = creds.get(A.creds.SSH_USER)
        if ssh_user:
            pb_kwargs['remote_user'] = ssh_user

        ansible_cfg = cfg.get(A.ANSIBLE, {})
        ansible_verbosity = ansible_cfg.get(A.ansible.VERBOSITY, 1)
        ansible.utils.VERBOSITY = ansible_verbosity
        for playbook in cfg.get(A.PLAYBOOKS, []):
            playbook_path = os.path.join(playbook_dir, playbook)

            # gratuitously stolen from main() in ``ansible-playbook``
            stats = callbacks.AggregateStats()
            playbook_cb = callbacks.PlaybookCallbacks(
                    verbose=ansible_verbosity
                    )
            runner_cb = callbacks.PlaybookRunnerCallbacks(
                    stats,
                    verbose=ansible_verbosity
                    )

            vault_password = ansible_cfg.get(A.ansible.VAULT_PASS)
            extra_kwargs = {
                    'playbook': playbook_path,

                    # TODO: do we really need new instances of the following
                    # for each playbook?
                    'callbacks': playbook_cb,
                    'runner_callbacks': runner_cb,
                    'stats': stats,

                    # ``host_list`` is used to generate the inventory, but
                    # don't worry, we override the inventory later
                    'host_list': [],
                    'vault_password': vault_password,
                    }
            pb_kwargs.update(extra_kwargs)
            pb = PlayBook(**pb_kwargs)
            inventory = BangsibleInventory(
                    copy.deepcopy(self.groups_and_vars.lists),
                    copy.deepcopy(self.groups_and_vars.dicts),
                    vault_password=vault_password
                    )
            inventory.set_playbook_basedir(playbook_dir)
            pb.inventory = inventory

            pb.run()

            hosts = sorted(pb.stats.processed.keys())
            playbook_cb.on_stats(pb.stats)

            failed = False
            for h in hosts:
                hsum = pb.stats.summarize(h)
                if hsum['failures'] or hsum['unreachable']:
                    failed = True
                print "%-30s : %s" % (h, hsum)
                # TODO: sort this out
                # print "%-30s : %s %s %s %s " % (
                #     hostcolor(h, hsum),
                #     colorize('ok', hsum['ok'], 'green'),
                #     colorize('changed', hsum['changed'], 'yellow'),
                #     colorize('unreachable', hsum['unreachable'], 'red'),
                #     colorize('failed', hsum['failures'], 'red'))

            if failed:
                raise BangError("Server configuration failed!")