--- /dev/null
+import os
+
+from cStringIO import StringIO
+
+from . import ansible
+
+from ..config import config as teuth_config
+
+
+class CephAnsible(ansible.Ansible):
+ name = 'ceph_ansible'
+
+ _default_playbook = [
+ dict(
+ hosts='mons',
+ become=True,
+ roles=['ceph-mon'],
+ ),
+ dict(
+ hosts='osds',
+ become=True,
+ roles=['ceph-osd'],
+ ),
+ dict(
+ hosts='mdss',
+ become=True,
+ roles=['ceph-mds'],
+ ),
+ dict(
+ hosts='rgws',
+ become=True,
+ roles=['ceph-rgw'],
+ ),
+ dict(
+ hosts='restapis',
+ become=True,
+ roles=['ceph-restapi'],
+ ),
+ ]
+
+ __doc__ = """
+ A subclass of Ansible that defaults to:
+
+ - ansible:
+ repo: {git_base}ceph-ansible.git
+ playbook: {playbook}
+
+ It always uses a dynamic inventory.
+ """.format(
+ git_base=teuth_config.ceph_git_base_url,
+ playbook=_default_playbook,
+ )
+
+ def __init__(self, ctx, config):
+ config = config or dict()
+ if 'playbook' not in config:
+ config['playbook'] = self._default_playbook
+ if 'repo' not in config:
+ config['repo'] = os.path.join(teuth_config.ceph_git_base_url,
+ 'ceph-ansible.git')
+ super(CephAnsible, self).__init__(ctx, config)
+
+ def get_inventory(self):
+ """
+ Stub this method so we always generate the hosts file
+ """
+ pass
+
+ def generate_hosts_file(self):
+ groups_to_roles = dict(
+ mons='mon',
+ mdss='mds',
+ osds='osd',
+ )
+ hosts_dict = dict()
+ for group in sorted(groups_to_roles.keys()):
+ role_prefix = groups_to_roles[group]
+ want = lambda role: role.startswith(role_prefix)
+ for (remote, roles) in self.cluster.only(want).remotes.iteritems():
+ hostname = remote.hostname
+ if group not in hosts_dict:
+ hosts_dict[group] = [hostname]
+ elif hostname not in hosts_dict[group]:
+ hosts_dict[group].append(hostname)
+
+ hosts_stringio = StringIO()
+ for group in sorted(hosts_dict.keys()):
+ hosts = hosts_dict[group]
+ hosts_stringio.write('[%s]\n' % group)
+ hosts_stringio.write('\n'.join(hosts))
+ hosts_stringio.write('\n\n')
+ hosts_stringio.seek(0)
+ self.inventory = self._write_hosts_file(hosts_stringio.read().strip())
+ self.generated_inventory = True
+
+task = CephAnsible
--- /dev/null
+from mock import patch, MagicMock
+from pytest import skip
+from StringIO import StringIO
+
+from teuthology.config import FakeNamespace
+from teuthology.orchestra.cluster import Cluster
+from teuthology.orchestra.remote import Remote
+from teuthology.task import ansible
+from teuthology.task.ceph_ansible import CephAnsible
+
+from .test_ansible import TestAnsibleTask
+
+SKIP_IRRELEVANT = "Not relevant to this subclass"
+
+
+class TestCephAnsibleTask(TestAnsibleTask):
+ klass = CephAnsible
+ task_name = 'ceph_ansible'
+
+ def setup(self):
+ self.ctx = FakeNamespace()
+ self.ctx.cluster = Cluster()
+ self.ctx.cluster.add(Remote('user@remote1'), ['mon.0'])
+ self.ctx.cluster.add(Remote('user@remote2'), ['mds.0'])
+ self.ctx.cluster.add(Remote('user@remote3'), ['osd.0'])
+ self.ctx.config = dict()
+ self.task_config = dict()
+ self.start_patchers()
+
+ def start_patchers(self):
+ super(TestCephAnsibleTask, self).start_patchers()
+ m_fetch_repo = MagicMock()
+ m_fetch_repo.return_value = 'PATH'
+ self.patcher_fetch_repo = patch(
+ 'teuthology.task.ceph_ansible.ansible.fetch_repo',
+ m_fetch_repo,
+ )
+ self.patcher_fetch_repo.start()
+
+ def stop_patchers(self):
+ super(TestCephAnsibleTask, self).stop_patchers()
+ self.patcher_fetch_repo.stop()
+
+ def test_playbook_none(self):
+ skip(SKIP_IRRELEVANT)
+
+ def test_inventory_none(self):
+ skip(SKIP_IRRELEVANT)
+
+ def test_inventory_path(self):
+ skip(SKIP_IRRELEVANT)
+
+ def test_inventory_etc(self):
+ skip(SKIP_IRRELEVANT)
+
+ def test_generate_hosts_file(self):
+ self.task_config.update(dict(
+ playbook=[]
+ ))
+ task = self.klass(self.ctx, self.task_config)
+ hosts_file_path = '/my/hosts/file'
+ hosts_file_obj = StringIO()
+ hosts_file_obj.name = hosts_file_path
+ with patch.object(ansible, 'NamedTemporaryFile') as m_NTF:
+ m_NTF.return_value = hosts_file_obj
+ task.generate_hosts_file()
+ m_NTF.assert_called_once_with(prefix="teuth_ansible_hosts_",
+ delete=False)
+ assert task.generated_inventory is True
+ assert task.inventory == hosts_file_path
+ hosts_file_obj.seek(0)
+ assert hosts_file_obj.read() == '\n'.join([
+ '[mdss]',
+ 'remote2',
+ '',
+ '[mons]',
+ 'remote1',
+ '',
+ '[osds]',
+ 'remote3',
+ ])