def check_for_new_presence(data):
    found = None
    old = unitdata.kv().get(kvdata.KEY_LXD_NAME)
    our_mach_id = sputils.get_machine_id()

    for node in data["nodes"]:
        if not node.startswith("cinder:"):
            continue
        mach_id = node[7:]
        parts = mach_id.split("/")
        if len(parts) == 3 and parts[1] == "lxd":
            if parts[0] == our_mach_id:
                rdebug(
                    "found our container: {lx}".format(lx=mach_id),
                    cond="announce",
                )
                if found is None:
                    found = mach_id
                    if old is None or old != mach_id:
                        rdebug("setting Cinder container {mach_id}".format(
                            mach_id=mach_id))
                        unitdata.kv().set(kvdata.KEY_LXD_NAME, mach_id)
                        reactive.set_state("storpool-block-charm.lxd")

    if not found:
        rdebug("- no Cinder containers here", cond="announce")
        if old is not None:
            rdebug("forgetting about Cinder container {old}".format(old=old))
            unitdata.kv().set(kvdata.KEY_LXD_NAME, None)
            reactive.set_state("storpool-block-charm.lxd")
Exemple #2
0
def get_status():
    status = {
        'cinder-hook': reactive.is_state('storage-backend.configure'),
        'node': sputils.get_machine_id(),
        'parent-node': sputils.get_parent_node(),
        'charm-config': hookenv.config(),
        'ready': False,
    }

    status['presence'] = service_hook.fetch_presence(RELATIONS)
    parent_name = 'block:' + status['parent-node']

    template = status['charm-config'].get('storpool_template')
    msg = None
    if not status['cinder-hook']:
        msg = 'No Cinder hook yet'
    elif parent_name not in status['presence']['nodes']:
        msg = 'No presence data from our parent node'
    elif template is None or template == '':
        msg = 'No "storpool_template" in the charm config'
    elif not reactive.is_state('cinder-storpool.ready'):
        msg = 'Something went wrong, please look at the unit log'
    if msg is not None:
        status['message'] = msg
        return status

    found = False
    status['proc'] = {}
    for cmd in ('cinder-volume', 'nova-compute'):
        d = osi.check_spopenstack_processes(cmd)
        if d:
            found = True
        status['proc'][cmd] = d
        bad = sorted(filter(lambda pid: not d[pid], d.keys()))
        if bad:
            status['message'] = 'No spopenstack group: {pid}'.format(pid=bad)
            return status

    if found:
        dirname = '/var/spool/openstack-storpool'
        if not os.path.isdir(dirname):
            status['message'] = 'No {d} directory'.format(d=dirname)
            return status
        st = os.stat(dirname)
        if not st.st_mode & 0o0020:
            status['message'] = '{d} not group-writable'.format(d=dirname)
            return status

    status['message'] = 'The StorPool Cinder backend should be up and running'
    status['ready'] = True
    return status
def we_are_the_leader():
    """
    Make note of the fact that this unit has been elected as the leader for
    the `storpool-block` charm.  This will prompt the unit to send presence
    information to the other charms along the `storpool-presence` hook.
    """
    rdebug("have we really been elected leader?")
    try:
        hookenv.leader_set(charm_storpool_block_unit=sputils.get_machine_id())
    except Exception as e:
        rdebug("no, could not run leader_set: {e}".format(e=e))
        reactive.remove_state("storpool-block-charm.leader")
        return
    rdebug("looks like we have been elected leader")
    reactive.set_state("storpool-block-charm.leader")
