Exemple #1
0
def unit_signup(unit_id):
    unit = ds_client.get(ds_client.key('cybergym-unit', unit_id))
    if request.method == 'POST':
        workout_query = ds_client.query(kind='cybergym-workout')
        workout_query.add_filter('unit_id', '=', unit_id)
        claimed_workout = None
        for workout in list(workout_query.fetch()):
            if 'student_name' in workout:
                if workout['student_name'] == None or workout[
                        'student_name'] == "":
                    with ds_client.transaction():
                        claimed_workout = workout
                        claimed_workout['student_name'] = request.form[
                            'student_name']
                        ds_client.put(claimed_workout)
                        if unit['build_type'] == 'arena':
                            return redirect('/student/arena_landing/%s' %
                                            claimed_workout.key.name)
                        else:
                            return redirect('/student/landing/%s' %
                                            claimed_workout.key.name)
            else:
                with ds_client.transaction():
                    claimed_workout = workout
                    claimed_workout['student_name'] = request.form[
                        'student_name']
                    ds_client.put(claimed_workout)
                    if unit['build_type'] == 'arena':
                        return redirect('/student/arena_landing/%s' %
                                        claimed_workout.key.name)
                    else:
                        return redirect('/student/landing/%s' %
                                        claimed_workout.key.name)
        return render_template('unit_signup.html', unit_full=True)
    return render_template('unit_signup.html')
Exemple #2
0
def registered_student_home():
    if 'user_email' in session and 'user_groups' in session:
        student_email = session['user_email']
        if ArenaAuthorizer.UserGroups.STUDENTS not in session['user_groups']:
            return render_template('403.html')

        workout_list = ds_client.query(kind='cybergym-workout')
        workout_list.add_filter("student_email", "=", str(student_email))
        student_workouts = []
        for workout in list(workout_list.fetch()):
            if workout['state'] != BUILD_STATES.DELETED:
                workout_info = {
                    'workout_id': workout.key.name,
                    'workout_name': workout['type'],
                    'timestamp': workout['timestamp']
                }
                student_workouts.append(workout_info)
        student_workouts = sorted(student_workouts,
                                  key=lambda i: (i['timestamp']),
                                  reverse=True)

        student_info = {'workouts': student_workouts}
        return render_template('student_home.html',
                               auth_config=auth_config,
                               student_info=student_info)
    else:
        return render_template('login.html', auth_config=auth_config)
def get_unit_arenas(unit_id):
    unit_workouts = ds_client.query(kind='cybergym-workout')
    unit_workouts.add_filter("unit_id", "=", unit_id)
    workout_list = []
    for workout in list(unit_workouts.fetch()):
        student_name = None
        if 'student_name' in workout:
            student_name = workout['student_name']
        workout_instance = workout = ds_client.get(workout.key)
        state = None
        if 'state' in workout_instance:
            state = workout_instance['state']

        team = None
        if 'team' in workout_instance:
            team = workout_instance['team']
        workout_info = {
            'name': workout.key.name,
            'student_name': student_name,
            'state': state,
            'teacher_email': workout['instructor_id'],
            'team': team
        }
        workout_list.append(workout_info)
    return workout_list
    def build_arena(self):
        """

        @return: Status
        @rtype: bool
        """
        self._get_build_allocation()
        if not self.allocation:
            # If there are not enough resource, delete the unit and workouts from the datastore before returning.
            ds_client.delete(self.unit_id)
            for workout in self.workouts:
                ds_client.delete(workout.key)
                servers = ds_client.query(kind='cybergym-server').add_filter(
                    'workout', '=', workout.key.name).fetch()
                for server in servers:
                    ds_client.delete(server)
            servers = ds_client.query(kind='cybergym-server').add_filter(
                'workout', '=', workout.key.name).fetch()
            for server in servers:
                ds_client.delete(server)
            return False
        _projects = list(self.allocation.keys())
        i = 0
        j = 0
        publisher = topic_path = current_project = None
        for workout in self.workouts:
            if i == 0:
                current_project = _projects[j]
                j += 1

                publisher = pubsub_v1.PublisherClient()
                topic_path = publisher.topic_path(
                    current_project, workout_globals.ps_build_workout_topic)
            workout['build_project_location'] = current_project
            ds_client.put(workout)
            publisher.publish(topic_path,
                              data=b'Cyber Gym Workout',
                              workout_id=workout.key.name)

            i += 1
            # If the index is greater than the current project's build allocation, reset the index, which triggers
            # a project change on the next iteration.
            if i >= self.allocation[current_project]:
                i = 0
        return True
 def __init__(self, unit_id):
     self.allocation = {}
     self.unit_id = unit_id
     self.unit = ds_client.get(ds_client.key('cybergym-unit', unit_id))
     unit_workouts = ds_client.query(kind='cybergym-workout')
     unit_workouts.add_filter("unit_id", "=", unit_id)
     self.workouts = list(unit_workouts.fetch())
     self.workout_count = len(self.workouts)
     self.remaining_count = self.workout_count
 def _process_class_roster(self):
     class_list = ds_client.query(kind='cybergym-class')
     class_list.add_filter('teacher_email', '=', self.email)
     class_list.add_filter('class_name', '=', self.class_name)
     self.class_record = list(class_list.fetch())[0]
     for student_name in self.class_record['roster']:
         if self.class_record['student_auth'] == 'email':
             self.student_emails.append(student_name['student_email'])
         self.student_names.append(student_name)
