Beispiel #1
0
class TelegrafIntegration(playbook_plugin.CephAnsiblePlaybook):

    NAME = "Telegraf Integration Plugin for Decapod"
    DESCRIPTION = DESCRIPTION
    PUBLIC = True
    REQUIRED_SERVER_LIST = True
    SERVER_LIST_POLICY = playbook_plugin.ServerListPolicy.in_this_cluster
    CLUSTER_MUST_BE_DEPLOYED = True

    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def make_playbook_configuration(self, cluster, servers, hints):
        global_vars = self.make_global_vars(cluster, servers, hints)
        inventory = self.make_inventory(cluster, servers, hints)

        return global_vars, inventory

    def make_global_vars(self, cluster, servers, hints):
        result = {
            "configpath": self.config["configpath"],
            "install": hints["install"],
        }
        result.update(self.config["settings"])
        result.update(self.config["role"])

        return result

    def make_inventory(self, cluster, servers, hints):
        hostvars = {}
        for srv in servers:
            hostvars.setdefault(srv.ip, {})["ansible_user"] = srv.username

        return {"telegraf": sorted(hostvars), "_meta": {"hostvars": hostvars}}
Beispiel #2
0
class AddMds(playbook_plugin.CephAnsibleNewWithVerification):

    NAME = "Add metadata server host"
    DESCRIPTION = DESCRIPTION
    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_pre_execute(self, task):
        super().on_pre_execute(["mdss"], task)

    def get_inventory_groups(self, cluster, servers, hints):
        base = super().get_inventory_groups(cluster, servers, hints)

        cluster_servers = server.ServerModel.cluster_servers(cluster.model_id)
        cluster_servers = {item._id: item for item in cluster_servers}
        mdss = [
            cluster_servers[item["server_id"]]
            for item in cluster.configuration.state if item["role"] == "mdss"
        ]

        mdss_ips = {srv.ip for srv in mdss}
        for srv in servers:
            if srv.ip not in mdss_ips:
                mdss_ips.add(srv.ip)
                mdss.append(srv)

        base["mdss"] = mdss

        return base
Beispiel #3
0
class AddClient(playbook_plugin.CephAnsibleNewWithVerification):

    NAME = "Add RBD and CLI clients to the hosts"
    DESCRIPTION = DESCRIPTION
    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_pre_execute(self, task):
        super().on_pre_execute(["clients"], task)

        playbook_config = self.get_playbook_configuration(task)
        config = playbook_config.configuration["inventory"]
        cluster = playbook_config.cluster

        data = cluster_data.ClusterData.find_one(cluster.model_id)
        data.global_vars = config["global_vars"]
        hostvars = config["inventory"].get("_meta", {}).get("hostvars", {})
        for hostname, values in hostvars.items():
            data.update_host_vars(hostname, values)
        data.save()

    def get_inventory_groups(self, cluster, servers, hints):
        base = super().get_inventory_groups(cluster, servers, hints)
        base["clients"] = servers

        return base
Beispiel #4
0
class AddNfs(playbook_plugin.CephAnsibleNewWithVerification):

    NAME = "Add NFS Gateway host"
    DESCRIPTION = DESCRIPTION
    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_pre_execute(self, task):
        super().on_pre_execute(["nfss", "rgws"], task)

    def make_global_vars(self, cluster, data, servers, hints):
        base = super().make_global_vars(cluster, data, servers, hints)
        base.update(nfs_file_gw=bool(hints["file_access"]),
                    nfs_obj_gw=bool(hints["object_access"]))

        return base

    def get_inventory_groups(self, cluster, servers, hints):
        base = super().get_inventory_groups(cluster, servers, hints)
        rgws = servers if not hints["dont_deploy_rgw"] else []
        base.update(rgws=rgws, nfss=servers)

        return base

    def prepare_plugin(self):
        resource_path = pathutils.resource("decapod_plugin_playbook_add_nfs",
                                           "roles")
        resource_path.symlink_to(
            str(playbook_plugin.PATH_CEPH_ANSIBLE.joinpath("roles")))
Beispiel #5
0
class PurgeTelegraf(playbook_plugin.CephAnsiblePlaybook):

    NAME = "Telegraf removal plugin for Decapod"
    DESCRIPTION = DESCRIPTION
    PUBLIC = True
    REQUIRED_SERVER_LIST = True
    SERVER_LIST_POLICY = playbook_plugin.ServerListPolicy.any_server

    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def make_playbook_configuration(self, cluster, servers, hints):
        global_vars = self.make_global_vars(cluster, servers, hints)
        inventory = self.make_inventory(cluster, servers, hints)

        return global_vars, inventory

    def make_global_vars(self, cluster, servers, hints):
        return {
            "remove_config_section_only": hints["remove_config_section_only"],
            "configpath": self.config["configpath"]
        }

    def make_inventory(self, cluster, servers, hints):
        hostvars = {}
        for srv in servers:
            hostvars.setdefault(srv.ip, {})["ansible_user"] = srv.username

        return {"telegraf": sorted(hostvars), "_meta": {"hostvars": hostvars}}
Beispiel #6
0
class RemoveClient(playbook_plugin.CephAnsiblePlaybookRemove):

    NAME = DESCRIPTION
    DESCRIPTION = DESCRIPTION
    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def get_dynamic_inventory(self):
        servers = {srv._id: srv for srv in self.playbook_config.servers}
        inventory = super().get_dynamic_inventory()
        hostvars = inventory["_meta"]["hostvars"]

        for data in self.playbook_config.cluster.configuration.state:
            if data["server_id"] in servers and data["role"] in BLOCKED_ROLES:
                srv = servers[data["server_id"]]
                hostvars[srv.ip]["blocked_by"] = data["role"]

        for values in hostvars.values():
            values.setdefault("blocked_by", "")

        return inventory

    def on_post_execute(self, task, exc_value, exc_type, exc_tb):
        super().on_post_execute("clients", task, exc_value, exc_type, exc_tb)

    def make_global_vars(self, cluster, data, servers, hints):
        base = super().make_global_vars(cluster, data, servers, hints)
        base["uninstall_packages"] = bool(hints["uninstall_packages"])
        base["apt_purge"] = bool(hints["apt_purge"])

        return base

    def get_inventory_groups(self, cluster, servers, hints):
        return {"clients": servers}
