Exemplo n.º 1
0
def test_command_exists(mocked_ext):
    m = mocked_ext['shutil.which']
    m.return_value = ''
    assert not utils.command_exists('wobberjacky')

    m.return_value = '/bin/promised-land'
    assert utils.command_exists('pizza')
Exemplo n.º 2
0
def disable_ssh_accept_env():
    """Disable the 'AcceptEnv LANG LC_*' setting in sshd_config

    This setting is default on the Raspberry Pi,
    but leads to locale errors when an unsupported LANG is sent.

    Given that the Pi by default only includes the en_GB locale,
    the chances of being sent a unsupported locale are very real.
    """
    file = Path('/etc/ssh/sshd_config')
    if not file.exists():
        return

    content = file.read_text()
    updated = re.sub(r'^AcceptEnv LANG LC',
                     '#AcceptEnv LANG LC',
                     content,
                     flags=re.MULTILINE)

    if content == updated:
        return

    with NamedTemporaryFile('w') as tmp:
        tmp.write(updated)
        tmp.flush()
        utils.info('Updating SSHD config to disable AcceptEnv...')
        utils.show_data('/etc/ssh/sshd_config', updated)
        sh(f'sudo chmod --reference={file} {tmp.name}')
        sh(f'sudo cp -fp {tmp.name} {file}')

    if utils.command_exists('systemctl'):
        utils.info('Restarting SSH service...')
        sh('sudo systemctl restart ssh')
Exemplo n.º 3
0
def edit_avahi_config():
    conf = Path(const.AVAHI_CONF)

    if not conf.exists():
        return

    config = ConfigObj(str(conf), file_error=True)
    copy = deepcopy(config)
    config.setdefault('server', {}).setdefault('use-ipv6', 'no')
    config.setdefault('publish', {}).setdefault('publish-aaaa-on-ipv4', 'no')
    config.setdefault('reflector', {}).setdefault('enable-reflector', 'yes')

    if config == copy:
        return

    utils.show_data(conf, config.dict())

    with NamedTemporaryFile('w') as tmp:
        config.filename = None
        lines = config.write()
        # avahi-daemon.conf requires a 'key=value' syntax
        tmp.write('\n'.join(lines).replace(' = ', '=') + '\n')
        tmp.flush()
        sh(f'sudo chmod --reference={conf} {tmp.name}')
        sh(f'sudo cp -fp {tmp.name} {conf}')

    if utils.command_exists('systemctl'):
        utils.info('Restarting avahi-daemon service...')
        sh('sudo systemctl restart avahi-daemon')
    else:
        utils.warn(
            '"systemctl" command not found. Please restart your machine to enable Wifi discovery.'
        )
Exemplo n.º 4
0
def kill(zombies):
    """Stop and remove all containers on this host.

    This includes those not from Brewblox.

    If the --zombies flag is set,
    leftover processes that are still claiming a port will be forcibly removed.
    Use this if you get "port is already allocated" errors after your system crashed.
    """
    utils.confirm_mode()
    sudo = utils.optsudo()
    sh(f'{sudo}docker rm --force $({sudo}docker ps -aq)', check=False)

    if zombies:
        # We can't use psutil for this, as we need root rights to get pids
        if not utils.command_exists('netstat'):
            utils.warn(
                'Command `netstat` not found. Please install it by running:')
            utils.warn('')
            utils.warn(
                '    sudo apt-get update && sudo apt-get install net-tools')
            utils.warn('')
            return

        procs = re.findall(r'(\d+)/docker-proxy',
                           sh('sudo netstat -pna', capture=True))

        if procs:
            utils.info(f'Removing {len(procs)} zombies...')
            sh('sudo service docker stop')
            sh([f'sudo kill -9 {proc}' for proc in procs])
            sh('sudo service docker start')
Exemplo n.º 5
0
def add_particle_udev_rules():
    rules_dir = '/etc/udev/rules.d'
    target = f'{rules_dir}/50-particle.rules'
    if not utils.path_exists(target) and utils.command_exists('udevadm'):
        utils.info('Adding udev rules for Particle devices...')
        sh(f'sudo mkdir -p {rules_dir}')
        sh(f'sudo cp {const.CONFIG_DIR}/50-particle.rules {target}')
        sh('sudo udevadm control --reload-rules && sudo udevadm trigger')
