1313
1414graph_ref = GraphReference ()
1515
16- def to_camelcase (s ):
16+ def _get_props_stm (attr , supported_attr = []):
17+ """Format dict attributes as neo4j props"""
18+
19+ existing = dict (
20+ filter (lambda k : attr [k [0 ]] != None and (not supported_attr or k [0 ] in supported_attr ), attr .items ())
21+ )
22+ return ',' .join (map (lambda k : "{}: {}" .format (_to_camelcase (k ), repr (existing [k ])), existing ))
23+
24+
25+ def _get_set_stm (attr , node_name = "asset" , supported_attr = []):
26+ """Format dict as neo4j set statement"""
27+
28+ existing = dict (
29+ filter (lambda k : attr [k [0 ]] != None and (not supported_attr or k [0 ] in supported_attr ), attr .items ())
30+ )
31+ return ',' .join (map (lambda k : "{}.{}={}" .format (node_name , _to_camelcase (k ), repr (existing [k ])), existing ))
32+
33+
34+ def _add_psu (key , psu_index , attr ):
35+ """Add a PSU to an existing server
36+
37+ Args:
38+ key(int): server key psu will belong to
39+ psu_index(int): psu number
40+ attr(dict): psu attributes such as power_source, power_consumption, variation & draw
41+ """
42+
43+ with graph_ref .get_session () as session :
44+
45+ query = []
46+ # find the server
47+ query .append ("MATCH (asset:Asset {{ key: {} }})" .format (key ))
48+
49+ # create a PSU
50+ attr ['key' ] = int ("{}{}" .format (key , psu_index ))
51+ attr ['name' ] = 'psu' + str (psu_index )
52+ attr ['type' ] = 'psu'
53+
54+ props_stm = _get_props_stm (attr )
55+ query .append ("CREATE (psu:Asset:PSU:Component {{ {} }})" .format (props_stm ))
56+
57+ # set relationships
58+ query .append ("CREATE (asset)-[:HAS_COMPONENT]->(psu)" )
59+ query .append ("CREATE (asset)-[:POWERED_BY]->(psu)" )
60+
61+ session .run ("\n " .join (query ))
62+
63+
64+ def _to_camelcase (s ):
1765 return re .sub (r'(?!^)_([a-zA-Z])' , lambda m : m .group (1 ).upper (), s )
1866
67+
68+ def _generate_id (size = 12 , chars = string .ascii_uppercase + string .digits ):
69+ """ Ref: https://stackoverflow.com/a/23728630"""
70+ return '' .join (secrets .choice (chars ) for _ in range (size ))
71+
72+
73+ def _generate_mac ():
74+ return '' .join (random .choice ('0123456789abcdef' ) for _ in range (12 ))
75+
76+
1977def configure_asset (key , attr ):
20- """Update existing properties"""
78+ """Update existing properties
79+
80+ Args:
81+ key(int): key of the asset to be configured
82+ attr(dict): asset props' updates
83+ """
84+
2185 if 'func' in attr and attr ['func' ]:
2286 del attr ['func' ]
2387
2488 if 'asset_key' in attr and attr ['asset_key' ]:
2589 del attr ['asset_key' ]
2690
2791 with graph_ref .get_session () as session :
28-
29- existing = dict (filter (lambda k : attr [k [0 ]] != None , attr .items ()))
30- # print(existing)
31- # existing = filter(lambda k: "asset.{}={}".format(k, repr(attr[k])) if attr[k] else None, attr)
32- set_statement = ',' .join (map (lambda k : "asset.{}={}" .format (to_camelcase (k ), repr (existing [k ])), existing ))
92+
93+ set_statement = _get_set_stm (attr )
3394 query = "MATCH (asset:Asset {{ key: {key} }}) SET {set_stm}" .format (key = key , set_stm = set_statement )
3495
3596 session .run (query )
3697
98+
3799def remove_link (source_key , dest_key ):
38- """Remove existing power connection"""
100+ """Remove existing power connection
101+
102+ Args:
103+ source_key(int): key of the parent asset
104+ dest_key(int): key of the asset powered by source_key
105+ """
106+
39107 with graph_ref .get_session () as session :
40108 result = session .run ("""
41109 MATCH (src:Asset {key: $src_key})<-[power_link:POWERED_BY]-(dst:Asset {key: $dest_key})
@@ -44,7 +112,13 @@ def remove_link(source_key, dest_key):
44112
45113
46114def link_assets (source_key , dest_key ):
47- """Power a component by another component """
115+ """Power a component by another component
116+
117+ Args:
118+ source_key(int): key of the parent asset
119+ dest_key(int): key of the asset to be powered by the parent
120+ """
121+
48122 with graph_ref .get_session () as session :
49123
50124 # Validate that the asset does not power already existing device
@@ -80,37 +154,17 @@ def link_assets(source_key, dest_key):
80154 if (not record ) or (not 'link' in dict (record )):
81155 print ('Invalid link configuration was provided' )
82156
83- def _add_psu (key , psu_index , power_consumption , power_source , variation , draw_percentage = 1 ):
84- with graph_ref .get_session () as session :
85- session .run ("\
86- MATCH (asset:Asset {key: $pkey})\
87- CREATE (psu:Asset:PSU:Component { \
88- name: $psuname,\
89- key: $psukey,\
90- draw: $draw,\
91- type: 'psu',\
92- powerConsumption: $pc,\
93- powerSource: $ps,\
94- variation: $var \
95- })\
96- CREATE (asset)-[:HAS_COMPONENT]->(psu)\
97- CREATE (asset)-[:POWERED_BY]->(psu)" ,
98- pkey = key ,
99- pc = power_consumption ,
100- ps = power_source ,
101- psuname = 'psu' + psu_index ,
102- draw = draw_percentage ,
103- psukey = int ("{}{}" .format (key , psu_index )),
104- var = variation .name .lower ()
105- )
106-
107157
108158def create_outlet (key , attr ):
109159 """Add outlet to the model """
160+
110161 with graph_ref .get_session () as session :
111- session .run ("\
112- CREATE (:Asset:Outlet { name: $name, key: $key, type: 'outlet' })" , key = key , name = "out-{}" .format (key ))
113- set_properties (key , attr )
162+ attr ['name' ] = attr ['name' ] if 'name' in attr and attr ['name' ] else "out-{}" .format (key )
163+ attr ['type' ] = 'outlet'
164+ attr ['key' ] = key
165+
166+ props_stm = _get_props_stm (attr , supported_attr = ["name" , "type" , "key" , "off_delay" , "on_delay" ])
167+ session .run ("CREATE (:Asset:Outlet {{ {} }})" .format (props_stm ))
114168
115169
116170class ServerVariations (Enum ):
@@ -135,6 +189,7 @@ def create_server(key, attr, server_variation=ServerVariations.Server):
135189 if not attr ['domain_name' ]:
136190 raise KeyError ('Must provide VM name (domain name)' )
137191
192+ # Validate server domain name
138193 try :
139194 conn = libvirt .open ("qemu:///system" )
140195 conn .lookupByName (attr ['domain_name' ])
@@ -146,35 +201,36 @@ def create_server(key, attr, server_variation=ServerVariations.Server):
146201
147202 with graph_ref .get_session () as session :
148203
149- session .run ("\
150- CREATE (server:Asset { name: $name, domainName: $name, key: $key, type: $stype }) SET server :"+ server_variation .name ,
151- key = key , name = attr ['domain_name' ], stype = server_variation .name .lower ())
204+ query = [] # cypher query
152205
206+ attr ['name' ] = attr ['name' ] if 'name' in attr and attr ['name' ] else attr ['domain_name' ]
207+ attr ['type' ] = server_variation .name .lower ()
208+ attr ['key' ] = key
209+
210+ s_attr = ["name" , "domain_name" , "type" , "key" , "off_delay" , "on_delay" , "power_consumption" , "power_source" ]
211+ props_stm = _get_props_stm (attr , supported_attr = s_attr )
212+
213+ # create a server
214+ query .append ("CREATE (server:Asset {{ {} }}) SET server :{}" .format (props_stm , server_variation .name ))
215+
216+ # set BMC-server specific attributes if type is bmc
153217 if server_variation == ServerVariations .ServerWithBMC :
154- bmc_attr = {** IPMI_LAN_DEFAULTS , ** attr }
155- session .run ("""
156- MATCH (a:Asset {key: $key})
157- SET a.user=$user, a.password=$password, a.host=$host, a.port=$port, a.vmport=$vmport
158- """ ,
159- key = key ,
160- user = bmc_attr ['user' ],
161- password = bmc_attr ['password' ],
162- host = bmc_attr ['host' ],
163- port = bmc_attr ['port' ],
164- vmport = bmc_attr ['vmport' ]
165- )
218+ bmc_attr = {** IPMI_LAN_DEFAULTS , ** attr } # merge
166219
220+ set_stm = _get_set_stm (bmc_attr , node_name = "server" , supported_attr = IPMI_LAN_DEFAULTS .keys ())
221+ query .append ("SET {}" .format (set_stm ))
167222
168- set_properties (key , attr )
223+ session .run ("\n " .join (query ))
224+
225+ # add PSUs to the model
169226 for i in range (attr ['psu_num' ]):
170- _add_psu (
171- key ,
172- str (i + 1 ),
173- attr ['psu_power_consumption' ],
174- attr ['psu_power_source' ],
175- server_variation ,
176- attr ['psu_load' ][i ] if attr ['psu_load' ] else 1
177- )
227+ psu_attr = {
228+ "power_consumption" : attr ['psu_power_consumption' ],
229+ "power_source" : attr ['psu_power_source' ],
230+ "variation" : server_variation .name .lower (),
231+ "draw" : attr ['psu_load' ][i ] if attr ['psu_load' ] else 1
232+ }
233+ _add_psu (key , psu_index = i + 1 , attr = psu_attr )
178234
179235
180236def set_properties (key , attr ):
@@ -210,14 +266,6 @@ def set_properties(key, attr):
210266 SET asset.imgUrl=$img_url" , pkey = key , img_url = attr ['img_url' ])
211267
212268
213- def id_generator (size = 12 , chars = string .ascii_uppercase + string .digits ):
214- """ Ref: https://stackoverflow.com/a/23728630"""
215- return '' .join (secrets .choice (chars ) for _ in range (size ))
216-
217-
218- def mac_generator ():
219- return '' .join (random .choice ('0123456789abcdef' ) for _ in range (12 ))
220-
221269
222270def create_ups (key , attr , preset_file = os .path .join (os .path .dirname (__file__ ), 'presets/apc_ups.json' )):
223271 """Add UPS to the system model """
@@ -267,10 +315,10 @@ def create_ups(key, attr, preset_file=os.path.join(os.path.dirname(__file__), 'p
267315
268316 for k , v in data ["OIDs" ].items ():
269317 if k == 'SerialNumber' :
270- v ['defaultValue' ] = id_generator ()
318+ v ['defaultValue' ] = _generate_id ()
271319
272320 if k == 'MAC' :
273- v ['defaultValue' ] = mac_generator ()
321+ v ['defaultValue' ] = _generate_mac ()
274322
275323 if k == "BasicBatteryStatus" :
276324 oid_desc = dict ((y ,x ) for x ,y in v ["oidDesc" ].items ())
@@ -467,10 +515,10 @@ def create_pdu(key, attr, preset_file=os.path.join(os.path.dirname(__file__), 'p
467515 # Add PDU OIDS to the model
468516 for k , v in data ["OIDs" ].items ():
469517 if k == 'SerialNumber' :
470- v ['defaultValue' ] = id_generator ()
518+ v ['defaultValue' ] = _generate_id ()
471519
472520 if k == 'MAC' :
473- v ['defaultValue' ] = mac_generator ()
521+ v ['defaultValue' ] = _generate_mac ()
474522
475523 session .run ("\
476524 MATCH (pdu:PDU {key: $pkey})\
@@ -595,10 +643,10 @@ def drop_model():
595643def delete_asset (key ):
596644 """ Delete by key """
597645 with graph_ref .get_session () as session :
598- session .run ("MATCH (a:Asset { key: $key }) \
599- OPTIONAL MATCH (a)-[:HAS_COMPONENT]->(s) \
600- OPTIONAL MATCH (a)-[:HAS_OID]->(oid) \
601- OPTIONAL MATCH (a)-[:HAS_BATTERY]->(b) \
602- OPTIONAL MATCH (oid)-[:HAS_STATE_DETAILS]->(sd) \
603- DETACH DELETE a,s,oid,sd,b" , key = key )
646+ session .run (""" MATCH (a:Asset { key: $key })
647+ OPTIONAL MATCH (a)-[:HAS_COMPONENT]->(s)
648+ OPTIONAL MATCH (a)-[:HAS_OID]->(oid)
649+ OPTIONAL MATCH (a)-[:HAS_BATTERY]->(b)
650+ OPTIONAL MATCH (oid)-[:HAS_STATE_DETAILS]->(sd)
651+ DETACH DELETE a,s,oid,sd,b""" , key = key )
604652
0 commit comments