def __init__(self, config_file): # Initialize configuration manager self.cfg_manager = cfg_mgmt.CfgManager(config_file) # Initialize internal variables self.lms_host = self.cfg_manager.get_setting(Storyboard.CONFIG_LMS_HOST) self.lms_repository = self.cfg_manager.get_setting(Storyboard.CONFIG_LMS_REPOSITORY) self.course_name = self.cfg_manager.get_setting(Storyboard.CONFIG_COURSE_NAME) self.section_id = self.cfg_manager.get_setting(Storyboard.CONFIG_SECTION_ID) # Display debug info logging.debug("LMS manager settings:") logging.debug(" - LMS host: {}".format(self.lms_host)) logging.debug(" - LMS repository: {}".format(self.lms_repository)) logging.debug(" - Course name: {}".format(self.course_name)) logging.debug(" - Section id: {}".format(self.section_id))
def __init__(self, config_file): # Initialize configuration manager self.cfg_manager = cfg_mgmt.CfgManager(config_file) # Initialize internal variables self.lms_host = self.cfg_manager.get_setting( Storyboard.CONFIG_LMS_HOST) if not self.lms_host: logging.error( "Setting not defined in config file: {} => abort".format( Storyboard.CONFIG_LMS_HOST)) sys.exit(1) self.range_dir = self.cfg_manager.get_setting( Storyboard.CONFIG_RANGE_DIRECTORY) if not self.range_dir: logging.error( "Setting not defined in config file: {} => abort".format( Storyboard.CONFIG_RANGE_DIRECTORY)) sys.exit(1)
def main(argv): # Configure logging logging.basicConfig(level=logging.INFO, format='* %(levelname)s: %(filename)s: %(message)s') # Program parameters and their default values yaml_file = None scorm_file = None config_file = None session_id = None activity_id = None # Program actions convert_action = False add_to_lms_action = False remove_from_lms_action = False # Get program directory dir_path = os.path.dirname(os.path.realpath(__file__)) # Print banner (read version from CHANGES file) version_string = 'CyLMS' changes_filename = str(dir_path) + "/CHANGES" try: changes_file = open(changes_filename) for line in changes_file: if re.match("CyLMS v", line): version_string = line.rstrip() break except IOError: # In case file cannot be opened/read, we use the default value pass print( "#########################################################################" ) print("{}: Cybersecurity Training Support for LMS".format(version_string)) print( "#########################################################################" ) # Parse command line arguments try: # Make sure to add ':' for short-form and '=' for long-form options that require an argument opts, args = getopt.getopt(argv, "hc:f:a:r:", [ "help", "convert-content=", "config-file=", "add-to-lms=", "remove-from-lms=" ]) except getopt.GetoptError as err: logging.error("Command-line argument error: {}".format(str(err))) usage() sys.exit(1) for opt, arg in opts: if opt in ("-h", "--help"): usage() sys.exit() elif opt in ("-c", "--convert-content"): yaml_file = os.path.abspath(arg) convert_action = True elif opt in ("-f", "--config-file"): config_file = os.path.abspath(arg) cfg_manager = cfg_mgmt.CfgManager(config_file) elif opt in ("-a", "--add-to-lms"): session_id = arg add_to_lms_action = True elif opt in ("-r", "--remove-from-lms"): id_list = arg.split(",") # Check that split resulted in exactly 2 non-empty strings if id_list and len(id_list) == 2 and id_list[0] and id_list[1]: session_id = id_list[0] activity_id = id_list[1] remove_from_lms_action = True else: logging.error("Operation remove-from-lms requires a comma-separated argument (e.g., '1,10'),\n\t"\ "but a different format was encountered: '{}'".format(arg)) usage() sys.exit(1) else: # Nothing to be done on else, since unrecognized options are caught by # the getopt.GetoptError exception above pass # Check that at least one action is enabled if not (convert_action or add_to_lms_action or remove_from_lms_action): logging.error("No action argument was provided => abort execution.") usage() sys.exit(1) # Proceed with the convert-content action if convert_action: logging.info( "Convert training content file '{}' to SCORM package.".format( yaml_file)) # Build name of SCORM package file scorm_file = yaml_file + ".zip" # Convert content to SCORM package success_status = cnt2lms.yaml2scorm(yaml_file, scorm_file, dir_path) # Check whether the conversion was successful if success_status: logging.debug( "Converted training content file '{}' successfully.".format( yaml_file)) else: logging.error( "Failed to convert training content file '{}'.".format( yaml_file)) sys.exit(1) # Proceed with the add-to-lms action if add_to_lms_action: logging.info( "Add converted SCORM package '{}' to LMS.".format(scorm_file)) # Check whether the SCORM package name is defined if scorm_file: # Check whether the configuration manager is defined if cfg_manager: # Check whether the session id (number) is defined if session_id: # Create LMS manager object lms_manager = lms_mgmt.LmsManager(config_file) # Build target package name and copy local SCORM package to repository target_file = LMS_PACKAGE_FILE_FORMAT.format(session_id) success_status = lms_manager.copy_package( scorm_file, target_file) # Check whether copy package operation was successful if success_status: # If copy was successful, we add a corresponding activity to LMS activity_name = ACTIVITY_NAME_FORMAT.format(session_id) activity_id = lms_manager.add_activity( activity_name, target_file) # Check whether adding the activity was successful if activity_id: # If the value of the activity is needed, it should be parsed # from the program output by the caller logging.info( "Added converted SCORM package '{}' to LMS successfully => activity_id={}" .format(scorm_file, activity_id)) sys.exit() else: logging.error( "Failed to add converted SCORM package '{}' to LMS." .format(scorm_file)) sys.exit(1) else: logging.error( "SCORM package copy to LMS repository failed => abort execution." ) sys.exit(1) else: logging.error( "Session id is undefined => abort execution.") usage() sys.exit(1) else: logging.error( "Configuration manager is undefined => abort execution.") usage() sys.exit(1) else: logging.error( "SCORM package file name is undefined => abort execution.\n\t (Note that the 'add-to-lms' action can only be used together with 'convert-content')" ) usage() sys.exit(1) # Proceed with the remove-from-lms action if remove_from_lms_action: logging.info( "Remove session #{} (activity with id '{}') from LMS.".format( session_id, activity_id)) # Create LMS manager object lms_manager = lms_mgmt.LmsManager(config_file) # Delete activity with given id package_file = LMS_PACKAGE_FILE_FORMAT.format(session_id) success_status = lms_manager.delete_activity(activity_id, package_file) # Check whether the deletion was successful if success_status: # TODO: Add code to also delete the actual package file from LMS repository?@ logging.debug( "Removed activity with id '{}' from LMS successfully.".format( activity_id)) else: logging.error( "Failed to remove activity with id '{}' from LMS.".format( activity_id)) sys.exit(1)
def main(args): # Configure logging logging.basicConfig(level=logging.INFO, format='* %(levelname)s: %(filename)s: %(message)s') # Program parameters and their default values yaml_file = None config_file = None session_id = None activity_id = None # Program actions convert_action = False add_to_lms_action = False remove_from_lms_action = False vnc_setup_action = False # Get program directory dir_path = os.path.dirname(os.path.realpath(__file__)) # Print banner (read version from CHANGES file) version_string = 'CyLMS' changes_filename = str(dir_path) + "/CHANGES" try: changes_file = open(changes_filename) for line in changes_file: if re.match("CyLMS v", line): version_string = line.rstrip() break except IOError: # In case file cannot be opened/read, we use the default value pass print( "#########################################################################" ) print("{}: Cybersecurity Training Support for LMS".format(version_string)) print( "#########################################################################" ) # Parse command line arguments try: # Make sure to add ':' for short-form and '=' for long-form options that require an argument opts, trailing_args = getopt.getopt(args, "hc:f:a:r:v:", [ "help", "convert-content=", "config-file=", "add-to-lms=", "remove-from-lms=", "vnc-setup=" ]) except getopt.GetoptError as err: logging.error("Command-line argument error: {}".format(str(err))) usage() sys.exit(1) for opt, arg in opts: if opt in ("-h", "--help"): usage() sys.exit() elif opt in ("-c", "--convert-content"): yaml_file = os.path.abspath(arg) convert_action = True elif opt in ("-f", "--config-file"): config_file = os.path.abspath(arg) elif opt in ("-a", "--add-to-lms"): session_id = arg add_to_lms_action = True elif opt in ("-r", "--remove-from-lms"): id_list = arg.split(",") # Check that split resulted in exactly 2 non-empty strings if id_list and len(id_list) == 2 and id_list[0] and id_list[1]: session_id = id_list[0] activity_id = id_list[1] remove_from_lms_action = True else: logging.error("Action 'remove-from-lms' requires a comma-separated argument (e.g., '1,10'),\n\t"\ "but a different format was encountered: '{}'".format(arg)) usage() sys.exit(1) elif opt in ("-v", "--vnc-setup"): session_id = arg vnc_setup_action = True else: # Nothing to be done on else, since unrecognized options are caught by # the getopt.GetoptError exception above pass if trailing_args: logging.error( "Unrecognized trailing arguments {} => abort execution.".format( trailing_args)) usage() sys.exit(1) # Initialize additional variables if config_file: cfg_manager = cfg_mgmt.CfgManager(config_file) else: cfg_manager = None scorm_file = None # Check that at least one action is enabled if not (convert_action or add_to_lms_action or remove_from_lms_action or vnc_setup_action): logging.error("No action argument was provided => abort execution.") usage() sys.exit(1) # Check that incompatible actions are not used together if convert_action and remove_from_lms_action: logging.error( "The actions 'convert-content' and 'remove-from-lms' are not compatible => abort execution." ) usage() sys.exit(1) # Set show range flag from configuration file or default value if cfg_manager: if cfg_manager.get_setting(Storyboard.CONFIG_ENABLE_VNC): enable_vnc = True else: enable_vnc = False logging.debug( "Use configured value of enable_vnc flag: {}".format(enable_vnc)) else: logging.debug("Use default value of enable_vnc flag: {}".format( ENABLE_VNC_DEFAULT)) enable_vnc = ENABLE_VNC_DEFAULT # Proceed with the convert-content action if convert_action: logging.info( "Convert training content file '{}' to SCORM package.".format( yaml_file)) # Build base name of SCORM package file scorm_file_base = yaml_file # Convert content to SCORM package if not session_id: session_id = SESSION_ID_DEFAULT scorm_file, training_title = cnt2lms.yaml2scorm( yaml_file, scorm_file_base, dir_path, enable_vnc, session_id, config_file) # Check whether the conversion was successful if scorm_file: logging.debug( "Converted training content file '{}' successfully.".format( yaml_file)) else: logging.error( "Failed to convert training content file '{}'.".format( yaml_file)) sys.exit(1) # Proceed with the add-to-lms action if add_to_lms_action: # Check whether the SCORM package name is defined if scorm_file: logging.info( "Add converted SCORM package '{}' to LMS.".format(scorm_file)) # Check whether the configuration manager is defined if cfg_manager: # Check whether the session id (number) is defined if session_id: # Create LMS manager object lms_manager = lms_mgmt.LmsManager(config_file) # Build target package name and copy local SCORM package to repository target_file = LMS_PACKAGE_FILE_FORMAT.format(session_id) success_status = lms_manager.copy_package( scorm_file, target_file) # Check whether copy package operation was successful if success_status: # If copy was successful, we add a corresponding activity to LMS activity_name = ACTIVITY_NAME_FORMAT.format( session_id, training_title.encode('utf-8')) activity_description = ACTIVITY_DESCRIPTION_FORMAT.format( time.strftime("%Y-%m-%d %H:%M:%S")) activity_id = lms_manager.add_activity( activity_name, activity_description, target_file) # Check whether adding the activity was successful if activity_id: # If the value of the activity is needed, it should be parsed # from the program output by the caller logging.info( "Added converted SCORM package '{}' to LMS successfully => activity_id={}" .format(scorm_file, activity_id)) # Don't exit to allow chaining with the vnc-setup command else: logging.error( "Failed to add converted SCORM package '{}' to LMS." .format(scorm_file)) sys.exit(1) else: logging.error( "SCORM package copy to LMS repository failed => abort execution." ) sys.exit(1) else: logging.error( "Session id is undefined => abort execution.") usage() sys.exit(1) else: # Check whether we have an internal error, or the config file was not provided if config_file: logging.error( "Internal error: Configuration manager is undefined => abort execution." ) else: logging.error( "Configuration file name is undefined => abort execution.\n\t (Note that the 'add-to-lms' action requires the 'config-file' option.)" ) usage() sys.exit(1) else: logging.error( "SCORM package file name is undefined => abort execution.\n\t (Note that the 'add-to-lms' action can only be used together with 'convert-content'.)" ) usage() sys.exit(1) # Proceed with the remove-from-lms action if remove_from_lms_action: if session_id and activity_id: logging.info( "Remove session #{} (activity with id '{}') from LMS.".format( session_id, activity_id)) if config_file: # Create LMS manager object lms_manager = lms_mgmt.LmsManager(config_file) if lms_manager: # Delete activity with given id package_file = LMS_PACKAGE_FILE_FORMAT.format(session_id) success_status = lms_manager.delete_activity( activity_id, package_file) # Check whether the deletion was successful if success_status: # TODO: Add code to also delete the actual package file from LMS repository? logging.debug( "Removed activity with id '{}' from LMS successfully." .format(activity_id)) else: logging.error( "Failed to remove activity with id '{}' from LMS.". format(activity_id)) sys.exit( ) # Not fatal error anymore, should it be? sys.exit(1) else: logging.error( "Internal error: LMS manager is undefined => abort execution." ) sys.exit(1) # If showing the access range button is not enabled return, # otherwise stop the noVNC servers if not enable_vnc: return # Create a VNC manager object vnc_manager = vnc_mgmt.VncManager(config_file) if vnc_manager: vnc_ports = vnc_manager.get_range_info(session_id) if vnc_ports: if vnc_manager.stop_novnc_servers( session_id, vnc_ports): logging.debug( "Stopped VNC servers for session #{} successfully." .format(session_id)) sys.exit( ) # It is OK to exit here as no command chaining is possible else: logging.error( "Failed to stop VNC servers => abort VNC server stopping" ) sys.exit( ) # Not fatal error anymore, should it be? sys.exit(1) else: logging.error( "Failed to get cyber range info => abort VNC server stopping" ) sys.exit(1) else: logging.error( "Internal error: VNC manager is undefined => abort execution." ) sys.exit(1) else: logging.error( "Configuration file name is undefined => abort execution.\n\t (Note that the 'remove-from-lms' action requires the 'config-file' option.)" ) usage() sys.exit(1) # Proceed with the vnc-setup action if vnc_setup_action: if session_id: logging.info( "Set up VNC server to access the cyber range for session #{}.". format(session_id)) if config_file: # If showing the access range button is not enabled return, # otherwise stop the noVNC servers if not enable_vnc: logging.info( "VNC setup not enabled in the config file => ignore command" ) return # Create a VNC manager object vnc_manager = vnc_mgmt.VncManager(config_file) if vnc_manager: vnc_ports = vnc_manager.get_range_info(session_id) if vnc_ports: if vnc_manager.create_access_file( session_id, vnc_ports): if vnc_manager.start_novnc_servers(vnc_ports): logging.debug( "Started VNC servers for session #{} successfully." .format(session_id)) sys.exit( ) # It is OK to exit here as no command chaining is possible else: logging.error( "Failed to start VNC servers => abort VNC setup" ) sys.exit(1) else: logging.error( "Failed to create range access file => abort VNC setup" ) sys.exit(1) else: logging.error( "Failed to get cyber range info => abort VNC server stopping" ) sys.exit(1) else: logging.error( "Internal error: VNC manager is undefined => abort execution." ) sys.exit(1) else: logging.error( "Configuration file name is undefined => abort execution.\n\t (Note that the 'remove-from-lms' action requires the 'config-file' option.)" ) usage() sys.exit(1)