Example #1
0
    def get_inventory(self, node_filter=None, refresh=False):
        """
        Note that this will raise an exception (e.g. if the salt-api is down,
        or the username/password is incorret).  Same for other methods.
        Callers should expect this and react appropriately.  The orchestrator
        cli, for example, just prints the traceback in the console, so the
        user at least sees the error.
        """
        self.inventory_cache.remove_outdated()
        if not self.inventory_cache.any_outdated() and not refresh:
            if node_filter is None:
                return orchestrator.TrivialReadCompletion(
                    orchestrator.InventoryNode.from_nested_items(self.inventory_cache.items()))
            elif node_filter.labels is None:
                try:
                    return orchestrator.TrivialReadCompletion(
                        orchestrator.InventoryNode.from_nested_items(
                            self.inventory_cache.items_filtered(node_filter.nodes)))
                except KeyError:
                    # items_filtered() will raise KeyError if passed a node name that doesn't exist
                    return orchestrator.TrivialReadCompletion([])

        def process_result(event_data):
            result = []
            if event_data['success']:
                for node_name, node_devs in event_data["return"].items():
                    if node_filter is None:
                        self.inventory_cache[node_name] = orchestrator.OutdatableData(node_devs)
                    devs = orchestrator.InventoryDevice.from_ceph_volume_inventory_list(node_devs)
                    result.append(orchestrator.InventoryNode(node_name, devs))
            else:
                self.log.error(event_data['return'])
            return result

        with self._completion_lock:
            c = DeepSeaReadCompletion(process_result)

            nodes = []
            roles = []
            if node_filter:
                nodes = node_filter.nodes
                roles = node_filter.labels

            resp = self._do_request_with_login("POST", data = {
                "client": "runner_async",
                "fun": "mgr_orch.get_inventory",
                "nodes": nodes,
                "roles": roles
            })

            # ['return'][0]['tag'] in the resonse JSON is what we need to match
            # on when looking for the result event (e.g.: "salt/run/20181018074024331230")
            self._all_completions["{}/ret".format(resp.json()['return'][0]['tag'])] = c

            return c
Example #2
0
 def remote_from_orchestrator_cli_self_test(self, what):
     import orchestrator
     if what == 'OrchestratorError':
         c = orchestrator.TrivialReadCompletion(result=None)
         c.fail(orchestrator.OrchestratorError('hello, world'))
         return c
     elif what == "ZeroDivisionError":
         c = orchestrator.TrivialReadCompletion(result=None)
         c.fail(ZeroDivisionError('hello, world'))
         return c
     assert False, repr(what)
Example #3
0
    def describe_service(self, service_type=None, service_id=None, node_name=None, refresh=False):

        # Note: describe_service() does *not* support OSDs.  This is because
        # DeepSea doesn't really record what OSDs are deployed where; Ceph is
        # considered the canonical source of this information, so having this
        # function query OSD information from DeepSea doesn't make a lot of
        # sense (DeepSea would have to call back into Ceph).

        assert service_type in ("mon", "mgr", "mds", "rgw", None), service_type + " unsupported"

        self.service_cache.remove_outdated()
        if not self.service_cache.any_outdated() and not refresh:
            # Let's hope the services are complete.
            try:
                node_filter = [node_name] if node_name else None
                services_by_node = [d[1].data for d in self.service_cache.items_filtered(node_filter)]
                services = [orchestrator.ServiceDescription.from_json(s) for services in services_by_node for s in services]
                services = [s for s in services if
                            (True if service_type is None else s.service_type == service_type) and
                            (True if service_id is None else s.service_instance == service_id)]
                return orchestrator.TrivialReadCompletion(services)
            except KeyError:
                # items_filtered() will raise KeyError if passed a node name that doesn't exist
                return orchestrator.TrivialReadCompletion([])

        def process_result(event_data):
            result = []
            if event_data['success']:
                for node_name, service_info in event_data["return"].items():
                    node_service_cache = []
                    for service_type, service_instance in service_info.items():
                        desc = orchestrator.ServiceDescription(nodename=node_name,
                                                               service_instance=service_instance,
                                                               service_type=service_type)
                        result.append(desc)
                        node_service_cache.append(desc.to_json())
                    self.service_cache[node_name] = orchestrator.OutdatableData(node_service_cache)
            else:
                self.log.error(event_data['return'])
            return result

        with self._completion_lock:
            c = DeepSeaReadCompletion(process_result)

            resp = self._do_request_with_login("POST", data = {
                "client": "runner_async",
                "fun": "mgr_orch.describe_service",
                "role": service_type,
                "service_id": service_id,
                "node": node_name
            })
            self._all_completions["{}/ret".format(resp.json()['return'][0]['tag'])] = c

            return c