def admin_workout(workout_id):
    workout = ds_client.get(ds_client.key('cybergym-workout', workout_id))
    if workout:
        workout_server_query = ds_client.query(kind='cybergym-server')
        workout_server_query.add_filter('workout', '=', workout_id)
        server_list = []
        for server in list(workout_server_query.fetch()):
            server_list.append(server)
        return render_template('admin_workout.html', workout=workout, servers=server_list)
    else:
        abort(404)
    def _get_build_allocation(self):
        """
        Identify the build allocation among the parent and all children based on available resources and identified
        quotas.
        @param workout_count: Number of workouts to build in this request
        @type workout_count: Integer
        @return: The parent/or children projects to use, which may split across multiple projects
        @rtype: Dict {'<parent>': 80, '<child1>': 300, '<child2>': 300}
        """
        # First, get all available projects.
        available = {project: self.MAX_BUILDS}
        admin_info = ds_client.get(
            ds_client.key(AdminInfoEntity.KIND, 'cybergym'))
        if AdminInfoEntity.Entities.CHILD_PROJECTS in admin_info:
            for child_project in admin_info[
                    AdminInfoEntity.Entities.CHILD_PROJECTS]:
                available[child_project] = self.MAX_BUILDS

        # Next find all active workouts for the application
        query_workouts = ds_client.query(kind='cybergym-workout')
        query_workouts.add_filter('active', '=', True)

        # Determine the available build left for each parent and children projects.
        for workout in list(query_workouts.fetch()):
            workout_project = workout.get('build_project_location', project)
            if workout_project not in available:
                cloud_log(
                    LogIDs.MAIN_APP,
                    f"Error in workout specification. The project {workout_project} is not "
                    f"a valid project for this application.", LOG_LEVELS.ERROR)
                return False
            available[workout_project] -= 1

        # Set the build allocation for this project based on availability.
        for _project in available:
            workouts_available = available[_project]
            if workouts_available > 0:
                _project_allocation = min(self.remaining_count,
                                          workouts_available)
                self.allocation[_project] = _project_allocation
                self.remaining_count -= _project_allocation

        if self.remaining_count > 0:
            cloud_log(
                LogIDs.MAIN_APP,
                "Error: Not enough available resources to complete the build!",
                LOG_LEVELS.ERROR)
            return False
        else:
            return True
