From 99aadb094a435fa0c07657b82648a13aa749df80 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 4 Mar 2020 08:48:58 -0600 Subject: [PATCH] cephadm: include timestamps for started, deploy started == when the container started deployed == when the container was (most recently) (re)deployed Signed-off-by: Sage Weil --- src/cephadm/cephadm | 60 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/src/cephadm/cephadm b/src/cephadm/cephadm index 36552890347..1180eb5ce72 100755 --- a/src/cephadm/cephadm +++ b/src/cephadm/cephadm @@ -35,6 +35,7 @@ You can invoke cephadm in two ways: """ import argparse +import datetime import fcntl import json import logging @@ -80,6 +81,8 @@ else: container_path = '' cached_stdin = None +DATEFMT = '%Y-%m-%dT%H:%M:%S.%f' + class Error(Exception): pass @@ -608,6 +611,53 @@ def pathify(p): return os.path.join(os.getcwd(), p) return p +def get_file_timestamp(fn): + try: + mt = os.path.getmtime(fn) + return datetime.datetime.fromtimestamp( + mt, tz=datetime.timezone.utc + ).strftime(DATEFMT) + except Exception as e: + return None + +def try_convert_datetime(s): + # This is super irritating because + # 1) podman and docker use different formats + # 2) python's strptime can't parse either one + # + # I've seen: + # docker 18.09.7: 2020-03-03T09:21:43.636153304Z + # podman 1.7.0: 2020-03-03T15:52:30.136257504-06:00 + # 2020-03-03 15:52:30.136257504 -0600 CST + # (In the podman case, there is a different string format for + # 'inspect' and 'inspect --format {{.Created}}'!!) + + # In *all* cases, the 9 digit second precision is too much for + # python's strptime. Shorten it to 6 digits. + p = re.compile(r'(\.\d\d\d\d\d\d)\d\d\d') + s = p.sub(r'\1', s) + + # replace trailling Z with -0000, since (on python 3.6.8) it won't parse + if s and s[-1] == 'Z': + s = s[:-1] + '-0000' + + # cut off the redundnat 'CST' part that strptime can't parse, if + # present. + v = s.split(' ') + s = ' '.join(v[0:3]) + + # try parsing with several format strings + fmts = [ + '%Y-%m-%dT%H:%M:%S.%f%z', + '%Y-%m-%d %H:%M:%S.%f %z', + ] + for f in fmts: + try: + # return timestamp normalized to UTC, rendered as DATEFMT. + return datetime.datetime.strptime(s, f).astimezone(tz=datetime.timezone.utc).strftime(DATEFMT) + except ValueError: + pass + return None def get_podman_version(): # type: () -> Tuple[int, ...] @@ -2346,6 +2396,7 @@ def list_daemons(detail=True, legacy_dir=None): image_name = None image_id = None version = None + start_stamp = None if 'podman' in container_path and get_podman_version() < (1, 6, 2): image_field = '.ImageID' @@ -2355,14 +2406,16 @@ def list_daemons(detail=True, legacy_dir=None): out, err, code = call( [ container_path, 'inspect', - '--format', '{{.Id}},{{.Config.Image}},{{%s}},{{index .Config.Labels "io.ceph.version"}}' % image_field, + '--format', '{{.Id}},{{.Config.Image}},{{%s}},{{.Created}},{{index .Config.Labels "io.ceph.version"}}' % image_field, 'ceph-%s-%s' % (fsid, j) ], verbose_on_failure=False) if not code: - (container_id, image_name, image_id, version) = out.strip().split(',') + (container_id, image_name, image_id, start, + version) = out.strip().split(',') image_id = normalize_container_id(image_id) daemon_type = name.split('.', 1)[0] + start_stamp = try_convert_datetime(start) if daemon_type in Ceph.daemons: if not version or '.' not in version: version = seen_versions.get(image_id, None) @@ -2388,6 +2441,9 @@ def list_daemons(detail=True, legacy_dir=None): i['container_image_name'] = image_name i['container_image_id'] = image_id i['version'] = version + i['started'] = start_stamp + i['deployed'] = get_file_timestamp( + os.path.join(data_dir, fsid, j, 'unit.image')) ls.append(i) # /var/lib/rook -- 2.39.5