Beispiel #7
0
class UpgradeCeph(playbook_plugin.CephAnsiblePlaybook):

    NAME = "Upgrade Ceph"
    DESCRIPTION = DESCRIPTION
    PUBLIC = True
    REQUIRED_SERVER_LIST = False
    SERVER_LIST_POLICY = playbook_plugin.ServerListPolicy.in_this_cluster
    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def make_playbook_configuration(self, cluster, servers, hints):
        global_vars = self.make_global_vars(cluster, servers, hints)
        inventory = self.make_inventory(cluster, servers, hints)

        return global_vars, inventory

    def make_global_vars(self, cluster, servers, hints):
        data = cluster_data.ClusterData.find_one(cluster.model_id)

        return {
            "do_timesync": hints["sync_time"],
            "ntp_server": self.config["ntp_server"],
            "mon": self.config["mon"],
            "osd": self.config["osd"],
            "cluster": data.global_vars["cluster"]
        }

    def make_inventory(self, cluster, servers, hints):
        radosgw_nodes, groups = self.get_inventory_groups(
            cluster, servers, hints)
        inventory = {"_meta": {"hostvars": {}}}

        for name, group_servers in groups.items():
            for srv in group_servers:
                inventory.setdefault(name, []).append(srv.ip)

                hostvars = inventory["_meta"]["hostvars"].setdefault(
                    srv.ip, {})
                hostvars["ansible_user"] = srv.username
                hostvars["has_radosgw"] = srv.ip in radosgw_nodes

        return inventory

    def get_inventory_groups(self, cluster, servers, hints):
        cluster_servers = server.ServerModel.cluster_servers(cluster.model_id)
        cluster_servers = {item._id: item for item in cluster_servers}

        inventory = {}
        for item in cluster.configuration.state:
            if item["role"] in ("mons", "clients", "osds"):
                inventory.setdefault(item["role"], []).append(
                    cluster_servers[item["server_id"]])

        radosgw_nodes = {
            cluster_servers[item["server_id"]].ip
            for item in cluster.configuration.state if item["role"] == "rgws"}

        return radosgw_nodes, inventory
Beispiel #8
0
class AddRestapi(playbook_plugin.CephAnsibleNewWithVerification):

    NAME = "Add Ceph REST API host"
    DESCRIPTION = DESCRIPTION
    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_pre_execute(self, task):
        super().on_pre_execute(["restapis"], task)

    def get_inventory_groups(self, cluster, servers, hints):
        base = super().get_inventory_groups(cluster, servers, hints)
        base["restapis"] = servers

        return base
Beispiel #9
0
class AddRgw(playbook_plugin.CephAnsibleNewWithVerification):

    NAME = "Add Rados Gateway to the cluster"
    DESCRIPTION = DESCRIPTION
    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_pre_execute(self, task):
        super().on_pre_execute(["rgws"], task)

    def get_inventory_groups(self, cluster, servers, hints):
        base = super().get_inventory_groups(cluster, servers, hints)
        base["rgws"] = servers

        return base
Beispiel #10
0
class RemoveNfs(playbook_plugin.CephAnsiblePlaybookRemove):

    NAME = "Remove NFS Gateway host"
    DESCRIPTION = DESCRIPTION
    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_post_execute(self, task, exc_value, exc_type, exc_tb):
        playbook_plugin.CephAnsiblePlaybook.on_post_execute(
            task, exc_value, exc_type, exc_tb)

        if exc_value:
            LOG.warning("Cannot remove NFS gateway host: %s (%s)", exc_value,
                        exc_type)
            raise exc_value

        playbook_config = self.get_playbook_configuration(task)
        config = playbook_config.configuration["inventory"]
        cluster = playbook_config.cluster
        servers = playbook_config.servers
        servers = {srv.ip: srv for srv in servers}

        nfs_servers = config.pop("nfss")
        nfs_servers = [servers[ip] for ip in nfs_servers]
        cluster.remove_servers(nfs_servers, "nfss")

        rgw_servers = config.pop("rgws", [])
        if rgw_servers:
            rgw_servers = [servers[ip] for ip in rgw_servers]
            cluster.remove_servers(rgw_servers, "rgws")

        if cluster.configuration.changed:
            cluster.save()

    def make_global_vars(self, cluster, data, servers, hints):
        base = super().make_global_vars(cluster, data, servers, hints)
        base["ceph_nfs_rgw_user"] = data.global_vars["ceph_nfs_rgw_user"]
        base["remove_nfs_rgw_user"] = bool(hints["remove_nfs_rgw_user"])

        return base

    def get_inventory_groups(self, cluster, servers, hints):
        groups = {"nfss": servers}
        if hints["remove_rgw"]:
            groups["rgws"] = servers

        return groups
Beispiel #11
0
class RemovePool(playbook_plugin.Playbook):

    NAME = "Remove Ceph pool"
    DESCRIPTION = DESCRIPTION
    PUBLIC = True
    REQUIRED_SERVER_LIST = False
    SERVER_LIST_POLICY = playbook_plugin.ServerListPolicy.not_in_other_cluster

    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def make_playbook_configuration(self, cluster, servers, hints):
        data = cluster_data.ClusterData.find_one(cluster.model_id)
        global_vars = self.make_global_vars(cluster, data, hints)
        inventory = self.make_inventory(cluster, hints)

        return global_vars, inventory

    def make_global_vars(self, cluster, data, hints):
        return {
            "pool_names": [hints["pool_name"]],
            "cluster": data.global_vars.get("cluster", cluster.name)
        }

    def make_inventory(self, cluster, hints):
        cluster_servers = server.ServerModel.cluster_servers(cluster.model_id)
        cluster_servers = {item._id: item for item in cluster_servers}

        mons = [
            cluster_servers[item["server_id"]]
            for item in cluster.configuration.state if item["role"] == "mons"
        ]

        return {
            "mons": [mons[0].ip],
            "_meta": {
                "hostvars": {
                    mons[0].ip: {
                        "ansible_user": mons[0].username
                    }
                }
            }
        }