Exemple #9
0
def get_unit_workouts(unit_id):
    unit_workouts = ds_client.query(kind='cybergym-workout')
    unit_workouts.add_filter("unit_id", "=", unit_id)
    workout_list = []
    for workout in list(unit_workouts.fetch()):

        workout_instance = workout = ds_client.get(workout.key)
        workout_info = {
            'name': workout.key.name,
            # 'running': workout_instance['running'],
            'complete': workout_instance['complete'],
        }
        workout_list.append(workout_info)

    return workout_list
def get_active_workouts():
    active_workout_query = ds_client.query(kind='cybergym-workout')

    workout_list = active_workout_query.fetch()
    active_workouts = []

    for workout in workout_list:
        if 'state' in workout:
            if workout['state'] != "DELETED" and workout[
                    'state'] != "COMPLETED_DELETING_SERVERS":
                if 'hourly_cost' in workout and 'runtime_counter' in workout:
                    if workout['hourly_cost'] and workout['runtime_counter']:
                        estimated_cost = (float(workout['hourly_cost']) / 3600
                                          ) * float(workout['runtime_counter'])
                        workout['estimated_cost'] = format(
                            estimated_cost, '.2f')
                workout['id'] = workout.key.name
                active_workouts.append(workout)
    return json.dumps(active_workouts)
Exemple #11
0
def get_teacher_info():
    if (request.method == "POST"):
        teacher_email = request.get_json()
        unit_list = ds_client.query(kind='cybergym-unit')
        unit_list.add_filter("instructor_id", "=",
                             str(teacher_email['teacher_email']))
        # unit_list.add_filter("resources_deleted", "=", False)
        teacher_units = []

        for unit in list(unit_list.fetch()):
            unit_info = {
                'unit_id': unit.key.name,
                'workout_name': unit['workout_name'],
                'build_type': unit['build_type'],
                'unit_name': unit['unit_name'],
                'timestamp': unit['timestamp']
            }
            teacher_units.append(unit_info)

        teacher_units = sorted(teacher_units,
                               key=lambda i: (i['timestamp']),
                               reverse=True)
    return json.dumps(teacher_units)
def get_unit_workouts(unit_id):
    unit_workouts = ds_client.query(kind='cybergym-workout')
    unit_workouts.add_filter("unit_id", "=", unit_id)
    workout_list = []
    for workout in list(unit_workouts.fetch()):
        student_name = None
        if 'student_name' in workout:
            student_name = workout['student_name']
        if not student_name:
            student_name = ""
        # workout_instance = workout = ds_client.get(workout.key)
        state = None
        if 'state' in workout:
            state = workout['state']

        submitted_answers = None
        if 'submitted_answers' in workout:
            submitted_answers = workout['submitted_answers']

        uploaded_files = None
        if 'uploaded_files' in workout:
            uploaded_files = workout['uploaded_files']

        assessment = workout.get('assessment', None)
        auto_answers = get_auto_assessment(workout)
        workout_info = {
            'name': workout.key.name,
            'student_name': student_name,
            'state': state,
            'submitted_answers': submitted_answers,
            'uploaded_files': uploaded_files,
            'auto_answers': auto_answers,
            'assessment': assessment
        }
        workout_list.append(workout_info)

    return workout_list
    def _submit_flag(self, submission):
        """
        Handles flag submission for Arena CTF challenges. Calculates score based on value provided in the Arena yaml, with a bonus awarded
        to the first team to submit. Also marks completion for all team members.
        @param: submission: Dict generated from form submission
        """
        points = 0
        response = {}
        answer_found = False
        flag_attempt = submission['answer']
        question_key = submission['question']
        point_value = 0
        unit = ds_client.get(
            ds_client.key('cybergym-unit', self.workout['unit_id']))

        for i in range(len(self.assessment_info['questions'])):
            if question_key == self.assessment_info['questions'][i].get(
                    'question') and flag_attempt == self.assessment_info[
                        'questions'][i].get('answer'):
                point_value = self.assessment_info['questions'][i].get(
                    'points')
                answer_found = True

        # if flag_attempt in valid_answers:
        if answer_found:
            answer_time = time.gmtime(time.time())
            time_string = str(answer_time[3]) + ":" + str(
                answer_time[4]) + ":" + str(answer_time[5])
            team_members = []
            if 'team' in self.workout:
                team_query = ds_client.query(kind='cybergym-workout')
                team_query.add_filter('team', '=', self.workout['team'])
                team_query.add_filter('unit_id', '=', self.workout['unit_id'])
                for team_member in list(team_query.fetch()):
                    team_members.append(team_member)

            answer_time_dict = {
                'answer': flag_attempt,
                'timestamp': time_string
            }

            #check if this is the first time this flag has been found
            if 'found_flags' in unit:
                if flag_attempt not in unit['found_flags']:
                    unit['found_flags'].append(flag_attempt)
                    point_value += 50
                    ds_client.put(unit)
                    answer_time_dict['first'] = True
            else:
                unit['found_flags'] = []
                unit['found_flags'].append(flag_attempt)
                point_value += 50
                answer_time_dict['first'] = True
                ds_client.put(unit)

            #check if the answer has already been submitted by another team member
            if self.submitted_answers:
                if flag_attempt not in self.workout['submitted_answers']:
                    self.submitted_answers.append(answer_time_dict)
                else:
                    response = {
                        'answer_correct': True,
                        'points_gained': 0,
                    }
                    return response

            else:
                self.submitted_answers = []
                self.submitted_answers.append(answer_time_dict)
            self.workout['submitted_answers'] = self.submitted_answers

            points += int(point_value)
            response = {
                'answer_correct': True,
                'points_gained': points,
            }

            #Add submitted answer to team member entities
            if 'team' in self.workout:
                for member in team_members:
                    if 'points' in member:
                        prev_score = member['points']
                        prev_score += point_value
                        member['points'] = prev_score
                    else:
                        member['points'] = point_value
                    member['submitted_answers'] = self.submitted_answers
                    ds_client.put(member)
            else:
                if 'points' in self.workout:
                    prev_score = self.workout['points']
                    prev_score += point_value
                    self.workout['points'] = prev_score
                else:
                    self.workout['points'] = point_value
                ds_client.put(self.workout)
        else:
            response = {'answer_correct': False, 'points_gained': 0}

        return response
