def validate_create_session_request()

in source/idea/idea-virtual-desktop-controller/src/ideavirtualdesktopcontroller/app/api/virtual_desktop_api.py [0:0]


    def validate_create_session_request(self, session: VirtualDesktopSession) -> (VirtualDesktopSession, bool):
        if Utils.is_empty(session.project) or Utils.is_empty(session.project.project_id):
            session.failure_reason = 'missing session.project.project_id'
            return session, False

        # validate if the user belongs to this project
        is_user_part_of_project = False
        user_projects = self.get_user_projects(username=session.owner)
        for project in user_projects:
            if project.project_id == session.project.project_id:
                is_user_part_of_project = True
                session.project = project
                break

        if not is_user_part_of_project:
            session.failure_reason = f'User {session.owner} does not belong in the selected project.'
            return session, False

        # Validate Software Stack Object
        if Utils.is_empty(session.software_stack):
            session.failure_reason = 'missing session.software_stack'
            return session, False

        # Missing information in Software Stack
        if Utils.is_empty(session.software_stack.stack_id) or Utils.is_empty(session.software_stack.base_os):
            session.failure_reason = 'missing session.software_stack.stack_id and/or session.software_stack.base_os'
            return session, False

        try:
            software_stack_dict = software_stacks.get_software_stack(stack_id=session.software_stack.stack_id, base_os=session.software_stack.base_os)
            software_stack = self.software_stack_db.convert_db_dict_to_software_stack_object(software_stack_dict)
        except SoftwareStackNotFound:
            session.failure_reason = f'Invalid session.software_stack.stack_id: {session.software_stack.stack_id} and/or session.software_stack.base_os: {session.software_stack.base_os}'
            return session, False

        # validate software stack part of the same project
        is_software_stack_part_of_project = False
        for project in software_stack.projects:
            if project.project_id == session.project.project_id:
                is_software_stack_part_of_project = True
                break

        if not is_software_stack_part_of_project:
            session.failure_reason = f'session.software_stack.stack_id: {session.software_stack.stack_id} is not part of session.project.project_id: {session.project.project_id}'
            return session, False

        # Check the Project budget if we are enabled for budget enforcement
        if self.context.config().get_bool('vdc.controller.enforce_project_budgets', default=True):
            self._logger.info(f"eVDI Project budgets is enabled. Checking budgets.")

            if not session.project.is_budgets_enabled():
                self._logger.info(f"Project budget disabled or not configured for {session.project.name}")
            else:
                project_budget_name = Utils.get_as_string(session.project.budget.budget_name, default=None)

                if project_budget_name:
                    self._logger.debug(f"Found Budget name: {project_budget_name}")
                    project_budget = self.context.aws_util().budgets_get_budget(budget_name=project_budget_name)

                    if Utils.is_not_empty(project_budget):
                        self._logger.debug(f"Budget details: {project_budget}")

                        if project_budget.actual_spend > project_budget.budget_limit:
                            self._logger.error(f"Budget exceeded. Denying session request")
                            session.failure_reason = f"Project {project_budget_name} budget has been exceeded. Unable to start session. Contact your Administrator."
                            return session, False

                        else:
                            self._logger.debug(f"Budget usage is acceptable. Proceeding")
        else:
            self._logger.info(f"VDI Project budgets are disabled (vdc.controller.enforce_project_budgets). Not checking budgets.")

        session.software_stack = software_stack

        # Validate hibernation value
        if Utils.is_empty(session.hibernation_enabled):
            session.failure_reason = 'missing session.hibernation_enabled'
            return session, False

        # Instance Type validation
        if Utils.is_empty(session.server) or Utils.is_empty(session.server.instance_type):
            session.failure_reason = 'missing session.server.instance_type'
            return session, False

        is_instance_type_valid = False
        allowed_instance_types = self.controller_utils.get_valid_instance_types_by_software_stack(session.hibernation_enabled, session.software_stack)
        for allowed_instance_type in allowed_instance_types:
            is_instance_type_valid = session.server.instance_type == Utils.get_value_as_string('InstanceType', allowed_instance_type, '')
            if is_instance_type_valid:
                break

        if not is_instance_type_valid:
            session.failure_reason = f'Invalid session.server.instance_type: {session.server.instance_type}. Not allowed for current configuration'
            return session, False

        # Technical Validation for Hibernation.
        if session.hibernation_enabled and session.software_stack.base_os is VirtualDesktopBaseOS.WINDOWS:
            ram = self.controller_utils.get_instance_ram(session.server.instance_type).as_unit(SocaMemoryUnit.GiB)
            if ram.value > 16:
                session.failure_reason = f'OS {session.software_stack.base_os} does not support Instance Hibernation for instances with RAM greater than 16GiB.'
                return session, False
        else:
            pass

        # Technical Validations for Session Type
        # // https://docs.aws.amazon.com/dcv/latest/adminguide/servers.html - AMD GPU, Windows support Console sessions only
        if session.type is VirtualDesktopSessionType.VIRTUAL and session.software_stack.base_os is VirtualDesktopBaseOS.WINDOWS:
            session.failure_reason = f'{session.software_stack.base_os} does not support Virtual Sessions'
            return session, False

        gpu_manufacturer = self.controller_utils.get_gpu_manufacturer(session.server.instance_type)
        if session.type is VirtualDesktopSessionType.VIRTUAL and gpu_manufacturer is VirtualDesktopGPU.AMD:
            session.failure_reason = f'Instance type: {session.server.instance_type} with GPU {gpu_manufacturer} does not support Virtual Sessions'
            return session, False

        if session.software_stack.placement and session.software_stack.placement.tenancy == VirtualDesktopTenancy.HOST and not self.controller_utils.dedicated_hosts_supported(session.server.instance_type):
            session.failure_reason = f'Instance type: {session.server.instance_type} does not support Dedicated Hosts tenancy'
            return session, False

        architecture = self.controller_utils.get_architecture(session.server.instance_type)
        if session.type is VirtualDesktopSessionType.CONSOLE and architecture is VirtualDesktopArchitecture.ARM64:
            session.failure_reason = f'Instance type: {session.server.instance_type} does not support Console Sessions'
            return session, False

        # Validate Root Volume Size
        if Utils.is_empty(session.server) or Utils.is_empty(session.server.root_volume_size):
            session.failure_reason = 'missing session.server.root_volume_size'
            return session, False

        # Business Validation for MAX Root Volume
        max_root_volume_size = self.context.config().get_int('virtual-desktop-controller.dcv_session.max_root_volume_memory', required=True)
        if session.server.root_volume_size > max_root_volume_size:
            session.failure_reason = f'root volume size: {session.server.root_volume_size} is greater than maximum allowed root volume size: {max_root_volume_size}GB.'
            return session, False

        # Technical Validation for Root Volume
        min_root_volume: SocaMemory = software_stack.min_storage
        if session.hibernation_enabled:
            min_root_volume = software_stack.min_storage + self.controller_utils.get_instance_ram(session.server.instance_type)

        if session.server.root_volume_size < min_root_volume:
            session.failure_reason = f'root volume size: {session.server.root_volume_size} is less than the minimum required root volume size: {min_root_volume} when hibernation is {"enabled" if session.hibernation_enabled else "disabled"}.'
            return session, False

        return session, True