From 4296962edc8838c1340abf0fdb6ea3e2f744f668 Mon Sep 17 00:00:00 2001 From: John Spray Date: Wed, 7 Jun 2017 12:48:40 -0400 Subject: [PATCH 1/4] mgr/dashboard: keep a global librados instance ...so that classes that need one aren't creating their own all the time. Signed-off-by: John Spray --- src/pybind/mgr/dashboard/module.py | 31 ++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/pybind/mgr/dashboard/module.py b/src/pybind/mgr/dashboard/module.py index 7b0e17b295825..948449f9b50d8 100644 --- a/src/pybind/mgr/dashboard/module.py +++ b/src/pybind/mgr/dashboard/module.py @@ -31,6 +31,7 @@ def global_instance(): from types import OsdMap, NotFound, Config, FsMap, MonMap, \ PgSummary, Health, MonStatus +import rados from rbd_ls import RbdLs from cephfs_clients import CephFSClients @@ -63,6 +64,9 @@ def __init__(self, *args, **kwargs): self.log_buffer = collections.deque(maxlen=LOG_BUFFER_SIZE) + # Keep a librados instance for those that need it. + self._rados = None + # Stateful instances of RbdLs, hold cached results. Key to dict # is pool name. self.rbd_ls = {} @@ -75,6 +79,28 @@ def __init__(self, *args, **kwargs): self.pool_stats = defaultdict(lambda: defaultdict( lambda: collections.deque(maxlen=10))) + @property + def rados(self): + """ + A librados instance to be shared by any classes within + this mgr module that want one. + """ + if self._rados: + return self._rados + + from mgr_module import ceph_state + ctx_capsule = ceph_state.get_context() + self._rados = rados.Rados(context=ctx_capsule) + self._rados.connect() + + return self._rados + + def get_localized_config(self, key): + r = self.get_config(self.get_mgr_id() + '/' + key) + if r is None: + r = self.get_config(key) + return r + def update_pool_stats(self): df = global_instance().get("df") pool_stats = dict([(p['id'], p['stats']) for p in df['pools']]) @@ -145,6 +171,11 @@ def shutdown(self): cherrypy.engine.exit() log.info("Stopped server") + log.info("Stopping librados...") + if self._rados: + self._rados.shutdown() + log.info("Stopped librados.") + def get_latest(self, daemon_type, daemon_name, stat): data = self.get_counter(daemon_type, daemon_name, stat)[stat] if data: From f23c68f168906c620627319b58f3e45b11c91a17 Mon Sep 17 00:00:00 2001 From: John Spray Date: Wed, 7 Jun 2017 12:50:38 -0400 Subject: [PATCH 2/4] mgr/dashboard: fix error handling in remote_view_cache Signed-off-by: John Spray --- src/pybind/mgr/dashboard/remote_view_cache.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/pybind/mgr/dashboard/remote_view_cache.py b/src/pybind/mgr/dashboard/remote_view_cache.py index 07858ab4ed80e..294975d762cdd 100644 --- a/src/pybind/mgr/dashboard/remote_view_cache.py +++ b/src/pybind/mgr/dashboard/remote_view_cache.py @@ -22,13 +22,12 @@ def run(self): self._view.value = None self._view.value_when = None self._view.getter_thread = None - self.event.set() - - with self._view.lock: - self._view.latency = t1 - t0 - self._view.value = val - self._view.value_when = datetime.datetime.now() - self._view.getter_thread = None + else: + with self._view.lock: + self._view.latency = t1 - t0 + self._view.value = val + self._view.value_when = datetime.datetime.now() + self._view.getter_thread = None self.event.set() @@ -133,4 +132,4 @@ def _init(self): pass def _get(self): - raise NotImplementedError() \ No newline at end of file + raise NotImplementedError() From f175090596f1115be5c8a243ff95a317d829f4e9 Mon Sep 17 00:00:00 2001 From: John Spray Date: Thu, 8 Jun 2017 08:06:12 -0400 Subject: [PATCH 3/4] mgr/dashboard: fix server view on funny version format Signed-off-by: John Spray --- src/pybind/mgr/dashboard/servers.html | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/pybind/mgr/dashboard/servers.html b/src/pybind/mgr/dashboard/servers.html index 81a93e787c69f..301bca24bbe12 100644 --- a/src/pybind/mgr/dashboard/servers.html +++ b/src/pybind/mgr/dashboard/servers.html @@ -24,9 +24,16 @@ }; rivets.formatters.short_version = function(version) { - // Turn "ceph version 1.2.3-g9asdasd (as98d7a0s8d7)" - // into "1.2.3-g9asdasd" - return /ceph version (.+) \(.+\)$/.exec(version)[1]; + // Expect "ceph version 1.2.3-g9asdasd (as98d7a0s8d7)" + var result = /ceph version\s+([^ ]+)\s+\(.+\)/.exec(version); + if (result) { + // Return the "1.2.3-g9asdasd" part + return result[1]; + } else { + // Unexpected format, pass it through + return version; + } + return }; rivets.bind($("#content"), content_data); @@ -74,4 +81,4 @@