Exemplo n.º 6
0
    def check_system_opts(self):
        self.apt_install = True

        apt_deps = ' '.join(const.APT_DEPENDENCIES)
        if not utils.command_exists('apt-get'):
            utils.info(
                '`apt-get` is not available. You may need to find another way to install dependencies.'
            )
            utils.info(f'Apt packages: "{apt_deps}"')
            self.apt_install = False
        elif not self.use_defaults:
            self.apt_install = utils.confirm(
                f'Do you want to install apt packages "{apt_deps}"?')
Exemplo n.º 7
0
def fix_ipv6(config_file=None, restart=True):
    utils.info('Fixing Docker IPv6 settings...')

    if utils.is_wsl():
        utils.info('WSL environment detected. Skipping IPv6 config changes.')
        return

    # Config is either provided, or parsed from active daemon process
    if not config_file:
        default_config_file = '/etc/docker/daemon.json'
        dockerd_proc = sh('ps aux | grep dockerd', capture=True)
        proc_match = re.match(r'.*--config-file[\s=](?P<file>.*\.json).*',
                              dockerd_proc,
                              flags=re.MULTILINE)
        config_file = proc_match and proc_match.group(
            'file') or default_config_file

    utils.info(f'Using Docker config file {config_file}')

    # Read config. Create file if not exists
    sh(f"sudo touch '{config_file}'")
    config = sh(f"sudo cat '{config_file}'", capture=True)

    if 'fixed-cidr-v6' in config:
        utils.info('IPv6 settings are already present. Making no changes.')
        return

    # Edit and write. Do not overwrite existing values
    config = json.loads(config or '{}')
    config.setdefault('ipv6', False)
    config.setdefault('fixed-cidr-v6', '2001:db8:1::/64')
    config_str = json.dumps(config, indent=2)
    sh(f"echo '{config_str}' | sudo tee '{config_file}' > /dev/null")

    # Restart daemon
    if restart:
        if utils.command_exists('service'):
            utils.info('Restarting Docker service...')
            sh('sudo service docker restart')
        else:
            utils.warn(
                '"service" command not found. Please restart your machine to apply config changes.'
            )
Exemplo n.º 8
0
def coredump(upload):
    """Read and upload a core dump file for the Spark 4.

    This requires the Spark to be connected over USB.
    Not compatible with the Spark 2 or 3.

    If the Spark 4 crashes, it stores what it was doing at the time of the crash.
    This command exports and uploads this data.

    The `esptool` python package is required, and will be installed if not found.
    """
    if not utils.command_exists('esptool.py'):
        sh('python3 -m pip install esptool')
    sh('sudo -E env "PATH=$PATH" esptool.py --chip esp32 --baud 115200 read_flash 0xA10000 81920 coredump.bin')
    sh('base64 coredump.bin > coredump.b64')

    if upload:
        click.echo(utils.file_netcat('termbin.com', 9999, Path('./coredump.b64')).decode())
    else:
        utils.info('Skipping upload. If you want to manually upload the file, run: ' +
                   click.style('brewblox-ctl termbin ./coredump.b64', fg='green'))
Exemplo n.º 9
0
    def check_docker_opts(self):
        self.docker_install = True
        self.docker_group_add = True
        self.docker_pull = True

        if utils.command_exists('docker'):
            utils.info('Docker is already installed.')
            self.docker_install = False
        elif not self.use_defaults:
            self.docker_install = utils.confirm(
                'Do you want to install docker?')

        if utils.is_docker_user():
            user = utils.getenv('USER')
            utils.info(f'{user} already belongs to the docker group.')
            self.docker_group_add = False
        elif not self.use_defaults:
            self.docker_group_add = utils.confirm(
                'Do you want to run docker commands without sudo?')

        if not self.use_defaults:
            self.docker_pull = utils.confirm(
                'Do you want to pull the docker images for your services?')
Exemplo n.º 10
0
def update_system_packages():
    if utils.command_exists('apt-get'):
        utils.info('Updating apt packages...')
        sh('sudo apt-get update && sudo apt-get upgrade -y')