Beispiel #12
0
class RestartServices(playbook_plugin.Playbook):

    NAME = "Restart services"
    DESCRIPTION = DESCRIPTION
    PUBLIC = True
    REQUIRED_SERVER_LIST = True
    SERVER_LIST_POLICY = playbook_plugin.ServerListPolicy.in_this_cluster
    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def make_playbook_configuration(self, cluster, servers, hints):
        global_vars = self.make_global_vars(cluster, servers, hints)
        inventory = self.make_inventory(cluster, servers, hints)

        return global_vars, inventory

    def make_global_vars(self, cluster, servers, hints):
        data = cluster_data.ClusterData.find_one(cluster.model_id)

        return {
            "mon": self.config["mon"],
            "osd": self.config["osd"],
            "radosgw": self.config["radosgw"],
            "restapi": self.config["restapi"],
            "cluster": data.global_vars["cluster"]
        }

    def make_inventory(self, cluster, servers, hints):
        groups = self.get_inventory_groups(cluster, servers, hints)
        inventory = {"_meta": {"hostvars": {}}}

        for name, group_servers in groups.items():
            for srv in group_servers:
                inventory.setdefault(name, []).append(srv.ip)

                hostvars = inventory["_meta"]["hostvars"].setdefault(
                    srv.ip, {})
                hostvars["ansible_user"] = srv.username

        return inventory

    def get_inventory_groups(self, cluster, servers, hints):
        cluster_servers = server.ServerModel.cluster_servers(cluster.model_id)
        cluster_servers = {item._id: item for item in cluster_servers}

        allowed_groups = set()
        if hints["restart_osd"]:
            allowed_groups.add("osds")
        if hints["restart_mon"]:
            allowed_groups.add("mons")
        if hints["restart_rgw"]:
            allowed_groups.add("rgws")
        if hints["restart_restapi"]:
            allowed_groups.add("restapis")
        allowed_servers = {srv._id for srv in servers}

        inventory = {}
        for item in cluster.configuration.state:
            if item["role"] in allowed_groups and item[
                    "server_id"] in allowed_servers:  # NOQA
                inventory.setdefault(item["role"], []).append(
                    cluster_servers[item["server_id"]])

        return inventory
Beispiel #13
0
}
"""Schema for playbook hints."""

LOG = log.getLogger(__name__)
"""Logger."""


class {{ cookiecutter.plugin_class_name }}(playbook_plugin.CephAnsiblePlaybook):

    NAME = "{{ cookiecutter.plugin_display_name }}"
    DESCRIPTION = DESCRIPTION
    PUBLIC = {{ cookiecutter.is_public }}
    REQUIRED_SERVER_LIST = {{ cookiecutter.required_server_list }}
    SERVER_LIST_POLICY = playbook_plugin.ServerListPolicy.not_in_other_cluster

    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_pre_execute(self, task):
        super().on_pre_execute(task)

        playbook_config = self.get_playbook_configuration(task)
        config = playbook_config.configuration["inventory"]
        cluster = playbook_config.cluster
        servers = playbook_config.servers
        servers = {srv.ip: srv for srv in servers}

    def on_post_execute(self, task, exc_value, exc_type, exc_tb):
        super().on_post_execute(task, exc_value, exc_type, exc_tb)

        if exc_value:
            raise exc_value
Beispiel #14
0
class AddOSD(playbook_plugin.CephAnsibleNewWithVerification):

    NAME = "Add OSD to Ceph cluster"
    DESCRIPTION = DESCRIPTION
    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_pre_execute(self, task):
        super().on_pre_execute(["osds"], task)

        playbook_config = self.get_playbook_configuration(task)
        config = playbook_config.configuration["inventory"]
        cluster = playbook_config.cluster

        data = cluster_data.ClusterData.find_one(cluster.model_id)
        hostvars = config.get("_meta", {}).get("hostvars", {})
        for hostname, values in hostvars.items():
            data.update_host_vars(hostname, values)
        data.save()

    def make_global_vars(self, cluster, data, servers, hints):
        base = super().make_global_vars(cluster, data, servers, hints)

        base["journal_collocation"] = False
        base["dmcrypt_journal_collocation"] = False
        base["dmcrypt_dedicated_journal"] = False
        base["raw_multi_journal"] = False
        if hints["dmcrypt"]:
            if hints["collocation"]:
                base["dmcrypt_journal_collocation"] = True
            else:
                base["dmcrypt_dedicated_journal"] = True
        elif hints["collocation"]:
            base["journal_collocation"] = True
        else:
            base["raw_multi_journal"] = True

        return base

    def make_inventory(self, cluster, data, servers, hints):
        global_vars = self.make_global_vars(cluster, data, servers, hints)
        groups = self.get_inventory_groups(cluster, servers, hints)
        inventory = {"_meta": {"hostvars": {}}}
        all_servers = server.ServerModel.cluster_servers(cluster.model_id)

        for name, group_servers in groups.items():
            for srv in group_servers:
                inventory.setdefault(name, []).append(srv.ip)

                hostvars = inventory["_meta"]["hostvars"].setdefault(
                    srv.ip, {})
                hostvars.update(data.get_host_vars(srv.ip))

                if "ansible_user" not in hostvars:
                    hostvars["ansible_user"] = srv.username
                if "monitor_interface" not in hostvars:
                    if "monitor_address" not in hostvars:
                        hostvars["monitor_address"] = \
                            networkutils.get_public_network_ip(
                                srv, all_servers)

                if hints["collocation"]:
                    hostvars["devices"] = diskutils.get_devices(srv)
                else:
                    hostvars["devices"] = []
                    hostvars["raw_journal_devices"] = []
                    for pair in diskutils.get_data_journal_pairs_iter(
                            srv, int(global_vars["journal_size"])):
                        hostvars["devices"].append(pair["data"])
                        hostvars["raw_journal_devices"].append(pair["journal"])

        return inventory

    def get_inventory_groups(self, cluster, servers, hints):
        base = super().get_inventory_groups(cluster, servers, hints)
        base["osds"] = servers

        return base

    def prepare_plugin(self):
        resource_path = pathutils.resource(
            "decapod_plugin_playbook_add_osd", "roles")
        resource_path.symlink_to(
            str(playbook_plugin.PATH_CEPH_ANSIBLE.joinpath("roles")))
