Beispiel #1
0
def run():
    """
    Invoke install_package() and enable_and_start() as needed.
    """
    rdebug("Run, beacon, run!")
    run_beacon.run()
    rdebug("Returning to the storpool_block setup")
    sputils.check_systemd_service("storpool_block")

    rdebug("Checking for the 'storpool' Python module")
    try:
        subprocess.check_call(["python2", "-c", "from storpool import spapi"],
                              shell=False)
    except subprocess.CalledProcessError:
        raise sperror.StorPoolMissingComponentsException(["python2-storpool"])

    rdebug("Checking for the 'storpool.spopenstack' Python module")
    try:
        subprocess.check_call(
            ["python2", "-c", "from storpool.spopenstack import spattachdb"],
            shell=False,
        )
    except subprocess.CalledProcessError:
        raise sperror.StorPoolMissingComponentsException(
            ["python2-storpool.spopenstack"])

    spstatus.npset("maintenance", "")
def check_systemd_service(name, in_lxc=False):
    """
    Check for a systemd service with the specified name.
    """
    service = "{name}.service".format(name=name)
    spstatus.npset(
        "maintenance",
        "checking for the {name} service".format(name=name),
    )
    if in_lxc or check_in_lxc():
        rdebug(
            "running in an LXC container, not checking for " + service,
            prefix=name,
        )
        return

    lines = (
        subprocess.check_output(
            ["systemctl", "show", "-p", "Type", service],
            shell=False,
        )
        .decode("UTF-8")
        .splitlines()
    )
    rdebug("got {lines}".format(lines=repr(lines)), prefix=name)
    if len(lines) != 1 or lines[0] not in ("Type=forking", "Type=simple"):
        raise sperror.StorPoolMissingComponentsException([service])
def write_out_config():
    """
    Write out the StorPool configuration file specified in the charm config.
    """
    rdebug('about to write out the /etc/storpool.conf file')
    spstatus.npset('maintenance', 'updating the /etc/storpool.conf file')
    with tempfile.NamedTemporaryFile(dir='/tmp', mode='w+t',
                                     delete=True) as spconf:
        rdebug('about to write the contents to the temporary file {sp}'.format(
            sp=spconf.name))
        templating.render(
            source='storpool.conf',
            target=spconf.name,
            owner='root',
            perms=0o600,
            context={
                'storpool_conf': spconfig.m()['storpool_conf'],
            },
        )
        rdebug('about to invoke txn install')
        txn.install('-o', 'root', '-g', 'root', '-m', '644', '--', spconf.name,
                    '/etc/storpool.conf')
        rdebug('it seems that /etc/storpool.conf has been created')

        rdebug('trying to read it now')
        spconfig.drop_cache()
        cfg = spconfig.get_dict()
        oid = cfg['SP_OURID']
        spconfig.set_our_id(oid)
        rdebug('got {len} keys in the StorPool config, our id is {oid}'.format(
            len=len(cfg), oid=oid))

    rdebug('setting the config-written state')
    reactive.set_state('l-storpool-config.config-written')
    spstatus.npset('maintenance', '')
def config_changed():
    """
    Check if the configuration is complete or has been changed.
    """
    rdebug('config-changed happened')
    reactive.remove_state('l-storpool-config.configure')
    config = spconfig.m()

    # Remove any states that say we have accomplished anything...
    for state in STATES_REDO['unset']:
        reactive.remove_state(state)
    spconfig.unset_our_id()

    spconf = config.get('storpool_conf', None)
    rdebug('and we do{xnot} have a storpool_conf setting'.format(
        xnot=' not' if spconf is None else ''))
    if spconf is None or spconf == '':
        return

    # And let's make sure we try installing any packages we need...
    reactive.set_state('l-storpool-config.config-available')
    reactive.set_state('l-storpool-config.package-try-install')

    # This will probably race with some others, but oh well
    spstatus.npset(
        'maintenance', 'waiting for the StorPool charm configuration and '
        'the StorPool repo setup')
def not_ready_no_config():
    """
    Note that some configuration settings are missing.
    """
    rdebug('well, it seems we have a repo, but we do not have a config yet')
    spstatus.npset('maintenance',
                   'waiting for the StorPool charm configuration')