-{% endblock %} \ No newline at end of file +{% endblock %} From b48011d60b72f0b8a87340ad0e406a099a902032 Mon Sep 17 00:00:00 2001 From: John Spray Date: Thu, 8 Jun 2017 08:57:50 -0400 Subject: [PATCH 4/4] mgr/dashboard: remove confusing SyncObject stuff This was a hangover from when these wrapper classes were borrowed from Calamari, which used these versions/equality functions to work out when to go fetch data from the ceph cluster. Signed-off-by: John Spray --- src/pybind/mgr/dashboard/module.py | 15 ++++---- src/pybind/mgr/dashboard/types.py | 58 ++++++++---------------------- 2 files changed, 23 insertions(+), 50 deletions(-) diff --git a/src/pybind/mgr/dashboard/module.py b/src/pybind/mgr/dashboard/module.py index 948449f9b50d8..79077ca84c3b6 100644 --- a/src/pybind/mgr/dashboard/module.py +++ b/src/pybind/mgr/dashboard/module.py @@ -129,26 +129,26 @@ def get_sync_object(self, object_type, path=None): data['crush'] = self.get("osd_map_crush") data['crush_map_text'] = self.get("osd_map_crush_map_text") data['osd_metadata'] = self.get("osd_metadata") - obj = OsdMap(data['epoch'], data) + obj = OsdMap(data) elif object_type == Config: data = self.get("config") - obj = Config(0, data) + obj = Config( data) elif object_type == MonMap: data = self.get("mon_map") - obj = MonMap(data['epoch'], data) + obj = MonMap(data) elif object_type == FsMap: data = self.get("fs_map") - obj = FsMap(data['epoch'], data) + obj = FsMap(data) elif object_type == PgSummary: data = self.get("pg_summary") self.log.debug("JSON: {0}".format(data)) - obj = PgSummary(0, data) + obj = PgSummary(data) elif object_type == Health: data = self.get("health") - obj = Health(0, json.loads(data['json'])) + obj = Health(json.loads(data['json'])) elif object_type == MonStatus: data = self.get("mon_status") - obj = MonStatus(0, json.loads(data['json'])) + obj = MonStatus(json.loads(data['json'])) else: raise NotImplementedError(object_type) @@ -577,6 +577,7 @@ def servers(self): ) def _servers(self): + servers = global_instance().list_servers() return { 'servers': global_instance().list_servers() } diff --git a/src/pybind/mgr/dashboard/types.py b/src/pybind/mgr/dashboard/types.py index 9ed2629e50209..79122266dc726 100644 --- a/src/pybind/mgr/dashboard/types.py +++ b/src/pybind/mgr/dashboard/types.py @@ -33,47 +33,19 @@ def wrapper(*args): return wrapper -class SyncObject(object): - """ - An object from a Ceph cluster that we are maintaining - a copy of on the Calamari server. - - We wrap these JSON-serializable objects in a python object to: - - - Decorate them with things like id-to-entry dicts - - Have a generic way of seeing the version of an object +OSD_FLAGS = ('pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', + 'norecover', 'noscrub', 'nodeep-scrub') - """ - def __init__(self, version, data): - self.version = version +class DataWrapper(object): + def __init__(self, data): self.data = data - @classmethod - def cmp(cls, a, b): - """ - Slight bastardization of cmp. Takes two versions, - and returns a cmp-like value, except that if versions - are not sortable we only return 0 or 1. - """ - # Version is something unique per version (like a hash) - return 1 if a != b else 0 - - -class VersionedSyncObject(SyncObject): - @classmethod - def cmp(cls, a, b): - # Version is something numeric like an epoch - return cmp(a, b) - - -OSD_FLAGS = ('pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norecover', 'noscrub', 'nodeep-scrub') - -class OsdMap(VersionedSyncObject): +class OsdMap(DataWrapper): str = OSD_MAP - def __init__(self, version, data): - super(OsdMap, self).__init__(version, data) + def __init__(self, data): + super(OsdMap, self).__init__(data) if data is not None: self.osds_by_id = dict([(o['osd'], o) for o in data['osds']]) self.pools_by_id = dict([(p['pool'], p) for p in data['pools']]) @@ -204,37 +176,37 @@ def osd_pools(self): return osds -class FsMap(VersionedSyncObject): +class FsMap(DataWrapper): str = 'fs_map' -class MonMap(VersionedSyncObject): +class MonMap(DataWrapper): str = 'mon_map' -class MonStatus(VersionedSyncObject): +class MonStatus(DataWrapper): str = 'mon_status' - def __init__(self, version, data): - super(MonStatus, self).__init__(version, data) + def __init__(self, data): + super(MonStatus, self).__init__(data) if data is not None: self.mons_by_rank = dict([(m['rank'], m) for m in data['monmap']['mons']]) else: self.mons_by_rank = {} -class PgSummary(SyncObject): +class PgSummary(DataWrapper): """ A summary of the state of PGs in the cluster, reported by pool and by OSD. """ str = 'pg_summary' -class Health(SyncObject): +class Health(DataWrapper): str = 'health' -class Config(SyncObject): +class Config(DataWrapper): str = 'config'