Beispiel #15
0
class DeployCluster(playbook_plugin.CephAnsiblePlaybook):

    NAME = "Deploy Ceph cluster"
    DESCRIPTION = DESCRIPTION
    PUBLIC = True
    REQUIRED_SERVER_LIST = True
    SERVER_LIST_POLICY = playbook_plugin.ServerListPolicy.not_in_any_cluster
    CLUSTER_MUST_BE_DEPLOYED = False

    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_pre_execute(self, task):
        super().on_pre_execute(task)

        playbook_config = self.get_playbook_configuration(task)
        config = playbook_config.configuration
        cluster = playbook_config.cluster
        servers = playbook_config.servers
        servers = {srv.ip: srv for srv in servers}

        for name, group_vars in config["inventory"].items():
            if name == "_meta" or not group_vars:
                continue
            group_servers = [servers[ip] for ip in group_vars]
            cluster.add_servers(group_servers, name)

        if cluster.configuration.changed:
            cluster.save()

        # Save persistent cluster data. We will override existing settings
        # because cluster is created from scratch using this plugin.
        global_vars = config["global_vars"].copy()
        global_vars.pop("ceph_facts_template", None)
        global_vars.pop("restapi_template_local_path", None)

        data = cluster_data.ClusterData.find_one(cluster.model_id)
        data.global_vars = global_vars
        data.host_vars = config["inventory"].get("_meta",
                                                 {}).get("hostvars", {})
        data.save()

    def make_playbook_configuration(self, cluster, servers, hints):
        if cluster.configuration.state or cluster.server_list:
            raise exceptions.NotEmptyServerList(cluster.model_id)

        global_vars = self.make_global_vars(cluster, servers, hints)
        inventory = self.make_inventory(cluster, servers, hints, global_vars)

        if not monitor_secret.MonitorSecret.find_one(cluster.model_id):
            monitor_secret.MonitorSecret.upsert(
                cluster.model_id, monitor_secret.generate_monitor_secret())

        return global_vars, inventory

    def get_dynamic_inventory(self):
        # we need to inject monitor_secret here to avoid
        # showing it in interface
        if not self.playbook_config:
            raise exceptions.UnknownPlaybookConfiguration()

        configuration = self.playbook_config.configuration
        inventory = configuration["inventory"]
        secret = monitor_secret.MonitorSecret.find_one(
            configuration["global_vars"]["fsid"])
        if not secret:
            raise exceptions.SecretWasNotFound(
                configuration["global_vars"]["fsid"])

        all_hosts = set()
        for name, group_vars in inventory.items():
            if name == "_meta":
                continue
            all_hosts.update(group_vars)

        for hostname in all_hosts:
            dct = inventory["_meta"]["hostvars"].setdefault(hostname, {})
            dct["monitor_secret"] = secret.value

        return inventory

    def make_global_vars(self, cluster, servers, hints):
        result = super().make_global_vars(cluster, servers, hints)

        result["journal_collocation"] = False
        result["dmcrypt_journal_collocation"] = False
        result["dmcrypt_dedicated_journal"] = False
        result["raw_multi_journal"] = False
        if hints["dmcrypt"]:
            if hints["collocation"]:
                result["dmcrypt_journal_collocation"] = True
            else:
                result["dmcrypt_dedicated_journal"] = True
        elif hints["collocation"]:
            result["journal_collocation"] = True
        else:
            result["raw_multi_journal"] = True

        result["restapi_template_local_path"] = str(
            pathutils.resource("decapod_plugin_playbook_deploy_cluster",
                               "ceph-rest-api.service"))

        return result

    def make_inventory(self, cluster, servers, hints, global_vars):
        groups = self.get_inventory_groups(servers, hints)
        inventory = {"_meta": {"hostvars": {}}}

        for name, group_servers in groups.items():
            inventory[name] = [srv.ip for srv in group_servers]

        for srv in servers:
            hostvars = inventory["_meta"]["hostvars"].setdefault(srv.ip, {})
            hostvars["ansible_user"] = srv.username
            hostvars["monitor_address"] = networkutils.get_public_network_ip(
                srv, servers)

            if hints["collocation"]:
                hostvars["devices"] = diskutils.get_devices(srv)
            else:
                hostvars["devices"] = []
                hostvars["raw_journal_devices"] = []
                for pair in diskutils.get_data_journal_pairs_iter(
                        srv, int(global_vars["journal_size"])):
                    hostvars["devices"].append(pair["data"])
                    hostvars["raw_journal_devices"].append(pair["journal"])

        return inventory

    def get_inventory_groups(self, servers, hints):
        servers = sorted(servers, key=diskutils.get_server_storage_size)
        mons = servers[:hints["mon_count"]]
        osds = servers[hints["mon_count"]:]

        result = {
            "mons": mons,
            "osds": osds,
            "rgws": [],
            "mdss": [],
            "nfss": [],
            "rbdmirrors": [],
            "clients": [],
            "iscsi_gw": [],
            "restapis": []
        }
        if hints["rest_api"]:
            result["restapis"] = result["mons"]

        return result

    def prepare_plugin(self):
        resource_path = pathutils.resource(
            "decapod_plugin_playbook_deploy_cluster", "roles")
        resource_path.symlink_to(
            str(playbook_plugin.PATH_CEPH_ANSIBLE.joinpath("roles")))