Example #4
0
 def service_action(self,
                    action,
                    service_type,
                    service_name=None,
                    service_id=None):
     self.log.debug('service_action action %s type %s name %s id %s' %
                    (action, service_type, service_name, service_id))
     if action == 'reload':
         return orchestrator.TrivialReadCompletion(["Reload is a no-op"])
     daemons = self._get_services(service_type,
                                  service_name=service_name,
                                  service_id=service_id)
     results = []
     for d in daemons:
         results.append(
             self._worker_pool.apply_async(
                 self._service_action,
                 (d.service_type, d.service_instance, d.nodename, action)))
     if not results:
         n = service_name
         if n:
             n += '-*'
         raise OrchestratorError('Unable to find %s.%s%s daemon(s)' %
                                 (service_type, service_id, n))
     return SSHWriteCompletion(results)
Example #5
0
 def describe_service(self,
                      service_type=None,
                      service_id=None,
                      node_name=None,
                      refresh=False):
     if service_type not in ("mds", "osd", "mgr", "mon", "nfs", None):
         raise orchestrator.OrchestratorValidationError(service_type +
                                                        " unsupported")
     result = self._get_services(service_type, service_id, node_name)
     return orchestrator.TrivialReadCompletion(result)
Example #6
0
    def describe_service(self,
                         service_type=None,
                         service_id=None,
                         node_name=None,
                         refresh=False):

        if service_type not in ("mds", "osd", "mgr", "mon", "nfs", None):
            raise orchestrator.OrchestratorValidationError(service_type +
                                                           " unsupported")

        #daemons = self.get_daemons()
        daemons = {}
        for host, _ in self._get_hosts():
            self.log.info("refresh stale daemons for '{}'".format(host))
            out, code = self._run_ceph_daemon(host,
                                              'mon',
                                              'ls', [],
                                              no_fsid=True)
            daemons[host] = json.loads(''.join(out))

        result = []
        for host, ls in daemons.items():
            for d in ls:
                if not d['style'].startswith('ceph-daemon'):
                    self.log.debug('ignoring non-ceph-daemon on %s: %s' %
                                   (host, d))
                    continue
                if d['fsid'] != self._cluster_fsid:
                    self.log.debug('ignoring foreign daemon on %s: %s' %
                                   (host, d))
                    continue
                self.log.debug('including %s' % d)
                sd = orchestrator.ServiceDescription()
                sd.service_type = d['name'].split('.')[0]
                if service_type and service_type != sd.service_type:
                    continue
                if '.' in d['name']:
                    sd.service_instance = d['name'].split('.')[1]
                else:
                    sd.service_instance = host  # e.g., crash
                if service_id and service_id != sd.service_instance:
                    continue
                sd.nodename = host
                sd.container_id = d['container_id']
                sd.version = d['version']
                sd.status_desc = d['state']
                sd.status = {
                    'running': 1,
                    'inactive': 0,
                    'error': -1,
                    'unknown': -1,
                }[d['state']]
                result.append(sd)

        return orchestrator.TrivialReadCompletion(result)
Example #7
0
    def get_hosts(self):
        """
        Return a list of hosts managed by the orchestrator.

        Notes:
          - skip async: manager reads from cache.

        TODO:
          - InventoryNode probably needs to be able to report labels
        """
        nodes = [
            orchestrator.InventoryNode(host_name, [])
            for host_name in self.inventory_cache
        ]
        return orchestrator.TrivialReadCompletion(nodes)
Example #8
0
    def self_test(self):
        old_orch = self._select_orchestrator()
        self._set_backend('')
        assert self._select_orchestrator() is None
        self._set_backend(old_orch)

        e = self.remote('selftest', 'remote_from_orchestrator_cli_self_test', "ZeroDivisionError")
        try:
            orchestrator.raise_if_exception(e)
            assert False
        except ZeroDivisionError as e:
            assert e.args == ('hello', 'world')

        e = self.remote('selftest', 'remote_from_orchestrator_cli_self_test', "OrchestratorError")
        try:
            orchestrator.raise_if_exception(e)
            assert False
        except orchestrator.OrchestratorError as e:
            assert e.args == ('hello', 'world')

        c = orchestrator.TrivialReadCompletion(result=True)
        assert c.has_result
