import mock import os import pytest import time from contextlib import contextmanager from pyfakefs import fake_filesystem from typing import Dict, List, Optional def import_cephadm(): """Import cephadm as a module.""" import cephadm as _cephadm return _cephadm def mock_docker(): _cephadm = import_cephadm() docker = mock.Mock(_cephadm.Docker) docker.path = '/usr/bin/docker' return docker def mock_podman(): _cephadm = import_cephadm() podman = mock.Mock(_cephadm.Podman) podman.path = '/usr/bin/podman' podman.version = (2, 1, 0) return podman def _daemon_path(): return os.getcwd() def mock_bad_firewalld(): def raise_bad_firewalld(): raise Exception('Called bad firewalld') _cephadm = import_cephadm() f = mock.Mock(_cephadm.Firewalld) f.enable_service_for = lambda _: raise_bad_firewalld() f.apply_rules = lambda: raise_bad_firewalld() f.open_ports = lambda _: raise_bad_firewalld() def _mock_scrape_host(obj, interval): try: raise ValueError("wah") except Exception as e: obj._handle_thread_exception(e, 'host') def _mock_run(obj): t = obj._create_thread(obj._scrape_host_facts, 'host', 5) time.sleep(1) if not t.is_alive(): obj.cephadm_cache.update_health('host', "inactive", "host thread stopped") @pytest.fixture() def cephadm_fs( fs: fake_filesystem.FakeFilesystem, ): """ use pyfakefs to stub filesystem calls """ uid = os.getuid() gid = os.getgid() def fchown(fd, _uid, _gid): """pyfakefs doesn't provide a working fchown or fchmod. In order to get permissions working generally across renames we need to provide our own implemenation. """ file_obj = fs.get_open_file(fd).get_object() file_obj.st_uid = _uid file_obj.st_gid = _gid _cephadm = import_cephadm() with mock.patch('os.fchown', side_effect=fchown), \ mock.patch('os.fchmod'), \ mock.patch('platform.processor', return_value='x86_64'), \ mock.patch('cephadm.extract_uid_gid', return_value=(uid, gid)): try: if not fake_filesystem.is_root(): fake_filesystem.set_uid(0) except AttributeError: pass fs.create_dir(_cephadm.DATA_DIR) fs.create_dir(_cephadm.LOG_DIR) fs.create_dir(_cephadm.LOCK_DIR) fs.create_dir(_cephadm.LOGROTATE_DIR) fs.create_dir(_cephadm.UNIT_DIR) fs.create_dir('/sys/block') yield fs @pytest.fixture() def host_sysfs(fs: fake_filesystem.FakeFilesystem): """Create a fake filesystem to represent sysfs""" enc_path = '/sys/class/scsi_generic/sg2/device/enclosure/0:0:1:0' dev_path = '/sys/class/scsi_generic/sg2/device' slot_count = 12 fs.create_dir(dev_path) fs.create_file(os.path.join(dev_path, 'vendor'), contents="EnclosuresInc") fs.create_file(os.path.join(dev_path, 'model'), contents="D12") fs.create_file(os.path.join(enc_path, 'id'), contents='1') fs.create_file(os.path.join(enc_path, 'components'), contents=str(slot_count)) for slot_num in range(slot_count): slot_dir = os.path.join(enc_path, str(slot_num)) fs.create_file(os.path.join(slot_dir, 'locate'), contents='0') fs.create_file(os.path.join(slot_dir, 'fault'), contents='0') fs.create_file(os.path.join(slot_dir, 'slot'), contents=str(slot_num)) if slot_num < 6: fs.create_file(os.path.join(slot_dir, 'status'), contents='Ok') slot_dev = os.path.join(slot_dir, 'device') fs.create_dir(slot_dev) fs.create_file(os.path.join(slot_dev, 'vpd_pg80'), contents=f'fake{slot_num:0>3}') else: fs.create_file(os.path.join(slot_dir, 'status'), contents='not installed') yield fs @contextmanager def with_cephadm_ctx( cmd: List[str], list_networks: Optional[Dict[str, Dict[str, List[str]]]] = None, hostname: Optional[str] = None, ): """ :param cmd: cephadm command argv :param list_networks: mock 'list-networks' return :param hostname: mock 'socket.gethostname' return """ if not hostname: hostname = 'host1' _cephadm = import_cephadm() with mock.patch('cephadm.attempt_bind'), \ mock.patch('cephadm.call', return_value=('', '', 0)), \ mock.patch('cephadm.call_timeout', return_value=0), \ mock.patch('cephadm.find_executable', return_value='foo'), \ mock.patch('cephadm.get_container_info', return_value=None), \ mock.patch('cephadm.is_available', return_value=True), \ mock.patch('cephadm.json_loads_retry', return_value={'epoch' : 1}), \ mock.patch('cephadm.logger'), \ mock.patch('socket.gethostname', return_value=hostname): ctx: _cephadm.CephadmContext = _cephadm.cephadm_init_ctx(cmd) ctx.container_engine = mock_podman() if list_networks is not None: with mock.patch('cephadm.list_networks', return_value=list_networks): yield ctx else: yield ctx