Beispiel #16
0
class AddOSD(playbook_plugin.CephAnsiblePlaybook):

    NAME = "Add OSD to Ceph cluster"
    DESCRIPTION = DESCRIPTION
    PUBLIC = True
    REQUIRED_SERVER_LIST = True
    SERVER_LIST_POLICY = playbook_plugin.ServerListPolicy.not_in_other_cluster

    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_pre_execute(self, task):
        super().on_pre_execute(task)

        playbook_config = self.get_playbook_configuration(task)
        config = playbook_config.configuration["inventory"]
        cluster = playbook_config.cluster
        servers = playbook_config.servers
        servers = {srv.ip: srv for srv in servers}

        for name, group_vars in config.items():
            if name == "_meta" or not group_vars:
                continue
            group_servers = [servers[ip] for ip in group_vars]
            cluster.add_servers(group_servers, name)

        if cluster.configuration.changed:
            cluster.save()

        data = cluster_data.ClusterData.find_one(cluster.model_id)
        hostvars = config.get("_meta", {}).get("hostvars", {})
        for hostname, values in hostvars.items():
            data.update_host_vars(hostname, values)
        data.save()

    def make_playbook_configuration(self, cluster, servers, hints):
        cluster_config = cluster.configuration.make_api_structure()
        if not cluster_config.get("mons"):
            raise exceptions.NoMonitorsError(cluster.model_id)

        data = cluster_data.ClusterData.find_one(cluster.model_id)
        global_vars = self.make_global_vars(cluster, data, servers, hints)
        inventory = self.make_inventory(cluster, data, servers, hints,
                                        global_vars)

        return global_vars, inventory

    def make_global_vars(self, cluster, data, servers, hints):
        result = super().make_global_vars(cluster, servers, hints)
        result.update(data.global_vars)

        result["journal_collocation"] = False
        result["dmcrypt_journal_collocation"] = False
        result["dmcrypt_dedicated_journal"] = False
        result["raw_multi_journal"] = False
        result["ceph_version_verify"] = bool(hints["ceph_version_verify"])
        result["ceph_version_verify_packagename"] = \
            self.config["ceph_version_verify_packagename"]
        if hints["dmcrypt"]:
            if hints["collocation"]:
                result["dmcrypt_journal_collocation"] = True
            else:
                result["dmcrypt_dedicated_journal"] = True
        elif hints["collocation"]:
            result["journal_collocation"] = True
        else:
            result["raw_multi_journal"] = True

        if "journal_size" not in result:
            result["journal_size"] = self.config["journal"]["size"]
        result["ceph_facts_template"] = pathutils.resource(
            "decapod_common", "facts", "ceph_facts_module.py.j2")
        result["ceph_facts_template"] = str(result["ceph_facts_template"])

        return result

    def make_inventory(self, cluster, data, servers, hints, global_vars):
        groups = self.get_inventory_groups(cluster, servers, hints)
        inventory = {"_meta": {"hostvars": {}}}
        all_servers = server.ServerModel.cluster_servers(cluster.model_id)

        for name, group_servers in groups.items():
            for srv in group_servers:
                inventory.setdefault(name, []).append(srv.ip)

                hostvars = inventory["_meta"]["hostvars"].setdefault(
                    srv.ip, {})
                hostvars.update(data.get_host_vars(srv.ip))

                if "ansible_user" not in hostvars:
                    hostvars["ansible_user"] = srv.username
                if "monitor_interface" not in hostvars:
                    hostvars["monitor_interface"] = \
                        networkutils.get_public_network_if(srv, all_servers)

                if hints["collocation"]:
                    hostvars["devices"] = diskutils.get_devices(srv)
                else:
                    hostvars["devices"] = []
                    hostvars["raw_journal_devices"] = []
                    for pair in diskutils.get_data_journal_pairs_iter(
                            srv, int(global_vars["journal_size"])):
                        hostvars["devices"].append(pair["data"])
                        hostvars["raw_journal_devices"].append(pair["journal"])

        return inventory

    def get_inventory_groups(self, cluster, servers, hints):
        cluster_servers = server.ServerModel.cluster_servers(cluster.model_id)
        cluster_servers = {item._id: item for item in cluster_servers}

        mons = [
            cluster_servers[item["server_id"]]
            for item in cluster.configuration.state if item["role"] == "mons"
        ]

        return {
            "mons": mons,
            "osds": servers,
            "already_deployed": list(cluster_servers.values())
        }

    def prepare_plugin(self):
        resource_path = pathutils.resource("decapod_plugin_playbook_add_osd",
                                           "roles")
        resource_path.symlink_to(
            str(playbook_plugin.PATH_CEPH_ANSIBLE.joinpath("roles")))
Beispiel #17
0
class AddMon(playbook_plugin.CephAnsibleNewWithVerification):

    NAME = "Add monitor to the cluster"
    DESCRIPTION = DESCRIPTION
    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_pre_execute(self, task):
        super().on_pre_execute(["mons"], task)

        playbook_config = self.get_playbook_configuration(task)
        config = playbook_config.configuration["inventory"]
        cluster = playbook_config.cluster

        data = cluster_data.ClusterData.find_one(cluster.model_id)
        hostvars = config.get("_meta", {}).get("hostvars", {})
        for hostname, values in hostvars.items():
            data.update_host_vars(hostname, values)
        data.save()

    def get_dynamic_inventory(self):
        if not self.playbook_config:
            raise exceptions.UnknownPlaybookConfiguration()

        configuration = self.playbook_config.configuration
        inventory = configuration["inventory"]
        secret = get_monitor_secret(configuration["global_vars"]["fsid"])
        if not secret:
            raise exceptions.SecretWasNotFound(
                configuration["global_vars"]["fsid"])

        all_hosts = set()
        for name, group_vars in inventory.items():
            if name == "_meta":
                continue
            all_hosts.update(group_vars)

        for hostname in all_hosts:
            dct = inventory["_meta"]["hostvars"].setdefault(hostname, {})
            dct["monitor_secret"] = secret.value

        return inventory

    def make_inventory(self, cluster, data, servers, hints):
        groups = self.get_inventory_groups(cluster, servers, hints)
        inventory = {"_meta": {"hostvars": {}}}
        all_servers = server.ServerModel.cluster_servers(cluster.model_id)

        for name, group_servers in groups.items():
            for srv in group_servers:
                inventory.setdefault(name, []).append(srv.ip)

                hostvars = inventory["_meta"]["hostvars"].setdefault(
                    srv.ip, {})
                hostvars.update(data.get_host_vars(srv.ip))

                if "ansible_user" not in hostvars:
                    hostvars["ansible_user"] = srv.username
                if "monitor_interface" not in hostvars:
                    if "monitor_address" not in hostvars:
                        hostvars["monitor_address"] = \
                            networkutils.get_public_network_ip(
                                srv, all_servers)

        return inventory

    def get_inventory_groups(self, cluster, servers, hints):
        cluster_servers = server.ServerModel.cluster_servers(cluster.model_id)
        cluster_servers = {item._id: item for item in cluster_servers}

        old_mons = [
            cluster_servers[item["server_id"]]
            for item in cluster.configuration.state if item["role"] == "mons"
        ]
        mons = {srv.model_id: srv for srv in old_mons}
        mons.update((srv.model_id, srv) for srv in servers)

        return {
            "oldmons": old_mons,
            "mons": sorted(mons.values(), key=lambda srv: srv.ip),
            "already_deployed": list(cluster_servers.values())
        }

    def prepare_plugin(self):
        resource_path = pathutils.resource("decapod_plugin_playbook_add_mon",
                                           "roles")
        resource_path.symlink_to(
            str(playbook_plugin.PATH_CEPH_ANSIBLE.joinpath("roles")))