def admin_page():
    admin_info = ds_client.get(ds_client.key('cybergym-admin-info', 'cybergym'))
    comment_query = ds_client.query(kind='cybergym-comments')
    comment_query.order = ['date']
    comment_list = comment_query.fetch()
    comments = []
    active_workouts = []
    for comment in comment_list:
        comments.append(comment)
        
    instance_list = []
    machine_instances = compute.instances().list(project=project, zone=zone, filter=("name:cybergym-*")).execute()
    if 'items' in machine_instances:
        for instance in machine_instances['items']:
            if 'name' in instance:
                instance_list.append(str(instance['name']))


    if request.method == "POST":
        response = {
            'action_completed': 'false'
        }
        data = request.get_json(force=True)
        if data['action'] == 'approve_user':
            if data['user_email'] in admin_info['pending']:
                admin_info['authorized_users'].append(data['user_email'])
                admin_info['pending'].remove(data['user_email'])
                ds_client.put(admin_info)
                response['action_completed'] = 'true'
                g_logger = log_client.logger('admin-app')
                g_logger.log_struct(
                    {
                        "message": "User {} approved for general access".format(data['user_email'])
                    }, severity=LOG_LEVELS.INFO
                )
        elif data['action'] == 'deny_user':
            if data['user_email'] in admin_info['pending']:
                admin_info['pending'].remove(data['user_email'])
                ds_client.put(admin_info)
                response['action_completed'] = 'true'
                g_logger = log_client.logger('admin-app')
                g_logger.log_struct(
                    {
                        "message": "User {} denied general access".format(data['user_email'])
                    }, severity=LOG_LEVELS.INFO
                )
        elif data['action'] == 'promote_user':
            if data['user_email'] in admin_info['authorized_users']:
                admin_info['admins'].append(data['user_email'])
                ds_client.put(admin_info)
                response['action_completed'] = 'true'
                g_logger = log_client.logger('admin-app')
                g_logger.log_struct(
                    {
                        "message": "User {} granted administrator privileges".format(data['user_email'])
                    }, severity=LOG_LEVELS.INFO
                )
        elif data['action'] == 'remove_user':
            if data['user_email'] in admin_info['authorized_users']:
                admin_info['authorized_users'].remove(data['user_email'])
                ds_client.put(admin_info)
                response['action_completed'] = 'true'
                g_logger = log_client.logger('admin-app')
                g_logger.log_struct(
                    {
                        "message": "User {} removed".format(data['user_email'])
                    }, severity=LOG_LEVELS.INFO
                )
        elif data['action'] == 'revoke_access':
            if data['user_email'] in admin_info['admins']:
                admin_info['admins'].remove(data['user_email'])
                ds_client.put(admin_info)
                response['action_completed'] = 'true'
                g_logger = log_client.logger('admin-app')
                g_logger.log_struct(
                    {
                        "message": "User {} is no longer an administrator".format(data['user_email'])
                    }, severity=LOG_LEVELS.INFO
                )
        return json.dumps(response)
    admin_actions = {key: value for key, value in AdminActions.__dict__.items() if not (key.startswith('__') and key.endswith('__'))}
    # print(admin_actions)
    return render_template('admin_page.html', auth_config=auth_config, admin_info=admin_info, admin_actions=admin_actions, active_workouts=active_workouts, comments=comments, instance_list=json.dumps(instance_list))
