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")
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")
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
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)