mirror of
https://github.com/ceph/ceph
synced 2025-04-08 02:31:44 +00:00
cephadm: log to file
Log configuration improved adding a log file with debug level. Signed-off-by: Juan Miguel Olmo Martínez <jolmomar@redhat.com>
This commit is contained in:
parent
1e4e394699
commit
81a7df0498
src/cephadm
@ -44,6 +44,7 @@ import fcntl
|
||||
import ipaddress
|
||||
import json
|
||||
import logging
|
||||
from logging.config import dictConfig
|
||||
import os
|
||||
import platform
|
||||
import pwd
|
||||
@ -93,6 +94,36 @@ cached_stdin = None
|
||||
|
||||
DATEFMT = '%Y-%m-%dT%H:%M:%S.%f'
|
||||
|
||||
# Log and console output config
|
||||
logging_config = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': True,
|
||||
'formatters': {
|
||||
'cephadm': {
|
||||
'format': '%(asctime)s %(levelname)s %(message)s'
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'console':{
|
||||
'level':'INFO',
|
||||
'class':'logging.StreamHandler',
|
||||
},
|
||||
'log_file': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'formatter': 'cephadm',
|
||||
'filename': '%s/cephadm.log' % LOG_DIR,
|
||||
'maxBytes': 1024000,
|
||||
'backupCount': 1,
|
||||
}
|
||||
},
|
||||
'loggers': {
|
||||
'': {
|
||||
'level': 'DEBUG',
|
||||
'handlers': ['console', 'log_file'],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class termcolor:
|
||||
yellow = '\033[93m'
|
||||
@ -840,7 +871,8 @@ def call(command, # type: List[str]
|
||||
assert False
|
||||
except (IOError, OSError):
|
||||
pass
|
||||
logger.debug(desc + ':profile rt=%s, stop=%s, exit=%s, reads=%s'
|
||||
if verbose:
|
||||
logger.debug(desc + ':profile rt=%s, stop=%s, exit=%s, reads=%s'
|
||||
% (time.time()-start_time, stop, process.poll(), reads))
|
||||
|
||||
returncode = process.wait()
|
||||
@ -2572,7 +2604,7 @@ def command_bootstrap():
|
||||
raise Error('hostname is a fully qualified domain name (%s); either fix (e.g., "sudo hostname %s" or similar) or pass --allow-fqdn-hostname' % (hostname, hostname.split('.')[0]))
|
||||
mon_id = args.mon_id or hostname
|
||||
mgr_id = args.mgr_id or generate_service_id()
|
||||
logging.info('Cluster fsid: %s' % fsid)
|
||||
logger.info('Cluster fsid: %s' % fsid)
|
||||
ipv6 = False
|
||||
|
||||
l = FileLock(fsid)
|
||||
@ -3471,6 +3503,7 @@ def command_list_networks():
|
||||
|
||||
def command_ls():
|
||||
# type: () -> None
|
||||
|
||||
ls = list_daemons(detail=not args.no_detail,
|
||||
legacy_dir=args.legacy_dir)
|
||||
print(json.dumps(ls, indent=4))
|
||||
@ -3597,7 +3630,7 @@ def list_daemons(detail=True, legacy_dir=None):
|
||||
version = err.split(' ')[2]
|
||||
seen_versions[image_id] = version
|
||||
else:
|
||||
logging.warning('version for unknown daemon type %s' % daemon_type)
|
||||
logger.warning('version for unknown daemon type %s' % daemon_type)
|
||||
else:
|
||||
vfile = os.path.join(data_dir, fsid, j, 'unit.image') # type: ignore
|
||||
try:
|
||||
@ -4296,7 +4329,7 @@ class Packager(object):
|
||||
|
||||
def query_shaman(self, distro, distro_version, branch, commit):
|
||||
# query shaman
|
||||
logging.info('Fetching repo metadata from shaman and chacra...')
|
||||
logger.info('Fetching repo metadata from shaman and chacra...')
|
||||
shaman_url = 'https://shaman.ceph.com/api/repos/ceph/{branch}/{sha1}/{distro}/{distro_version}/repo/?arch={arch}'.format(
|
||||
distro=distro,
|
||||
distro_version=distro_version,
|
||||
@ -4307,13 +4340,13 @@ class Packager(object):
|
||||
try:
|
||||
shaman_response = urlopen(shaman_url)
|
||||
except HTTPError as err:
|
||||
logging.error('repository not found in shaman (might not be available yet)')
|
||||
logger.error('repository not found in shaman (might not be available yet)')
|
||||
raise Error('%s, failed to fetch %s' % (err, shaman_url))
|
||||
try:
|
||||
chacra_url = shaman_response.geturl()
|
||||
chacra_response = urlopen(chacra_url)
|
||||
except HTTPError as err:
|
||||
logging.error('repository not found in chacra (might not be available yet)')
|
||||
logger.error('repository not found in chacra (might not be available yet)')
|
||||
raise Error('%s, failed to fetch %s' % (err, chacra_url))
|
||||
return chacra_response.read().decode('utf-8')
|
||||
|
||||
@ -4350,11 +4383,11 @@ class Apt(Packager):
|
||||
|
||||
def add_repo(self):
|
||||
url, name = self.repo_gpgkey()
|
||||
logging.info('Installing repo GPG key from %s...' % url)
|
||||
logger.info('Installing repo GPG key from %s...' % url)
|
||||
try:
|
||||
response = urlopen(url)
|
||||
except HTTPError as err:
|
||||
logging.error('failed to fetch GPG repo key from %s: %s' % (
|
||||
logger.error('failed to fetch GPG repo key from %s: %s' % (
|
||||
url, err))
|
||||
raise Error('failed to fetch GPG key')
|
||||
key = response.read().decode('utf-8')
|
||||
@ -4371,7 +4404,7 @@ class Apt(Packager):
|
||||
content = self.query_shaman(self.distro, self.distro_codename, self.branch,
|
||||
self.commit)
|
||||
|
||||
logging.info('Installing repo file at %s...' % self.repo_path())
|
||||
logger.info('Installing repo file at %s...' % self.repo_path())
|
||||
with open(self.repo_path(), 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
@ -4379,28 +4412,28 @@ class Apt(Packager):
|
||||
for name in ['autobuild', 'release']:
|
||||
p = '/etc/apt/trusted.gpg.d/ceph.%s.gpg' % name
|
||||
if os.path.exists(p):
|
||||
logging.info('Removing repo GPG key %s...' % p)
|
||||
logger.info('Removing repo GPG key %s...' % p)
|
||||
os.unlink(p)
|
||||
if os.path.exists(self.repo_path()):
|
||||
logging.info('Removing repo at %s...' % self.repo_path())
|
||||
logger.info('Removing repo at %s...' % self.repo_path())
|
||||
os.unlink(self.repo_path())
|
||||
|
||||
def install(self, ls):
|
||||
logging.info('Installing packages %s...' % ls)
|
||||
logger.info('Installing packages %s...' % ls)
|
||||
call_throws(['apt', 'install', '-y'] + ls)
|
||||
|
||||
def install_podman(self):
|
||||
if self.distro == 'ubuntu':
|
||||
logging.info('Setting up repo for pdoman...')
|
||||
logger.info('Setting up repo for pdoman...')
|
||||
self.install(['software-properties-common'])
|
||||
call_throws(['add-apt-repository', '-y', 'ppa:projectatomic/ppa'])
|
||||
call_throws(['apt', 'update'])
|
||||
|
||||
logging.info('Attempting podman install...')
|
||||
logger.info('Attempting podman install...')
|
||||
try:
|
||||
self.install(['podman'])
|
||||
except Error as e:
|
||||
logging.info('Podman did not work. Falling back to docker...')
|
||||
logger.info('Podman did not work. Falling back to docker...')
|
||||
self.install(['docker.io'])
|
||||
|
||||
|
||||
@ -4514,7 +4547,7 @@ class YumDnf(Packager):
|
||||
self.branch,
|
||||
self.commit)
|
||||
|
||||
logging.info('Writing repo to %s...' % self.repo_path())
|
||||
logger.info('Writing repo to %s...' % self.repo_path())
|
||||
with open(self.repo_path(), 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
@ -4611,7 +4644,7 @@ class Zypper(Packager):
|
||||
self.branch,
|
||||
self.commit)
|
||||
|
||||
logging.info('Writing repo to %s...' % self.repo_path())
|
||||
logger.info('Writing repo to %s...' % self.repo_path())
|
||||
with open(self.repo_path(), 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
@ -4684,7 +4717,7 @@ def get_ipv4_address(ifname):
|
||||
offset,
|
||||
struct.pack('256s', bytes(ifname[:15], 'utf-8'))
|
||||
)[20:24])
|
||||
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
addr = _extract(s, 35093) # '0x8915' = SIOCGIFADDR
|
||||
@ -4692,7 +4725,7 @@ def get_ipv4_address(ifname):
|
||||
except OSError:
|
||||
# interface does not have an ipv4 address
|
||||
return ''
|
||||
|
||||
|
||||
dec_mask = sum([bin(int(i)).count('1')
|
||||
for i in dq_mask.split('.')])
|
||||
return '{}/{}'.format(addr, dec_mask)
|
||||
@ -4720,8 +4753,8 @@ def get_ipv6_address(ifname):
|
||||
|
||||
def bytes_to_human(num, mode='decimal'):
|
||||
# type: (float, str) -> str
|
||||
"""Convert a bytes value into it's human-readable form.
|
||||
|
||||
"""Convert a bytes value into it's human-readable form.
|
||||
|
||||
:param num: number, in bytes, to convert
|
||||
:param mode: Either decimal (default) or binary to determine divisor
|
||||
:returns: string representing the bytes value in a more readable format
|
||||
@ -4745,7 +4778,7 @@ def bytes_to_human(num, mode='decimal'):
|
||||
def read_file(path_list, file_name=''):
|
||||
# type: (List[str], str) -> str
|
||||
"""Returns the content of the first file found within the `path_list`
|
||||
|
||||
|
||||
:param path_list: list of file paths to search
|
||||
:param file_name: optional file_name to be applied to a file path
|
||||
:returns: content of the file or 'Unknown'
|
||||
@ -4993,7 +5026,7 @@ class HostFacts():
|
||||
if not os.path.exists(nic_path):
|
||||
continue
|
||||
for iface in os.listdir(nic_path):
|
||||
|
||||
|
||||
lower_devs_list = [os.path.basename(link.replace("lower_", "")) for link in glob(os.path.join(nic_path, iface, "lower_*"))]
|
||||
upper_devs_list = [os.path.basename(link.replace("upper_", "")) for link in glob(os.path.join(nic_path, iface, "upper_*"))]
|
||||
|
||||
@ -5070,7 +5103,7 @@ class HostFacts():
|
||||
# type: () -> int
|
||||
"""Determine the memory installed (kb)"""
|
||||
return self._get_mem_data('MemTotal')
|
||||
|
||||
|
||||
@property
|
||||
def memory_free_kb(self):
|
||||
# type: () -> int
|
||||
@ -5117,7 +5150,7 @@ class HostFacts():
|
||||
# type: () -> float
|
||||
"""Return the current time as Epoch seconds"""
|
||||
return time.time()
|
||||
|
||||
|
||||
@property
|
||||
def system_uptime(self):
|
||||
# type: () -> float
|
||||
@ -5136,7 +5169,7 @@ class HostFacts():
|
||||
for selinux_path in HostFacts._selinux_path_list:
|
||||
if os.path.exists(selinux_path):
|
||||
selinux_config = read_file([selinux_path]).splitlines()
|
||||
security['type'] = 'SELinux'
|
||||
security['type'] = 'SELinux'
|
||||
for line in selinux_config:
|
||||
if line.strip().startswith('#'):
|
||||
continue
|
||||
@ -5171,7 +5204,7 @@ class HostFacts():
|
||||
summary_str = ",".join(["{} {}".format(v, k) for k, v in summary.items()])
|
||||
security = {**security, **summary} # type: ignore
|
||||
security['description'] += "({})".format(summary_str)
|
||||
|
||||
|
||||
return security
|
||||
|
||||
if os.path.exists('/sys/kernel/security/lsm'):
|
||||
@ -5196,7 +5229,7 @@ class HostFacts():
|
||||
"""Return the attributes of this HostFacts object as json"""
|
||||
data = {k: getattr(self, k) for k in dir(self)
|
||||
if not k.startswith('_') and
|
||||
isinstance(getattr(self, k),
|
||||
isinstance(getattr(self, k),
|
||||
(float, int, str, list, dict, tuple))
|
||||
}
|
||||
return json.dumps(data, indent=2, sort_keys=True)
|
||||
@ -5735,18 +5768,26 @@ def _parse_args(av):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# Logger configuration
|
||||
if not os.path.exists(LOG_DIR):
|
||||
os.makedirs(LOG_DIR)
|
||||
dictConfig(logging_config)
|
||||
logger = logging.getLogger()
|
||||
|
||||
# allow argv to be injected
|
||||
try:
|
||||
av = injected_argv # type: ignore
|
||||
except NameError:
|
||||
av = sys.argv[1:]
|
||||
logger.debug("%s\ncephadm %s" % ("-" * 80, av))
|
||||
args = _parse_args(av)
|
||||
|
||||
# More verbose console output
|
||||
if args.verbose:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
else:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger('cephadm')
|
||||
for handler in logger.handlers:
|
||||
if handler.name == "console":
|
||||
handler.setLevel(logging.DEBUG)
|
||||
|
||||
# root?
|
||||
if os.geteuid() != 0:
|
||||
|
@ -1,18 +1,16 @@
|
||||
# type: ignore
|
||||
import argparse
|
||||
import mock
|
||||
from mock import patch
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import pytest
|
||||
|
||||
if sys.version_info >= (3, 3):
|
||||
with patch('builtins.open', create=True):
|
||||
from importlib.machinery import SourceFileLoader
|
||||
cd = SourceFileLoader('cephadm', 'cephadm').load_module()
|
||||
else:
|
||||
import imp
|
||||
cd = imp.load_source('cephadm', 'cephadm')
|
||||
|
||||
class TestCephAdm(object):
|
||||
def test_is_fsid(self):
|
||||
|
Loading…
Reference in New Issue
Block a user