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
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)
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
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)
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)
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)
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)
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
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
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