Example #9
0
    def _get_services(self,
                      service_type=None,
                      service_name=None,
                      service_id=None,
                      node_name=None,
                      refresh=False):
        hosts = []
        wait_for = []
        for host, host_info in self.service_cache.items_filtered():
            hosts.append(host)
            if host_info.outdated(self.service_cache_timeout) or refresh:
                self.log.info("refresing stale services for '{}'".format(host))
                wait_for.append(
                    SSHReadCompletion(
                        self._worker_pool.apply_async(
                            self._refresh_host_services, (host, ))))
            else:
                self.log.debug('have recent services for %s: %s' %
                               (host, host_info.data))
                wait_for.append(
                    orchestrator.TrivialReadCompletion([host_info.data]))
        self._orchestrator_wait(wait_for)

        services = {}
        for host, c in zip(hosts, wait_for):
            services[host] = c.result[0]

        result = []
        for host, ls in services.items():
            for d in ls:
                if not d['style'].startswith('ceph-daemon'):
                    self.log.debug('ignoring non-ceph-daemon on %s: %s' %
                                   (host, d))
                    continue
                if d['fsid'] != self._cluster_fsid:
                    self.log.debug('ignoring foreign daemon on %s: %s' %
                                   (host, d))
                    continue
                self.log.debug('including %s' % d)
                sd = orchestrator.ServiceDescription()
                sd.service_type = d['name'].split('.')[0]
                if service_type and service_type != sd.service_type:
                    continue
                if '.' in d['name']:
                    sd.service_instance = '.'.join(d['name'].split('.')[1:])
                else:
                    sd.service_instance = host  # e.g., crash
                if service_id and service_id != sd.service_instance:
                    continue
                if service_name and not sd.service_instance.startswith(
                        service_name + '.'):
                    continue
                sd.nodename = host
                sd.container_id = d['container_id']
                sd.version = d['version']
                sd.status_desc = d['state']
                sd.status = {
                    'running': 1,
                    'stopped': 0,
                    'error': -1,
                    'unknown': -1,
                }[d['state']]
                result.append(sd)
        return result
Example #10
0
    def describe_service(self, service_type=None, service_id=None, node_name=None, refresh=False):

        # Note: describe_service() does *not* support OSDs.  This is because
        # DeepSea doesn't really record what OSDs are deployed where; Ceph is
        # considered the canonical source of this information, so having this
        # function query OSD information from DeepSea doesn't make a lot of
        # sense (DeepSea would have to call back into Ceph).

        assert service_type in ("mon", "mgr", "mds", "rgw", "nfs", "iscsi", None), service_type + " unsupported"

        def _deepsea_to_ceph(service):
            if service == "ganesha":
                return "nfs"
            elif service == "igw":
                return "iscsi"
            else:
                return service

        # presently unused
        def _ceph_to_deepsea(service):
            if service == "nfs":
                return "ganesha"
            elif service == "iscsi":
                return "igw"
            else:
                return service

        self.service_cache.remove_outdated()
        if not self.service_cache.any_outdated() and not refresh:
            # Let's hope the services are complete.
            try:
                node_filter = [node_name] if node_name else None
                services_by_node = [d[1].data for d in self.service_cache.items_filtered(node_filter)]
                services = [orchestrator.ServiceDescription.from_json(s) for services in services_by_node for s in services]
                services = [s for s in services if
                            (True if service_type is None else s.service_type == service_type) and
                            (True if service_id is None else s.service_instance == service_id)]
                return orchestrator.TrivialReadCompletion(services)
            except KeyError:
                # items_filtered() will raise KeyError if passed a node name that doesn't exist
                return orchestrator.TrivialReadCompletion([])

        def process_result(event_data):
            result = []
            if event_data['success']:
                for service_node, service_info in event_data["return"].items():
                    node_service_cache = []
                    for this_service_type, service_dict in service_info.items():
                        if isinstance(service_dict, str):
                            # map old form where deepsea only returned service IDs
                            # to new form where it retuns a dict
                            service_dict = { 'service_instance': service_dict }
                        desc = orchestrator.ServiceDescription(nodename=service_node,
                                                               service_instance=service_dict['service_instance'],
                                                               service_type=_deepsea_to_ceph(this_service_type),
                                                               # the following may or may not be present
                                                               container_id=service_dict.get('container_id', None),
                                                               service=service_dict.get('service', None),
                                                               version=service_dict.get('version', None),
                                                               rados_config_location=service_dict.get('rados_config_location', None),
                                                               service_url = service_dict.get('service_url', None),
                                                               status=service_dict.get('status', None),
                                                               status_desc=service_dict.get('status_desc', None)
                                                               )
                        # Always add every service to the cache...
                        node_service_cache.append(desc.to_json())
                        # ...but only return the ones the caller asked for
                        if ((service_type is None or desc.service_type == service_type) and
                            (service_id is None or desc.service_instance == service_id) and
                            (node_name is None or desc.nodename == node_name)):
                            result.append(desc)

                    self.service_cache[service_node] = orchestrator.OutdatableData(node_service_cache)
            else:
                self.log.error(event_data['return'])
            return result

        with self._completion_lock:
            c = DeepSeaReadCompletion(process_result)

            # Always request all services, so we always have all services cached.
            resp = self._do_request_with_login("POST", data = {
                "client": "runner_async",
                "fun": "mgr_orch.describe_service"
            })
            self._all_completions["{}/ret".format(resp.json()['return'][0]['tag'])] = c

            return c