def first_install():
    """
    On initial installation, note that we need to collect and submit the data.
    """
    rdebug('install invoked, triggering both a recollection and '
           'a resubmission')
    reactive.set_state('storpool-inventory.collecting')
    reactive.remove_state('storpool-inventory.collected')
    reactive.set_state('storpool-inventory.submitting')
    reactive.remove_state('storpool-inventory.submitted')
    spstatus.npset('maintenance', 'setting up')
def do_install_apt_repo():
    """
    Check and, if necessary, add the StorPool repository.
    """
    rdebug('install-apt-repo invoked')
    spstatus.npset('maintenance', 'checking for the APT repository')

    if not has_apt_repo():
        install_apt_repo()

    rdebug('install-apt-repo seems fine')
    spstatus.npset('maintenance', '')
    reactive.set_state('storpool-repo-add.installed-apt-repo')
def do_install_apt_key():
    """
    Check and, if necessary, install the StorPool package signing key.
    """
    rdebug('install-apt-key invoked')
    spstatus.npset('maintenance', 'checking for the APT key')

    if not has_apt_key():
        install_apt_key()

    rdebug('install-apt-key seems fine')
    spstatus.npset('maintenance', '')
    reactive.set_state('storpool-repo-add.installed-apt-key')
Beispiel #9
0
def enable_and_start():
    """
    Run the StorPool OpenStack integration on the current node and,
    if configured, its LXD containers.
    """
    if not hookenv.config()["storpool_openstack_install"]:
        rdebug("skipping the installation into containers")
        return

    # to do: check for the nova-compute and cinder-volume services

    # to do: set up the groups

    spstatus.npset("maintenance", "")
def do_update_apt():
    """
    Invoke `apt-get update` to fetch data from the StorPool repository.
    """
    rdebug('invoking apt-get update')
    spstatus.npset('maintenance', 'updating the APT cache')

    subprocess.check_call(['apt-get', 'update'])

    rdebug('update-apt seems fine')
    spstatus.npset('maintenance', '')
    reactive.set_state('storpool-repo-add.updated-apt')

    # And, finally, the others can do stuff, too
    reactive.set_state('storpool-repo-add.available')
def stop():
    """
    Clean up and no longer attempt to install anything.
    """
    rdebug('storpool-repo-add stopping as requested')

    for fname in (apt_sources_list(), apt_keyring()):
        if os.path.isfile(fname):
            rdebug('- trying to remove {name}'.format(name=fname))
            try:
                os.unlink(fname)
            except Exception as e:
                rdebug('  - could not remove {name}: {e}'
                       .format(name=fname, e=e))
        else:
            rdebug('- no {name} to remove'.format(name=fname))

    for state in STATES_REDO['set'] + STATES_REDO['unset']:
        reactive.remove_state(state)

    reactive.remove_state('storpool-repo-add.stop')
    reactive.set_state('storpool-repo-add.stopped')
    spstatus.npset('maintenance', '')
def try_to_submit():
    """
    Once the data has been collected and `submit_url` is set, go ahead.
    """
    url = hookenv.config().get('submit_url', None)
    rdebug('trying to submit to {url}'.format(url=url))
    reactive.remove_state('storpool-inventory.submitting')

    if url is None:
        rdebug('erm, how did we get here with no submit URL?')
        return

    spstatus.npset('maintenance', 'submitting the collected data')
    try:
        global datafile
        rdebug('about to read {df}'.format(df=datafile))
        with open(datafile, mode='r', encoding='latin1') as f:
            contents = ''.join(f.readlines())
        rdebug('read {ln} characters of data from the collect file'.format(
            ln=len(contents)))
        data = json.dumps({'filename': platform.node(), 'contents': contents})
        rdebug('encoded stuff into {ln} characters of data to submit'.format(
            ln=len(data)))
        data_enc = data.encode('latin1')
        rdebug('submitting {ln} bytes of data to {url}'.format(
            ln=len(data_enc), url=url))
        with urllib.request.urlopen(url, data=data_enc) as resp:
            rdebug('got some kind of an HTTP response')
            code = resp.getcode()
            rdebug('got response code {code}'.format(code=code))
            if code is not None and code >= 200 and code < 300:
                rdebug('success!')
                reactive.set_state('storpool-inventory.submitted')
                spstatus.set('active', 'here, have a blob of data')
    except Exception as e:
        rdebug('could not submit the data: {e}'.format(e=e))
        sputils.err('failed to submit the collected data')