Exemple #15
0
def landing_page(workout_id):
    unit_list = ds_client.query(kind='cybergym-unit')
    workouts_list = list(
        unit_list.add_filter('workouts', '=', str(workout_id)).fetch())
    if not workouts_list:
        return render_template('404.html')
    else:
        workout = ds_client.get(ds_client.key('cybergym-workout', workout_id))
        unit = ds_client.get(ds_client.key('cybergym-unit',
                                           workout['unit_id']))
        admin_info = ds_client.get(
            ds_client.key('cybergym-admin-info', 'cybergym'))

    registration_required = workout.get('registration_required', False)
    logged_in_user = session.get('user_email', None)
    registered_user = workout.get('student_email', None)
    instructor_user = unit.get('instructor_id', None)
    allowed_users = admin_info['admins'].copy() + [instructor_user
                                                   ] + [registered_user]
    workout_server_query = ds_client.query(kind='cybergym-server')
    workout_server_query.add_filter('workout', '=', workout_id)
    server_list = []
    survey_yaml = parse_survey_yaml()
    survey = survey_yaml.get('survey', None) if survey_yaml else None
    for server in list(workout_server_query.fetch()):
        server_name = server['name'][11:]
        server['name'] = server_name
        try:
            snapshots = compute.snapshots().list(
                project=project,
                filter=f"name = {server.key.name}*").execute()
            server['snapshots'] = []
            for snapshot in snapshots['items']:
                server['snapshots'].append({
                    'snapshot_name':
                    snapshot['name'],
                    'creation_date':
                    snapshot['creationTimestamp']
                })
        except Exception as e:
            pass
        server_list.append(server)
    if registration_required and logged_in_user not in allowed_users:
        return render_template('login.html', auth_config=auth_config)

    if workout:
        assessment_manager = CyberArenaAssessment(workout_id)
        expiration = None
        is_expired = True
        if 'expiration' in workout:
            if (int(time.time()) -
                (int(workout['timestamp']) +
                 ((int(workout['expiration'])) * 60 * 60 * 24))) < 0:
                is_expired = False
            expiration = time.strftime(
                '%d %B %Y',
                (time.localtime((int(workout['expiration']) * 60 * 60 * 24) +
                                int(workout['timestamp']))))
        shutoff = None
        if 'run_hours' in workout:
            run_hours = int(workout['run_hours'])
            if run_hours == 0:
                shutoff = "expired"
            else:
                shutoff = time.strftime(
                    '%d %B %Y at %I:%M %p',
                    (time.localtime((int(workout['run_hours']) * 60 * 60) +
                                    int(workout['start_time']))))
        build_type = unit['build_type']
        container_url = None
        if build_type == 'container':
            container_url = workout['container_url']

        assessment = None
        if 'assessment' in workout and workout['assessment']:
            assessment = assessment_manager.get_assessment_questions()
        build_now = unit.get('build_now', True)
        if (request.method == "POST"):
            attempt = assessment_manager.submit()
            return json.dumps(attempt)
        return render_template('landing_page.html',
                               build_type=build_type,
                               workout=workout,
                               description=unit['description'],
                               container_url=container_url,
                               dns_suffix=dns_suffix,
                               expiration=expiration,
                               shutoff=shutoff,
                               assessment=assessment,
                               is_expired=is_expired,
                               build_now=build_now,
                               auth_config=auth_config,
                               servers=server_list,
                               survey=survey)
    else:
        return render_template('no_workout.html')
