logging.info("Syntax: trngcli.py SRV_ADDR:SRV_PORT POST_PARAMS") sys.exit(1) # If no http-like prefix exists, add the appropriate one if not (server_url.startswith(HTTP_PREFIX) or server_url.startswith(HTTPS_PREFIX)): if Storyboard.ENABLE_HTTPS: server_url = HTTPS_PREFIX + server_url else: server_url = HTTP_PREFIX + server_url logging.info( "CyTrONE training client connecting to {0}...".format(server_url)) # Parse parameters params = query.Parameters() params.parse_parameters(POST_parameters) action = params.get(query.Parameters.ACTION) # Additional parameter needed for instantiate range action if action == query.Parameters.INSTANTIATE_RANGE and INSTANTIATE_RANGE_FROM_FILE: try: instantiate_file = open(SAMPLE_INSTANTIATE_RANGE, "r") instantiate_content = instantiate_file.read() instantiate_file.close() logging.info("Use cyber range description from file {0}.".format( SAMPLE_INSTANTIATE_RANGE)) description_parameters = { query.Parameters.DESCRIPTION_FILE: instantiate_content }
def do_POST(self): # Get the parameters of the POST request params = query.Parameters(self) if DEBUG: print SEPARATOR print "* DEBUG: instsrv: Client POST request: POST parameters: %s" % (params) # Get parameter values for given keys user_id = params.get(query.Parameters.USER) action = params.get(query.Parameters.ACTION) description_file = params.get(query.Parameters.DESCRIPTION_FILE) range_id = params.get(query.Parameters.RANGE_ID) if DEBUG: print SEPARATOR print "PARAMETERS:" print SEPARATOR print "USER: %s" % (user_id) print "ACTION: %s" % (action) print "DESCRIPTION FILE:\n%s" % (description_file) print "RANGE_ID: %s" % (range_id) print SEPARATOR ## Handle user information # Get user information from YAML file # Note: Only reading data that is (potentially) modified externally => # no need for synchronization user_info = userinfo.UserInfo() if not user_info.parse_YAML_file(DATABASE_DIR + USERS_FILE): self.send_error(SERVER_ERROR, "User information issue") return if DEBUG: user_info.pretty_print() # Check that user id is valid user_obj = user_info.get_user(user_id) if not user_obj: self.send_error(REQUEST_ERROR, "Invalid user id") return ## Handle action information # Check that action is valid if action not in self.VALID_ACTIONS: self.send_error(REQUEST_ERROR, "Invalid action") return # If we reached this point, it means processing was successful # => act according to each action if action == query.Parameters.INSTANTIATE_RANGE: # Check that description is not empty if not description_file: self.send_error(REQUEST_ERROR, "Invalid description file") return # Check that range id was provided if not range_id: self.send_error(REQUEST_ERROR, "Invalid range id") return # Save the description received as a file try: range_file_name = RANGE_DESCRIPTION_TEMPLATE.format(range_id) range_file = open(range_file_name, "w") range_file.write(description_file) range_file.close() print "* INFO: instsrv: Saved POSTed cyber range description to file '%s'." % (range_file_name) except IOError: print "* ERROR: instsrv: Could not write to file %s." % (range_file_name) print "* INFO: instsrv: Start cyber range instantiation." # Use CyRIS to really do cyber range instantiation if USE_CYRIS: try: command = "python -u " + CYRIS_PATH + "main/cyris.py " + range_file_name + " " + CYRIS_PATH + CYRIS_CONFIG_FILENAME return_value = os.system(command) exit_status = os.WEXITSTATUS(return_value) if exit_status != 0: self.handle_cyris_error(range_id) self.send_error(SERVER_ERROR, "CyRIS execution issue") return status_filename = CYRIS_PATH + CYRIS_RANGE_DIRECTORY + str(range_id) + "/" + CYRIS_STATUS_FILENAME with open(status_filename, 'r') as status_file: status_file_content = status_file.read() if DEBUG: print "* DEBUG: instsrv: Status file content=", status_file_content if Storyboard.SERVER_STATUS_SUCCESS in status_file_content: # Get notification text notification_filename_short = CYRIS_NOTIFICATION_TEMPLATE.format(range_id) notification_filename = "{0}{1}{2}/{3}".format(CYRIS_PATH, CYRIS_RANGE_DIRECTORY, range_id, notification_filename_short) if DEBUG: print "* DEBUG: instsrv: Notification file name=", notification_filename message = None with open(notification_filename, 'r') as notification_file: notification_file_content = notification_file.read() message = urllib.quote(notification_file_content) response_content = self.build_response(Storyboard.SERVER_STATUS_SUCCESS, message) # We try to prepare the terminal for Moodle, but # errors are only considered as warnings for the # moment, since this functionality is not publicly # released yet in cnt2lms try: if USE_CNT2LMS_SCRIPT_GENERATION: ssh_command = "ssh -tt -o 'ProxyCommand ssh [email protected] -W %h:%p' cyuser@moodle" python_command = "python -u " + CNT2LMS_PATH + "get_cyris_result.py " + CYRIS_MASTER_HOST + " " + CYRIS_MASTER_ACCOUNT + " " + CYRIS_PATH + CYRIS_RANGE_DIRECTORY + " " + range_id + " 1" command = ssh_command + " \"" + python_command + "\"" print "* DEBUG: instsrv: get_cyris_result command: " + command return_value = os.system(command) exit_status = os.WEXITSTATUS(return_value) if exit_status == 0: #response_content = RESPONSE_SUCCESS pass else: #self.send_error(SERVER_ERROR, "LMS terminal preparation issue") #return print "* DEBUG: instsrv: LMS terminal preparation issue" except IOError: #self.send_error(SERVER_ERROR, "LMS terminal preparation I/O error) #return print "* DEBUG: instsrv: LMS terminal preparation I/O error" else: # Even though CyRIS is now destroying automatically the cyber range # in case of error, as it may fail we still try to clean up here self.handle_cyris_error(range_id) response_content = self.build_response(Storyboard.SERVER_STATUS_ERROR, Storyboard.INSTANTIATION_STATUS_FILE_NOT_FOUND) except IOError: self.handle_cyris_error(range_id) self.send_error(SERVER_ERROR, Storyboard.INSTANTIATION_CYRIS_IO_ERROR) return # Don't use CyRIS, just simulate the instantiation else: # Simulate time needed to instantiate the cyber range if SIMULATION_DURATION == -1: sleep_time = random.randint(2,5) else: sleep_time = SIMULATION_DURATION print Storyboard.SEPARATOR3 print "* INFO: instsrv: Simulate instantiation by sleeping %d s." % (sleep_time) print Storyboard.SEPARATOR3 time.sleep(sleep_time) # Simulate the success or failure of the instantiation if random.random() > 0.0: # Get sample notification text notification_filename = "{0}/{1}".format(DATABASE_DIR, CYRIS_NOTIFICATION_SIMULATED) if DEBUG: print "* DEBUG: instsrv: Simulated notification file name=", notification_filename message = None with open(notification_filename, 'r') as notification_file: notification_file_content = notification_file.read() message = urllib.quote(notification_file_content) response_content = self.build_response(Storyboard.SERVER_STATUS_SUCCESS, message) else: response_content = self.build_response(Storyboard.SERVER_STATUS_ERROR, Storyboard.INSTANTIATION_SIMULATED_ERROR) # Destroy the cyber range elif action == query.Parameters.DESTROY_RANGE: # Check that the range id is valid if not range_id: self.send_error(REQUEST_ERROR, "Invalid range id") return print "* INFO: instsrv: Start destruction of cyber range with id %s." % (range_id) # Use CyRIS to really do cyber range destruction if USE_CYRIS: destruction_filename = CYRIS_PATH + CYRIS_DESTRUCTION_SCRIPT destruction_command = "{0} {1} {2}".format(destruction_filename, range_id, CYRIS_PATH + CYRIS_CONFIG_FILENAME) print "* DEBUG: instrv: destruction_command: " + destruction_command return_value = os.system(destruction_command) exit_status = os.WEXITSTATUS(return_value) if exit_status == 0: response_content = self.build_response(Storyboard.SERVER_STATUS_SUCCESS) else: response_content = self.build_response(Storyboard.SERVER_STATUS_ERROR, "CyRIS destruction issue") # Don't use CyRIS, just simulate the destruction else: # Simulate time needed to destroy the cyber range if SIMULATION_DURATION == -1: sleep_time = random.randint(2,5) else: sleep_time = SIMULATION_DURATION print Storyboard.SEPARATOR3 print "* INFO: instsrv: Simulate destruction by sleeping %d s." % (sleep_time) print Storyboard.SEPARATOR3 time.sleep(sleep_time) # Simulate the success or failure of the destruction if random.random() > 0.0: response_content = self.build_response(Storyboard.SERVER_STATUS_SUCCESS) else: response_content = self.build_response(Storyboard.SERVER_STATUS_ERROR, Storyboard.DESTRUCTION_SIMULATED_ERROR) # Catch potential unimplemented actions (if any) else: print "* WARNING: instsrv: Unknown action: %s." % (action) # Send response header to requester (triggers log_message()) self.send_response(HTTP_OK_CODE) self.send_header("Content-type", "text/html") self.end_headers() # Send scenario database content information to requester self.wfile.write(response_content) # Output server reply if DEBUG: print "* DEBUG: instsrv: Server response content: %s" % (response_content)
def do_POST(self): # Get the parameters of the POST request params = query.Parameters(self) if DO_DEBUG: print SEPARATOR print "* INFO: contsrv: Request to content server: POST parameters: %s" % ( params) # Get parameter values for given keys user_id = params.get(query.Parameters.USER) action = params.get(query.Parameters.ACTION) description_file = params.get(query.Parameters.DESCRIPTION_FILE) range_id = params.get(query.Parameters.RANGE_ID) activity_id = params.get(query.Parameters.ACTIVITY_ID) if DO_DEBUG: print SEPARATOR print "PARAMETERS:" print SEPARATOR print "USER: %s" % (user_id) print "ACTION: %s" % (action) print "DESCRIPTION FILE:\n%s" % (description_file) print "RANGE_ID: %s" % (range_id) print "ACTIVITY_ID: %s" % (activity_id) print SEPARATOR ## Handle user information # Get user information from YAML file # Note: Only reading data that is (potentially) modified externally => # no need for synchronization user_info = userinfo.UserInfo() if not user_info.parse_YAML_file(DATABASE_DIR + USERS_FILE): self.send_error(SERVER_ERROR, "User information issue") return if DO_DEBUG: user_info.pretty_print() # Check that user id is valid user_obj = user_info.get_user(user_id) if not user_obj: self.send_error(REQUEST_ERROR, "Invalid user id") return ## Handle action information # Check that action is valid if action not in self.VALID_ACTIONS: self.send_error(REQUEST_ERROR, "Invalid action") return # If we reached this point, it means processing was successful # => act according to each action if action == query.Parameters.UPLOAD_CONTENT: # Check that description is not empty if not description_file: self.send_error(REQUEST_ERROR, "Invalid description file") return # Save the description received as a file try: content_file_name = CONTENT_DESCRIPTION_TEMPLATE.format( range_id) content_file = open(content_file_name, "w") content_file.write(description_file) content_file.close() print "* INFO: contsrv: Saved POSTed content description to file '%s'." % ( content_file_name) except IOError as error: print( "* ERROR: contsrv: Could not write to file {}: {}".format( content_file_name, error)) print "* INFO: contsrv: Start LMS content upload." # Use Moodle to really do the content upload if USE_MOODLE: try: # ./cylms.py --convert-content training_example.yml --config-file config_example --add-to-lms 1 try: add_output = subprocess.check_output( [ "python", "-u", CYLMS_PATH + "cylms.py", "--convert-content", content_file_name, "--config-file", CYLMS_CONFIG, "--add-to-lms", range_id ], stderr=subprocess.STDOUT) # Find the activity id activity_id = None for output_line in add_output.splitlines(): print(output_line) # Extract the course id activity_id_tag = "activity_id=" if activity_id_tag in output_line: # Split line of form ...to LMS successfully => activity_id=101 activity_id = output_line.split( activity_id_tag)[1] if DO_DEBUG: print( "* DEBUG: contsrv: Extracted activity id from command output: {}" .format(activity_id)) response_content = RESPONSE_SUCCESS_ID_PREFIX + activity_id + RESPONSE_SUCCESS_ID_SUFFIX # Check whether the activity id was extracted if not activity_id: self.send_error(SERVER_ERROR, "LMS upload issue") return except subprocess.CalledProcessError as error: print("* ERROR: contsrv: Error message: {}".format( error.output)) self.send_error(SERVER_ERROR, "CyLMS execution issue") return except IOError: self.send_error(SERVER_ERROR, "LMS upload I/O error") return # Don't use Moodle, just simulate the content upload else: # Simulate time needed to instantiate the cyber range if SIMULATION_DURATION == -1: sleep_time = random.randint(SIMULATION_RAND_MIN, SIMULATION_RAND_MAX) else: sleep_time = SIMULATION_DURATION print Storyboard.SEPARATOR3 print "* INFO: contsrv: Simulate upload by sleeping %d s." % ( sleep_time) print Storyboard.SEPARATOR3 time.sleep(sleep_time) # Simulate the success or failure of the upload random_number = random.random() if random_number > 0.0: # In case of success, we need to set the activity id to some 'harmless' value activity_id = "N/A" response_content = RESPONSE_SUCCESS_ID_PREFIX + activity_id + RESPONSE_SUCCESS_ID_SUFFIX else: response_content = RESPONSE_ERROR elif action == query.Parameters.REMOVE_CONTENT: print "* INFO: contsrv: Start LMS content removal." # Check that range_id is not empty if not range_id: self.send_error(REQUEST_ERROR, "Invalid range id") return # Check that activity_id is not empty if not activity_id: self.send_error(REQUEST_ERROR, "Invalid LMS activity id") return # Use Moodle to really do the content removal if USE_MOODLE: try: # ./cylms.py --config-file config_example --remove-from-lms 1,ID config_arg = " --config-file {}".format(CYLMS_CONFIG) remove_arg = " --remove-from-lms {},{}".format( range_id, activity_id) command = "python -u " + CYLMS_PATH + "cylms.py" + config_arg + remove_arg if DO_DEBUG: print("* DEBUG: contsrv: command: " + command) return_value = os.system(command) exit_status = os.WEXITSTATUS(return_value) if exit_status == 0: response_content = RESPONSE_SUCCESS else: self.send_error(SERVER_ERROR, "LMS content removal issue") return except IOError: self.send_error(SERVER_ERROR, "LMS content removal I/O error") return # Don't use Moodle, just simulate the content removal else: # Simulate time needed to instantiate the cyber range if SIMULATION_DURATION == -1: sleep_time = random.randint(SIMULATION_RAND_MIN, SIMULATION_RAND_MAX) else: sleep_time = SIMULATION_DURATION print Storyboard.SEPARATOR3 print "* INFO: contsrv: Simulate removal by sleeping %d s." % ( sleep_time) print Storyboard.SEPARATOR3 time.sleep(sleep_time) # Simulate the success or failure of the upload random_number = random.random() if random_number > 0.0: response_content = RESPONSE_SUCCESS else: response_content = RESPONSE_ERROR # Catch potential unimplemented actions (if any) else: print "* WARNING: contsrv: Unknown action: %s." % (action) # Send response header to requester (triggers log_message()) self.send_response(SUCCESS_CODE) self.send_header("Content-type", "text/html") self.end_headers() # Send scenario database content information to requester self.wfile.write(response_content) # Output server reply print "* INFO: contsrv: Server response content: %s" % ( response_content)
def do_POST(self): # Get the parameters of the POST request params = query.Parameters(self) print "" print Storyboard.SEPARATOR2 if Storyboard.ENABLE_PASSWORD: print( "* INFO: trngsrv: Request POST parameters: [not shown because password use is enabled]" ) else: print( "* INFO: trngsrv: Request POST parameters: {}".format(params)) # Get the values of the parameters for given keys user_id = params.get(query.Parameters.USER) password = params.get(query.Parameters.PASSWORD) action = params.get(query.Parameters.ACTION) language = params.get(query.Parameters.LANG) instance_count = params.get(query.Parameters.COUNT) ttype = params.get(query.Parameters.TYPE) scenario = params.get(query.Parameters.SCENARIO) level = params.get(query.Parameters.LEVEL) range_id = params.get(query.Parameters.RANGE_ID) if DEBUG: print Storyboard.SEPARATOR1 print "POST PARAMETERS:" print Storyboard.SEPARATOR1 print "USER: %s" % (user_id) if password: print "PASSWORD: ******" print "ACTION: %s" % (action) print "LANGUAGE: %s" % (language) print "COUNT: %s" % (instance_count) print "TYPE: %s" % (ttype) print "SCENARIO: %s" % (scenario) print "LEVEL: %s" % (level) print "RANGE_ID: %s" % (range_id) print Storyboard.SEPARATOR1 ## Verify user information # Get user information from YAML file # Note: Only reading data that is (potentially) modified externally => # no need for synchronization user_info = userinfo.UserInfo() if not user_info.parse_YAML_file(DATABASE_DIR + USERS_FILE): self.respond_error(Storyboard.USER_SETTINGS_LOADING_ERROR) return if DEBUG: user_info.pretty_print() # Check that user id is valid if not user_id: self.respond_error(Storyboard.USER_ID_MISSING_ERROR) return user_obj = user_info.get_user(user_id) if not user_obj: self.respond_error(Storyboard.USER_ID_INVALID_ERROR) return # Check password (if enabled) if Storyboard.ENABLE_PASSWORD: # Check whether password exists in database for current user if not user_obj.password: self.respond_error( Storyboard.USER_PASSWORD_NOT_IN_DATABASE_ERROR) return # If a password was provided, verify that it matches the encrypted one from the database if password: if not Password.verify(password, user_obj.password): self.respond_error( Storyboard.USER_ID_PASSWORD_INVALID_ERROR) return else: self.respond_error(Storyboard.USER_PASSWORD_MISSING_ERROR) return ## Verify action information # Check that action is valid if not action: self.respond_error(Storyboard.ACTION_MISSING_ERROR) return if action not in self.VALID_ACTIONS: self.respond_error(Storyboard.ACTION_INVALID_ERROR) return # Create training database content information object training_info = trnginfo.TrainingInfo() # Check that language is valid if not language: self.respond_error(Storyboard.LANGUAGE_MISSING_ERROR) return if language not in self.VALID_LANGUAGES: self.respond_error(Storyboard.LANGUAGE_INVALID_ERROR) return # Select the scenario information file based on the # requested language if language == query.Parameters.JA: training_settings_file = DATABASE_DIR + SCENARIOS_FILE_JA else: training_settings_file = DATABASE_DIR + SCENARIOS_FILE_EN if DEBUG: print "* DEBUG: trngsrv: Read training settings from '%s'..." % ( training_settings_file) # Note: Only reading data that is (potentially) modified externally => # no need for synchronization result = training_info.parse_YAML_file(training_settings_file) if not result: self.respond_error(Storyboard.TRAINING_SETTINGS_LOADING_ERROR) return if DEBUG: training_info.pretty_print() # If we reached this point, it means processing was successful # => act according to each action #################################################################### # Fetch content action # Note: Only reading data that is (potentially) modified externally => # no need for synchronization if action == query.Parameters.FETCH_CONTENT: # Convert the training info to the external JSON # representation that will be provided to the client response_data = training_info.get_JSON_representation() #################################################################### # Create training action # Note: Requires synchronized access to active sessions list elif action == query.Parameters.CREATE_TRAINING: if not instance_count: self.respond_error(Storyboard.INSTANCE_COUNT_MISSING_ERROR) return try: instance_count_value = int(instance_count) except ValueError as error: self.respond_error(Storyboard.INSTANCE_COUNT_INVALID_ERROR) return if not ttype: self.respond_error(Storyboard.TRAINING_TYPE_MISSING_ERROR) return if not scenario: self.respond_error(Storyboard.SCENARIO_NAME_MISSING_ERROR) return if not level: self.respond_error(Storyboard.LEVEL_NAME_MISSING_ERROR) return # Synchronize access to active sessions list and related variables self.lock_active_sessions.acquire() try: cyber_range_id = self.generate_cyber_range_id( self.pending_sessions) if cyber_range_id: # Convert to string for internal representation cyber_range_id = str(cyber_range_id) self.pending_sessions.append(cyber_range_id) print "* INFO: trngsrv: Allocated session with ID #%s." % ( cyber_range_id) else: self.respond_error(Storyboard.SESSION_ALLOCATION_ERROR) return finally: self.lock_active_sessions.release() ######################################## # Handle content upload content_file_name = training_info.get_content_name(scenario, level) if content_file_name == None: self.removePendingSession(cyber_range_id) self.respond_error(Storyboard.CONTENT_IDENTIFICATION_ERROR) return content_file_name = DATABASE_DIR + content_file_name if DEBUG: print "* DEBUG: trngsrv: Training content file: %s" % ( content_file_name) # Open the content file try: content_file = open(content_file_name, "r") content_file_content = content_file.read() content_file.close() except IOError as error: print "* ERROR: trngsrv: File error: %s." % (error) self.removePendingSession(cyber_range_id) self.respond_error(Storyboard.CONTENT_LOADING_ERROR) return try: # Note: creating a dictionary as below does not # preserve the order of the parameters, but this has # no negative influence in our implementation query_tuples = { query.Parameters.USER: user_id, query.Parameters.ACTION: query.Parameters.UPLOAD_CONTENT, query.Parameters.DESCRIPTION_FILE: content_file_content, query.Parameters.RANGE_ID: cyber_range_id } query_params = urllib.urlencode(query_tuples) print "* INFO: trngsrv: Send upload request to content server %s." % ( CONTENT_SERVER_URL) if DEBUG: print "* DEBUG: trngsrv: POST parameters: %s" % ( query_params) data_stream = urllib.urlopen(CONTENT_SERVER_URL, query_params) data = data_stream.read() if DEBUG: print "* DEBUG: trngsrv: Content server response body: %s" % ( data) (status, activity_id) = query.Response.parse_server_response(data) if DEBUG: print( "* DEBUG: trngsrv: Response status: {}".format(status)) print("* DEBUG: trngsrv: Response activity_id: {}".format( activity_id)) if status == Storyboard.SERVER_STATUS_SUCCESS: # Store activity id in session description pass else: print "* ERROR: trngsrv: Content upload error." self.removePendingSession(cyber_range_id) self.respond_error(Storyboard.CONTENT_UPLOAD_ERROR) return # Save the response data contsrv_response = data except IOError as error: print "* ERROR: trngsrv: URL error: %s." % (error) self.removePendingSession(cyber_range_id) self.respond_error(Storyboard.CONTENT_SERVER_ERROR) return ######################################## # Handle instantiation spec_file_name = training_info.get_specification_name( scenario, level) if spec_file_name == None: self.removePendingSession(cyber_range_id) self.respond_error(Storyboard.TEMPLATE_IDENTIFICATION_ERROR) return spec_file_name = DATABASE_DIR + spec_file_name if DEBUG: print "* DEBUG: trngsrv: Scenario specification file: %s" % ( spec_file_name) # Open the specification file (template) try: spec_file = open(spec_file_name, "r") spec_file_content = spec_file.read() spec_file.close() except IOError as error: print "* ERROR: trngsrv: File error: %s." % (error) self.removePendingSession(cyber_range_id) self.respond_error(Storyboard.TEMPLATE_LOADING_ERROR) return # Do instantiation try: # Replace variables in the specification file spec_file_content = user_obj.replace_variables( spec_file_content, cyber_range_id, instance_count_value) # Note: creating a dictionary as below does not # preserve the order of the parameters, but this has # no negative influence in our implementation query_tuples = { query.Parameters.USER: user_id, query.Parameters.ACTION: query.Parameters.INSTANTIATE_RANGE, query.Parameters.DESCRIPTION_FILE: spec_file_content, query.Parameters.RANGE_ID: cyber_range_id } query_params = urllib.urlencode(query_tuples) print "* INFO: trngsrv: Send instantiate request to instantiation server %s." % ( INSTANTIATION_SERVER_URL) if DEBUG: print "* DEBUG: trngsrv: POST parameters: %s" % ( query_params) data_stream = urllib.urlopen(INSTANTIATION_SERVER_URL, query_params) data = data_stream.read() if DEBUG: print "* DEBUG: trngsrv: Instantiation server response body: %s" % ( data) # Remove pending session self.removePendingSession(cyber_range_id) (status, message) = query.Response.parse_server_response(data) if DEBUG: print "* DEBUG: trngsrv: Response status:", status print "* DEBUG: trngsrv: Response message:", message if status == Storyboard.SERVER_STATUS_SUCCESS: session_name = "Training Session #%s" % (cyber_range_id) crt_time = time.asctime() print "* INFO: trngsrv: Instantiation successful => save training session: %s (time: %s)." % ( session_name, crt_time) # Synchronize access to active sessions list self.lock_active_sessions.acquire() try: # Read session info session_info = sessinfo.SessionInfo() session_info.parse_YAML_file(ACTIVE_SESSIONS_FILE) # Add new session and save to file # Scenarios and levels should be given as arrays, # so we convert values to arrays when passing arguments session_info.add_session(session_name, cyber_range_id, user_id, crt_time, ttype, [scenario], [level], language, instance_count, activity_id) session_info.write_YAML_file(ACTIVE_SESSIONS_FILE) finally: self.lock_active_sessions.release() else: print "* ERROR: trngsrv: Range instantiation error." self.respond_error(Storyboard.INSTANTIATION_ERROR) return # Save the response data instsrv_response = data if DEBUG: print "* DEBUG: trngsrv: contsrv response: %s" % ( contsrv_response) print "* DEBUG: trngsrv: instsrv response: %s" % ( instsrv_response) # Prepare the response as a message # TODO: Should create a function to handle this if message: response_data = '[{{"{0}": "{1}"}}]'.format( Storyboard.SERVER_MESSAGE_KEY, message) else: response_data = None except IOError as error: print "* ERROR: trngsrv: URL error: %s." % (error) self.removePendingSession(cyber_range_id) self.respond_error(Storyboard.INSTANTIATION_SERVER_ERROR) return #################################################################### # Retrieve saved training configurations action # Note: Requires synchronized access to saved configurations list elif action == query.Parameters.GET_CONFIGURATIONS: self.lock_saved_configurations.acquire() try: # Read session info session_info = sessinfo.SessionInfo() session_info.parse_YAML_file(SAVED_CONFIGURATIONS_FILE) # TODO: Catch error in operation above finally: self.lock_saved_configurations.release() # Convert the training session info to the external JSON # representation that will be provided to the client response_data = session_info.get_JSON_representation(user_id) #################################################################### # Retrieve active training sessions action # Note: Requires synchronized access to active sessions list elif action == query.Parameters.GET_SESSIONS: self.lock_active_sessions.acquire() try: # Read session info session_info = sessinfo.SessionInfo() session_info.parse_YAML_file(ACTIVE_SESSIONS_FILE) # TODO: Catch error in operation above finally: self.lock_active_sessions.release() # Convert the training session info to the external JSON # representation that will be provided to the client response_data = session_info.get_JSON_representation(user_id) #################################################################### # End training action # Note: Requires synchronized access to active sessions list elif action == query.Parameters.END_TRAINING: if not range_id: print "* ERROR: trngsrv: %s." % ( Storyboard.SESSION_ID_MISSING_ERROR) self.respond_error(Storyboard.SESSION_ID_MISSING_ERROR) return activity_id = None self.lock_active_sessions.acquire() try: activity_id = self.check_range_id_exists(range_id, user_id) if not activity_id: print "* ERROR: trngsrv: Session with ID " + range_id + " doesn't exist for user " + user_id error_msg = Storyboard.SESSION_ID_INVALID_ERROR + ": " + range_id self.respond_error(error_msg) return finally: self.lock_active_sessions.release() ######################################## # Handle content removal try: # Note: Creating a dictionary as below does not # preserve the order of the parameters, but this has # no negative influence in our implementation query_tuples = { query.Parameters.USER: user_id, query.Parameters.ACTION: query.Parameters.REMOVE_CONTENT, query.Parameters.RANGE_ID: range_id, query.Parameters.ACTIVITY_ID: activity_id } query_params = urllib.urlencode(query_tuples) print "* INFO: trngsrv: Send removal request to content server %s." % ( CONTENT_SERVER_URL) print "* INFO: trngsrv: POST parameters: %s" % (query_params) data_stream = urllib.urlopen(CONTENT_SERVER_URL, query_params) data = data_stream.read() if DEBUG: print "* DEBUG: trngsrv: Content server response body: %s" % ( data) # We don't parse the response since the content server does not provide # a uniformly formatted one; instead we only check for SUCCESS key if Storyboard.SERVER_STATUS_SUCCESS in data: # Nothing to do on success as there is no additional info provided pass else: print "* ERROR: trngsrv: Content removal error." self.respond_error(Storyboard.CONTENT_REMOVAL_ERROR) return # Save the response data contsrv_response = data except IOError as error: print "* ERROR: trngsrv: URL error: %s." % (error) self.respond_error(Storyboard.CONTENT_SERVER_ERROR) return ######################################## # Handle range destruction try: # Note: creating a dictionary as below does not # preserve the order of the parameters, but this has # no negative influence in our implementation query_tuples = { query.Parameters.USER: user_id, query.Parameters.ACTION: query.Parameters.DESTROY_RANGE, query.Parameters.RANGE_ID: range_id } query_params = urllib.urlencode(query_tuples) print "* INFO: trngsrv: Send destroy request to instantiation server %s." % ( INSTANTIATION_SERVER_URL) print "* INFO: trngsrv: POST parameters: %s" % (query_params) data_stream = urllib.urlopen(INSTANTIATION_SERVER_URL, query_params) data = data_stream.read() if DEBUG: print "* DEBUG: trngsrv: Instantiation server response body: %s" % ( data) (status, message) = query.Response.parse_server_response(data) if status == Storyboard.SERVER_STATUS_SUCCESS: self.lock_active_sessions.acquire() try: # Read session info session_info = sessinfo.SessionInfo() session_info.parse_YAML_file(ACTIVE_SESSIONS_FILE) # Remove session and save to file if not session_info.remove_session(range_id, user_id): print "* ERROR: Cannot remove training session %s." % ( range_id) self.respond_error( Storyboard.SESSION_INFO_CONSISTENCY_ERROR) return else: session_info.write_YAML_file(ACTIVE_SESSIONS_FILE) finally: self.lock_active_sessions.release() else: print "* ERROR: trngsrv: Range destruction error: %s." % ( message) self.respond_error(Storyboard.DESTRUCTION_ERROR) return instsrv_response = data if DEBUG: print "* DEBUG: trngsrv: contsrv response: %s" % ( contsrv_response) print "* DEBUG: trngsrv: instsrv response: %s" % ( instsrv_response) # Prepare the response: no data needs to be returned, # hence we set the content to None response_data = None except IOError as error: print "* ERROR: trngsrv: URL error: %s." % (error) self.respond_error(Storyboard.INSTANTIATION_SERVER_ERROR) return #################################################################### # Catch unknown actions else: print "* WARNING: trngsrv: Unknown action: %s." % (action) # Emulate communication delay if EMULATE_DELAY: sleep_time = random.randint(2, 5) print "* INFO: trngsrv: Simulate communication by sleeping %d s." % ( sleep_time) time.sleep(sleep_time) # Respond to requester with SUCCESS self.respond_success(response_data)