def setup_interfaces():
    """
    Set up the IPv4 addresses of some interfaces if requested.
    """
    if sputils.check_in_lxc():
        rdebug('running in an LXC container, not setting up interfaces')
        reactive.set_state('l-storpool-config.config-network')
        return

    rdebug('trying to parse the StorPool interface configuration')
    spstatus.npset('maintenance',
                   'parsing the StorPool interface configuration')
    cfg = spconfig.get_dict()
    ifaces = cfg.get('SP_IFACE', None)
    if ifaces is None:
        hookenv.set('error', 'No SP_IFACES in the StorPool config')
        return
    rdebug('got interfaces: {ifaces}'.format(ifaces=ifaces))

    spcnetwork.fixup_interfaces(ifaces)

    rdebug('well, looks like it is all done...')
    reactive.set_state('l-storpool-config.config-network')
    spstatus.npset('maintenance', '')
Beispiel #14
0
def copy_config_files():
    """
    Install some configuration files.
    """
    spstatus.npset('maintenance', 'copying the storpool-common config files')
    basedir = '/usr/lib/storpool/etcfiles/storpool-common'
    for f in (
            '/etc/rsyslog.d/99-StorPool.conf',
            '/etc/sysctl.d/99-StorPool.conf',
    ):
        rdebug('installing {fname}'.format(fname=f))
        txn.install('-o', 'root', '-g', 'root', '-m', '644', basedir + f, f)

    rdebug('about to restart rsyslog')
    spstatus.npset('maintenance', 'restarting the system logging service')
    host.service_restart('rsyslog')

    reactive.set_state('storpool-common.config-written')
    spstatus.npset('maintenance', '')
def have_config():
    """
    Check whether the `submit_url` configuration parameter has been set or
    changed; if so, trigger a collect-and-submit cycle.
    """
    rdebug('config-changed')
    config = hookenv.config()

    url = config.get('submit_url', None)
    if url is not None and url != '':
        if config.changed('submit_url') or \
           not rhelpers.is_state('storpool-inventory.configured'):
            spstatus.reset()
            reactive.set_state('storpool-inventory.configured')
            rdebug(
                'we have a new submission URL address: {url}'.format(url=url))
            reactive.set_state('storpool-inventory.submitting')
            reactive.remove_state('storpool-inventory.submitted')

            if not rhelpers.is_state('storpool-inventory.collected') and \
               not rhelpers.is_state('storpool-inventory.collecting'):
                rdebug('triggering another collection attempt')
                spstatus.npset('maintenance',
                               'about to try to collect data again')
                reactive.set_state('storpool-inventory.collecting')
            else:
                spstatus.npset('maintenance',
                               'about to resubmit any collected data')
        else:
            rdebug('the submission URL address seems to be the same as before')
    else:
        rdebug('we do not seem to have a submission URL address')
        reactive.remove_state('storpool-inventory.configured')
        reactive.remove_state('storpool-inventory.submitting')
        reactive.remove_state('storpool-inventory.submitted')
        spstatus.reset()
        spstatus.npset('maintenance', 'waiting for configuration')
Beispiel #16
0
def install_package():
    """
    Install the StorPool block package.
    """
    rdebug('the block repo has become available and the common packages '
           'have been configured')

    if sputils.check_in_lxc():
        rdebug('running in an LXC container, not doing anything more')
        reactive.set_state('storpool-block.package-installed')
        return

    spstatus.npset('maintenance', 'obtaining the requested StorPool version')
    spver = spconfig.m().get('storpool_version', None)
    if spver is None or spver == '':
        rdebug('no storpool_version key in the charm config yet')
        return

    spstatus.npset('maintenance', 'installing the StorPool block packages')
    (err, newly_installed) = sprepo.install_packages({
        'storpool-block': spver,
    })
    if err is not None:
        rdebug('oof, we could not install packages: {err}'.format(err=err))
        rdebug('removing the package-installed state')
        return

    if newly_installed:
        rdebug('it seems we managed to install some packages: {names}'.format(
            names=newly_installed))
        sprepo.record_packages('storpool-block', newly_installed)
    else:
        rdebug('it seems that all the packages were installed already')

    rdebug('setting the package-installed state')
    reactive.set_state('storpool-block.package-installed')
    spstatus.npset('maintenance', '')
