From 1562145715c5ca265a6bc6237d27f97c341491fb Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Tue, 9 Feb 2016 10:39:57 -0700 Subject: [PATCH] Add ceph_ansible task Signed-off-by: Zack Cerza --- teuthology/task/ceph_ansible.py | 96 +++++++++++++++++++++++ teuthology/test/task/test_ceph_ansible.py | 81 +++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 teuthology/task/ceph_ansible.py create mode 100644 teuthology/test/task/test_ceph_ansible.py diff --git a/teuthology/task/ceph_ansible.py b/teuthology/task/ceph_ansible.py new file mode 100644 index 0000000000..1da38ad39d --- /dev/null +++ b/teuthology/task/ceph_ansible.py @@ -0,0 +1,96 @@ +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 diff --git a/teuthology/test/task/test_ceph_ansible.py b/teuthology/test/task/test_ceph_ansible.py new file mode 100644 index 0000000000..253e116eee --- /dev/null +++ b/teuthology/test/task/test_ceph_ansible.py @@ -0,0 +1,81 @@ +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', + ]) -- 2.39.5