def process_assessment(workout, workout_id, request, assessment):
    valid_answers = []
    if workout['type'] == 'arena':
        points = 0
        response = {}
        answer_found = False
        flag_attempt = request.form.get('answer')
        question_key = request.form.get('question_content')
        point_value = int(request.form.get('point_value'))
        unit = ds_client.get(ds_client.key('cybergym-unit',
                                           workout['unit_id']))

        for i in range(len(assessment)):
            if question_key == assessment[i].get(
                    'question') and flag_attempt == assessment[i].get(
                        'answer'):
                answer_found = True

        # if flag_attempt in valid_answers:
        if answer_found:
            answer_time = time.gmtime(time.time())
            time_string = str(answer_time[3]) + ":" + str(
                answer_time[4]) + ":" + str(answer_time[5])
            team_query = ds_client.query(kind='cybergym-workout')
            team_query.add_filter('teacher_email', '=',
                                  workout['teacher_email'])
            team_query.add_filter('unit_id', '=', workout['unit_id'])
            team_members = []

            for team_member in list(team_query.fetch()):
                team_members.append(team_member)
            answer_time_dict = {
                'answer': flag_attempt,
                'timestamp': time_string
            }

            #check if the answer has already been submitted by another team member
            if 'submitted_answers' in workout:
                if flag_attempt not in workout['submitted_answers']:
                    workout['submitted_answers'].append(answer_time_dict)
                else:
                    response = {
                        'answer_correct': True,
                        'points_gained': 0,
                    }
                    return json.dumps(response)

            else:
                workout['submitted_answers'] = []
                workout['submitted_answers'].append(answer_time_dict)
            points += int(point_value)
            response = {
                'answer_correct': True,
                'points_gained': points,
            }
            #check if this is the first time this flag has been found
            if 'found_flags' in unit:
                if flag_attempt in unit['found_flags']:
                    print("flag already found")
                else:
                    unit['found_flags'].append(flag_attempt)
                    point_value += 50
                    ds_client.put(unit)
            else:
                unit['found_flags'] = []
                unit['found_flags'].append(flag_attempt)
                point_value += 50
                ds_client.put(unit)

            for member in team_members:
                if 'points' in member:
                    prev_score = member['points']
                    prev_score += point_value
                    member['points'] = prev_score
                else:
                    member['points'] = point_value
                member['submitted_answers'] = workout['submitted_answers']
                ds_client.put(member)
        else:
            if unit['found_flags']:
                if flag_attempt in unit['found_flags']:
                    response = {'answer_correct': True, 'points_gained': 0}
                    return json.dumps(response)
            else:
                response = {'answer_correct': False, 'points_gained': 0}
                return json.dumps(response)
        return json.dumps(response)
    else:
        num_correct = 0
        for i in range(len(assessment)):
            if (assessment[i].get('type') != 'upload'):
                valid_answers.append(assessment[i].get('answer'))

        assessment_answers = request.form.getlist('answer')
        assessment_questions = request.form.getlist('question')

        assessment_upload_prompt = request.form.getlist('upload_prompt')
        assessment_uploads = request.files.getlist('file_upload')

        store_student_uploads(workout_id, assessment_uploads)

        for i in range(len(assessment_answers)):

            user_input = {
                "question": assessment_questions[i],
                "answer": assessment_answers[i]
            }
            user_answer = str(user_input['answer'])
            true_answer = str(assessment[i].get('answer'))

            if valid_answers[i]:
                if (user_answer.lower() == valid_answers[i].lower()):
                    num_correct += 1

        uploaded_files = []
        urls = retrieve_student_uploads(workout_id)
        for index, item in enumerate(assessment_uploads):
            user_input = {
                "question": assessment_upload_prompt[index],
                "storage_url": urls[index]
            }
            uploaded_files.append(user_input)

        percentage_correct = num_correct / len(assessment_questions) * 100
        workout['uploaded_files'] = uploaded_files
        workout['submitted_answers'] = assessment_answers
        workout['assessment_score'] = percentage_correct
        ds_client.put(workout)
        return uploaded_files, assessment_answers, percentage_correct