def install_package():
    """
    Install the base StorPool packages.
    """
    rdebug('the repo hook has become available and '
           'we do have the configuration')

    spstatus.npset('maintenance', 'obtaining the requested StorPool version')
    spver = spconfig.m().get('storpool_version', None)
    if spver is None or spver == '':
        rdebug('no storpool_version key in the charm config yet')
        return

    spstatus.npset('maintenance',
                   'installing the StorPool configuration packages')
    reactive.remove_state('l-storpool-config.package-try-install')
    (err, newly_installed) = sprepo.install_packages({
        'txn-install': '*',
        'storpool-config': spver,
    })
    if err is not None:
        rdebug('oof, we could not install packages: {err}'.format(err=err))
        rdebug('removing the package-installed state')
        reactive.remove_state('l-storpool-config.package-installed')
        return

    if newly_installed:
        rdebug('it seems we managed to install some packages: {names}'.format(
            names=newly_installed))
        sprepo.record_packages('storpool-config', newly_installed)
    else:
        rdebug('it seems that all the packages were installed already')

    rdebug('setting the package-installed state')
    reactive.set_state('l-storpool-config.package-installed')
    spstatus.npset('maintenance', '')
def collect():
    """
    Generate and run a shell script invoking various system tools to
    collect some information.
    """
    spstatus.reset()
    rdebug('about to collect some data, are we not')
    reactive.remove_state('storpool-inventory.collecting')

    spstatus.npset('maintenance', 'installing packages for data collection')
    try:
        (err, newly_installed) = sprepo.install_packages({
            'dmidecode': '*',
            'lshw': '*',
            'nvme-cli': '*',
            'pciutils': '*',
            'usbutils': '*',
        })
        if err is not None:
            raise Exception('{e}'.format(e=err))
        if newly_installed:
            rdebug('it seems we installed some new packages: {lst}'.format(
                lst=' '.join(newly_installed)))
        else:
            rdebug('it seems we already had everything we needed')
        sprepo.record_packages('storpool-inventory-charm', newly_installed)
        spstatus.npset('maintenance', '')
    except Exception as e:
        sputils.err('failed to install the OS packages')
        return

    spstatus.npset('maintenance', 'collecting data')
    try:
        with tempfile.TemporaryDirectory(dir='/tmp',
                                         prefix='storpool-inventory.') as d:
            rdebug('created a temporary directory {d}'.format(d=d))
            """
            No need to create a working directory for the present...

            workname = 'collect-' + platform.node()
            workdir = d + '/' + workname
            os.mkdir(workdir, mode=0o700)
            rdebug('created the working directory {w}'.format(w=workdir))
            """
            workdir = d

            collect_script = workdir + '/collect.sh'
            with open(collect_script, mode='w') as f:
                print(collect_commands.format(w=workdir), end='', file=f)
            os.chmod(collect_script, 0o700)
            rdebug('running the collect script'.format(cs=collect_script))
            subprocess.call([
                'sh', '-c',
                "{cs} > '{w}/collect.txt' 2>'{w}/collect.err'".format(
                    cs=collect_script, w=workdir)
            ])

            collected = {}
            rdebug('scanning the {w} directory now'.format(w=workdir))
            for e in os.scandir(workdir):
                if not e.is_file():
                    continue
                rdebug('- {name}'.format(name=e.name))
                with open(workdir + '/' + e.name, mode='r',
                          encoding='latin1') as f:
                    collected[e.name] = ''.join(f.readlines())
            rdebug('collected {ln} entries: {ks}'.format(
                ln=len(collected), ks=sorted(collected.keys())))
            data = json.dumps(collected)
            rdebug('and dumped them to {ln} characters of data'.format(
                ln=len(data)))

            global datafile
            rdebug('about to write {df}'.format(df=datafile))
            if not os.path.isdir(datadir):
                os.mkdir(datadir, mode=0o700)
            with open(datafile, mode='w', encoding='latin1') as f:
                rdebug('about to write to the file')
                print(data, file=f)
                rdebug('done writing to the file, it seems')
            rdebug('about to check the size of the collect file')
            st = os.stat(datafile)
            rdebug('it seems we wrote {ln} bytes to the file'.format(
                ln=st.st_size))

            rdebug('we seem to be done here!')
            reactive.set_state('storpool-inventory.collected')
            spstatus.npset('maintenance', '')
    except Exception as e:
        rdebug('something bad happened: {e}'.format(e=e))
        sputils.err('maintenance', 'failed to collect the data')