Exemple #4
0
def ensure_our_presence():
    """
    Make sure that our node is declared as present.
    """
    rdebug('about to make sure that we are represented in the presence data')
    state = service_hook.get_present_nodes()
    rdebug('got some state: {state}'.format(state=state))

    # Let us make sure our own data is here
    sp_node = sputils.get_machine_id()
    oid = spconfig.get_our_id()
    if sp_node not in state:
        rdebug('adding our own node {sp_node}'.format(sp_node=sp_node))
        service_hook.add_present_node(sp_node, oid, 'block-p')
        rdebug('something changed, will announce (if leader): {state}'.format(
            state=service_hook.get_present_nodes()))
        reactive.set_state('storpool-block-charm.announce-presence')
    def do_test_ensure_our_presence(self):
        """
        Make sure ensure_our_presence() really adds our node and also
        triggers an announcement if necessary.
        """
        states = r_state.r_get_states()
        presence = spservice.get_present_nodes()

        r_state.set_state(PRESENCE_STATE)
        do_announce = r_state.r_get_states()
        r_state.remove_state(PRESENCE_STATE)
        no_announce = r_state.r_get_states()
        self.assertNotEquals(do_announce, no_announce)
        self.assertEquals(no_announce.union(set([PRESENCE_STATE])),
                          do_announce)

        sp_node = sputils.get_machine_id()
        other_nodes = {
            'not-' + sp_node: '17',
            'neither-' + sp_node: '18',
        }
        with_ours = {
            **other_nodes,
            sp_node: '16',
        }

        # No change if our node is there.
        r_state.r_set_states(no_announce)
        spservice.r_set_present_nodes(with_ours)
        testee.ensure_our_presence()
        self.assertEquals(no_announce, r_state.r_get_states())
        self.assertEquals(with_ours, spservice.get_present_nodes())

        # Add just our node if it's not there.
        r_state.r_set_states(no_announce)
        spservice.r_set_present_nodes(other_nodes)
        testee.ensure_our_presence()
        self.assertEquals(do_announce, r_state.r_get_states())
        self.assertEquals(with_ours, spservice.get_present_nodes())

        r_state.r_set_states(states)
        spservice.r_set_present_nodes(presence)
def announce_presence(force=False):
    data = service_hook.fetch_presence(RELATIONS)

    mach_id = "block:" + sputils.get_machine_id()

    announce = force
    block_joined = reactive.is_state("block-p.notify-joined")
    cinder_joined = reactive.is_state("storpool-presence.notify-joined")
    if cinder_joined or block_joined:
        announce = True

    generation = int(data["generation"])
    if generation < 0:
        generation = 0

    if reactive.is_state("storpool-block-charm.bump-generation"):
        generation = generation + 1
        announce = True

    if announce:
        our_node = {"generation": generation, "hostname": platform.node()}
        if reactive.is_state("storpool-block-charm.leader"):
            our_node["config"] = {
                "storpool_repo_url": "",
                "storpool_version": "",
                "storpool_openstack_version": "",
                "storpool_conf": "",
            }

        ndata = {"generation": generation, "nodes": {mach_id: our_node}}
        rdebug("announcing {data}".format(data=ndata), cond="announce")
        service_hook.send_presence(ndata, RELATIONS)

    reactive.remove_state("storpool-block-charm.bump-generation")

    check_for_new_presence(data)
