1- import json
21import tempfile
2+ import xml .etree .ElementTree as ET
33from importlib .resources import files
44from pathlib import Path
55
@@ -34,9 +34,9 @@ def set_kubectl_context(namespace: str):
3434
3535@network .command ()
3636@click .argument ("graph_file" , default = DEFAULT_GRAPH_FILE , type = click .Path ())
37- @click .option ("--force" , default = False , is_flag = True , type = bool )
3837@click .option ("--network" , default = "warnet" , show_default = True )
39- def start (graph_file : Path , force : bool , network : str ):
38+ @click .option ("--logging/--no-logging" , default = False )
39+ def start (graph_file : Path , logging : bool , network : str ):
4040 """
4141 Start a warnet with topology loaded from a <graph_file> into [network]
4242 """
@@ -76,6 +76,13 @@ def start(graph_file: Path, force: bool, network: str):
7676 print (
7777 "Warning: Failed to set kubectl context. You may need to manually switch to the warnet namespace."
7878 )
79+
80+ if logging :
81+ helm_result = setup_logging_helm ()
82+ if helm_result :
83+ print ("Helm charts installed successfully." )
84+ else :
85+ print ("Failed to install Helm charts." )
7986 else :
8087 print (f"Failed to start warnet '{ network } '." )
8188 finally :
@@ -92,6 +99,9 @@ def down(network: str):
9299 # Delete the namespace
93100 command = f"kubectl delete namespace { network } "
94101 result = run_command (command , stream_output = True )
102+ # TODO: Fix this
103+ command = "kubectl delete namespace warnet-logging"
104+ result = run_command (command , stream_output = True )
95105
96106 if result :
97107 print (f"Warnet '{ network } ' has been successfully brought down and the namespace deleted." )
@@ -145,6 +155,11 @@ def generate_kubernetes_yaml(graph: nx.Graph) -> list:
145155 kubernetes_objects .append (namespace )
146156
147157 for node , data in graph .nodes (data = True ):
158+ # Create a ConfigMap for each node
159+ config = generate_node_config (node , data )
160+ config_map = create_config_map (node , config )
161+ kubernetes_objects .append (config_map )
162+
148163 # Create a deployment for each node
149164 deployment = create_node_deployment (node , data )
150165 kubernetes_objects .append (deployment )
@@ -163,7 +178,6 @@ def create_namespace() -> dict:
163178def create_node_deployment (node : int , data : dict ) -> dict :
164179 image = data .get ("image" , "bitcoindevproject/bitcoin:27.0" )
165180 version = data .get ("version" , "27.0" )
166- bitcoin_config = data .get ("bitcoin_config" , "" )
167181
168182 return {
169183 "apiVersion" : "v1" ,
@@ -180,10 +194,17 @@ def create_node_deployment(node: int, data: dict) -> dict:
180194 "image" : image ,
181195 "env" : [
182196 {"name" : "BITCOIN_VERSION" , "value" : version },
183- {"name" : "BITCOIN_CONFIG" , "value" : bitcoin_config },
197+ ],
198+ "volumeMounts" : [
199+ {
200+ "name" : "config" ,
201+ "mountPath" : "/root/.bitcoin/bitcoin.conf" ,
202+ "subPath" : "bitcoin.conf" ,
203+ }
184204 ],
185205 }
186- ]
206+ ],
207+ "volumes" : [{"name" : "config" , "configMap" : {"name" : f"bitcoin-config-node-{ node } " }}],
187208 },
188209 }
189210
@@ -198,3 +219,94 @@ def create_node_service(node: int) -> dict:
198219 "ports" : [{"port" : 8333 , "targetPort" : 8333 }],
199220 },
200221 }
222+
223+
224+ def setup_logging_helm ():
225+ """
226+ Run the required Helm commands for setting up Grafana, Prometheus, and Loki.
227+ """
228+ helm_commands = [
229+ "helm repo add grafana https://grafana.github.io/helm-charts" ,
230+ "helm repo add prometheus-community https://prometheus-community.github.io/helm-charts" ,
231+ "helm repo update" ,
232+ f"helm upgrade --install --namespace warnet-logging --create-namespace --values { WAR_MANIFESTS } /loki_values.yaml loki grafana/loki --version 5.47.2" ,
233+ "helm upgrade --install --namespace warnet-logging promtail grafana/promtail" ,
234+ "helm upgrade --install --namespace warnet-logging prometheus prometheus-community/kube-prometheus-stack --namespace warnet-logging --set grafana.enabled=false" ,
235+ f"helm upgrade --install --namespace warnet-logging loki-grafana grafana/grafana --values { WAR_MANIFESTS } /grafana_values.yaml" ,
236+ ]
237+
238+ for command in helm_commands :
239+ result = run_command (command , stream_output = True )
240+ if not result :
241+ print (f"Failed to run Helm command: { command } " )
242+ return False
243+ return True
244+
245+
246+ @network .command ()
247+ @click .argument ("graph_file" , default = DEFAULT_GRAPH_FILE , type = click .Path (exists = True ))
248+ def connect (graph_file : Path ):
249+ """
250+ Connect nodes based on the edges defined in the graph file.
251+ """
252+ # Parse the GraphML file
253+ tree = ET .parse (graph_file )
254+ root = tree .getroot ()
255+
256+ # Find all edge elements
257+ edges = root .findall (".//{http://graphml.graphdrawing.org/xmlns}edge" )
258+
259+ for edge in edges :
260+ source = edge .get ("source" )
261+ target = edge .get ("target" )
262+
263+ # Construct the kubectl command
264+ command = f"kubectl exec -it warnet-node-{ source } -- bitcoin-cli -rpcuser=user -rpcpassword=password addnode warnet-node-{ target } -service:8333 add"
265+
266+ print (f"Connecting node { source } to node { target } " )
267+ result = run_command (command , stream_output = True )
268+
269+ if result :
270+ print (f"Successfully connected node { source } to node { target } " )
271+ else :
272+ print (f"Failed to connect node { source } to node { target } " )
273+
274+ print ("All connections attempted." )
275+
276+
277+ def generate_node_config (node : int , data : dict ) -> str :
278+ base_config = """
279+ regtest=1
280+ checkmempool=0
281+ acceptnonstdtxn=1
282+ debuglogfile=0
283+ logips=1
284+ logtimemicros=1
285+ capturemessages=1
286+ fallbackfee=0.00001000
287+ listen=1
288+
289+ [regtest]
290+ rpcuser=user
291+ rpcpassword=password
292+ rpcport=18443
293+ rpcallowip=0.0.0.0/0
294+ rpcbind=0.0.0.0
295+
296+ zmqpubrawblock=tcp://0.0.0.0:28332
297+ zmqpubrawtx=tcp://0.0.0.0:28333
298+ """
299+ node_specific_config = data .get ("bitcoin_config" , "" )
300+ return f"{ base_config } \n { node_specific_config } "
301+
302+
303+ def create_config_map (node : int , config : str ) -> dict :
304+ return {
305+ "apiVersion" : "v1" ,
306+ "kind" : "ConfigMap" ,
307+ "metadata" : {
308+ "name" : f"bitcoin-config-node-{ node } " ,
309+ "namespace" : "warnet" ,
310+ },
311+ "data" : {"bitcoin.conf" : config },
312+ }
0 commit comments