Beispiel #18
0
class AddMon(playbook_plugin.CephAnsiblePlaybook):

    NAME = "Add monitor to the cluster"
    DESCRIPTION = DESCRIPTION
    PUBLIC = True
    REQUIRED_SERVER_LIST = True
    SERVER_LIST_POLICY = playbook_plugin.ServerListPolicy.not_in_other_cluster

    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_pre_execute(self, task):
        super().on_pre_execute(task)

        playbook_config = self.get_playbook_configuration(task)
        config = playbook_config.configuration["inventory"]
        cluster = playbook_config.cluster
        servers = playbook_config.servers
        servers = {srv.ip: srv for srv in servers}

        for name, group_vars in config.items():
            if name == "_meta" or not group_vars:
                continue
            group_servers = [servers[ip] for ip in group_vars]
            cluster.add_servers(group_servers, name)

        if cluster.configuration.changed:
            cluster.save()

        data = cluster_data.ClusterData.find_one(cluster.model_id)
        hostvars = config.get("_meta", {}).get("hostvars", {})
        for hostname, values in hostvars.items():
            data.update_host_vars(hostname, values)
        data.save()

    def make_playbook_configuration(self, cluster, servers, hints):
        data = cluster_data.ClusterData.find_one(cluster.model_id)
        global_vars = self.make_global_vars(cluster, data, servers, hints)
        inventory = self.make_inventory(cluster, data, servers, hints)

        return global_vars, inventory

    def make_global_vars(self, cluster, data, servers, hints):
        result = super().make_global_vars(cluster, servers, hints)
        result.update(data.global_vars)

        result["ceph_version_verify"] = bool(hints["ceph_version_verify"])
        result["ceph_version_verify_packagename"] = \
            self.config["ceph_version_verify_packagename"]
        result["ceph_facts_template"] = pathutils.resource(
            "decapod_common", "facts", "ceph_facts_module.py.j2")
        result["ceph_facts_template"] = str(result["ceph_facts_template"])

        return result

    def get_dynamic_inventory(self):
        if not self.playbook_config:
            raise exceptions.UnknownPlaybookConfiguration()

        configuration = self.playbook_config.configuration
        inventory = configuration["inventory"]
        secret = get_monitor_secret(configuration["global_vars"]["fsid"])
        if not secret:
            raise exceptions.SecretWasNotFound(
                configuration["global_vars"]["fsid"])

        all_hosts = set()
        for name, group_vars in inventory.items():
            if name == "_meta":
                continue
            all_hosts.update(group_vars)

        for hostname in all_hosts:
            dct = inventory["_meta"]["hostvars"].setdefault(hostname, {})
            dct["monitor_secret"] = secret.value

        return inventory

    def make_inventory(self, cluster, data, servers, hints):
        groups = self.get_inventory_groups(cluster, servers, hints)
        inventory = {"_meta": {"hostvars": {}}}

        for name, group_servers in groups.items():
            for srv in group_servers:
                inventory.setdefault(name, []).append(srv.ip)

                hostvars = inventory["_meta"]["hostvars"].setdefault(
                    srv.ip, {})
                hostvars.update(data.get_host_vars(srv.ip))

                if "ansible_user" not in hostvars:
                    hostvars["ansible_user"] = srv.username
                if "monitor_interface" not in hostvars:
                    hostvars["monitor_interface"] = \
                        networkutils.get_public_network_if(srv, servers)

        return inventory

    def get_inventory_groups(self, cluster, servers, hints):
        cluster_servers = server.ServerModel.cluster_servers(cluster.model_id)
        cluster_servers = {item._id: item for item in cluster_servers}

        mons = [
            cluster_servers[item["server_id"]]
            for item in cluster.configuration.state if item["role"] == "mons"
        ]
        mons = {srv.model_id: srv for srv in mons}
        mons.update((srv.model_id, srv) for srv in servers)

        return {
            "mons": sorted(mons.values(), key=lambda srv: srv.ip),
            "already_deployed": list(cluster_servers.values())
        }

    def prepare_plugin(self):
        resource_path = pathutils.resource("decapod_plugin_playbook_add_mon",
                                           "roles")
        resource_path.symlink_to(
            str(playbook_plugin.PATH_CEPH_ANSIBLE.joinpath("roles")))