Exemple #17
0
    def __init__(self,
                 server_spec,
                 build_id,
                 startup_scripts=None,
                 student_entry=False,
                 options=None):
        """
        Initialize the server specification from a build specification.
        @param server_spec: The raw build specification for the server.
        @type server_spec: dict
        @param build_id: The ID associated with the build
        @type build_id: str
        @param startup_scripts: The startup scripts for all servers associated with this build
        @type startup_scripts: dict
        @param student_entry: Whether this is a student entry server
        @type: bool
        @param options: Additional options to include with the server
        @type options: dict
        """
        base_name = server_spec['name']
        self.server_name = f"{build_id}-{base_name}"
        # First check to see if the server configuration already exists. If so, then return without error
        exists_check = ds_client.query(kind='cybergym-server')
        exists_check.add_filter("name", "=", self.server_name)
        if exists_check.fetch().num_results > 0:
            cloud_log(
                LogIDs.MAIN_APP,
                f"The server {self.server_name} already exists. Skipping configuration.",
                LOG_LEVELS.ERROR)
            raise ReferenceError

        self.build_id = build_id
        self.custom_image = server_spec.get('image', None)
        self.build_type = server_spec.get("build_type", None)
        self.machine_image = server_spec.get("machine_image", None)
        self.sshkey = server_spec.get("sshkey", None)
        self.tags = server_spec.get('tags', None)
        self.machine_type = server_spec.get("machine_type", "n1-standard-1")
        self.network_routing = server_spec.get("network_routing", None)
        self.min_cpu_platform = server_spec.get("minCpuPlatform", None)
        self.add_disk = server_spec.get("add_disk", None)
        self.options = server_spec.get("options", None)
        self.snapshot = server_spec.get('snapshot', None)
        # Add the network configuration
        self.networks = []
        for n in server_spec['nics']:
            n['external_NAT'] = n[
                'external_NAT'] if 'external_NAT' in n else False
            nic = {
                "network": f"{build_id}-{n['network']}",
                "internal_IP": n['internal_IP'],
                "subnet": f"{build_id}-{n['network']}-{n['subnet']}",
                "external_NAT": n['external_NAT']
            }
            # Nested VMs are sometimes used for vulnerable servers. This adds those specified IP addresses as
            # aliases to the NIC
            if 'IP_aliases' in n and n['IP_aliases']:
                alias_ip_ranges = []
                for ipaddr in n['IP_aliases']:
                    alias_ip_ranges.append({"ipCidrRange": ipaddr})
                nic['aliasIpRanges'] = alias_ip_ranges
            self.networks.append(nic)
        # Competitions may have meta_data defined, but compute workouts use assessments. First, check for meta_data
        # if this is a competition, and then look for startup scripts which have been identified from the assessment
        self.meta_data = server_spec.get("meta_data", None)
        if not self.meta_data and startup_scripts and base_name in startup_scripts:
            self.meta_data = startup_scripts[base_name]
        self.student_entry = student_entry
        self.guacamole_startup_script = server_spec.get(
            'guacamole_startup_script', None)
        self.options = options