Exemplo n.º 11
0
def install(use_defaults,
            apt_install,
            docker_install,
            docker_user,
            no_reboot,
            dir,
            release):
    """Create Brewblox directory; install system dependencies; reboot.

    Brewblox can be installed multiple times on the same computer.
    Settings and databases are stored in a Brewblox directory (default: ./brewblox).

    This command also installs system-wide dependencies (docker).
    After `brewblox-ctl install`, run `brewblox-ctl setup` in the created Brewblox directory.

    A reboot is required after installing docker, or adding the user to the 'docker' group.

    By default, `brewblox-ctl install` attempts to download packages using the apt package manager.
    If you are using a system without apt (eg. Synology NAS), this step will be skipped.
    You will need to manually install any missing libraries.

    \b
    Steps:
        - Install apt packages.
        - Install docker.
        - Add user to 'docker' group.
        - Create Brewblox directory (default ./brewblox).
        - Set variables in .env file.
        - Reboot.
    """
    utils.confirm_mode()

    apt_deps = 'curl net-tools libssl-dev libffi-dev'
    user = utils.getenv('USER')
    default_dir = path.abspath('./brewblox')
    prompt_reboot = True

    if use_defaults is None:
        use_defaults = utils.confirm('Do you want to install with default settings?')

    # Check if packages should be installed
    if not utils.command_exists('apt'):
        utils.info('Apt is not available. You may need to find another way to install dependencies.')
        utils.info('Apt packages: "{}"'.format(apt_deps))
        apt_install = False

    if apt_install is None:
        if use_defaults:
            apt_install = True
        else:
            apt_install = utils.confirm('Do you want to install apt packages "{}"?'.format(apt_deps))

    # Check if docker should be installed
    if utils.command_exists('docker'):
        utils.info('Docker is already installed.')
        docker_install = False

    if docker_install is None:
        if use_defaults:
            docker_install = True
        else:
            docker_install = utils.confirm('Do you want to install docker?')

    # Check if user should be added to docker group
    if utils.is_docker_user():
        utils.info('{} already belongs to the docker group.'.format(user))
        docker_user = False

    if docker_user is None:
        if use_defaults:
            docker_user = True
        else:
            docker_user = utils.confirm('Do you want to run docker commands without sudo?')

    # Check used directory
    if dir is None:
        if use_defaults or utils.confirm("The default directory is '{}'. Do you want to continue?".format(default_dir)):
            dir = default_dir
        else:
            return

    if utils.path_exists(dir):
        if not utils.confirm('{} already exists. Do you want to continue?'.format(dir)):
            return

    if not no_reboot:
        prompt_reboot = utils.confirm('A reboot is required after installation. ' +
                                      'Do you want to be prompted before that happens?')

    # Install Apt packages
    if apt_install:
        utils.info('Installing apt packages...')
        sh([
            'sudo apt update',
            'sudo apt upgrade -y',
            'sudo apt install -y {}'.format(apt_deps),
        ])
    else:
        utils.info('Skipped: apt install.')

    # Install docker
    if docker_install:
        utils.info('Installing docker...')
        sh('curl -sL get.docker.com | sh')
    else:
        utils.info('Skipped: docker install.')

    # Add user to 'docker' group
    if docker_user:
        utils.info("Adding {} to 'docker' group...".format(user))
        sh('sudo usermod -aG docker $USER')
    else:
        utils.info("Skipped: adding {} to 'docker' group.".format(user))

    # Create install directory
    utils.info('Creating Brewblox directory ({})...'.format(dir))
    sh('mkdir -p {}'.format(dir))

    # Set variables in .env file
    utils.info('Setting variables in .env file...')
    dotenv_path = path.abspath('{}/.env'.format(dir))
    sh('touch {}'.format(dotenv_path))
    utils.setenv(const.RELEASE_KEY, release, dotenv_path)
    utils.setenv(const.CFG_VERSION_KEY, '0.0.0', dotenv_path)
    utils.setenv(const.SKIP_CONFIRM_KEY, str(use_defaults), dotenv_path)

    utils.info('Done!')

    # Reboot
    if not no_reboot:
        if prompt_reboot:
            utils.info('Press ENTER to reboot.')
            input()
        else:
            utils.info('Rebooting in 10 seconds...')
            sleep(10)
        sh('sudo reboot')
    else:
        utils.info('Skipped: reboot.')