Beispiel #19
0
class AddOSD(playbook_plugin.CephAnsiblePlaybook):

    NAME = "Add OSD to Ceph cluster"
    DESCRIPTION = DESCRIPTION
    PUBLIC = True
    REQUIRED_SERVER_LIST = True

    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_pre_execute(self, task):
        super().on_pre_execute(task)

        playbook_config = self.get_playbook_configuration(task)
        config = playbook_config.configuration["inventory"]
        cluster = playbook_config.cluster
        servers = playbook_config.servers
        servers = {srv.ip: srv for srv in servers}

        for name, group_vars in config.items():
            if name == "_meta" or not group_vars:
                continue
            group_servers = [servers[ip] for ip in group_vars]
            cluster.add_servers(group_servers, name)

        if cluster.configuration.changed:
            cluster.save()

    def make_playbook_configuration(self, cluster, servers, hints):
        cluster_config = cluster.configuration.make_api_structure()
        if not cluster_config.get("mons"):
            raise exceptions.NoMonitorsError(cluster.model_id)

        global_vars = self.make_global_vars(cluster, servers, hints)
        inventory = self.make_inventory(cluster, servers, hints)

        return global_vars, inventory

    def make_global_vars(self, cluster, servers, hints):
        result = super().make_global_vars(cluster, servers, hints)

        result["journal_collocation"] = False
        result["dmcrypt_journal_collocation"] = False
        result["dmcrypt_dedicated_journal"] = False
        result["raw_multi_journal"] = False
        if hints["dmcrypt"]:
            if hints["collocation"]:
                result["dmcrypt_journal_collocation"] = True
            else:
                result["dmcrypt_dedicated_journal"] = True
        elif hints["collocation"]:
            result["journal_collocation"] = True
        else:
            result["raw_multi_journal"] = True

        result["journal_size"] = self.config["journal"]["size"]
        result["ceph_facts_template"] = pkg_resources.resource_filename(
            "decapod_common", "facts/ceph_facts_module.py.j2")

        return result

    def make_inventory(self, cluster, servers, hints):
        groups = self.get_inventory_groups(cluster, servers, hints)
        inventory = {"_meta": {"hostvars": {}}}
        all_servers = server.ServerModel.cluster_servers(cluster.model_id)

        for name, group_servers in groups.items():
            for srv in group_servers:
                inventory.setdefault(name, []).append(srv.ip)

                hostvars = inventory["_meta"]["hostvars"].setdefault(
                    srv.ip, {})
                hostvars["ansible_user"] = srv.username
                hostvars["monitor_interface"] = networkutils.get_public_network_if(  # NOQA
                    srv, all_servers)

                if hints["collocation"]:
                    hostvars["devices"] = diskutils.get_devices(srv)
                else:
                    hostvars["devices"] = []
                    hostvars["raw_journal_devices"] = []
                    for pair in diskutils.get_data_journal_pairs_iter(srv):
                        hostvars["devices"].append(pair["data"])
                        hostvars["raw_journal_devices"].append(pair["journal"])

        return inventory

    def get_inventory_groups(self, cluster, servers, hints):
        cluster_servers = server.ServerModel.cluster_servers(cluster.model_id)
        cluster_servers = {item._id: item for item in cluster_servers}

        mons = [
            cluster_servers[item["server_id"]]
            for item in cluster.configuration.state if item["role"] == "mons"
        ]

        return {"mons": mons, "osds": servers}
Beispiel #20
0
class DeployCluster(playbook_plugin.CephAnsiblePlaybook):

    NAME = "Deploy Ceph cluster"
    DESCRIPTION = DESCRIPTION
    PUBLIC = True
    REQUIRED_SERVER_LIST = True

    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_pre_execute(self, task):
        super().on_pre_execute(task)

        playbook_config = self.get_playbook_configuration(task)
        config = playbook_config.configuration["inventory"]
        cluster = playbook_config.cluster
        servers = playbook_config.servers
        servers = {srv.ip: srv for srv in servers}

        for name, group_vars in config.items():
            if name == "_meta" or not group_vars:
                continue
            group_servers = [servers[ip] for ip in group_vars]
            cluster.add_servers(group_servers, name)

        if cluster.configuration.changed:
            cluster.save()

    def make_playbook_configuration(self, cluster, servers, hints):
        if cluster.configuration.state or cluster.server_list:
            raise exceptions.NotEmptyServerList(cluster.model_id)

        global_vars = self.make_global_vars(cluster, servers, hints)
        inventory = self.make_inventory(cluster, servers, hints)

        if not monitor_secret.MonitorSecret.find_one(cluster.model_id):
            monitor_secret.MonitorSecret.upsert(
                cluster.model_id, monitor_secret.generate_monitor_secret())

        return global_vars, inventory

    def get_dynamic_inventory(self):
        # we need to inject monitor_secret here to avoid
        # showing it in interface
        if not self.playbook_config:
            raise exceptions.UnknownPlaybookConfiguration()

        configuration = self.playbook_config.configuration
        inventory = configuration["inventory"]
        secret = monitor_secret.MonitorSecret.find_one(
            configuration["global_vars"]["fsid"])
        if not secret:
            raise exceptions.SecretWasNotFound(
                configuration["global_vars"]["fsid"])

        all_hosts = set()
        for name, group_vars in inventory.items():
            if name == "_meta":
                continue
            all_hosts.update(group_vars)

        for hostname in all_hosts:
            dct = inventory["_meta"]["hostvars"].setdefault(hostname, {})
            dct["monitor_secret"] = secret.value

        return inventory

    def make_global_vars(self, cluster, servers, hints):
        result = super().make_global_vars(cluster, servers, hints)

        result["journal_collocation"] = False
        result["dmcrypt_journal_collocation"] = False
        result["dmcrypt_dedicated_journal"] = False
        result["raw_multi_journal"] = False
        if hints["dmcrypt"]:
            if hints["collocation"]:
                result["dmcrypt_journal_collocation"] = True
            else:
                result["dmcrypt_dedicated_journal"] = True
        elif hints["collocation"]:
            result["journal_collocation"] = True
        else:
            result["raw_multi_journal"] = True

        result["journal_size"] = self.config["journal"]["size"]
        result["ceph_facts_template"] = pkg_resources.resource_filename(
            "decapod_common", "facts/ceph_facts_module.py.j2")
        result["restapi_template_local_path"] = \
            pkg_resources.resource_filename(
                "decapod_plugin_playbook_deploy_cluster",
                "ceph-rest-api.service")

        return result

    def make_inventory(self, cluster, servers, hints):
        groups = self.get_inventory_groups(servers, hints)
        inventory = {"_meta": {"hostvars": {}}}

        for name, group_servers in groups.items():
            inventory[name] = [srv.ip for srv in group_servers]

        for srv in servers:
            hostvars = inventory["_meta"]["hostvars"].setdefault(srv.ip, {})
            hostvars["ansible_user"] = srv.username
            hostvars["monitor_interface"] = networkutils.get_public_network_if(
                srv, servers)

            if hints["collocation"]:
                hostvars["devices"] = diskutils.get_devices(srv)
            else:
                hostvars["devices"] = []
                hostvars["raw_journal_devices"] = []
                for pair in diskutils.get_data_journal_pairs_iter(srv):
                    hostvars["devices"].append(pair["data"])
                    hostvars["raw_journal_devices"].append(pair["journal"])

        return inventory

    def get_inventory_groups(self, servers, hints):
        servers = sorted(servers, key=diskutils.get_server_storage_size)
        mons = servers[:hints["mon_count"]]
        osds = servers[hints["mon_count"]:]

        result = {
            "mons": mons,
            "osds": osds,
            "rgws": [],
            "mdss": [],
            "nfss": [],
            "rbd_mirrors": [],
            "clients": [],
            "iscsi_gw": [],
            "restapis": []
        }
        if hints["rest_api"]:
            result["restapis"] = result["mons"]

        return result