def not_ready_no_repo():
    """
    Note that the `storpool-repo` layer has not yet completed its work.
    """
    rdebug('well, it seems we have a config, but we do not have a repo yet')
    spstatus.npset('maintenance', 'waiting for the StorPool repo setup')
Beispiel #20
0
def install_package():
    """
    Install the base StorPool packages.
    """
    rdebug('the common repo has become available and '
           'we do have the configuration')

    rdebug('checking the kernel command line')
    with open('/proc/cmdline', mode='r') as f:
        ln = f.readline()
        if not ln:
            sputils.err('Could not read a single line from /proc/cmdline')
            return
        words = ln.split()

        # OK, so this is a bit naive, but it will do the job
        global KERNEL_REQUIRED_PARAMS
        missing = list(
            filter(lambda param: param not in words, KERNEL_REQUIRED_PARAMS))
        if missing:
            if sputils.bypassed('kernel_parameters'):
                hookenv.log(
                    'The "kernel_parameters" bypass is meant FOR '
                    'DEVELOPMENT ONLY!  DO NOT run a StorPool cluster '
                    'in production with it!', hookenv.WARNING)
            else:
                sputils.err('Missing kernel parameters: {missing}'.format(
                    missing=' '.join(missing)))
                return

    spstatus.npset('maintenance', 'obtaining the requested StorPool version')
    spver = spconfig.m().get('storpool_version', None)
    if spver is None or spver == '':
        rdebug('no storpool_version key in the charm config yet')
        return

    spstatus.npset('maintenance', 'installing the StorPool common packages')
    (err, newly_installed) = sprepo.install_packages({
        'storpool-cli':
        spver,
        'storpool-common':
        spver,
        'storpool-etcfiles':
        spver,
        'kmod-storpool-' + os.uname().release:
        spver,
        'python-storpool':
        spver,
    })
    if err is not None:
        rdebug('oof, we could not install packages: {err}'.format(err=err))
        rdebug('removing the package-installed state')
        return

    if newly_installed:
        rdebug('it seems we managed to install some packages: {names}'.format(
            names=newly_installed))
        sprepo.record_packages('storpool-common', newly_installed)
    else:
        rdebug('it seems that all the packages were installed already')

    rdebug('updating the kernel module dependencies')
    spstatus.npset('maintenance', 'updating the kernel module dependencies')
    subprocess.check_call(['depmod', '-a'])

    rdebug('gathering CPU information for the cgroup configuration')
    with open('/proc/cpuinfo', mode='r') as f:
        lns = f.readlines()
        all_cpus = sorted(
            map(
                lambda lst: int(lst[2]),
                filter(lambda lst: lst and lst[0] == 'processor',
                       map(lambda s: s.split(), lns))))
    if sputils.bypassed('very_few_cpus'):
        hookenv.log(
            'The "very_few_cpus" bypass is meant '
            'FOR DEVELOPMENT ONLY!  DO NOT run a StorPool cluster in '
            'production with it!', hookenv.WARNING)
        last_cpu = all_cpus[-1]
        all_cpus.extend([last_cpu, last_cpu, last_cpu])
    if len(all_cpus) < 4:
        sputils.err('Not enough CPUs, need at least 4')
        return
    tdata = {
        'cpu_rdma': str(all_cpus[0]),
        'cpu_beacon': str(all_cpus[1]),
        'cpu_block': str(all_cpus[2]),
        'cpu_rest': '{min}-{max}'.format(min=all_cpus[3], max=all_cpus[-1]),
    }

    rdebug('gathering system memory information for the cgroup configuration')
    with open('/proc/meminfo', mode='r') as f:
        while True:
            line = f.readline()
            if not line:
                sputils.err('Could not find MemTotal in /proc/meminfo')
                return
            words = line.split()
            if words[0] == 'MemTotal:':
                mem_total = int(words[1])
                unit = words[2].upper()
                if unit.startswith('K'):
                    mem_total = int(mem_total / 1024)
                elif unit.startswith('M'):
                    pass
                elif unit.startswith('G'):
                    mem_total = mem_total * 1024
                else:
                    sputils.err('Could not parse the "{u}" unit for '
                                'MemTotal in /proc/meminfo'.format(u=words[2]))
                    return
                break
    mem_system = 4 * 1024
    mem_user = 4 * 1024
    mem_storpool = 1 * 1024
    mem_kernel = 10 * 1024
    if sputils.bypassed('very_little_memory'):
        hookenv.log(
            'The "very_little_memory" bypass is meant '
            'FOR DEVELOPMENT ONLY!  DO NOT run a StorPool cluster in '
            'production with it!', hookenv.WARNING)
        mem_system = 1 * 1900
        mem_user = 1 * 512
        mem_storpool = 1 * 1024
        mem_kernel = 1 * 512
    mem_reserved = mem_system + mem_user + mem_storpool + mem_kernel
    if mem_total <= mem_reserved:
        sputils.err(
            'Not enough memory, only have {total}M, need {mem}M'.format(
                mem=mem_reserved, total=mem_total))
        return
    mem_machine = mem_total - mem_reserved
    tdata.update({
        'mem_system': mem_system,
        'mem_user': mem_user,
        'mem_storpool': mem_storpool,
        'mem_machine': mem_machine,
    })

    rdebug('generating the cgroup configuration: {tdata}'.format(tdata=tdata))
    if not os.path.isdir('/etc/cgconfig.d'):
        os.mkdir('/etc/cgconfig.d', mode=0o755)
    cgconfig_dir = '/usr/share/doc/storpool/examples/cgconfig/ubuntu1604'
    for (path, _, files) in os.walk(cgconfig_dir):
        for fname in files:
            src = path + '/' + fname
            dst = src.replace(cgconfig_dir, '')
            dstdir = os.path.dirname(dst)
            if not os.path.isdir(dstdir):
                os.makedirs(dstdir, mode=0o755)

            if fname in (
                    'machine.slice.conf',
                    'storpool.slice.conf',
                    'system.slice.conf',
                    'user.slice.conf',
                    'machine-cgsetup.conf',
            ):
                with tempfile.NamedTemporaryFile(dir='/tmp',
                                                 mode='w+t',
                                                 delete=True) as tempf:
                    rdebug('- generating {tempf} for {dst}'.format(
                        dst=dst, tempf=tempf.name))
                    templating.render(
                        source=fname,
                        target=tempf.name,
                        owner='root',
                        perms=0o644,
                        context=tdata,
                    )
                    rdebug('- generating {dst}'.format(dst=dst))
                    txn.install('-o', 'root', '-g', 'root', '-m', '644', '--',
                                tempf.name, dst)
            else:
                mode = '{:o}'.format(os.stat(src).st_mode & 0o777)
                rdebug('- installing {src} as {dst}'.format(src=src, dst=dst))
                txn.install('-o', 'root', '-g', 'root', '-m', mode, '--', src,
                            dst)

    rdebug('starting the cgconfig service')
    rdebug('- refreshing the systemctl service database')
    subprocess.check_call(['systemctl', 'daemon-reload'])
    rdebug('- starting the cgconfig service')
    try:
        host.service_resume('cgconfig')
    except Exception:
        pass

    rdebug('setting the package-installed state')
    reactive.set_state('storpool-common.package-installed')
    spstatus.npset('maintenance', '')
Beispiel #21
0
def run():
    rdebug("Run, OpenStack integration, run!")
    run_osi.run()
    rdebug("Returning to the storpool-beacon setup")
    sputils.check_systemd_service("storpool_beacon")
    spstatus.npset("maintenance", "")
def report_no_config():
    """
    Note that the `storpool_repo_url` has not been set yet.
    """
    rdebug('no StorPool configuration yet')
    spstatus.npset('maintenance', 'waiting for the StorPool configuration')