Coverage for backend.py: 84%
75 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 14:47 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 14:47 +0000
1# Flex Net Sim Backend API
2# A Flask API for running Flex Net Sim network simulations
4from flask import Flask, request, Response, stream_with_context
5from flask_cors import CORS
6from utils.helpers import *
7import time
8import json
10# --- Flask Application Setup ---
11app = Flask(__name__)
13# Enable CORS only for /run_simulation_stream
14CORS(app, resources={r"/run_simulation_stream": {"origins": "*"}})
16@app.route("/run_simulation", methods=["POST"])
17def run_simulation():
18 """
19 Executes a network simulation with the provided parameters.
21 Accepts a JSON request with simulation parameters and runs the C++ executable
22 with those parameters. Returns the simulation results.
24 Returns:
25 JSON response: Simulation data or error details
26 """
27 # Validate prerequisites
28 is_valid, error_response = validate_simulation_prerequisites()
29 if not is_valid:
30 compile_simulation(True)
31 return error_response
33 try:
34 data = request.get_json()
36 # Parse and validate parameters
37 is_valid, result = parse_simulation_parameters(data)
38 if not is_valid:
39 return result
41 # Build and execute command
42 command = build_simulation_command(result)
43 logger.debug(f"Running simulation with command: {' '.join(command)}")
45 # Execute simulation
46 process = subprocess.Popen(
47 command,
48 stdout=subprocess.PIPE,
49 stderr=subprocess.PIPE,
50 text=True
51 )
52 stdout, stderr = process.communicate()
54 # Handle execution result
55 if process.returncode != 0:
56 logger.error(f"Simulation execution failed. Return code: {process.returncode}, Error: {stderr.strip()}")
57 return jsonify({
58 "status": "error",
59 "message": "Simulation execution failed",
60 "error": stderr.strip()
61 }), 500
63 # Return successful result
64 return jsonify({
65 "status": "success",
66 "data": stdout.strip()
67 }), 200
69 except Exception as e:
70 # Handle unexpected errors
71 logger.exception("Unexpected error during simulation:")
72 return jsonify({
73 "status": "error",
74 "message": "An unexpected error occurred"
75 }), 500
77@app.route("/run_simulation_stream", methods=["POST"])
78def run_simulation_stream():
79 """
80 Executes a network simulation with the provided parameters and streams the output.
82 Accepts a JSON request with simulation parameters and runs the C++ executable
83 with those parameters. Returns a streaming response with simulation results
84 as they become available.
86 Returns:
87 Streaming response: Line-by-line simulation data
88 """
89 # Validate prerequisites
90 is_valid, error_response = validate_simulation_prerequisites()
91 if not is_valid:
92 compile_simulation(True)
93 return error_response
95 try:
96 data = request.get_json()
98 # Parse and validate parameters
99 is_valid, result = parse_simulation_parameters(data)
100 if not is_valid:
101 return result
103 # Build command
104 command = build_simulation_command(result)
105 logger.debug(f"Running streaming simulation with command: {' '.join(command)}")
107 # Create streaming function
108 def generate():
109 # Send initial event
110 yield f"event: start\n"
111 yield f"data: {json.dumps({'status': 'started', 'message': 'Simulation started', 'timestamp': time.time()})}\n\n"
113 # Execute simulation with streaming output
114 process = subprocess.Popen(
115 command,
116 stdout=subprocess.PIPE,
117 stderr=subprocess.PIPE,
118 text=True,
119 bufsize=1 # Line buffered
120 )
122 # Stream stdout
123 for line in iter(process.stdout.readline, ""):
124 if line:
125 yield f"event: data\n"
126 yield f"data: {json.dumps({'status': 'running', 'message': line.strip(), 'timestamp': time.time()})}\n\n"
128 # Check for errors at the end
129 process.stdout.close()
130 return_code = process.wait()
132 if return_code != 0:
133 error = process.stderr.read().strip()
134 yield f"event: error\n"
135 yield f"data: {json.dumps({'status': 'error', 'message': 'Simulation execution failed', 'error': error, 'timestamp': time.time()})}\n\n"
136 logger.error(f"Streaming simulation failed. Return code: {return_code}, Error: {error}")
138 # Close the stream resources
139 process.stderr.close()
141 # Send completion event
142 yield f"event: end\n"
143 yield f"data: {json.dumps({'status': 'completed', 'message': 'Simulation completed', 'timestamp': time.time()})}\n\n"
145 return Response(
146 stream_with_context(generate()),
147 mimetype="text/event-stream",
148 headers={
149 "Cache-Control": "no-cache",
150 "X-Accel-Buffering": "no",
151 "Connection": "keep-alive"
152 }
153 )
155 except Exception as e:
156 # Handle unexpected errors
157 logger.exception("Unexpected error during streaming simulation:")
158 return jsonify({
159 "status": "error",
160 "message": "An unexpected error occurred",
161 "timestamp": time.time()
162 }), 500
164@app.route("/help", methods=["GET"])
165def simulation_help():
166 """
167 Provides API documentation in plain text format.
169 Returns information about available endpoints, parameters,
170 and example usage for the Flex Net Sim API.
172 Returns:
173 Plain text response: API documentation
174 """
176 help_message = """\
177 Flex Net Sim API Documentation
179 ENDPOINTS:
180 - /run_simulation (POST): Returns complete simulation results
181 - /run_simulation_stream (POST): Streams results in real-time using Server-Sent Events
183 COMMON PARAMETERS (JSON body, all optional):
184 algorithm: "FirstFit" or "BestFit" (default: "FirstFit")
185 networkType: 1 for EON (default: 1)
186 goalConnections: 1-10000000 (default: 100000)
187 confidence: 0-1 (default: 0.05)
188 lambdaParam: > 0 (default: 1.0)
189 mu: > 0 (default: 10.0)
190 network: "NSFNet", "Cost239", "EuroCore", "GermanNet", "UKNet" (default: "NSFNet")
191 bitrate: "fixed-rate" or "flex-rate" (default: "fixed-rate")
192 K: 1-6 (default: 3)
194 EXAMPLE - STANDARD REQUEST:
195 curl -X POST -H "Content-Type: application/json" \\
196 -d '{"algorithm": "FirstFit", "goalConnections": 1000000, "lambdaParam": 120, "mu": 1}' \\
197 https://fns-api-cloud-run-787143541358.us-central1.run.app/run_simulation
199 EXAMPLE - STREAMING REQUEST:
200 curl -X POST -H "Content-Type: application/json" \\
201 -d '{"algorithm": "FirstFit", "goalConnections": 1000000, "lambdaParam": 120, "mu": 1}' \\
202 https://fns-api-cloud-run-787143541358.us-central1.run.app/run_simulation_stream
204 RESPONSES:
205 Standard (/run_simulation):
206 Success (200): {"status": "success", "data": "simulation results..."}
207 Invalid Parameters (400): {"status": "error", "message": "Invalid parameters", "error": "Details"}
208 Error (500): {"status": "error", "message": "Error message", "error": "Details"}
210 Streaming (/run_simulation_stream):
211 Success (200): Server-Sent Events (text/event-stream)
212 event: start
213 data: {"status": "started", "message": "Simulation started"}
215 event: data
216 data: {"status": "running", "message": "Line of output"}
218 event: end
219 data: {"status": "completed", "message": "Simulation completed"}
221 Invalid Parameters (400): {"status": "error", "message": "Invalid parameters", "error": "Details"}
222 Error (500): {"status": "error", "message": "Error message", "error": "Details"}
223 """
225 return app.response_class(
226 response=help_message,
227 status=200,
228 mimetype="text/plain"
229 )
231# --- Application Initialization ---
232with app.app_context():
233 # Compile the simulation on startup
234 compile_success = compile_simulation()
236# --- Main Entry Point ---
237if __name__ == "__main__":
238 if not compile_success:
239 logger.error("Application startup failed: Compilation error. Check logs for details.")
240 else:
241 logger.info("Starting Flex Net Sim API server...")
242 app.run(host="0.0.0.0")