Beispiel #21
0
class AddRbdmirror(playbook_plugin.CephAnsibleNewWithVerification):

    NAME = "Add RBD Mirroring host"
    DESCRIPTION = DESCRIPTION
    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_pre_execute(self, task):
        super().on_pre_execute(["rbdmirrors"], task)

        playbook_config = self.get_playbook_configuration(task)
        config = playbook_config.configuration["inventory"]
        cluster = playbook_config.cluster

        data = cluster_data.ClusterData.find_one(cluster.model_id)
        hostvars = config.get("_meta", {}).get("hostvars", {})
        for hostname, values in hostvars.items():
            data.update_host_vars(hostname, values)
        data.save()

    def get_dynamic_inventory(self):
        inventory = super().get_dynamic_inventory()

        hostvars = inventory["_meta"]["hostvars"]
        for data in hostvars.values():
            data["ceph_rbd_mirror_configure"] = False
            if "rbd_mirrors" not in data:
                continue

            reworked = {}
            for usercluster, pools in data["rbd_mirrors"].items():
                user, cluster = usercluster.split("@")
                pool_list = reworked.setdefault(user, {})
                for pool in pools:
                    pool_list.setdefault(cluster, []).append(pool)

            data["rbd_mirrors"] = reworked

        return inventory

    def get_extra_vars(self, task):
        extra_vars = super().get_extra_vars(task)
        extra_vars.pop("ceph_rbd_mirror_configure", None)
        extra_vars.setdefault("ceph_rbd_mirror_local_user", "admin")

        return extra_vars

    def make_global_vars(self, cluster, data, servers, hints):
        base = super().make_global_vars(cluster, data, servers, hints)
        base["add_peers"] = bool(hints["add_peers"])

        return base

    def make_inventory(self, cluster, data, servers, hints):
        groups = self.get_inventory_groups(cluster, servers, hints)
        inventory = {"_meta": {"hostvars": {}}}

        for name, group_servers in groups.items():
            for srv in group_servers:
                inventory.setdefault(name, []).append(srv.ip)

                hostvars = inventory["_meta"]["hostvars"].setdefault(
                    srv.ip, {})
                hostvars.update(data.get_host_vars(srv.ip))
                hostvars["ansible_user"] = srv.username

                if name == "rbdmirrors":
                    self.update_hostvars(hostvars, srv, hints)

        return inventory

    def get_inventory_groups(self, cluster, servers, hints):
        base = super().get_inventory_groups(cluster, servers, hints)
        base["rbdmirrors"] = servers

        return base

    def update_hostvars(self, hostvars, srv, hints):
        pools = hostvars.setdefault("rbd_mirrors", {})

        mirror_for = "{0}@{1}".format(
            hints["remote_username"], hints["remote_clustername"])
        pool_list = set(pools.get(mirror_for, []))
        pool_list.add(hints["poolname"])

        pools[mirror_for] = sorted(pool_list)
        hostvars["rbd_mirrors"] = pools
Beispiel #22
0
class CinderIntegration(playbook_plugin.CephAnsiblePlaybook):

    NAME = "Cinder Integration"
    DESCRIPTION = DESCRIPTION
    PUBLIC = True
    REQUIRED_SERVER_LIST = False
    SERVER_LIST_POLICY = playbook_plugin.ServerListPolicy.in_this_cluster

    HINTS = playbook_plugin_hints.Hints(HINTS_SCHEMA)

    def on_post_execute(self, task, exc_value, exc_type, exc_tb):
        try:
            if exc_value:
                raise exc_value

            configuration = self.get_playbook_configuration(task)

            cint = cinder_integration.CinderIntegration.find_one(
                configuration.cluster_id)
            cint.keyrings.clear()
            cint.config = self.fetchdir.joinpath("ceph.conf").read_text()
            for filename in self.fetchdir.glob("*.keyring"):
                cint.keyrings[filename.name] = filename.read_text()
            cint.save()
        finally:
            super().on_post_execute(task, exc_value, exc_type, exc_tb)

    def make_playbook_configuration(self, cluster, servers, hints):
        data = cluster_data.ClusterData.find_one(cluster.model_id)
        global_vars = self.make_global_vars(cluster, data, servers, hints)
        inventory = self.make_inventory(cluster, data, servers, hints)

        return global_vars, inventory

    def make_global_vars(self, cluster, data, servers, hints):
        return {
            "cluster": data.global_vars.get("cluster", cluster.name)
        }

    def make_inventory(self, cluster, data, servers, hints):
        groups = self.get_inventory_groups(cluster, servers, hints)
        inventory = {"_meta": {"hostvars": {}}}
        config = self.config.copy()

        for config_key in "pools", "clients":
            if not hints["glance"]:
                config[config_key].pop("images", None)
            if not hints["nova"]:
                config[config_key].pop("compute", None)
            if not hints["cinder"]:
                config[config_key].pop("volumes", None)

        for name, group_servers in groups.items():
            for srv in group_servers:
                inventory.setdefault(name, []).append(srv.ip)

                hostvars = inventory["_meta"]["hostvars"].setdefault(
                    srv.ip, {})
                hostvars["ansible_user"] = srv.username
                hostvars.update(config)

        return inventory

    def get_inventory_groups(self, cluster, servers, hints):
        cluster_servers = server.ServerModel.cluster_servers(cluster.model_id)
        cluster_servers = {item._id: item for item in cluster_servers}

        mons = [
            cluster_servers[item["server_id"]]
            for item in cluster.configuration.state if item["role"] == "mons"
        ]

        return {"mons": [mons[0]]}