def get_status():
    inst = reactive.is_state("storpool-block-charm.services-started")
    status = {
        "node": sputils.get_machine_id(),
        "charm-config": dict(hookenv.config()),
        "storpool-conf": read_storpool_conf(),
        "installed": inst,
        "presence": service_hook.fetch_presence(RELATIONS),
        "lxd": unitdata.kv().get(kvdata.KEY_LXD_NAME),
        "ready": False,
    }

    for name in (
            "storpool_repo_url",
            "storpool_version",
            "storpool_openstack_version",
    ):
        value = status["charm-config"].get(name)
        if value is None or value == "":
            status["message"] = "No {name} in the config".format(name=name)
            return status
    if not inst:
        status["message"] = "Packages not installed yet"
        return status

    spstatus.set("maintenance", "checking the StorPool configuration")
    rdebug("about to try to obtain our StorPool ID")
    try:
        out = subprocess.check_output(["storpool_showconf", "-ne", "SP_OURID"])
        out = out.decode()
        out = out.split("\n")
        our_id = out[0]
    except Exception as e:
        status["message"] = "Could not obtain the StorPool ID: {e}".format(e=e)
        return status

    spstatus.set("maintenance", "checking the Cinder and Nova processes...")
    found = False
    status["proc"] = {}
    for cmd in ("cinder-volume", "nova-compute"):
        d = osi.check_spopenstack_processes(cmd)
        if d:
            found = True
        status["proc"][cmd] = d
        bad = sorted(filter(lambda pid: not d[pid], d.keys()))
        if bad:
            status["message"] = "No spopenstack group: {pid}".format(pid=bad)
            return status

    if found:
        spstatus.set("maintenance", "checking for the spool directory")
        dirname = pathlib.Path("/var/spool/openstack-storpool")
        if not dirname.is_dir():
            status["message"] = "No {d} directory".format(d=dirname)
            return status
        st = dirname.stat()
        if not st.st_mode & 0o0020:
            status["message"] = "{d} not group-writable".format(d=dirname)
            return status

    spstatus.set("maintenance", "checking the StorPool services...")
    svcs = ("storpool_beacon", "storpool_block")
    rdebug("checking for services: {svcs}".format(svcs=svcs))
    missing = list(filter(lambda s: not host.service_running(s), svcs))
    rdebug("missing: {missing}".format(missing=missing))
    if missing:
        status["message"] = "StorPool services not running: {missing}".format(
            missing=" ".join(missing))
        return status

    spstatus.set("maintenance", "querying the StorPool API")
    rdebug("checking the network status of the StorPool client")
    try:
        out = subprocess.check_output(["storpool", "-jB", "service", "list"])
        out = out.decode()
        data = json.loads(out)
        rdebug("got API response: {d}".format(d=data))
        if "error" in data:
            raise Exception(
                "API response: {d}".format(d=data["error"]["descr"]))
        state = data["data"]["clients"][our_id]["status"]
        rdebug("got our client status {st}".format(st=state))
        if state != "running":
            status["message"] = "StorPool client: {st}".format(st=state)
            return status
    except Exception as e:
        status["message"] = "Could not query the StorPool API: {e}".format(e=e)
        return status

    spstatus.set("maintenance", "querying the StorPool API for client status")
    rdebug("checking the status of the StorPool client")
    try:
        out = subprocess.check_output(["storpool", "-jB", "client", "status"])
        out = out.decode()
        data = json.loads(out)
        rdebug("got API response: {d}".format(d=data))
        if "error" in data:
            raise Exception(
                "API response: {d}".format(d=data["error"]["descr"]))
        int_id = int(our_id)
        found = list(filter(lambda e: e["id"] == int_id, data["data"]))
        if not found:
            raise Exception(
                "No client status reported for {our_id}".format(our_id=our_id))
        state = found[0]["configStatus"]
        status["message"] = "StorPool client: {st}".format(st=state)

        if state == "ok":
            status["ready"] = True
            rdebug("get_status: calling for Cinder LXD reconfiguration")
            reactive.set_state("storpool-block-charm.lxd")
        else:
            status["ready"] = False
        return status
    except Exception as e:
        status["message"] = "Could not query the StorPool API: {e}".format(e=e)
        return status
Exemple #8
0
def announce_presence(force=False):
    data = service_hook.fetch_presence(RELATIONS)
    rdebug('processing presence data at generation {gen}'.format(
        gen=data['generation']),
           cond='announce')

    rdebug('state: {d}'.format(d=repr(
        sorted((key, value['hostname'], value['generation'], 'config' in value)
               for key, value in data['nodes'].items()))),
           cond='announce')

    announce = force
    cinder_joined = reactive.is_state('cinder-p.notify-joined')
    block_joined = reactive.is_state('storpool-presence.notify-joined')
    if cinder_joined or block_joined:
        announce = True

    # Look for a block unit's config info.
    cfg = None
    cfg_gen = -1
    for node, ndata in data['nodes'].items():
        if not node.startswith('block:'):
            continue
        ncfg = ndata.get('config')
        if ncfg is None:
            continue
        if cfg is None:
            cfg = ncfg
            cfg_gen = ndata['generation']
        else:
            cfg = None
            break

    parent_id = 'block:' + sputils.get_parent_node()
    if parent_id not in data['nodes']:
        rdebug('no {parent} in the presence data yet'.format(parent=parent_id),
               cond='announce')
        deconfigure()
    else:
        rdebug('found presence data for {parent}'.format(parent=parent_id),
               cond='announce')
        if cfg is not None:
            # ...then, finally, process that config!
            reactive.set_state('storpool-presence.configured')
            last_gen = spconfig.get_meta_generation()
            if last_gen is None or int(cfg_gen) > int(last_gen):
                announce = True
                spconfig.set_meta_generation(cfg_gen)
                reactive.set_state('cinder-storpool.run')
        else:
            deconfigure()

    generation = data['generation']
    if int(generation) < 0:
        generation = 0
    if announce:
        mach_id = 'cinder:' + sputils.get_machine_id()
        data = {
            'generation': generation,
            'nodes': {
                mach_id: {
                    'generation': generation,
                    'hostname': sputils.get_machine_id()
                },
            },
        }
        rdebug('announcing {data}'.format(data=data), cond='announce')
        service_hook.send_presence(data, RELATIONS)