def _set_sys_env_props( cls, session, properties: dict, s_attr_prop: list, env_prop_type: SysEnvProperty ): """Update system environment properties""" s_attr_rand_prop = ["start", "end"] if "event" in properties and properties["event"]: event = properties["event"] else: event = None query = [] query.append("MERGE (sys:SystemEnvironment { sref: 1 })") query.append( 'MERGE (sys)-[:HAS_PROP]->(env_prop:EnvProp {{ name: "{}" }})'.format( env_prop_type.name ) ) if event: query.append( 'MERGE (env_prop)-[:HAS_PROP]->(event:EnvProp {{ event: "{}" }})'.format( event ) ) set_stm = [] if event: set_stm.append( qh.get_set_stm( properties, node_name="env_prop", supported_attr=s_attr_rand_prop ) ) set_stm.append(qh.get_set_stm(properties, "event", s_attr_prop)) else: set_stm.append( qh.get_set_stm( properties, node_name="env_prop", supported_attr=s_attr_rand_prop + s_attr_prop, ) ) query.extend(map(lambda x: "SET {}".format(x) if x else "", set_stm)) return session.run("\n".join(query))
def set_controller_prop(cls, session, server_key, controller, properties): """Update controller state Args: session: database session server_key(int): key of the server controller belongs to controller(int): controller number properties(dict): settable controller props e.g. 'mem_c_errors', 'mem_uc_errors', 'alarm' Returns: bool: True if properties were updated, False if controller number is invalid """ query = [] s_attr = ["mem_c_errors", "mem_uc_errors", "alarm"] # query as (server)->(storage_controller) query.append("MATCH (server:Asset {{ key: {} }})".format(server_key)) query.append( "MATCH (server)-[:HAS_CONTROLLER]->(ctrl:Controller {{ controllerNum: {} }})" .format(controller)) set_stm = qh.get_set_stm(properties, node_name="ctrl", supported_attr=s_attr) query.append("SET {}".format(set_stm)) query.append("RETURN ctrl") result = session.run("\n".join(query)).single() return (result and result.get("ctrl")) is not None
def set_cv_replacement(cls, session, server_key, controller, repl_status, wt_on_fail): # TODO: cachevault serial NUMBER! """Update cachevault replacement status Args: session: database session server_key(int): key of the server cachevault belongs to controller(int): controller num Returns: bool: True if properties were updated, False if controller number is invalid """ query = [] query.extend([ "MATCH (:Asset {{ key: {} }})-[:HAS_CONTROLLER]->(ctrl:Controller {{ controllerNum: {} }})" .format(server_key, controller), "MATCH (ctrl)-[:HAS_CACHEVAULT]->(cv:CacheVault)", ]) set_stm = qh.get_set_stm( { "replacement": repl_status, "writeThrough": wt_on_fail }, node_name="cv", supported_attr=["replacement", "writeThrough"], ) query.append("SET {}".format(set_stm)) query.append("RETURN ctrl, cv") result = session.run("\n".join(query)).single() return (result and result.get("ctrl") and result.get("cv")) is not None
def set_physical_drive_prop(cls, session, server_key, controller, did, properties): """Update physical drive properties (such as error counts or state) Args: session: database session server_key(int): key of the server physical drive belongs to controller(int): controller number did(int): drive id properties(dict): e.g. 'media_error_count', 'other_error_count' 'predictive_error_count' or 'state' Returns: bool: True if properties were updated, False if controller and/or did are invalid """ query = [] s_attr = [ "media_error_count", "other_error_count", "predictive_error_count", "State", "time_stamp", "rebuild_time", ] properties[ "State"] = properties["state"] if "state" in properties else None # query as (server)->(storage_controller)->(physical drive) query.append("MATCH (server:Asset {{ key: {} }})".format(server_key)) query.append( "MATCH (server)-[:HAS_CONTROLLER]->(ctrl:Controller {{ controllerNum: {} }})" .format(controller)) query.append( "MATCH (ctrl)-[:HAS_PHYSICAL_DRIVE]->(pd:PhysicalDrive {{ DID: {} }})" .format(did)) # record uptime so that the rebuilding process gets simulated if properties["State"] and properties["State"] == "Onln": properties["time_stamp"] = time.time() set_stm = qh.get_set_stm(properties, node_name="pd", supported_attr=s_attr) query.append("SET {}".format(set_stm)) query.append("RETURN ctrl, pd") result = session.run("\n".join(query)).single() return (result and result.get("ctrl") and result.get("pd")) is not None
def set_thermal_cpu_target(attr): """Set-up a new thermal relationship between a sensor and CPU load of the server sensor belongs to Returns: bool: True if a new relationship was created """ try: _ = json.loads(attr["model"]) except ValueError as error: raise ValueError("Model .json is not valid: {}".format(error)) query = [] # find the source sensor & server asset query.append( "MATCH (cpu:CPU)<-[:HAS_CPU]-(server:Asset {{ key: {} }})".format( attr["asset_key"] ) ) # find the destination or target sensor query.append( 'MATCH (target {{ name: "{}" }} )<-[:HAS_SENSOR]-(server)'.format( attr["target_sensor"] ) ) # set the thermal relationship & relationship attributes set_stm = qh.get_set_stm(attr, node_name="rel", supported_attr=["model"]) query.append("MERGE (cpu)<-[rel:HEATED_BY]-(target)") query.append("SET {}".format(set_stm)) rel_query = [] rel_query.append("MATCH (cpu)<-[ex_rel:HEATED_BY]-(target)") rel_query.append("RETURN ex_rel") with GRAPH_REF.get_session() as session: result = session.run("\n".join(query[0:2] + rel_query)) rel_exists = result.single() session.run("\n".join(query)) return rel_exists is None
def configure_asset(key, attr): """Update existing properties Args: key(int): key of the asset to be configured attr(dict): asset props' updates """ if "asset_key" in attr and attr["asset_key"]: del attr["asset_key"] with GRAPH_REF.get_session() as session: set_statement = qh.get_set_stm(attr) query = "MATCH (asset:Asset {{ key: {key} }}) SET {set_stm}".format( key=key, set_stm=set_statement) session.run(query)
def _set_thermal_target(attr, query): """Set thermal relationship between 2 ndoes Args: attr(dict): relationship properties (such as rate, event, degrees etc. ) query(list): query that includes look up of the 'target' & 'source' nodes Returns: bool: True if the relationship is new """ # determine relationship type thermal_rel_type = "" if attr["action"] == "increase": thermal_rel_type = "HEATED_BY" elif attr["action"] == "decrease": thermal_rel_type = "COOLED_BY" else: raise KeyError("Unrecognized event type: {}".format(attr["event"])) # fist check if the relationship already exists rel_query = [] rel_query.append( "MATCH (source)<-[ex_rel:{}]-(target)".format(thermal_rel_type)) rel_query.append("RETURN ex_rel") with GRAPH_REF.get_session() as session: result = session.run("\n".join(query + rel_query)) rel_exists = result.single() # set the thermal relationship & relationship attributes s_attr = [ "pause_at", "rate", "event", "degrees", "jitter", "action", "model" ] set_stm = qh.get_set_stm(attr, node_name="rel", supported_attr=s_attr) query.append( "MERGE (source)<-[rel:{}]-(target)".format(thermal_rel_type)) query.append("SET {}".format(set_stm)) session.run("\n".join(query)) return rel_exists is None
def test_set_stm_filter(self): """Test if supported attr filters out attr""" attr = {"a": 1, "b": 2, "c": 3, "d": 4} s_attr = ["a", "b", "c"] formatted_stm = qh.get_set_stm(attr, node_name="test", supported_attr=s_attr) self.assertEqual("test.a=1,test.b=2,test.c=3", formatted_stm)
def test_set_stm(self): """Test statement formatter used for updating node attributes""" attr = {"a": 1, "b": 2, "c": 3} formatted_stm = qh.get_set_stm(attr, node_name="test") self.assertEqual("test.a=1,test.b=2,test.c=3", formatted_stm)
def create_server(key, attr, server_variation=ServerVariations.Server): """Create a simulated server """ if not attr["power_consumption"]: raise KeyError("Server asset requires power_consumption attribute") if not attr["domain_name"]: raise KeyError("Must provide VM name (domain name)") # Validate server domain name try: conn = libvirt.open("qemu:///system") conn.lookupByName(attr["domain_name"]) except libvirt.libvirtError: raise KeyError("VM does not exist") finally: conn.close() with GRAPH_REF.get_session() as session: query = [] # cypher query attr["name"] = (attr["name"] if "name" in attr and attr["name"] else attr["domain_name"]) attr["type"] = server_variation.name.lower() attr["key"] = key s_attr = [ "domain_name", "power_consumption", "power_source", ] + CREATE_SHARED_ATTR props_stm = qh.get_props_stm(attr, supported_attr=s_attr) # create a server query.append("CREATE (server:Asset {{ {} }}) SET server :{}".format( props_stm, server_variation.name)) query.append("CREATE (server)-[:HAS_CPU]->(:CPU)") # set BMC-server specific attributes if type is bmc if server_variation == ServerVariations.ServerWithBMC: bmc_attr = {**IPMI_LAN_DEFAULTS, **attr} # merge set_stm = qh.get_set_stm(bmc_attr, node_name="server", supported_attr=IPMI_LAN_DEFAULTS.keys()) query.append("SET {}".format(set_stm)) session.run("\n".join(query)) if server_variation == ServerVariations.ServerWithBMC: # if preset is provided -> use the user-defined file f_loc = os.path.dirname(__file__) s_def_file = (lambda p, j: os.path.expanduser(attr[p]) if p in attr and attr[p] else os.path.join( f_loc, "presets/" + j)) sensor_file = s_def_file("sensor_def", "sensors.json") storage_def_file = s_def_file("storage_def", "storage.json") storage_state_file = s_def_file("storage_states", "storage_states.json") _add_sensors(key, sensor_file) _add_storage(key, storage_def_file, storage_state_file) if server_variation == ServerVariations.ServerWithBMC: with open(sensor_file) as preset_handler, GRAPH_REF.get_session( ) as session: data = json.load(preset_handler) for psu in data["psu"]: _add_psu(key, psu_index=psu["id"], attr=psu) else: # add PSUs to the model for i in range(attr["psu_num"]): psu_attr = { "power_consumption": attr["psu_power_consumption"], "power_source": attr["psu_power_source"], "variation": server_variation.name.lower(), "draw": attr["psu_load"][i] if attr["psu_load"] else 1, "id": i, } _add_psu(key, psu_index=i + 1, attr=psu_attr)